s3:smb2_ioctl: Fix Coverity ID 701771 Uninitialized scalar variable
[samba.git] / source3 / smbd / smb2_ioctl.c
1 /*
2    Unix SMB/CIFS implementation.
3    Core SMB2 server
4
5    Copyright (C) Stefan Metzmacher 2009
6
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.
11
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.
16
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/>.
19 */
20
21 #include "includes.h"
22 #include "smbd/smbd.h"
23 #include "smbd/globals.h"
24 #include "../libcli/smb/smb_common.h"
25 #include "../lib/util/tevent_ntstatus.h"
26 #include "rpc_server/srv_pipe_hnd.h"
27 #include "include/ntioctl.h"
28 #include "../librpc/ndr/libndr.h"
29
30 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
31                                                struct tevent_context *ev,
32                                                struct smbd_smb2_request *smb2req,
33                                                uint32_t in_ctl_code,
34                                                uint64_t in_file_id_volatile,
35                                                DATA_BLOB in_input,
36                                                uint32_t in_max_output,
37                                                uint32_t in_flags);
38 static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req,
39                                      TALLOC_CTX *mem_ctx,
40                                      DATA_BLOB *out_output,
41                                      bool *disconnect);
42
43 static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq);
44 NTSTATUS smbd_smb2_request_process_ioctl(struct smbd_smb2_request *req)
45 {
46         NTSTATUS status;
47         const uint8_t *inbody;
48         int i = req->current_idx;
49         uint32_t in_ctl_code;
50         uint64_t in_file_id_persistent;
51         uint64_t in_file_id_volatile;
52         uint32_t in_input_offset;
53         uint32_t in_input_length;
54         DATA_BLOB in_input_buffer;
55         uint32_t in_max_output_length;
56         uint32_t in_flags;
57         struct tevent_req *subreq;
58
59         status = smbd_smb2_request_verify_sizes(req, 0x39);
60         if (!NT_STATUS_IS_OK(status)) {
61                 return smbd_smb2_request_error(req, status);
62         }
63         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
64
65         in_ctl_code             = IVAL(inbody, 0x04);
66         in_file_id_persistent   = BVAL(inbody, 0x08);
67         in_file_id_volatile     = BVAL(inbody, 0x10);
68         in_input_offset         = IVAL(inbody, 0x18);
69         in_input_length         = IVAL(inbody, 0x1C);
70         in_max_output_length    = IVAL(inbody, 0x2C);
71         in_flags                = IVAL(inbody, 0x30);
72
73         /*
74          * InputOffset (4 bytes): The offset, in bytes, from the beginning of
75          * the SMB2 header to the input data buffer. If no input data is
76          * required for the FSCTL/IOCTL command being issued, the client SHOULD
77          * set this value to 0.<49>
78          * <49> If no input data is required for the FSCTL/IOCTL command being
79          * issued, Windows-based clients set this field to any value.
80          */
81         if ((in_input_length > 0)
82          && (in_input_offset != (SMB2_HDR_BODY + req->in.vector[i+1].iov_len))) {
83                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
84         }
85
86         if (in_input_length > req->in.vector[i+2].iov_len) {
87                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
88         }
89
90         in_input_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base;
91         in_input_buffer.length = in_input_length;
92
93         if (req->compat_chain_fsp) {
94                 /* skip check */
95         } else if (in_file_id_persistent == UINT64_MAX &&
96                    in_file_id_volatile == UINT64_MAX) {
97                 /* without a handle */
98         } else if (in_file_id_persistent != in_file_id_volatile) {
99                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
100         }
101
102         subreq = smbd_smb2_ioctl_send(req,
103                                       req->sconn->ev_ctx,
104                                       req,
105                                       in_ctl_code,
106                                       in_file_id_volatile,
107                                       in_input_buffer,
108                                       in_max_output_length,
109                                       in_flags);
110         if (subreq == NULL) {
111                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
112         }
113         tevent_req_set_callback(subreq, smbd_smb2_request_ioctl_done, req);
114
115         return smbd_smb2_request_pending_queue(req, subreq, 1000);
116 }
117
118 static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq)
119 {
120         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
121                                         struct smbd_smb2_request);
122         const uint8_t *inbody;
123         int i = req->current_idx;
124         DATA_BLOB outbody;
125         DATA_BLOB outdyn;
126         uint32_t in_ctl_code;
127         uint64_t in_file_id_persistent;
128         uint64_t in_file_id_volatile;
129         uint32_t out_input_offset;
130         uint32_t out_output_offset;
131         DATA_BLOB out_output_buffer = data_blob_null;
132         NTSTATUS status;
133         NTSTATUS error; /* transport error */
134         bool disconnect = false;
135
136         status = smbd_smb2_ioctl_recv(subreq, req,
137                                       &out_output_buffer,
138                                       &disconnect);
139
140         DEBUG(10,("smbd_smb2_request_ioctl_done: smbd_smb2_ioctl_recv returned "
141                 "%u status %s\n",
142                 (unsigned int)out_output_buffer.length,
143                 nt_errstr(status) ));
144
145         TALLOC_FREE(subreq);
146         if (disconnect) {
147                 error = status;
148                 smbd_server_connection_terminate(req->sconn,
149                                                  nt_errstr(error));
150                 return;
151         }
152
153         if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
154                 /* also ok */
155         } else if (!NT_STATUS_IS_OK(status)) {
156                 error = smbd_smb2_request_error(req, status);
157                 if (!NT_STATUS_IS_OK(error)) {
158                         smbd_server_connection_terminate(req->sconn,
159                                                          nt_errstr(error));
160                         return;
161                 }
162                 return;
163         }
164
165         out_input_offset = SMB2_HDR_BODY + 0x30;
166         out_output_offset = SMB2_HDR_BODY + 0x30;
167
168         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
169
170         in_ctl_code             = IVAL(inbody, 0x04);
171         in_file_id_persistent   = BVAL(inbody, 0x08);
172         in_file_id_volatile     = BVAL(inbody, 0x10);
173
174         outbody = data_blob_talloc(req->out.vector, NULL, 0x30);
175         if (outbody.data == NULL) {
176                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
177                 if (!NT_STATUS_IS_OK(error)) {
178                         smbd_server_connection_terminate(req->sconn,
179                                                          nt_errstr(error));
180                         return;
181                 }
182                 return;
183         }
184
185         SSVAL(outbody.data, 0x00, 0x30 + 1);    /* struct size */
186         SSVAL(outbody.data, 0x02, 0);           /* reserved */
187         SIVAL(outbody.data, 0x04,
188               in_ctl_code);                     /* ctl code */
189         SBVAL(outbody.data, 0x08,
190               in_file_id_persistent);           /* file id (persistent) */
191         SBVAL(outbody.data, 0x10,
192               in_file_id_volatile);             /* file id (volatile) */
193         SIVAL(outbody.data, 0x18,
194               out_input_offset);                /* input offset */
195         SIVAL(outbody.data, 0x1C, 0);           /* input count */
196         SIVAL(outbody.data, 0x20,
197               out_output_offset);               /* output offset */
198         SIVAL(outbody.data, 0x24,
199               out_output_buffer.length);        /* output count */
200         SIVAL(outbody.data, 0x28, 0);           /* flags */
201         SIVAL(outbody.data, 0x2C, 0);           /* reserved */
202
203         /*
204          * Note: Windows Vista and 2008 send back also the
205          *       input from the request. But it was fixed in
206          *       Windows 7.
207          */
208         outdyn = out_output_buffer;
209
210         error = smbd_smb2_request_done_ex(req, status, outbody, &outdyn,
211                                           __location__);
212         if (!NT_STATUS_IS_OK(error)) {
213                 smbd_server_connection_terminate(req->sconn,
214                                                  nt_errstr(error));
215                 return;
216         }
217 }
218
219 struct smbd_smb2_ioctl_state {
220         struct smbd_smb2_request *smb2req;
221         struct smb_request *smbreq;
222         files_struct *fsp;
223         DATA_BLOB in_input;
224         uint32_t in_max_output;
225         DATA_BLOB out_output;
226         bool disconnect;
227 };
228
229 static void smbd_smb2_ioctl_pipe_write_done(struct tevent_req *subreq);
230 static void smbd_smb2_ioctl_pipe_read_done(struct tevent_req *subreq);
231
232 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
233                                                struct tevent_context *ev,
234                                                struct smbd_smb2_request *smb2req,
235                                                uint32_t in_ctl_code,
236                                                uint64_t in_file_id_volatile,
237                                                DATA_BLOB in_input,
238                                                uint32_t in_max_output,
239                                                uint32_t in_flags)
240 {
241         struct tevent_req *req;
242         struct smbd_smb2_ioctl_state *state;
243         struct smb_request *smbreq;
244         files_struct *fsp = NULL;
245         struct tevent_req *subreq;
246
247         req = tevent_req_create(mem_ctx, &state,
248                                 struct smbd_smb2_ioctl_state);
249         if (req == NULL) {
250                 return NULL;
251         }
252         state->smb2req = smb2req;
253         state->smbreq = NULL;
254         state->fsp = NULL;
255         state->in_input = in_input;
256         state->in_max_output = in_max_output;
257         state->out_output = data_blob_null;
258
259         DEBUG(10, ("smbd_smb2_ioctl: ctl_code[0x%08x] file_id[0x%016llX]\n",
260                    (unsigned)in_ctl_code,
261                    (unsigned long long)in_file_id_volatile));
262
263         smbreq = smbd_smb2_fake_smb_request(smb2req);
264         if (tevent_req_nomem(smbreq, req)) {
265                 return tevent_req_post(req, ev);
266         }
267         state->smbreq = smbreq;
268
269         if (in_file_id_volatile != UINT64_MAX) {
270                 fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile);
271                 if (fsp == NULL) {
272                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
273                         return tevent_req_post(req, ev);
274                 }
275                 if (smbreq->conn != fsp->conn) {
276                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
277                         return tevent_req_post(req, ev);
278                 }
279                 if (smb2req->session->vuid != fsp->vuid) {
280                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
281                         return tevent_req_post(req, ev);
282                 }
283                 state->fsp = fsp;
284         }
285
286         switch (in_ctl_code) {
287         case 0x00060194: /* FSCTL_DFS_GET_REFERRALS */
288         {
289                 uint16_t in_max_referral_level;
290                 DATA_BLOB in_file_name_buffer;
291                 char *in_file_name_string;
292                 size_t in_file_name_string_size;
293                 bool ok;
294                 bool overflow = false;
295                 NTSTATUS status;
296                 int dfs_size;
297                 char *dfs_data = NULL;
298
299                 if (!IS_IPC(smbreq->conn)) {
300                         tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
301                         return tevent_req_post(req, ev);
302                 }
303
304                 if (!lp_host_msdfs()) {
305                         tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);
306                         return tevent_req_post(req, ev);
307                 }
308
309                 if (in_input.length < (2 + 2)) {
310                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
311                         return tevent_req_post(req, ev);
312                 }
313
314                 in_max_referral_level = SVAL(in_input.data, 0);
315                 in_file_name_buffer.data = in_input.data + 2;
316                 in_file_name_buffer.length = in_input.length - 2;
317
318                 ok = convert_string_talloc(state, CH_UTF16, CH_UNIX,
319                                            in_file_name_buffer.data,
320                                            in_file_name_buffer.length,
321                                            &in_file_name_string,
322                                            &in_file_name_string_size);
323                 if (!ok) {
324                         tevent_req_nterror(req, NT_STATUS_ILLEGAL_CHARACTER);
325                         return tevent_req_post(req, ev);
326                 }
327
328                 dfs_size = setup_dfs_referral(smbreq->conn,
329                                               in_file_name_string,
330                                               in_max_referral_level,
331                                               &dfs_data, &status);
332                 if (dfs_size < 0) {
333                         tevent_req_nterror(req, status);
334                         return tevent_req_post(req, ev);
335                 }
336
337                 if (dfs_size > in_max_output) {
338                         /*
339                          * TODO: we need a testsuite for this
340                          */
341                         overflow = true;
342                         dfs_size = in_max_output;
343                 }
344
345                 state->out_output = data_blob_talloc(state,
346                                                      (uint8_t *)dfs_data,
347                                                      dfs_size);
348                 SAFE_FREE(dfs_data);
349                 if (dfs_size > 0 &&
350                     tevent_req_nomem(state->out_output.data, req)) {
351                         return tevent_req_post(req, ev);
352                 }
353
354                 if (overflow) {
355                         tevent_req_nterror(req, STATUS_BUFFER_OVERFLOW);
356                 } else {
357                         tevent_req_done(req);
358                 }
359                 return tevent_req_post(req, ev);
360         }
361         case 0x0011C017: /* FSCTL_PIPE_TRANSCEIVE */
362
363                 if (!IS_IPC(smbreq->conn)) {
364                         tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
365                         return tevent_req_post(req, ev);
366                 }
367
368                 if (fsp == NULL) {
369                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
370                         return tevent_req_post(req, ev);
371                 }
372
373                 if (!fsp_is_np(fsp)) {
374                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
375                         return tevent_req_post(req, ev);
376                 }
377
378                 DEBUG(10,("smbd_smb2_ioctl_send: np_write_send of size %u\n",
379                         (unsigned int)in_input.length ));
380
381                 subreq = np_write_send(state, ev,
382                                        fsp->fake_file_handle,
383                                        in_input.data,
384                                        in_input.length);
385                 if (tevent_req_nomem(subreq, req)) {
386                         return tevent_req_post(req, ev);
387                 }
388                 tevent_req_set_callback(subreq,
389                                         smbd_smb2_ioctl_pipe_write_done,
390                                         req);
391                 return req;
392
393         case FSCTL_VALIDATE_NEGOTIATE_INFO_224:
394         {
395                 struct smbXsrv_connection *conn = smbreq->sconn->conn;
396                 uint32_t in_capabilities;
397                 DATA_BLOB in_guid_blob;
398                 struct GUID in_guid;
399                 uint16_t in_security_mode;
400                 uint16_t in_max_dialect;
401                 uint16_t max_dialect;
402                 DATA_BLOB out_guid_blob;
403                 NTSTATUS status;
404
405                 if (in_input.length != 0x18) {
406                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
407                         return tevent_req_post(req, ev);
408                 }
409
410                 if (in_max_output < 0x18) {
411                         tevent_req_nterror(req, NT_STATUS_BUFFER_TOO_SMALL);
412                         return tevent_req_post(req, ev);
413                 }
414
415                 in_capabilities = IVAL(in_input.data, 0x00);
416                 in_guid_blob = data_blob_const(in_input.data + 0x04, 16);
417                 in_security_mode = SVAL(in_input.data, 0x14);
418                 in_max_dialect = SVAL(in_input.data, 0x16);
419
420                 status = GUID_from_ndr_blob(&in_guid_blob, &in_guid);
421                 if (tevent_req_nterror(req, status)) {
422                         return tevent_req_post(req, ev);
423                 }
424
425                 max_dialect = conn->smb2.client.dialects[conn->smb2.client.num_dialects-1];
426                 if (in_max_dialect != max_dialect) {
427                         state->disconnect = true;
428                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
429                         return tevent_req_post(req, ev);
430                 }
431
432                 if (!GUID_compare(&in_guid, &conn->smb2.client.guid)) {
433                         state->disconnect = true;
434                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
435                         return tevent_req_post(req, ev);
436                 }
437
438                 if (in_security_mode != conn->smb2.client.security_mode) {
439                         state->disconnect = true;
440                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
441                         return tevent_req_post(req, ev);
442                 }
443
444                 if (in_capabilities != conn->smb2.client.capabilities) {
445                         state->disconnect = true;
446                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
447                         return tevent_req_post(req, ev);
448                 }
449
450                 status = GUID_to_ndr_blob(&conn->smb2.server.guid, state,
451                                           &out_guid_blob);
452                 if (tevent_req_nterror(req, status)) {
453                         return tevent_req_post(req, ev);
454                 }
455
456                 state->out_output = data_blob_talloc(state, NULL, 0x18);
457                 if (tevent_req_nomem(state->out_output.data, req)) {
458                         return tevent_req_post(req, ev);
459                 }
460
461                 SIVAL(state->out_output.data, 0x00, conn->smb2.server.capabilities);
462                 memcpy(state->out_output.data+0x04, out_guid_blob.data, 16);
463                 SIVAL(state->out_output.data, 0x14, conn->smb2.server.security_mode);
464                 SIVAL(state->out_output.data, 0x16, conn->smb2.server.dialect);
465
466                 tevent_req_done(req);
467                 return tevent_req_post(req, ev);
468         }
469
470         case FSCTL_VALIDATE_NEGOTIATE_INFO:
471         {
472                 struct smbXsrv_connection *conn = smbreq->sconn->conn;
473                 uint32_t in_capabilities;
474                 DATA_BLOB in_guid_blob;
475                 struct GUID in_guid;
476                 uint16_t in_security_mode;
477                 uint16_t in_num_dialects;
478                 uint16_t i;
479                 DATA_BLOB out_guid_blob;
480                 NTSTATUS status;
481
482                 if (in_input.length < 0x18) {
483                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
484                         return tevent_req_post(req, ev);
485                 }
486
487                 in_capabilities = IVAL(in_input.data, 0x00);
488                 in_guid_blob = data_blob_const(in_input.data + 0x04, 16);
489                 in_security_mode = SVAL(in_input.data, 0x14);
490                 in_num_dialects = SVAL(in_input.data, 0x16);
491
492                 if (in_input.length != (0x18 + in_num_dialects*2)) {
493                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
494                         return tevent_req_post(req, ev);
495                 }
496
497                 if (in_max_output < 0x18) {
498                         tevent_req_nterror(req, NT_STATUS_BUFFER_TOO_SMALL);
499                         return tevent_req_post(req, ev);
500                 }
501
502                 status = GUID_from_ndr_blob(&in_guid_blob, &in_guid);
503                 if (tevent_req_nterror(req, status)) {
504                         return tevent_req_post(req, ev);
505                 }
506
507                 if (in_num_dialects != conn->smb2.client.num_dialects) {
508                         state->disconnect = true;
509                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
510                         return tevent_req_post(req, ev);
511                 }
512
513                 for (i=0; i < in_num_dialects; i++) {
514                         uint16_t v = SVAL(in_input.data, 0x18 + i*2);
515
516                         if (conn->smb2.client.dialects[i] != v) {
517                                 state->disconnect = true;
518                                 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
519                                 return tevent_req_post(req, ev);
520                         }
521                 }
522
523                 if (!GUID_compare(&in_guid, &conn->smb2.client.guid)) {
524                         state->disconnect = true;
525                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
526                         return tevent_req_post(req, ev);
527                 }
528
529                 if (in_security_mode != conn->smb2.client.security_mode) {
530                         state->disconnect = true;
531                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
532                         return tevent_req_post(req, ev);
533                 }
534
535                 if (in_capabilities != conn->smb2.client.capabilities) {
536                         state->disconnect = true;
537                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
538                         return tevent_req_post(req, ev);
539                 }
540
541                 status = GUID_to_ndr_blob(&conn->smb2.server.guid, state,
542                                           &out_guid_blob);
543                 if (tevent_req_nterror(req, status)) {
544                         return tevent_req_post(req, ev);
545                 }
546
547                 state->out_output = data_blob_talloc(state, NULL, 0x18);
548                 if (tevent_req_nomem(state->out_output.data, req)) {
549                         return tevent_req_post(req, ev);
550                 }
551
552                 SIVAL(state->out_output.data, 0x00, conn->smb2.server.capabilities);
553                 memcpy(state->out_output.data+0x04, out_guid_blob.data, 16);
554                 SIVAL(state->out_output.data, 0x14, conn->smb2.server.security_mode);
555                 SIVAL(state->out_output.data, 0x16, conn->smb2.server.dialect);
556
557                 tevent_req_done(req);
558                 return tevent_req_post(req, ev);
559         }
560
561         default: {
562                 uint8_t *out_data = NULL;
563                 uint32_t out_data_len = 0;
564                 NTSTATUS status;
565
566                 if (fsp == NULL) {
567                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
568                         return tevent_req_post(req, ev);
569                 }
570
571                 status = SMB_VFS_FSCTL(fsp,
572                                        state,
573                                        in_ctl_code,
574                                        smbreq->flags2,
575                                        in_input.data,
576                                        in_input.length,
577                                        &out_data,
578                                        in_max_output,
579                                        &out_data_len);
580                 state->out_output = data_blob_const(out_data, out_data_len);
581                 if (NT_STATUS_IS_OK(status)) {
582                         tevent_req_done(req);
583                         return tevent_req_post(req, ev);
584                 }
585
586                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
587                         if (IS_IPC(smbreq->conn)) {
588                                 status = NT_STATUS_FS_DRIVER_REQUIRED;
589                         } else {
590                                 status = NT_STATUS_INVALID_DEVICE_REQUEST;
591                         }
592                 }
593
594                 tevent_req_nterror(req, status);
595                 return tevent_req_post(req, ev);
596         }
597         }
598
599         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
600         return tevent_req_post(req, ev);
601 }
602
603 static void smbd_smb2_ioctl_pipe_write_done(struct tevent_req *subreq)
604 {
605         struct tevent_req *req = tevent_req_callback_data(subreq,
606                                  struct tevent_req);
607         struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
608                                               struct smbd_smb2_ioctl_state);
609         NTSTATUS status;
610         ssize_t nwritten = -1;
611
612         status = np_write_recv(subreq, &nwritten);
613
614         DEBUG(10,("smbd_smb2_ioctl_pipe_write_done: received %ld\n",
615                 (long int)nwritten ));
616
617         TALLOC_FREE(subreq);
618         if (!NT_STATUS_IS_OK(status)) {
619                 tevent_req_nterror(req, status);
620                 return;
621         }
622
623         if (nwritten != state->in_input.length) {
624                 tevent_req_nterror(req, NT_STATUS_PIPE_NOT_AVAILABLE);
625                 return;
626         }
627
628         state->out_output = data_blob_talloc(state, NULL, state->in_max_output);
629         if (state->in_max_output > 0 &&
630             tevent_req_nomem(state->out_output.data, req)) {
631                 return;
632         }
633
634         DEBUG(10,("smbd_smb2_ioctl_pipe_write_done: issuing np_read_send "
635                 "of size %u\n",
636                 (unsigned int)state->out_output.length ));
637
638         TALLOC_FREE(subreq);
639         subreq = np_read_send(state->smbreq->conn,
640                               state->smb2req->sconn->ev_ctx,
641                               state->fsp->fake_file_handle,
642                               state->out_output.data,
643                               state->out_output.length);
644         if (tevent_req_nomem(subreq, req)) {
645                 return;
646         }
647         tevent_req_set_callback(subreq, smbd_smb2_ioctl_pipe_read_done, req);
648 }
649
650 static void smbd_smb2_ioctl_pipe_read_done(struct tevent_req *subreq)
651 {
652         struct tevent_req *req = tevent_req_callback_data(subreq,
653                                  struct tevent_req);
654         struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
655                                               struct smbd_smb2_ioctl_state);
656         NTSTATUS status;
657         ssize_t nread = -1;
658         bool is_data_outstanding = false;
659
660         status = np_read_recv(subreq, &nread, &is_data_outstanding);
661
662         DEBUG(10,("smbd_smb2_ioctl_pipe_read_done: np_read_recv nread = %d "
663                  "is_data_outstanding = %d, status = %s\n",
664                 (int)nread,
665                 (int)is_data_outstanding,
666                 nt_errstr(status) ));
667
668         TALLOC_FREE(subreq);
669         if (!NT_STATUS_IS_OK(status)) {
670                 tevent_req_nterror(req, status);
671                 return;
672         }
673
674         state->out_output.length = nread;
675
676         if (is_data_outstanding) {
677                 tevent_req_nterror(req, STATUS_BUFFER_OVERFLOW);
678                 return;
679         }
680
681         tevent_req_done(req);
682 }
683
684 static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req,
685                                      TALLOC_CTX *mem_ctx,
686                                      DATA_BLOB *out_output,
687                                      bool *disconnect)
688 {
689         NTSTATUS status = NT_STATUS_OK;
690         struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
691                                               struct smbd_smb2_ioctl_state);
692
693         *disconnect = state->disconnect;
694
695         if (tevent_req_is_nterror(req, &status)) {
696                 if (!NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
697                         tevent_req_received(req);
698                         return status;
699                 }
700         }
701
702         *out_output = state->out_output;
703         talloc_steal(mem_ctx, out_output->data);
704
705         tevent_req_received(req);
706         return status;
707 }