s3:smbd: implement FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT
authorStefan Metzmacher <metze@samba.org>
Mon, 8 Jun 2020 14:33:45 +0000 (16:33 +0200)
committerStefan Metzmacher <metze@samba.org>
Wed, 8 Jul 2020 15:54:40 +0000 (15:54 +0000)
This will be used by smbtorture in order to simulate channel failures
without relying on iptables.

'smbd:FSCTL_SMBTORTURE = yes' is required in order to active this.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=11897

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Günther Deschner <gd@samba.org>
source3/smbd/globals.h
source3/smbd/smb2_glue.c
source3/smbd/smb2_ioctl.c
source3/smbd/smb2_server.c

index 176c9053f3a42ed56a97879e836d714de8f8b574..968a6a5cab8bb668db2ffc96fb0ce75ee4266bc0 100644 (file)
@@ -375,6 +375,7 @@ struct smbXsrv_connection {
        } transport;
 
        struct {
+               bool force_unacked_timeout;
                uint64_t unacked_bytes;
                uint32_t rto_usecs;
                struct tevent_req *checker_subreq;
index 85f83c462054022976803b6113e810788c88058f..0e3c489f46c50ae7e46b40ab8b506c5db42dda7e 100644 (file)
@@ -41,9 +41,13 @@ struct smb_request *smbd_smb2_fake_smb_request(struct smbd_smb2_request *req)
        }
 
        smbreq->request_time = req->request_time;
-       smbreq->vuid = req->session->global->session_wire_id;
-       smbreq->tid = req->tcon->compat->cnum;
-       smbreq->conn = req->tcon->compat;
+       if (req->session != NULL) {
+               smbreq->vuid = req->session->global->session_wire_id;
+       }
+       if (req->tcon != NULL) {
+               smbreq->tid = req->tcon->compat->cnum;
+               smbreq->conn = req->tcon->compat;
+       }
        smbreq->sconn = req->sconn;
        smbreq->xconn = req->xconn;
        smbreq->session = req->session;
index be70e3a091285e44407394d67a023b7b34787efa..01ae6d64ac583f8bd9d53344c72337db58f31fe8 100644 (file)
@@ -194,6 +194,7 @@ NTSTATUS smbd_smb2_request_process_ioctl(struct smbd_smb2_request *req)
        case FSCTL_VALIDATE_NEGOTIATE_INFO_224:
        case FSCTL_VALIDATE_NEGOTIATE_INFO:
        case FSCTL_QUERY_NETWORK_INTERFACE_INFO:
+       case FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT:
                /*
                 * Some SMB2 specific CtlCodes like FSCTL_DFS_GET_REFERRALS or
                 * FSCTL_PIPE_WAIT does not take a file handle.
@@ -366,6 +367,45 @@ static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq)
        }
 }
 
+static struct tevent_req *smb2_ioctl_smbtorture(uint32_t ctl_code,
+                                       struct tevent_context *ev,
+                                       struct tevent_req *req,
+                                       struct smbd_smb2_ioctl_state *state)
+{
+       NTSTATUS status;
+       bool ok;
+
+       ok = lp_parm_bool(-1, "smbd", "FSCTL_SMBTORTURE", false);
+       if (!ok) {
+               goto not_supported;
+       }
+
+       switch (ctl_code) {
+       case FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT:
+               if (state->in_input.length != 0) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, ev);
+               }
+
+               state->smb2req->xconn->ack.force_unacked_timeout = true;
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+
+       default:
+               goto not_supported;
+       }
+
+not_supported:
+       if (IS_IPC(state->smbreq->conn)) {
+               status = NT_STATUS_FS_DRIVER_REQUIRED;
+       } else {
+               status = NT_STATUS_INVALID_DEVICE_REQUEST;
+       }
+
+       tevent_req_nterror(req, status);
+       return tevent_req_post(req, ev);
+}
+
 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
                                               struct tevent_context *ev,
                                               struct smbd_smb2_request *smb2req,
@@ -415,6 +455,9 @@ static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
        case FSCTL_NETWORK_FILESYSTEM:
                return smb2_ioctl_network_fs(in_ctl_code, ev, req, state);
                break;
+       case FSCTL_SMBTORTURE:
+               return smb2_ioctl_smbtorture(in_ctl_code, ev, req, state);
+               break;
        default:
                if (IS_IPC(smbreq->conn)) {
                        tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);
index 699f293799bf2e2471a395de4ffa2a9a85df85d1..4c26f822bd4d4dd9672cdace5c21001f79f548df 100644 (file)
@@ -1201,6 +1201,17 @@ static NTSTATUS smbXsrv_connection_get_acked_bytes(struct smbXsrv_connection *xc
 
        *_acked_bytes = 0;
 
+       if (xconn->ack.force_unacked_timeout) {
+               /*
+                * Smbtorture tries to test channel failures...
+                * Just pretend nothing was acked...
+                */
+               DBG_INFO("Simulating channel failure: "
+                        "xconn->ack.unacked_bytes[%llu]\n",
+                        (unsigned long long)xconn->ack.unacked_bytes);
+               return NT_STATUS_OK;
+       }
+
 #ifdef __IOCTL_SEND_QUEUE_SIZE_OPCODE
        {
                int value = 0;
@@ -3045,6 +3056,42 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
                }
        } else if (opcode == SMB2_OP_CANCEL) {
                /* Cancel requests are allowed to skip the signing */
+       } else if (opcode == SMB2_OP_IOCTL) {
+               /*
+                * Some special IOCTL calls don't require
+                * file, tcon nor session.
+                *
+                * They typically don't do any real action
+                * on behalf of the client.
+                *
+                * They are mainly used to alter the behavior
+                * of the connection for testing. So we can
+                * run as root and skip all file, tcon and session
+                * checks below.
+                */
+               static const struct smbd_smb2_dispatch_table _root_ioctl_call = {
+                       _OP(SMB2_OP_IOCTL),
+                       .as_root = true,
+               };
+               const uint8_t *body = SMBD_SMB2_IN_BODY_PTR(req);
+               size_t body_size = SMBD_SMB2_IN_BODY_LEN(req);
+               uint32_t in_ctl_code;
+               size_t needed = 4;
+
+               if (needed > body_size) {
+                       return smbd_smb2_request_error(req,
+                                       NT_STATUS_INVALID_PARAMETER);
+               }
+
+               in_ctl_code = IVAL(body, 0x04);
+               /*
+                * Only add trusted IOCTL codes here!
+                */
+               switch (in_ctl_code) {
+               case FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT:
+                       call = &_root_ioctl_call;
+                       break;
+               }
        } else if (signing_required) {
                /*
                 * If signing is required we try to sign