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
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; \
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
,
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
);
53 parse NTTRANS_CREATE request
55 static NTSTATUS
nttrans_create(struct smbsrv_request
*req
,
56 struct smb_nttrans
*trans
)
60 uint32_t sd_length
, ea_length
;
64 if (trans
->in
.params
.length
< 54) {
65 return NT_STATUS_INVALID_PARAMETER
;
68 /* parse the request */
69 io
= talloc(req
, union smb_open
);
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
,
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) {
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
)) {
125 /* and an optional ea_list */
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
)) {
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
)) {
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
);
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
;
188 if (trans
->in
.params
.length
< 8) {
189 return NT_STATUS_INVALID_PARAMETER
;
192 /* parse the request */
193 io
= talloc(req
, union smb_fileinfo
);
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
)) {
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
)) {
221 SIVAL(trans
->out
.params
.data
, 0, trans
->out
.data
.length
);
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
;
236 if (trans
->in
.params
.length
< 8) {
237 return NT_STATUS_INVALID_PARAMETER
;
240 /* parse the request */
241 io
= talloc(req
, union smb_setfileinfo
);
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
)) {
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
)) {
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);
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
)
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
;
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
;
363 if (req
->in
.wct
< 19) {
364 req_reply_error(req
, NT_STATUS_FOOBAR
);
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
);
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
);
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
);
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"));
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
);
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
);
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 */
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
);
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
,
502 params_left
-= this_param
;
503 data_left
-= this_data
;
504 params
+= this_param
;
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
);