smbd: Make find_share_mode_lease() static
[kai/samba-autobuild/.git] / source3 / smbd / smb2_break.c
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 NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
45 {
46         NTSTATUS status;
47         const uint8_t *inbody;
48         uint8_t in_oplock_level;
49         uint64_t in_file_id_persistent;
50         uint64_t in_file_id_volatile;
51         struct files_struct *in_fsp;
52         struct tevent_req *subreq;
53
54         status = smbd_smb2_request_verify_sizes(req, 0x18);
55         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
56                 /*
57                  * Retry as a lease break
58                  */
59                 return smbd_smb2_request_process_lease_break(req);
60         }
61         if (!NT_STATUS_IS_OK(status)) {
62                 return smbd_smb2_request_error(req, status);
63         }
64         inbody = SMBD_SMB2_IN_BODY_PTR(req);
65
66         in_oplock_level         = CVAL(inbody, 0x02);
67
68         /* 0x03 1 bytes reserved */
69         /* 0x04 4 bytes reserved */
70         in_file_id_persistent           = BVAL(inbody, 0x08);
71         in_file_id_volatile             = BVAL(inbody, 0x10);
72
73         in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
74         if (in_fsp == NULL) {
75                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
76         }
77
78         /* Are we awaiting a break message ? */
79         if (in_fsp->oplock_timeout == NULL) {
80                 return smbd_smb2_request_error(
81                         req, NT_STATUS_INVALID_OPLOCK_PROTOCOL);
82         }
83
84         if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE &&
85             in_oplock_level != SMB2_OPLOCK_LEVEL_II) {
86                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
87         }
88
89         subreq = smbd_smb2_oplock_break_send(req, req->sconn->ev_ctx,
90                                              req, in_fsp, in_oplock_level);
91         if (subreq == NULL) {
92                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
93         }
94         tevent_req_set_callback(subreq, smbd_smb2_request_oplock_break_done, req);
95
96         return smbd_smb2_request_pending_queue(req, subreq, 500);
97 }
98
99 static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq)
100 {
101         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
102                                         struct smbd_smb2_request);
103         const uint8_t *inbody;
104         uint64_t in_file_id_persistent;
105         uint64_t in_file_id_volatile;
106         uint8_t out_oplock_level = 0;
107         DATA_BLOB outbody;
108         NTSTATUS status;
109         NTSTATUS error; /* transport error */
110
111         status = smbd_smb2_oplock_break_recv(subreq, &out_oplock_level);
112         TALLOC_FREE(subreq);
113         if (!NT_STATUS_IS_OK(status)) {
114                 error = smbd_smb2_request_error(req, status);
115                 if (!NT_STATUS_IS_OK(error)) {
116                         smbd_server_connection_terminate(req->xconn,
117                                                          nt_errstr(error));
118                         return;
119                 }
120                 return;
121         }
122
123         inbody = SMBD_SMB2_IN_BODY_PTR(req);
124
125         in_file_id_persistent   = BVAL(inbody, 0x08);
126         in_file_id_volatile     = BVAL(inbody, 0x10);
127
128         outbody = smbd_smb2_generate_outbody(req, 0x18);
129         if (outbody.data == NULL) {
130                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
131                 if (!NT_STATUS_IS_OK(error)) {
132                         smbd_server_connection_terminate(req->xconn,
133                                                          nt_errstr(error));
134                         return;
135                 }
136                 return;
137         }
138
139         SSVAL(outbody.data, 0x00, 0x18);        /* struct size */
140         SCVAL(outbody.data, 0x02,
141               out_oplock_level);                /* SMB2 oplock level */
142         SCVAL(outbody.data, 0x03, 0);           /* reserved */
143         SIVAL(outbody.data, 0x04, 0);           /* reserved */
144         SBVAL(outbody.data, 0x08,
145               in_file_id_persistent);           /* file id (persistent) */
146         SBVAL(outbody.data, 0x10,
147               in_file_id_volatile);             /* file id (volatile) */
148
149         error = smbd_smb2_request_done(req, outbody, NULL);
150         if (!NT_STATUS_IS_OK(error)) {
151                 smbd_server_connection_terminate(req->xconn,
152                                                  nt_errstr(error));
153                 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 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         struct tevent_req *req;
169         struct smbd_smb2_oplock_break_state *state;
170         struct smb_request *smbreq;
171         int oplocklevel = map_smb2_oplock_levels_to_samba(in_oplock_level);
172         bool break_to_none = (oplocklevel == NO_OPLOCK);
173         bool result;
174
175         req = tevent_req_create(mem_ctx, &state,
176                                 struct smbd_smb2_oplock_break_state);
177         if (req == NULL) {
178                 return NULL;
179         }
180         state->smb2req = smb2req;
181         state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
182
183         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         smbreq = smbd_smb2_fake_smb_request(smb2req);
189         if (tevent_req_nomem(smbreq, req)) {
190                 return tevent_req_post(req, ev);
191         }
192
193         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         if ((fsp->sent_oplock_break == BREAK_TO_NONE_SENT) ||
200                         (break_to_none)) {
201                 result = remove_oplock(fsp);
202                 state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
203         } else {
204                 result = downgrade_oplock(fsp);
205                 state->out_oplock_level = SMB2_OPLOCK_LEVEL_II;
206         }
207
208         if (!result) {
209                 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                 smb_panic("internal tdb error");
213         }
214
215         tevent_req_done(req);
216         return tevent_req_post(req, ev);
217 }
218
219 static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
220                                             uint8_t *out_oplock_level)
221 {
222         NTSTATUS status;
223         struct smbd_smb2_oplock_break_state *state =
224                 tevent_req_data(req,
225                 struct smbd_smb2_oplock_break_state);
226
227         if (tevent_req_is_nterror(req, &status)) {
228                 tevent_req_received(req);
229                 return status;
230         }
231
232         *out_oplock_level = state->out_oplock_level;
233
234         tevent_req_received(req);
235         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 static NTSTATUS smbd_smb2_request_process_lease_break(
249         struct smbd_smb2_request *req)
250 {
251         NTSTATUS status;
252         const uint8_t *inbody;
253         struct smb2_lease_key in_lease_key;
254         uint32_t in_lease_state;
255         struct tevent_req *subreq;
256
257         status = smbd_smb2_request_verify_sizes(req, 0x24);
258         if (!NT_STATUS_IS_OK(status)) {
259                 return smbd_smb2_request_error(req, status);
260         }
261
262         inbody = SMBD_SMB2_IN_BODY_PTR(req);
263
264         in_lease_key.data[0] = BVAL(inbody, 8);
265         in_lease_key.data[1] = BVAL(inbody, 16);
266         in_lease_state = IVAL(inbody, 24);
267
268         subreq = smbd_smb2_lease_break_send(req, req->sconn->ev_ctx, req,
269                                             in_lease_key, in_lease_state);
270         if (subreq == NULL) {
271                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
272         }
273         tevent_req_set_callback(subreq, smbd_smb2_request_lease_break_done, req);
274
275         return smbd_smb2_request_pending_queue(req, subreq, 500);
276 }
277
278 static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq)
279 {
280         struct smbd_smb2_request *req = tevent_req_callback_data(
281                 subreq, struct smbd_smb2_request);
282         const uint8_t *inbody;
283         struct smb2_lease_key in_lease_key;
284         uint32_t out_lease_state = 0;
285         DATA_BLOB outbody;
286         NTSTATUS status;
287         NTSTATUS error; /* transport error */
288
289         status = smbd_smb2_lease_break_recv(subreq, &out_lease_state);
290         TALLOC_FREE(subreq);
291         if (!NT_STATUS_IS_OK(status)) {
292                 error = smbd_smb2_request_error(req, status);
293                 if (!NT_STATUS_IS_OK(error)) {
294                         smbd_server_connection_terminate(req->xconn,
295                                                          nt_errstr(error));
296                         return;
297                 }
298                 return;
299         }
300
301         inbody = SMBD_SMB2_IN_BODY_PTR(req);
302
303         in_lease_key.data[0] = BVAL(inbody, 8);
304         in_lease_key.data[1] = BVAL(inbody, 16);
305
306         outbody = smbd_smb2_generate_outbody(req, 0x24);
307         if (outbody.data == NULL) {
308                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
309                 if (!NT_STATUS_IS_OK(error)) {
310                         smbd_server_connection_terminate(req->xconn,
311                                                          nt_errstr(error));
312                         return;
313                 }
314                 return;
315         }
316
317         SSVAL(outbody.data, 0x00, 0x24);        /* struct size */
318         SSVAL(outbody.data, 0x02, 0);           /* reserved */
319         SIVAL(outbody.data, 0x04, 0);           /* flags, must be 0 */
320         SBVAL(outbody.data, 0x08, in_lease_key.data[0]);
321         SBVAL(outbody.data, 0x10, in_lease_key.data[1]);
322         SIVAL(outbody.data, 0x18, out_lease_state);
323         SBVAL(outbody.data, 0x1c, 0);           /* leaseduration, must be 0 */
324
325         error = smbd_smb2_request_done(req, outbody, NULL);
326         if (!NT_STATUS_IS_OK(error)) {
327                 smbd_server_connection_terminate(req->xconn,
328                                                  nt_errstr(error));
329                 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 static void lease_parser(
346         uint32_t num_files,
347         const struct leases_db_file *files,
348         void *private_data)
349 {
350         struct lease_lookup_state *lls =
351                 (struct lease_lookup_state *)private_data;
352
353         lls->status = NT_STATUS_OK;
354         lls->num_file_ids = num_files;
355         lls->status = leases_db_copy_file_ids(lls->mem_ctx,
356                                 num_files,
357                                 files,
358                                 &lls->ids);
359 }
360
361 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         struct tevent_req *req;
367         struct smbd_smb2_lease_break_state *state;
368         struct lease_lookup_state lls = {.mem_ctx = mem_ctx};
369         NTSTATUS status;
370
371         req = tevent_req_create(mem_ctx, &state,
372                                 struct smbd_smb2_lease_break_state);
373         if (req == NULL) {
374                 return NULL;
375         }
376         state->lease_state = in_lease_state;
377
378         /* Find any file ids with this lease key. */
379         status = leases_db_parse(&smb2_req->xconn->smb2.client.guid,
380                                  &in_lease_key,
381                                  lease_parser,
382                                  &lls);
383
384         if (!NT_STATUS_IS_OK(status)) {
385                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
386                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
387                         DEBUG(10, ("No record for lease key found\n"));
388                 }
389                 tevent_req_nterror(req, status);
390                 return tevent_req_post(req, ev);
391         }
392
393         if (tevent_req_nterror(req, lls.status)) {
394                 return tevent_req_post(req, ev);
395         }
396
397         if (lls.num_file_ids == 0) {
398                 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
399                 return tevent_req_post(req, ev);
400         }
401
402         status = downgrade_lease(smb2_req->xconn,
403                                 lls.num_file_ids,
404                                 lls.ids,
405                                 &in_lease_key,
406                                 in_lease_state);
407
408         if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
409                 tevent_req_done(req);
410                 return tevent_req_post(req, ev);
411         }
412         if (tevent_req_nterror(req, status)) {
413                 DEBUG(10, ("downgrade_lease returned %s\n",
414                            nt_errstr(status)));
415                 return tevent_req_post(req, ev);
416         }
417
418         tevent_req_done(req);
419         return tevent_req_post(req, ev);
420 }
421
422 static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
423                                            uint32_t *out_lease_state)
424 {
425         struct smbd_smb2_lease_break_state *state = tevent_req_data(
426                 req, struct smbd_smb2_lease_break_state);
427         NTSTATUS status;
428
429         if (tevent_req_is_nterror(req, &status)) {
430                 return status;
431         }
432         *out_lease_state = state->lease_state;
433         return NT_STATUS_OK;
434 }
435
436 /*********************************************************
437  Create and send an asynchronous
438  SMB2 OPLOCK_BREAK_NOTIFICATION.
439 *********************************************************/
440
441 void send_break_message_smb2(files_struct *fsp,
442                              uint32_t break_from,
443                              uint32_t break_to)
444 {
445         NTSTATUS status;
446         struct smbXsrv_connection *xconn = NULL;
447         struct smbXsrv_session *session = NULL;
448         struct timeval tv = timeval_current();
449         NTTIME now = timeval_to_nttime(&tv);
450
451         /*
452          * TODO: in future we should have a better algorithm
453          * to find the correct connection for a break message.
454          * Then we also need some retries if a channel gets disconnected.
455          */
456         xconn = fsp->conn->sconn->client->connections;
457
458         status = smb2srv_session_lookup_conn(xconn,
459                                              fsp->vuid,
460                                              now,
461                                              &session);
462         if (NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED) ||
463             (session == NULL))
464         {
465
466                 DEBUG(10,("send_break_message_smb2: skip oplock break "
467                         "for file %s, %s, smb2 level %u session %llu not found\n",
468                         fsp_str_dbg(fsp),
469                         fsp_fnum_dbg(fsp),
470                         (unsigned int)break_to,
471                         (unsigned long long)fsp->vuid));
472                 return;
473         }
474
475         DEBUG(10,("send_break_message_smb2: sending oplock break "
476                 "for file %s, %s, smb2 level %u\n",
477                 fsp_str_dbg(fsp),
478                 fsp_fnum_dbg(fsp),
479                 (unsigned int)break_to ));
480
481         if (fsp->oplock_type == LEASE_OPLOCK) {
482                 uint32_t break_flags = 0;
483                 uint16_t new_epoch;
484
485                 if (fsp->lease->lease.lease_state != SMB2_LEASE_NONE) {
486                         break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
487                 }
488
489                 if (fsp->lease->lease.lease_version > 1) {
490                         new_epoch = fsp->lease->lease.lease_epoch;
491                 } else {
492                         new_epoch = 0;
493                 }
494
495                 status = smbd_smb2_send_lease_break(xconn, new_epoch, break_flags,
496                                                     &fsp->lease->lease.lease_key,
497                                                     break_from, break_to);
498         } else {
499                 uint8_t smb2_oplock_level;
500                 smb2_oplock_level = (break_to & SMB2_LEASE_READ) ?
501                         SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE;
502                 status = smbd_smb2_send_oplock_break(xconn,
503                                                      session,
504                                                      fsp->conn->tcon,
505                                                      fsp->op,
506                                                      smb2_oplock_level);
507         }
508         if (!NT_STATUS_IS_OK(status)) {
509                 smbd_server_connection_terminate(xconn,
510                                                  nt_errstr(status));
511                 return;
512         }
513 }