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 3 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, see <http://www.gnu.org/licenses/>.
21 This file handles the parsing of transact2 requests
25 #include "smb_server/smb_server.h"
26 #include "ntvfs/ntvfs.h"
27 #include "libcli/raw/libcliraw.h"
28 #include "libcli/raw/raw_proto.h"
29 #include "librpc/gen_ndr/ndr_security.h"
32 hold the state of a nttrans op while in progress. Needed to allow for async backend
36 struct smb_nttrans
*trans
;
37 NTSTATUS (*send_fn
)(struct nttrans_op
*);
42 /* setup a nttrans reply, given the data and params sizes */
43 static NTSTATUS
nttrans_setup_reply(struct nttrans_op
*op
,
44 struct smb_nttrans
*trans
,
45 uint32_t param_size
, uint32_t data_size
,
48 trans
->out
.setup_count
= setup_count
;
49 if (setup_count
!= 0) {
50 trans
->out
.setup
= talloc_zero_array(op
, uint8_t, setup_count
*2);
51 NT_STATUS_HAVE_NO_MEMORY(trans
->out
.setup
);
53 trans
->out
.params
= data_blob_talloc(op
, NULL
, param_size
);
54 if (param_size
!= 0) {
55 NT_STATUS_HAVE_NO_MEMORY(trans
->out
.params
.data
);
57 trans
->out
.data
= data_blob_talloc(op
, NULL
, data_size
);
59 NT_STATUS_HAVE_NO_MEMORY(trans
->out
.data
.data
);
65 send a nttrans create reply
67 static NTSTATUS
nttrans_create_send(struct nttrans_op
*op
)
69 union smb_open
*io
= talloc_get_type(op
->op_info
, union smb_open
);
73 status
= nttrans_setup_reply(op
, op
->trans
, 69, 0, 0);
74 NT_STATUS_NOT_OK_RETURN(status
);
75 params
= op
->trans
->out
.params
.data
;
77 SSVAL(params
, 0, io
->ntcreatex
.out
.oplock_level
);
78 smbsrv_push_fnum(params
, 2, io
->ntcreatex
.out
.file
.ntvfs
);
79 SIVAL(params
, 4, io
->ntcreatex
.out
.create_action
);
80 SIVAL(params
, 8, 0); /* ea error offset */
81 push_nttime(params
, 12, io
->ntcreatex
.out
.create_time
);
82 push_nttime(params
, 20, io
->ntcreatex
.out
.access_time
);
83 push_nttime(params
, 28, io
->ntcreatex
.out
.write_time
);
84 push_nttime(params
, 36, io
->ntcreatex
.out
.change_time
);
85 SIVAL(params
, 44, io
->ntcreatex
.out
.attrib
);
86 SBVAL(params
, 48, io
->ntcreatex
.out
.alloc_size
);
87 SBVAL(params
, 56, io
->ntcreatex
.out
.size
);
88 SSVAL(params
, 64, io
->ntcreatex
.out
.file_type
);
89 SSVAL(params
, 66, io
->ntcreatex
.out
.ipc_state
);
90 SCVAL(params
, 68, io
->ntcreatex
.out
.is_directory
);
96 parse NTTRANS_CREATE request
98 static NTSTATUS
nttrans_create(struct smbsrv_request
*req
,
99 struct nttrans_op
*op
)
101 struct smb_nttrans
*trans
= op
->trans
;
104 uint32_t sd_length
, ea_length
;
107 enum ndr_err_code ndr_err
;
109 if (trans
->in
.params
.length
< 54) {
110 return NT_STATUS_INVALID_PARAMETER
;
113 /* parse the request */
114 io
= talloc(op
, union smb_open
);
115 NT_STATUS_HAVE_NO_MEMORY(io
);
117 io
->ntcreatex
.level
= RAW_OPEN_NTTRANS_CREATE
;
119 params
= trans
->in
.params
.data
;
121 io
->ntcreatex
.in
.flags
= IVAL(params
, 0);
122 io
->ntcreatex
.in
.root_fid
.ntvfs
= smbsrv_pull_fnum(req
, params
, 4);
123 io
->ntcreatex
.in
.access_mask
= IVAL(params
, 8);
124 io
->ntcreatex
.in
.alloc_size
= BVAL(params
, 12);
125 io
->ntcreatex
.in
.file_attr
= IVAL(params
, 20);
126 io
->ntcreatex
.in
.share_access
= IVAL(params
, 24);
127 io
->ntcreatex
.in
.open_disposition
= IVAL(params
, 28);
128 io
->ntcreatex
.in
.create_options
= IVAL(params
, 32);
129 sd_length
= IVAL(params
, 36);
130 ea_length
= IVAL(params
, 40);
131 fname_len
= IVAL(params
, 44);
132 io
->ntcreatex
.in
.impersonation
= IVAL(params
, 48);
133 io
->ntcreatex
.in
.security_flags
= CVAL(params
, 52);
134 io
->ntcreatex
.in
.sec_desc
= NULL
;
135 io
->ntcreatex
.in
.ea_list
= NULL
;
136 io
->ntcreatex
.in
.query_maximal_access
= false;
137 io
->ntcreatex
.in
.private_flags
= 0;
139 req_pull_string(&req
->in
.bufinfo
, &io
->ntcreatex
.in
.fname
,
141 MIN(fname_len
+1, trans
->in
.params
.length
- 53),
142 STR_NO_RANGE_CHECK
| STR_TERMINATE
);
143 if (!io
->ntcreatex
.in
.fname
) {
144 return NT_STATUS_INVALID_PARAMETER
;
147 if (sd_length
> trans
->in
.data
.length
||
148 ea_length
> trans
->in
.data
.length
||
149 (sd_length
+ea_length
) > trans
->in
.data
.length
) {
150 return NT_STATUS_INVALID_PARAMETER
;
153 /* this call has an optional security descriptor */
154 if (sd_length
!= 0) {
156 blob
.data
= trans
->in
.data
.data
;
157 blob
.length
= sd_length
;
158 io
->ntcreatex
.in
.sec_desc
= talloc(io
, struct security_descriptor
);
159 if (io
->ntcreatex
.in
.sec_desc
== NULL
) {
160 return NT_STATUS_NO_MEMORY
;
162 ndr_err
= ndr_pull_struct_blob(&blob
, io
,
163 io
->ntcreatex
.in
.sec_desc
,
164 (ndr_pull_flags_fn_t
)ndr_pull_security_descriptor
);
165 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
166 return ndr_map_error2ntstatus(ndr_err
);
170 /* and an optional ea_list */
173 blob
.data
= trans
->in
.data
.data
+ sd_length
;
174 blob
.length
= ea_length
;
175 io
->ntcreatex
.in
.ea_list
= talloc(io
, struct smb_ea_list
);
176 if (io
->ntcreatex
.in
.ea_list
== NULL
) {
177 return NT_STATUS_NO_MEMORY
;
180 status
= ea_pull_list_chained(&blob
, io
,
181 &io
->ntcreatex
.in
.ea_list
->num_eas
,
182 &io
->ntcreatex
.in
.ea_list
->eas
);
183 if (!NT_STATUS_IS_OK(status
)) {
188 op
->send_fn
= nttrans_create_send
;
191 return ntvfs_open(req
->ntvfs
, io
);
196 send NTTRANS_QUERY_SEC_DESC reply
198 static NTSTATUS
nttrans_query_sec_desc_send(struct nttrans_op
*op
)
200 union smb_fileinfo
*io
= talloc_get_type(op
->op_info
, union smb_fileinfo
);
203 enum ndr_err_code ndr_err
;
205 status
= nttrans_setup_reply(op
, op
->trans
, 4, 0, 0);
206 NT_STATUS_NOT_OK_RETURN(status
);
207 params
= op
->trans
->out
.params
.data
;
209 ndr_err
= ndr_push_struct_blob(&op
->trans
->out
.data
, op
,
210 io
->query_secdesc
.out
.sd
,
211 (ndr_push_flags_fn_t
)ndr_push_security_descriptor
);
212 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
213 return ndr_map_error2ntstatus(ndr_err
);
216 SIVAL(params
, 0, op
->trans
->out
.data
.length
);
222 parse NTTRANS_QUERY_SEC_DESC request
224 static NTSTATUS
nttrans_query_sec_desc(struct smbsrv_request
*req
,
225 struct nttrans_op
*op
)
227 struct smb_nttrans
*trans
= op
->trans
;
228 union smb_fileinfo
*io
;
230 if (trans
->in
.params
.length
< 8) {
231 return NT_STATUS_INVALID_PARAMETER
;
234 /* parse the request */
235 io
= talloc(op
, union smb_fileinfo
);
236 NT_STATUS_HAVE_NO_MEMORY(io
);
238 io
->query_secdesc
.level
= RAW_FILEINFO_SEC_DESC
;
239 io
->query_secdesc
.in
.file
.ntvfs
= smbsrv_pull_fnum(req
, trans
->in
.params
.data
, 0);
240 io
->query_secdesc
.in
.secinfo_flags
= IVAL(trans
->in
.params
.data
, 4);
243 op
->send_fn
= nttrans_query_sec_desc_send
;
245 SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io
->query_secdesc
.in
.file
.ntvfs
);
246 return ntvfs_qfileinfo(req
->ntvfs
, io
);
251 parse NTTRANS_SET_SEC_DESC request
253 static NTSTATUS
nttrans_set_sec_desc(struct smbsrv_request
*req
,
254 struct nttrans_op
*op
)
256 struct smb_nttrans
*trans
= op
->trans
;
257 union smb_setfileinfo
*io
;
258 enum ndr_err_code ndr_err
;
260 if (trans
->in
.params
.length
< 8) {
261 return NT_STATUS_INVALID_PARAMETER
;
264 /* parse the request */
265 io
= talloc(req
, union smb_setfileinfo
);
266 NT_STATUS_HAVE_NO_MEMORY(io
);
268 io
->set_secdesc
.level
= RAW_SFILEINFO_SEC_DESC
;
269 io
->set_secdesc
.in
.file
.ntvfs
= smbsrv_pull_fnum(req
, trans
->in
.params
.data
, 0);
270 io
->set_secdesc
.in
.secinfo_flags
= IVAL(trans
->in
.params
.data
, 4);
272 io
->set_secdesc
.in
.sd
= talloc(io
, struct security_descriptor
);
273 NT_STATUS_HAVE_NO_MEMORY(io
->set_secdesc
.in
.sd
);
275 ndr_err
= ndr_pull_struct_blob(&trans
->in
.data
, req
,
276 io
->set_secdesc
.in
.sd
,
277 (ndr_pull_flags_fn_t
)ndr_pull_security_descriptor
);
278 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
279 return ndr_map_error2ntstatus(ndr_err
);
282 SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io
->set_secdesc
.in
.file
.ntvfs
);
283 return ntvfs_setfileinfo(req
->ntvfs
, io
);
287 /* parse NTTRANS_RENAME request
289 static NTSTATUS
nttrans_rename(struct smbsrv_request
*req
,
290 struct nttrans_op
*op
)
292 struct smb_nttrans
*trans
= op
->trans
;
293 union smb_rename
*io
;
295 if (trans
->in
.params
.length
< 5) {
296 return NT_STATUS_INVALID_PARAMETER
;
299 /* parse the request */
300 io
= talloc(req
, union smb_rename
);
301 NT_STATUS_HAVE_NO_MEMORY(io
);
303 io
->nttrans
.level
= RAW_RENAME_NTTRANS
;
304 io
->nttrans
.in
.file
.ntvfs
= smbsrv_pull_fnum(req
, trans
->in
.params
.data
, 0);
305 io
->nttrans
.in
.flags
= SVAL(trans
->in
.params
.data
, 2);
307 smbsrv_blob_pull_string(&req
->in
.bufinfo
, &trans
->in
.params
, 4,
308 &io
->nttrans
.in
.new_name
,
310 if (!io
->nttrans
.in
.new_name
) {
311 return NT_STATUS_INVALID_PARAMETER
;
314 SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io
->nttrans
.in
.file
.ntvfs
);
315 return ntvfs_rename(req
->ntvfs
, io
);
319 parse NTTRANS_IOCTL send
321 static NTSTATUS
nttrans_ioctl_send(struct nttrans_op
*op
)
323 union smb_ioctl
*info
= talloc_get_type(op
->op_info
, union smb_ioctl
);
327 * we pass 0 as data_count here,
328 * because we reuse the DATA_BLOB from the smb_ioctl
331 status
= nttrans_setup_reply(op
, op
->trans
, 0, 0, 1);
332 NT_STATUS_NOT_OK_RETURN(status
);
334 op
->trans
->out
.setup
[0] = 0;
335 op
->trans
->out
.data
= info
->ntioctl
.out
.blob
;
342 parse NTTRANS_IOCTL request
344 static NTSTATUS
nttrans_ioctl(struct smbsrv_request
*req
,
345 struct nttrans_op
*op
)
347 struct smb_nttrans
*trans
= op
->trans
;
350 /* should have at least 4 setup words */
351 if (trans
->in
.setup_count
!= 4) {
352 return NT_STATUS_INVALID_PARAMETER
;
355 nt
= talloc(op
, union smb_ioctl
);
356 NT_STATUS_HAVE_NO_MEMORY(nt
);
358 nt
->ntioctl
.level
= RAW_IOCTL_NTIOCTL
;
359 nt
->ntioctl
.in
.function
= IVAL(trans
->in
.setup
, 0);
360 nt
->ntioctl
.in
.file
.ntvfs
= smbsrv_pull_fnum(req
, (uint8_t *)trans
->in
.setup
, 4);
361 nt
->ntioctl
.in
.fsctl
= CVAL(trans
->in
.setup
, 6);
362 nt
->ntioctl
.in
.filter
= CVAL(trans
->in
.setup
, 7);
363 nt
->ntioctl
.in
.max_data
= trans
->in
.max_data
;
364 nt
->ntioctl
.in
.blob
= trans
->in
.data
;
367 op
->send_fn
= nttrans_ioctl_send
;
369 SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(nt
->ntioctl
.in
.file
.ntvfs
);
370 return ntvfs_ioctl(req
->ntvfs
, nt
);
375 send NTTRANS_NOTIFY_CHANGE reply
377 static NTSTATUS
nttrans_notify_change_send(struct nttrans_op
*op
)
379 union smb_notify
*info
= talloc_get_type(op
->op_info
, union smb_notify
);
384 #define MAX_BYTES_PER_CHAR 3
386 /* work out how big the reply buffer could be */
387 for (i
=0;i
<info
->nttrans
.out
.num_changes
;i
++) {
388 size
+= 12 + 3 + (1+strlen(info
->nttrans
.out
.changes
[i
].name
.s
)) * MAX_BYTES_PER_CHAR
;
391 status
= nttrans_setup_reply(op
, op
->trans
, size
, 0, 0);
392 NT_STATUS_NOT_OK_RETURN(status
);
393 p
= op
->trans
->out
.params
.data
;
395 /* construct the changes buffer */
396 for (i
=0;i
<info
->nttrans
.out
.num_changes
;i
++) {
400 SIVAL(p
, 4, info
->nttrans
.out
.changes
[i
].action
);
401 len
= push_string(p
+ 12, info
->nttrans
.out
.changes
[i
].name
.s
,
402 op
->trans
->out
.params
.length
-
403 (p
+12 - op
->trans
->out
.params
.data
), STR_UNICODE
);
409 int pad
= 4 - (ofs
& 3);
410 memset(p
+ofs
, 0, pad
);
414 if (i
== info
->nttrans
.out
.num_changes
-1) {
423 op
->trans
->out
.params
.length
= p
- op
->trans
->out
.params
.data
;
429 parse NTTRANS_NOTIFY_CHANGE request
431 static NTSTATUS
nttrans_notify_change(struct smbsrv_request
*req
,
432 struct nttrans_op
*op
)
434 struct smb_nttrans
*trans
= op
->trans
;
435 union smb_notify
*info
;
437 /* should have at least 4 setup words */
438 if (trans
->in
.setup_count
!= 4) {
439 return NT_STATUS_INVALID_PARAMETER
;
442 info
= talloc(op
, union smb_notify
);
443 NT_STATUS_HAVE_NO_MEMORY(info
);
445 info
->nttrans
.level
= RAW_NOTIFY_NTTRANS
;
446 info
->nttrans
.in
.completion_filter
= IVAL(trans
->in
.setup
, 0);
447 info
->nttrans
.in
.file
.ntvfs
= smbsrv_pull_fnum(req
, (uint8_t *)trans
->in
.setup
, 4);
448 info
->nttrans
.in
.recursive
= SVAL(trans
->in
.setup
, 6);
449 info
->nttrans
.in
.buffer_size
= trans
->in
.max_param
;
452 op
->send_fn
= nttrans_notify_change_send
;
454 SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(info
->nttrans
.in
.file
.ntvfs
);
455 return ntvfs_notify(req
->ntvfs
, info
);
459 backend for nttrans requests
461 static NTSTATUS
nttrans_backend(struct smbsrv_request
*req
,
462 struct nttrans_op
*op
)
464 /* the nttrans command is in function */
465 switch (op
->trans
->in
.function
) {
466 case NT_TRANSACT_CREATE
:
467 return nttrans_create(req
, op
);
468 case NT_TRANSACT_IOCTL
:
469 return nttrans_ioctl(req
, op
);
470 case NT_TRANSACT_RENAME
:
471 return nttrans_rename(req
, op
);
472 case NT_TRANSACT_QUERY_SECURITY_DESC
:
473 return nttrans_query_sec_desc(req
, op
);
474 case NT_TRANSACT_SET_SECURITY_DESC
:
475 return nttrans_set_sec_desc(req
, op
);
476 case NT_TRANSACT_NOTIFY_CHANGE
:
477 return nttrans_notify_change(req
, op
);
480 /* an unknown nttrans command */
481 return NT_STATUS_DOS(ERRSRV
, ERRerror
);
485 static void reply_nttrans_send(struct ntvfs_request
*ntvfs
)
487 struct smbsrv_request
*req
;
488 uint32_t params_left
, data_left
;
489 uint8_t *params
, *data
;
490 struct smb_nttrans
*trans
;
491 struct nttrans_op
*op
;
493 SMBSRV_CHECK_ASYNC_STATUS(op
, struct nttrans_op
);
497 /* if this function needs work to form the nttrans reply buffer, then
499 if (op
->send_fn
!= NULL
) {
501 status
= op
->send_fn(op
);
502 if (!NT_STATUS_IS_OK(status
)) {
503 smbsrv_send_error(req
, status
);
508 smbsrv_setup_reply(req
, 18 + trans
->out
.setup_count
, 0);
510 /* note that we don't check the max_setup count (matching w2k3
513 if (trans
->out
.params
.length
> trans
->in
.max_param
) {
514 smbsrv_setup_error(req
, NT_STATUS_BUFFER_TOO_SMALL
);
515 trans
->out
.params
.length
= trans
->in
.max_param
;
517 if (trans
->out
.data
.length
> trans
->in
.max_data
) {
518 smbsrv_setup_error(req
, NT_STATUS_BUFFER_TOO_SMALL
);
519 trans
->out
.data
.length
= trans
->in
.max_data
;
522 params_left
= trans
->out
.params
.length
;
523 data_left
= trans
->out
.data
.length
;
524 params
= trans
->out
.params
.data
;
525 data
= trans
->out
.data
.data
;
527 /* we need to divide up the reply into chunks that fit into
528 the negotiated buffer size */
530 uint32_t this_data
, this_param
, max_bytes
;
531 unsigned int align1
= 1, align2
= (params_left
? 2 : 0);
532 struct smbsrv_request
*this_req
;
534 max_bytes
= req_max_data(req
) - (align1
+ align2
);
536 this_param
= params_left
;
537 if (this_param
> max_bytes
) {
538 this_param
= max_bytes
;
540 max_bytes
-= this_param
;
542 this_data
= data_left
;
543 if (this_data
> max_bytes
) {
544 this_data
= max_bytes
;
547 /* don't destroy unless this is the last chunk */
548 if (params_left
- this_param
!= 0 ||
549 data_left
- this_data
!= 0) {
550 this_req
= smbsrv_setup_secondary_request(req
);
555 req_grow_data(this_req
, this_param
+ this_data
+ (align1
+ align2
));
557 SSVAL(this_req
->out
.vwv
, 0, 0); /* reserved */
558 SCVAL(this_req
->out
.vwv
, 2, 0); /* reserved */
559 SIVAL(this_req
->out
.vwv
, 3, trans
->out
.params
.length
);
560 SIVAL(this_req
->out
.vwv
, 7, trans
->out
.data
.length
);
562 SIVAL(this_req
->out
.vwv
, 11, this_param
);
563 SIVAL(this_req
->out
.vwv
, 15, align1
+ PTR_DIFF(this_req
->out
.data
, this_req
->out
.hdr
));
564 SIVAL(this_req
->out
.vwv
, 19, PTR_DIFF(params
, trans
->out
.params
.data
));
566 SIVAL(this_req
->out
.vwv
, 23, this_data
);
567 SIVAL(this_req
->out
.vwv
, 27, align1
+ align2
+
568 PTR_DIFF(this_req
->out
.data
+ this_param
, this_req
->out
.hdr
));
569 SIVAL(this_req
->out
.vwv
, 31, PTR_DIFF(data
, trans
->out
.data
.data
));
571 SCVAL(this_req
->out
.vwv
, 35, trans
->out
.setup_count
);
572 memcpy((char *)(this_req
->out
.vwv
) + VWV(18), trans
->out
.setup
,
573 sizeof(uint16_t) * trans
->out
.setup_count
);
574 memset(this_req
->out
.data
, 0, align1
);
575 if (this_param
!= 0) {
576 memcpy(this_req
->out
.data
+ align1
, params
, this_param
);
578 memset(this_req
->out
.data
+this_param
+align1
, 0, align2
);
579 if (this_data
!= 0) {
580 memcpy(this_req
->out
.data
+this_param
+align1
+align2
,
584 params_left
-= this_param
;
585 data_left
-= this_data
;
586 params
+= this_param
;
589 smbsrv_send_reply(this_req
);
590 } while (params_left
!= 0 || data_left
!= 0);
594 send a continue request
596 static void reply_nttrans_continue(struct smbsrv_request
*req
, struct smb_nttrans
*trans
)
598 struct smbsrv_request
*req2
;
599 struct smbsrv_trans_partial
*tp
;
602 /* make sure they don't flood us */
603 for (count
=0,tp
=req
->smb_conn
->trans_partial
;tp
;tp
=tp
->next
) count
++;
605 smbsrv_send_error(req
, NT_STATUS_INSUFFICIENT_RESOURCES
);
609 tp
= talloc(req
, struct smbsrv_trans_partial
);
612 tp
->u
.nttrans
= trans
;
613 tp
->command
= SMBnttrans
;
615 DLIST_ADD(req
->smb_conn
->trans_partial
, tp
);
616 talloc_set_destructor(tp
, smbsrv_trans_partial_destructor
);
618 req2
= smbsrv_setup_secondary_request(req
);
620 /* send a 'please continue' reply */
621 smbsrv_setup_reply(req2
, 0, 0);
622 smbsrv_send_reply(req2
);
627 answer a reconstructed trans request
629 static void reply_nttrans_complete(struct smbsrv_request
*req
, struct smb_nttrans
*trans
)
631 struct nttrans_op
*op
;
633 SMBSRV_TALLOC_IO_PTR(op
, struct nttrans_op
);
634 SMBSRV_SETUP_NTVFS_REQUEST(reply_nttrans_send
, NTVFS_ASYNC_STATE_MAY_ASYNC
);
640 /* its a full request, give it to the backend */
641 ZERO_STRUCT(trans
->out
);
642 SMBSRV_CALL_NTVFS_BACKEND(nttrans_backend(req
, op
));
646 /****************************************************************************
647 Reply to an SMBnttrans request
648 ****************************************************************************/
649 void smbsrv_reply_nttrans(struct smbsrv_request
*req
)
651 struct smb_nttrans
*trans
;
652 uint32_t param_ofs
, data_ofs
;
653 uint32_t param_count
, data_count
;
654 uint32_t param_total
, data_total
;
657 if (req
->in
.wct
< 19) {
658 smbsrv_send_error(req
, NT_STATUS_FOOBAR
);
662 trans
= talloc(req
, struct smb_nttrans
);
664 smbsrv_send_error(req
, NT_STATUS_NO_MEMORY
);
668 trans
->in
.max_setup
= CVAL(req
->in
.vwv
, 0);
669 param_total
= IVAL(req
->in
.vwv
, 3);
670 data_total
= IVAL(req
->in
.vwv
, 7);
671 trans
->in
.max_param
= IVAL(req
->in
.vwv
, 11);
672 trans
->in
.max_data
= IVAL(req
->in
.vwv
, 15);
673 param_count
= IVAL(req
->in
.vwv
, 19);
674 param_ofs
= IVAL(req
->in
.vwv
, 23);
675 data_count
= IVAL(req
->in
.vwv
, 27);
676 data_ofs
= IVAL(req
->in
.vwv
, 31);
677 trans
->in
.setup_count
= CVAL(req
->in
.vwv
, 35);
678 trans
->in
.function
= SVAL(req
->in
.vwv
, 36);
680 if (req
->in
.wct
!= 19 + trans
->in
.setup_count
) {
681 smbsrv_send_error(req
, NT_STATUS_DOS(ERRSRV
, ERRerror
));
685 /* parse out the setup words */
686 trans
->in
.setup
= talloc_array(req
, uint8_t, trans
->in
.setup_count
*2);
687 if (!trans
->in
.setup
) {
688 smbsrv_send_error(req
, NT_STATUS_NO_MEMORY
);
691 memcpy(trans
->in
.setup
, (char *)(req
->in
.vwv
) + VWV(19),
692 sizeof(uint16_t) * trans
->in
.setup_count
);
694 if (!req_pull_blob(&req
->in
.bufinfo
, req
->in
.hdr
+ param_ofs
, param_count
, &trans
->in
.params
) ||
695 !req_pull_blob(&req
->in
.bufinfo
, req
->in
.hdr
+ data_ofs
, data_count
, &trans
->in
.data
)) {
696 smbsrv_send_error(req
, NT_STATUS_FOOBAR
);
700 /* is it a partial request? if so, then send a 'send more' message */
701 if (param_total
> param_count
|| data_total
> data_count
) {
702 reply_nttrans_continue(req
, trans
);
706 reply_nttrans_complete(req
, trans
);
710 /****************************************************************************
711 Reply to an SMBnttranss request
712 ****************************************************************************/
713 void smbsrv_reply_nttranss(struct smbsrv_request
*req
)
715 struct smbsrv_trans_partial
*tp
;
716 struct smb_nttrans
*trans
= NULL
;
717 uint32_t param_ofs
, data_ofs
;
718 uint32_t param_count
, data_count
;
719 uint32_t param_disp
, data_disp
;
720 uint32_t param_total
, data_total
;
721 DATA_BLOB params
, data
;
724 if (req
->in
.wct
!= 18) {
725 smbsrv_send_error(req
, NT_STATUS_DOS(ERRSRV
, ERRerror
));
729 for (tp
=req
->smb_conn
->trans_partial
;tp
;tp
=tp
->next
) {
730 if (tp
->command
== SMBnttrans
&&
731 SVAL(tp
->req
->in
.hdr
, HDR_MID
) == SVAL(req
->in
.hdr
, HDR_MID
)) {
732 /* TODO: check the VUID, PID and TID too? */
738 smbsrv_send_error(req
, NT_STATUS_INVALID_PARAMETER
);
742 trans
= tp
->u
.nttrans
;
744 param_total
= IVAL(req
->in
.vwv
, 3);
745 data_total
= IVAL(req
->in
.vwv
, 7);
746 param_count
= IVAL(req
->in
.vwv
, 11);
747 param_ofs
= IVAL(req
->in
.vwv
, 15);
748 param_disp
= IVAL(req
->in
.vwv
, 19);
749 data_count
= IVAL(req
->in
.vwv
, 23);
750 data_ofs
= IVAL(req
->in
.vwv
, 27);
751 data_disp
= IVAL(req
->in
.vwv
, 31);
753 if (!req_pull_blob(&req
->in
.bufinfo
, req
->in
.hdr
+ param_ofs
, param_count
, ¶ms
) ||
754 !req_pull_blob(&req
->in
.bufinfo
, req
->in
.hdr
+ data_ofs
, data_count
, &data
)) {
755 smbsrv_send_error(req
, NT_STATUS_INVALID_PARAMETER
);
759 /* only allow contiguous requests */
760 if ((param_count
!= 0 &&
761 param_disp
!= trans
->in
.params
.length
) ||
763 data_disp
!= trans
->in
.data
.length
)) {
764 smbsrv_send_error(req
, NT_STATUS_INVALID_PARAMETER
);
768 /* add to the existing request */
769 if (param_count
!= 0) {
770 trans
->in
.params
.data
= talloc_realloc(trans
,
771 trans
->in
.params
.data
,
773 param_disp
+ param_count
);
774 if (trans
->in
.params
.data
== NULL
) {
775 smbsrv_send_error(tp
->req
, NT_STATUS_NO_MEMORY
);
778 trans
->in
.params
.length
= param_disp
+ param_count
;
781 if (data_count
!= 0) {
782 trans
->in
.data
.data
= talloc_realloc(trans
,
785 data_disp
+ data_count
);
786 if (trans
->in
.data
.data
== NULL
) {
787 smbsrv_send_error(tp
->req
, NT_STATUS_NO_MEMORY
);
790 trans
->in
.data
.length
= data_disp
+ data_count
;
793 memcpy(trans
->in
.params
.data
+ param_disp
, params
.data
, params
.length
);
794 memcpy(trans
->in
.data
.data
+ data_disp
, data
.data
, data
.length
);
796 /* the sequence number of the reply is taken from the last secondary
798 tp
->req
->seq_num
= req
->seq_num
;
800 /* we don't reply to Transs2 requests */
803 if (trans
->in
.params
.length
== param_total
&&
804 trans
->in
.data
.length
== data_total
) {
805 /* its now complete */
808 reply_nttrans_complete(req
, trans
);