From bd1285d40d0671a0ba80b4143a871426f369a926 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 8 Jun 2020 16:33:45 +0200 Subject: [PATCH] s3:smbd: implement FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Günther Deschner --- source3/smbd/globals.h | 1 + source3/smbd/smb2_glue.c | 10 +++++--- source3/smbd/smb2_ioctl.c | 43 ++++++++++++++++++++++++++++++++++ source3/smbd/smb2_server.c | 47 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 3 deletions(-) diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index 176c9053f3a..968a6a5cab8 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -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; diff --git a/source3/smbd/smb2_glue.c b/source3/smbd/smb2_glue.c index 85f83c46205..0e3c489f46c 100644 --- a/source3/smbd/smb2_glue.c +++ b/source3/smbd/smb2_glue.c @@ -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; diff --git a/source3/smbd/smb2_ioctl.c b/source3/smbd/smb2_ioctl.c index be70e3a0912..01ae6d64ac5 100644 --- a/source3/smbd/smb2_ioctl.c +++ b/source3/smbd/smb2_ioctl.c @@ -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); diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 699f293799b..4c26f822bd4 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -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