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;
for (idx=1; idx < count; idx += 3) {
const uint8_t *inhdr = NULL;
+ uint32_t in_flags;
uint8_t *outhdr = NULL;
uint8_t *outbody = NULL;
- uint8_t *outdyn = NULL;
- size_t outdyn_size = 1;
uint32_t next_command_ofs = 0;
struct iovec *current = &vector[idx];
if ((idx + 3) < count) {
/* we have a next command */
- next_command_ofs = SMB2_HDR_BODY + 8 + 8;
- outdyn_size = 8;
+ next_command_ofs = SMB2_HDR_BODY + 8;
}
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 + outdyn_size);
+ SMB2_HDR_BODY + 8);
if (outhdr == NULL) {
return NT_STATUS_NO_MEMORY;
}
outbody = outhdr + SMB2_HDR_BODY;
- outdyn = outbody + 8;
current[0].iov_base = (void *)outhdr;
current[0].iov_len = SMB2_HDR_BODY;
current[1].iov_base = (void *)outbody;
current[1].iov_len = 8;
- current[2].iov_base = (void *)outdyn;
- current[2].iov_len = outdyn_size;
+ current[2].iov_base = NULL;
+ current[2].iov_len = 0;
/* setup the SMB2 header */
SIVAL(outhdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC);
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));
memset(outhdr + SMB2_HDR_SIGNATURE, 0, 16);
/* setup error body header */
- SSVAL(outbody, 0x00, 9);
+ SSVAL(outbody, 0x00, 0x08 + 1);
SSVAL(outbody, 0x02, 0);
SIVAL(outbody, 0x04, 0);
-
- /* setup the dynamic part */
- SCVAL(outdyn, 0x00, 0);
}
req->out.vector = vector;
return NT_STATUS_OK;
}
-static void smbd_server_connection_terminate(struct smbd_server_connection *conn,
- const char *reason)
+void smbd_server_connection_terminate_ex(struct smbd_server_connection *sconn,
+ const char *reason,
+ const char *location)
{
- DEBUG(10,("smbd_server_connection_terminate: reason[%s]\n", reason));
+ DEBUG(10,("smbd_server_connection_terminate_ex: reason[%s] at %s\n",
+ reason, location));
exit_server_cleanly(reason);
}
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);
if (!NT_STATUS_IS_OK(status)) {
return smbd_smb2_request_error(req, status);
}
- return smbd_smb2_request_error(req, NT_STATUS_NOT_IMPLEMENTED);
+ return smbd_smb2_request_process_ioctl(req);
case SMB2_OP_CANCEL:
return smbd_smb2_request_error(req, NT_STATUS_NOT_IMPLEMENTED);
req->current_idx += 3;
- if (req->current_idx > req->in.vector_count) {
+ if (req->current_idx < req->out.vector_count) {
struct timeval zero = timeval_zero();
subreq = tevent_wakeup_send(req,
req->conn->smb2.event_ctx,
talloc_free(mem_pool);
}
-static NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
+NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
NTSTATUS status,
- const char *wherestr,
- DATA_BLOB *info)
+ DATA_BLOB *info,
+ const char *location)
{
uint8_t *outhdr;
uint8_t *outbody;
int i = req->current_idx;
- DEBUG(10,("smbd_smb2_request_error_ex: idx[%d] status[%s] at %s |%s|\n",
- i, nt_errstr(status), wherestr, info ? " +info" : ""));
+ DEBUG(10,("smbd_smb2_request_error_ex: idx[%d] status[%s] |%s| at %s\n",
+ i, nt_errstr(status), info ? " +info" : "",
+ location));
outhdr = (uint8_t *)req->out.vector[i].iov_base;
req->out.vector[i+2].iov_base = (void *)info->data;
req->out.vector[i+2].iov_len = info->length;
} else {
- req->out.vector[i+2].iov_base = (void *)(outbody + 8);
- req->out.vector[i+2].iov_len = 1;
+ req->out.vector[i+2].iov_base = NULL;
+ 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);
}
-NTSTATUS smbd_smb2_request_error_(struct smbd_smb2_request *req,
- NTSTATUS status, const char *wherestr)
-{
- return smbd_smb2_request_error_ex(req, status, wherestr, NULL);
-}
-
NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req,
NTSTATUS status,
- DATA_BLOB body, DATA_BLOB *dyn)
+ DATA_BLOB body, DATA_BLOB *dyn,
+ const char *location)
{
uint8_t *outhdr;
uint8_t *outdyn;
uint32_t next_command_ofs;
DEBUG(10,("smbd_smb2_request_done_ex: "
- "idx[%d] status[%s] body[%u] dyn[%s:%u]\n",
+ "idx[%d] status[%s] body[%u] dyn[%s:%u] at %s\n",
i, nt_errstr(status), (unsigned int)body.length,
dyn ? "yes": "no",
- (unsigned int)(dyn ? dyn->length : 0)));
+ (unsigned int)(dyn ? dyn->length : 0),
+ location));
if (body.length < 2) {
return smbd_smb2_request_error(req, NT_STATUS_INTERNAL_ERROR);
req->out.vector[i+1].iov_len = body.length;
if (dyn) {
- if (dyn->length > 0) {
- req->out.vector[i+2].iov_base = (void *)dyn->data;
- req->out.vector[i+2].iov_len = dyn->length;
- } else {
- req->out.vector[i+2].iov_base = (void *)outdyn;
- req->out.vector[i+2].iov_len = 1;
- }
+ req->out.vector[i+2].iov_base = (void *)dyn->data;
+ req->out.vector[i+2].iov_len = dyn->length;
} else {
req->out.vector[i+2].iov_base = NULL;
req->out.vector[i+2].iov_len = 0;
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;
}
- /* the error packet is the last response in the chain */
SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND, next_command_ofs);
return smbd_smb2_request_reply(req);
}
-NTSTATUS smbd_smb2_request_done(struct smbd_smb2_request *req,
- DATA_BLOB body, DATA_BLOB *dyn)
-{
- return smbd_smb2_request_done_ex(req, NT_STATUS_OK, body, dyn);
-}
-
struct smbd_smb2_request_read_state {
size_t missing;
bool asked_for_header;
return;
}
- /* TODO: validate the incoming request */
+ if (req->in.nbt_hdr[0] != 0x00) {
+ DEBUG(1,("smbd_smb2_request_incoming: ignore NBT[0x%02X] msg\n",
+ req->in.nbt_hdr[0]));
+ talloc_free(req->mem_pool);
+ req = NULL;
+ goto next;
+ }
+
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));
return;
}
+next:
/* ask for the next request (this constructs the main loop) */
subreq = smbd_smb2_request_read_send(conn,conn->smb2.event_ctx, conn);
if (subreq == NULL) {