/* pull a uint32_t length/ uint16_t ofs/blob triple from a data blob the ptr points to the start of the offset/length pair */ NTSTATUS smb2_pull_s32o16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) { uint32_t ofs, size; if (smb2_oob(buf, ptr, 8)) { return NT_STATUS_INVALID_PARAMETER; } size = IVAL(ptr, 0); ofs = SVAL(ptr, 4); if (ofs == 0) { *blob = data_blob(NULL, 0); return NT_STATUS_OK; } if (smb2_oob(buf, buf->hdr + ofs, size)) { return NT_STATUS_INVALID_PARAMETER; } *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); NT_STATUS_HAVE_NO_MEMORY(blob->data); return NT_STATUS_OK; }
/* pull a uint32_t ofs/ uint32_t length/blob triple from a data blob the ptr points to the start of the offset/length pair */ NTSTATUS smb2_pull_o32s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) { uint32_t ofs, size; if (smb2_oob(buf, ptr, 8)) { return NT_STATUS_BUFFER_TOO_SMALL; } ofs = IVAL(ptr, 0); size = IVAL(ptr, 4); if (ofs == 0 || size == 0) { *blob = data_blob(NULL, 0); return NT_STATUS_OK; } if (smb2_oob(buf, buf->hdr + ofs, size)) { return NT_STATUS_BUFFER_TOO_SMALL; } *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); NT_STATUS_HAVE_NO_MEMORY(blob->data); return NT_STATUS_OK; }
/* push a uint16_t ofs/ uint16_t length/blob triple into a data blob the ofs points to the start of the offset/length pair, and is relative to the body start */ NTSTATUS smb2_push_o16s16_blob(struct smb2_request_buffer *buf, uint16_t ofs, DATA_BLOB blob) { NTSTATUS status; size_t offset; size_t padding_length; size_t padding_fix; uint8_t *ptr = buf->body+ofs; if (buf->dynamic == NULL) { return NT_STATUS_INVALID_PARAMETER; } /* we have only 16 bit for the size */ if (blob.length > 0xFFFF) { return NT_STATUS_INVALID_PARAMETER; } /* check if there're enough room for ofs and size */ if (smb2_oob(buf, ptr, 4)) { return NT_STATUS_INVALID_PARAMETER; } if (blob.data == NULL) { if (blob.length != 0) { return NT_STATUS_INTERNAL_ERROR; } SSVAL(ptr, 0, 0); SSVAL(ptr, 2, 0); return NT_STATUS_OK; } offset = buf->dynamic - buf->hdr; padding_length = smb2_padding_size(offset, 2); offset += padding_length; padding_fix = smb2_padding_fix(buf); SSVAL(ptr, 0, offset); SSVAL(ptr, 2, blob.length); status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix); NT_STATUS_NOT_OK_RETURN(status); memset(buf->dynamic, 0, padding_length); buf->dynamic += padding_length; memcpy(buf->dynamic, blob.data, blob.length); buf->dynamic += blob.length; buf->size += blob.length + padding_length - padding_fix; buf->body_size += blob.length + padding_length; return NT_STATUS_OK; }
/* push a uint32_t length/ uint32_t ofs/blob triple into a data blob the ofs points to the start of the length/offset pair, and is relative to the body start */ NTSTATUS smb2_push_s32o32_blob(struct smb2_request_buffer *buf, uint32_t ofs, DATA_BLOB blob) { NTSTATUS status; size_t offset; size_t padding_length; size_t padding_fix; uint8_t *ptr = buf->body+ofs; if (buf->dynamic == NULL) { return NT_STATUS_INVALID_PARAMETER; } /* check if there're enough room for ofs and size */ if (smb2_oob(buf, ptr, 8)) { return NT_STATUS_BUFFER_TOO_SMALL; } if (blob.length == 0) { SIVAL(ptr, 0, 0); SIVAL(ptr, 4, 0); return NT_STATUS_OK; } offset = buf->dynamic - buf->hdr; padding_length = smb2_padding_size(offset, 8); offset += padding_length; padding_fix = smb2_padding_fix(buf); SIVAL(ptr, 0, blob.length); SIVAL(ptr, 4, offset); status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix); NT_STATUS_NOT_OK_RETURN(status); memset(buf->dynamic, 0, padding_length); buf->dynamic += padding_length; memcpy(buf->dynamic, blob.data, blob.length); buf->dynamic += blob.length; buf->size += blob.length + padding_length - padding_fix; buf->body_size += blob.length + padding_length; return NT_STATUS_OK; }
NTSTATUS smbsrv_recv_smb2_request(void *private_data, DATA_BLOB blob) { struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection); struct smb2srv_request *req; struct timeval cur_time = timeval_current(); uint32_t protocol_version; uint16_t buffer_code; uint32_t dynamic_size; uint32_t flags; smb_conn->statistics.last_request_time = cur_time; /* see if its a special NBT packet */ if (CVAL(blob.data,0) != 0) { DEBUG(2,("Special NBT packet on SMB2 connection")); smbsrv_terminate_connection(smb_conn, "Special NBT packet on SMB2 connection"); return NT_STATUS_OK; } if (blob.length < (NBT_HDR_SIZE + SMB2_MIN_SIZE_NO_BODY)) { DEBUG(2,("Invalid SMB2 packet length count %ld\n", (long)blob.length)); smbsrv_terminate_connection(smb_conn, "Invalid SMB2 packet"); return NT_STATUS_OK; } protocol_version = IVAL(blob.data, NBT_HDR_SIZE); if (protocol_version != SMB2_MAGIC) { DEBUG(2,("Invalid SMB packet: protocol prefix: 0x%08X\n", protocol_version)); smbsrv_terminate_connection(smb_conn, "NON-SMB2 packet"); return NT_STATUS_OK; } req = smb2srv_init_request(smb_conn); NT_STATUS_HAVE_NO_MEMORY(req); req->in.buffer = talloc_steal(req, blob.data); req->in.size = blob.length; req->request_time = cur_time; req->in.allocated = req->in.size; req->in.hdr = req->in.buffer+ NBT_HDR_SIZE; req->in.body = req->in.hdr + SMB2_HDR_BODY; req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE); req->in.dynamic = NULL; req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID); if (req->in.body_size < 2) { /* error handling for this is different for negprot to other packet types */ uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE); if (opcode == SMB2_OP_NEGPROT) { smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot"); return NT_STATUS_OK; } else { smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); return NT_STATUS_OK; } } buffer_code = SVAL(req->in.body, 0); req->in.body_fixed = (buffer_code & ~1); dynamic_size = req->in.body_size - req->in.body_fixed; if (dynamic_size != 0 && (buffer_code & 1)) { req->in.dynamic = req->in.body + req->in.body_fixed; if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) { DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n", dynamic_size)); smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); return NT_STATUS_OK; } } smb2srv_setup_bufinfo(req); /* * TODO: - make sure the length field is 64 * - make sure it's a request */ flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS); /* the first request should never have the related flag set */ if (flags & SMB2_HDR_FLAG_CHAINED) { req->chain_status = NT_STATUS_INVALID_PARAMETER; } return smb2srv_reply(req); }
static void smb2srv_chain_reply(struct smb2srv_request *p_req) { NTSTATUS status; struct smb2srv_request *req; uint32_t chain_offset; uint32_t protocol_version; uint16_t buffer_code; uint32_t dynamic_size; uint32_t flags; uint32_t last_hdr_offset; last_hdr_offset = p_req->in.hdr - p_req->in.buffer; chain_offset = p_req->chain_offset; p_req->chain_offset = 0; if (p_req->in.size < (last_hdr_offset + chain_offset + SMB2_MIN_SIZE_NO_BODY)) { DEBUG(2,("Invalid SMB2 chained packet at offset 0x%X from last hdr 0x%X\n", chain_offset, last_hdr_offset)); smbsrv_terminate_connection(p_req->smb_conn, "Invalid SMB2 chained packet"); return; } protocol_version = IVAL(p_req->in.buffer, last_hdr_offset + chain_offset); if (protocol_version != SMB2_MAGIC) { DEBUG(2,("Invalid SMB chained packet: protocol prefix: 0x%08X\n", protocol_version)); smbsrv_terminate_connection(p_req->smb_conn, "NON-SMB2 chained packet"); return; } req = smb2srv_init_request(p_req->smb_conn); if (!req) { smbsrv_terminate_connection(p_req->smb_conn, "SMB2 chained packet - no memory"); return; } req->in.buffer = talloc_steal(req, p_req->in.buffer); req->in.size = p_req->in.size; req->request_time = p_req->request_time; req->in.allocated = req->in.size; req->in.hdr = req->in.buffer+ last_hdr_offset + chain_offset; req->in.body = req->in.hdr + SMB2_HDR_BODY; req->in.body_size = req->in.size - (last_hdr_offset+ chain_offset + SMB2_HDR_BODY); req->in.dynamic = NULL; req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID); if (req->in.body_size < 2) { /* error handling for this is different for negprot to other packet types */ uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE); if (opcode == SMB2_OP_NEGPROT) { smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot"); } else { smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); } } buffer_code = SVAL(req->in.body, 0); req->in.body_fixed = (buffer_code & ~1); dynamic_size = req->in.body_size - req->in.body_fixed; if (dynamic_size != 0 && (buffer_code & 1)) { req->in.dynamic = req->in.body + req->in.body_fixed; if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) { DEBUG(1,("SMB2 chained request invalid dynamic size 0x%x\n", dynamic_size)); smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); return; } } smb2srv_setup_bufinfo(req); flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS); if (flags & SMB2_HDR_FLAG_CHAINED) { if (p_req->chained_file_handle) { memcpy(req->_chained_file_handle, p_req->_chained_file_handle, sizeof(req->_chained_file_handle)); req->chained_file_handle = req->_chained_file_handle; } req->chained_session_id = p_req->chained_session_id; req->chained_tree_id = p_req->chained_tree_id; req->chain_status = p_req->chain_status; } /* * TODO: - make sure the length field is 64 * - make sure it's a request */ status = smb2srv_reply(req); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); talloc_free(req); return; } }