/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2019 Nexenta Systems, Inc. All rights reserved. */ /* * This module provides the high level interface to the LSA RPC functions. */ #include #include #include #include #include #include #include static uint32_t lsa_lookup_name_int(char *, uint16_t, smb_account_t *, boolean_t); static uint32_t lsa_lookup_sid_int(smb_sid_t *, smb_account_t *, boolean_t); static uint32_t lsa_lookup_name_builtin(char *, char *, smb_account_t *); static uint32_t lsa_lookup_name_domain(char *, smb_account_t *); static uint32_t lsa_lookup_sid_builtin(smb_sid_t *, smb_account_t *); static uint32_t lsa_lookup_sid_domain(smb_sid_t *, smb_account_t *); static uint32_t lsa_list_accounts(mlsvc_handle_t *); static uint32_t lsa_map_status(uint32_t); /* * Lookup the given account and returns the account information * in the passed smb_account_t structure. * * The lookup is performed in the following order: * well known accounts * local accounts * domain accounts * * If it's established the given account is well know or local * but the lookup fails for some reason, the next step(s) won't be * performed. * * If the name is a domain account, it may refer to a user, group or * alias. If it is a local account, its type should be specified * in the sid_type parameter. In case the account type is unknown * sid_type should be set to SidTypeUnknown. * * account argument could be either [domain\]name or [domain/]name. * * Return status: * * NT_STATUS_SUCCESS Account is successfully translated * NT_STATUS_NONE_MAPPED Couldn't translate the account */ uint32_t lsa_lookup_name(char *account, uint16_t type, smb_account_t *info) { return (lsa_lookup_name_int(account, type, info, B_TRUE)); } /* Variant that avoids the call out to AD. */ uint32_t lsa_lookup_lname(char *account, uint16_t type, smb_account_t *info) { return (lsa_lookup_name_int(account, type, info, B_FALSE)); } uint32_t lsa_lookup_name_int(char *account, uint16_t type, smb_account_t *info, boolean_t try_ad) { char nambuf[SMB_USERNAME_MAXLEN]; char dombuf[SMB_PI_MAX_DOMAIN]; char *name, *domain; uint32_t status; char *slash; if (account == NULL) return (NT_STATUS_NONE_MAPPED); (void) strsubst(account, '/', '\\'); (void) strcanon(account, "\\"); /* \john -> john */ account += strspn(account, "\\"); if ((slash = strchr(account, '\\')) != NULL) { *slash = '\0'; (void) strlcpy(dombuf, account, sizeof (dombuf)); (void) strlcpy(nambuf, slash + 1, sizeof (nambuf)); *slash = '\\'; name = nambuf; domain = dombuf; } else { name = account; domain = NULL; } status = lsa_lookup_name_builtin(domain, name, info); if (status == NT_STATUS_NOT_FOUND) { status = smb_sam_lookup_name(domain, name, type, info); if (status == NT_STATUS_SUCCESS) return (status); if (try_ad && ((domain == NULL) || (status == NT_STATUS_NOT_FOUND))) { status = lsa_lookup_name_domain(account, info); } } return ((status == NT_STATUS_SUCCESS) ? status : NT_STATUS_NONE_MAPPED); } uint32_t lsa_lookup_sid(smb_sid_t *sid, smb_account_t *info) { return (lsa_lookup_sid_int(sid, info, B_TRUE)); } /* Variant that avoids the call out to AD. */ uint32_t lsa_lookup_lsid(smb_sid_t *sid, smb_account_t *info) { return (lsa_lookup_sid_int(sid, info, B_FALSE)); } static uint32_t lsa_lookup_sid_int(smb_sid_t *sid, smb_account_t *info, boolean_t try_ad) { uint32_t status; if (!smb_sid_isvalid(sid)) return (NT_STATUS_INVALID_SID); status = lsa_lookup_sid_builtin(sid, info); if (status == NT_STATUS_NOT_FOUND) { status = smb_sam_lookup_sid(sid, info); if (try_ad && status == NT_STATUS_NOT_FOUND) { status = lsa_lookup_sid_domain(sid, info); } } return ((status == NT_STATUS_SUCCESS) ? status : NT_STATUS_NONE_MAPPED); } /* * Obtains the primary domain SID and name from the specified server * (domain controller). * * The requested information will be returned via 'info' argument. * * Returns NT status codes. (Raw, not LSA-ized) */ DWORD lsa_query_primary_domain_info(char *server, char *domain, smb_domain_t *info) { mlsvc_handle_t domain_handle; char user[SMB_USERNAME_MAXLEN]; DWORD status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); status = lsar_open(server, domain, user, &domain_handle); if (status != 0) return (status); status = lsar_query_info_policy(&domain_handle, MSLSA_POLICY_PRIMARY_DOMAIN_INFO, info); (void) lsar_close(&domain_handle); return (status); } /* * Obtains the account domain SID and name from the current server * (domain controller). * * The requested information will be returned via 'info' argument. * * Returns NT status codes. (Raw, not LSA-ized) */ DWORD lsa_query_account_domain_info(char *server, char *domain, smb_domain_t *info) { mlsvc_handle_t domain_handle; char user[SMB_USERNAME_MAXLEN]; DWORD status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); status = lsar_open(server, domain, user, &domain_handle); if (status != 0) return (status); status = lsar_query_info_policy(&domain_handle, MSLSA_POLICY_ACCOUNT_DOMAIN_INFO, info); (void) lsar_close(&domain_handle); return (status); } /* * lsa_query_dns_domain_info * * Obtains the DNS domain info from the specified server * (domain controller). * * The requested information will be returned via 'info' argument. * * Returns NT status codes. (Raw, not LSA-ized) */ DWORD lsa_query_dns_domain_info(char *server, char *domain, smb_domain_t *info) { mlsvc_handle_t domain_handle; char user[SMB_USERNAME_MAXLEN]; DWORD status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); status = lsar_open(server, domain, user, &domain_handle); if (status != 0) return (status); status = lsar_query_info_policy(&domain_handle, MSLSA_POLICY_DNS_DOMAIN_INFO, info); (void) lsar_close(&domain_handle); return (status); } /* * Enumerate the trusted domains of primary domain. * This is the basic enumaration call which only returns the * NetBIOS name of the domain and its SID. * * The requested information will be returned via 'info' argument. * * Returns NT status codes. (Raw, not LSA-ized) */ DWORD lsa_enum_trusted_domains(char *server, char *domain, smb_trusted_domains_t *info) { mlsvc_handle_t domain_handle; char user[SMB_USERNAME_MAXLEN]; DWORD enum_context; DWORD status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); status = lsar_open(server, domain, user, &domain_handle); if (status != 0) return (status); enum_context = 0; status = lsar_enum_trusted_domains(&domain_handle, &enum_context, info); if (status == NT_STATUS_NO_MORE_ENTRIES) { /* * STATUS_NO_MORE_ENTRIES indicates that we * have all of the available information. */ status = NT_STATUS_SUCCESS; } (void) lsar_close(&domain_handle); return (status); } /* * Enumerate the trusted domains of the primary domain. * This is the extended enumaration call which besides * NetBIOS name of the domain and its SID, it will return * the FQDN plus some trust information which is not used. * * The requested information will be returned via 'info' argument. * * Returns NT status codes. (Raw, not LSA-ized) */ DWORD lsa_enum_trusted_domains_ex(char *server, char *domain, smb_trusted_domains_t *info) { mlsvc_handle_t domain_handle; char user[SMB_USERNAME_MAXLEN]; DWORD enum_context; DWORD status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); status = lsar_open(server, domain, user, &domain_handle); if (status != 0) return (status); enum_context = 0; status = lsar_enum_trusted_domains_ex(&domain_handle, &enum_context, info); if (status == NT_STATUS_NO_MORE_ENTRIES) { /* * STATUS_NO_MORE_ENTRIES indicates that we * have all of the available information. */ status = NT_STATUS_SUCCESS; } (void) lsar_close(&domain_handle); return (status); } /* * Lookup well known accounts table * * Return status: * * NT_STATUS_SUCCESS Account is translated successfully * NT_STATUS_NOT_FOUND This is not a well known account * NT_STATUS_NONE_MAPPED Account is found but domains don't match * NT_STATUS_NO_MEMORY Memory shortage * NT_STATUS_INTERNAL_ERROR Internal error/unexpected failure */ static uint32_t lsa_lookup_name_builtin(char *domain, char *name, smb_account_t *info) { smb_wka_t *wka; char *wkadom; bzero(info, sizeof (smb_account_t)); if ((wka = smb_wka_lookup_name(name)) == NULL) return (NT_STATUS_NOT_FOUND); if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL) return (NT_STATUS_INTERNAL_ERROR); if ((domain != NULL) && (smb_strcasecmp(domain, wkadom, 0) != 0)) return (NT_STATUS_NONE_MAPPED); info->a_name = strdup(name); info->a_sid = smb_sid_dup(wka->wka_binsid); info->a_domain = strdup(wkadom); info->a_domsid = smb_sid_split(wka->wka_binsid, &info->a_rid); info->a_type = wka->wka_type; if (!smb_account_validate(info)) { smb_account_free(info); return (NT_STATUS_NO_MEMORY); } return (NT_STATUS_SUCCESS); } /* * Lookup a domain account by its name. * * The information is returned in the user_info structure. * The caller is responsible for allocating and releasing * this structure. * * Returns NT status codes. (LSA-ized) */ static uint32_t lsa_lookup_name_domain(char *account_name, smb_account_t *info) { mlsvc_handle_t domain_handle; smb_domainex_t dinfo; char user[SMB_USERNAME_MAXLEN]; uint32_t status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); if (!smb_domain_getinfo(&dinfo)) return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname, user, &domain_handle); if (status != 0) return (lsa_map_status(status)); status = lsar_lookup_names(&domain_handle, account_name, info); (void) lsar_close(&domain_handle); return (status); } /* * lsa_lookup_privs * * Request the privileges associated with the specified account. In * order to get the privileges, we first have to lookup the name on * the specified domain controller and obtain the appropriate SID. * The SID can then be used to open the account and obtain the * account privileges. The results from both the name lookup and the * privileges are returned in the user_info structure. The caller is * responsible for allocating and releasing this structure. * * Returns NT status codes. (LSA-ized) */ /*ARGSUSED*/ DWORD lsa_lookup_privs(char *account_name, char *target_name, smb_account_t *ainfo) { mlsvc_handle_t domain_handle; smb_domainex_t dinfo; char user[SMB_USERNAME_MAXLEN]; DWORD status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); if (!smb_domain_getinfo(&dinfo)) return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname, user, &domain_handle); if (status != 0) return (lsa_map_status(status)); status = lsa_list_accounts(&domain_handle); (void) lsar_close(&domain_handle); return (status); } /* * lsa_list_privs * * List the privileges supported by the specified server. * This function is only intended for diagnostics. * * Returns NT status codes. (LSA-ized) */ DWORD lsa_list_privs(char *server, char *domain) { static char name[128]; static struct ms_luid luid; mlsvc_handle_t domain_handle; char user[SMB_USERNAME_MAXLEN]; DWORD status; int rc; int i; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); status = lsar_open(server, domain, user, &domain_handle); if (status != 0) return (lsa_map_status(status)); for (i = 0; i < 30; ++i) { luid.low_part = i; rc = lsar_lookup_priv_name(&domain_handle, &luid, name, 128); if (rc != 0) continue; (void) lsar_lookup_priv_value(&domain_handle, name, &luid); (void) lsar_lookup_priv_display_name(&domain_handle, name, name, 128); } (void) lsar_close(&domain_handle); return (NT_STATUS_SUCCESS); } /* * lsa_list_accounts * * This function can be used to list the accounts in the specified * domain. For now the SIDs are just listed in the system log. * * Returns NT status */ static DWORD lsa_list_accounts(mlsvc_handle_t *domain_handle) { mlsvc_handle_t account_handle; struct mslsa_EnumAccountBuf accounts; struct mslsa_sid *sid; smb_account_t ainfo; DWORD enum_context = 0; DWORD status; int i; bzero(&accounts, sizeof (struct mslsa_EnumAccountBuf)); do { status = lsar_enum_accounts(domain_handle, &enum_context, &accounts); if (status != 0) return (status); for (i = 0; i < accounts.entries_read; ++i) { sid = accounts.info[i].sid; if (lsar_open_account(domain_handle, sid, &account_handle) == 0) { (void) lsar_enum_privs_account(&account_handle, &ainfo); (void) lsar_close(&account_handle); } free(accounts.info[i].sid); } if (accounts.info) free(accounts.info); } while (status == 0 && accounts.entries_read != 0); return (0); } /* * Lookup well known accounts table for the given SID * * Return status: * * NT_STATUS_SUCCESS Account is translated successfully * NT_STATUS_NOT_FOUND This is not a well known account * NT_STATUS_NO_MEMORY Memory shortage * NT_STATUS_INTERNAL_ERROR Internal error/unexpected failure */ static uint32_t lsa_lookup_sid_builtin(smb_sid_t *sid, smb_account_t *ainfo) { smb_wka_t *wka; char *wkadom; bzero(ainfo, sizeof (smb_account_t)); if ((wka = smb_wka_lookup_sid(sid)) == NULL) return (NT_STATUS_NOT_FOUND); if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL) return (NT_STATUS_INTERNAL_ERROR); ainfo->a_name = strdup(wka->wka_name); ainfo->a_sid = smb_sid_dup(wka->wka_binsid); ainfo->a_domain = strdup(wkadom); ainfo->a_domsid = smb_sid_split(ainfo->a_sid, &ainfo->a_rid); ainfo->a_type = wka->wka_type; if (!smb_account_validate(ainfo)) { smb_account_free(ainfo); return (NT_STATUS_NO_MEMORY); } return (NT_STATUS_SUCCESS); } /* * Lookup a domain account by its SID. * * The information is returned in the user_info structure. * The caller is responsible for allocating and releasing * this structure. * * Returns NT status codes. (LSA-ized) */ static uint32_t lsa_lookup_sid_domain(smb_sid_t *sid, smb_account_t *ainfo) { mlsvc_handle_t domain_handle; smb_domainex_t dinfo; char user[SMB_USERNAME_MAXLEN]; uint32_t status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); if (!smb_domain_getinfo(&dinfo)) return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname, user, &domain_handle); if (status != 0) return (lsa_map_status(status)); status = lsar_lookup_sids(&domain_handle, sid, ainfo); (void) lsar_close(&domain_handle); return (status); } /* * Most functions that call the local security authority expect * only a limited set of status returns. This function maps the * status we get from talking to our domain controller into one * that LSA functions can return. Most common errors become: * NT_STATUS_CANT_ACCESS_DOMAIN_INFO (when no DC etc.) */ static uint32_t lsa_map_status(uint32_t status) { switch (status) { case NT_STATUS_SUCCESS: break; case NT_STATUS_INVALID_PARAMETER: /* rpc bind */ break; case NT_STATUS_NO_MEMORY: break; case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND: case NT_STATUS_BAD_NETWORK_PATH: /* get server addr */ case NT_STATUS_NETWORK_ACCESS_DENIED: /* authentication */ case NT_STATUS_BAD_NETWORK_NAME: /* tree connect */ case NT_STATUS_ACCESS_DENIED: /* open pipe */ status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; break; default: status = NT_STATUS_UNSUCCESSFUL; break; } return (status); }