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