smbd:smb2: implement create replay
authorMichael Adam <obnox@samba.org>
Sun, 28 Feb 2016 01:32:36 +0000 (02:32 +0100)
committerMichael Adam <obnox@samba.org>
Thu, 3 Mar 2016 12:09:24 +0000 (13:09 +0100)
Signed-off-by: Michael Adam <obnox@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
selftest/knownfail
source3/smbd/smb2_create.c

index 34d1f2d1b5a13ca43514ccd06d7e202f7088c87e..1ac99d4dc9f5000b5ce6e1f829b850f27a2fe6b5 100644 (file)
 ^samba3.smb2.setinfo.setinfo
 ^samba3.smb2.session.*reauth5 # some special anonymous checks?
 ^samba3.smb2.compound.interim2 # wrong return code (STATUS_CANCELLED)
-^samba3.smb2.replay.replay-commands
-^samba3.smb2.replay.replay-regular
-^samba3.smb2.replay.replay-dhv2-oplock1
-^samba3.smb2.replay.replay-dhv2-oplock2
-^samba3.smb2.replay.replay-dhv2-oplock3
-^samba3.smb2.replay.replay-dhv2-oplock-lease
-^samba3.smb2.replay.replay-dhv2-lease1
-^samba3.smb2.replay.replay-dhv2-lease2
-^samba3.smb2.replay.replay-dhv2-lease3
-^samba3.smb2.replay.replay-dhv2-lease-oplock
 ^samba3.smb2.replay.replay3
 ^samba3.smb2.replay.replay4
 ^samba3.smb2.lock.*replay
index 6f01c3932cd32a8ae57f94187749f0b59b6d64fd..d50b20c8746b4db22de52ee160029ecefd0d8b12 100644 (file)
@@ -475,6 +475,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
        struct smb2_create_blob *dh2q = NULL;
        struct smb2_create_blob *rqls = NULL;
        struct smbXsrv_open *op = NULL;
+       bool replay_operation = false;
 
        if(lp_fake_oplocks(SNUM(smb2req->tcon->compat))) {
                requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
@@ -779,6 +780,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                        const uint8_t *p = dh2q->data.data;
                        uint32_t durable_v2_timeout = 0;
                        DATA_BLOB create_guid_blob;
+                       const uint8_t *hdr;
+                       uint32_t flags;
 
                        if (dh2q->data.length != 32) {
                                tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
@@ -823,6 +826,38 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                                 */
                                durable_timeout_msec = (60*1000);
                        }
+
+                       /*
+                        * Check for replay operation.
+                        * Only consider it when we have dh2q.
+                        * If we do not have a replay operation, verify that
+                        * the create_guid is not cached for replay.
+                        */
+                       hdr = SMBD_SMB2_IN_HDR_PTR(smb2req);
+                       flags = IVAL(hdr, SMB2_HDR_FLAGS);
+                       replay_operation =
+                               !!(flags & SMB2_HDR_FLAG_REPLAY_OPERATION);
+
+                       status = smb2srv_open_lookup_replay_cache(
+                                       smb2req->xconn, create_guid,
+                                       0 /* now */, &op);
+
+                       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+                               replay_operation = false;
+                       } else if (tevent_req_nterror(req, status)) {
+                               DBG_WARNING("smb2srv_open_lookup_replay_cache "
+                                           "failed: %s\n", nt_errstr(status));
+                               return tevent_req_post(req, ev);
+                       } else if (!replay_operation) {
+                               /*
+                                * If a create without replay operation flag
+                                * is sent but with a create_guid that is
+                                * currently in the replay cache -- fail.
+                                */
+                               status = NT_STATUS_DUPLICATE_OBJECTID;
+                               (void)tevent_req_nterror(req, status);
+                               return tevent_req_post(req, ev);
+                       }
                }
 
                if (dhnc) {
@@ -918,6 +953,32 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                                DEBUG(10, ("v2 lease key only for SMB3\n"));
                                lease_ptr = NULL;
                        }
+
+                       /*
+                        * Replay with a lease is only allowed if the
+                        * established open carries a lease with the
+                        * same lease key.
+                        */
+                       if (replay_operation) {
+                               struct smb2_lease *op_ls =
+                                               &op->compat->lease->lease;
+                               int op_oplock = op->compat->oplock_type;
+
+                               if (map_samba_oplock_levels_to_smb2(op_oplock)
+                                   != SMB2_OPLOCK_LEVEL_LEASE)
+                               {
+                                       status = NT_STATUS_ACCESS_DENIED;
+                                       (void)tevent_req_nterror(req, status);
+                                       return tevent_req_post(req, ev);
+                               }
+                               if (!smb2_lease_key_equal(&lease.lease_key,
+                                                         &op_ls->lease_key))
+                               {
+                                       status = NT_STATUS_ACCESS_DENIED;
+                                       (void)tevent_req_nterror(req, status);
+                                       return tevent_req_post(req, ev);
+                               }
+                       }
                }
 
                /* these are ignored for SMB2 */
@@ -930,9 +991,16 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
                /*
                 * For the backend file open procedure, there are
-                * two possible modes: durable_reconnect or not.
+                * three possible modes: replay operation (in which case
+                * there is nothing else to do), durable_reconnect or
+                * new open.
                 */
-               if (do_durable_reconnect) {
+               if (replay_operation) {
+                       result = op->compat;
+                       result->op = op;
+                       update_open = false;
+                       info = op->create_action;
+               } else if (do_durable_reconnect) {
                        DATA_BLOB new_cookie = data_blob_null;
                        NTTIME now = timeval_to_nttime(&smb2req->request_time);
 
@@ -1142,7 +1210,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                        }
                }
 
-               if (durable_requested &&
+               if (!replay_operation && durable_requested &&
                    (fsp_lease_type(result) & SMB2_LEASE_HANDLE))
                {
                        status = SMB_VFS_DURABLE_COOKIE(result,
@@ -1152,7 +1220,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                                op->global->backend_cookie = data_blob_null;
                        }
                }
-               if (op->global->backend_cookie.length > 0) {
+               if (!replay_operation && op->global->backend_cookie.length > 0)
+               {
                        update_open = true;
 
                        op->global->durable = true;
@@ -1189,7 +1258,18 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                        }
                }
 
-               if (dh2q && op->global->durable) {
+               if (dh2q && op->global->durable &&
+                   /*
+                    * For replay operations, we return the dh2q blob
+                    * in the case of oplocks not based on the state of
+                    * the open, but on whether it could have been granted
+                    * for the request data. In the case of leases instead,
+                    * the state of the open is used...
+                    */
+                   (!replay_operation ||
+                    in_oplock_level == SMB2_OPLOCK_LEVEL_BATCH ||
+                    in_oplock_level == SMB2_OPLOCK_LEVEL_LEASE))
+               {
                        uint8_t p[8] = { 0, };
                        DATA_BLOB blob = data_blob_const(p, sizeof(p));
                        uint32_t durable_v2_response_flags = 0;
@@ -1261,7 +1341,9 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
        smb2req->compat_chain_fsp = smb1req->chain_fsp;
 
-       if(lp_fake_oplocks(SNUM(smb2req->tcon->compat))) {
+       if (replay_operation) {
+               state->out_oplock_level = in_oplock_level;
+       } else if (lp_fake_oplocks(SNUM(smb2req->tcon->compat))) {
                state->out_oplock_level = in_oplock_level;
        } else {
                state->out_oplock_level = map_samba_oplock_levels_to_smb2(result->oplock_type);
@@ -1273,6 +1355,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
        } else {
                state->out_create_action = info;
        }
+       result->op->create_action = state->out_create_action;
        state->out_file_attributes = dos_mode(result->conn,
                                           result->fsp_name);