view usr/src/lib/smbsrv/libmlsvc/common/lsar_lookup.c @ 10717:fe0545fc3cdd

6612607 CIFS ADS client should use ldap_sasl_interactive_bind_s API 6877755 smbd should not route stderr, stdout to /dev/null 6882701 Wrong error message for attempt to map local user to Windows group, or vice versa 6885105 Potential for deadlock in smb_node_set_delete_on_close() 6881928 smbd core generated when running a script to join domain, set abe properties 6885538 Reduce dependencies on libsmbrdr 6820325 cifs service can't start on multi vlan+ipmp configuration
author Alan Wright <amw@Sun.COM>
date Mon, 05 Oct 2009 11:03:34 -0700
parents 3569b6c7f56c
children 37e5dcdf36d3
line wrap: on
line source

/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Local Security Authority RPC (LSARPC) library interface functions for
 * query, lookup and enumeration calls.
 */


#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/errno.h>

#include <smbsrv/libsmb.h>
#include <smbsrv/ntaccess.h>
#include <smbsrv/ntstatus.h>
#include <smbsrv/ntlocale.h>
#include <smbsrv/string.h>
#include <smbsrv/libmlsvc.h>
#include <lsalib.h>

/*
 * The maximum number of bytes we are prepared to deal with in a
 * response.
 */
#define	MLSVC_MAX_RESPONSE_LEN		1024

/*
 * This structure is used when lookuping up names. We only lookup one
 * name at a time but the structure will allow for more.
 */
typedef struct lookup_name_table {
	DWORD n_entry;
	mslsa_string_t name[8];
} lookup_name_table_t;

static void lsar_set_trusted_domains_ex(struct mslsa_EnumTrustedDomainBufEx *,
    smb_trusted_domains_t *);
static void lsar_set_trusted_domains(struct mslsa_EnumTrustedDomainBuf *,
    smb_trusted_domains_t *);

/*
 * lsar_query_security_desc
 *
 * Don't use this call yet. It is just a place holder for now.
 */
int
lsar_query_security_desc(mlsvc_handle_t *lsa_handle)
{
	struct mslsa_QuerySecurityObject arg;
	int rc;
	int opnum;

	opnum = LSARPC_OPNUM_QuerySecurityObject;

	bzero(&arg, sizeof (struct mslsa_QuerySecurityObject));
	(void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));

	rc = ndr_rpc_call(lsa_handle, opnum, &arg);
	ndr_rpc_release(lsa_handle);
	return (rc);
}


/*
 * 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);
}

/*
 * lsar_lookup_names
 *
 * Lookup a name and obtain the domain and user rid. The RPC call will
 * actually support lookup of multiple names but we probably don't
 * need to do that. On the final system the lookup level should be
 * level 2 but for now we want to restrict it to level 1 so that we
 * don't crash the PDC when we get things wrong.
 *
 * If the lookup fails, the status will typically be
 * NT_STATUS_NONE_MAPPED.
 */
uint32_t
lsar_lookup_names(mlsvc_handle_t *lsa_handle, char *name, smb_account_t *info)
{
	struct mslsa_LookupNames arg;
	struct mslsa_rid_entry *rid_entry;
	struct mslsa_domain_entry *domain_entry;
	lookup_name_table_t name_table;
	uint32_t status = NT_STATUS_SUCCESS;
	char *domname;
	int opnum;
	size_t length;
	char *p;

	if (lsa_handle == NULL || name == NULL || info == NULL)
		return (NT_STATUS_INVALID_PARAMETER);

	bzero(info, sizeof (smb_account_t));

	opnum = LSARPC_OPNUM_LookupNames;

	bzero(&arg, sizeof (struct mslsa_LookupNames));
	(void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));

	arg.name_table = (struct mslsa_lup_name_table *)&name_table;
	name_table.n_entry = 1;

	/*
	 * Windows NT expects the name length to exclude the terminating
	 * wchar null but doesn't care whether the allosize includes or
	 * excludes the null char. Windows 2000 insists that both the
	 * length and the allosize include the wchar null.
	 *
	 * Note: NT returns an error if the mapped_count is non-zero
	 * when the RPC is called.
	 */
	if (ndr_rpc_server_os(lsa_handle) == NATIVE_OS_WIN2000) {
		/*
		 * Windows 2000 doesn't like an LSA lookup for
		 * DOMAIN\Administrator.
		 */
		if ((p = strchr(name, '\\')) != 0) {
			++p;

			if (strcasecmp(p, "administrator") == 0)
				name = p;
		}

		length = mts_wcequiv_strlen(name) + sizeof (mts_wchar_t);
		arg.lookup_level = MSLSA_LOOKUP_LEVEL_1;
	} else {
		length = mts_wcequiv_strlen(name);
		arg.lookup_level = MSLSA_LOOKUP_LEVEL_1;
	}

	name_table.name[0].length = length;
	name_table.name[0].allosize = length;
	name_table.name[0].str = (unsigned char *)name;

	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_name = strdup(name);
	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);

	if (!smb_account_validate(info)) {
		smb_account_free(info);
		status = NT_STATUS_NO_MEMORY;
	}

	ndr_rpc_release(lsa_handle);
	return (status);
}

/*
 * lsar_lookup_sids
 *
 * Lookup a sid and obtain the domain sid and user name. The RPC call
 * will actually support lookup of multiple sids but we probably don't
 * need to do that. On the final system the lookup level should be
 * level 2 but for now we want to restrict it to level 1 so that we
 * don't crash the PDC when we get things wrong.
 */
uint32_t
lsar_lookup_sids(mlsvc_handle_t *lsa_handle, struct mslsa_sid *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;

	if (lsa_handle == NULL || sid == NULL || account == NULL)
		return (NT_STATUS_INVALID_PARAMETER);

	bzero(account, sizeof (smb_account_t));
	opnum = LSARPC_OPNUM_LookupSids;

	bzero(&arg, sizeof (struct mslsa_LookupSids));
	(void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
	arg.lookup_level = MSLSA_LOOKUP_LEVEL_2;

	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);

	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);

	if (!smb_account_validate(account)) {
		smb_account_free(account);
		status = NT_STATUS_NO_MEMORY;
	}

	ndr_rpc_release(lsa_handle);
	return (status);
}

/*
 * lsar_enum_accounts
 *
 * Enumerate the list of accounts (i.e. SIDs). Use the handle returned
 * from lsa_open_policy2. The enum_context is used to support multiple
 * calls to this enumeration function. It should be set to 0 on the
 * first call. It will be updated by the domain controller and should
 * simply be passed unchanged to subsequent calls until there are no
 * more accounts. A warning status of 0x1A indicates that no more data
 * is available. The list of accounts will be returned in accounts.
 * This list is dynamically allocated using malloc, it should be freed
 * by the caller when it is no longer required.
 */
int
lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context,
    struct mslsa_EnumAccountBuf *accounts)
{
	struct mslsa_EnumerateAccounts arg;
	struct mslsa_AccountInfo *info;
	int opnum;
	int rc;
	DWORD n_entries;
	DWORD i;
	int nbytes;

	if (lsa_handle == NULL || enum_context == NULL || accounts == NULL)
		return (-1);

	accounts->entries_read = 0;
	accounts->info = 0;

	opnum = LSARPC_OPNUM_EnumerateAccounts;

	bzero(&arg, sizeof (struct mslsa_EnumerateAccounts));
	(void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
	arg.enum_context = *enum_context;
	arg.max_length = MLSVC_MAX_RESPONSE_LEN;

	rc = ndr_rpc_call(lsa_handle, opnum, &arg);
	if (rc == 0) {
		if (arg.status != 0) {
			if ((arg.status & 0x00FFFFFF) == MLSVC_NO_MORE_DATA) {
				*enum_context = arg.enum_context;
			} else {
				ndr_rpc_status(lsa_handle, opnum, arg.status);
				rc = -1;
			}
		} else if (arg.enum_buf->entries_read != 0) {
			n_entries = arg.enum_buf->entries_read;
			nbytes = n_entries * sizeof (struct mslsa_AccountInfo);

			if ((info = malloc(nbytes)) == NULL) {
				ndr_rpc_release(lsa_handle);
				return (-1);
			}

			for (i = 0; i < n_entries; ++i)
				info[i].sid = (struct mslsa_sid *)smb_sid_dup(
				    (smb_sid_t *)arg.enum_buf->info[i].sid);

			accounts->entries_read = n_entries;
			accounts->info = info;
			*enum_context = arg.enum_context;
		}
	}

	ndr_rpc_release(lsa_handle);
	return (rc);
}

/*
 * lsar_enum_trusted_domains
 *
 * Enumerate the list of trusted domains. Use the handle returned from
 * lsa_open_policy2. The enum_context is used to support multiple calls
 * to this enumeration function. It should be set to 0 on the first
 * call. It will be updated by the domain controller and should simply
 * be passed unchanged to subsequent calls until there are no more
 * domains.
 *
 * The trusted domains aren't actually returned here. They are added
 * to the NT domain database. After all of the trusted domains have
 * been discovered, the database can be interrogated to find all of
 * the trusted domains.
 */
DWORD
lsar_enum_trusted_domains(mlsvc_handle_t *lsa_handle, DWORD *enum_context,
    smb_trusted_domains_t *list)
{
	struct mslsa_EnumTrustedDomain arg;
	int opnum;
	DWORD status;

	if (list == NULL)
		return (NT_STATUS_INVALID_PARAMETER);

	opnum = LSARPC_OPNUM_EnumTrustedDomain;

	bzero(list, sizeof (smb_trusted_domains_t));
	bzero(&arg, sizeof (struct mslsa_EnumTrustedDomain));
	(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 0x8000001A means NO_MORE_DATA,
		 * which is not an error.
		 */
		if (status != MLSVC_NO_MORE_DATA)
			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(arg.enum_buf, list);
		*enum_context = arg.enum_context;
		status = 0;
	}

	ndr_rpc_release(lsa_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 0x8000001A means NO_MORE_DATA,
		 * which is not an error.
		 */
		if (status != MLSVC_NO_MORE_DATA)
			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_enum_privs_account
 *
 * Privileges enum? Need an account handle.
 */
/*ARGSUSED*/
int
lsar_enum_privs_account(mlsvc_handle_t *account_handle, smb_account_t *account)
{
	struct mslsa_EnumPrivsAccount arg;
	int opnum;
	int rc;

	opnum = LSARPC_OPNUM_EnumPrivsAccount;

	bzero(&arg, sizeof (struct mslsa_EnumPrivsAccount));
	(void) memcpy(&arg.account_handle, &account_handle->handle,
	    sizeof (mslsa_handle_t));

	rc = ndr_rpc_call(account_handle, opnum, &arg);
	if ((rc == 0) && (arg.status != 0)) {
		ndr_rpc_status(account_handle, opnum, arg.status);
		rc = -1;
	}
	ndr_rpc_release(account_handle);
	return (rc);
}

/*
 * lsar_lookup_priv_value
 *
 * Map a privilege name to a local unique id (LUID). Privilege names
 * are consistent across the network. LUIDs are machine specific.
 * This function provides the means to map a privilege name to the
 * LUID used by a remote server to represent it. The handle here is
 * a policy handle.
 */
int
lsar_lookup_priv_value(mlsvc_handle_t *lsa_handle, char *name,
    struct ms_luid *luid)
{
	struct mslsa_LookupPrivValue arg;
	int opnum;
	int rc;
	size_t length;

	if (lsa_handle == NULL || name == NULL || luid == NULL)
		return (-1);

	opnum = LSARPC_OPNUM_LookupPrivValue;

	bzero(&arg, sizeof (struct mslsa_LookupPrivValue));
	(void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));

	length = mts_wcequiv_strlen(name);
	if (ndr_rpc_server_os(lsa_handle) == NATIVE_OS_WIN2000)
		length += sizeof (mts_wchar_t);

	arg.name.length = length;
	arg.name.allosize = length;
	arg.name.str = (unsigned char *)name;

	rc = ndr_rpc_call(lsa_handle, opnum, &arg);
	if (rc == 0) {
		if (arg.status != 0)
			rc = -1;
		else
			(void) memcpy(luid, &arg.luid, sizeof (struct ms_luid));
	}

	ndr_rpc_release(lsa_handle);
	return (rc);
}

/*
 * lsar_lookup_priv_name
 *
 * Map a local unique id (LUID) to a privilege name. Privilege names
 * are consistent across the network. LUIDs are machine specific.
 * This function the means to map the LUID used by a remote server to
 * the appropriate privilege name. The handle here is a policy handle.
 */
int
lsar_lookup_priv_name(mlsvc_handle_t *lsa_handle, struct ms_luid *luid,
    char *name, int namelen)
{
	struct mslsa_LookupPrivName arg;
	int opnum;
	int rc;

	if (lsa_handle == NULL || luid == NULL || name == NULL)
		return (-1);

	opnum = LSARPC_OPNUM_LookupPrivName;

	bzero(&arg, sizeof (struct mslsa_LookupPrivName));
	(void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
	(void) memcpy(&arg.luid, luid, sizeof (struct ms_luid));

	rc = ndr_rpc_call(lsa_handle, opnum, &arg);
	if (rc == 0) {
		if (arg.status != 0)
			rc = -1;
		else
			(void) strlcpy(name, (char const *)arg.name->str,
			    namelen);
	}

	ndr_rpc_release(lsa_handle);
	return (rc);
}

/*
 * 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 = mts_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);
}

/*
 * lsar_lookup_sids2
 */
uint32_t
lsar_lookup_sids2(mlsvc_handle_t *lsa_handle, struct mslsa_sid *sid,
    smb_account_t *account)
{
	struct lsar_lookup_sids2 arg;
	struct lsar_name_entry2 *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;

	if (lsa_handle == NULL || sid == NULL || account == NULL)
		return (NT_STATUS_INVALID_PARAMETER);

	bzero(account, sizeof (smb_account_t));
	opnum = LSARPC_OPNUM_LookupSids2;

	if (ndr_rpc_server_os(lsa_handle) != NATIVE_OS_WIN2000)
		return (NT_STATUS_REVISION_MISMATCH);

	bzero(&arg, sizeof (struct lsar_lookup_sids2));
	(void) memcpy(&arg.policy_handle, lsa_handle, sizeof (mslsa_handle_t));

	sid_entry.psid = sid;
	arg.lup_sid_table.n_entry = 1;
	arg.lup_sid_table.entries = &sid_entry;
	arg.lookup_level = MSLSA_LOOKUP_LEVEL_1;
	arg.requested_count = arg.lup_sid_table.n_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);

	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);

	if (!smb_account_validate(account)) {
		smb_account_free(account);
		status = NT_STATUS_NO_MEMORY;
	}

	ndr_rpc_release(lsa_handle);
	return (status);
}


/*
 * lsar_lookup_names2
 *
 * Windows NT expects the name length to exclude the terminating
 * wchar null but Windows 2000 insists that both the length and
 * the allosize include the wchar null. Windows NT doesn't care
 * whether or not the allosize includes or excludes the null char.
 *
 * As a precaution, I set the lookup level to 1 on Windows 2000
 * until I can do some more testing.
 *
 * Note that NT returns an error if the mapped_count is non-zero
 * when the RPC is called.
 *
 * It should be okay to lookup DOMAIN\Administrator in this function.
 */
uint32_t
lsar_lookup_names2(mlsvc_handle_t *lsa_handle, char *name, smb_account_t *info)
{
	struct lsar_LookupNames2 arg;
	struct lsar_rid_entry2 *rid_entry;
	struct mslsa_domain_entry *domain_entry;
	lookup_name_table_t name_table;
	uint32_t status = NT_STATUS_SUCCESS;
	char *domname;
	size_t length;
	int opnum;

	if (lsa_handle == NULL || name == NULL || info == NULL)
		return (NT_STATUS_INVALID_PARAMETER);

	bzero(info, sizeof (smb_account_t));

	opnum = LSARPC_OPNUM_LookupNames2;

	if (ndr_rpc_server_os(lsa_handle) != NATIVE_OS_WIN2000)
		return (NT_STATUS_REVISION_MISMATCH);

	bzero(&arg, sizeof (struct lsar_LookupNames2));
	(void) memcpy(&arg.policy_handle, lsa_handle, sizeof (mslsa_handle_t));
	arg.unknown_sb2 = 0x00000002;
	arg.lookup_level = MSLSA_LOOKUP_LEVEL_1;

	arg.name_table = (struct mslsa_lup_name_table *)&name_table;
	name_table.n_entry = 1;

	length = mts_wcequiv_strlen(name) + sizeof (mts_wchar_t);
	name_table.name[0].length = length;
	name_table.name[0].allosize = length;
	name_table.name[0].str = (unsigned char *)name;

	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_name = strdup(name);
	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);

	if (!smb_account_validate(info)) {
		smb_account_free(info);
		status = NT_STATUS_NO_MEMORY;
	}

	ndr_rpc_release(lsa_handle);
	return (status);
}

static void
lsar_set_trusted_domains_ex(struct mslsa_EnumTrustedDomainBufEx *enum_buf,
    smb_trusted_domains_t *list)
{
	char sidstr[SMB_SID_STRSZ];
	int i;

	if (list == NULL || enum_buf == NULL || enum_buf->entries_read == 0)
		return;

	list->td_num = 0;
	list->td_domains = calloc(enum_buf->entries_read,
	    sizeof (smb_domain_t));

	if (list->td_domains == NULL)
		return;

	list->td_num = enum_buf->entries_read;
	for (i = 0; i < list->td_num; i++) {
		smb_sid_tostr((smb_sid_t *)enum_buf->info[i].sid, sidstr);
		smb_domain_set_trust_info(
		    sidstr,
		    (char *)enum_buf->info[i].nb_name.str,
		    (char *)enum_buf->info[i].dns_name.str,
		    enum_buf->info[i].trust_direction,
		    enum_buf->info[i].trust_type,
		    enum_buf->info[i].trust_attrs,
		    &list->td_domains[i]);
	}
}

static void
lsar_set_trusted_domains(struct mslsa_EnumTrustedDomainBuf *enum_buf,
    smb_trusted_domains_t *list)
{
	char sidstr[SMB_SID_STRSZ];
	int i;

	if (list == NULL || enum_buf == NULL || enum_buf->entries_read == 0)
		return;

	list->td_num = 0;
	list->td_domains = calloc(enum_buf->entries_read,
	    sizeof (smb_domain_t));

	if (list->td_domains == NULL)
		return;

	list->td_num = enum_buf->entries_read;
	for (i = 0; i < list->td_num; i++) {
		smb_sid_tostr((smb_sid_t *)enum_buf->info[i].sid, sidstr);
		smb_domain_set_trust_info(
		    sidstr, (char *)enum_buf->info[i].name.str,
		    "", 0, 0, 0, &list->td_domains[i]);
	}
}