1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-22 13:34:15 +03:00

smbd:smb2: implement create replay

Signed-off-by: Michael Adam <obnox@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
This commit is contained in:
Michael Adam 2016-02-28 02:32:36 +01:00
parent 03a3aa9815
commit 2fd54b5332
2 changed files with 89 additions and 16 deletions

View File

@ -202,16 +202,6 @@
^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

View 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);