summaryrefslogtreecommitdiff
path: root/source3/smbd/smb2_lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/smbd/smb2_lock.c')
-rw-r--r--source3/smbd/smb2_lock.c180
1 files changed, 169 insertions, 11 deletions
diff --git a/source3/smbd/smb2_lock.c b/source3/smbd/smb2_lock.c
index 3ffe053481..121b4eb24d 100644
--- a/source3/smbd/smb2_lock.c
+++ b/source3/smbd/smb2_lock.c
@@ -31,6 +31,7 @@ struct smbd_smb2_lock_element {
static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct smbd_smb2_request *smb2req,
+ uint32_t in_smbpid,
uint64_t in_file_id_volatile,
uint16_t in_lock_count,
struct smbd_smb2_lock_element *in_locks);
@@ -41,15 +42,17 @@ NTSTATUS smbd_smb2_request_process_lock(struct smbd_smb2_request *req)
{
const uint8_t *inhdr;
const uint8_t *inbody;
- int i = req->current_idx;
+ const int i = req->current_idx;
size_t expected_body_size = 0x30;
size_t body_size;
+ uint32_t in_smbpid;
uint16_t in_lock_count;
uint64_t in_file_id_persistent;
uint64_t in_file_id_volatile;
struct smbd_smb2_lock_element *in_locks;
struct tevent_req *subreq;
const uint8_t *lock_buffer;
+ uint16_t l;
inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
@@ -63,8 +66,10 @@ NTSTATUS smbd_smb2_request_process_lock(struct smbd_smb2_request *req)
return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
}
+ in_smbpid = IVAL(inhdr, SMB2_HDR_PID);
+
in_lock_count = CVAL(inbody, 0x02);
- /* 0x04 4 bytes reserved */
+ /* 0x04 - 4 bytes reserved */
in_file_id_persistent = BVAL(inbody, 0x08);
in_file_id_volatile = BVAL(inbody, 0x10);
@@ -88,19 +93,21 @@ NTSTATUS smbd_smb2_request_process_lock(struct smbd_smb2_request *req)
return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
}
- i = 0;
+ l = 0;
lock_buffer = inbody + 0x18;
- in_locks[i].offset = BVAL(lock_buffer, 0x00);
- in_locks[i].length = BVAL(lock_buffer, 0x08);
- in_locks[i].flags = BVAL(lock_buffer, 0x10);
+ in_locks[l].offset = BVAL(lock_buffer, 0x00);
+ in_locks[l].length = BVAL(lock_buffer, 0x08);
+ in_locks[l].flags = IVAL(lock_buffer, 0x10);
+ /* 0x14 - 4 reserved bytes */
lock_buffer = (const uint8_t *)req->in.vector[i+2].iov_base;
- for (i=1; i < in_lock_count; i++) {
- in_locks[i].offset = BVAL(lock_buffer, 0x00);
- in_locks[i].length = BVAL(lock_buffer, 0x08);
- in_locks[i].flags = BVAL(lock_buffer, 0x10);
+ for (l=1; l < in_lock_count; l++) {
+ in_locks[l].offset = BVAL(lock_buffer, 0x00);
+ in_locks[l].length = BVAL(lock_buffer, 0x08);
+ in_locks[l].flags = IVAL(lock_buffer, 0x10);
+ /* 0x14 - 4 reserved bytes */
lock_buffer += 0x18;
}
@@ -108,6 +115,7 @@ NTSTATUS smbd_smb2_request_process_lock(struct smbd_smb2_request *req)
subreq = smbd_smb2_lock_send(req,
req->conn->smb2.event_ctx,
req,
+ in_smbpid,
in_file_id_volatile,
in_lock_count,
in_locks);
@@ -172,6 +180,7 @@ struct smbd_smb2_lock_state {
static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct smbd_smb2_request *smb2req,
+ uint32_t in_smbpid,
uint64_t in_file_id_volatile,
uint16_t in_lock_count,
struct smbd_smb2_lock_element *in_locks)
@@ -181,6 +190,12 @@ static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
struct smb_request *smbreq;
connection_struct *conn = smb2req->tcon->compat_conn;
files_struct *fsp;
+ int32_t timeout = -1;
+ bool isunlock = false;
+ uint16_t i;
+ struct smbd_lock_element *locks;
+ NTSTATUS status;
+ bool async = false;
req = tevent_req_create(mem_ctx, &state,
struct smbd_smb2_lock_state);
@@ -211,7 +226,150 @@ static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
return tevent_req_post(req, ev);
}
- tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+ locks = talloc_array(state, struct smbd_lock_element, in_lock_count);
+ if (locks == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return tevent_req_post(req, ev);
+ }
+
+ switch (in_locks[0].flags) {
+ case SMB2_LOCK_FLAG_SHARED:
+ case SMB2_LOCK_FLAG_EXCLUSIVE:
+ if (in_lock_count > 1) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ timeout = -1;
+ break;
+
+ case SMB2_LOCK_FLAG_SHARED|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
+ case SMB2_LOCK_FLAG_EXCLUSIVE|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
+ timeout = 0;
+ break;
+
+ case SMB2_LOCK_FLAG_UNLOCK:
+ /* only the first lock gives the UNLOCK bit - see
+ MS-SMB2 3.3.5.14 */
+ isunlock = true;
+ timeout = 0;
+ break;
+
+ default:
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ for (i=0; i<in_lock_count; i++) {
+ uint64_t max_count;
+ bool invalid = false;
+
+ switch (in_locks[i].flags) {
+ case SMB2_LOCK_FLAG_SHARED:
+ case SMB2_LOCK_FLAG_EXCLUSIVE:
+ if (i > 0) {
+ tevent_req_nterror(req,
+ NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ if (isunlock) {
+ tevent_req_nterror(req,
+ NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ break;
+
+ case SMB2_LOCK_FLAG_SHARED|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
+ case SMB2_LOCK_FLAG_EXCLUSIVE|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
+ if (isunlock) {
+ tevent_req_nterror(req,
+ NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ break;
+
+ case SMB2_LOCK_FLAG_UNLOCK:
+ if (!isunlock) {
+ tevent_req_nterror(req,
+ NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ break;
+
+ default:
+ if (isunlock) {
+ /*
+ * is the first element was a UNLOCK
+ * we need to deferr the error response
+ * to the backend, because we need to process
+ * all unlock elements before
+ */
+ invalid = true;
+ break;
+ }
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ locks[i].smbpid = in_smbpid;
+ locks[i].offset = in_locks[i].offset;
+ locks[i].count = in_locks[i].length;
+
+ if (in_locks[i].flags & SMB2_LOCK_FLAG_EXCLUSIVE) {
+ locks[i].brltype = WRITE_LOCK;
+ } else if (in_locks[i].flags & SMB2_LOCK_FLAG_SHARED) {
+ locks[i].brltype = READ_LOCK;
+ } else if (invalid) {
+ /*
+ * this is an invalid UNLOCK element
+ * and the backend needs to test for
+ * brltype != UNLOCK_LOCK and return
+ * NT_STATUS_INVALID_PARAMER
+ */
+ locks[i].brltype = READ_LOCK;
+ } else {
+ locks[i].brltype = UNLOCK_LOCK;
+ }
+
+ max_count = UINT64_MAX - locks[i].offset;
+ if (locks[i].count > max_count) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_LOCK_RANGE);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ if (isunlock) {
+ status = smbd_do_locking(smbreq, fsp,
+ 0,
+ timeout,
+ in_lock_count,
+ locks,
+ 0,
+ NULL,
+ &async);
+ } else {
+ status = smbd_do_locking(smbreq, fsp,
+ 0,
+ timeout,
+ 0,
+ NULL,
+ in_lock_count,
+ locks,
+ &async);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+ status = NT_STATUS_LOCK_NOT_GRANTED;
+ }
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ if (async) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_done(req);
return tevent_req_post(req, ev);
}