TODO implement REPLAY and channel_sequence checks
authorStefan Metzmacher <metze@samba.org>
Tue, 7 Aug 2012 08:11:29 +0000 (10:11 +0200)
committerStefan Metzmacher <metze@samba.org>
Tue, 18 Sep 2012 03:10:22 +0000 (05:10 +0200)
metze

source3/smbd/smb2_server.c

index be7997febfb919b9bacfd3705f4d10765bbafa2f..ef35a5e3b2fd98bd2b929812a912f99343801139 100644 (file)
@@ -40,6 +40,7 @@ static const struct smbd_smb2_dispatch_table {
        bool as_root;
        uint16_t fileid_ofs;
        bool allow_invalid_fileid;
+       bool modify;
 } smbd_smb2_table[] = {
 #define _OP(o) .opcode = o, .name = #o
        {
@@ -92,6 +93,7 @@ static const struct smbd_smb2_dispatch_table {
                .need_session = true,
                .need_tcon = true,
                .fileid_ofs = 0x10,
+               .modify = true;
        },{
                _OP(SMB2_OP_LOCK),
                .need_session = true,
@@ -103,6 +105,7 @@ static const struct smbd_smb2_dispatch_table {
                .need_tcon = true,
                .fileid_ofs = 0x08,
                .allow_invalid_fileid = true,
+               .modify = true;
        },{
                _OP(SMB2_OP_CANCEL),
                .as_root = true,
@@ -129,6 +132,7 @@ static const struct smbd_smb2_dispatch_table {
                .need_session = true,
                .need_tcon = true,
                .fileid_ofs = 0x10,
+               .modify = true;
        },{
                _OP(SMB2_OP_BREAK),
                .need_session = true,
@@ -1889,6 +1893,7 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
        uint16_t opcode;
        uint32_t flags;
        uint64_t mid;
+       uint16_t channel_sequence;
        NTSTATUS status;
        NTSTATUS session_status;
        uint32_t allowed_flags;
@@ -1896,6 +1901,7 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
        struct smbXsrv_session *x = NULL;
        bool signing_required = false;
        bool encryption_required = false;
+       struct smbXsrv_open *op = NULL;
 
        inhdr = SMBD_SMB2_IN_HDR_PTR(req);
 
@@ -1904,6 +1910,7 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
        flags = IVAL(inhdr, SMB2_HDR_FLAGS);
        opcode = IVAL(inhdr, SMB2_HDR_OPCODE);
        mid = BVAL(inhdr, SMB2_HDR_MESSAGE_ID);
+       channel_sequence = SVAL(inhdr, SMB2_HDR_CHANNEL_SEQUENCE);
        DEBUG(10,("smbd_smb2_request_dispatch: opcode[%s] mid = %llu\n",
                smb2_opcode_name(opcode),
                (unsigned long long)mid));
@@ -1990,6 +1997,9 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
        if (opcode == SMB2_OP_CANCEL) {
                allowed_flags |= SMB2_HDR_FLAG_ASYNC;
        }
+       if (conn->protocol >= PROTOCOL_SMB2_22) {
+               allowed_flags |= SMB2_HDR_FLAG_REPLAY_OPERATION;
+       }
        if ((flags & ~allowed_flags) != 0) {
                return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
        }
@@ -2108,7 +2118,9 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
                file_id_volatile        = BVAL(body, call->fileid_ofs + 8);
 
                fsp = file_fsp_smb2(req, file_id_persistent, file_id_volatile);
-               if (fsp == NULL) {
+               if (fsp != NULL) {
+                       op = fsp->op;
+               } else {
                        if (!call->allow_invalid_fileid) {
                                return smbd_smb2_request_error(req,
                                                NT_STATUS_FILE_CLOSED);
@@ -2125,6 +2137,61 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
                }
        }
 
+       if ((conn->protocol >= PROTOCOL_SMB2_22) && (op != NULL)) {
+               int cmp = channel_sequence - op->global->channel_sequence;
+               bool overflow = false;
+
+               if (abs(cmp) > INT16_MAX) {
+                       /*
+                        * we like to handle an 16-bit overflow here.
+                        *
+                        * If the difference between the numbers
+                        * if more than 15-bit, it is likely to be an overflow.
+                        */
+                       overflow = true;
+                       cmp *= -1;
+               }
+
+               if (!(flags & SMB2_HDR_FLAG_REPLAY_OPERATION)) {
+                       if (cmp == 0) {
+                               op->request_count += 1;
+                       } else if (cmp > 0) {
+                               op->pre_request_count += op->request_count;
+                               op->request_count = 0;
+                               op->request_count += 1;
+                               op->global->channel_sequence = channel_sequence;
+                               update_open = true;
+                       } else if (call->modify) {
+                               return smbd_smb2_request_error(req,
+                                       NT_STATUS_FILE_NOT_AVAILABLE);
+                       }
+               } else {
+                       if (cmp == 0) {
+                               if (op->pre_request_count == 0) {
+                                       op->request_count += 1;
+                               } else if (call->modify) {
+                                       return smbd_smb2_request_error(req,
+                                               NT_STATUS_FILE_NOT_AVAILABLE);
+                               }
+                       } else if (cmp > 0) {
+                               if (op->pre_request_count == 0) {
+                                       op->request_count += 1;
+                                       op->global->channel_sequence =
+                                               channel_sequence;
+                                       update_open = true;
+                               } else if (call->modify) {
+                                       return smbd_smb2_request_error(req,
+                                               NT_STATUS_FILE_NOT_AVAILABLE);
+                               }
+                       } else {
+                               if (call->modify) {
+                                       return smbd_smb2_request_error(req,
+                                               NT_STATUS_FILE_NOT_AVAILABLE);
+                               }
+                       }
+               }
+       }
+
        if (call->as_root) {
                SMB_ASSERT(call->fileid_ofs == 0);
                /* This call needs to be run as root */