source: vendor/current/source3/smbd/smb2_break.c

Last change on this file was 988, checked in by Silvan Scherrer, 9 years ago

Samba Server: update vendor to version 4.4.3

File size: 14.1 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 Core SMB2 server
4
5 Copyright (C) Stefan Metzmacher 2009
6 Copyright (C) Jeremy Allison 2010
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "includes.h"
23#include "smbd/smbd.h"
24#include "smbd/globals.h"
25#include "../libcli/smb/smb_common.h"
26#include "../lib/util/tevent_ntstatus.h"
27#include "locking/leases_db.h"
28
29static NTSTATUS smbd_smb2_request_process_lease_break(
30 struct smbd_smb2_request *req);
31
32static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
33 struct tevent_context *ev,
34 struct smbd_smb2_request *smb2req,
35 struct files_struct *in_fsp,
36 uint8_t in_oplock_level);
37static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
38 uint8_t *out_oplock_level);
39
40static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq);
41NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
42{
43 NTSTATUS status;
44 const uint8_t *inbody;
45 uint8_t in_oplock_level;
46 uint64_t in_file_id_persistent;
47 uint64_t in_file_id_volatile;
48 struct files_struct *in_fsp;
49 struct tevent_req *subreq;
50
51 status = smbd_smb2_request_verify_sizes(req, 0x18);
52 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
53 /*
54 * Retry as a lease break
55 */
56 return smbd_smb2_request_process_lease_break(req);
57 }
58 if (!NT_STATUS_IS_OK(status)) {
59 return smbd_smb2_request_error(req, status);
60 }
61 inbody = SMBD_SMB2_IN_BODY_PTR(req);
62
63 in_oplock_level = CVAL(inbody, 0x02);
64
65 /* 0x03 1 bytes reserved */
66 /* 0x04 4 bytes reserved */
67 in_file_id_persistent = BVAL(inbody, 0x08);
68 in_file_id_volatile = BVAL(inbody, 0x10);
69
70 in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
71 if (in_fsp == NULL) {
72 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
73 }
74
75 /* Are we awaiting a break message ? */
76 if (in_fsp->oplock_timeout == NULL) {
77 return smbd_smb2_request_error(
78 req, NT_STATUS_INVALID_OPLOCK_PROTOCOL);
79 }
80
81 if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE &&
82 in_oplock_level != SMB2_OPLOCK_LEVEL_II) {
83 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
84 }
85
86 subreq = smbd_smb2_oplock_break_send(req, req->sconn->ev_ctx,
87 req, in_fsp, in_oplock_level);
88 if (subreq == NULL) {
89 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
90 }
91 tevent_req_set_callback(subreq, smbd_smb2_request_oplock_break_done, req);
92
93 return smbd_smb2_request_pending_queue(req, subreq, 500);
94}
95
96static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq)
97{
98 struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
99 struct smbd_smb2_request);
100 const uint8_t *inbody;
101 uint64_t in_file_id_persistent;
102 uint64_t in_file_id_volatile;
103 uint8_t out_oplock_level = 0;
104 DATA_BLOB outbody;
105 NTSTATUS status;
106 NTSTATUS error; /* transport error */
107
108 status = smbd_smb2_oplock_break_recv(subreq, &out_oplock_level);
109 TALLOC_FREE(subreq);
110 if (!NT_STATUS_IS_OK(status)) {
111 error = smbd_smb2_request_error(req, status);
112 if (!NT_STATUS_IS_OK(error)) {
113 smbd_server_connection_terminate(req->xconn,
114 nt_errstr(error));
115 return;
116 }
117 return;
118 }
119
120 inbody = SMBD_SMB2_IN_BODY_PTR(req);
121
122 in_file_id_persistent = BVAL(inbody, 0x08);
123 in_file_id_volatile = BVAL(inbody, 0x10);
124
125 outbody = smbd_smb2_generate_outbody(req, 0x18);
126 if (outbody.data == NULL) {
127 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
128 if (!NT_STATUS_IS_OK(error)) {
129 smbd_server_connection_terminate(req->xconn,
130 nt_errstr(error));
131 return;
132 }
133 return;
134 }
135
136 SSVAL(outbody.data, 0x00, 0x18); /* struct size */
137 SCVAL(outbody.data, 0x02,
138 out_oplock_level); /* SMB2 oplock level */
139 SCVAL(outbody.data, 0x03, 0); /* reserved */
140 SIVAL(outbody.data, 0x04, 0); /* reserved */
141 SBVAL(outbody.data, 0x08,
142 in_file_id_persistent); /* file id (persistent) */
143 SBVAL(outbody.data, 0x10,
144 in_file_id_volatile); /* file id (volatile) */
145
146 error = smbd_smb2_request_done(req, outbody, NULL);
147 if (!NT_STATUS_IS_OK(error)) {
148 smbd_server_connection_terminate(req->xconn,
149 nt_errstr(error));
150 return;
151 }
152}
153
154struct smbd_smb2_oplock_break_state {
155 struct smbd_smb2_request *smb2req;
156 uint8_t out_oplock_level; /* SMB2 oplock level. */
157};
158
159static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
160 struct tevent_context *ev,
161 struct smbd_smb2_request *smb2req,
162 struct files_struct *fsp,
163 uint8_t in_oplock_level)
164{
165 struct tevent_req *req;
166 struct smbd_smb2_oplock_break_state *state;
167 struct smb_request *smbreq;
168 int oplocklevel = map_smb2_oplock_levels_to_samba(in_oplock_level);
169 bool break_to_none = (oplocklevel == NO_OPLOCK);
170 bool result;
171
172 req = tevent_req_create(mem_ctx, &state,
173 struct smbd_smb2_oplock_break_state);
174 if (req == NULL) {
175 return NULL;
176 }
177 state->smb2req = smb2req;
178 state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
179
180 DEBUG(10,("smbd_smb2_oplock_break_send: %s - %s, "
181 "samba level %d\n",
182 fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
183 oplocklevel));
184
185 smbreq = smbd_smb2_fake_smb_request(smb2req);
186 if (tevent_req_nomem(smbreq, req)) {
187 return tevent_req_post(req, ev);
188 }
189
190 DEBUG(5,("smbd_smb2_oplock_break_send: got SMB2 oplock break (%u) from client "
191 "for file %s, %s\n",
192 (unsigned int)in_oplock_level,
193 fsp_str_dbg(fsp),
194 fsp_fnum_dbg(fsp)));
195
196 if ((fsp->sent_oplock_break == BREAK_TO_NONE_SENT) ||
197 (break_to_none)) {
198 result = remove_oplock(fsp);
199 state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
200 } else {
201 result = downgrade_oplock(fsp);
202 state->out_oplock_level = SMB2_OPLOCK_LEVEL_II;
203 }
204
205 if (!result) {
206 DEBUG(0, ("smbd_smb2_oplock_break_send: error in removing "
207 "oplock on file %s\n", fsp_str_dbg(fsp)));
208 /* Hmmm. Is this panic justified? */
209 smb_panic("internal tdb error");
210 }
211
212 tevent_req_done(req);
213 return tevent_req_post(req, ev);
214}
215
216static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
217 uint8_t *out_oplock_level)
218{
219 NTSTATUS status;
220 struct smbd_smb2_oplock_break_state *state =
221 tevent_req_data(req,
222 struct smbd_smb2_oplock_break_state);
223
224 if (tevent_req_is_nterror(req, &status)) {
225 tevent_req_received(req);
226 return status;
227 }
228
229 *out_oplock_level = state->out_oplock_level;
230
231 tevent_req_received(req);
232 return NT_STATUS_OK;
233}
234
235static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq);
236
237static struct tevent_req *smbd_smb2_lease_break_send(
238 TALLOC_CTX *mem_ctx, struct tevent_context *ev,
239 struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
240 uint32_t in_lease_state);
241static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
242 uint32_t *out_lease_state);
243
244
245static NTSTATUS smbd_smb2_request_process_lease_break(
246 struct smbd_smb2_request *req)
247{
248 NTSTATUS status;
249 const uint8_t *inbody;
250 struct smb2_lease_key in_lease_key;
251 uint32_t in_lease_state;
252 struct tevent_req *subreq;
253
254 status = smbd_smb2_request_verify_sizes(req, 0x24);
255 if (!NT_STATUS_IS_OK(status)) {
256 return smbd_smb2_request_error(req, status);
257 }
258
259 inbody = SMBD_SMB2_IN_BODY_PTR(req);
260
261 in_lease_key.data[0] = BVAL(inbody, 8);
262 in_lease_key.data[1] = BVAL(inbody, 16);
263 in_lease_state = IVAL(inbody, 24);
264
265 subreq = smbd_smb2_lease_break_send(req, req->sconn->ev_ctx, req,
266 in_lease_key, in_lease_state);
267 if (subreq == NULL) {
268 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
269 }
270 tevent_req_set_callback(subreq, smbd_smb2_request_lease_break_done, req);
271
272 return smbd_smb2_request_pending_queue(req, subreq, 500);
273}
274
275static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq)
276{
277 struct smbd_smb2_request *req = tevent_req_callback_data(
278 subreq, struct smbd_smb2_request);
279 const uint8_t *inbody;
280 struct smb2_lease_key in_lease_key;
281 uint32_t out_lease_state = 0;
282 DATA_BLOB outbody;
283 NTSTATUS status;
284 NTSTATUS error; /* transport error */
285
286 status = smbd_smb2_lease_break_recv(subreq, &out_lease_state);
287 TALLOC_FREE(subreq);
288 if (!NT_STATUS_IS_OK(status)) {
289 error = smbd_smb2_request_error(req, status);
290 if (!NT_STATUS_IS_OK(error)) {
291 smbd_server_connection_terminate(req->xconn,
292 nt_errstr(error));
293 return;
294 }
295 return;
296 }
297
298 inbody = SMBD_SMB2_IN_BODY_PTR(req);
299
300 in_lease_key.data[0] = BVAL(inbody, 8);
301 in_lease_key.data[1] = BVAL(inbody, 16);
302
303 outbody = smbd_smb2_generate_outbody(req, 0x24);
304 if (outbody.data == NULL) {
305 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
306 if (!NT_STATUS_IS_OK(error)) {
307 smbd_server_connection_terminate(req->xconn,
308 nt_errstr(error));
309 return;
310 }
311 return;
312 }
313
314 SSVAL(outbody.data, 0x00, 0x24); /* struct size */
315 SSVAL(outbody.data, 0x02, 0); /* reserved */
316 SIVAL(outbody.data, 0x04, 0); /* flags, must be 0 */
317 SBVAL(outbody.data, 0x08, in_lease_key.data[0]);
318 SBVAL(outbody.data, 0x10, in_lease_key.data[1]);
319 SIVAL(outbody.data, 0x18, out_lease_state);
320 SBVAL(outbody.data, 0x1c, 0); /* leaseduration, must be 0 */
321
322 error = smbd_smb2_request_done(req, outbody, NULL);
323 if (!NT_STATUS_IS_OK(error)) {
324 smbd_server_connection_terminate(req->xconn,
325 nt_errstr(error));
326 return;
327 }
328}
329
330struct smbd_smb2_lease_break_state {
331 uint32_t lease_state;
332};
333
334struct lease_lookup_state {
335 TALLOC_CTX *mem_ctx;
336 /* Return parameters. */
337 uint32_t num_file_ids;
338 struct file_id *ids;
339 NTSTATUS status;
340};
341
342static void lease_parser(
343 uint32_t num_files,
344 const struct leases_db_file *files,
345 void *private_data)
346{
347 struct lease_lookup_state *lls =
348 (struct lease_lookup_state *)private_data;
349
350 lls->status = NT_STATUS_OK;
351 lls->num_file_ids = num_files;
352 lls->status = leases_db_copy_file_ids(lls->mem_ctx,
353 num_files,
354 files,
355 &lls->ids);
356}
357
358static struct tevent_req *smbd_smb2_lease_break_send(
359 TALLOC_CTX *mem_ctx, struct tevent_context *ev,
360 struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
361 uint32_t in_lease_state)
362{
363 struct tevent_req *req;
364 struct smbd_smb2_lease_break_state *state;
365 struct lease_lookup_state lls = {.mem_ctx = mem_ctx};
366 NTSTATUS status;
367
368 req = tevent_req_create(mem_ctx, &state,
369 struct smbd_smb2_lease_break_state);
370 if (req == NULL) {
371 return NULL;
372 }
373 state->lease_state = in_lease_state;
374
375 /* Find any file ids with this lease key. */
376 status = leases_db_parse(&smb2_req->xconn->smb2.client.guid,
377 &in_lease_key,
378 lease_parser,
379 &lls);
380
381 if (!NT_STATUS_IS_OK(status)) {
382 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
383 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
384 DEBUG(10, ("No record for lease key found\n"));
385 }
386 } else if (!NT_STATUS_IS_OK(lls.status)) {
387 status = lls.status;
388 } else if (lls.num_file_ids == 0) {
389 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
390 }
391
392 if (!NT_STATUS_IS_OK(status)) {
393 tevent_req_nterror(req, status);
394 return tevent_req_post(req, ev);
395 }
396
397 status = downgrade_lease(smb2_req->xconn,
398 lls.num_file_ids,
399 lls.ids,
400 &in_lease_key,
401 in_lease_state);
402
403 if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
404 tevent_req_done(req);
405 return tevent_req_post(req, ev);
406 }
407 if (tevent_req_nterror(req, status)) {
408 DEBUG(10, ("downgrade_lease returned %s\n",
409 nt_errstr(status)));
410 return tevent_req_post(req, ev);
411 }
412
413 tevent_req_done(req);
414 return tevent_req_post(req, ev);
415}
416
417static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
418 uint32_t *out_lease_state)
419{
420 struct smbd_smb2_lease_break_state *state = tevent_req_data(
421 req, struct smbd_smb2_lease_break_state);
422 NTSTATUS status;
423
424 if (tevent_req_is_nterror(req, &status)) {
425 return status;
426 }
427 *out_lease_state = state->lease_state;
428 return NT_STATUS_OK;
429}
430
431/*********************************************************
432 Create and send an asynchronous
433 SMB2 OPLOCK_BREAK_NOTIFICATION.
434*********************************************************/
435
436void send_break_message_smb2(files_struct *fsp,
437 uint32_t break_from,
438 uint32_t break_to)
439{
440 NTSTATUS status;
441 struct smbXsrv_connection *xconn = NULL;
442 struct smbXsrv_session *session = NULL;
443 struct timeval tv = timeval_current();
444 NTTIME now = timeval_to_nttime(&tv);
445
446 /*
447 * TODO: in future we should have a better algorithm
448 * to find the correct connection for a break message.
449 * Then we also need some retries if a channel gets disconnected.
450 */
451 xconn = fsp->conn->sconn->client->connections;
452
453 status = smb2srv_session_lookup_conn(xconn,
454 fsp->vuid,
455 now,
456 &session);
457 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED) ||
458 (session == NULL))
459 {
460
461 DEBUG(10,("send_break_message_smb2: skip oplock break "
462 "for file %s, %s, smb2 level %u session %llu not found\n",
463 fsp_str_dbg(fsp),
464 fsp_fnum_dbg(fsp),
465 (unsigned int)break_to,
466 (unsigned long long)fsp->vuid));
467 return;
468 }
469
470 DEBUG(10,("send_break_message_smb2: sending oplock break "
471 "for file %s, %s, smb2 level %u\n",
472 fsp_str_dbg(fsp),
473 fsp_fnum_dbg(fsp),
474 (unsigned int)break_to ));
475
476 if (fsp->oplock_type == LEASE_OPLOCK) {
477 uint32_t break_flags = 0;
478 uint16_t new_epoch;
479
480 if (fsp->lease->lease.lease_state != SMB2_LEASE_NONE) {
481 break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
482 }
483
484 if (fsp->lease->lease.lease_version > 1) {
485 new_epoch = fsp->lease->lease.lease_epoch;
486 } else {
487 new_epoch = 0;
488 }
489
490 status = smbd_smb2_send_lease_break(xconn, new_epoch, break_flags,
491 &fsp->lease->lease.lease_key,
492 break_from, break_to);
493 } else {
494 uint8_t smb2_oplock_level;
495 smb2_oplock_level = (break_to & SMB2_LEASE_READ) ?
496 SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE;
497 status = smbd_smb2_send_oplock_break(xconn,
498 session,
499 fsp->conn->tcon,
500 fsp->op,
501 smb2_oplock_level);
502 }
503 if (!NT_STATUS_IS_OK(status)) {
504 smbd_server_connection_terminate(xconn,
505 nt_errstr(status));
506 return;
507 }
508}
Note: See TracBrowser for help on using the repository browser.