/* * smbrdr_hdr_process * * Assuming 'srh->srh_mbuf' contains a response from a Windows client, * decodes the 32 bytes SMB header. * * Buffer overflow typically means that the server has more data than * it could fit in the response buffer. The client can use subsequent * SmbReadX requests to obtain the remaining data (KB 193839). * * Returns: * * NT_STATUS_INVALID_NETWORK_RESPONSE error decoding the header * NT_STATUS_REPLY_MESSAGE_MISMATCH response doesn't match the request * NT_STATUS_SUCCESS successful * smb_hdr->status.ntstatus error returned by server */ static DWORD smbrdr_hdr_process(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr) { int rc; rc = smb_decode_nt_hdr(&srh->srh_mbuf, smb_hdr); if (rc < SMB_HEADER_LEN) { smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_hdr_process[%d]: invalid header (%d)", srh->srh_cmd, rc); return (NT_STATUS_INVALID_NETWORK_RESPONSE); } switch (NT_SC_VALUE(smb_hdr->status.ntstatus)) { case NT_STATUS_SUCCESS: case NT_STATUS_BUFFER_OVERFLOW: break; default: smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_hdr_process[%d]: request failed (%s)", srh->srh_cmd, xlate_nt_status(smb_hdr->status.ntstatus)); return (smb_hdr->status.ntstatus); } if (smb_hdr->command != srh->srh_cmd) { smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_hdr_process[%d]: reply mismatch (%d)", srh->srh_cmd, smb_hdr->command); return (NT_STATUS_REPLY_MESSAGE_MISMATCH); } return (NT_STATUS_SUCCESS); }
/*ARGSUSED*/ static DWORD samr_connect4(char *server, char *domain, char *username, DWORD access_mask, mlsvc_handle_t *samr_handle) { struct samr_Connect4 arg; int opnum; DWORD status; int len; bzero(&arg, sizeof (struct samr_Connect4)); opnum = SAMR_OPNUM_Connect4; status = NT_STATUS_SUCCESS; len = strlen(server) + 4; arg.servername = ndr_rpc_malloc(samr_handle, len); (void) snprintf((char *)arg.servername, len, "\\\\%s", server); arg.revision = SAMR_REVISION_2; arg.access_mask = access_mask; if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) { status = NT_STATUS_UNSUCCESSFUL; } else if (arg.status != 0) { status = NT_SC_VALUE(arg.status); } else { (void) memcpy(&samr_handle->handle, &arg.handle, sizeof (ndr_hdid_t)); if (ndr_is_null_handle(samr_handle)) status = NT_STATUS_INVALID_HANDLE; } ndr_rpc_release(samr_handle); return (status); }
/* * samr_enum_local_domains * * Get the list of local domains supported by a server. * * Returns NT status codes. */ DWORD samr_enum_local_domains(mlsvc_handle_t *samr_handle) { struct samr_EnumLocalDomain arg; int opnum; DWORD status; if (ndr_is_null_handle(samr_handle)) return (NT_STATUS_INVALID_PARAMETER); opnum = SAMR_OPNUM_EnumLocalDomains; bzero(&arg, sizeof (struct samr_EnumLocalDomain)); (void) memcpy(&arg.handle, &samr_handle->handle, sizeof (samr_handle_t)); arg.enum_context = 0; arg.max_length = 0x00002000; /* Value used by NT */ if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) { status = NT_STATUS_INVALID_PARAMETER; } else { status = NT_SC_VALUE(arg.status); /* * Handle none-mapped status quietly. */ if (status != NT_STATUS_NONE_MAPPED) ndr_rpc_status(samr_handle, opnum, arg.status); } ndr_rpc_release(samr_handle); return (status); }
/*ARGSUSED*/ static DWORD samr_connect1(char *server, char *domain, char *username, DWORD access_mask, mlsvc_handle_t *samr_handle) { struct samr_Connect arg; int opnum; DWORD status; bzero(&arg, sizeof (struct samr_Connect)); opnum = SAMR_OPNUM_Connect; status = NT_STATUS_SUCCESS; arg.servername = ndr_rpc_malloc(samr_handle, sizeof (DWORD)); *(arg.servername) = 0x0001005c; arg.access_mask = access_mask; if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) { status = NT_STATUS_UNSUCCESSFUL; } else if (arg.status != 0) { status = NT_SC_VALUE(arg.status); } else { (void) memcpy(&samr_handle->handle, &arg.handle, sizeof (ndr_hdid_t)); if (ndr_is_null_handle(samr_handle)) status = NT_STATUS_INVALID_HANDLE; } ndr_rpc_release(samr_handle); return (status); }
/* * samr_get_user_pwinfo * * Get some user password info. I'm not sure what this is yet but it is * part of the create user sequence. The handle must be a valid user * handle. Since I don't know what this is returning, I haven't provided * any return data yet. * * Returns 0 on success. Otherwise returns an NT status code. */ DWORD samr_get_user_pwinfo(mlsvc_handle_t *user_handle) { struct samr_GetUserPwInfo arg; int opnum; DWORD status; if (ndr_is_null_handle(user_handle)) return (NT_STATUS_INVALID_PARAMETER); opnum = SAMR_OPNUM_GetUserPwInfo; bzero(&arg, sizeof (struct samr_GetUserPwInfo)); (void) memcpy(&arg.user_handle, &user_handle->handle, sizeof (samr_handle_t)); if (ndr_rpc_call(user_handle, opnum, &arg) != 0) { status = NT_STATUS_INVALID_PARAMETER; } else if (arg.status != 0) { ndr_rpc_status(user_handle, opnum, arg.status); status = NT_SC_VALUE(arg.status); } else { status = 0; } ndr_rpc_release(user_handle); return (status); }
/* * lsar_lookup_sids3 * * This function is only valid if the remote RPC server is a domain * controller and requires the security extensions defined in MS-RPCE. * * Domain controllers will return RPC_NT_PROTSEQ_NOT_SUPPORTED here * because we don't support the RPC_C_AUTHN_NETLOGON security provider. * Non-domain controllers will return NT_STATUS_INVALID_SERVER_STATE. */ static uint32_t /*LINTED E_STATIC_UNUSED*/ lsar_lookup_sids3(mlsvc_handle_t *lsa_handle, lsa_sid_t *sid, smb_account_t *account) { struct lsar_lookup_sids3 arg; lsar_translated_name_ex_t *name_entry; struct mslsa_lup_sid_entry sid_entry; struct mslsa_domain_entry *domain_entry; uint32_t status = NT_STATUS_SUCCESS; char *name; int opnum = LSARPC_OPNUM_LookupSids3; bzero(&arg, sizeof (struct lsar_lookup_sids3)); sid_entry.psid = sid; arg.lup_sid_table.n_entry = 1; arg.lup_sid_table.entries = &sid_entry; arg.lookup_level = LSA_LOOKUP_WKSTA; arg.client_revision = LSA_CLIENT_REVISION_AD; if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_INVALID_PARAMETER); } if (arg.status != NT_STATUS_SUCCESS) { ndr_rpc_status(lsa_handle, opnum, arg.status); ndr_rpc_release(lsa_handle); if (arg.status == RPC_NT_PROTSEQ_NOT_SUPPORTED || arg.status == NT_STATUS_INVALID_SERVER_STATE) return (NT_STATUS_INVALID_PARAMETER); return (NT_SC_VALUE(arg.status)); } if (arg.mapped_count == 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } name_entry = &arg.name_table.entries[0]; if (name_entry->domain_ix != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } name = (char *)name_entry->name.str; account->a_name = (name) ? strdup(name) : strdup(""); account->a_type = name_entry->sid_name_use; account->a_sid = smb_sid_dup((smb_sid_t *)sid); (void) smb_sid_getrid(account->a_sid, &account->a_rid); domain_entry = &arg.domain_table->entries[0]; if ((name = (char *)domain_entry->domain_name.str) != NULL) account->a_domain = strdup(name); account->a_domsid = smb_sid_dup((smb_sid_t *)domain_entry->domain_sid); ndr_rpc_release(lsa_handle); return (status); }
/* * lsar_lookup_sids1 */ static uint32_t lsar_lookup_sids1(mlsvc_handle_t *lsa_handle, lsa_sid_t *sid, smb_account_t *account) { struct mslsa_LookupSids arg; struct mslsa_lup_sid_entry sid_entry; struct mslsa_name_entry *name_entry; struct mslsa_domain_entry *domain_entry; uint32_t status = NT_STATUS_SUCCESS; char *name; int opnum = LSARPC_OPNUM_LookupSids; bzero(&arg, sizeof (struct mslsa_LookupSids)); (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); arg.lookup_level = LSA_LOOKUP_WKSTA; sid_entry.psid = sid; arg.lup_sid_table.n_entry = 1; arg.lup_sid_table.entries = &sid_entry; if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_INVALID_PARAMETER); } if (arg.status != NT_STATUS_SUCCESS) { ndr_rpc_status(lsa_handle, opnum, arg.status); ndr_rpc_release(lsa_handle); return (NT_SC_VALUE(arg.status)); } if (arg.mapped_count == 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } name_entry = &arg.name_table.entries[0]; if (name_entry->domain_ix != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } name = (char *)name_entry->name.str; account->a_name = (name) ? strdup(name) : strdup(""); account->a_type = name_entry->sid_name_use; account->a_sid = smb_sid_dup((smb_sid_t *)sid); (void) smb_sid_getrid(account->a_sid, &account->a_rid); domain_entry = &arg.domain_table->entries[0]; if ((name = (char *)domain_entry->domain_name.str) != NULL) account->a_domain = strdup(name); account->a_domsid = smb_sid_dup((smb_sid_t *)domain_entry->domain_sid); ndr_rpc_release(lsa_handle); return (status); }
/* * samr_create_user * * Create a user in the domain specified by the domain handle. If this * call is successful, the server will return the RID for the user and * a user handle, which may be used to set or query the SAM. * * Observed status codes: * NT_STATUS_INVALID_PARAMETER * NT_STATUS_INVALID_ACCOUNT_NAME * NT_STATUS_ACCESS_DENIED * NT_STATUS_USER_EXISTS * * Returns 0 on success. Otherwise returns an NT status code. */ DWORD samr_create_user(mlsvc_handle_t *domain_handle, char *username, DWORD account_flags, DWORD *rid, mlsvc_handle_t *user_handle) { struct samr_CreateUser arg; ndr_heap_t *heap; int opnum; int rc; DWORD status = 0; if (ndr_is_null_handle(domain_handle) || username == NULL || rid == NULL) { return (NT_STATUS_INVALID_PARAMETER); } opnum = SAMR_OPNUM_CreateUser; bzero(&arg, sizeof (struct samr_CreateUser)); (void) memcpy(&arg.handle, &domain_handle->handle, sizeof (ndr_hdid_t)); heap = ndr_rpc_get_heap(domain_handle); ndr_heap_mkvcs(heap, username, (ndr_vcstr_t *)&arg.username); arg.account_flags = account_flags; arg.desired_access = 0xE00500B0; rc = ndr_rpc_call(domain_handle, opnum, &arg); if (rc != 0) { status = NT_STATUS_INVALID_PARAMETER; } else if (arg.status != 0) { status = NT_SC_VALUE(arg.status); if (status != NT_STATUS_USER_EXISTS) { smb_tracef("SamrCreateUser[%s]: %s", username, xlate_nt_status(status)); } } else { ndr_inherit_handle(user_handle, domain_handle); (void) memcpy(&user_handle->handle, &arg.user_handle, sizeof (ndr_hdid_t)); *rid = arg.rid; if (ndr_is_null_handle(user_handle)) status = NT_STATUS_INVALID_HANDLE; else status = 0; } ndr_rpc_release(domain_handle); return (status); }
/* * lsar_lookup_names4 * * This function is only valid if the remote RPC server is a domain * controller and requires the security extensions defined in MS-RPCE. * * Domain controllers will return RPC_NT_PROTSEQ_NOT_SUPPORTED here * because we don't support the RPC_C_AUTHN_NETLOGON security provider. * Non-domain controllers will return NT_STATUS_INVALID_SERVER_STATE. */ static uint32_t /*LINTED E_STATIC_UNUSED*/ lsar_lookup_names4(mlsvc_handle_t *lsa_handle, lsa_names_t *names, smb_account_t *info) { struct lsar_LookupNames4 arg; lsar_translated_sid_ex2_t *sid_entry; struct mslsa_domain_entry *domain_entry; uint32_t status = NT_STATUS_SUCCESS; char *domname; int opnum = LSARPC_OPNUM_LookupNames4; bzero(&arg, sizeof (struct lsar_LookupNames4)); arg.lookup_level = LSA_LOOKUP_WKSTA; arg.client_revision = LSA_CLIENT_REVISION_AD; arg.name_table = (struct mslsa_lup_name_table *)names; if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_INVALID_PARAMETER); } if (arg.status != NT_STATUS_SUCCESS) { ndr_rpc_status(lsa_handle, opnum, arg.status); ndr_rpc_release(lsa_handle); if (arg.status == RPC_NT_PROTSEQ_NOT_SUPPORTED || arg.status == NT_STATUS_INVALID_SERVER_STATE) return (NT_STATUS_INVALID_PARAMETER); return (NT_SC_VALUE(arg.status)); } if (arg.mapped_count == 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } sid_entry = &arg.translated_sids.sids[0]; if (sid_entry->domain_index != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } domain_entry = &arg.domain_table->entries[0]; info->a_type = sid_entry->sid_name_use; info->a_domsid = smb_sid_dup((smb_sid_t *)domain_entry->domain_sid); if ((domname = (char *)domain_entry->domain_name.str) != NULL) info->a_domain = strdup(domname); info->a_sid = smb_sid_dup((smb_sid_t *)sid_entry->sid); (void) smb_sid_getrid(info->a_sid, &info->a_rid); ndr_rpc_release(lsa_handle); return (status); }
/* * samr_lookup_domain_names * * Lookup up the given name in the domain specified by domain_handle. * Upon a successful lookup the information is returned in the account * arg and caller must free allocated memories by calling smb_account_free(). * * Returns NT status codes. */ uint32_t samr_lookup_domain_names(mlsvc_handle_t *domain_handle, char *name, smb_account_t *account) { struct samr_LookupNames arg; int opnum; uint32_t status; size_t length; if (ndr_is_null_handle(domain_handle) || name == NULL || account == NULL) { return (NT_STATUS_INVALID_PARAMETER); } bzero(account, sizeof (smb_account_t)); opnum = SAMR_OPNUM_LookupNames; bzero(&arg, sizeof (struct samr_LookupNames)); (void) memcpy(&arg.handle, &domain_handle->handle, sizeof (samr_handle_t)); arg.n_entry = 1; arg.max_n_entry = 1000; arg.index = 0; arg.total = 1; length = smb_wcequiv_strlen(name); if (ndr_rpc_server_os(domain_handle) == NATIVE_OS_WIN2000) length += sizeof (smb_wchar_t); arg.name.length = length; arg.name.allosize = length; arg.name.str = (unsigned char *)name; if (ndr_rpc_call(domain_handle, opnum, &arg) != 0) { status = NT_STATUS_INVALID_PARAMETER; } else if (arg.status != NT_STATUS_SUCCESS) { status = NT_SC_VALUE(arg.status); /* * Handle none-mapped status quietly. */ if (status != NT_STATUS_NONE_MAPPED) ndr_rpc_status(domain_handle, opnum, arg.status); } else { account->a_type = arg.rid_types.rid_type[0]; account->a_rid = arg.rids.rid[0]; status = NT_STATUS_SUCCESS; } ndr_rpc_release(domain_handle); return (status); }
/* * lsar_lookup_names2 */ static uint32_t lsar_lookup_names2(mlsvc_handle_t *lsa_handle, lsa_names_t *names, smb_account_t *info) { struct lsar_LookupNames2 arg; struct lsar_rid_entry2 *rid_entry; struct mslsa_domain_entry *domain_entry; uint32_t status = NT_STATUS_SUCCESS; char *domname; int opnum = LSARPC_OPNUM_LookupNames2; bzero(&arg, sizeof (struct lsar_LookupNames2)); (void) memcpy(&arg.policy_handle, lsa_handle, sizeof (mslsa_handle_t)); arg.lookup_level = LSA_LOOKUP_WKSTA; arg.client_revision = LSA_CLIENT_REVISION_AD; arg.name_table = (struct mslsa_lup_name_table *)names; if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_INVALID_PARAMETER); } if (arg.status != NT_STATUS_SUCCESS) { ndr_rpc_status(lsa_handle, opnum, arg.status); ndr_rpc_release(lsa_handle); return (NT_SC_VALUE(arg.status)); } if (arg.mapped_count == 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } rid_entry = &arg.translated_sids.rids[0]; if (rid_entry->domain_index != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } domain_entry = &arg.domain_table->entries[0]; info->a_type = rid_entry->sid_name_use; info->a_domsid = smb_sid_dup((smb_sid_t *)domain_entry->domain_sid); if ((domname = (char *)domain_entry->domain_name.str) != NULL) info->a_domain = strdup(domname); info->a_rid = rid_entry->rid; info->a_sid = smb_sid_splice(info->a_domsid, info->a_rid); ndr_rpc_release(lsa_handle); return (status); }
/*ARGSUSED*/ static DWORD samr_connect5(char *server, char *domain, char *username, DWORD access_mask, mlsvc_handle_t *samr_handle) { struct samr_Connect5 arg; int len; int opnum; DWORD status; smb_domainex_t dinfo; bzero(&arg, sizeof (struct samr_Connect5)); opnum = SAMR_OPNUM_Connect5; status = NT_STATUS_SUCCESS; if (!smb_domain_getinfo(&dinfo)) return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); len = strlen(server) + strlen(dinfo.d_primary.di_fqname) + 4; arg.servername = ndr_rpc_malloc(samr_handle, len); if (*dinfo.d_primary.di_fqname != '\0') (void) snprintf((char *)arg.servername, len, "\\\\%s.%s", server, dinfo.d_primary.di_fqname); else (void) snprintf((char *)arg.servername, len, "\\\\%s", server); arg.access_mask = SAM_ENUM_LOCAL_DOMAIN; arg.unknown2_00000001 = 0x00000001; arg.unknown3_00000001 = 0x00000001; arg.unknown4_00000003 = 0x00000003; arg.unknown5_00000000 = 0x00000000; if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) { status = NT_STATUS_UNSUCCESSFUL; } else if (arg.status != 0) { status = NT_SC_VALUE(arg.status); } else { (void) memcpy(&samr_handle->handle, &arg.handle, sizeof (ndr_hdid_t)); if (ndr_is_null_handle(samr_handle)) status = NT_STATUS_INVALID_HANDLE; } ndr_rpc_release(samr_handle); return (status); }
DWORD lsar_enum_trusted_domains_ex(mlsvc_handle_t *lsa_handle, DWORD *enum_context, smb_trusted_domains_t *list) { struct mslsa_EnumTrustedDomainEx arg; int opnum; DWORD status; if (list == NULL) return (NT_STATUS_INVALID_PARAMETER); opnum = LSARPC_OPNUM_EnumTrustedDomainsEx; bzero(list, sizeof (smb_trusted_domains_t)); bzero(&arg, sizeof (struct mslsa_EnumTrustedDomainEx)); (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); arg.enum_context = *enum_context; arg.max_length = MLSVC_MAX_RESPONSE_LEN; if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) { status = NT_STATUS_INVALID_PARAMETER; } else if (arg.status != 0) { *enum_context = arg.enum_context; status = NT_SC_VALUE(arg.status); /* * STATUS_NO_MORE_ENTRIES provides call * status but does not indicate an error. */ if (status != NT_STATUS_NO_MORE_ENTRIES) ndr_rpc_status(lsa_handle, opnum, arg.status); } else if (arg.enum_buf->entries_read == 0) { *enum_context = arg.enum_context; status = 0; } else { lsar_set_trusted_domains_ex(arg.enum_buf, list); *enum_context = arg.enum_context; status = 0; } ndr_rpc_release(lsa_handle); return (status); }
/* * lsar_lookup_priv_display_name * * Map a privilege name to a privilege display name. The input handle * should be an LSA policy handle and the name would normally be one * of the privileges defined in smb_privilege.h * * There's something peculiar about the return status from NT servers, * it's not always present. So for now, I'm ignoring the status in the * RPC response. * * Returns NT status codes. */ DWORD lsar_lookup_priv_display_name(mlsvc_handle_t *lsa_handle, char *name, char *display_name, int display_len) { struct mslsa_LookupPrivDisplayName arg; int opnum; size_t length; DWORD status; if (lsa_handle == NULL || name == NULL || display_name == NULL) return (NT_STATUS_INVALID_PARAMETER); opnum = LSARPC_OPNUM_LookupPrivDisplayName; bzero(&arg, sizeof (struct mslsa_LookupPrivDisplayName)); (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); length = smb_wcequiv_strlen(name); arg.name.length = length; arg.name.allosize = length; arg.name.str = (unsigned char *)name; arg.client_language = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); arg.default_language = MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL); if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) status = NT_STATUS_INVALID_PARAMETER; #if 0 else if (arg.status != 0) status = NT_SC_VALUE(arg.status); #endif else { (void) strlcpy(display_name, (char const *)arg.display_name->str, display_len); status = NT_STATUS_SUCCESS; } ndr_rpc_release(lsa_handle); return (status); }
/*ARGSUSED*/ DWORD samr_set_user_info(mlsvc_handle_t *user_handle) { unsigned char ssn_key[SMBAUTH_SESSION_KEY_SZ]; struct samr_SetUserInfo arg; int opnum; DWORD status = 0; if (ndr_is_null_handle(user_handle)) return (NT_STATUS_INVALID_PARAMETER); if (ndr_rpc_get_ssnkey(user_handle, ssn_key, sizeof (ssn_key))) return (NT_STATUS_INVALID_PARAMETER); opnum = SAMR_OPNUM_SetUserInfo; bzero(&arg, sizeof (struct samr_SetUserInfo)); (void) memcpy(&arg.user_handle, &user_handle->handle, sizeof (samr_handle_t)); arg.info.index = SAMR_SET_USER_INFO_23; arg.info.switch_value = SAMR_SET_USER_INFO_23; samr_set_user_unknowns(&arg.info.ru.info23); samr_set_user_logon_hours(&arg); if (samr_set_user_password(ssn_key, arg.info.ru.info23.password) < 0) status = NT_STATUS_INTERNAL_ERROR; if (ndr_rpc_call(user_handle, opnum, &arg) != 0) { status = NT_STATUS_INVALID_PARAMETER; } else if (arg.status != 0) { ndr_rpc_status(user_handle, opnum, arg.status); status = NT_SC_VALUE(arg.status); } ndr_rpc_release(user_handle); return (status); }
/* * samr_open_user * * Use a domain handle to obtain a handle for a user, specified by the * user RID. A user RID (effectively a uid) can be obtained via the * LSA interface. A handle for the user is returned in user_handle. * Once you have a user handle it should be possible to query the SAM * for information on that user. */ DWORD samr_open_user(mlsvc_handle_t *domain_handle, DWORD access_mask, DWORD rid, mlsvc_handle_t *user_handle) { struct samr_OpenUser arg; int opnum; DWORD status = NT_STATUS_SUCCESS; if (ndr_is_null_handle(domain_handle) || user_handle == NULL) return (NT_STATUS_INVALID_PARAMETER); opnum = SAMR_OPNUM_OpenUser; bzero(&arg, sizeof (struct samr_OpenUser)); (void) memcpy(&arg.handle, &domain_handle->handle, sizeof (ndr_hdid_t)); arg.access_mask = access_mask; arg.rid = rid; if (ndr_rpc_call(domain_handle, opnum, &arg) != 0) { status = NT_STATUS_UNSUCCESSFUL; } else if (arg.status != 0) { ndr_rpc_status(domain_handle, opnum, arg.status); status = NT_SC_VALUE(arg.status); } else { ndr_inherit_handle(user_handle, domain_handle); (void) memcpy(&user_handle->handle, &arg.user_handle, sizeof (ndr_hdid_t)); if (ndr_is_null_handle(user_handle)) status = NT_STATUS_INVALID_HANDLE; } ndr_rpc_release(domain_handle); return (status); }
/* * netr_server_samlogon * * NetrServerSamLogon RPC: interactive or network. It is assumed that * we have already authenticated with the PDC. If everything works, * we build a user info structure and return it, where the caller will * probably build an access token. * * Returns an NT status. There are numerous possibilities here. * For example: * NT_STATUS_INVALID_INFO_CLASS * NT_STATUS_INVALID_PARAMETER * NT_STATUS_ACCESS_DENIED * NT_STATUS_PASSWORD_MUST_CHANGE * NT_STATUS_NO_SUCH_USER * NT_STATUS_WRONG_PASSWORD * NT_STATUS_LOGON_FAILURE * NT_STATUS_ACCOUNT_RESTRICTION * NT_STATUS_INVALID_LOGON_HOURS * NT_STATUS_INVALID_WORKSTATION * NT_STATUS_INTERNAL_ERROR * NT_STATUS_PASSWORD_EXPIRED * NT_STATUS_ACCOUNT_DISABLED */ uint32_t netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info, char *server, smb_logon_t *user_info, smb_token_t *token) { struct netr_SamLogon arg; struct netr_authenticator auth; struct netr_authenticator ret_auth; struct netr_logon_info1 info1; struct netr_logon_info2 info2; struct netr_validation_info3 *info3; ndr_heap_t *heap; int opnum; int rc, len; uint32_t status; bzero(&arg, sizeof (struct netr_SamLogon)); opnum = NETR_OPNUM_SamLogon; /* * Should we get the server and hostname from netr_info? */ len = strlen(server) + 4; arg.servername = ndr_rpc_malloc(netr_handle, len); arg.hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ); if (arg.servername == NULL || arg.hostname == NULL) { ndr_rpc_release(netr_handle); return (NT_STATUS_INTERNAL_ERROR); } (void) snprintf((char *)arg.servername, len, "\\\\%s", server); if (smb_getnetbiosname((char *)arg.hostname, NETBIOS_NAME_SZ) != 0) { ndr_rpc_release(netr_handle); return (NT_STATUS_INTERNAL_ERROR); } rc = netr_setup_authenticator(netr_info, &auth, &ret_auth); if (rc != SMBAUTH_SUCCESS) { ndr_rpc_release(netr_handle); return (NT_STATUS_INTERNAL_ERROR); } arg.auth = &auth; arg.ret_auth = &ret_auth; arg.validation_level = NETR_VALIDATION_LEVEL3; arg.logon_info.logon_level = user_info->lg_level; arg.logon_info.switch_value = user_info->lg_level; heap = ndr_rpc_get_heap(netr_handle); switch (user_info->lg_level) { case NETR_INTERACTIVE_LOGON: netr_setup_identity(heap, user_info, &info1.identity); netr_interactive_samlogon(netr_info, user_info, &info1); arg.logon_info.ru.info1 = &info1; break; case NETR_NETWORK_LOGON: if (user_info->lg_challenge_key.len < 8 || user_info->lg_challenge_key.val == NULL) { ndr_rpc_release(netr_handle); return (NT_STATUS_INVALID_PARAMETER); } netr_setup_identity(heap, user_info, &info2.identity); netr_network_samlogon(heap, netr_info, user_info, &info2); arg.logon_info.ru.info2 = &info2; break; default: ndr_rpc_release(netr_handle); return (NT_STATUS_INVALID_PARAMETER); } rc = ndr_rpc_call(netr_handle, opnum, &arg); if (rc != 0) { bzero(netr_info, sizeof (netr_info_t)); status = NT_STATUS_INVALID_PARAMETER; } else if (arg.status != 0) { status = NT_SC_VALUE(arg.status); /* * We need to validate the chain even though we have * a non-zero status. If the status is ACCESS_DENIED * this will trigger a new credential chain. However, * a valid credential is returned with some status * codes; for example, WRONG_PASSWORD. */ (void) netr_validate_chain(netr_info, arg.ret_auth); } else { status = netr_validate_chain(netr_info, arg.ret_auth); if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) { ndr_rpc_release(netr_handle); return (status); } info3 = arg.ru.info3; status = netr_setup_token(info3, user_info, netr_info, token); } ndr_rpc_release(netr_handle); return (status); }
/* * lsar_query_info_policy * * The general purpose of this function is to allow various pieces of * information to be queried on the domain controller. The only * information queries supported are MSLSA_POLICY_PRIMARY_DOMAIN_INFO * and MSLSA_POLICY_ACCOUNT_DOMAIN_INFO. * * On success, the return code will be 0 and the user_info structure * will be set up. The sid_name_use field will be set to SidTypeDomain * indicating that the domain name and domain sid fields are vaild. If * the infoClass returned from the server is not one of the supported * values, the sid_name_use willbe set to SidTypeUnknown. If the RPC * fails, a negative error code will be returned, in which case the * user_info will not have been updated. */ DWORD lsar_query_info_policy(mlsvc_handle_t *lsa_handle, WORD infoClass, smb_domain_t *info) { struct mslsa_QueryInfoPolicy arg; struct mslsa_PrimaryDomainInfo *pd_info; struct mslsa_AccountDomainInfo *ad_info; struct mslsa_DnsDomainInfo *dns_info; char guid_str[UUID_PRINTABLE_STRING_LENGTH]; char sidstr[SMB_SID_STRSZ]; int opnum; DWORD status; if (lsa_handle == NULL || info == NULL) return (NT_STATUS_INVALID_PARAMETER); opnum = LSARPC_OPNUM_QueryInfoPolicy; bzero(info, sizeof (smb_domain_t)); bzero(&arg, sizeof (struct mslsa_QueryInfoPolicy)); (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); arg.info_class = infoClass; if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) { status = NT_STATUS_INVALID_PARAMETER; } else if (arg.status != 0) { ndr_rpc_status(lsa_handle, opnum, arg.status); status = NT_SC_VALUE(arg.status); } else { switch (infoClass) { case MSLSA_POLICY_PRIMARY_DOMAIN_INFO: pd_info = &arg.ru.pd_info; smb_sid_tostr((smb_sid_t *)pd_info->sid, sidstr); info->di_type = SMB_DOMAIN_PRIMARY; smb_domain_set_basic_info(sidstr, (char *)pd_info->name.str, "", info); status = NT_STATUS_SUCCESS; break; case MSLSA_POLICY_ACCOUNT_DOMAIN_INFO: ad_info = &arg.ru.ad_info; smb_sid_tostr((smb_sid_t *)ad_info->sid, sidstr); info->di_type = SMB_DOMAIN_ACCOUNT; smb_domain_set_basic_info(sidstr, (char *)ad_info->name.str, "", info); status = NT_STATUS_SUCCESS; break; case MSLSA_POLICY_DNS_DOMAIN_INFO: dns_info = &arg.ru.dns_info; ndr_uuid_unparse((ndr_uuid_t *)&dns_info->guid, guid_str); smb_sid_tostr((smb_sid_t *)dns_info->sid, sidstr); info->di_type = SMB_DOMAIN_PRIMARY; smb_domain_set_dns_info(sidstr, (char *)dns_info->nb_domain.str, (char *)dns_info->dns_domain.str, (char *)dns_info->forest.str, guid_str, info); status = NT_STATUS_SUCCESS; break; default: status = NT_STATUS_INVALID_INFO_CLASS; break; } } ndr_rpc_release(lsa_handle); return (status); }