2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan Metzmacher 2011
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "system/network.h"
22 #include "lib/util/tevent_ntstatus.h"
23 #include "smb_common.h"
24 #include "smbXcli_base.h"
25 #include "librpc/ndr/libndr.h"
26 #include "librpc/gen_ndr/ioctl.h"
28 struct smb2cli_ioctl_state
{
31 uint32_t max_input_length
;
32 uint32_t max_output_length
;
33 struct iovec
*recv_iov
;
34 DATA_BLOB out_input_buffer
;
35 DATA_BLOB out_output_buffer
;
39 static void smb2cli_ioctl_done(struct tevent_req
*subreq
);
41 struct tevent_req
*smb2cli_ioctl_send(TALLOC_CTX
*mem_ctx
,
42 struct tevent_context
*ev
,
43 struct smbXcli_conn
*conn
,
44 uint32_t timeout_msec
,
45 struct smbXcli_session
*session
,
46 struct smbXcli_tcon
*tcon
,
47 uint64_t in_fid_persistent
,
48 uint64_t in_fid_volatile
,
50 uint32_t in_max_input_length
,
51 const DATA_BLOB
*in_input_buffer
,
52 uint32_t in_max_output_length
,
53 const DATA_BLOB
*in_output_buffer
,
56 struct tevent_req
*req
, *subreq
;
57 struct smb2cli_ioctl_state
*state
;
61 uint32_t input_buffer_offset
= 0;
62 uint32_t input_buffer_length
= 0;
63 uint32_t output_buffer_offset
= 0;
64 uint32_t output_buffer_length
= 0;
65 uint32_t pad_length
= 0;
67 uint32_t max_dyn_len
= 0;
69 req
= tevent_req_create(mem_ctx
, &state
,
70 struct smb2cli_ioctl_state
);
74 state
->ctl_code
= in_ctl_code
;
75 state
->max_input_length
= in_max_input_length
;
76 state
->max_output_length
= in_max_output_length
;
78 tmp64
= in_max_input_length
;
79 tmp64
+= in_max_output_length
;
80 if (tmp64
> UINT32_MAX
) {
81 max_dyn_len
= UINT32_MAX
;
86 if (in_input_buffer
) {
87 input_buffer_offset
= SMB2_HDR_BODY
+0x38;
88 input_buffer_length
= in_input_buffer
->length
;
91 if (in_output_buffer
) {
92 output_buffer_offset
= SMB2_HDR_BODY
+0x38;
93 output_buffer_length
= in_output_buffer
->length
;
94 if (input_buffer_length
> 0 && output_buffer_length
> 0) {
96 output_buffer_offset
+= input_buffer_length
;
97 tmp
= output_buffer_offset
;
98 output_buffer_offset
= NDR_ROUND(output_buffer_offset
, 8);
99 pad_length
= output_buffer_offset
- tmp
;
103 fixed
= state
->fixed
;
105 SSVAL(fixed
, 0x00, 0x39);
106 SSVAL(fixed
, 0x02, 0); /* reserved */
107 SIVAL(fixed
, 0x04, in_ctl_code
);
108 SBVAL(fixed
, 0x08, in_fid_persistent
);
109 SBVAL(fixed
, 0x10, in_fid_volatile
);
110 SIVAL(fixed
, 0x18, input_buffer_offset
);
111 SIVAL(fixed
, 0x1C, input_buffer_length
);
112 SIVAL(fixed
, 0x20, in_max_input_length
);
113 SIVAL(fixed
, 0x24, output_buffer_offset
);
114 SIVAL(fixed
, 0x28, output_buffer_length
);
115 SIVAL(fixed
, 0x2C, in_max_output_length
);
116 SIVAL(fixed
, 0x30, in_flags
);
117 SIVAL(fixed
, 0x34, 0); /* reserved */
119 if (input_buffer_length
> 0 && output_buffer_length
> 0) {
120 size_t avail
= UINT32_MAX
- (input_buffer_length
+ pad_length
);
121 size_t ofs
= output_buffer_offset
- input_buffer_offset
;
123 if (avail
< output_buffer_length
) {
124 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER_MIX
);
125 return tevent_req_post(req
, ev
);
128 dyn_len
= input_buffer_length
+ output_buffer_length
+ pad_length
;
130 dyn
= talloc_zero_array(state
, uint8_t, dyn_len
);
131 if (tevent_req_nomem(dyn
, req
)) {
132 return tevent_req_post(req
, ev
);
134 memcpy(dyn
, in_input_buffer
->data
,
135 in_input_buffer
->length
);
136 memcpy(dyn
+ ofs
, in_output_buffer
->data
,
137 in_output_buffer
->length
);
138 } else if (input_buffer_length
> 0) {
139 dyn
= in_input_buffer
->data
;
140 dyn_len
= in_input_buffer
->length
;
141 } else if (output_buffer_length
> 0) {
142 dyn
= in_output_buffer
->data
;
143 dyn_len
= in_output_buffer
->length
;
145 dyn
= state
->dyn_pad
;
146 dyn_len
= sizeof(state
->dyn_pad
);
149 subreq
= smb2cli_req_send(state
, ev
, conn
, SMB2_OP_IOCTL
,
154 state
->fixed
, sizeof(state
->fixed
),
157 if (tevent_req_nomem(subreq
, req
)) {
158 return tevent_req_post(req
, ev
);
160 tevent_req_set_callback(subreq
, smb2cli_ioctl_done
, req
);
165 * 3.3.4.4 Sending an Error Response
166 * An error code other than one of the following indicates a failure:
168 static bool smb2cli_ioctl_is_failure(uint32_t ctl_code
, NTSTATUS status
,
171 if (NT_STATUS_IS_OK(status
)) {
176 * STATUS_INVALID_PARAMETER in a FSCTL_SRV_COPYCHUNK or
177 * FSCTL_SRV_COPYCHUNK_WRITE Response, when returning an
178 * SRV_COPYCHUNK_RESPONSE as described in section 2.2.32.1.
180 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
) &&
181 (ctl_code
== FSCTL_SRV_COPYCHUNK
||
182 ctl_code
== FSCTL_SRV_COPYCHUNK_WRITE
) &&
183 data_size
== sizeof(struct srv_copychunk_rsp
)) {
190 static void smb2cli_ioctl_done(struct tevent_req
*subreq
)
192 struct tevent_req
*req
=
193 tevent_req_callback_data(subreq
,
195 struct smb2cli_ioctl_state
*state
=
197 struct smb2cli_ioctl_state
);
203 uint32_t dyn_ofs
= SMB2_HDR_BODY
+ 0x30;
204 uint32_t input_buffer_offset
;
205 uint32_t input_buffer_length
;
206 uint32_t output_buffer_offset
;
207 uint32_t output_buffer_length
;
208 static const struct smb2cli_req_expected_response expected
[] = {
210 .status
= NT_STATUS_OK
,
214 .status
= STATUS_BUFFER_OVERFLOW
,
219 * We need to make sure that
220 * a response with NT_STATUS_FILE_CLOSED
221 * without signing generates NT_STATUS_ACCESS_DENIED
222 * if the request was signed.
224 .status
= NT_STATUS_FILE_CLOSED
,
228 .status
= NT_STATUS_INVALID_PARAMETER
,
233 status
= smb2cli_req_recv(subreq
, state
, &iov
,
234 expected
, ARRAY_SIZE(expected
));
236 if (iov
== NULL
&& tevent_req_nterror(req
, status
)) {
240 state
->recv_iov
= iov
;
241 fixed
= (uint8_t *)iov
[1].iov_base
;
242 dyn
= (uint8_t *)iov
[2].iov_base
;
243 dyn_len
= iov
[2].iov_len
;
245 input_buffer_offset
= IVAL(fixed
, 0x18);
246 input_buffer_length
= IVAL(fixed
, 0x1C);
247 output_buffer_offset
= IVAL(fixed
, 0x20);
248 output_buffer_length
= IVAL(fixed
, 0x24);
250 if (smb2cli_ioctl_is_failure(state
->ctl_code
, status
, output_buffer_length
) &&
251 tevent_req_nterror(req
, status
)) {
255 if ((input_buffer_offset
> 0) && (input_buffer_length
> 0)) {
258 if (input_buffer_offset
!= dyn_ofs
) {
260 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
264 ofs
= input_buffer_length
;
265 ofs
= NDR_ROUND(ofs
, 8);
267 if (state
->max_input_length
== 0) {
269 * If max_input_length is 0 we ignore
270 * the input_buffer_length, because
271 * Windows 2008 echos the DCERPC request
272 * from the requested input_buffer
273 * to the response input_buffer.
275 input_buffer_length
= 0;
278 if (input_buffer_length
> dyn_len
) {
280 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
284 if (input_buffer_length
> state
->max_input_length
) {
286 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
290 state
->out_input_buffer
.data
= dyn
;
291 state
->out_input_buffer
.length
= input_buffer_length
;
295 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
304 if ((output_buffer_offset
> 0) && (output_buffer_length
> 0)) {
305 if (output_buffer_offset
!= dyn_ofs
) {
307 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
311 if (state
->max_output_length
== 0) {
313 * We do the same logic as for
316 output_buffer_length
= 0;
319 if (output_buffer_length
> dyn_len
) {
321 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
325 if (output_buffer_length
> state
->max_output_length
) {
327 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
331 state
->out_output_buffer
.data
= dyn
;
332 state
->out_output_buffer
.length
= output_buffer_length
;
335 if (tevent_req_nterror(req
, status
)) {
339 tevent_req_done(req
);
342 NTSTATUS
smb2cli_ioctl_recv(struct tevent_req
*req
,
344 DATA_BLOB
*out_input_buffer
,
345 DATA_BLOB
*out_output_buffer
)
347 struct smb2cli_ioctl_state
*state
=
349 struct smb2cli_ioctl_state
);
350 NTSTATUS status
= NT_STATUS_OK
;
352 if (tevent_req_is_nterror(req
, &status
) &&
353 smb2cli_ioctl_is_failure(state
->ctl_code
, status
, state
->out_output_buffer
.length
)) {
354 tevent_req_received(req
);
358 talloc_steal(mem_ctx
, state
->recv_iov
);
359 if (out_input_buffer
) {
360 *out_input_buffer
= state
->out_input_buffer
;
362 if (out_output_buffer
) {
363 *out_output_buffer
= state
->out_output_buffer
;
366 tevent_req_received(req
);
370 NTSTATUS
smb2cli_ioctl(struct smbXcli_conn
*conn
,
371 uint32_t timeout_msec
,
372 struct smbXcli_session
*session
,
373 struct smbXcli_tcon
*tcon
,
374 uint64_t in_fid_persistent
,
375 uint64_t in_fid_volatile
,
376 uint32_t in_ctl_code
,
377 uint32_t in_max_input_length
,
378 const DATA_BLOB
*in_input_buffer
,
379 uint32_t in_max_output_length
,
380 const DATA_BLOB
*in_output_buffer
,
383 DATA_BLOB
*out_input_buffer
,
384 DATA_BLOB
*out_output_buffer
)
386 TALLOC_CTX
*frame
= talloc_stackframe();
387 struct tevent_context
*ev
;
388 struct tevent_req
*req
;
389 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
391 if (smbXcli_conn_has_async_calls(conn
)) {
393 * Can't use sync call while an async call is in flight
395 status
= NT_STATUS_INVALID_PARAMETER_MIX
;
398 ev
= samba_tevent_context_init(frame
);
402 req
= smb2cli_ioctl_send(frame
, ev
, conn
, timeout_msec
,
409 in_max_output_length
,
415 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
418 status
= smb2cli_ioctl_recv(req
, mem_ctx
,