/*
Unix SMB / Netbios implementation .
Winbind daemon for ntdom nss module
Copyright ( C ) Tim Potter 2000 - 2001
Copyright ( C ) 2001 by Martin Pool < mbp @ samba . org >
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "winbindd.h"
# include "sids.h"
/**
* @ file winbindd_util . c
*
* Winbind daemon for NT domain authentication nss module .
* */
/**
* Used to clobber name fields that have an undefined value .
*
* Correct code should never look at a field that has this value .
* */
static const fstring name_deadbeef = " <deadbeef> " ;
/* Globals for domain list stuff */
struct winbindd_domain * domain_list = NULL ;
/* Given a domain name, return the struct winbindd domain info for it
if it is actually working . */
struct winbindd_domain * find_domain_from_name ( char * domain_name )
{
struct winbindd_domain * tmp ;
if ( domain_list = = NULL )
get_domain_info ( ) ;
/* Search through list */
for ( tmp = domain_list ; tmp ! = NULL ; tmp = tmp - > next ) {
if ( strcasecmp ( domain_name , tmp - > name ) = = 0 )
return tmp ;
}
/* Not found */
return NULL ;
}
/* Given a domain sid, return the struct winbindd domain info for it */
struct winbindd_domain * find_domain_from_sid ( DOM_SID * sid )
{
struct winbindd_domain * tmp ;
if ( domain_list = = NULL )
get_domain_info ( ) ;
/* Search through list */
for ( tmp = domain_list ; tmp ! = NULL ; tmp = tmp - > next ) {
if ( sid_compare_domain ( sid , & tmp - > sid ) = = 0 )
return tmp ;
}
/* Not found */
return NULL ;
}
/* Add a trusted domain to our list of domains */
static struct winbindd_domain * add_trusted_domain ( char * domain_name ,
DOM_SID * domain_sid ,
struct winbindd_methods * methods )
{
struct winbindd_domain * domain , * tmp ;
for ( tmp = domain_list ; tmp ! = NULL ; tmp = tmp - > next ) {
if ( strcmp ( domain_name , tmp - > name ) = = 0 ) {
DEBUG ( 3 , ( " domain %s already in domain list \n " , domain_name ) ) ;
return tmp ;
}
}
DEBUG ( 1 , ( " adding domain %s \n " , domain_name ) ) ;
/* Create new domain entry */
if ( ( domain = ( struct winbindd_domain * ) malloc ( sizeof ( * domain ) ) ) = = NULL )
return NULL ;
/* Fill in fields */
ZERO_STRUCTP ( domain ) ;
fstrcpy ( domain - > name , domain_name ) ;
sid_copy ( & domain - > sid , domain_sid ) ;
domain - > methods = methods ;
/* Link to domain list */
DLIST_ADD ( domain_list , domain ) ;
return domain ;
}
/* Look up global info for the winbind daemon */
BOOL get_domain_info ( void )
{
uint32 enum_ctx = 0 , num_doms = 0 ;
char * * domains = NULL ;
DOM_SID * sids = NULL , domain_sid ;
NTSTATUS result ;
CLI_POLICY_HND * hnd ;
int i ;
fstring level5_dom ;
BOOL rv = False ;
TALLOC_CTX * mem_ctx ;
extern struct winbindd_methods msrpc_methods ;
struct winbindd_methods * methods ;
switch ( lp_security ( ) ) {
# ifdef HAVE_ADS
case SEC_ADS :
{
extern struct winbindd_methods ads_methods ;
methods = & ads_methods ;
break ;
}
# endif
default :
methods = & msrpc_methods ;
}
DEBUG ( 1 , ( " getting trusted domain list \n " ) ) ;
if ( ! ( mem_ctx = talloc_init ( ) ) )
return False ;
/* Add our workgroup - keep handle to look up trusted domains */
if ( ! ( hnd = cm_get_lsa_handle ( lp_workgroup ( ) ) ) )
goto done ;
result = cli_lsa_query_info_policy ( hnd - > cli , mem_ctx ,
& hnd - > pol , 0x05 , level5_dom , & domain_sid ) ;
if ( ! NT_STATUS_IS_OK ( result ) )
goto done ;
add_trusted_domain ( lp_workgroup ( ) , & domain_sid , methods ) ;
/* Enumerate list of trusted domains */
if ( ! ( hnd = cm_get_lsa_handle ( lp_workgroup ( ) ) ) )
goto done ;
result = cli_lsa_enum_trust_dom ( hnd - > cli , mem_ctx ,
& hnd - > pol , & enum_ctx , & num_doms , & domains , & sids ) ;
if ( ! NT_STATUS_IS_OK ( result ) )
goto done ;
/* Add each domain to the trusted domain list */
for ( i = 0 ; i < num_doms ; i + + )
add_trusted_domain ( domains [ i ] , & sids [ i ] , methods ) ;
rv = True ;
done :
talloc_destroy ( mem_ctx ) ;
return rv ;
}
/* Connect to a domain controller using get_any_dc_name() to discover
the domain name and sid */
BOOL lookup_domain_sid ( char * domain_name , struct winbindd_domain * domain )
{
fstring level5_dom ;
uint32 enum_ctx = 0 , num_doms = 0 ;
char * * domains = NULL ;
DOM_SID * sids = NULL ;
CLI_POLICY_HND * hnd ;
NTSTATUS result ;
BOOL rv = False ;
TALLOC_CTX * mem_ctx ;
DEBUG ( 1 , ( " looking up sid for domain %s \n " , domain_name ) ) ;
if ( ! ( mem_ctx = talloc_init ( ) ) )
return False ;
if ( ! ( hnd = cm_get_lsa_handle ( domain_name ) ) )
goto done ;
/* Do a level 5 query info policy if we are looking up the SID for
our own domain . */
if ( strequal ( domain_name , lp_workgroup ( ) ) ) {
result = cli_lsa_query_info_policy ( hnd - > cli , mem_ctx ,
& hnd - > pol , 0x05 , level5_dom ,
& domain - > sid ) ;
rv = NT_STATUS_IS_OK ( result ) ;
goto done ;
}
/* Use lsaenumdomains to get sid for this domain */
result = cli_lsa_enum_trust_dom ( hnd - > cli , mem_ctx , & hnd - > pol ,
& enum_ctx , & num_doms , & domains , & sids ) ;
/* Look for domain name */
if ( NT_STATUS_IS_OK ( result ) & & domains & & sids ) {
BOOL found = False ;
int i ;
for ( i = 0 ; i < num_doms ; i + + ) {
if ( strequal ( domain_name , domains [ i ] ) ) {
sid_copy ( & domain - > sid , & sids [ i ] ) ;
found = True ;
break ;
}
}
rv = found ;
goto done ;
}
rv = False ; /* An error occured with a trusted domain */
done :
talloc_destroy ( mem_ctx ) ;
return rv ;
}
/* Store a SID in a domain indexed by name in the cache. */
static void store_sid_by_name_in_cache ( struct winbindd_domain * domain ,
const char * name ,
DOM_SID * sid , enum SID_NAME_USE type )
{
struct winbindd_sid sid_val ;
sid_to_string ( sid_val . sid , sid ) ;
sid_val . type = ( int ) type ;
DEBUG ( 10 , ( " store_sid_by_name_in_cache: storing cache entry %s -> SID %s \n " ,
name , sid_val . sid ) ) ;
winbindd_store_sid_cache_entry ( domain , name , & sid_val ) ;
}
/* Lookup a SID in a domain indexed by name in the cache. */
static BOOL winbindd_lookup_sid_by_name_in_cache ( struct winbindd_domain * domain ,
const char * name ,
DOM_SID * sid , enum SID_NAME_USE * type )
{
struct winbindd_sid sid_ret ;
if ( ! winbindd_fetch_sid_cache_entry ( domain , name , & sid_ret ) )
return False ;
string_to_sid ( sid , sid_ret . sid ) ;
* type = ( enum SID_NAME_USE ) sid_ret . type ;
DEBUG ( 10 , ( " winbindd_lookup_sid_by_name_in_cache: Cache hit for name %s. SID = %s \n " ,
name , sid_ret . sid ) ) ;
return True ;
}
/* Store a name in a domain indexed by SID in the cache. */
static void store_name_by_sid_in_cache ( struct winbindd_domain * domain ,
DOM_SID * sid ,
const char * name , enum SID_NAME_USE type )
{
fstring sid_str ;
uint32 rid ;
DOM_SID domain_sid ;
struct winbindd_name name_val ;
/* Split sid into domain sid and user rid */
sid_copy ( & domain_sid , sid ) ;
sid_split_rid ( & domain_sid , & rid ) ;
sid_to_string ( sid_str , sid ) ;
fstrcpy ( name_val . name , name ) ;
name_val . type = ( int ) type ;
DEBUG ( 10 , ( " store_name_by_sid_in_cache: storing cache entry SID %s -> %s \n " ,
sid_str , name_val . name ) ) ;
winbindd_store_name_cache_entry ( domain , sid_str , & name_val ) ;
}
/* Lookup a name in a domain indexed by SID in the cache. */
static BOOL winbindd_lookup_name_by_sid_in_cache ( DOM_SID * sid , fstring name , enum SID_NAME_USE * type )
{
fstring sid_str ;
uint32 rid ;
DOM_SID domain_sid ;
struct winbindd_name name_ret ;
struct winbindd_domain * domain ;
/* Split sid into domain sid and user rid */
sid_copy ( & domain_sid , sid ) ;
sid_split_rid ( & domain_sid , & rid ) ;
if ( ( domain = find_domain_from_sid ( & domain_sid ) ) = = NULL )
return False ;
sid_to_string ( sid_str , sid ) ;
if ( ! winbindd_fetch_name_cache_entry ( domain , sid_str , & name_ret ) )
return False ;
fstrcpy ( name , name_ret . name ) ;
* type = ( enum SID_NAME_USE ) name_ret . type ;
DEBUG ( 10 , ( " winbindd_lookup_name_by_sid_in_cache: Cache hit for SID = %s, name %s \n " ,
sid_str , name ) ) ;
return True ;
}
/* Lookup a sid in a domain from a name */
BOOL winbindd_lookup_sid_by_name ( struct winbindd_domain * domain ,
const char * name , DOM_SID * sid , enum SID_NAME_USE * type )
{
NTSTATUS result ;
/* Don't bother with machine accounts */
if ( name [ strlen ( name ) - 1 ] = = ' $ ' )
return False ;
/* First check cache. */
if ( winbindd_lookup_sid_by_name_in_cache ( domain , name , sid , type ) ) {
if ( * type = = SID_NAME_USE_NONE )
return False ; /* Negative cache hit. */
return True ;
}
/* Lookup name */
result = domain - > methods - > name_to_sid ( domain , name , sid , type ) ;
/* Return rid and type if lookup successful */
if ( NT_STATUS_IS_OK ( result ) ) {
store_sid_by_name_in_cache ( domain , name , sid , * type ) ;
store_name_by_sid_in_cache ( domain , sid , name , * type ) ;
} else {
/* JRA. Here's where we add the -ve cache store with a
name type of SID_NAME_USE_NONE . */
DOM_SID nullsid ;
ZERO_STRUCT ( nullsid ) ;
store_sid_by_name_in_cache ( domain , name , & nullsid , SID_NAME_USE_NONE ) ;
* type = SID_NAME_UNKNOWN ;
}
return NT_STATUS_IS_OK ( result ) ;
}
/**
* @ brief Lookup a name in a domain from a sid .
*
* @ param sid Security ID you want to look up .
*
* @ param name On success , set to the name corresponding to @ p sid .
*
* @ param type On success , contains the type of name : alias , group or
* user .
*
* @ retval True if the name exists , in which case @ p name and @ p type
* are set , otherwise False .
* */
BOOL winbindd_lookup_name_by_sid ( DOM_SID * sid ,
fstring name ,
enum SID_NAME_USE * type )
{
char * names ;
NTSTATUS result ;
TALLOC_CTX * mem_ctx ;
BOOL rv = False ;
struct winbindd_domain * domain ;
/* First check cache. */
if ( winbindd_lookup_name_by_sid_in_cache ( sid , name , type ) ) {
if ( * type = = SID_NAME_USE_NONE ) {
fstrcpy ( name , name_deadbeef ) ;
* type = SID_NAME_UNKNOWN ;
return False ; /* Negative cache hit. */
} else
return True ;
}
domain = find_domain_from_sid ( sid ) ;
if ( ! domain ) {
DEBUG ( 1 , ( " Can't find domain from sid \n " ) ) ;
return False ;
}
/* Lookup name */
if ( ! ( mem_ctx = talloc_init ( ) ) )
return False ;
result = domain - > methods - > sid_to_name ( domain , mem_ctx , sid , & names , type ) ;
/* Return name and type if successful */
if ( ( rv = NT_STATUS_IS_OK ( result ) ) ) {
fstrcpy ( name , names ) ;
store_sid_by_name_in_cache ( domain , names , sid , * type ) ;
store_name_by_sid_in_cache ( domain , sid , names , * type ) ;
} else {
/* OK, so we tried to look up a name in this sid, and
* didn ' t find it . Therefore add a negative cache
* entry . */
store_name_by_sid_in_cache ( domain , sid , " " , SID_NAME_USE_NONE ) ;
* type = SID_NAME_UNKNOWN ;
fstrcpy ( name , name_deadbeef ) ;
}
talloc_destroy ( mem_ctx ) ;
return rv ;
}
/* Free state information held for {set,get,end}{pw,gr}ent() functions */
void free_getent_state ( struct getent_state * state )
{
struct getent_state * temp ;
/* Iterate over state list */
temp = state ;
while ( temp ! = NULL ) {
struct getent_state * next ;
/* Free sam entries then list entry */
SAFE_FREE ( state - > sam_entries ) ;
DLIST_REMOVE ( state , state ) ;
next = temp - > next ;
SAFE_FREE ( temp ) ;
temp = next ;
}
}
/* Initialise trusted domain info */
BOOL winbindd_param_init ( void )
{
/* Parse winbind uid and winbind_gid parameters */
if ( ! lp_winbind_uid ( & server_state . uid_low , & server_state . uid_high ) ) {
DEBUG ( 0 , ( " winbind uid range missing or invalid \n " ) ) ;
return False ;
}
if ( ! lp_winbind_gid ( & server_state . gid_low , & server_state . gid_high ) ) {
DEBUG ( 0 , ( " winbind gid range missing or invalid \n " ) ) ;
return False ;
}
return True ;
}
/* Check if a domain is present in a comma-separated list of domains */
BOOL check_domain_env ( char * domain_env , char * domain )
{
fstring name ;
char * tmp = domain_env ;
while ( next_token ( & tmp , name , " , " , sizeof ( fstring ) ) ) {
if ( strequal ( name , domain ) )
return True ;
}
return False ;
}
/* Parse a string of the form DOMAIN/user into a domain and a user */
BOOL parse_domain_user ( const char * domuser , fstring domain , fstring user )
{
char * p = strchr ( domuser , * lp_winbind_separator ( ) ) ;
if ( ! p )
return False ;
fstrcpy ( user , p + 1 ) ;
fstrcpy ( domain , domuser ) ;
domain [ PTR_DIFF ( p , domuser ) ] = 0 ;
strupper ( domain ) ;
return True ;
}