NTSTATUS smbd_smb2_request_process_tcon(struct smbd_smb2_request *req) { const uint8_t *inbody; uint16_t in_path_offset; uint16_t in_path_length; DATA_BLOB in_path_buffer; char *in_path_string; size_t in_path_string_size; NTSTATUS status; bool ok; struct tevent_req *subreq; status = smbd_smb2_request_verify_sizes(req, 0x09); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); in_path_offset = SVAL(inbody, 0x04); in_path_length = SVAL(inbody, 0x06); if (in_path_offset != (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } if (in_path_length > SMBD_SMB2_IN_DYN_LEN(req)) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } in_path_buffer.data = SMBD_SMB2_IN_DYN_PTR(req); in_path_buffer.length = in_path_length; ok = convert_string_talloc(req, CH_UTF16, CH_UNIX, in_path_buffer.data, in_path_buffer.length, &in_path_string, &in_path_string_size); if (!ok) { return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER); } if (in_path_buffer.length == 0) { in_path_string_size = 0; } if (strlen(in_path_string) != in_path_string_size) { return smbd_smb2_request_error(req, NT_STATUS_BAD_NETWORK_NAME); } subreq = smbd_smb2_tree_connect_send(req, req->sconn->ev_ctx, req, in_path_string); if (subreq == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(subreq, smbd_smb2_request_tcon_done, req); return smbd_smb2_request_pending_queue(req, subreq, 500); }
NTSTATUS smbd_smb2_request_process_close(struct smbd_smb2_request *req) { const uint8_t *inbody; uint16_t in_flags; uint64_t in_file_id_persistent; uint64_t in_file_id_volatile; struct files_struct *in_fsp; NTSTATUS status; struct tevent_req *subreq; status = smbd_smb2_request_verify_sizes(req, 0x18); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); in_flags = SVAL(inbody, 0x02); in_file_id_persistent = BVAL(inbody, 0x08); in_file_id_volatile = BVAL(inbody, 0x10); in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile); if (in_fsp == NULL) { return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); } subreq = smbd_smb2_close_send(req, req->sconn->ev_ctx, req, in_fsp, in_flags); if (subreq == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(subreq, smbd_smb2_request_close_done, req); return smbd_smb2_request_pending_queue(req, subreq, 500); }
NTSTATUS smbd_smb2_request_process_read(struct smbd_smb2_request *req) { struct smbXsrv_connection *xconn = req->xconn; NTSTATUS status; const uint8_t *inbody; uint32_t in_length; uint64_t in_offset; uint64_t in_file_id_persistent; uint64_t in_file_id_volatile; struct files_struct *in_fsp; uint32_t in_minimum_count; uint32_t in_remaining_bytes; struct tevent_req *subreq; status = smbd_smb2_request_verify_sizes(req, 0x31); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); in_length = IVAL(inbody, 0x04); in_offset = BVAL(inbody, 0x08); in_file_id_persistent = BVAL(inbody, 0x10); in_file_id_volatile = BVAL(inbody, 0x18); in_minimum_count = IVAL(inbody, 0x20); in_remaining_bytes = IVAL(inbody, 0x28); /* check the max read size */ if (in_length > xconn->smb2.server.max_read) { DEBUG(2,("smbd_smb2_request_process_read: " "client ignored max read: %s: 0x%08X: 0x%08X\n", __location__, in_length, xconn->smb2.server.max_read)); return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } status = smbd_smb2_request_verify_creditcharge(req, in_length); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile); if (in_fsp == NULL) { return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); } subreq = smbd_smb2_read_send(req, req->sconn->ev_ctx, req, in_fsp, in_length, in_offset, in_minimum_count, in_remaining_bytes); if (subreq == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(subreq, smbd_smb2_request_read_done, req); return smbd_smb2_request_pending_queue(req, subreq, 500); }
NTSTATUS smbd_smb2_request_process_sesssetup(struct smbd_smb2_request *smb2req) { const uint8_t *inhdr; const uint8_t *inbody; uint64_t in_session_id; uint8_t in_flags; uint8_t in_security_mode; uint64_t in_previous_session_id; uint16_t in_security_offset; uint16_t in_security_length; DATA_BLOB in_security_buffer; NTSTATUS status; struct tevent_req *subreq; status = smbd_smb2_request_verify_sizes(smb2req, 0x19); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(smb2req, status); } inhdr = SMBD_SMB2_IN_HDR_PTR(smb2req); inbody = SMBD_SMB2_IN_BODY_PTR(smb2req); in_session_id = BVAL(inhdr, SMB2_HDR_SESSION_ID); in_flags = CVAL(inbody, 0x02); in_security_mode = CVAL(inbody, 0x03); /* Capabilities = IVAL(inbody, 0x04) */ /* Channel = IVAL(inbody, 0x08) */ in_security_offset = SVAL(inbody, 0x0C); in_security_length = SVAL(inbody, 0x0E); in_previous_session_id = BVAL(inbody, 0x10); if (in_security_offset != (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(smb2req))) { return smbd_smb2_request_error(smb2req, NT_STATUS_INVALID_PARAMETER); } if (in_security_length > SMBD_SMB2_IN_DYN_LEN(smb2req)) { return smbd_smb2_request_error(smb2req, NT_STATUS_INVALID_PARAMETER); } in_security_buffer.data = SMBD_SMB2_IN_DYN_PTR(smb2req); in_security_buffer.length = in_security_length; subreq = smbd_smb2_session_setup_wrap_send(smb2req, smb2req->sconn->ev_ctx, smb2req, in_session_id, in_flags, in_security_mode, in_previous_session_id, in_security_buffer); if (subreq == NULL) { return smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(subreq, smbd_smb2_request_sesssetup_done, smb2req); return smbd_smb2_request_pending_queue(smb2req, subreq, 500); }
NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req) { struct smbXsrv_connection *xconn = req->xconn; NTSTATUS status; const uint8_t *inbody; uint16_t in_flags; uint32_t in_output_buffer_length; uint64_t in_file_id_persistent; uint64_t in_file_id_volatile; struct files_struct *in_fsp; uint64_t in_completion_filter; struct tevent_req *subreq; status = smbd_smb2_request_verify_sizes(req, 0x20); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); in_flags = SVAL(inbody, 0x02); in_output_buffer_length = IVAL(inbody, 0x04); in_file_id_persistent = BVAL(inbody, 0x08); in_file_id_volatile = BVAL(inbody, 0x10); in_completion_filter = IVAL(inbody, 0x18); /* * 0x00010000 is what Windows 7 uses, * Windows 2008 uses 0x00080000 */ if (in_output_buffer_length > xconn->smb2.server.max_trans) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } status = smbd_smb2_request_verify_creditcharge(req, in_output_buffer_length); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile); if (in_fsp == NULL) { return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); } subreq = smbd_smb2_notify_send(req, req->sconn->ev_ctx, req, in_fsp, in_flags, in_output_buffer_length, in_completion_filter); if (subreq == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(subreq, smbd_smb2_request_notify_done, req); return smbd_smb2_request_pending_queue(req, subreq, 500); }
NTSTATUS smbd_smb2_request_process_write(struct smbd_smb2_request *req) { struct smbXsrv_connection *xconn = req->xconn; NTSTATUS status; const uint8_t *inbody; uint16_t in_data_offset; uint32_t in_data_length; DATA_BLOB in_data_buffer; uint64_t in_offset; uint64_t in_file_id_persistent; uint64_t in_file_id_volatile; struct files_struct *in_fsp; uint32_t in_flags; size_t in_dyn_len = 0; uint8_t *in_dyn_ptr = NULL; struct tevent_req *subreq; status = smbd_smb2_request_verify_sizes(req, 0x31); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); in_data_offset = SVAL(inbody, 0x02); in_data_length = IVAL(inbody, 0x04); in_offset = BVAL(inbody, 0x08); in_file_id_persistent = BVAL(inbody, 0x10); in_file_id_volatile = BVAL(inbody, 0x18); in_flags = IVAL(inbody, 0x2C); if (in_data_offset != (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } if (req->smb1req != NULL && req->smb1req->unread_bytes > 0) { in_dyn_ptr = NULL; in_dyn_len = req->smb1req->unread_bytes; } else { in_dyn_ptr = SMBD_SMB2_IN_DYN_PTR(req); in_dyn_len = SMBD_SMB2_IN_DYN_LEN(req); } if (in_data_length > in_dyn_len) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } /* check the max write size */ if (in_data_length > xconn->smb2.server.max_write) { DEBUG(2,("smbd_smb2_request_process_write : " "client ignored max write :%s: 0x%08X: 0x%08X\n", __location__, in_data_length, xconn->smb2.server.max_write)); return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } /* * Note: that in_dyn_ptr is NULL for the recvfile case. */ in_data_buffer.data = in_dyn_ptr; in_data_buffer.length = in_data_length; status = smbd_smb2_request_verify_creditcharge(req, in_data_length); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile); if (in_fsp == NULL) { return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); } subreq = smbd_smb2_write_send(req, req->sconn->ev_ctx, req, in_fsp, in_data_buffer, in_offset, in_flags); if (subreq == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(subreq, smbd_smb2_request_write_done, req); return smbd_smb2_request_pending_queue(req, subreq, 500); }
NTSTATUS smbd_smb2_request_process_getinfo(struct smbd_smb2_request *req) { NTSTATUS status; const uint8_t *inbody; uint8_t in_info_type; uint8_t in_file_info_class; uint32_t in_output_buffer_length; uint16_t in_input_buffer_offset; uint32_t in_input_buffer_length; DATA_BLOB in_input_buffer; uint32_t in_additional_information; uint32_t in_flags; uint64_t in_file_id_persistent; uint64_t in_file_id_volatile; struct files_struct *in_fsp; struct tevent_req *subreq; status = smbd_smb2_request_verify_sizes(req, 0x29); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); in_info_type = CVAL(inbody, 0x02); in_file_info_class = CVAL(inbody, 0x03); in_output_buffer_length = IVAL(inbody, 0x04); in_input_buffer_offset = SVAL(inbody, 0x08); /* 0x0A 2 bytes reserved */ in_input_buffer_length = IVAL(inbody, 0x0C); in_additional_information = IVAL(inbody, 0x10); in_flags = IVAL(inbody, 0x14); in_file_id_persistent = BVAL(inbody, 0x18); in_file_id_volatile = BVAL(inbody, 0x20); if (in_input_buffer_offset == 0 && in_input_buffer_length == 0) { /* This is ok */ } else if (in_input_buffer_offset != (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } if (in_input_buffer_length > SMBD_SMB2_IN_DYN_LEN(req)) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } in_input_buffer.data = SMBD_SMB2_IN_DYN_PTR(req); in_input_buffer.length = in_input_buffer_length; if (in_input_buffer.length > req->sconn->smb2.max_trans) { DEBUG(2,("smbd_smb2_request_process_getinfo: " "client ignored max trans: %s: 0x%08X: 0x%08X\n", __location__, (unsigned)in_input_buffer.length, (unsigned)req->sconn->smb2.max_trans)); return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } if (in_output_buffer_length > req->sconn->smb2.max_trans) { DEBUG(2,("smbd_smb2_request_process_getinfo: " "client ignored max trans: %s: 0x%08X: 0x%08X\n", __location__, in_output_buffer_length, req->sconn->smb2.max_trans)); return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } status = smbd_smb2_request_verify_creditcharge(req, MAX(in_input_buffer.length,in_output_buffer_length)); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile); if (in_fsp == NULL) { return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); } subreq = smbd_smb2_getinfo_send(req, req->sconn->ev_ctx, req, in_fsp, in_info_type, in_file_info_class, in_output_buffer_length, in_input_buffer, in_additional_information, in_flags); if (subreq == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(subreq, smbd_smb2_request_getinfo_done, req); return smbd_smb2_request_pending_queue(req, subreq, 500); }
NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req) { struct smbXsrv_connection *xconn = req->xconn; NTSTATUS status; const uint8_t *inbody; const uint8_t *indyn = NULL; DATA_BLOB outbody; DATA_BLOB outdyn; DATA_BLOB negprot_spnego_blob; uint16_t security_offset; DATA_BLOB security_buffer; size_t expected_dyn_size = 0; size_t c; uint16_t security_mode; uint16_t dialect_count; uint16_t in_security_mode; uint32_t in_capabilities; DATA_BLOB in_guid_blob; struct GUID in_guid; struct smb2_negotiate_contexts in_c = { .num_contexts = 0, }; struct smb2_negotiate_context *in_preauth = NULL; struct smb2_negotiate_context *in_cipher = NULL; struct smb2_negotiate_contexts out_c = { .num_contexts = 0, }; DATA_BLOB out_negotiate_context_blob = data_blob_null; uint32_t out_negotiate_context_offset = 0; uint16_t out_negotiate_context_count = 0; uint16_t dialect = 0; uint32_t capabilities; DATA_BLOB out_guid_blob; struct GUID out_guid; enum protocol_types protocol = PROTOCOL_NONE; uint32_t max_limit; uint32_t max_trans = lp_smb2_max_trans(); uint32_t max_read = lp_smb2_max_read(); uint32_t max_write = lp_smb2_max_write(); NTTIME now = timeval_to_nttime(&req->request_time); bool signing_required = true; bool ok; status = smbd_smb2_request_verify_sizes(req, 0x24); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); dialect_count = SVAL(inbody, 0x02); in_security_mode = SVAL(inbody, 0x04); in_capabilities = IVAL(inbody, 0x08); in_guid_blob = data_blob_const(inbody + 0x0C, 16); if (dialect_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } status = GUID_from_ndr_blob(&in_guid_blob, &in_guid); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } expected_dyn_size = dialect_count * 2; if (SMBD_SMB2_IN_DYN_LEN(req) < expected_dyn_size) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } indyn = SMBD_SMB2_IN_DYN_PTR(req); protocol = smbd_smb2_protocol_dialect_match(indyn, dialect_count, &dialect); for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_server_max_protocol() < PROTOCOL_SMB2_10) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_2FF) { if (xconn->smb2.allow_2ff) { xconn->smb2.allow_2ff = false; protocol = PROTOCOL_SMB2_10; break; } } } if (protocol == PROTOCOL_NONE) { return smbd_smb2_request_error(req, NT_STATUS_NOT_SUPPORTED); } if (protocol >= PROTOCOL_SMB3_10) { uint32_t in_negotiate_context_offset = 0; uint16_t in_negotiate_context_count = 0; DATA_BLOB in_negotiate_context_blob = data_blob_null; size_t ofs; in_negotiate_context_offset = IVAL(inbody, 0x1C); in_negotiate_context_count = SVAL(inbody, 0x20); ofs = SMB2_HDR_BODY; ofs += SMBD_SMB2_IN_BODY_LEN(req); ofs += expected_dyn_size; if ((ofs % 8) != 0) { ofs += 8 - (ofs % 8); } if (in_negotiate_context_offset != ofs) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } ofs -= SMB2_HDR_BODY; ofs -= SMBD_SMB2_IN_BODY_LEN(req); if (SMBD_SMB2_IN_DYN_LEN(req) < ofs) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } in_negotiate_context_blob = data_blob_const(indyn, SMBD_SMB2_IN_DYN_LEN(req)); in_negotiate_context_blob.data += ofs; in_negotiate_context_blob.length -= ofs; status = smb2_negotiate_context_parse(req, in_negotiate_context_blob, &in_c); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } if (in_negotiate_context_count != in_c.num_contexts) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } } if ((dialect != SMB2_DIALECT_REVISION_2FF) && (protocol >= PROTOCOL_SMB2_10) && !GUID_all_zero(&in_guid)) { ok = remote_arch_cache_update(&in_guid); if (!ok) { return smbd_smb2_request_error( req, NT_STATUS_UNSUCCESSFUL); } } switch (get_remote_arch()) { case RA_VISTA: case RA_SAMBA: case RA_CIFSFS: case RA_OSX: break; default: set_remote_arch(RA_VISTA); break; } fstr_sprintf(remote_proto, "SMB%X_%02X", (dialect >> 8) & 0xFF, dialect & 0xFF); reload_services(req->sconn, conn_snum_used, true); DEBUG(3,("Selected protocol %s\n", remote_proto)); in_preauth = smb2_negotiate_context_find(&in_c, SMB2_PREAUTH_INTEGRITY_CAPABILITIES); if (protocol >= PROTOCOL_SMB3_10 && in_preauth == NULL) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } in_cipher = smb2_negotiate_context_find(&in_c, SMB2_ENCRYPTION_CAPABILITIES); /* negprot_spnego() returns a the server guid in the first 16 bytes */ negprot_spnego_blob = negprot_spnego(req, xconn); if (negprot_spnego_blob.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } if (negprot_spnego_blob.length < 16) { return smbd_smb2_request_error(req, NT_STATUS_INTERNAL_ERROR); } security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; /* * We use xconn->smb1.signing_state as that's already present * and used lpcfg_server_signing_allowed() to get the correct * defaults, e.g. signing_required for an ad_dc. */ signing_required = smb_signing_is_mandatory(xconn->smb1.signing_state); if (signing_required) { security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED; } capabilities = 0; if (lp_host_msdfs()) { capabilities |= SMB2_CAP_DFS; } if (protocol >= PROTOCOL_SMB2_10 && lp_smb2_leases() && lp_oplocks(GLOBAL_SECTION_SNUM) && !lp_kernel_oplocks(GLOBAL_SECTION_SNUM)) { capabilities |= SMB2_CAP_LEASING; } if ((protocol >= PROTOCOL_SMB2_24) && (lp_smb_encrypt(-1) != SMB_SIGNING_OFF) && (in_capabilities & SMB2_CAP_ENCRYPTION)) { capabilities |= SMB2_CAP_ENCRYPTION; } /* * 0x10000 (65536) is the maximum allowed message size * for SMB 2.0 */ max_limit = 0x10000; if (protocol >= PROTOCOL_SMB2_10) { int p = 0; if (tsocket_address_is_inet(req->sconn->local_address, "ip")) { p = tsocket_address_inet_port(req->sconn->local_address); } /* largeMTU is not supported over NBT (tcp port 139) */ if (p != NBT_SMB_PORT) { capabilities |= SMB2_CAP_LARGE_MTU; xconn->smb2.credits.multicredit = true; /* * We allow up to almost 16MB. * * The maximum PDU size is 0xFFFFFF (16776960) * and we need some space for the header. */ max_limit = 0xFFFF00; } } /* * the defaults are 8MB, but we'll limit this to max_limit based on * the dialect (64kb for SMB 2.0, 8MB for SMB >= 2.1 with LargeMTU) * * user configured values exceeding the limits will be overwritten, * only smaller values will be accepted */ max_trans = MIN(max_limit, lp_smb2_max_trans()); max_read = MIN(max_limit, lp_smb2_max_read()); max_write = MIN(max_limit, lp_smb2_max_write()); if (in_preauth != NULL) { size_t needed = 4; uint16_t hash_count; uint16_t salt_length; uint16_t selected_preauth = 0; const uint8_t *p; uint8_t buf[38]; DATA_BLOB b; size_t i; if (in_preauth->data.length < needed) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } hash_count = SVAL(in_preauth->data.data, 0); salt_length = SVAL(in_preauth->data.data, 2); if (hash_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } p = in_preauth->data.data + needed; needed += hash_count * 2; needed += salt_length; if (in_preauth->data.length < needed) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } for (i=0; i < hash_count; i++) { uint16_t v; v = SVAL(p, 0); p += 2; if (v == SMB2_PREAUTH_INTEGRITY_SHA512) { selected_preauth = v; break; } } if (selected_preauth == 0) { return smbd_smb2_request_error(req, NT_STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP); } SSVAL(buf, 0, 1); /* HashAlgorithmCount */ SSVAL(buf, 2, 32); /* SaltLength */ SSVAL(buf, 4, selected_preauth); generate_random_buffer(buf + 6, 32); b = data_blob_const(buf, sizeof(buf)); status = smb2_negotiate_context_add(req, &out_c, SMB2_PREAUTH_INTEGRITY_CAPABILITIES, b); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } req->preauth = &req->xconn->smb2.preauth; } if (in_cipher != NULL) { size_t needed = 2; uint16_t cipher_count; const uint8_t *p; uint8_t buf[4]; DATA_BLOB b; size_t i; bool aes_128_ccm_supported = false; bool aes_128_gcm_supported = false; capabilities &= ~SMB2_CAP_ENCRYPTION; if (in_cipher->data.length < needed) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } cipher_count = SVAL(in_cipher->data.data, 0); if (cipher_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } p = in_cipher->data.data + needed; needed += cipher_count * 2; if (in_cipher->data.length < needed) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } for (i=0; i < cipher_count; i++) { uint16_t v; v = SVAL(p, 0); p += 2; if (v == SMB2_ENCRYPTION_AES128_GCM) { aes_128_gcm_supported = true; } if (v == SMB2_ENCRYPTION_AES128_CCM) { aes_128_ccm_supported = true; } } /* * For now we preferr CCM because our implementation * is faster than GCM, see bug #11451. */ if (aes_128_ccm_supported) { xconn->smb2.server.cipher = SMB2_ENCRYPTION_AES128_CCM; } else if (aes_128_gcm_supported) { xconn->smb2.server.cipher = SMB2_ENCRYPTION_AES128_GCM; } SSVAL(buf, 0, 1); /* ChiperCount */ SSVAL(buf, 2, xconn->smb2.server.cipher); b = data_blob_const(buf, sizeof(buf)); status = smb2_negotiate_context_add(req, &out_c, SMB2_ENCRYPTION_CAPABILITIES, b); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } } if (capabilities & SMB2_CAP_ENCRYPTION) { xconn->smb2.server.cipher = SMB2_ENCRYPTION_AES128_CCM; } if (protocol >= PROTOCOL_SMB2_22 && xconn->client->server_multi_channel_enabled) { if (in_capabilities & SMB2_CAP_MULTI_CHANNEL) { capabilities |= SMB2_CAP_MULTI_CHANNEL; } } security_offset = SMB2_HDR_BODY + 0x40; #if 1 /* Try SPNEGO auth... */ security_buffer = data_blob_const(negprot_spnego_blob.data + 16, negprot_spnego_blob.length - 16); #else /* for now we want raw NTLMSSP */ security_buffer = data_blob_const(NULL, 0); #endif if (out_c.num_contexts != 0) { status = smb2_negotiate_context_push(req, &out_negotiate_context_blob, out_c); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } } if (out_negotiate_context_blob.length != 0) { static const uint8_t zeros[8]; size_t pad = 0; size_t ofs; outdyn = data_blob_dup_talloc(req, security_buffer); if (outdyn.length != security_buffer.length) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } ofs = security_offset + security_buffer.length; if ((ofs % 8) != 0) { pad = 8 - (ofs % 8); } ofs += pad; ok = data_blob_append(req, &outdyn, zeros, pad); if (!ok) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } ok = data_blob_append(req, &outdyn, out_negotiate_context_blob.data, out_negotiate_context_blob.length); if (!ok) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } out_negotiate_context_offset = ofs; out_negotiate_context_count = out_c.num_contexts; } else { outdyn = security_buffer; } out_guid_blob = data_blob_const(negprot_spnego_blob.data, 16); status = GUID_from_ndr_blob(&out_guid_blob, &out_guid); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } outbody = smbd_smb2_generate_outbody(req, 0x40); if (outbody.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } SSVAL(outbody.data, 0x00, 0x40 + 1); /* struct size */ SSVAL(outbody.data, 0x02, security_mode); /* security mode */ SSVAL(outbody.data, 0x04, dialect); /* dialect revision */ SSVAL(outbody.data, 0x06, out_negotiate_context_count); /* reserved/NegotiateContextCount */ memcpy(outbody.data + 0x08, out_guid_blob.data, 16); /* server guid */ SIVAL(outbody.data, 0x18, capabilities); /* capabilities */ SIVAL(outbody.data, 0x1C, max_trans); /* max transact size */ SIVAL(outbody.data, 0x20, max_read); /* max read size */ SIVAL(outbody.data, 0x24, max_write); /* max write size */ SBVAL(outbody.data, 0x28, now); /* system time */ SBVAL(outbody.data, 0x30, 0); /* server start time */ SSVAL(outbody.data, 0x38, security_offset); /* security buffer offset */ SSVAL(outbody.data, 0x3A, security_buffer.length); /* security buffer length */ SIVAL(outbody.data, 0x3C, out_negotiate_context_offset); /* reserved/NegotiateContextOffset */ req->sconn->using_smb2 = true; if (dialect != SMB2_DIALECT_REVISION_2FF) { struct smbXsrv_client_global0 *global0 = NULL; status = smbXsrv_connection_init_tables(xconn, protocol); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } xconn->smb2.client.capabilities = in_capabilities; xconn->smb2.client.security_mode = in_security_mode; xconn->smb2.client.guid = in_guid; xconn->smb2.client.num_dialects = dialect_count; xconn->smb2.client.dialects = talloc_array(xconn, uint16_t, dialect_count); if (xconn->smb2.client.dialects == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } for (c=0; c < dialect_count; c++) { xconn->smb2.client.dialects[c] = SVAL(indyn, c*2); } xconn->smb2.server.capabilities = capabilities; xconn->smb2.server.security_mode = security_mode; xconn->smb2.server.guid = out_guid; xconn->smb2.server.dialect = dialect; xconn->smb2.server.max_trans = max_trans; xconn->smb2.server.max_read = max_read; xconn->smb2.server.max_write = max_write; if (xconn->protocol < PROTOCOL_SMB2_10) { /* * SMB2_02 doesn't support client guids */ return smbd_smb2_request_done(req, outbody, &outdyn); } if (!xconn->client->server_multi_channel_enabled) { /* * Only deal with the client guid database * if multi-channel is enabled. */ return smbd_smb2_request_done(req, outbody, &outdyn); } if (xconn->smb2.client.guid_verified) { /* * The connection was passed from another * smbd process. */ return smbd_smb2_request_done(req, outbody, &outdyn); } status = smb2srv_client_lookup_global(xconn->client, xconn->smb2.client.guid, req, &global0); /* * TODO: check for races... */ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECTID_NOT_FOUND)) { /* * This stores the new client information in * smbXsrv_client_global.tdb */ xconn->client->global->client_guid = xconn->smb2.client.guid; status = smbXsrv_client_update(xconn->client); if (!NT_STATUS_IS_OK(status)) { return status; } xconn->smb2.client.guid_verified = true; } else if (NT_STATUS_IS_OK(status)) { status = smb2srv_client_connection_pass(req, global0); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } smbd_server_connection_terminate(xconn, "passed connection"); return NT_STATUS_OBJECTID_EXISTS; } else { return smbd_smb2_request_error(req, status); } } return smbd_smb2_request_done(req, outbody, &outdyn); }
NTSTATUS smbd_smb2_request_process_create(struct smbd_smb2_request *smb2req) { const uint8_t *inbody; const struct iovec *indyniov; uint8_t in_oplock_level; uint32_t in_impersonation_level; uint32_t in_desired_access; uint32_t in_file_attributes; uint32_t in_share_access; uint32_t in_create_disposition; uint32_t in_create_options; uint16_t in_name_offset; uint16_t in_name_length; DATA_BLOB in_name_buffer; char *in_name_string; size_t in_name_string_size; uint32_t name_offset = 0; uint32_t name_available_length = 0; uint32_t in_context_offset; uint32_t in_context_length; DATA_BLOB in_context_buffer; struct smb2_create_blobs in_context_blobs; uint32_t context_offset = 0; uint32_t context_available_length = 0; uint32_t dyn_offset; NTSTATUS status; bool ok; struct tevent_req *tsubreq; status = smbd_smb2_request_verify_sizes(smb2req, 0x39); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(smb2req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(smb2req); in_oplock_level = CVAL(inbody, 0x03); in_impersonation_level = IVAL(inbody, 0x04); in_desired_access = IVAL(inbody, 0x18); in_file_attributes = IVAL(inbody, 0x1C); in_share_access = IVAL(inbody, 0x20); in_create_disposition = IVAL(inbody, 0x24); in_create_options = IVAL(inbody, 0x28); in_name_offset = SVAL(inbody, 0x2C); in_name_length = SVAL(inbody, 0x2E); in_context_offset = IVAL(inbody, 0x30); in_context_length = IVAL(inbody, 0x34); /* * First check if the dynamic name and context buffers * are correctly specified. * * Note: That we don't check if the name and context buffers * overlap */ dyn_offset = SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(smb2req); if (in_name_offset == 0 && in_name_length == 0) { /* This is ok */ name_offset = 0; } else if (in_name_offset < dyn_offset) { return smbd_smb2_request_error(smb2req, NT_STATUS_INVALID_PARAMETER); } else { name_offset = in_name_offset - dyn_offset; } indyniov = SMBD_SMB2_IN_DYN_IOV(smb2req); if (name_offset > indyniov->iov_len) { return smbd_smb2_request_error(smb2req, NT_STATUS_INVALID_PARAMETER); } name_available_length = indyniov->iov_len - name_offset; if (in_name_length > name_available_length) { return smbd_smb2_request_error(smb2req, NT_STATUS_INVALID_PARAMETER); } in_name_buffer.data = (uint8_t *)indyniov->iov_base + name_offset; in_name_buffer.length = in_name_length; if (in_context_offset == 0 && in_context_length == 0) { /* This is ok */ context_offset = 0; } else if (in_context_offset < dyn_offset) { return smbd_smb2_request_error(smb2req, NT_STATUS_INVALID_PARAMETER); } else { context_offset = in_context_offset - dyn_offset; } if (context_offset > indyniov->iov_len) { return smbd_smb2_request_error(smb2req, NT_STATUS_INVALID_PARAMETER); } context_available_length = indyniov->iov_len - context_offset; if (in_context_length > context_available_length) { return smbd_smb2_request_error(smb2req, NT_STATUS_INVALID_PARAMETER); } in_context_buffer.data = (uint8_t *)indyniov->iov_base + context_offset; in_context_buffer.length = in_context_length; /* * Now interpret the name and context buffers */ ok = convert_string_talloc(smb2req, CH_UTF16, CH_UNIX, in_name_buffer.data, in_name_buffer.length, &in_name_string, &in_name_string_size); if (!ok) { return smbd_smb2_request_error(smb2req, NT_STATUS_ILLEGAL_CHARACTER); } if (in_name_buffer.length == 0) { in_name_string_size = 0; } if (strlen(in_name_string) != in_name_string_size) { return smbd_smb2_request_error(smb2req, NT_STATUS_OBJECT_NAME_INVALID); } ZERO_STRUCT(in_context_blobs); status = smb2_create_blob_parse(smb2req, in_context_buffer, &in_context_blobs); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(smb2req, status); } tsubreq = smbd_smb2_create_send(smb2req, smb2req->sconn->ev_ctx, smb2req, in_oplock_level, in_impersonation_level, in_desired_access, in_file_attributes, in_share_access, in_create_disposition, in_create_options, in_name_string, in_context_blobs); if (tsubreq == NULL) { smb2req->subreq = NULL; return smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(tsubreq, smbd_smb2_request_create_done, smb2req); return smbd_smb2_request_pending_queue(smb2req, tsubreq, 500); }
NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req) { NTSTATUS status; const uint8_t *inbody; const uint8_t *indyn = NULL; DATA_BLOB outbody; DATA_BLOB outdyn; DATA_BLOB negprot_spnego_blob; uint16_t security_offset; DATA_BLOB security_buffer; size_t expected_dyn_size = 0; size_t c; uint16_t security_mode; uint16_t dialect_count; uint16_t in_security_mode; uint32_t in_capabilities; DATA_BLOB in_guid_blob; struct GUID in_guid; uint16_t dialect = 0; uint32_t capabilities; DATA_BLOB out_guid_blob; struct GUID out_guid; enum protocol_types protocol = PROTOCOL_NONE; uint32_t max_limit; uint32_t max_trans = lp_smb2_max_trans(); uint32_t max_read = lp_smb2_max_read(); uint32_t max_write = lp_smb2_max_write(); NTTIME now = timeval_to_nttime(&req->request_time); status = smbd_smb2_request_verify_sizes(req, 0x24); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); dialect_count = SVAL(inbody, 0x02); in_security_mode = SVAL(inbody, 0x04); in_capabilities = IVAL(inbody, 0x08); in_guid_blob = data_blob_const(inbody + 0x0C, 16); if (dialect_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } status = GUID_from_ndr_blob(&in_guid_blob, &in_guid); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } expected_dyn_size = dialect_count * 2; if (SMBD_SMB2_IN_DYN_LEN(req) < expected_dyn_size) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } indyn = SMBD_SMB2_IN_DYN_PTR(req); for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB3_00) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB3_00) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB3_DIALECT_REVISION_300) { protocol = PROTOCOL_SMB3_00; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_24) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_24) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_224) { protocol = PROTOCOL_SMB2_24; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_22) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_22) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_222) { protocol = PROTOCOL_SMB2_22; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_10) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_10) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_210) { protocol = PROTOCOL_SMB2_10; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_02) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_02) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_202) { protocol = PROTOCOL_SMB2_02; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_10) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_2FF) { if (req->sconn->smb2.negprot_2ff) { req->sconn->smb2.negprot_2ff = false; protocol = PROTOCOL_SMB2_10; break; } } } if (protocol == PROTOCOL_NONE) { return smbd_smb2_request_error(req, NT_STATUS_NOT_SUPPORTED); } if (get_remote_arch() != RA_SAMBA) { set_remote_arch(RA_VISTA); } fstr_sprintf(remote_proto, "SMB%X_%02X", (dialect >> 8) & 0xFF, dialect & 0xFF); reload_services(req->sconn, conn_snum_used, true); DEBUG(3,("Selected protocol %s\n", remote_proto)); /* negprot_spnego() returns a the server guid in the first 16 bytes */ negprot_spnego_blob = negprot_spnego(req, req->sconn); if (negprot_spnego_blob.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } if (negprot_spnego_blob.length < 16) { return smbd_smb2_request_error(req, NT_STATUS_INTERNAL_ERROR); } security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; if (lp_server_signing() == SMB_SIGNING_REQUIRED) { security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED; } capabilities = 0; if (lp_host_msdfs()) { capabilities |= SMB2_CAP_DFS; } if ((protocol >= PROTOCOL_SMB2_24) && (lp_smb_encrypt(-1) != SMB_SIGNING_OFF)) { if (in_capabilities & SMB2_CAP_ENCRYPTION) { capabilities |= SMB2_CAP_ENCRYPTION; } } /* * 0x10000 (65536) is the maximum allowed message size * for SMB 2.0 */ max_limit = 0x10000; if (protocol >= PROTOCOL_SMB2_10) { int p = 0; if (tsocket_address_is_inet(req->sconn->local_address, "ip")) { p = tsocket_address_inet_port(req->sconn->local_address); } /* largeMTU is not supported over NBT (tcp port 139) */ if (p != NBT_SMB_PORT) { capabilities |= SMB2_CAP_LARGE_MTU; req->sconn->smb2.supports_multicredit = true; /* SMB >= 2.1 has 1 MB of allowed size */ max_limit = 0x100000; /* 1MB */ } } /* * the defaults are 1MB, but we'll limit this to max_limit based on * the dialect (64kb for SMB2.0, 1MB for SMB2.1 with LargeMTU) * * user configured values exceeding the limits will be overwritten, * only smaller values will be accepted */ max_trans = MIN(max_limit, lp_smb2_max_trans()); max_read = MIN(max_limit, lp_smb2_max_read()); max_write = MIN(max_limit, lp_smb2_max_write()); security_offset = SMB2_HDR_BODY + 0x40; #if 1 /* Try SPNEGO auth... */ security_buffer = data_blob_const(negprot_spnego_blob.data + 16, negprot_spnego_blob.length - 16); #else /* for now we want raw NTLMSSP */ security_buffer = data_blob_const(NULL, 0); #endif out_guid_blob = data_blob_const(negprot_spnego_blob.data, 16); status = GUID_from_ndr_blob(&out_guid_blob, &out_guid); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } outbody = data_blob_talloc(req->out.vector, NULL, 0x40); if (outbody.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } SSVAL(outbody.data, 0x00, 0x40 + 1); /* struct size */ SSVAL(outbody.data, 0x02, security_mode); /* security mode */ SSVAL(outbody.data, 0x04, dialect); /* dialect revision */ SSVAL(outbody.data, 0x06, 0); /* reserved */ memcpy(outbody.data + 0x08, out_guid_blob.data, 16); /* server guid */ SIVAL(outbody.data, 0x18, capabilities); /* capabilities */ SIVAL(outbody.data, 0x1C, max_trans); /* max transact size */ SIVAL(outbody.data, 0x20, max_read); /* max read size */ SIVAL(outbody.data, 0x24, max_write); /* max write size */ SBVAL(outbody.data, 0x28, now); /* system time */ SBVAL(outbody.data, 0x30, 0); /* server start time */ SSVAL(outbody.data, 0x38, security_offset); /* security buffer offset */ SSVAL(outbody.data, 0x3A, security_buffer.length); /* security buffer length */ SIVAL(outbody.data, 0x3C, 0); /* reserved */ outdyn = security_buffer; req->sconn->using_smb2 = true; if (dialect != SMB2_DIALECT_REVISION_2FF) { struct smbXsrv_connection *conn = req->sconn->conn; status = smbXsrv_connection_init_tables(conn, protocol); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } conn->smb2.client.capabilities = in_capabilities; conn->smb2.client.security_mode = in_security_mode; conn->smb2.client.guid = in_guid; conn->smb2.client.num_dialects = dialect_count; conn->smb2.client.dialects = talloc_array(conn, uint16_t, dialect_count); if (conn->smb2.client.dialects == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } for (c=0; c < dialect_count; c++) { conn->smb2.client.dialects[c] = SVAL(indyn, c*2); } conn->smb2.server.capabilities = capabilities; conn->smb2.server.security_mode = security_mode; conn->smb2.server.guid = out_guid; conn->smb2.server.dialect = dialect; conn->smb2.server.max_trans = max_trans; conn->smb2.server.max_read = max_read; conn->smb2.server.max_write = max_write; req->sconn->smb2.max_trans = max_trans; req->sconn->smb2.max_read = max_read; req->sconn->smb2.max_write = max_write; } return smbd_smb2_request_done(req, outbody, &outdyn); }
NTSTATUS smbd_smb2_request_process_query_directory(struct smbd_smb2_request *req) { NTSTATUS status; const uint8_t *inbody; uint8_t in_file_info_class; uint8_t in_flags; uint32_t in_file_index; uint64_t in_file_id_persistent; uint64_t in_file_id_volatile; struct files_struct *in_fsp; uint16_t in_file_name_offset; uint16_t in_file_name_length; DATA_BLOB in_file_name_buffer; char *in_file_name_string; size_t in_file_name_string_size; uint32_t in_output_buffer_length; struct tevent_req *subreq; bool ok; status = smbd_smb2_request_verify_sizes(req, 0x21); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); in_file_info_class = CVAL(inbody, 0x02); in_flags = CVAL(inbody, 0x03); in_file_index = IVAL(inbody, 0x04); in_file_id_persistent = BVAL(inbody, 0x08); in_file_id_volatile = BVAL(inbody, 0x10); in_file_name_offset = SVAL(inbody, 0x18); in_file_name_length = SVAL(inbody, 0x1A); in_output_buffer_length = IVAL(inbody, 0x1C); if (in_file_name_offset == 0 && in_file_name_length == 0) { /* This is ok */ } else if (in_file_name_offset != (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } if (in_file_name_length > SMBD_SMB2_IN_DYN_LEN(req)) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } /* The output header is 8 bytes. */ if (in_output_buffer_length <= 8) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } DEBUG(10,("smbd_smb2_request_find_done: in_output_buffer_length = %u\n", (unsigned int)in_output_buffer_length )); /* Take into account the output header. */ in_output_buffer_length -= 8; in_file_name_buffer.data = SMBD_SMB2_IN_DYN_PTR(req); in_file_name_buffer.length = in_file_name_length; ok = convert_string_talloc(req, CH_UTF16, CH_UNIX, in_file_name_buffer.data, in_file_name_buffer.length, &in_file_name_string, &in_file_name_string_size); if (!ok) { return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER); } if (in_file_name_buffer.length == 0) { in_file_name_string_size = 0; } if (strlen(in_file_name_string) != in_file_name_string_size) { return smbd_smb2_request_error(req, NT_STATUS_OBJECT_NAME_INVALID); } in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile); if (in_fsp == NULL) { return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); } subreq = smbd_smb2_query_directory_send(req, req->sconn->ev_ctx, req, in_fsp, in_file_info_class, in_flags, in_file_index, in_output_buffer_length, in_file_name_string); if (subreq == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(subreq, smbd_smb2_request_find_done, req); return smbd_smb2_request_pending_queue(req, subreq, 500); }
NTSTATUS smbd_smb2_request_process_ioctl(struct smbd_smb2_request *req) { NTSTATUS status; const uint8_t *inbody; uint32_t min_buffer_offset; uint32_t max_buffer_offset; uint32_t min_output_offset; uint32_t allowed_length_in; uint32_t allowed_length_out; uint32_t in_ctl_code; uint64_t in_file_id_persistent; uint64_t in_file_id_volatile; struct files_struct *in_fsp = NULL; uint32_t in_input_offset; uint32_t in_input_length; DATA_BLOB in_input_buffer = data_blob_null; uint32_t in_max_input_length; uint32_t in_output_offset; uint32_t in_output_length; DATA_BLOB in_output_buffer = data_blob_null; uint32_t in_max_output_length; uint32_t in_flags; uint32_t data_length_in; uint32_t data_length_out; uint32_t data_length_tmp; uint32_t data_length_max; struct tevent_req *subreq; status = smbd_smb2_request_verify_sizes(req, 0x39); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); in_ctl_code = IVAL(inbody, 0x04); in_file_id_persistent = BVAL(inbody, 0x08); in_file_id_volatile = BVAL(inbody, 0x10); in_input_offset = IVAL(inbody, 0x18); in_input_length = IVAL(inbody, 0x1C); in_max_input_length = IVAL(inbody, 0x20); in_output_offset = IVAL(inbody, 0x24); in_output_length = IVAL(inbody, 0x28); in_max_output_length = IVAL(inbody, 0x2C); in_flags = IVAL(inbody, 0x30); min_buffer_offset = SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req); max_buffer_offset = min_buffer_offset + SMBD_SMB2_IN_DYN_LEN(req); min_output_offset = min_buffer_offset; /* * InputOffset (4 bytes): The offset, in bytes, from the beginning of * the SMB2 header to the input data buffer. If no input data is * required for the FSCTL/IOCTL command being issued, the client SHOULD * set this value to 0.<49> * <49> If no input data is required for the FSCTL/IOCTL command being * issued, Windows-based clients set this field to any value. */ allowed_length_in = 0; if ((in_input_offset > 0) && (in_input_length > 0)) { uint32_t tmp_ofs; if (in_input_offset < min_buffer_offset) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } if (in_input_offset > max_buffer_offset) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } allowed_length_in = max_buffer_offset - in_input_offset; tmp_ofs = in_input_offset - min_buffer_offset; in_input_buffer.data = SMBD_SMB2_IN_DYN_PTR(req); in_input_buffer.data += tmp_ofs; in_input_buffer.length = in_input_length; min_output_offset += tmp_ofs; min_output_offset += in_input_length; } if (in_input_length > allowed_length_in) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } allowed_length_out = 0; if (in_output_offset > 0) { if (in_output_offset < min_buffer_offset) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } if (in_output_offset > max_buffer_offset) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } allowed_length_out = max_buffer_offset - in_output_offset; } if (in_output_length > allowed_length_out) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } if (in_output_length > 0) { uint32_t tmp_ofs; if (in_output_offset < min_output_offset) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } tmp_ofs = in_output_offset - min_buffer_offset; in_output_buffer.data = SMBD_SMB2_IN_DYN_PTR(req); in_output_buffer.data += tmp_ofs; in_output_buffer.length = in_output_length; } /* * verify the credits and avoid overflows * in_input_buffer.length and in_output_buffer.length * are already verified. */ data_length_in = in_input_buffer.length + in_output_buffer.length; data_length_out = in_max_input_length; data_length_tmp = UINT32_MAX - data_length_out; if (data_length_tmp < in_max_output_length) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } data_length_out += in_max_output_length; data_length_max = MAX(data_length_in, data_length_out); status = smbd_smb2_request_verify_creditcharge(req, data_length_max); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } /* * If the Flags field of the request is not SMB2_0_IOCTL_IS_FSCTL the * server MUST fail the request with STATUS_NOT_SUPPORTED. */ if (in_flags != SMB2_IOCTL_FLAG_IS_FSCTL) { return smbd_smb2_request_error(req, NT_STATUS_NOT_SUPPORTED); } switch (in_ctl_code) { case FSCTL_DFS_GET_REFERRALS: case FSCTL_DFS_GET_REFERRALS_EX: case FSCTL_PIPE_WAIT: case FSCTL_VALIDATE_NEGOTIATE_INFO_224: case FSCTL_VALIDATE_NEGOTIATE_INFO: case FSCTL_QUERY_NETWORK_INTERFACE_INFO: /* * Some SMB2 specific CtlCodes like FSCTL_DFS_GET_REFERRALS or * FSCTL_PIPE_WAIT does not take a file handle. * * If FileId in the SMB2 Header of the request is not * 0xFFFFFFFFFFFFFFFF, then the server MUST fail the request * with STATUS_INVALID_PARAMETER. */ if (in_file_id_persistent != UINT64_MAX || in_file_id_volatile != UINT64_MAX) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } break; default: in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile); if (in_fsp == NULL) { return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); } break; } subreq = smbd_smb2_ioctl_send(req, req->sconn->ev_ctx, req, in_fsp, in_ctl_code, in_input_buffer, in_max_output_length, in_flags); if (subreq == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(subreq, smbd_smb2_request_ioctl_done, req); return smbd_smb2_request_pending_queue(req, subreq, 1000); }
static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq) { struct smbd_smb2_request *req = tevent_req_callback_data(subreq, struct smbd_smb2_request); const uint8_t *inbody; DATA_BLOB outbody; DATA_BLOB outdyn; uint32_t in_ctl_code; uint64_t in_file_id_persistent; uint64_t in_file_id_volatile; uint32_t out_input_offset; uint32_t out_output_offset; DATA_BLOB out_output_buffer = data_blob_null; NTSTATUS status; NTSTATUS error; /* transport error */ bool disconnect = false; status = smbd_smb2_ioctl_recv(subreq, req, &out_output_buffer, &disconnect); DEBUG(10,("smbd_smb2_request_ioctl_done: smbd_smb2_ioctl_recv returned " "%u status %s\n", (unsigned int)out_output_buffer.length, nt_errstr(status) )); TALLOC_FREE(subreq); if (disconnect) { error = status; smbd_server_connection_terminate(req->xconn, nt_errstr(error)); return; } inbody = SMBD_SMB2_IN_BODY_PTR(req); in_ctl_code = IVAL(inbody, 0x04); in_file_id_persistent = BVAL(inbody, 0x08); in_file_id_volatile = BVAL(inbody, 0x10); if (smbd_smb2_ioctl_is_failure(in_ctl_code, status, out_output_buffer.length)) { error = smbd_smb2_request_error(req, status); if (!NT_STATUS_IS_OK(error)) { smbd_server_connection_terminate(req->xconn, nt_errstr(error)); return; } return; } out_input_offset = SMB2_HDR_BODY + 0x30; out_output_offset = SMB2_HDR_BODY + 0x30; outbody = smbd_smb2_generate_outbody(req, 0x30); if (outbody.data == NULL) { error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); if (!NT_STATUS_IS_OK(error)) { smbd_server_connection_terminate(req->xconn, nt_errstr(error)); return; } return; } SSVAL(outbody.data, 0x00, 0x30 + 1); /* struct size */ SSVAL(outbody.data, 0x02, 0); /* reserved */ SIVAL(outbody.data, 0x04, in_ctl_code); /* ctl code */ SBVAL(outbody.data, 0x08, in_file_id_persistent); /* file id (persistent) */ SBVAL(outbody.data, 0x10, in_file_id_volatile); /* file id (volatile) */ SIVAL(outbody.data, 0x18, out_input_offset); /* input offset */ SIVAL(outbody.data, 0x1C, 0); /* input count */ SIVAL(outbody.data, 0x20, out_output_offset); /* output offset */ SIVAL(outbody.data, 0x24, out_output_buffer.length); /* output count */ SIVAL(outbody.data, 0x28, 0); /* flags */ SIVAL(outbody.data, 0x2C, 0); /* reserved */ /* * Note: Windows Vista and 2008 send back also the * input from the request. But it was fixed in * Windows 7. */ outdyn = out_output_buffer; error = smbd_smb2_request_done_ex(req, status, outbody, &outdyn, __location__); if (!NT_STATUS_IS_OK(error)) { smbd_server_connection_terminate(req->xconn, nt_errstr(error)); return; } }