notifyd: Use messaging_register for MSG_SMB_NOTIFY_GET_DB
[vlendec/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 static NTSTATUS smbd_smb2_request_process_lease_break(
30         struct smbd_smb2_request *req);
31
32 static 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);
37 static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
38                                             uint8_t *out_oplock_level);
39
40 static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq);
41 NTSTATUS 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
96 static 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
154 struct smbd_smb2_oplock_break_state {
155         struct smbd_smb2_request *smb2req;
156         uint8_t out_oplock_level; /* SMB2 oplock level. */
157 };
158
159 static 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
216 static 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
235 static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq);
236
237 static 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);
241 static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
242                                            uint32_t *out_lease_state);
243
244
245 static 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
275 static 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
330 struct smbd_smb2_lease_break_state {
331         uint32_t lease_state;
332 };
333
334 struct 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
342 static 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
358 static 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
417 static 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
436 void 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 }