s3:smbd: fix the logic for compounded requests
[ira/wip.git] / source3 / smbd / smb2_server.c
index c63c8da43c39ef4d555a8339d442b2403d9c775c..0413832f905c85bd0be1756ccca4481a3929a43d 100644 (file)
@@ -41,7 +41,7 @@ static NTSTATUS smbd_initialize_smb2(struct smbd_server_connection *conn)
        NTSTATUS status;
        int ret;
 
-       TALLOC_FREE(conn->fde);
+       TALLOC_FREE(conn->smb1.fde);
 
        conn->smb2.event_ctx = smbd_event_context();
 
@@ -55,6 +55,13 @@ static NTSTATUS smbd_initialize_smb2(struct smbd_server_connection *conn)
                return NT_STATUS_NO_MEMORY;
        }
 
+       conn->smb2.sessions.idtree = idr_init(conn);
+       if (conn->smb2.sessions.idtree == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       conn->smb2.sessions.limit = 0x0000FFFE;
+       conn->smb2.sessions.list = NULL;
+
        ret = tstream_bsd_existing_socket(conn, smbd_server_fd(),
                                          &conn->smb2.stream);
        if (ret == -1) {
@@ -238,17 +245,18 @@ static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
                      NT_STATUS_V(NT_STATUS_INTERNAL_ERROR));
                SSVAL(outhdr, SMB2_HDR_OPCODE,
                      SVAL(inhdr, SMB2_HDR_OPCODE));
-               SSVAL(outhdr, SMB2_HDR_CREDIT,          0);
+               /* Make up a number for now... JRA. FIXME ! FIXME !*/
+               SSVAL(outhdr, SMB2_HDR_CREDIT,          20);
                SIVAL(outhdr, SMB2_HDR_FLAGS,           SMB2_HDR_FLAG_REDIRECT);
                SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND,    next_command_ofs);
-               SIVAL(outhdr, SMB2_HDR_MESSAGE_ID,
-                     IVAL(inhdr, SMB2_HDR_MESSAGE_ID));
+               SBVAL(outhdr, SMB2_HDR_MESSAGE_ID,
+                     BVAL(inhdr, SMB2_HDR_MESSAGE_ID));
                SIVAL(outhdr, SMB2_HDR_PID,
                      IVAL(inhdr, SMB2_HDR_PID));
                SIVAL(outhdr, SMB2_HDR_TID,
                      IVAL(inhdr, SMB2_HDR_TID));
-               SIVAL(outhdr, SMB2_HDR_SESSION_ID,
-                     IVAL(inhdr, SMB2_HDR_SESSION_ID));
+               SBVAL(outhdr, SMB2_HDR_SESSION_ID,
+                     BVAL(inhdr, SMB2_HDR_SESSION_ID));
                memset(outhdr + SMB2_HDR_SIGNATURE, 0, 16);
 
                /* setup error body header */
@@ -269,10 +277,12 @@ static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
        return NT_STATUS_OK;
 }
 
-static void smbd_server_connection_terminate(struct smbd_server_connection *conn,
-                                            const char *reason)
+void smbd_server_connection_terminate_ex(struct smbd_server_connection *sconn,
+                                        const char *reason,
+                                        const char *location)
 {
-       DEBUG(10,("smbd_server_connection_terminate: reason[%s]\n", reason));
+       DEBUG(10,("smbd_server_connection_terminate_ex: reason[%s] at %s\n",
+                 reason, location));
        exit_server_cleanly(reason);
 }
 
@@ -281,33 +291,202 @@ static NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
        const uint8_t *inhdr;
        int i = req->current_idx;
        uint16_t opcode;
+       uint32_t flags;
+       NTSTATUS status;
+       NTSTATUS session_status;
 
        inhdr = (const uint8_t *)req->in.vector[i].iov_base;
 
        /* TODO: verify more things */
 
+       flags = IVAL(inhdr, SMB2_HDR_FLAGS);
        opcode = IVAL(inhdr, SMB2_HDR_OPCODE);
        DEBUG(10,("smbd_smb2_request_dispatch: opcode[%u]\n", opcode));
+
+#define TMP_SMB2_ALLOWED_FLAGS ( \
+       SMB2_HDR_FLAG_CHAINED | \
+       SMB2_HDR_FLAG_SIGNED | \
+       SMB2_HDR_FLAG_DFS)
+       if ((flags & ~TMP_SMB2_ALLOWED_FLAGS) != 0) {
+               return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
+       }
+#undef TMP_SMB2_ALLOWED_FLAGS
+
+       session_status = smbd_smb2_request_check_session(req);
+
+       req->do_signing = false;
+       if (flags & SMB2_HDR_FLAG_SIGNED) {
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+
+               req->do_signing = true;
+               status = smb2_signing_check_pdu(req->session->session_key,
+                                               &req->in.vector[i], 3);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+       } else if (req->session && req->session->do_signing) {
+               return smbd_smb2_request_error(req, NT_STATUS_ACCESS_DENIED);
+       }
+
        switch (opcode) {
        case SMB2_OP_NEGPROT:
+               return smbd_smb2_request_process_negprot(req);
+
        case SMB2_OP_SESSSETUP:
+               return smbd_smb2_request_process_sesssetup(req);
+
        case SMB2_OP_LOGOFF:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               return smbd_smb2_request_process_logoff(req);
+
        case SMB2_OP_TCON:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_session(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_process_tcon(req);
+
        case SMB2_OP_TDIS:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_process_tdis(req);
+
        case SMB2_OP_CREATE:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_process_create(req);
+
        case SMB2_OP_CLOSE:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_process_close(req);
+
        case SMB2_OP_FLUSH:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_process_flush(req);
+
        case SMB2_OP_READ:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_process_read(req);
+
        case SMB2_OP_WRITE:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_process_write(req);
+
        case SMB2_OP_LOCK:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_error(req, NT_STATUS_NOT_IMPLEMENTED);
+
        case SMB2_OP_IOCTL:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_process_ioctl(req);
+
        case SMB2_OP_CANCEL:
+               return smbd_smb2_request_error(req, NT_STATUS_NOT_IMPLEMENTED);
+
        case SMB2_OP_KEEPALIVE:
+               return smbd_smb2_request_process_keepalive(req);
+
        case SMB2_OP_FIND:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_error(req, NT_STATUS_NOT_IMPLEMENTED);
+
        case SMB2_OP_NOTIFY:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_error(req, NT_STATUS_NOT_IMPLEMENTED);
+
        case SMB2_OP_GETINFO:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_error(req, NT_STATUS_NOT_IMPLEMENTED);
+
        case SMB2_OP_SETINFO:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
+               return smbd_smb2_request_error(req, NT_STATUS_NOT_IMPLEMENTED);
+
        case SMB2_OP_BREAK:
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
+               status = smbd_smb2_request_check_tcon(req);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return smbd_smb2_request_error(req, status);
+               }
                return smbd_smb2_request_error(req, NT_STATUS_NOT_IMPLEMENTED);
        }
 
@@ -321,13 +500,21 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
 {
        struct tevent_req *subreq;
 
-       /* TODO: sign the response here */
-
        smb2_setup_nbt_length(req->out.vector, req->out.vector_count);
 
+       if (req->do_signing) {
+               int i = req->current_idx;
+               NTSTATUS status;
+               status = smb2_signing_sign_pdu(req->session->session_key,
+                                              &req->out.vector[i], 3);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
+
        req->current_idx += 3;
 
-       if (req->current_idx > req->in.vector_count) {
+       if (req->current_idx < req->out.vector_count) {
                struct timeval zero = timeval_zero();
                subreq = tevent_wakeup_send(req,
                                            req->conn->smb2.event_ctx,
@@ -356,16 +543,60 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
        return NT_STATUS_OK;
 }
 
+static void smbd_smb2_request_dispatch_compound(struct tevent_req *subreq)
+{
+       struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
+                                       struct smbd_smb2_request);
+       struct smbd_server_connection *conn = req->conn;
+       NTSTATUS status;
+
+       tevent_wakeup_recv(subreq);
+       TALLOC_FREE(subreq);
+
+       DEBUG(10,("smbd_smb2_request_dispatch_compound: idx[%d] of %d vectors\n",
+                 req->current_idx, req->in.vector_count));
+
+       status = smbd_smb2_request_dispatch(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               smbd_server_connection_terminate(conn, nt_errstr(status));
+               return;
+       }
+}
+
+static void smbd_smb2_request_writev_done(struct tevent_req *subreq)
+{
+       struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
+                                       struct smbd_smb2_request);
+       struct smbd_server_connection *conn = req->conn;
+       int ret;
+       int sys_errno;
+       TALLOC_CTX *mem_pool;
+
+       ret = tstream_writev_queue_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               NTSTATUS status = map_nt_error_from_unix(sys_errno);
+               smbd_server_connection_terminate(conn, nt_errstr(status));
+               return;
+       }
+
+       mem_pool = req->mem_pool;
+       req = NULL;
+       talloc_free(mem_pool);
+}
+
 NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
                                    NTSTATUS status,
-                                   DATA_BLOB *info)
+                                   DATA_BLOB *info,
+                                   const char *location)
 {
        uint8_t *outhdr;
        uint8_t *outbody;
        int i = req->current_idx;
 
-       DEBUG(10,("smbd_smb2_request_error_ex: idx[%d] status[%s]%s\n",
-                 i, nt_errstr(status), info ? " +info" : ""));
+       DEBUG(10,("smbd_smb2_request_error_ex: idx[%d] status[%s] |%s| at %s\n",
+                 i, nt_errstr(status), info ? " +info" : "",
+                 location));
 
        outhdr = (uint8_t *)req->out.vector[i].iov_base;
 
@@ -392,24 +623,22 @@ NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
        return smbd_smb2_request_reply(req);
 }
 
-NTSTATUS smbd_smb2_request_error(struct smbd_smb2_request *req,
-                                NTSTATUS status)
-{
-       return smbd_smb2_request_error_ex(req, status, NULL);
-}
-
-NTSTATUS smbd_smb2_request_done(struct smbd_smb2_request *req,
-                               DATA_BLOB body, DATA_BLOB *dyn)
+NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req,
+                                  NTSTATUS status,
+                                  DATA_BLOB body, DATA_BLOB *dyn,
+                                  const char *location)
 {
        uint8_t *outhdr;
        uint8_t *outdyn;
        int i = req->current_idx;
        uint32_t next_command_ofs;
 
-       DEBUG(10,("smbd_smb2_request_done: idx[%d] body[%u] dyn[%s:%u]\n",
-                 i, (unsigned int)body.length,
+       DEBUG(10,("smbd_smb2_request_done_ex: "
+                 "idx[%d] status[%s] body[%u] dyn[%s:%u] at %s\n",
+                 i, nt_errstr(status), (unsigned int)body.length,
                  dyn ? "yes": "no",
-                 (unsigned int)(dyn ? dyn->length : 0)));
+                 (unsigned int)(dyn ? dyn->length : 0),
+                 location));
 
        if (body.length < 2) {
                return smbd_smb2_request_error(req, NT_STATUS_INTERNAL_ERROR);
@@ -423,8 +652,8 @@ NTSTATUS smbd_smb2_request_done(struct smbd_smb2_request *req,
        /* the fallback dynamic buffer */
        outdyn = outhdr + SMB2_HDR_BODY + 8;
 
-       next_command_ofs = SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND, 0);
-       SIVAL(outhdr, SMB2_HDR_STATUS, NT_STATUS_V(NT_STATUS_OK));
+       next_command_ofs = IVAL(outhdr, SMB2_HDR_NEXT_COMMAND);
+       SIVAL(outhdr, SMB2_HDR_STATUS, NT_STATUS_V(status));
 
        req->out.vector[i+1].iov_base = (void *)body.data;
        req->out.vector[i+1].iov_len = body.length;
@@ -434,8 +663,7 @@ NTSTATUS smbd_smb2_request_done(struct smbd_smb2_request *req,
                        req->out.vector[i+2].iov_base   = (void *)dyn->data;
                        req->out.vector[i+2].iov_len    = dyn->length;
                } else {
-                       req->out.vector[i+2].iov_base   = (void *)outdyn;
-                       req->out.vector[i+2].iov_len    = 1;
+                       /* the dyn section is already initialized */
                }
        } else {
                req->out.vector[i+2].iov_base = NULL;
@@ -460,48 +688,6 @@ NTSTATUS smbd_smb2_request_done(struct smbd_smb2_request *req,
        return smbd_smb2_request_reply(req);
 }
 
-static void smbd_smb2_request_dispatch_compound(struct tevent_req *subreq)
-{
-       struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
-                                       struct smbd_smb2_request);
-       struct smbd_server_connection *conn = req->conn;
-       NTSTATUS status;
-
-       tevent_wakeup_recv(subreq);
-       TALLOC_FREE(subreq);
-
-       DEBUG(10,("smbd_smb2_request_dispatch_compound: idx[%d] of %d vectors\n",
-                 req->current_idx, req->in.vector_count));
-
-       status = smbd_smb2_request_dispatch(req);
-       if (!NT_STATUS_IS_OK(status)) {
-               smbd_server_connection_terminate(conn, nt_errstr(status));
-               return;
-       }
-}
-
-static void smbd_smb2_request_writev_done(struct tevent_req *subreq)
-{
-       struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
-                                       struct smbd_smb2_request);
-       struct smbd_server_connection *conn = req->conn;
-       int ret;
-       int sys_errno;
-       TALLOC_CTX *mem_pool;
-
-       ret = tstream_writev_queue_recv(subreq, &sys_errno);
-       TALLOC_FREE(subreq);
-       if (ret == -1) {
-               NTSTATUS status = map_nt_error_from_unix(sys_errno);
-               smbd_server_connection_terminate(conn, nt_errstr(status));
-               return;
-       }
-
-       mem_pool = req->mem_pool;
-       req = NULL;
-       talloc_free(mem_pool);
-}
-
 struct smbd_smb2_request_read_state {
        size_t missing;
        bool asked_for_header;