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