r9413: Bring Samba4 back up to date with lorikeet-heimdal.
[Samba/aatanasov.git] / source / smb_server / nttrans.c
blob215b378283c6844887d919bc12375ab73f237a28
1 /*
2 Unix SMB/CIFS implementation.
3 NT transaction handling
4 Copyright (C) Andrew Tridgell 2003
5 Copyright (C) James J Myers 2003 <myersjj@samba.org>
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 2 of the License, or
10 (at your option) any later version.
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.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 This file handles the parsing of transact2 requests
25 #include "includes.h"
26 #include "smb_server/smb_server.h"
27 #include "librpc/gen_ndr/ndr_security.h"
31 #define CHECK_MIN_BLOB_SIZE(blob, size) do { \
32 if ((blob)->length < (size)) { \
33 return NT_STATUS_INFO_LENGTH_MISMATCH; \
34 }} while (0)
37 /* setup a nttrans reply, given the data and params sizes */
38 static void nttrans_setup_reply(struct smbsrv_request *req,
39 struct smb_nttrans *trans,
40 uint16_t param_size, uint16_t data_size,
41 uint16_t setup_count)
43 trans->out.setup_count = setup_count;
44 if (setup_count != 0) {
45 trans->out.setup = talloc_zero_array(req, uint16_t, setup_count);
47 trans->out.params = data_blob_talloc(req, NULL, param_size);
48 trans->out.data = data_blob_talloc(req, NULL, data_size);
52 /*
53 parse NTTRANS_CREATE request
55 static NTSTATUS nttrans_create(struct smbsrv_request *req,
56 struct smb_nttrans *trans)
58 union smb_open *io;
59 uint16_t fname_len;
60 uint32_t sd_length, ea_length;
61 NTSTATUS status;
62 uint8_t *params;
64 if (trans->in.params.length < 54) {
65 return NT_STATUS_INVALID_PARAMETER;
68 /* parse the request */
69 io = talloc(req, union smb_open);
70 if (io == NULL) {
71 return NT_STATUS_NO_MEMORY;
74 io->ntcreatex.level = RAW_OPEN_NTTRANS_CREATE;
76 params = trans->in.params.data;
78 io->ntcreatex.in.flags = IVAL(params, 0);
79 io->ntcreatex.in.root_fid = IVAL(params, 4);
80 io->ntcreatex.in.access_mask = IVAL(params, 8);
81 io->ntcreatex.in.alloc_size = BVAL(params, 12);
82 io->ntcreatex.in.file_attr = IVAL(params, 20);
83 io->ntcreatex.in.share_access = IVAL(params, 24);
84 io->ntcreatex.in.open_disposition = IVAL(params, 28);
85 io->ntcreatex.in.create_options = IVAL(params, 32);
86 sd_length = IVAL(params, 36);
87 ea_length = IVAL(params, 40);
88 fname_len = IVAL(params, 44);
89 io->ntcreatex.in.impersonation = IVAL(params, 48);
90 io->ntcreatex.in.security_flags = CVAL(params, 52);
91 io->ntcreatex.in.sec_desc = NULL;
92 io->ntcreatex.in.ea_list = NULL;
94 req_pull_string(req, &io->ntcreatex.in.fname,
95 params + 54,
96 trans->in.params.length - 54,
97 STR_NO_RANGE_CHECK | STR_TERMINATE);
98 if (!io->ntcreatex.in.fname) {
99 return NT_STATUS_INVALID_PARAMETER;
102 if (sd_length > trans->in.data.length ||
103 ea_length > trans->in.data.length ||
104 (sd_length+ea_length) > trans->in.data.length) {
105 return NT_STATUS_INVALID_PARAMETER;
108 /* this call has an optional security descriptor */
109 if (sd_length != 0) {
110 DATA_BLOB blob;
111 blob.data = trans->in.data.data;
112 blob.length = sd_length;
113 io->ntcreatex.in.sec_desc = talloc(io, struct security_descriptor);
114 if (io->ntcreatex.in.sec_desc == NULL) {
115 return NT_STATUS_NO_MEMORY;
117 status = ndr_pull_struct_blob(&blob, io,
118 io->ntcreatex.in.sec_desc,
119 (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
120 if (!NT_STATUS_IS_OK(status)) {
121 return status;
125 /* and an optional ea_list */
126 if (ea_length > 4) {
127 DATA_BLOB blob;
128 blob.data = trans->in.data.data + sd_length;
129 blob.length = ea_length;
130 io->ntcreatex.in.ea_list = talloc(io, struct smb_ea_list);
131 if (io->ntcreatex.in.ea_list == NULL) {
132 return NT_STATUS_NO_MEMORY;
135 status = ea_pull_list_chained(&blob, io,
136 &io->ntcreatex.in.ea_list->num_eas,
137 &io->ntcreatex.in.ea_list->eas);
138 if (!NT_STATUS_IS_OK(status)) {
139 return status;
143 /* call the backend - notice that we do it sync for now, until we support
144 async nttrans requests */
145 status = ntvfs_openfile(req, io);
146 if (!NT_STATUS_IS_OK(status)) {
147 return status;
150 trans->out.setup_count = 0;
151 trans->out.setup = NULL;
152 trans->out.params = data_blob_talloc(req, NULL, 69);
153 trans->out.data = data_blob(NULL, 0);
155 params = trans->out.params.data;
156 if (params == NULL) {
157 return NT_STATUS_NO_MEMORY;
160 SSVAL(params, 0, io->ntcreatex.out.oplock_level);
161 SSVAL(params, 2, io->ntcreatex.out.fnum);
162 SIVAL(params, 4, io->ntcreatex.out.create_action);
163 SIVAL(params, 8, 0); /* ea error offset */
164 push_nttime(params, 12, io->ntcreatex.out.create_time);
165 push_nttime(params, 20, io->ntcreatex.out.access_time);
166 push_nttime(params, 28, io->ntcreatex.out.write_time);
167 push_nttime(params, 36, io->ntcreatex.out.change_time);
168 SIVAL(params, 44, io->ntcreatex.out.attrib);
169 SBVAL(params, 48, io->ntcreatex.out.alloc_size);
170 SBVAL(params, 56, io->ntcreatex.out.size);
171 SSVAL(params, 64, io->ntcreatex.out.file_type);
172 SSVAL(params, 66, io->ntcreatex.out.ipc_state);
173 SCVAL(params, 68, io->ntcreatex.out.is_directory);
175 return NT_STATUS_OK;
180 parse NTTRANS_QUERY_SEC_DESC request
182 static NTSTATUS nttrans_query_sec_desc(struct smbsrv_request *req,
183 struct smb_nttrans *trans)
185 union smb_fileinfo *io;
186 NTSTATUS status;
188 if (trans->in.params.length < 8) {
189 return NT_STATUS_INVALID_PARAMETER;
192 /* parse the request */
193 io = talloc(req, union smb_fileinfo);
194 if (io == NULL) {
195 return NT_STATUS_NO_MEMORY;
198 io->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
199 io->query_secdesc.in.fnum = SVAL(trans->in.params.data, 0);
200 io->query_secdesc.secinfo_flags = IVAL(trans->in.params.data, 4);
202 /* call the backend - notice that we do it sync for now, until we support
203 async nttrans requests */
204 status = ntvfs_qfileinfo(req, io);
205 if (!NT_STATUS_IS_OK(status)) {
206 return status;
209 trans->out.setup_count = 0;
210 trans->out.setup = NULL;
211 trans->out.params = data_blob_talloc(req, NULL, 4);
212 trans->out.data = data_blob(NULL, 0);
214 status = ndr_push_struct_blob(&trans->out.data, req,
215 io->query_secdesc.out.sd,
216 (ndr_push_flags_fn_t)ndr_push_security_descriptor);
217 if (!NT_STATUS_IS_OK(status)) {
218 return status;
221 SIVAL(trans->out.params.data, 0, trans->out.data.length);
223 return NT_STATUS_OK;
228 parse NTTRANS_SET_SEC_DESC request
230 static NTSTATUS nttrans_set_sec_desc(struct smbsrv_request *req,
231 struct smb_nttrans *trans)
233 union smb_setfileinfo *io;
234 NTSTATUS status;
236 if (trans->in.params.length < 8) {
237 return NT_STATUS_INVALID_PARAMETER;
240 /* parse the request */
241 io = talloc(req, union smb_setfileinfo);
242 if (io == NULL) {
243 return NT_STATUS_NO_MEMORY;
246 io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
247 io->set_secdesc.file.fnum = SVAL(trans->in.params.data, 0);
248 io->set_secdesc.in.secinfo_flags = IVAL(trans->in.params.data, 4);
250 io->set_secdesc.in.sd = talloc(io, struct security_descriptor);
251 if (io->set_secdesc.in.sd == NULL) {
252 return NT_STATUS_NO_MEMORY;
255 status = ndr_pull_struct_blob(&trans->in.data, req,
256 io->set_secdesc.in.sd,
257 (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
258 if (!NT_STATUS_IS_OK(status)) {
259 return status;
262 /* call the backend - notice that we do it sync for now, until we support
263 async nttrans requests */
264 status = ntvfs_setfileinfo(req, io);
265 if (!NT_STATUS_IS_OK(status)) {
266 return status;
269 trans->out.setup_count = 0;
270 trans->out.setup = NULL;
271 trans->out.params = data_blob(NULL, 0);
272 trans->out.data = data_blob(NULL, 0);
274 return NT_STATUS_OK;
278 /* parse NTTRANS_RENAME request
280 static NTSTATUS nttrans_rename(struct smbsrv_request *req,
281 struct smb_nttrans *trans)
283 return NT_STATUS_FOOBAR;
287 parse NTTRANS_IOCTL request
289 static NTSTATUS nttrans_ioctl(struct smbsrv_request *req,
290 struct smb_nttrans *trans)
292 union smb_ioctl nt;
293 uint32_t function;
294 uint16_t fnum;
295 uint8_t filter;
296 BOOL fsctl;
297 DATA_BLOB *blob;
299 /* should have at least 4 setup words */
300 if (trans->in.setup_count != 4) {
301 return NT_STATUS_INVALID_PARAMETER;
304 function = IVAL(trans->in.setup, 0);
305 fnum = SVAL(trans->in.setup, 4);
306 fsctl = CVAL(trans->in.setup, 6);
307 filter = CVAL(trans->in.setup, 7);
309 blob = &trans->in.data;
311 nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
312 nt.ntioctl.in.fnum = fnum;
313 nt.ntioctl.in.function = function;
314 nt.ntioctl.in.fsctl = fsctl;
315 nt.ntioctl.in.filter = filter;
317 nttrans_setup_reply(req, trans, 0, 0, 1);
318 trans->out.setup[0] = 0;
320 return ntvfs_ioctl(req, &nt);
324 backend for nttrans requests
326 static NTSTATUS nttrans_backend(struct smbsrv_request *req,
327 struct smb_nttrans *trans)
329 /* the nttrans command is in function */
330 switch (trans->in.function) {
331 case NT_TRANSACT_CREATE:
332 return nttrans_create(req, trans);
333 case NT_TRANSACT_IOCTL:
334 return nttrans_ioctl(req, trans);
335 case NT_TRANSACT_RENAME:
336 return nttrans_rename(req, trans);
337 case NT_TRANSACT_QUERY_SECURITY_DESC:
338 return nttrans_query_sec_desc(req, trans);
339 case NT_TRANSACT_SET_SECURITY_DESC:
340 return nttrans_set_sec_desc(req, trans);
343 /* an unknown nttrans command */
344 return NT_STATUS_FOOBAR;
348 /****************************************************************************
349 Reply to an SMBnttrans request
350 ****************************************************************************/
351 void reply_nttrans(struct smbsrv_request *req)
353 struct smb_nttrans trans;
354 int i;
355 uint16_t param_ofs, data_ofs;
356 uint16_t param_count, data_count;
357 uint16_t params_left, data_left;
358 uint16_t param_total, data_total;
359 uint8_t *params, *data;
360 NTSTATUS status;
362 /* parse request */
363 if (req->in.wct < 19) {
364 req_reply_error(req, NT_STATUS_FOOBAR);
365 return;
368 trans.in.max_setup = CVAL(req->in.vwv, 0);
369 param_total = IVAL(req->in.vwv, 3);
370 data_total = IVAL(req->in.vwv, 7);
371 trans.in.max_param = IVAL(req->in.vwv, 11);
372 trans.in.max_data = IVAL(req->in.vwv, 15);
373 param_count = IVAL(req->in.vwv, 19);
374 param_ofs = IVAL(req->in.vwv, 23);
375 data_count = IVAL(req->in.vwv, 27);
376 data_ofs = IVAL(req->in.vwv, 31);
377 trans.in.setup_count = CVAL(req->in.vwv, 35);
378 trans.in.function = SVAL(req->in.vwv, 36);
380 if (req->in.wct != 19 + trans.in.setup_count) {
381 req_reply_dos_error(req, ERRSRV, ERRerror);
382 return;
385 /* parse out the setup words */
386 trans.in.setup = talloc_array(req, uint16_t, trans.in.setup_count);
387 if (!trans.in.setup) {
388 req_reply_error(req, NT_STATUS_NO_MEMORY);
389 return;
391 for (i=0;i<trans.in.setup_count;i++) {
392 trans.in.setup[i] = SVAL(req->in.vwv, VWV(19+i));
395 if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, &trans.in.params) ||
396 !req_pull_blob(req, req->in.hdr + data_ofs, data_count, &trans.in.data)) {
397 req_reply_error(req, NT_STATUS_FOOBAR);
398 return;
401 /* is it a partial request? if so, then send a 'send more' message */
402 if (param_total > param_count ||
403 data_total > data_count) {
404 DEBUG(0,("REWRITE: not handling partial nttrans requests!\n"));
405 return;
408 /* its a full request, give it to the backend */
409 status = nttrans_backend(req, &trans);
411 if (NT_STATUS_IS_ERR(status)) {
412 req_reply_error(req, status);
413 return;
416 #if 0
417 /* w2k3 does not check the max_setup count */
418 if (trans.out.setup_count > trans.in.max_setup) {
419 req_reply_error(req, NT_STATUS_BUFFER_TOO_SMALL);
420 return;
422 #endif
423 if (trans.out.params.length > trans.in.max_param) {
424 status = NT_STATUS_BUFFER_TOO_SMALL;
425 trans.out.params.length = trans.in.max_param;
427 if (trans.out.data.length > trans.in.max_data) {
428 status = NT_STATUS_BUFFER_TOO_SMALL;
429 trans.out.data.length = trans.in.max_data;
432 params_left = trans.out.params.length;
433 data_left = trans.out.data.length;
434 params = trans.out.params.data;
435 data = trans.out.data.data;
437 req_setup_reply(req, 18 + trans.out.setup_count, 0);
439 if (!NT_STATUS_IS_OK(status)) {
440 req_setup_error(req, status);
443 /* we need to divide up the reply into chunks that fit into
444 the negotiated buffer size */
445 do {
446 uint16_t this_data, this_param, max_bytes;
447 uint_t align1 = 1, align2 = (params_left ? 2 : 0);
448 struct smbsrv_request *this_req;
450 max_bytes = req_max_data(req) - (align1 + align2);
452 this_param = params_left;
453 if (this_param > max_bytes) {
454 this_param = max_bytes;
456 max_bytes -= this_param;
458 this_data = data_left;
459 if (this_data > max_bytes) {
460 this_data = max_bytes;
463 /* don't destroy unless this is the last chunk */
464 if (params_left - this_param != 0 ||
465 data_left - this_data != 0) {
466 this_req = req_setup_secondary(req);
467 } else {
468 this_req = req;
471 req_grow_data(req, this_param + this_data + (align1 + align2));
473 SSVAL(this_req->out.vwv, 0, 0); /* reserved */
474 SCVAL(this_req->out.vwv, 2, 0); /* reserved */
475 SIVAL(this_req->out.vwv, 3, trans.out.params.length);
476 SIVAL(this_req->out.vwv, 7, trans.out.data.length);
478 SIVAL(this_req->out.vwv, 11, this_param);
479 SIVAL(this_req->out.vwv, 15, align1 + PTR_DIFF(this_req->out.data, this_req->out.hdr));
480 SIVAL(this_req->out.vwv, 19, PTR_DIFF(params, trans.out.params.data));
482 SIVAL(this_req->out.vwv, 23, this_data);
483 SIVAL(this_req->out.vwv, 27, align1 + align2 +
484 PTR_DIFF(this_req->out.data + this_param, this_req->out.hdr));
485 SIVAL(this_req->out.vwv, 31, PTR_DIFF(data, trans.out.data.data));
487 SCVAL(this_req->out.vwv, 35, trans.out.setup_count);
488 for (i=0;i<trans.out.setup_count;i++) {
489 SSVAL(this_req->out.vwv, VWV(18+i), trans.out.setup[i]);
492 memset(this_req->out.data, 0, align1);
493 if (this_param != 0) {
494 memcpy(this_req->out.data + align1, params, this_param);
496 memset(this_req->out.data+this_param+align1, 0, align2);
497 if (this_data != 0) {
498 memcpy(this_req->out.data+this_param+align1+align2,
499 data, this_data);
502 params_left -= this_param;
503 data_left -= this_data;
504 params += this_param;
505 data += this_data;
507 req_send_reply(this_req);
508 } while (params_left != 0 || data_left != 0);
512 /****************************************************************************
513 Reply to an SMBnttranss request
514 ****************************************************************************/
515 void reply_nttranss(struct smbsrv_request *req)
517 req_reply_error(req, NT_STATUS_FOOBAR);