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 dee06b37aff94ad1f947ce72f9417d8e6d4f45b6..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,6 +284,7 @@ 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;
                uint32_t next_command_ofs = 0;
@@ -215,6 +296,7 @@ static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
                }
 
                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);
@@ -243,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));
@@ -323,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);
@@ -609,9 +707,11 @@ NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
                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);
 }
@@ -666,9 +766,54 @@ 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;
        }
 
        SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND, next_command_ofs);
@@ -1115,12 +1260,17 @@ static void smbd_smb2_request_incoming(struct tevent_req *subreq)
                goto next;
        }
 
-       /* TODO: validate the incoming request */
        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));