s3:smbd: implement SMB2 Cancel correctly.
[samba.git] / source3 / smbd / smb2_create.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 static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
26                                                 struct tevent_context *ev,
27                                                 struct smbd_smb2_request *smb2req,
28                                                 uint8_t in_oplock_level,
29                                                 uint32_t in_impersonation_level,
30                                                 uint32_t in_desired_access,
31                                                 uint32_t in_file_attributes,
32                                                 uint32_t in_share_access,
33                                                 uint32_t in_create_disposition,
34                                                 uint32_t in_create_options,
35                                                 const char *in_name);
36 static NTSTATUS smbd_smb2_create_recv(struct tevent_req *req,
37                                       uint8_t *out_oplock_level,
38                                       uint32_t *out_create_action,
39                                       NTTIME *out_creation_time,
40                                       NTTIME *out_last_access_time,
41                                       NTTIME *out_last_write_time,
42                                       NTTIME *out_change_time,
43                                       uint64_t *out_allocation_size,
44                                       uint64_t *out_end_of_file,
45                                       uint32_t *out_file_attributes,
46                                       uint64_t *out_file_id_volatile);
47
48 static void smbd_smb2_request_create_done(struct tevent_req *subreq);
49 NTSTATUS smbd_smb2_request_process_create(struct smbd_smb2_request *req)
50 {
51         const uint8_t *inbody;
52         int i = req->current_idx;
53         size_t expected_body_size = 0x39;
54         size_t body_size;
55         uint8_t in_oplock_level;
56         uint32_t in_impersonation_level;
57         uint32_t in_desired_access;
58         uint32_t in_file_attributes;
59         uint32_t in_share_access;
60         uint32_t in_create_disposition;
61         uint32_t in_create_options;
62         uint16_t in_name_offset;
63         uint16_t in_name_length;
64         DATA_BLOB in_name_buffer;
65         char *in_name_string;
66         size_t in_name_string_size;
67         bool ok;
68         struct tevent_req *subreq;
69
70         if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
71                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
72         }
73
74         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
75
76         body_size = SVAL(inbody, 0x00);
77         if (body_size != expected_body_size) {
78                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
79         }
80
81         in_oplock_level         = CVAL(inbody, 0x03);
82         in_impersonation_level  = IVAL(inbody, 0x04);
83         in_desired_access       = IVAL(inbody, 0x18);
84         in_file_attributes      = IVAL(inbody, 0x1C);
85         in_share_access         = IVAL(inbody, 0x20);
86         in_create_disposition   = IVAL(inbody, 0x24);
87         in_create_options       = IVAL(inbody, 0x28);
88         in_name_offset          = SVAL(inbody, 0x2C);
89         in_name_length          = SVAL(inbody, 0x2E);
90
91         if (in_name_offset == 0 && in_name_length == 0) {
92                 /* This is ok */
93         } else if (in_name_offset != (SMB2_HDR_BODY + (body_size & 0xFFFFFFFE))) {
94                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
95         }
96
97         if (in_name_length > req->in.vector[i+2].iov_len) {
98                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
99         }
100
101         in_name_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base;
102         in_name_buffer.length = in_name_length;
103
104         ok = convert_string_talloc(req, CH_UTF16, CH_UNIX,
105                                    in_name_buffer.data,
106                                    in_name_buffer.length,
107                                    &in_name_string,
108                                    &in_name_string_size, false);
109         if (!ok) {
110                 return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
111         }
112
113         subreq = smbd_smb2_create_send(req,
114                                        req->sconn->smb2.event_ctx,
115                                        req,
116                                        in_oplock_level,
117                                        in_impersonation_level,
118                                        in_desired_access,
119                                        in_file_attributes,
120                                        in_share_access,
121                                        in_create_disposition,
122                                        in_create_options,
123                                        in_name_string);
124         if (subreq == NULL) {
125                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
126         }
127         tevent_req_set_callback(subreq, smbd_smb2_request_create_done, req);
128
129         return smbd_smb2_request_pending_queue(req, subreq);
130 }
131
132 static void smbd_smb2_request_create_done(struct tevent_req *subreq)
133 {
134         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
135                                         struct smbd_smb2_request);
136         int i = req->current_idx;
137         uint8_t *outhdr;
138         DATA_BLOB outbody;
139         DATA_BLOB outdyn;
140         uint8_t out_oplock_level = 0;
141         uint32_t out_create_action = 0;
142         NTTIME out_creation_time = 0;
143         NTTIME out_last_access_time = 0;
144         NTTIME out_last_write_time = 0;
145         NTTIME out_change_time = 0;
146         uint64_t out_allocation_size = 0;
147         uint64_t out_end_of_file = 0;
148         uint32_t out_file_attributes = 0;
149         uint64_t out_file_id_volatile = 0;
150         NTSTATUS status;
151         NTSTATUS error; /* transport error */
152
153         status = smbd_smb2_create_recv(subreq,
154                                        &out_oplock_level,
155                                        &out_create_action,
156                                        &out_creation_time,
157                                        &out_last_access_time,
158                                        &out_last_write_time,
159                                        &out_change_time,
160                                        &out_allocation_size,
161                                        &out_end_of_file,
162                                        &out_file_attributes,
163                                        &out_file_id_volatile);
164         TALLOC_FREE(subreq);
165         if (!NT_STATUS_IS_OK(status)) {
166                 error = smbd_smb2_request_error(req, status);
167                 if (!NT_STATUS_IS_OK(error)) {
168                         smbd_server_connection_terminate(req->sconn,
169                                                          nt_errstr(error));
170                         return;
171                 }
172                 return;
173         }
174
175         outhdr = (uint8_t *)req->out.vector[i].iov_base;
176
177         outbody = data_blob_talloc(req->out.vector, NULL, 0x58);
178         if (outbody.data == NULL) {
179                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
180                 if (!NT_STATUS_IS_OK(error)) {
181                         smbd_server_connection_terminate(req->sconn,
182                                                          nt_errstr(error));
183                         return;
184                 }
185                 return;
186         }
187
188         SSVAL(outbody.data, 0x00, 0x58 + 1);    /* struct size */
189         SCVAL(outbody.data, 0x02,
190               out_oplock_level);                /* oplock level */
191         SCVAL(outbody.data, 0x03, 0);           /* reserved */
192         SIVAL(outbody.data, 0x04,
193               out_create_action);               /* create action */
194         SBVAL(outbody.data, 0x08,
195               out_creation_time);               /* creation time */
196         SBVAL(outbody.data, 0x10,
197               out_last_access_time);            /* last access time */
198         SBVAL(outbody.data, 0x18,
199               out_last_write_time);             /* last write time */
200         SBVAL(outbody.data, 0x20,
201               out_change_time);                 /* change time */
202         SBVAL(outbody.data, 0x28,
203               out_allocation_size);             /* allocation size */
204         SBVAL(outbody.data, 0x30,
205               out_end_of_file);                 /* end of file */
206         SIVAL(outbody.data, 0x38,
207               out_file_attributes);             /* file attributes */
208         SIVAL(outbody.data, 0x3C, 0);           /* reserved */
209         SBVAL(outbody.data, 0x40, 0);           /* file id (persistent) */
210         SBVAL(outbody.data, 0x48,
211               out_file_id_volatile);            /* file id (volatile) */
212         SIVAL(outbody.data, 0x50, 0);           /* create contexts offset */
213         SIVAL(outbody.data, 0x54, 0);           /* create contexts length */
214
215         outdyn = data_blob_const(NULL, 0);
216
217         error = smbd_smb2_request_done(req, outbody, &outdyn);
218         if (!NT_STATUS_IS_OK(error)) {
219                 smbd_server_connection_terminate(req->sconn,
220                                                  nt_errstr(error));
221                 return;
222         }
223 }
224
225 struct smbd_smb2_create_state {
226         struct smbd_smb2_request *smb2req;
227         uint8_t out_oplock_level;
228         uint32_t out_create_action;
229         NTTIME out_creation_time;
230         NTTIME out_last_access_time;
231         NTTIME out_last_write_time;
232         NTTIME out_change_time;
233         uint64_t out_allocation_size;
234         uint64_t out_end_of_file;
235         uint32_t out_file_attributes;
236         uint64_t out_file_id_volatile;
237 };
238
239 static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
240                                                 struct tevent_context *ev,
241                                                 struct smbd_smb2_request *smb2req,
242                                                 uint8_t in_oplock_level,
243                                                 uint32_t in_impersonation_level,
244                                                 uint32_t in_desired_access,
245                                                 uint32_t in_file_attributes,
246                                                 uint32_t in_share_access,
247                                                 uint32_t in_create_disposition,
248                                                 uint32_t in_create_options,
249                                                 const char *in_name)
250 {
251         struct tevent_req *req;
252         struct smbd_smb2_create_state *state;
253         NTSTATUS status;
254         struct smb_request *smbreq;
255         files_struct *result;
256         int info;
257         SMB_STRUCT_STAT sbuf;
258
259         req = tevent_req_create(mem_ctx, &state,
260                                 struct smbd_smb2_create_state);
261         if (req == NULL) {
262                 return NULL;
263         }
264         state->smb2req = smb2req;
265
266         DEBUG(10,("smbd_smb2_create: name[%s]\n",
267                   in_name));
268
269         smbreq = smbd_smb2_fake_smb_request(smb2req);
270         if (tevent_req_nomem(smbreq, req)) {
271                 return tevent_req_post(req, ev);
272         }
273
274         if (IS_IPC(smbreq->conn)) {
275                 const char *pipe_name = in_name;
276
277                 if (!lp_nt_pipe_support()) {
278                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
279                         return tevent_req_post(req, ev);
280                 }
281
282                 /* Strip \\ off the name. */
283                 if (pipe_name[0] == '\\') {
284                         pipe_name++;
285                 }
286
287                 status = open_np_file(smbreq, pipe_name, &result);
288                 if (!NT_STATUS_IS_OK(status)) {
289                         tevent_req_nterror(req, status);
290                         return tevent_req_post(req, ev);
291                 }
292                 info = FILE_WAS_OPENED;
293                 ZERO_STRUCT(sbuf);
294         } else if (CAN_PRINT(smbreq->conn)) {
295                 status = file_new(smbreq, smbreq->conn, &result);
296                 if(!NT_STATUS_IS_OK(status)) {
297                         tevent_req_nterror(req, status);
298                         return tevent_req_post(req, ev);
299                 }
300
301                 status = print_fsp_open(smbreq,
302                                         smbreq->conn,
303                                         in_name,
304                                         smbreq->vuid,
305                                         result,
306                                         &sbuf);
307                 if (!NT_STATUS_IS_OK(status)) {
308                         file_free(smbreq, result);
309                         tevent_req_nterror(req, status);
310                         return tevent_req_post(req, ev);
311                 }
312                 info = FILE_WAS_CREATED;
313         } else {
314                 char *fname;
315                 struct smb_filename *smb_fname = NULL;
316
317                 /* these are ignored for SMB2 */
318                 in_create_options &= ~(0x10);/* NTCREATEX_OPTIONS_SYNC_ALERT */
319                 in_create_options &= ~(0x20);/* NTCREATEX_OPTIONS_ASYNC_ALERT */
320
321                 fname = talloc_strdup(state, in_name);
322                 if (tevent_req_nomem(fname, req)) {
323                         return tevent_req_post(req, ev);
324                 }
325
326                 /* convert '\\' into '/' */
327                 status = check_path_syntax(fname);
328                 if (!NT_STATUS_IS_OK(status)) {
329                         tevent_req_nterror(req, status);
330                         return tevent_req_post(req, ev);
331                 }
332
333                 status = filename_convert(req,
334                                           smbreq->conn,
335                                           smbreq->flags2 & FLAGS2_DFS_PATHNAMES,
336                                           fname,
337                                           0,
338                                           NULL,
339                                           &smb_fname);
340                 if (!NT_STATUS_IS_OK(status)) {
341                         tevent_req_nterror(req, status);
342                         return tevent_req_post(req, ev);
343                 }
344
345                 status = SMB_VFS_CREATE_FILE(smbreq->conn,
346                                              smbreq,
347                                              0, /* root_dir_fid */
348                                              smb_fname,
349                                              in_desired_access,
350                                              in_share_access,
351                                              in_create_disposition,
352                                              in_create_options,
353                                              in_file_attributes,
354                                              0, /* oplock_request */
355                                              0, /* allocation_size */
356                                              NULL, /* security_descriptor */
357                                              NULL, /* ea_list */
358                                              &result,
359                                              &info);
360                 if (!NT_STATUS_IS_OK(status)) {
361                         tevent_req_nterror(req, status);
362                         return tevent_req_post(req, ev);
363                 }
364                 sbuf = smb_fname->st;
365         }
366
367         smb2req->compat_chain_fsp = smbreq->chain_fsp;
368
369         state->out_oplock_level = 0;
370         if ((in_create_disposition == FILE_SUPERSEDE)
371             && (info == FILE_WAS_OVERWRITTEN)) {
372                 state->out_create_action = FILE_WAS_SUPERSEDED;
373         } else {
374                 state->out_create_action = info;
375         }
376         unix_timespec_to_nt_time(&state->out_creation_time, sbuf.st_ex_btime);
377         unix_timespec_to_nt_time(&state->out_last_access_time, sbuf.st_ex_atime);
378         unix_timespec_to_nt_time(&state->out_last_write_time,sbuf.st_ex_mtime);
379         unix_timespec_to_nt_time(&state->out_change_time, sbuf.st_ex_ctime);
380         state->out_allocation_size      = sbuf.st_ex_blksize * sbuf.st_ex_blocks;
381         state->out_end_of_file          = sbuf.st_ex_size;
382         state->out_file_attributes      = dos_mode(result->conn,
383                                                    result->fsp_name);
384         if (state->out_file_attributes == 0) {
385                 state->out_file_attributes = FILE_ATTRIBUTE_NORMAL;
386         }
387         state->out_file_id_volatile = result->fnum;
388
389         tevent_req_done(req);
390         return tevent_req_post(req, ev);
391 }
392
393 static NTSTATUS smbd_smb2_create_recv(struct tevent_req *req,
394                                       uint8_t *out_oplock_level,
395                                       uint32_t *out_create_action,
396                                       NTTIME *out_creation_time,
397                                       NTTIME *out_last_access_time,
398                                       NTTIME *out_last_write_time,
399                                       NTTIME *out_change_time,
400                                       uint64_t *out_allocation_size,
401                                       uint64_t *out_end_of_file,
402                                       uint32_t *out_file_attributes,
403                                       uint64_t *out_file_id_volatile)
404 {
405         NTSTATUS status;
406         struct smbd_smb2_create_state *state = tevent_req_data(req,
407                                                struct smbd_smb2_create_state);
408
409         if (tevent_req_is_nterror(req, &status)) {
410                 tevent_req_received(req);
411                 return status;
412         }
413
414         *out_oplock_level       = state->out_oplock_level;
415         *out_create_action      = state->out_create_action;
416         *out_creation_time      = state->out_creation_time;
417         *out_last_access_time   = state->out_last_access_time;
418         *out_last_write_time    = state->out_last_write_time;
419         *out_change_time        = state->out_change_time;
420         *out_allocation_size    = state->out_allocation_size;
421         *out_end_of_file        = state->out_end_of_file;
422         *out_file_attributes    = state->out_file_attributes;
423         *out_file_id_volatile   = state->out_file_id_volatile;
424
425         tevent_req_received(req);
426         return NT_STATUS_OK;
427 }