s3:smbd: convert '\\' into '/' in SMB2 Create
[ira/wip.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 "../source4/libcli/smb2/smb2_constants.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         if (tevent_req_is_in_progress(subreq)) {
130                 return smbd_smb2_request_pending_queue(req);
131         }
132
133         return NT_STATUS_OK;
134 }
135
136 static void smbd_smb2_request_create_done(struct tevent_req *subreq)
137 {
138         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
139                                         struct smbd_smb2_request);
140         int i = req->current_idx;
141         uint8_t *outhdr;
142         DATA_BLOB outbody;
143         DATA_BLOB outdyn;
144         uint8_t out_oplock_level = 0;
145         uint32_t out_create_action = 0;
146         NTTIME out_creation_time = 0;
147         NTTIME out_last_access_time = 0;
148         NTTIME out_last_write_time = 0;
149         NTTIME out_change_time = 0;
150         uint64_t out_allocation_size = 0;
151         uint64_t out_end_of_file = 0;
152         uint32_t out_file_attributes = 0;
153         uint64_t out_file_id_volatile = 0;
154         NTSTATUS status;
155         NTSTATUS error; /* transport error */
156
157         status = smbd_smb2_create_recv(subreq,
158                                        &out_oplock_level,
159                                        &out_create_action,
160                                        &out_creation_time,
161                                        &out_last_access_time,
162                                        &out_last_write_time,
163                                        &out_change_time,
164                                        &out_allocation_size,
165                                        &out_end_of_file,
166                                        &out_file_attributes,
167                                        &out_file_id_volatile);
168         TALLOC_FREE(subreq);
169         if (!NT_STATUS_IS_OK(status)) {
170                 error = smbd_smb2_request_error(req, status);
171                 if (!NT_STATUS_IS_OK(error)) {
172                         smbd_server_connection_terminate(req->sconn,
173                                                          nt_errstr(error));
174                         return;
175                 }
176                 return;
177         }
178
179         outhdr = (uint8_t *)req->out.vector[i].iov_base;
180
181         outbody = data_blob_talloc(req->out.vector, NULL, 0x58);
182         if (outbody.data == NULL) {
183                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
184                 if (!NT_STATUS_IS_OK(error)) {
185                         smbd_server_connection_terminate(req->sconn,
186                                                          nt_errstr(error));
187                         return;
188                 }
189                 return;
190         }
191
192         SSVAL(outbody.data, 0x00, 0x58 + 1);    /* struct size */
193         SCVAL(outbody.data, 0x02,
194               out_oplock_level);                /* oplock level */
195         SCVAL(outbody.data, 0x03, 0);           /* reserved */
196         SIVAL(outbody.data, 0x04,
197               out_create_action);               /* create action */
198         SBVAL(outbody.data, 0x08,
199               out_creation_time);               /* creation time */
200         SBVAL(outbody.data, 0x10,
201               out_last_access_time);            /* last access time */
202         SBVAL(outbody.data, 0x18,
203               out_last_write_time);             /* last write time */
204         SBVAL(outbody.data, 0x20,
205               out_change_time);                 /* change time */
206         SBVAL(outbody.data, 0x28,
207               out_allocation_size);             /* allocation size */
208         SBVAL(outbody.data, 0x30,
209               out_end_of_file);                 /* end of file */
210         SIVAL(outbody.data, 0x38,
211               out_file_attributes);             /* file attributes */
212         SIVAL(outbody.data, 0x3C, 0);           /* reserved */
213         SBVAL(outbody.data, 0x40, 0);           /* file id (persistent) */
214         SBVAL(outbody.data, 0x48,
215               out_file_id_volatile);            /* file id (volatile) */
216         SIVAL(outbody.data, 0x50, 0);           /* create contexts offset */
217         SIVAL(outbody.data, 0x54, 0);           /* create contexts length */
218
219         outdyn = data_blob_const(NULL, 0);
220
221         error = smbd_smb2_request_done(req, outbody, &outdyn);
222         if (!NT_STATUS_IS_OK(error)) {
223                 smbd_server_connection_terminate(req->sconn,
224                                                  nt_errstr(error));
225                 return;
226         }
227 }
228
229 struct smbd_smb2_create_state {
230         struct smbd_smb2_request *smb2req;
231         uint8_t out_oplock_level;
232         uint32_t out_create_action;
233         NTTIME out_creation_time;
234         NTTIME out_last_access_time;
235         NTTIME out_last_write_time;
236         NTTIME out_change_time;
237         uint64_t out_allocation_size;
238         uint64_t out_end_of_file;
239         uint32_t out_file_attributes;
240         uint64_t out_file_id_volatile;
241 };
242
243 static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
244                                                 struct tevent_context *ev,
245                                                 struct smbd_smb2_request *smb2req,
246                                                 uint8_t in_oplock_level,
247                                                 uint32_t in_impersonation_level,
248                                                 uint32_t in_desired_access,
249                                                 uint32_t in_file_attributes,
250                                                 uint32_t in_share_access,
251                                                 uint32_t in_create_disposition,
252                                                 uint32_t in_create_options,
253                                                 const char *in_name)
254 {
255         struct tevent_req *req;
256         struct smbd_smb2_create_state *state;
257         NTSTATUS status;
258         struct smb_request *smbreq;
259         files_struct *result;
260         int info;
261         SMB_STRUCT_STAT sbuf;
262
263         req = tevent_req_create(mem_ctx, &state,
264                                 struct smbd_smb2_create_state);
265         if (req == NULL) {
266                 return NULL;
267         }
268         state->smb2req = smb2req;
269
270         DEBUG(10,("smbd_smb2_create: name[%s]\n",
271                   in_name));
272
273         smbreq = smbd_smb2_fake_smb_request(smb2req);
274         if (tevent_req_nomem(smbreq, req)) {
275                 goto out;
276         }
277
278         if (IS_IPC(smbreq->conn)) {
279                 const char *pipe_name = in_name;
280
281                 if (!lp_nt_pipe_support()) {
282                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
283                         goto out;
284                 }
285
286                 /* Strip \\ off the name. */
287                 if (pipe_name[0] == '\\') {
288                         pipe_name++;
289                 }
290
291                 status = open_np_file(smbreq, pipe_name, &result);
292                 if (!NT_STATUS_IS_OK(status)) {
293                         tevent_req_nterror(req, status);
294                         goto out;
295                 }
296                 info = FILE_WAS_OPENED;
297                 ZERO_STRUCT(sbuf);
298         } else if (CAN_PRINT(smbreq->conn)) {
299                 status = file_new(smbreq, smbreq->conn, &result);
300                 if(!NT_STATUS_IS_OK(status)) {
301                         tevent_req_nterror(req, status);
302                         goto out;
303                 }
304
305                 status = print_fsp_open(smbreq,
306                                         smbreq->conn,
307                                         in_name,
308                                         smbreq->vuid,
309                                         result,
310                                         &sbuf);
311                 if (!NT_STATUS_IS_OK(status)) {
312                         file_free(smbreq, result);
313                         tevent_req_nterror(req, status);
314                         goto out;
315                 }
316                 info = FILE_WAS_CREATED;
317         } else {
318                 struct smb_filename *smb_fname = NULL;
319
320                 /* these are ignored for SMB2 */
321                 in_create_options &= ~(0x10);/* NTCREATEX_OPTIONS_SYNC_ALERT */
322                 in_create_options &= ~(0x20);/* NTCREATEX_OPTIONS_ASYNC_ALERT */
323
324                 /* convert '\\' into '/' */
325                 status = check_path_syntax(in_name);
326                 if (!NT_STATUS_IS_OK(status)) {
327                         tevent_req_nterror(req, status);
328                         TALLOC_FREE(smb_fname);
329                         goto out;
330                 }
331
332                 status = filename_convert(talloc_tos(),
333                                         smbreq->conn,
334                                         smbreq->flags2 & FLAGS2_DFS_PATHNAMES,
335                                         in_name,
336                                         0,
337                                         NULL,
338                                         &smb_fname);
339                 if (!NT_STATUS_IS_OK(status)) {
340                         tevent_req_nterror(req, status);
341                         TALLOC_FREE(smb_fname);
342                         goto out;
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                         TALLOC_FREE(smb_fname);
363                         goto out;
364                 }
365                 sbuf = smb_fname->st;
366                 TALLOC_FREE(smb_fname);
367         }
368
369         smb2req->compat_chain_fsp = smbreq->chain_fsp;
370
371         state->out_oplock_level = 0;
372         if ((in_create_disposition == FILE_SUPERSEDE)
373             && (info == FILE_WAS_OVERWRITTEN)) {
374                 state->out_create_action = FILE_WAS_SUPERSEDED;
375         } else {
376                 state->out_create_action = info;
377         }
378         unix_timespec_to_nt_time(&state->out_creation_time, sbuf.st_ex_btime);
379         unix_timespec_to_nt_time(&state->out_last_access_time, sbuf.st_ex_atime);
380         unix_timespec_to_nt_time(&state->out_last_write_time,sbuf.st_ex_mtime);
381         unix_timespec_to_nt_time(&state->out_change_time, sbuf.st_ex_ctime);
382         state->out_allocation_size      = sbuf.st_ex_blksize * sbuf.st_ex_blocks;
383         state->out_end_of_file          = sbuf.st_ex_size;
384         state->out_file_attributes      = dos_mode(result->conn,
385                                                    result->fsp_name);
386         if (state->out_file_attributes == 0) {
387                 state->out_file_attributes = FILE_ATTRIBUTE_NORMAL;
388         }
389         state->out_file_id_volatile = result->fnum;
390
391         tevent_req_done(req);
392  out:
393         return tevent_req_post(req, ev);
394 }
395
396 static NTSTATUS smbd_smb2_create_recv(struct tevent_req *req,
397                                       uint8_t *out_oplock_level,
398                                       uint32_t *out_create_action,
399                                       NTTIME *out_creation_time,
400                                       NTTIME *out_last_access_time,
401                                       NTTIME *out_last_write_time,
402                                       NTTIME *out_change_time,
403                                       uint64_t *out_allocation_size,
404                                       uint64_t *out_end_of_file,
405                                       uint32_t *out_file_attributes,
406                                       uint64_t *out_file_id_volatile)
407 {
408         NTSTATUS status;
409         struct smbd_smb2_create_state *state = tevent_req_data(req,
410                                                struct smbd_smb2_create_state);
411
412         if (tevent_req_is_nterror(req, &status)) {
413                 tevent_req_received(req);
414                 return status;
415         }
416
417         *out_oplock_level       = state->out_oplock_level;
418         *out_create_action      = state->out_create_action;
419         *out_creation_time      = state->out_creation_time;
420         *out_last_access_time   = state->out_last_access_time;
421         *out_last_write_time    = state->out_last_write_time;
422         *out_change_time        = state->out_change_time;
423         *out_allocation_size    = state->out_allocation_size;
424         *out_end_of_file        = state->out_end_of_file;
425         *out_file_attributes    = state->out_file_attributes;
426         *out_file_id_volatile   = state->out_file_id_volatile;
427
428         tevent_req_received(req);
429         return NT_STATUS_OK;
430 }