s3:smbd: FSCTL_PIPE_TRANSCEIVE on a none IPC$ share should give NOT_SUPPORTED
[ira/wip.git] / source3 / smbd / smb2_ioctl.c
index e7a3d358bbaf0ddf214987f2414f689892807941..9ff6c8ea979436fe180444f597ffab4beed761a7 100644 (file)
@@ -109,10 +109,15 @@ static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq)
 {
        struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
                                        struct smbd_smb2_request);
+       const uint8_t *inbody;
        int i = req->current_idx;
        uint8_t *outhdr;
        DATA_BLOB outbody;
        DATA_BLOB outdyn;
+       uint32_t in_ctl_code;
+       uint64_t in_file_id_persistent;
+       uint64_t in_file_id_volatile;
+       uint32_t out_input_offset;
        uint32_t out_output_offset;
        DATA_BLOB out_output_buffer;
        NTSTATUS status;
@@ -120,7 +125,9 @@ static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq)
 
        status = smbd_smb2_ioctl_recv(subreq, req, &out_output_buffer);
        TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
+       if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+               /* also ok */
+       } else if (!NT_STATUS_IS_OK(status)) {
                error = smbd_smb2_request_error(req, status);
                if (!NT_STATUS_IS_OK(error)) {
                        smbd_server_connection_terminate(req->conn,
@@ -130,8 +137,15 @@ static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq)
                return;
        }
 
+       out_input_offset = SMB2_HDR_BODY + 0x30;
        out_output_offset = SMB2_HDR_BODY + 0x30;
 
+       inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
+
+       in_ctl_code             = IVAL(inbody, 0x04);
+       in_file_id_persistent   = BVAL(inbody, 0x08);
+       in_file_id_volatile     = BVAL(inbody, 0x10);
+
        outhdr = (uint8_t *)req->out.vector[i].iov_base;
 
        outbody = data_blob_talloc(req->out.vector, NULL, 0x30);
@@ -147,10 +161,14 @@ static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq)
 
        SSVAL(outbody.data, 0x00, 0x30 + 1);    /* struct size */
        SSVAL(outbody.data, 0x02, 0);           /* reserved */
-       SIVAL(outbody.data, 0x04, 0);           /* ctl code */
-       SBVAL(outbody.data, 0x08, 0);           /* file id (persistent) */
-       SBVAL(outbody.data, 0x10, 0);           /* file id (volatile) */
-       SIVAL(outbody.data, 0x18, 0);           /* input offset */
+       SIVAL(outbody.data, 0x04,
+             in_ctl_code);                     /* ctl code */
+       SBVAL(outbody.data, 0x08,
+             in_file_id_persistent);           /* file id (persistent) */
+       SBVAL(outbody.data, 0x10,
+             in_file_id_volatile);             /* file id (volatile) */
+       SIVAL(outbody.data, 0x18,
+             out_input_offset);                /* input offset */
        SIVAL(outbody.data, 0x1C, 0);           /* input count */
        SIVAL(outbody.data, 0x20,
              out_output_offset);               /* output offset */
@@ -160,13 +178,14 @@ static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq)
        SIVAL(outbody.data, 0x2C, 0);           /* reserved */
 
        /*
-        * Note: Windows sends back also the input from the request.
-        *       I think this is stupid and I hope not required.
-        *       For now we avoid a talloc + memcopy here...
+        * Note: Windows Vista and 2008 send back also the
+        *       input from the request. But it was fixed in
+        *       Windows 7.
         */
        outdyn = out_output_buffer;
 
-       error = smbd_smb2_request_done(req, outbody, &outdyn);
+       error = smbd_smb2_request_done_ex(req, status, outbody, &outdyn,
+                                         __location__);
        if (!NT_STATUS_IS_OK(error)) {
                smbd_server_connection_terminate(req->conn,
                                                 nt_errstr(error));
@@ -240,13 +259,87 @@ static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
        }
 
        switch (in_ctl_code) {
-       case 0x0011C017: /* FSCTL_PIPE_TRANSCEIVE */
+       case 0x00060194: /* FSCTL_DFS_GET_REFERRALS */
+       {
+               uint16_t in_max_referral_level;
+               DATA_BLOB in_file_name_buffer;
+               char *in_file_name_string;
+               size_t in_file_name_string_size;
+               bool ok;
+               bool overflow = false;
+               NTSTATUS status;
+               int dfs_size;
+               char *dfs_data = NULL;
 
                if (!IS_IPC(smbreq->conn)) {
                        tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
                        return tevent_req_post(req, ev);
                }
 
+               if (!lp_host_msdfs()) {
+                       tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);
+                       return tevent_req_post(req, ev);
+               }
+
+               if (in_input.length < (2 + 2)) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, ev);
+               }
+
+               in_max_referral_level = SVAL(in_input.data, 0);
+               in_file_name_buffer.data = in_input.data + 2;
+               in_file_name_buffer.length = in_input.length - 2;
+
+               ok = convert_string_talloc(state, CH_UTF16, CH_UNIX,
+                                          in_file_name_buffer.data,
+                                          in_file_name_buffer.length,
+                                          &in_file_name_string,
+                                          &in_file_name_string_size, false);
+               if (!ok) {
+                       tevent_req_nterror(req, NT_STATUS_ILLEGAL_CHARACTER);
+                       return tevent_req_post(req, ev);
+               }
+
+               dfs_size = setup_dfs_referral(smbreq->conn,
+                                             in_file_name_string,
+                                             in_max_referral_level,
+                                             &dfs_data, &status);
+               if (dfs_size < 0) {
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, ev);
+               }
+
+               if (dfs_size > in_max_output) {
+                       /*
+                        * TODO: we need a testsuite for this
+                        */
+                       overflow = true;
+                       dfs_size = in_max_output;
+               }
+
+               state->out_output = data_blob_talloc(state,
+                                                    (uint8_t *)dfs_data,
+                                                    dfs_size);
+               SAFE_FREE(dfs_data);
+               if (dfs_size > 0 &&
+                   tevent_req_nomem(state->out_output.data, req)) {
+                       return tevent_req_post(req, ev);
+               }
+
+               if (overflow) {
+                       tevent_req_nterror(req, STATUS_BUFFER_OVERFLOW);
+               } else {
+                       tevent_req_done(req);
+               }
+               return tevent_req_post(req, ev);
+       }
+       case 0x0011C017: /* FSCTL_PIPE_TRANSCEIVE */
+
+               if (!IS_IPC(smbreq->conn)) {
+                       tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
+                       return tevent_req_post(req, ev);
+               }
+
                if (fsp == NULL) {
                        tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
                        return tevent_req_post(req, ev);
@@ -351,13 +444,15 @@ static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req,
                                              struct smbd_smb2_ioctl_state);
 
        if (tevent_req_is_nterror(req, &status)) {
-               tevent_req_received(req);
-               return status;
+               if (!NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+                       tevent_req_received(req);
+                       return status;
+               }
        }
 
        *out_output = state->out_output;
        talloc_steal(mem_ctx, out_output->data);
 
        tevent_req_received(req);
-       return NT_STATUS_OK;
+       return status;
 }