s3:smbd: the SMB2-COMPOUND test shows that the related vs. unrelated flags isn't...
[nivanova/samba-autobuild/.git] / source3 / smbd / smb2_server.c
index 5c4bdca3e57461705a948f209e956aa5b16221e7..c889555a1cbaf5c37196c4ea0f8c6da8c657d84a 100644 (file)
@@ -186,6 +186,86 @@ static NTSTATUS smbd_smb2_request_create(struct smbd_server_connection *conn,
        return NT_STATUS_OK;
 }
 
+static NTSTATUS smbd_smb2_request_validate(struct smbd_smb2_request *req)
+{
+       int count;
+       int idx;
+       bool compound_related = false;
+
+       count = req->in.vector_count;
+
+       if (count < 4) {
+               /* It's not a SMB2 request */
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       for (idx=1; idx < count; idx += 3) {
+               const uint8_t *inhdr = NULL;
+               uint32_t flags;
+
+               if (req->in.vector[idx].iov_len != SMB2_HDR_BODY) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               if (req->in.vector[idx+1].iov_len < 2) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               inhdr = (const uint8_t *)req->in.vector[idx].iov_base;
+
+               /* setup the SMB2 header */
+               if (IVAL(inhdr, SMB2_HDR_PROTOCOL_ID) != SMB2_MAGIC) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               flags = IVAL(inhdr, SMB2_HDR_FLAGS);
+               if (idx == 1) {
+                       /*
+                        * the 1st request should never have the
+                        * SMB2_HDR_FLAG_CHAINED flag set
+                        */
+                       if (flags & SMB2_HDR_FLAG_CHAINED) {
+                               req->next_status = NT_STATUS_INVALID_PARAMETER;
+                               return NT_STATUS_OK;
+                       }
+               } else if (idx == 4) {
+                       /*
+                        * the 2nd request triggers related vs. unrelated
+                        * compounded requests
+                        */
+                       if (flags & SMB2_HDR_FLAG_CHAINED) {
+                               compound_related = true;
+                       }
+               } else if (idx > 4) {
+#if 0
+                       /*
+                        * It seems the this tests are wrong
+                        * see the SMB2-COMPOUND test
+                        */
+
+                       /*
+                        * all other requests should match the 2nd one
+                        */
+                       if (flags & SMB2_HDR_FLAG_CHAINED) {
+                               if (!compound_related) {
+                                       req->next_status =
+                                               NT_STATUS_INVALID_PARAMETER;
+                                       return NT_STATUS_OK;
+                               }
+                       } else {
+                               if (compound_related) {
+                                       req->next_status =
+                                               NT_STATUS_INVALID_PARAMETER;
+                                       return NT_STATUS_OK;
+                               }
+                       }
+#endif
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
 static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
 {
        struct iovec *vector;
@@ -204,29 +284,27 @@ static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
 
        for (idx=1; idx < count; idx += 3) {
                const uint8_t *inhdr = NULL;
+               uint32_t in_flags;
                uint8_t *outhdr = NULL;
                uint8_t *outbody = NULL;
-               uint8_t *outdyn = NULL;
-               size_t outdyn_size = 1;
                uint32_t next_command_ofs = 0;
                struct iovec *current = &vector[idx];
 
                if ((idx + 3) < count) {
                        /* we have a next command */
-                       next_command_ofs = SMB2_HDR_BODY + 8 + 8;
-                       outdyn_size = 8;
+                       next_command_ofs = SMB2_HDR_BODY + 8;
                }
 
                inhdr = (const uint8_t *)req->in.vector[idx].iov_base;
+               in_flags = IVAL(inhdr, SMB2_HDR_FLAGS);
 
                outhdr = talloc_array(vector, uint8_t,
-                                     SMB2_HDR_BODY + 8 + outdyn_size);
+                                     SMB2_HDR_BODY + 8);
                if (outhdr == NULL) {
                        return NT_STATUS_NO_MEMORY;
                }
 
                outbody = outhdr + SMB2_HDR_BODY;
-               outdyn = outbody + 8;
 
                current[0].iov_base     = (void *)outhdr;
                current[0].iov_len      = SMB2_HDR_BODY;
@@ -234,8 +312,8 @@ static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
                current[1].iov_base     = (void *)outbody;
                current[1].iov_len      = 8;
 
-               current[2].iov_base     = (void *)outdyn;
-               current[2].iov_len      = outdyn_size;
+               current[2].iov_base     = NULL;
+               current[2].iov_len      = 0;
 
                /* setup the SMB2 header */
                SIVAL(outhdr, SMB2_HDR_PROTOCOL_ID,     SMB2_MAGIC);
@@ -247,7 +325,8 @@ static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
                      SVAL(inhdr, SMB2_HDR_OPCODE));
                /* 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_FLAGS,
+                     IVAL(inhdr, SMB2_HDR_FLAGS) | SMB2_HDR_FLAG_REDIRECT);
                SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND,    next_command_ofs);
                SBVAL(outhdr, SMB2_HDR_MESSAGE_ID,
                      BVAL(inhdr, SMB2_HDR_MESSAGE_ID));
@@ -260,12 +339,9 @@ static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
                memset(outhdr + SMB2_HDR_SIGNATURE, 0, 16);
 
                /* setup error body header */
-               SSVAL(outbody, 0x00, 9);
+               SSVAL(outbody, 0x00, 0x08 + 1);
                SSVAL(outbody, 0x02, 0);
                SIVAL(outbody, 0x04, 0);
-
-               /* setup the dynamic part */
-               SCVAL(outdyn, 0x00, 0);
        }
 
        req->out.vector = vector;
@@ -277,10 +353,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);
 }
 
@@ -328,6 +406,21 @@ static NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
                return smbd_smb2_request_error(req, NT_STATUS_ACCESS_DENIED);
        }
 
+       if (flags & SMB2_HDR_FLAG_CHAINED) {
+               /*
+                * This check is mostly for giving the correct error code
+                * for compounded requests.
+                *
+                * TODO: we may need to move this after the session
+                *       and tcon checks.
+                */
+               if (!NT_STATUS_IS_OK(req->next_status)) {
+                       return smbd_smb2_request_error(req, req->next_status);
+               }
+       } else {
+               req->compat_chain_fsp = NULL;
+       }
+
        switch (opcode) {
        case SMB2_OP_NEGPROT:
                return smbd_smb2_request_process_negprot(req);
@@ -429,7 +522,7 @@ static NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
                if (!NT_STATUS_IS_OK(status)) {
                        return smbd_smb2_request_error(req, status);
                }
-               return smbd_smb2_request_error(req, NT_STATUS_NOT_IMPLEMENTED);
+               return smbd_smb2_request_process_ioctl(req);
 
        case SMB2_OP_CANCEL:
                return smbd_smb2_request_error(req, NT_STATUS_NOT_IMPLEMENTED);
@@ -512,7 +605,7 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
 
        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,
@@ -583,17 +676,18 @@ static void smbd_smb2_request_writev_done(struct tevent_req *subreq)
        talloc_free(mem_pool);
 }
 
-static NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
+NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
                                    NTSTATUS status,
-                                   const char *wherestr,
-                                   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] at %s |%s|\n",
-                 i, nt_errstr(status), wherestr, 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;
 
@@ -609,26 +703,23 @@ static NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
                req->out.vector[i+2].iov_base   = (void *)info->data;
                req->out.vector[i+2].iov_len    = info->length;
        } else {
-               req->out.vector[i+2].iov_base = (void *)(outbody + 8);
-               req->out.vector[i+2].iov_len = 1;
+               req->out.vector[i+2].iov_base = NULL;
+               req->out.vector[i+2].iov_len = 0;
        }
 
-       /* the error packet is the last response in the chain */
-       SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND, 0);
-       req->out.vector_count = req->current_idx + 3;
+       /*
+        * if a request fails, all other remaining
+        * compounded requests should fail too
+        */
+       req->next_status = NT_STATUS_INVALID_PARAMETER;
 
        return smbd_smb2_request_reply(req);
 }
 
-NTSTATUS smbd_smb2_request_error_(struct smbd_smb2_request *req,
-                                NTSTATUS status, const char *wherestr)
-{
-       return smbd_smb2_request_error_ex(req, status, wherestr, NULL);
-}
-
 NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req,
                                   NTSTATUS status,
-                                  DATA_BLOB body, DATA_BLOB *dyn)
+                                  DATA_BLOB body, DATA_BLOB *dyn,
+                                  const char *location)
 {
        uint8_t *outhdr;
        uint8_t *outdyn;
@@ -636,10 +727,11 @@ NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req,
        uint32_t next_command_ofs;
 
        DEBUG(10,("smbd_smb2_request_done_ex: "
-                 "idx[%d] status[%s] body[%u] dyn[%s:%u]\n",
+                 "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);
@@ -660,13 +752,8 @@ NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req,
        req->out.vector[i+1].iov_len = body.length;
 
        if (dyn) {
-               if (dyn->length > 0) {
-                       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;
-               }
+               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 = NULL;
                req->out.vector[i+2].iov_len = 0;
@@ -679,23 +766,61 @@ NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req,
                next_command_ofs += req->out.vector[i+2].iov_len;
        }
 
-       /* TODO: we need to add padding ... */
        if ((next_command_ofs % 8) != 0) {
-               return smbd_smb2_request_error(req, NT_STATUS_INTERNAL_ERROR);
+               size_t pad_size = 8 - (next_command_ofs % 8);
+               if (req->out.vector[i+2].iov_len == 0) {
+                       /*
+                        * if the dyn buffer is empty
+                        * we can use it to add padding
+                        */
+                       uint8_t *pad;
+
+                       pad = talloc_zero_array(req->out.vector,
+                                               uint8_t, pad_size);
+                       if (pad == NULL) {
+                               return smbd_smb2_request_error(req,
+                                               NT_STATUS_NO_MEMORY);
+                       }
+
+                       req->out.vector[i+2].iov_base = (void *)pad;
+                       req->out.vector[i+2].iov_len = pad_size;
+               } else {
+                       /*
+                        * For now we copy the dynamic buffer
+                        * and add the padding to the new buffer
+                        */
+                       size_t old_size;
+                       uint8_t *old_dyn;
+                       size_t new_size;
+                       uint8_t *new_dyn;
+
+                       old_size = req->out.vector[i+2].iov_len;
+                       old_dyn = (uint8_t *)req->out.vector[i+2].iov_base;
+
+                       new_size = old_size + pad_size;
+                       new_dyn = talloc_array(req->out.vector,
+                                              uint8_t, new_size);
+                       if (new_dyn == NULL) {
+                               return smbd_smb2_request_error(req,
+                                               NT_STATUS_NO_MEMORY);
+                       }
+
+                       memcpy(new_dyn, old_dyn, old_size);
+                       memset(new_dyn + old_size, 0, pad_size);
+
+                       req->out.vector[i+2].iov_base = (void *)new_dyn;
+                       req->out.vector[i+2].iov_len = new_size;
+
+                       TALLOC_FREE(old_dyn);
+               }
+               next_command_ofs += pad_size;
        }
 
-       /* the error packet is the last response in the chain */
        SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND, next_command_ofs);
 
        return smbd_smb2_request_reply(req);
 }
 
-NTSTATUS smbd_smb2_request_done(struct smbd_smb2_request *req,
-                               DATA_BLOB body, DATA_BLOB *dyn)
-{
-       return smbd_smb2_request_done_ex(req, NT_STATUS_OK, body, dyn);
-}
-
 struct smbd_smb2_request_read_state {
        size_t missing;
        bool asked_for_header;
@@ -1127,12 +1252,25 @@ static void smbd_smb2_request_incoming(struct tevent_req *subreq)
                return;
        }
 
-       /* TODO: validate the incoming request */
+       if (req->in.nbt_hdr[0] != 0x00) {
+               DEBUG(1,("smbd_smb2_request_incoming: ignore NBT[0x%02X] msg\n",
+                        req->in.nbt_hdr[0]));
+               talloc_free(req->mem_pool);
+               req = NULL;
+               goto next;
+       }
+
        req->current_idx = 1;
 
        DEBUG(10,("smbd_smb2_request_incoming: idx[%d] of %d vectors\n",
                 req->current_idx, req->in.vector_count));
 
+       status = smbd_smb2_request_validate(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               smbd_server_connection_terminate(conn, nt_errstr(status));
+               return;
+       }
+
        status = smbd_smb2_request_setup_out(req);
        if (!NT_STATUS_IS_OK(status)) {
                smbd_server_connection_terminate(conn, nt_errstr(status));
@@ -1145,6 +1283,7 @@ static void smbd_smb2_request_incoming(struct tevent_req *subreq)
                return;
        }
 
+next:
        /* ask for the next request (this constructs the main loop) */
        subreq = smbd_smb2_request_read_send(conn,conn->smb2.event_ctx, conn);
        if (subreq == NULL) {