view usr/src/lib/smbsrv/libmlsvc/common/mlsvc_lsa.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 96eda55bfd54
children
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) server-side interface definition.
 */

#include <unistd.h>
#include <strings.h>
#include <pwd.h>
#include <grp.h>

#include <smbsrv/libsmb.h>
#include <smbsrv/libmlrpc.h>
#include <smbsrv/libmlsvc.h>
#include <smbsrv/ndl/lsarpc.ndl>
#include <lsalib.h>
#include <smbsrv/ntstatus.h>
#include <smbsrv/nterror.h>
#include <smbsrv/smbinfo.h>
#include <smbsrv/nmpipes.h>
#include <smbsrv/ntlocale.h>

struct local_group_table {
	WORD sid_name_use;
	WORD domain_ix;
	char *sid;
	char *name;
};

static int lsarpc_key_domain;
static int lsarpc_key_account;

static int lsarpc_call_stub(ndr_xa_t *mxa);

static int lsarpc_s_CloseHandle(void *arg, ndr_xa_t *);
static int lsarpc_s_QuerySecurityObject(void *arg, ndr_xa_t *);
static int lsarpc_s_EnumAccounts(void *arg, ndr_xa_t *);
static int lsarpc_s_EnumTrustedDomain(void *arg, ndr_xa_t *);
static int lsarpc_s_EnumTrustedDomainsEx(void *arg, ndr_xa_t *);
static int lsarpc_s_OpenAccount(void *arg, ndr_xa_t *);
static int lsarpc_s_EnumPrivsAccount(void *arg, ndr_xa_t *);
static int lsarpc_s_LookupPrivValue(void *arg, ndr_xa_t *);
static int lsarpc_s_LookupPrivName(void *arg, ndr_xa_t *);
static int lsarpc_s_LookupPrivDisplayName(void *arg, ndr_xa_t *);
static int lsarpc_s_CreateSecret(void *, ndr_xa_t *);
static int lsarpc_s_OpenSecret(void *, ndr_xa_t *);
static int lsarpc_s_QueryInfoPolicy(void *arg, ndr_xa_t *);
static int lsarpc_s_OpenDomainHandle(void *arg, ndr_xa_t *);
static int lsarpc_s_OpenDomainHandle(void *arg, ndr_xa_t *);
static int lsarpc_s_LookupSids(void *arg, ndr_xa_t *);
static int lsarpc_s_LookupNames(void *arg, ndr_xa_t *);
static int lsarpc_s_GetConnectedUser(void *arg, ndr_xa_t *);
static int lsarpc_s_LookupSids2(void *arg, ndr_xa_t *);
static int lsarpc_s_LookupNames2(void *arg, ndr_xa_t *);

static DWORD lsarpc_s_PrimaryDomainInfo(struct mslsa_PrimaryDomainInfo *,
    ndr_xa_t *);
static DWORD lsarpc_s_AccountDomainInfo(struct mslsa_AccountDomainInfo *,
    ndr_xa_t *);
static int lsarpc_s_UpdateDomainTable(ndr_xa_t *,
    smb_account_t *, struct mslsa_domain_table *, DWORD *);

static ndr_stub_table_t lsarpc_stub_table[] = {
	{ lsarpc_s_CloseHandle,		  LSARPC_OPNUM_CloseHandle },
	{ lsarpc_s_QuerySecurityObject,	  LSARPC_OPNUM_QuerySecurityObject },
	{ lsarpc_s_EnumAccounts,	  LSARPC_OPNUM_EnumerateAccounts },
	{ lsarpc_s_EnumTrustedDomain,	  LSARPC_OPNUM_EnumTrustedDomain },
	{ lsarpc_s_EnumTrustedDomainsEx,  LSARPC_OPNUM_EnumTrustedDomainsEx },
	{ lsarpc_s_OpenAccount,		  LSARPC_OPNUM_OpenAccount },
	{ lsarpc_s_EnumPrivsAccount,	  LSARPC_OPNUM_EnumPrivsAccount },
	{ lsarpc_s_LookupPrivValue,	  LSARPC_OPNUM_LookupPrivValue },
	{ lsarpc_s_LookupPrivName,	  LSARPC_OPNUM_LookupPrivName },
	{ lsarpc_s_LookupPrivDisplayName, LSARPC_OPNUM_LookupPrivDisplayName },
	{ lsarpc_s_CreateSecret,	  LSARPC_OPNUM_CreateSecret },
	{ lsarpc_s_OpenSecret,		  LSARPC_OPNUM_OpenSecret },
	{ lsarpc_s_QueryInfoPolicy,	  LSARPC_OPNUM_QueryInfoPolicy },
	{ lsarpc_s_OpenDomainHandle,	  LSARPC_OPNUM_OpenPolicy },
	{ lsarpc_s_OpenDomainHandle,	  LSARPC_OPNUM_OpenPolicy2 },
	{ lsarpc_s_LookupSids,		  LSARPC_OPNUM_LookupSids },
	{ lsarpc_s_LookupNames,		  LSARPC_OPNUM_LookupNames },
	{ lsarpc_s_GetConnectedUser,	  LSARPC_OPNUM_GetConnectedUser },
	{ lsarpc_s_LookupSids2,		  LSARPC_OPNUM_LookupSids2 },
	{ lsarpc_s_LookupNames2,	  LSARPC_OPNUM_LookupNames2 },
	{0}
};

static ndr_service_t lsarpc_service = {
	"LSARPC",			/* name */
	"Local Security Authority",	/* desc */
	"\\lsarpc",			/* endpoint */
	PIPE_LSASS,			/* sec_addr_port */
	"12345778-1234-abcd-ef00-0123456789ab", 0,	/* abstract */
	NDR_TRANSFER_SYNTAX_UUID,		2,	/* transfer */
	0,				/* no bind_instance_size */
	NULL,				/* no bind_req() */
	NULL,				/* no unbind_and_close() */
	lsarpc_call_stub,		/* call_stub() */
	&TYPEINFO(lsarpc_interface),	/* interface ti */
	lsarpc_stub_table		/* stub_table */
};

/*
 * lsarpc_initialize
 *
 * This function registers the LSA RPC interface with the RPC runtime
 * library. It must be called in order to use either the client side
 * or the server side functions.
 */
void
lsarpc_initialize(void)
{
	(void) ndr_svc_register(&lsarpc_service);
}

/*
 * Custom call_stub to set the stream string policy.
 */
static int
lsarpc_call_stub(ndr_xa_t *mxa)
{
	NDS_SETF(&mxa->send_nds, NDS_F_NOTERM);
	NDS_SETF(&mxa->recv_nds, NDS_F_NOTERM);

	return (ndr_generic_call_stub(mxa));
}

/*
 * lsarpc_s_OpenDomainHandle opnum=0x06
 *
 * This is a request to open the LSA (OpenPolicy and OpenPolicy2).
 * The client is looking for an LSA domain handle.
 */
static int
lsarpc_s_OpenDomainHandle(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_OpenPolicy2 *param = arg;
	ndr_hdid_t *id;

	if ((id = ndr_hdalloc(mxa, &lsarpc_key_domain)) != NULL) {
		bcopy(id, &param->domain_handle, sizeof (mslsa_handle_t));
		param->status = NT_STATUS_SUCCESS;
	} else {
		bzero(&param->domain_handle, sizeof (mslsa_handle_t));
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
	}

	return (NDR_DRC_OK);
}

/*
 * lsarpc_s_CloseHandle opnum=0x00
 *
 * This is a request to close the LSA interface specified by the handle.
 * We don't track handles (yet), so just zero out the handle and return
 * NDR_DRC_OK. Setting the handle to zero appears to be standard
 * behaviour and someone may rely on it, i.e. we do on the client side.
 */
static int
lsarpc_s_CloseHandle(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_CloseHandle *param = arg;
	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;

	ndr_hdfree(mxa, id);

	bzero(&param->result_handle, sizeof (param->result_handle));
	param->status = NT_STATUS_SUCCESS;
	return (NDR_DRC_OK);
}

/*
 * lsarpc_s_QuerySecurityObject
 */
/*ARGSUSED*/
static int
lsarpc_s_QuerySecurityObject(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_QuerySecurityObject *param = arg;

	bzero(param, sizeof (struct mslsa_QuerySecurityObject));
	param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);

	return (NDR_DRC_OK);
}

/*
 * lsarpc_s_EnumAccounts
 *
 * Enumerate the list of local accounts SIDs. The client should supply
 * a valid OpenPolicy2 handle. The enum_context is used to support
 * multiple enumeration calls to obtain the complete list of SIDs.
 * It should be set to 0 on the first call and passed unchanged on
 * subsequent calls until there are no more accounts - the server will
 * return NT_SC_WARNING(MLSVC_NO_MORE_DATA).
 *
 * For now just set the status to access-denied. Note that we still have
 * to provide a valid address for enum_buf because it's a reference and
 * the marshalling rules require that references must not be null.
 * The enum_context is used to support multiple
 */
static int
lsarpc_s_EnumAccounts(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_EnumerateAccounts *param = arg;
	struct mslsa_EnumAccountBuf *enum_buf;

	bzero(param, sizeof (struct mslsa_EnumerateAccounts));

	enum_buf = NDR_NEW(mxa, struct mslsa_EnumAccountBuf);
	if (enum_buf == NULL) {
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	bzero(enum_buf, sizeof (struct mslsa_EnumAccountBuf));
	param->enum_buf = enum_buf;
	param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
	return (NDR_DRC_OK);
}


/*
 * lsarpc_s_EnumTrustedDomain
 *
 * This is the server side function for handling requests to enumerate
 * the list of trusted domains: currently held in the NT domain database.
 * This call requires an OpenPolicy2 handle. The enum_context is used to
 * support multiple enumeration calls to obtain the complete list.
 * It should be set to 0 on the first call and passed unchanged on
 * subsequent calls until there are no more accounts - the server will
 * return NT_SC_WARNING(MLSVC_NO_MORE_DATA).
 *
 * For now just set the status to access-denied. Note that we still have
 * to provide a valid address for enum_buf because it's a reference and
 * the marshalling rules require that references must not be null.
 */
static int
lsarpc_s_EnumTrustedDomain(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_EnumTrustedDomain *param = arg;
	struct mslsa_EnumTrustedDomainBuf *enum_buf;

	bzero(param, sizeof (struct mslsa_EnumTrustedDomain));

	enum_buf = NDR_NEW(mxa, struct mslsa_EnumTrustedDomainBuf);
	if (enum_buf == NULL) {
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	bzero(enum_buf, sizeof (struct mslsa_EnumTrustedDomainBuf));
	param->enum_buf = enum_buf;
	param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
	return (NDR_DRC_OK);
}

/*
 * lsarpc_s_EnumTrustedDomainsEx
 *
 * This is the server side function for handling requests to enumerate
 * the list of trusted domains: currently held in the NT domain database.
 * This call requires an OpenPolicy2 handle. The enum_context is used to
 * support multiple enumeration calls to obtain the complete list.
 * It should be set to 0 on the first call and passed unchanged on
 * subsequent calls until there are no more accounts - the server will
 * return NT_SC_WARNING(MLSVC_NO_MORE_DATA).
 *
 * For now just set the status to access-denied. Note that we still have
 * to provide a valid address for enum_buf because it's a reference and
 * the marshalling rules require that references must not be null.
 */
static int
lsarpc_s_EnumTrustedDomainsEx(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_EnumTrustedDomainEx *param = arg;
	struct mslsa_EnumTrustedDomainBufEx *enum_buf;

	bzero(param, sizeof (struct mslsa_EnumTrustedDomainEx));

	enum_buf = NDR_NEW(mxa, struct mslsa_EnumTrustedDomainBufEx);
	if (enum_buf == NULL) {
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	bzero(enum_buf, sizeof (struct mslsa_EnumTrustedDomainBufEx));
	param->enum_buf = enum_buf;
	param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
	return (NDR_DRC_OK);
}

/*
 * lsarpc_s_OpenAccount
 *
 * This is a request to open an account handle.
 */
static int
lsarpc_s_OpenAccount(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_OpenAccount *param = arg;
	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
	ndr_handle_t *hd;

	hd = ndr_hdlookup(mxa, id);
	if ((hd == NULL) || (hd->nh_data != &lsarpc_key_domain)) {
		bzero(param, sizeof (struct mslsa_OpenAccount));
		param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
		return (NDR_DRC_OK);
	}

	if ((id = ndr_hdalloc(mxa, &lsarpc_key_account)) != NULL) {
		bcopy(id, &param->account_handle, sizeof (mslsa_handle_t));
		param->status = NT_STATUS_SUCCESS;
	} else {
		bzero(&param->account_handle, sizeof (mslsa_handle_t));
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
	}

	return (NDR_DRC_OK);
}


/*
 * lsarpc_s_EnumPrivsAccount
 *
 * This is the server side function for handling requests for account
 * privileges. For now just set the status to not-supported status and
 * return NDR_DRC_OK. Note that we still have to provide a valid
 * address for enum_buf because it's a reference and the marshalling
 * rules require that references must not be null.
 */
/*ARGSUSED*/
static int
lsarpc_s_EnumPrivsAccount(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_EnumPrivsAccount *param = arg;

	bzero(param, sizeof (struct mslsa_EnumPrivsAccount));
	param->status = NT_SC_ERROR(NT_STATUS_NOT_SUPPORTED);
	return (NDR_DRC_OK);
}

/*
 * lsarpc_s_LookupPrivValue
 *
 * Server side function used to map a privilege name to a locally unique
 * identifier (LUID).
 */
/*ARGSUSED*/
static int
lsarpc_s_LookupPrivValue(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_LookupPrivValue *param = arg;
	smb_privinfo_t *pi;

	if ((pi = smb_priv_getbyname((char *)param->name.str)) == NULL) {
		bzero(param, sizeof (struct mslsa_LookupPrivValue));
		param->status = NT_SC_ERROR(NT_STATUS_NO_SUCH_PRIVILEGE);
		return (NDR_DRC_OK);
	}

	param->luid.low_part = pi->id;
	param->luid.high_part = 0;
	param->status = NT_STATUS_SUCCESS;
	return (NDR_DRC_OK);
}

/*
 * lsarpc_s_LookupPrivName
 *
 * Server side function used to map a locally unique identifier (LUID)
 * to the appropriate privilege name string.
 */
static int
lsarpc_s_LookupPrivName(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_LookupPrivName *param = arg;
	smb_privinfo_t *pi;
	int rc;

	if ((pi = smb_priv_getbyvalue(param->luid.low_part)) == NULL) {
		bzero(param, sizeof (struct mslsa_LookupPrivName));
		param->status = NT_SC_ERROR(NT_STATUS_NO_SUCH_PRIVILEGE);
		return (NDR_DRC_OK);
	}

	param->name = NDR_NEW(mxa, mslsa_string_t);
	if (param->name == NULL) {
		bzero(param, sizeof (struct mslsa_LookupPrivName));
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	rc = NDR_MSTRING(mxa, pi->name, (ndr_mstring_t *)param->name);
	if (rc == -1) {
		bzero(param, sizeof (struct mslsa_LookupPrivName));
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	param->status = NT_STATUS_SUCCESS;
	return (NDR_DRC_OK);
}

/*
 * lsarpc_s_LookupPrivDisplayName
 *
 * This is the server side function for handling requests for account
 * privileges. For now just set the status to not-supported status and
 * return NDR_DRC_OK.
 */
static int
lsarpc_s_LookupPrivDisplayName(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_LookupPrivDisplayName *param = arg;
	smb_privinfo_t *pi;
	int rc;

	if ((pi = smb_priv_getbyname((char *)param->name.str)) == NULL) {
		bzero(param, sizeof (struct mslsa_LookupPrivDisplayName));
		param->status = NT_SC_ERROR(NT_STATUS_NO_SUCH_PRIVILEGE);
		return (NDR_DRC_OK);
	}

	param->display_name = NDR_NEW(mxa, mslsa_string_t);
	if (param->display_name == NULL) {
		bzero(param, sizeof (struct mslsa_LookupPrivDisplayName));
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	rc = NDR_MSTRING(mxa, pi->display_name,
	    (ndr_mstring_t *)param->display_name);
	if (rc == -1) {
		bzero(param, sizeof (struct mslsa_LookupPrivDisplayName));
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	param->language_ret = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
	param->status = NT_STATUS_SUCCESS;
	return (NDR_DRC_OK);
}

static int
lsarpc_s_CreateSecret(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_CreateSecret *param = arg;
	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
	ndr_handle_t *hd;

	hd = ndr_hdlookup(mxa, id);
	if ((hd == NULL) || (hd->nh_data != &lsarpc_key_domain)) {
		bzero(param, sizeof (struct mslsa_OpenAccount));
		param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
		return (NDR_DRC_OK);
	}

	bzero(&param->secret_handle, sizeof (mslsa_handle_t));
	param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
	return (NDR_DRC_OK);
}

static int
lsarpc_s_OpenSecret(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_OpenSecret *param = arg;
	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
	ndr_handle_t *hd;

	hd = ndr_hdlookup(mxa, id);
	if ((hd == NULL) || (hd->nh_data != &lsarpc_key_domain)) {
		bzero(param, sizeof (struct mslsa_OpenAccount));
		param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
		return (NDR_DRC_OK);
	}

	bzero(&param->secret_handle, sizeof (mslsa_handle_t));
	param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
	return (NDR_DRC_OK);
}

/*
 * lsarpc_s_GetConnectedUser
 *
 * This is still guesswork. Netmon doesn't know about this
 * call and I'm not really sure what it is intended to achieve.
 * Another packet capture application, Ethereal, calls this RPC as
 * GetConnectedUser.
 * We will receive our own hostname in the request and it appears
 * we should respond with an account name and the domain name of connected
 * user from the client that makes this call.
 */
static int
lsarpc_s_GetConnectedUser(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_GetConnectedUser *param = arg;
	smb_netuserinfo_t *user = &mxa->pipe->np_user;
	DWORD status = NT_STATUS_SUCCESS;
	smb_domainex_t di;
	int rc1;
	int rc2;

	if (!smb_domain_getinfo(&di)) {
		bzero(param, sizeof (struct mslsa_GetConnectedUser));
		status = NT_SC_ERROR(NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
		param->status = status;
		return (NDR_DRC_OK);
	}

	param->owner = NDR_NEW(mxa, struct mslsa_string_desc);
	param->domain = NDR_NEW(mxa, struct mslsa_DomainName);
	if (param->owner == NULL || param->domain == NULL) {
		status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		param->status = status;
		return (NDR_DRC_OK);
	}

	param->domain->name = NDR_NEW(mxa, struct mslsa_string_desc);
	if (param->domain->name == NULL) {
		status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		param->status = status;
		return (NDR_DRC_OK);
	}

	rc1 = NDR_MSTRING(mxa, user->ui_account,
	    (ndr_mstring_t *)param->owner);
	rc2 = NDR_MSTRING(mxa, user->ui_domain,
	    (ndr_mstring_t *)param->domain->name);

	if (rc1 == -1 || rc2 == -1)
		status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);

	param->status = status;
	return (NDR_DRC_OK);
}


/*
 * lsarpc_s_QueryInfoPolicy
 *
 * This is the server side function for handling LSA information policy
 * queries. Currently, we only support primary domain and account
 * domain queries. This is just a front end to switch on the request
 * and hand it off to the appropriate function to actually deal with
 * obtaining and building the response.
 */
static int
lsarpc_s_QueryInfoPolicy(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_QueryInfoPolicy *param = arg;
	union mslsa_PolicyInfoResUnion *ru = &param->ru;
	int security_mode;
	DWORD status;

	param->switch_value = param->info_class;

	switch (param->info_class) {
	case MSLSA_POLICY_AUDIT_EVENTS_INFO:
		ru->audit_events.enabled = 0;
		ru->audit_events.count = 1;
		ru->audit_events.settings
		    = NDR_MALLOC(mxa, sizeof (DWORD));
		bzero(ru->audit_events.settings, sizeof (DWORD));
		status = NT_STATUS_SUCCESS;
		break;

	case MSLSA_POLICY_PRIMARY_DOMAIN_INFO:
		status = lsarpc_s_PrimaryDomainInfo(&ru->pd_info, mxa);
		break;

	case MSLSA_POLICY_ACCOUNT_DOMAIN_INFO:
		status = lsarpc_s_AccountDomainInfo(&ru->ad_info, mxa);
		break;

	case MSLSA_POLICY_SERVER_ROLE_INFO:
		security_mode = smb_config_get_secmode();

		if (security_mode == SMB_SECMODE_DOMAIN)
			ru->server_role.role = LSA_ROLE_MEMBER_SERVER;
		else
			ru->server_role.role = LSA_ROLE_STANDALONE_SERVER;

		ru->server_role.pad = 0;
		status = NT_STATUS_SUCCESS;
		break;

	default:
		bzero(param, sizeof (struct mslsa_QueryInfoPolicy));
		param->status = NT_SC_ERROR(NT_STATUS_INVALID_INFO_CLASS);
		return (NDR_DRC_OK);
	}

	if (status != NT_STATUS_SUCCESS)
		param->status = NT_SC_ERROR(status);
	else
		param->status = NT_STATUS_SUCCESS;
	param->address = (DWORD)(uintptr_t)ru;

	return (NDR_DRC_OK);
}


/*
 * lsarpc_s_PrimaryDomainInfo
 *
 * Service primary domain policy queries.  In domain mode, return the
 * primary domain name and SID.   In workgroup mode, return the local
 * hostname and local domain SID.
 *
 * Note: info is zeroed on entry to ensure the SID and name do not
 * contain spurious values if an error is returned.
 */
static DWORD
lsarpc_s_PrimaryDomainInfo(struct mslsa_PrimaryDomainInfo *info,
    ndr_xa_t *mxa)
{
	smb_domain_t di;
	boolean_t found;
	int rc;

	bzero(info, sizeof (struct mslsa_PrimaryDomainInfo));

	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
		found = smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di);
	else
		found = smb_domain_lookup_type(SMB_DOMAIN_PRIMARY, &di);

	if (!found)
		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);

	rc = NDR_MSTRING(mxa, di.di_nbname, (ndr_mstring_t *)&info->name);
	info->sid = (struct mslsa_sid *)NDR_SIDDUP(mxa, di.di_binsid);

	if ((rc == -1) || (info->sid == NULL))
		return (NT_STATUS_NO_MEMORY);

	return (NT_STATUS_SUCCESS);
}


/*
 * lsarpc_s_AccountDomainInfo
 *
 * Service account domain policy queries.  We return our local domain
 * information so that the client knows who to query for information
 * on local names and SIDs.  The domain name is the local hostname.
 *
 * Note: info is zeroed on entry to ensure the SID and name do not
 * contain spurious values if an error is returned.
 */
static DWORD
lsarpc_s_AccountDomainInfo(struct mslsa_AccountDomainInfo *info,
    ndr_xa_t *mxa)
{
	smb_domain_t di;
	int rc;

	bzero(info, sizeof (struct mslsa_AccountDomainInfo));

	if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);

	rc = NDR_MSTRING(mxa, di.di_nbname, (ndr_mstring_t *)&info->name);
	info->sid = (struct mslsa_sid *)NDR_SIDDUP(mxa, di.di_binsid);

	if ((rc == -1) || (info->sid == NULL))
		return (NT_STATUS_NO_MEMORY);

	return (NT_STATUS_SUCCESS);
}

/*
 * lsarpc_s_LookupNames
 *
 * This is the service side function for handling name lookup requests.
 * Currently, we only support lookups of a single name. This is also a
 * pass through interface so all we do is act as a proxy between the
 * client and the DC.
 */
static int
lsarpc_s_LookupNames(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_LookupNames *param = arg;
	struct mslsa_rid_entry *rids;
	struct mslsa_domain_table *domain_table;
	struct mslsa_domain_entry *domain_entry;
	smb_account_t account;
	uint32_t status;
	char *accname;
	int rc = 0;

	if (param->name_table->n_entry != 1)
		return (NDR_DRC_FAULT_PARAM_0_UNIMPLEMENTED);

	rids = NDR_NEW(mxa, struct mslsa_rid_entry);
	domain_table = NDR_NEW(mxa, struct mslsa_domain_table);
	domain_entry = NDR_NEW(mxa, struct mslsa_domain_entry);

	if (rids == NULL || domain_table == NULL || domain_entry == NULL) {
		bzero(param, sizeof (struct mslsa_LookupNames));
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	accname = (char *)param->name_table->names->str;
	status = lsa_lookup_name(accname, SidTypeUnknown, &account);
	if (status != NT_STATUS_SUCCESS) {
		bzero(param, sizeof (struct mslsa_LookupNames));
		param->status = NT_SC_ERROR(status);
		return (NDR_DRC_OK);
	}

	/*
	 * Set up the rid table.
	 */
	rids[0].sid_name_use = account.a_type;
	rids[0].rid = account.a_rid;
	rids[0].domain_index = 0;
	param->translated_sids.n_entry = 1;
	param->translated_sids.rids = rids;

	/*
	 * Set up the domain table.
	 */
	domain_table->entries = domain_entry;
	domain_table->n_entry = 1;
	domain_table->max_n_entry = MLSVC_DOMAIN_MAX;

	rc = NDR_MSTRING(mxa, account.a_domain,
	    (ndr_mstring_t *)&domain_entry->domain_name);
	domain_entry->domain_sid =
	    (struct mslsa_sid *)NDR_SIDDUP(mxa, account.a_domsid);

	if (rc == -1 || domain_entry->domain_sid == NULL) {
		smb_account_free(&account);
		bzero(param, sizeof (struct mslsa_LookupNames));
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	param->domain_table = domain_table;
	param->mapped_count = 1;
	param->status = NT_STATUS_SUCCESS;

	smb_account_free(&account);
	return (NDR_DRC_OK);
}

/*
 * lsarpc_s_LookupSids
 *
 * This is the service side function for handling sid lookup requests.
 * We have to set up both the name table and the domain table in the
 * response. For each SID, we check for UNIX domain (local lookup) or
 * NT domain (DC lookup) and call the appropriate lookup function. This
 * should resolve the SID to a name. Then we need to update the domain
 * table and make the name entry point at the appropriate domain table
 * entry.
 *
 * On success return 0. Otherwise return an RPC specific error code.
 */
static int
lsarpc_s_LookupSids(void *arg, ndr_xa_t *mxa)
{
	struct mslsa_LookupSids *param = arg;
	struct mslsa_domain_table *domain_table;
	struct mslsa_domain_entry *domain_entry;
	struct mslsa_name_entry *names;
	struct mslsa_name_entry *name;
	smb_account_t account;
	smb_sid_t *sid;
	DWORD n_entry;
	int result;
	int i;

	n_entry = param->lup_sid_table.n_entry;
	names = NDR_NEWN(mxa, struct mslsa_name_entry, n_entry);
	domain_table = NDR_NEW(mxa, struct mslsa_domain_table);
	domain_entry = NDR_NEWN(mxa, struct mslsa_domain_entry,
	    MLSVC_DOMAIN_MAX);

	if (names == NULL || domain_table == NULL || domain_entry == NULL) {
		bzero(param, sizeof (struct mslsa_LookupSids));
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	domain_table->entries = domain_entry;
	domain_table->n_entry = 0;
	domain_table->max_n_entry = MLSVC_DOMAIN_MAX;

	name = names;
	for (i = 0; i < n_entry; ++i, name++) {
		bzero(&names[i], sizeof (struct mslsa_name_entry));
		sid = (smb_sid_t *)param->lup_sid_table.entries[i].psid;

		result = lsa_lookup_sid(sid, &account);
		if (result != NT_STATUS_SUCCESS)
			goto lookup_sid_failed;

		if (*account.a_name != '\0') {
			if (NDR_MSTRING(mxa, account.a_name,
			    (ndr_mstring_t *)&name->name) == -1) {
				result = NT_STATUS_NO_MEMORY;
				goto lookup_sid_failed;
			}
		}
		name->sid_name_use = account.a_type;

		result = lsarpc_s_UpdateDomainTable(mxa, &account,
		    domain_table, &name->domain_ix);

		if (result == -1) {
			result = NT_STATUS_INTERNAL_ERROR;
			goto lookup_sid_failed;
		}

		smb_account_free(&account);
	}

	param->domain_table = domain_table;
	param->name_table.n_entry = n_entry;
	param->name_table.entries = names;
	param->mapped_count = n_entry;
	param->status = 0;

	return (NDR_DRC_OK);

lookup_sid_failed:
	param->domain_table = 0;
	param->name_table.n_entry = 0;
	param->name_table.entries = 0;
	param->mapped_count = 0;
	param->status = NT_SC_ERROR(result);

	smb_account_free(&account);
	return (NDR_DRC_OK);
}

/*
 * lsarpc_s_UpdateDomainTable
 *
 * This routine is responsible for maintaining the domain table which
 * will be returned from a SID lookup. Whenever a name is added to the
 * name table, this function should be called with the corresponding
 * domain name. If the domain information is not already in the table,
 * it is added. On success return 0; Otherwise -1 is returned.
 */
static int
lsarpc_s_UpdateDomainTable(ndr_xa_t *mxa,
    smb_account_t *account, struct mslsa_domain_table *domain_table,
    DWORD *domain_idx)
{
	struct mslsa_domain_entry *dentry;
	DWORD n_entry;
	DWORD i;
	int rc;

	if (account->a_type == SidTypeUnknown ||
	    account->a_type == SidTypeInvalid) {
		/*
		 * These types don't need to reference an entry in the
		 * domain table. So return -1.
		 */
		*domain_idx = (DWORD)-1;
		return (0);
	}

	if ((dentry = domain_table->entries) == NULL)
		return (-1);

	if ((n_entry = domain_table->n_entry) >= MLSVC_DOMAIN_MAX)
		return (-1);

	for (i = 0; i < n_entry; ++i) {
		if (smb_sid_cmp((smb_sid_t *)dentry[i].domain_sid,
		    account->a_domsid)) {
			*domain_idx = i;
			return (0);
		}
	}

	if (i == MLSVC_DOMAIN_MAX)
		return (-1);

	rc = NDR_MSTRING(mxa, account->a_domain,
	    (ndr_mstring_t *)&dentry[i].domain_name);
	dentry[i].domain_sid =
	    (struct mslsa_sid *)NDR_SIDDUP(mxa, account->a_domsid);

	if (rc == -1 || dentry[i].domain_sid == NULL)
		return (-1);

	++domain_table->n_entry;
	*domain_idx = i;
	return (0);
}

/*
 * lsarpc_s_LookupSids2
 *
 * Other than the use of lsar_lookup_sids2 and lsar_name_entry2, this
 * is identical to lsarpc_s_LookupSids.
 */
static int
lsarpc_s_LookupSids2(void *arg, ndr_xa_t *mxa)
{
	struct lsar_lookup_sids2 *param = arg;
	struct lsar_name_entry2 *names;
	struct lsar_name_entry2 *name;
	struct mslsa_domain_table *domain_table;
	struct mslsa_domain_entry *domain_entry;
	smb_account_t account;
	smb_sid_t *sid;
	DWORD n_entry;
	int result;
	int i;

	n_entry = param->lup_sid_table.n_entry;
	names = NDR_NEWN(mxa, struct lsar_name_entry2, n_entry);
	domain_table = NDR_NEW(mxa, struct mslsa_domain_table);
	domain_entry = NDR_NEWN(mxa, struct mslsa_domain_entry,
	    MLSVC_DOMAIN_MAX);

	if (names == NULL || domain_table == NULL || domain_entry == NULL) {
		bzero(param, sizeof (struct lsar_lookup_sids2));
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	domain_table->entries = domain_entry;
	domain_table->n_entry = 0;
	domain_table->max_n_entry = MLSVC_DOMAIN_MAX;

	name = names;
	for (i = 0; i < n_entry; ++i, name++) {
		bzero(name, sizeof (struct lsar_name_entry2));
		sid = (smb_sid_t *)param->lup_sid_table.entries[i].psid;

		result = lsa_lookup_sid(sid, &account);
		if (result != NT_STATUS_SUCCESS)
			goto lookup_sid_failed;

		if (*account.a_name != '\0') {
			if (NDR_MSTRING(mxa, account.a_name,
			    (ndr_mstring_t *)&name->name) == -1) {
				result = NT_STATUS_NO_MEMORY;
				goto lookup_sid_failed;
			}
		}
		name->sid_name_use = account.a_type;

		result = lsarpc_s_UpdateDomainTable(mxa, &account,
		    domain_table, &name->domain_ix);

		if (result == -1) {
			result = NT_STATUS_INTERNAL_ERROR;
			goto lookup_sid_failed;
		}

		smb_account_free(&account);
	}

	param->domain_table = domain_table;
	param->name_table.n_entry = n_entry;
	param->name_table.entries = names;
	param->mapped_count = n_entry;
	param->status = 0;

	return (NDR_DRC_OK);

lookup_sid_failed:
	param->domain_table = 0;
	param->name_table.n_entry = 0;
	param->name_table.entries = 0;
	param->mapped_count = 0;
	param->status = NT_SC_ERROR(result);

	smb_account_free(&account);
	return (NDR_DRC_OK);
}

/*
 * lsarpc_s_LookupNames2
 *
 * Other than the use of lsar_LookupNames2 and lsar_rid_entry2, this
 * is identical to lsarpc_s_LookupNames.
 */
static int
lsarpc_s_LookupNames2(void *arg, ndr_xa_t *mxa)
{
	struct lsar_LookupNames2 *param = arg;
	struct lsar_rid_entry2 *rids;
	struct mslsa_domain_table *domain_table;
	struct mslsa_domain_entry *domain_entry;
	smb_account_t account;
	uint32_t status;
	char *accname;
	int rc = 0;

	if (param->name_table->n_entry != 1)
		return (NDR_DRC_FAULT_PARAM_0_UNIMPLEMENTED);

	rids = NDR_NEW(mxa, struct lsar_rid_entry2);
	domain_table = NDR_NEW(mxa, struct mslsa_domain_table);
	domain_entry = NDR_NEW(mxa, struct mslsa_domain_entry);

	if (rids == NULL || domain_table == NULL || domain_entry == NULL) {
		bzero(param, sizeof (struct lsar_LookupNames2));
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	accname = (char *)param->name_table->names->str;
	status = lsa_lookup_name(accname, SidTypeUnknown, &account);
	if (status != NT_STATUS_SUCCESS) {
		bzero(param, sizeof (struct lsar_LookupNames2));
		param->status = NT_SC_ERROR(status);
		return (NDR_DRC_OK);
	}

	/*
	 * Set up the rid table.
	 */
	bzero(rids, sizeof (struct lsar_rid_entry2));
	rids[0].sid_name_use = account.a_type;
	rids[0].rid = account.a_rid;
	rids[0].domain_index = 0;
	param->translated_sids.n_entry = 1;
	param->translated_sids.rids = rids;

	/*
	 * Set up the domain table.
	 */
	domain_table->entries = domain_entry;
	domain_table->n_entry = 1;
	domain_table->max_n_entry = MLSVC_DOMAIN_MAX;

	rc = NDR_MSTRING(mxa, account.a_domain,
	    (ndr_mstring_t *)&domain_entry->domain_name);

	domain_entry->domain_sid =
	    (struct mslsa_sid *)NDR_SIDDUP(mxa, account.a_domsid);

	if (rc == -1 || domain_entry->domain_sid == NULL) {
		smb_account_free(&account);
		bzero(param, sizeof (struct lsar_LookupNames2));
		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
		return (NDR_DRC_OK);
	}

	param->domain_table = domain_table;
	param->mapped_count = 1;
	param->status = NT_STATUS_SUCCESS;

	smb_account_free(&account);
	return (NDR_DRC_OK);
}


/*
 * There is a bug in the way that ndrgen and the marshalling code handles
 * unions so we need to fix some of the data offsets at runtime. The
 * following macros and the fixup functions handle the corrections.
 */

DECL_FIXUP_STRUCT(mslsa_PolicyInfoResUnion);
DECL_FIXUP_STRUCT(mslsa_PolicyInfoRes);
DECL_FIXUP_STRUCT(mslsa_QueryInfoPolicy);
void
fixup_mslsa_QueryInfoPolicy(struct mslsa_QueryInfoPolicy *val)
{
	unsigned short size1 = 0;
	unsigned short size2 = 0;
	unsigned short size3 = 0;

	switch (val->info_class) {
		case MSLSA_POLICY_AUDIT_EVENTS_INFO:
			size1 = sizeof (struct mslsa_AuditEventsInfo);
			break;

		case MSLSA_POLICY_PRIMARY_DOMAIN_INFO:
			size1 = sizeof (struct mslsa_PrimaryDomainInfo);
			break;

		case MSLSA_POLICY_ACCOUNT_DOMAIN_INFO:
			size1 = sizeof (struct mslsa_AccountDomainInfo);
			break;

		case MSLSA_POLICY_SERVER_ROLE_INFO:
			size1 = sizeof (struct mslsa_ServerRoleInfo);
			break;

		case MSLSA_POLICY_DNS_DOMAIN_INFO:
			size1 = sizeof (struct mslsa_DnsDomainInfo);
			break;

		default:
			return;
	};

	size2 = size1 + (2 * sizeof (DWORD));
	size3 = size2 + sizeof (ndr_request_hdr_t) + sizeof (DWORD);

	FIXUP_PDU_SIZE(mslsa_PolicyInfoResUnion, size1);
	FIXUP_PDU_SIZE(mslsa_PolicyInfoRes, size2);
	FIXUP_PDU_SIZE(mslsa_QueryInfoPolicy, size3);
}