mlsvc_lsa.c revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0
/*
* 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
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Local Security Authority RPC (LSARPC) server-side interface definition.
*/
#include <unistd.h>
#include <strings.h>
#include <pwd.h>
#include <grp.h>
#include <smbsrv/libmlsvc.h>
#include <smbsrv/mlsvc_util.h>
#include <smbsrv/ntstatus.h>
#include <smbsrv/ntlocale.h>
struct local_group_table {
char *sid;
char *name;
};
static int lsarpc_s_PrimaryDomainInfo(struct mslsa_PrimaryDomainInfo *,
struct mlrpc_xaction *);
static int lsarpc_s_AccountDomainInfo(struct mslsa_AccountDomainInfo *,
struct mlrpc_xaction *);
smb_userinfo_t *, struct mslsa_name_entry *, int);
smb_userinfo_t *, struct mslsa_name_entry *);
smb_userinfo_t *, struct mslsa_name_entry *);
smb_userinfo_t *, struct mslsa_name_entry *);
static int lsarpc_s_UpdateDomainTable(struct mlrpc_xaction *,
static int lsarpc_w2k_enable;
static mlrpc_stub_table_t lsarpc_stub_table[] = {
{0}
};
static mlrpc_service_t lsarpc_service = {
"LSARPC", /* name */
"Local Security Authority", /* desc */
"\\lsarpc", /* endpoint */
PIPE_LSASS, /* sec_addr_port */
"12345778-1234-abcd-ef000123456789ab", 0, /* abstract */
"8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
0, /* no bind_instance_size */
0, /* no bind_req() */
0, /* no unbind_and_close() */
0, /* use generic_call_stub() */
lsarpc_stub_table /* stub_table */
};
/*
* Windows 2000 interface.
*/
static mlrpc_service_t lsarpc_w2k_service = {
"LSARPC_W2K", /* name */
"Local Security Authority", /* desc */
"\\lsarpc", /* endpoint */
PIPE_LSASS, /* sec_addr_port */
"3919286a-b10c-11d0-9ba800c04fd92ef5", 0, /* abstract */
"8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
0, /* no bind_instance_size */
0, /* no bind_req() */
0, /* no unbind_and_close() */
0, /* use generic_call_stub() */
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) mlrpc_register_service(&lsarpc_service);
if (lsarpc_w2k_enable)
(void) mlrpc_register_service(&lsarpc_w2k_service);
}
/*
* 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. Handles appear to
* be a 20 byte opaque object with the top 4 bytes all zero. As it is
* opaque to the client, we can put anything we like in it. Real handles
* do appear to contain a sequence number which is incremented when a
* new handle is issued. However, we don't really care about that
* (yet). Always return MLRPC_DRC_OK.
*/
/*ARGSUSED*/
static int
{
return (MLRPC_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
* MLRPC_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.
*/
/*ARGSUSED*/
static int
{
return (MLRPC_DRC_OK);
}
/*
* lsarpc_s_QuerySecurityObject
*/
/*ARGSUSED*/
static int
{
return (MLRPC_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
{
struct mslsa_EnumAccountBuf *enum_buf;
return (MLRPC_DRC_OK);
}
return (MLRPC_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
{
struct mslsa_EnumTrustedDomainBuf *enum_buf;
return (MLRPC_DRC_OK);
}
return (MLRPC_DRC_OK);
}
/*
* lsarpc_s_OpenAccount
*
* This is a request to open an account handle. This function hasn't
* been tested. It is just a template in case some server somewhere
* makes this call. See lsarpc_s_OpenDomainHandle for more information.
*/
/*ARGSUSED*/
static int
{
} else {
"AccountHandle");
}
return (MLRPC_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 MLRPC_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
{
return (MLRPC_DRC_OK);
}
/*
* lsarpc_s_LookupPrivValue
*
* Server side function used to map a privilege name to a locally unique
* identifier (LUID).
*/
/*ARGSUSED*/
static int
{
return (MLRPC_DRC_OK);
}
return (MLRPC_DRC_OK);
}
/*
* lsarpc_s_LookupPrivName
*
* Server side function used to map a locally unique identifier (LUID)
* to the appropriate privilege name string.
*/
static int
{
int rc;
return (MLRPC_DRC_OK);
}
return (MLRPC_DRC_OK);
}
if (rc == 0) {
return (MLRPC_DRC_OK);
}
return (MLRPC_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 MLRPC_DRC_OK.
*/
static int
{
int rc;
return (MLRPC_DRC_OK);
}
return (MLRPC_DRC_OK);
}
if (rc == 0) {
return (MLRPC_DRC_OK);
}
return (MLRPC_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
{
int rc1;
int rc2;
return (MLRPC_DRC_OK);
}
if (smb_getdomaininfo(0) == NULL) {
return (MLRPC_DRC_OK);
}
return (MLRPC_DRC_OK);
}
return (MLRPC_DRC_OK);
}
return (MLRPC_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
{
struct mslsa_PolicyInfo *info;
int result;
mxa, sizeof (struct mslsa_PolicyInfo));
switch (param->info_class) {
break;
break;
default:
break;
}
return (result);
}
/*
* lsarpc_s_PrimaryDomainInfo
*
* This is the service side function for handling primary domain policy
* queries. This will return the primary domain name and sid. This is
* currently a pass through interface so all we do is act as a proxy
* between the client and the DC. If there is no session, fake up the
* response with default values - useful for share mode.
*
* If the server name matches the local hostname, we should return
* the local domain SID.
*/
static int
struct mlrpc_xaction *mxa)
{
int security_mode;
char domain_name[MLSVC_DOMAIN_NAME_MAX];
char *name;
int rc;
if (security_mode != SMB_SECMODE_DOMAIN) {
if (rc != 0) {
return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
}
name = domain_name;
} else {
if ((di = smb_getdomaininfo(0)) == 0) {
return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
}
if (ntdp == 0) {
(void) lsa_query_primary_domain_info();
}
if (ntdp == 0) {
sid = nt_sid_gen_null_sid();
} else {
}
}
if (sid == 0) {
return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
}
if (status != NT_STATUS_SUCCESS)
return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
return (MLRPC_DRC_OK);
}
/*
* lsarpc_s_AccountDomainInfo
*
* This is the service side function for handling account domain policy
* queries. This is where we return our local domain information so that
* NT knows who to query for information on local names and SIDs. The
* domain name is the local hostname.
*/
static int
struct mlrpc_xaction *mxa)
{
char domain_name[MLSVC_DOMAIN_NAME_MAX];
int rc;
return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
domain_name, mxa);
if (rc == 0)
return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
return (MLRPC_DRC_OK);
}
/*
* 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
{
struct mslsa_rid_entry *rids;
smb_userinfo_t *user_info = 0;
struct mslsa_domain_table *domain_table;
struct mslsa_domain_entry *domain_entry;
char *name = "";
int rc = 0;
return (MLRPC_DRC_FAULT_PARAM_0_UNIMPLEMENTED);
goto name_lookup_failed;
}
if (rc < 0) {
goto name_lookup_failed;
}
if (rc > 0) {
goto name_lookup_failed;
}
}
/*
* Set up the rid table.
*/
rids[0].domain_index = 0;
/*
* Set up the domain table.
*/
goto name_lookup_failed;
}
return (MLRPC_DRC_OK);
return (MLRPC_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
{
struct mslsa_domain_table *domain_table;
struct mslsa_domain_entry *domain_entry;
struct mslsa_name_entry *names;
int result;
int i;
return (MLRPC_DRC_OK);
}
domain_table->n_entry = 0;
for (i = 0; i < n_entry; ++i) {
if (nt_sid_is_local(sid)) {
&names[i]);
} else {
&names[i]);
if (result != 0)
if (result != 0) {
}
}
if (result == -1) {
param->domain_table = 0;
param->mapped_count = 0;
return (MLRPC_DRC_OK);
}
if (result == -1) {
param->domain_table = 0;
param->mapped_count = 0;
return (MLRPC_DRC_OK);
}
}
return (MLRPC_DRC_OK);
}
/*
* lsarpc_s_LookupLocalSid
*
* This function handles local domain SID lookup. If the SID matches the
* local domain SID, we lookup the local files to map the RID to a name.
* We attempt to handle both users and groups. When the SID was supplied
* to the client, the ID type should have been encoded in the RID. We
* decode the RID and lookup it up in either the passwd file or the
* group file as appropriate.
*
* On success, 0 is returned. Otherwise -1 is returned.
*/
static int
{
char buffer[MLSVC_DOMAIN_NAME_MAX];
char namebuf[MLSVC_DOMAIN_NAME_MAX];
int unix_id;
return (-1);
/*
* Only free tmp_sid in error paths. If it is assigned to the
* user_info, it will be freed later when that structure is
* released.
*/
return (-1);
rid = 0;
lds = nt_domain_local_sid();
} else {
switch (SAM_RID_TYPE(rid)) {
case SAM_RT_NT_UID:
break;
case SAM_RT_NT_GID:
if (grp)
else {
"%d (no name)", rid);
}
break;
case SAM_RT_UNIX_UID:
/*
* It is always possible that the rid will not
* correspond to an entry in the local passwd or group
* file. In this case we can return the RID with a
* message to indicate the problem, which seems better
* than returning an invalid SID error.
*/
"%d (no name)", unix_id);
break;
case SAM_RT_UNIX_GID:
"%d (no name)", unix_id);
break;
}
}
return (-1);
}
/*
* Set up the rest of user_info.
* Don't free tmp_sid after this.
*/
if (!mlsvc_string_save(
return (-1);
}
return (0);
}
/*
* lsarpc_s_LookupNtSid
*
* This function handles NT domain SID lookup on the domain controller.
* Most of the work is performed by lsa_lookup_sid. We just have to
* update the name data for the response. It is assumed that any SID
* passed to this function has already been checked and correctly
* identified as an NT domain SID. It shouldn't break anything if you
* get it wrong, the domain controller will just reject the SID.
*
* On success, 0 is returned. Otherwise -1 is returned.
*/
static int
{
char *username;
if (smb_getdomaininfo(0) == 0) {
return (-1);
}
if (version == 2)
else
if (status != 0)
return (-1);
switch (user_info->sid_name_use) {
case SidTypeDomain:
break;
case SidTypeUser:
case SidTypeGroup:
case SidTypeAlias:
case SidTypeDeletedAccount:
case SidTypeWellKnownGroup:
break;
default:
return (-1);
}
if (username == 0)
username = "unknown";
return (-1);
return (0);
}
/*
* lsarpc_s_LookupBuiltinSid
*
* This function handles predefined local groups and aliases in the NT
* AUTHORITY or BUILTIN domains, and some other miscellaneous bits. I
* don't think NT cares about the domain field of well-known groups or
* aliases but it seems sensible to set it up anyway. If we get a match,
* set up the name in the response heap.
*
* On success, 0 is returned. Otherwise non-zero is returned. A non-zero
* return value should not be automatically interpreted as an error. The
* caller should attempt to resolve the SID through alternative means.
*/
static int
{
char *np;
return (1);
return (-1);
}
else
else
return (-1);
}
if (sid_name_use == SidTypeAlias &&
}
if (sid_name_use == SidTypeUnknown) {
return (1);
}
if (!mlsvc_string_save(
return (-1);
}
return (0);
}
/*
* lsarpc_s_UnknownSid
*
* This function handles unknown SIDs. By the time this is called we
* know that this is not a local SID and that the PDC has no idea to
* whom this sid refers. It may be a remnant from a time when the
* server was in another domain. All we can do is turn into the SID
* into a string and return it in place of a user name.
*
* On success, 0 is returned. Otherwise -1 is returned.
*/
static int
{
char domain_name[MLSVC_DOMAIN_NAME_MAX];
char *sidbuf;
return (-1);
return (-1);
(void) utf8_strupr(domain_name);
if (!mlsvc_string_save(
return (-1);
}
return (0);
}
/*
* 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
{
struct mslsa_domain_entry *dentry;
DWORD i;
/*
* These types don't need to reference an entry in the
* domain table. So return -1.
*/
return (0);
}
return (-1);
return (-1);
for (i = 0; i < n_entry; ++i) {
user_info->domain_sid)) {
*domain_idx = i;
return (0);
}
}
if (i == MLSVC_DOMAIN_MAX)
return (-1);
return (-1);
dentry[i].domain_sid =
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
{
struct lsar_name_entry2 *names;
struct mslsa_domain_table *domain_table;
struct mslsa_domain_entry *domain_entry;
int result;
int i;
return (MLRPC_DRC_OK);
}
domain_table->n_entry = 0;
for (i = 0; i < n_entry; ++i) {
if (nt_sid_is_local(sid)) {
(struct mslsa_name_entry *)&names[i]);
} else {
(struct mslsa_name_entry *)&names[i]);
if (result != 0)
if (result != 0) {
(struct mslsa_name_entry *)&names[i]);
}
}
if (result == -1) {
param->domain_table = 0;
param->mapped_count = 0;
return (MLRPC_DRC_OK);
}
if (result == -1) {
param->domain_table = 0;
param->mapped_count = 0;
return (MLRPC_DRC_OK);
}
}
return (MLRPC_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
{
struct lsar_rid_entry2 *rids;
smb_userinfo_t *user_info = 0;
struct mslsa_domain_table *domain_table;
struct mslsa_domain_entry *domain_entry;
char *name = "";
int rc = 0;
return (MLRPC_DRC_FAULT_PARAM_0_UNIMPLEMENTED);
if (rids == 0 || domain_table == 0 ||
domain_entry == 0 || user_info == 0) {
goto name_lookup2_failed;
}
if (rc < 0) {
goto name_lookup2_failed;
}
if (rc > 0) {
goto name_lookup2_failed;
}
}
/*
* Set up the rid table.
*/
rids[0].domain_index = 0;
/*
* Set up the domain table.
*/
goto name_lookup2_failed;
}
return (MLRPC_DRC_OK);
return (MLRPC_DRC_OK);
}