common-lib: Use wrapper for string to integer conversion
[sfrench/samba-autobuild/.git] / libcli / smb / smb2cli_ioctl.c
1 /*
2    Unix SMB/CIFS implementation.
3    smb2 lib
4    Copyright (C) Stefan Metzmacher 2011
5
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.
10
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.
15
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/>.
18 */
19
20 #include "includes.h"
21 #include "system/network.h"
22 #include "lib/util/tevent_ntstatus.h"
23 #include "smb_common.h"
24 #include "smbXcli_base.h"
25
26 struct smb2cli_ioctl_state {
27         uint8_t fixed[0x38];
28         uint8_t dyn_pad[1];
29         uint32_t max_input_length;
30         uint32_t max_output_length;
31         struct iovec *recv_iov;
32         bool out_valid;
33         DATA_BLOB out_input_buffer;
34         DATA_BLOB out_output_buffer;
35         uint32_t ctl_code;
36 };
37
38 static void smb2cli_ioctl_done(struct tevent_req *subreq);
39
40 struct tevent_req *smb2cli_ioctl_send(TALLOC_CTX *mem_ctx,
41                                       struct tevent_context *ev,
42                                       struct smbXcli_conn *conn,
43                                       uint32_t timeout_msec,
44                                       struct smbXcli_session *session,
45                                       struct smbXcli_tcon *tcon,
46                                       uint64_t in_fid_persistent,
47                                       uint64_t in_fid_volatile,
48                                       uint32_t in_ctl_code,
49                                       uint32_t in_max_input_length,
50                                       const DATA_BLOB *in_input_buffer,
51                                       uint32_t in_max_output_length,
52                                       const DATA_BLOB *in_output_buffer,
53                                       uint32_t in_flags)
54 {
55         struct tevent_req *req, *subreq;
56         struct smb2cli_ioctl_state *state;
57         uint8_t *fixed;
58         uint8_t *dyn;
59         size_t dyn_len;
60         uint32_t input_buffer_offset = 0;
61         uint32_t input_buffer_length = 0;
62         uint32_t output_buffer_offset = 0;
63         uint32_t output_buffer_length = 0;
64         uint32_t pad_length = 0;
65         uint64_t tmp64;
66         uint32_t max_dyn_len = 0;
67
68         req = tevent_req_create(mem_ctx, &state,
69                                 struct smb2cli_ioctl_state);
70         if (req == NULL) {
71                 return NULL;
72         }
73         state->ctl_code = in_ctl_code;
74         state->max_input_length = in_max_input_length;
75         state->max_output_length = in_max_output_length;
76
77         tmp64 = in_max_input_length;
78         tmp64 += in_max_output_length;
79         if (tmp64 > UINT32_MAX) {
80                 max_dyn_len = UINT32_MAX;
81         } else {
82                 max_dyn_len = tmp64;
83         }
84
85         if (in_input_buffer) {
86                 input_buffer_offset = SMB2_HDR_BODY+0x38;
87                 input_buffer_length = in_input_buffer->length;
88         }
89
90         if (in_output_buffer) {
91                 output_buffer_offset = SMB2_HDR_BODY+0x38;
92                 output_buffer_length = in_output_buffer->length;
93                 if (input_buffer_length > 0 && output_buffer_length > 0) {
94                         uint32_t tmp;
95                         output_buffer_offset += input_buffer_length;
96                         tmp = output_buffer_offset;
97                         output_buffer_offset = NDR_ROUND(output_buffer_offset, 8);
98                         pad_length = output_buffer_offset - tmp;
99                 }
100         }
101
102         fixed = state->fixed;
103
104         SSVAL(fixed, 0x00, 0x39);
105         SSVAL(fixed, 0x02, 0); /* reserved */
106         SIVAL(fixed, 0x04, in_ctl_code);
107         SBVAL(fixed, 0x08, in_fid_persistent);
108         SBVAL(fixed, 0x10, in_fid_volatile);
109         SIVAL(fixed, 0x18, input_buffer_offset);
110         SIVAL(fixed, 0x1C, input_buffer_length);
111         SIVAL(fixed, 0x20, in_max_input_length);
112         SIVAL(fixed, 0x24, output_buffer_offset);
113         SIVAL(fixed, 0x28, output_buffer_length);
114         SIVAL(fixed, 0x2C, in_max_output_length);
115         SIVAL(fixed, 0x30, in_flags);
116         SIVAL(fixed, 0x34, 0); /* reserved */
117
118         if (input_buffer_length > 0 && output_buffer_length > 0) {
119                 size_t avail = UINT32_MAX - (input_buffer_length + pad_length);
120                 size_t ofs = output_buffer_offset - input_buffer_offset;
121
122                 if (avail < output_buffer_length) {
123                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
124                         return tevent_req_post(req, ev);
125                 }
126
127                 dyn_len = input_buffer_length + output_buffer_length + pad_length;
128
129                 dyn = talloc_zero_array(state, uint8_t, dyn_len);
130                 if (tevent_req_nomem(dyn, req)) {
131                         return tevent_req_post(req, ev);
132                 }
133                 memcpy(dyn, in_input_buffer->data,
134                        in_input_buffer->length);
135                 memcpy(dyn + ofs, in_output_buffer->data,
136                        in_output_buffer->length);
137         } else if (input_buffer_length > 0) {
138                 dyn = in_input_buffer->data;
139                 dyn_len = in_input_buffer->length;
140         } else if (output_buffer_length > 0) {
141                 dyn = in_output_buffer->data;
142                 dyn_len = in_output_buffer->length;
143         } else {
144                 dyn = state->dyn_pad;
145                 dyn_len = sizeof(state->dyn_pad);
146         }
147
148         subreq = smb2cli_req_send(state, ev, conn, SMB2_OP_IOCTL,
149                                   0, 0, /* flags */
150                                   timeout_msec,
151                                   tcon,
152                                   session,
153                                   state->fixed, sizeof(state->fixed),
154                                   dyn, dyn_len,
155                                   max_dyn_len);
156         if (tevent_req_nomem(subreq, req)) {
157                 return tevent_req_post(req, ev);
158         }
159         tevent_req_set_callback(subreq, smb2cli_ioctl_done, req);
160         return req;
161 }
162
163 static void smb2cli_ioctl_done(struct tevent_req *subreq)
164 {
165         struct tevent_req *req =
166                 tevent_req_callback_data(subreq,
167                 struct tevent_req);
168         struct smb2cli_ioctl_state *state =
169                 tevent_req_data(req,
170                 struct smb2cli_ioctl_state);
171         NTSTATUS status;
172         struct iovec *iov;
173         uint8_t *fixed;
174         uint8_t *dyn;
175         size_t dyn_len;
176         uint32_t dyn_ofs = SMB2_HDR_BODY + 0x30;
177         uint32_t input_buffer_offset;
178         uint32_t input_buffer_length;
179         uint32_t output_buffer_offset;
180         uint32_t output_buffer_length;
181         static const struct smb2cli_req_expected_response expected[] = {
182         {
183                 .status = NT_STATUS_OK,
184                 .body_size = 0x31
185         },
186         {
187                 .status = STATUS_BUFFER_OVERFLOW,
188                 .body_size = 0x31
189         },
190         {
191                 /*
192                  * We need to make sure that
193                  * a response with NT_STATUS_FILE_CLOSED
194                  * without signing generates NT_STATUS_ACCESS_DENIED
195                  * if the request was signed.
196                  */
197                 .status = NT_STATUS_FILE_CLOSED,
198                 .body_size = 0x09,
199         },
200         {
201                 /*
202                  * a normal error
203                  */
204                 .status = NT_STATUS_INVALID_PARAMETER,
205                 .body_size = 0x09
206         },
207         {
208                 /*
209                  * a special case for FSCTL_SRV_COPYCHUNK_*
210                  */
211                 .status = NT_STATUS_INVALID_PARAMETER,
212                 .body_size = 0x31
213         },
214         };
215
216         status = smb2cli_req_recv(subreq, state, &iov,
217                                   expected, ARRAY_SIZE(expected));
218         TALLOC_FREE(subreq);
219         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
220                 switch (state->ctl_code) {
221                 case FSCTL_SRV_COPYCHUNK:
222                 case FSCTL_SRV_COPYCHUNK_WRITE:
223                         break;
224                 default:
225                         tevent_req_nterror(req, status);
226                         return;
227                 }
228
229                 if (iov[1].iov_len != 0x30) {
230                         tevent_req_nterror(req,
231                                         NT_STATUS_INVALID_NETWORK_RESPONSE);
232                         return;
233                 }
234         } else if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
235                 /* no error */
236         } else {
237                 if (tevent_req_nterror(req, status)) {
238                         return;
239                 }
240         }
241
242         /*
243          * At this stage we're sure that got a body size of 0x31,
244          * either with NT_STATUS_OK, STATUS_BUFFER_OVERFLOW or
245          * NT_STATUS_INVALID_PARAMETER.
246          */
247
248         state->recv_iov = iov;
249         fixed = (uint8_t *)iov[1].iov_base;
250         dyn = (uint8_t *)iov[2].iov_base;
251         dyn_len = iov[2].iov_len;
252
253         input_buffer_offset = IVAL(fixed, 0x18);
254         input_buffer_length = IVAL(fixed, 0x1C);
255         output_buffer_offset = IVAL(fixed, 0x20);
256         output_buffer_length = IVAL(fixed, 0x24);
257
258         if ((input_buffer_offset > 0) && (input_buffer_length > 0)) {
259                 uint32_t ofs;
260
261                 if (input_buffer_offset != dyn_ofs) {
262                         tevent_req_nterror(
263                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
264                         return;
265                 }
266
267                 ofs = input_buffer_length;
268                 ofs = NDR_ROUND(ofs, 8);
269
270                 if (state->max_input_length == 0) {
271                         /*
272                          * If max_input_length is 0 we ignore
273                          * the input_buffer_length, because
274                          * Windows 2008 echos the DCERPC request
275                          * from the requested input_buffer
276                          * to the response input_buffer.
277                          */
278                         input_buffer_length = 0;
279                 }
280
281                 if (input_buffer_length > dyn_len) {
282                         tevent_req_nterror(
283                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
284                         return;
285                 }
286
287                 if (input_buffer_length > state->max_input_length) {
288                         tevent_req_nterror(
289                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
290                         return;
291                 }
292
293                 state->out_input_buffer.data = dyn;
294                 state->out_input_buffer.length = input_buffer_length;
295
296                 if (ofs > dyn_len) {
297                         tevent_req_nterror(
298                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
299                         return;
300                 }
301
302                 dyn_ofs += ofs;
303                 dyn += ofs;
304                 dyn_len -= ofs;
305         }
306
307         if ((output_buffer_offset > 0) && (output_buffer_length > 0)) {
308                 if (output_buffer_offset != dyn_ofs) {
309                         tevent_req_nterror(
310                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
311                         return;
312                 }
313
314                 if (state->max_output_length == 0) {
315                         /*
316                          * We do the same logic as for
317                          * max_input_length.
318                          */
319                         output_buffer_length = 0;
320                 }
321
322                 if (output_buffer_length > dyn_len) {
323                         tevent_req_nterror(
324                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
325                         return;
326                 }
327
328                 if (output_buffer_length > state->max_output_length) {
329                         tevent_req_nterror(
330                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
331                         return;
332                 }
333
334                 state->out_output_buffer.data = dyn;
335                 state->out_output_buffer.length = output_buffer_length;
336         }
337
338         state->out_valid = true;
339
340         if (tevent_req_nterror(req, status)) {
341                 return;
342         }
343
344         tevent_req_done(req);
345 }
346
347 NTSTATUS smb2cli_ioctl_recv(struct tevent_req *req,
348                             TALLOC_CTX *mem_ctx,
349                             DATA_BLOB *out_input_buffer,
350                             DATA_BLOB *out_output_buffer)
351 {
352         struct smb2cli_ioctl_state *state =
353                 tevent_req_data(req,
354                 struct smb2cli_ioctl_state);
355         NTSTATUS status = NT_STATUS_OK;
356
357         if (tevent_req_is_nterror(req, &status) && !state->out_valid) {
358                 if (out_input_buffer) {
359                         *out_input_buffer = data_blob_null;
360                 }
361                 if (out_output_buffer) {
362                         *out_output_buffer = data_blob_null;
363                 }
364                 tevent_req_received(req);
365                 return status;
366         }
367
368         talloc_steal(mem_ctx, state->recv_iov);
369         if (out_input_buffer) {
370                 *out_input_buffer = state->out_input_buffer;
371         }
372         if (out_output_buffer) {
373                 *out_output_buffer = state->out_output_buffer;
374         }
375
376         tevent_req_received(req);
377         return status;
378 }
379
380 NTSTATUS smb2cli_ioctl(struct smbXcli_conn *conn,
381                        uint32_t timeout_msec,
382                        struct smbXcli_session *session,
383                        struct smbXcli_tcon *tcon,
384                        uint64_t in_fid_persistent,
385                        uint64_t in_fid_volatile,
386                        uint32_t in_ctl_code,
387                        uint32_t in_max_input_length,
388                        const DATA_BLOB *in_input_buffer,
389                        uint32_t in_max_output_length,
390                        const DATA_BLOB *in_output_buffer,
391                        uint32_t in_flags,
392                        TALLOC_CTX *mem_ctx,
393                        DATA_BLOB *out_input_buffer,
394                        DATA_BLOB *out_output_buffer)
395 {
396         TALLOC_CTX *frame = talloc_stackframe();
397         struct tevent_context *ev;
398         struct tevent_req *req;
399         NTSTATUS status = NT_STATUS_NO_MEMORY;
400
401         if (smbXcli_conn_has_async_calls(conn)) {
402                 /*
403                  * Can't use sync call while an async call is in flight
404                  */
405                 status = NT_STATUS_INVALID_PARAMETER_MIX;
406                 goto fail;
407         }
408         ev = samba_tevent_context_init(frame);
409         if (ev == NULL) {
410                 goto fail;
411         }
412         req = smb2cli_ioctl_send(frame, ev, conn, timeout_msec,
413                                  session, tcon,
414                                  in_fid_persistent,
415                                  in_fid_volatile,
416                                  in_ctl_code,
417                                  in_max_input_length,
418                                  in_input_buffer,
419                                  in_max_output_length,
420                                  in_output_buffer,
421                                  in_flags);
422         if (req == NULL) {
423                 goto fail;
424         }
425         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
426                 goto fail;
427         }
428         status = smb2cli_ioctl_recv(req, mem_ctx,
429                                     out_input_buffer,
430                                     out_output_buffer);
431  fail:
432         TALLOC_FREE(frame);
433         return status;
434 }