Plumb SMB2 stubs into all the places we defer SMB1 operations.
[ira/wip.git] / source3 / smbd / smb2_lock.c
1 /*
2    Unix SMB/CIFS implementation.
3    Core SMB2 server
4
5    Copyright (C) Stefan Metzmacher 2009
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "smbd/globals.h"
23 #include "../libcli/smb/smb_common.h"
24
25 struct smbd_smb2_lock_element {
26         uint64_t offset;
27         uint64_t length;
28         uint32_t flags;
29 };
30
31 static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
32                                                  struct tevent_context *ev,
33                                                  struct smbd_smb2_request *smb2req,
34                                                  uint32_t in_smbpid,
35                                                  uint64_t in_file_id_volatile,
36                                                  uint16_t in_lock_count,
37                                                  struct smbd_smb2_lock_element *in_locks);
38 static NTSTATUS smbd_smb2_lock_recv(struct tevent_req *req);
39
40 static void smbd_smb2_request_lock_done(struct tevent_req *subreq);
41 NTSTATUS smbd_smb2_request_process_lock(struct smbd_smb2_request *req)
42 {
43         const uint8_t *inhdr;
44         const uint8_t *inbody;
45         const int i = req->current_idx;
46         size_t expected_body_size = 0x30;
47         size_t body_size;
48         uint32_t in_smbpid;
49         uint16_t in_lock_count;
50         uint64_t in_file_id_persistent;
51         uint64_t in_file_id_volatile;
52         struct smbd_smb2_lock_element *in_locks;
53         struct tevent_req *subreq;
54         const uint8_t *lock_buffer;
55         uint16_t l;
56
57         inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
58         if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
59                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
60         }
61
62         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
63
64         body_size = SVAL(inbody, 0x00);
65         if (body_size != expected_body_size) {
66                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
67         }
68
69         in_smbpid                       = IVAL(inhdr, SMB2_HDR_PID);
70
71         in_lock_count                   = CVAL(inbody, 0x02);
72         /* 0x04 - 4 bytes reserved */
73         in_file_id_persistent           = BVAL(inbody, 0x08);
74         in_file_id_volatile             = BVAL(inbody, 0x10);
75
76         if (in_lock_count < 1) {
77                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
78         }
79
80         if (((in_lock_count - 1) * 0x18) > req->in.vector[i+2].iov_len) {
81                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
82         }
83
84         if (req->compat_chain_fsp) {
85                 /* skip check */
86         } else if (in_file_id_persistent != 0) {
87                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
88         }
89
90         in_locks = talloc_array(req, struct smbd_smb2_lock_element,
91                                 in_lock_count);
92         if (in_locks == NULL) {
93                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
94         }
95
96         l = 0;
97         lock_buffer = inbody + 0x18;
98
99         in_locks[l].offset      = BVAL(lock_buffer, 0x00);
100         in_locks[l].length      = BVAL(lock_buffer, 0x08);
101         in_locks[l].flags       = IVAL(lock_buffer, 0x10);
102         /* 0x14 - 4 reserved bytes */
103
104         lock_buffer = (const uint8_t *)req->in.vector[i+2].iov_base;
105
106         for (l=1; l < in_lock_count; l++) {
107                 in_locks[l].offset      = BVAL(lock_buffer, 0x00);
108                 in_locks[l].length      = BVAL(lock_buffer, 0x08);
109                 in_locks[l].flags       = IVAL(lock_buffer, 0x10);
110                 /* 0x14 - 4 reserved bytes */
111
112                 lock_buffer += 0x18;
113         }
114
115         subreq = smbd_smb2_lock_send(req,
116                                      req->sconn->smb2.event_ctx,
117                                      req,
118                                      in_smbpid,
119                                      in_file_id_volatile,
120                                      in_lock_count,
121                                      in_locks);
122         if (subreq == NULL) {
123                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
124         }
125         tevent_req_set_callback(subreq, smbd_smb2_request_lock_done, req);
126
127         return smbd_smb2_request_pending_queue(req, subreq);
128 }
129
130 static void smbd_smb2_request_lock_done(struct tevent_req *subreq)
131 {
132         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
133                                         struct smbd_smb2_request);
134         DATA_BLOB outbody;
135         NTSTATUS status;
136         NTSTATUS error; /* transport error */
137
138         status = smbd_smb2_lock_recv(subreq);
139         TALLOC_FREE(subreq);
140         if (!NT_STATUS_IS_OK(status)) {
141                 error = smbd_smb2_request_error(req, status);
142                 if (!NT_STATUS_IS_OK(error)) {
143                         smbd_server_connection_terminate(req->sconn,
144                                                          nt_errstr(error));
145                         return;
146                 }
147                 return;
148         }
149
150         outbody = data_blob_talloc(req->out.vector, NULL, 0x04);
151         if (outbody.data == NULL) {
152                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
153                 if (!NT_STATUS_IS_OK(error)) {
154                         smbd_server_connection_terminate(req->sconn,
155                                                          nt_errstr(error));
156                         return;
157                 }
158                 return;
159         }
160
161         SSVAL(outbody.data, 0x00, 0x04);        /* struct size */
162         SSVAL(outbody.data, 0x02, 0);           /* reserved */
163
164         error = smbd_smb2_request_done(req, outbody, NULL);
165         if (!NT_STATUS_IS_OK(error)) {
166                 smbd_server_connection_terminate(req->sconn,
167                                                  nt_errstr(error));
168                 return;
169         }
170 }
171
172 struct smbd_smb2_lock_state {
173         struct smbd_smb2_request *smb2req;
174 };
175
176 static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
177                                                  struct tevent_context *ev,
178                                                  struct smbd_smb2_request *smb2req,
179                                                  uint32_t in_smbpid,
180                                                  uint64_t in_file_id_volatile,
181                                                  uint16_t in_lock_count,
182                                                  struct smbd_smb2_lock_element *in_locks)
183 {
184         struct tevent_req *req;
185         struct smbd_smb2_lock_state *state;
186         struct smb_request *smbreq;
187         connection_struct *conn = smb2req->tcon->compat_conn;
188         files_struct *fsp;
189         int32_t timeout = -1;
190         bool isunlock = false;
191         uint16_t i;
192         struct smbd_lock_element *locks;
193         NTSTATUS status;
194         bool async = false;
195
196         req = tevent_req_create(mem_ctx, &state,
197                                 struct smbd_smb2_lock_state);
198         if (req == NULL) {
199                 return NULL;
200         }
201         state->smb2req = smb2req;
202
203         DEBUG(10,("smbd_smb2_lock_send: file_id[0x%016llX]\n",
204                   (unsigned long long)in_file_id_volatile));
205
206         smbreq = smbd_smb2_fake_smb_request(smb2req);
207         if (tevent_req_nomem(smbreq, req)) {
208                 return tevent_req_post(req, ev);
209         }
210
211         fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile);
212         if (fsp == NULL) {
213                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
214                 return tevent_req_post(req, ev);
215         }
216         if (conn != fsp->conn) {
217                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
218                 return tevent_req_post(req, ev);
219         }
220         if (smb2req->session->vuid != fsp->vuid) {
221                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
222                 return tevent_req_post(req, ev);
223         }
224
225         locks = talloc_array(state, struct smbd_lock_element, in_lock_count);
226         if (locks == NULL) {
227                 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
228                 return tevent_req_post(req, ev);
229         }
230
231         switch (in_locks[0].flags) {
232         case SMB2_LOCK_FLAG_SHARED:
233         case SMB2_LOCK_FLAG_EXCLUSIVE:
234                 if (in_lock_count > 1) {
235                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
236                         return tevent_req_post(req, ev);
237                 }
238                 timeout = -1;
239                 break;
240
241         case SMB2_LOCK_FLAG_SHARED|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
242         case SMB2_LOCK_FLAG_EXCLUSIVE|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
243                 timeout = 0;
244                 break;
245
246         case SMB2_LOCK_FLAG_UNLOCK:
247                 /* only the first lock gives the UNLOCK bit - see
248                    MS-SMB2 3.3.5.14 */
249                 isunlock = true;
250                 timeout = 0;
251                 break;
252
253         default:
254                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
255                 return tevent_req_post(req, ev);
256         }
257
258         for (i=0; i<in_lock_count; i++) {
259                 uint64_t max_count;
260                 bool invalid = false;
261
262                 switch (in_locks[i].flags) {
263                 case SMB2_LOCK_FLAG_SHARED:
264                 case SMB2_LOCK_FLAG_EXCLUSIVE:
265                         if (i > 0) {
266                                 tevent_req_nterror(req,
267                                                    NT_STATUS_INVALID_PARAMETER);
268                                 return tevent_req_post(req, ev);
269                         }
270                         if (isunlock) {
271                                 tevent_req_nterror(req,
272                                                    NT_STATUS_INVALID_PARAMETER);
273                                 return tevent_req_post(req, ev);
274                         }
275                         break;
276
277                 case SMB2_LOCK_FLAG_SHARED|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
278                 case SMB2_LOCK_FLAG_EXCLUSIVE|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
279                         if (isunlock) {
280                                 tevent_req_nterror(req,
281                                                    NT_STATUS_INVALID_PARAMETER);
282                                 return tevent_req_post(req, ev);
283                         }
284                         break;
285
286                 case SMB2_LOCK_FLAG_UNLOCK:
287                         if (!isunlock) {
288                                 tevent_req_nterror(req,
289                                                    NT_STATUS_INVALID_PARAMETER);
290                                 return tevent_req_post(req, ev);
291                         }
292                         break;
293
294                 default:
295                         if (isunlock) {
296                                 /*
297                                  * is the first element was a UNLOCK
298                                  * we need to deferr the error response
299                                  * to the backend, because we need to process
300                                  * all unlock elements before
301                                  */
302                                 invalid = true;
303                                 break;
304                         }
305                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
306                         return tevent_req_post(req, ev);
307                 }
308
309                 locks[i].smbpid = in_smbpid;
310                 locks[i].offset = in_locks[i].offset;
311                 locks[i].count  = in_locks[i].length;
312
313                 if (in_locks[i].flags & SMB2_LOCK_FLAG_EXCLUSIVE) {
314                         locks[i].brltype = WRITE_LOCK;
315                 } else if (in_locks[i].flags & SMB2_LOCK_FLAG_SHARED) {
316                         locks[i].brltype = READ_LOCK;
317                 } else if (invalid) {
318                         /*
319                          * this is an invalid UNLOCK element
320                          * and the backend needs to test for
321                          * brltype != UNLOCK_LOCK and return
322                          * NT_STATUS_INVALID_PARAMER
323                          */
324                         locks[i].brltype = READ_LOCK;
325                 } else {
326                         locks[i].brltype = UNLOCK_LOCK;
327                 }
328
329                 max_count = UINT64_MAX - locks[i].offset;
330                 if (locks[i].count > max_count) {
331                         tevent_req_nterror(req, NT_STATUS_INVALID_LOCK_RANGE);
332                         return tevent_req_post(req, ev);
333                 }
334         }
335
336         if (isunlock) {
337                 status = smbd_do_locking(smbreq, fsp,
338                                          0,
339                                          timeout,
340                                          in_lock_count,
341                                          locks,
342                                          0,
343                                          NULL,
344                                          &async);
345         } else {
346                 status = smbd_do_locking(smbreq, fsp,
347                                          0,
348                                          timeout,
349                                          0,
350                                          NULL,
351                                          in_lock_count,
352                                          locks,
353                                          &async);
354         }
355         if (!NT_STATUS_IS_OK(status)) {
356                 if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
357                        status = NT_STATUS_LOCK_NOT_GRANTED;
358                 }
359                 tevent_req_nterror(req, status);
360                 return tevent_req_post(req, ev);
361         }
362
363         if (async) {
364                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
365                 return tevent_req_post(req, ev);
366         }
367
368         tevent_req_done(req);
369         return tevent_req_post(req, ev);
370 }
371
372 static NTSTATUS smbd_smb2_lock_recv(struct tevent_req *req)
373 {
374         NTSTATUS status;
375
376         if (tevent_req_is_nterror(req, &status)) {
377                 tevent_req_received(req);
378                 return status;
379         }
380
381         tevent_req_received(req);
382         return NT_STATUS_OK;
383 }
384
385 /*
386  * Dummy (for now) function to cope with SMB2 blocking lock
387  * requests.
388  */
389
390 bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
391                                 struct smb_request *req,
392                                 files_struct *fsp,
393                                 int lock_timeout,
394                                 int lock_num,
395                                 uint32_t lock_pid,
396                                 enum brl_type lock_type,
397                                 enum brl_flavour lock_flav,
398                                 uint64_t offset,
399                                 uint64_t count,
400                                 uint32_t blocking_pid)
401 {
402         return false;
403 }