2003-08-28 00:52:56 +04:00
/*
Unix SMB / CIFS implementation .
uid / user handling
Copyright ( C ) Andrew Tridgell 1992 - 1998
Copyright ( C ) Gerald ( Jerry ) Carter 2003
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 "includes.h"
/*****************************************************************
* THE CANONICAL * convert name to SID function .
Tries local lookup first - for local domains - then uses winbind .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
BOOL lookup_name ( const char * domain , const char * name , DOM_SID * psid , enum SID_NAME_USE * name_type )
{
fstring sid ;
BOOL local_lookup = False ;
* name_type = SID_NAME_UNKNOWN ;
/* If we are looking up a domain user, make sure it is
for the local machine only */
2004-03-07 11:22:06 +03:00
if ( strequal ( domain , get_global_sam_name ( ) ) ) {
2003-08-28 00:52:56 +04:00
if ( local_lookup_name ( name , psid , name_type ) ) {
DEBUG ( 10 ,
( " lookup_name: (local) [%s] \\ [%s] -> SID %s (type %s: %u) \n " ,
domain , name , sid_to_string ( sid , psid ) ,
sid_type_lookup ( * name_type ) , ( unsigned int ) * name_type ) ) ;
return True ;
}
} else {
/* Remote */
if ( winbind_lookup_name ( domain , name , psid , name_type ) ) {
DEBUG ( 10 , ( " lookup_name (winbindd): [%s] \\ [%s] -> SID %s (type %u) \n " ,
domain , name , sid_to_string ( sid , psid ) ,
( unsigned int ) * name_type ) ) ;
return True ;
}
}
DEBUG ( 10 , ( " lookup_name: %s lookup for [%s] \\ [%s] failed \n " ,
local_lookup ? " local " : " winbind " , domain , name ) ) ;
return False ;
}
/*****************************************************************
* THE CANONICAL * convert SID to name function .
Tries local lookup first - for local sids , then tries winbind .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-03-31 19:21:48 +04:00
BOOL lookup_sid ( const DOM_SID * sid , fstring dom_name , fstring name , enum SID_NAME_USE * name_type )
2003-08-28 00:52:56 +04:00
{
if ( ! name_type )
return False ;
* name_type = SID_NAME_UNKNOWN ;
/* Check if this is our own sid. This should perhaps be done by
winbind ? For the moment handle it here . */
2004-11-19 16:26:17 +03:00
if ( sid - > num_auths = = 4 & & sid_equal ( get_global_sam_sid ( ) , sid ) ) {
DOM_SID tmp_sid ;
sid_copy ( & tmp_sid , sid ) ;
return map_domain_sid_to_name ( & tmp_sid , dom_name ) & &
local_lookup_sid ( sid , name , name_type ) ;
}
2003-08-28 00:52:56 +04:00
if ( sid - > num_auths = = 5 ) {
DOM_SID tmp_sid ;
uint32 rid ;
sid_copy ( & tmp_sid , sid ) ;
sid_split_rid ( & tmp_sid , & rid ) ;
if ( sid_equal ( get_global_sam_sid ( ) , & tmp_sid ) ) {
return map_domain_sid_to_name ( & tmp_sid , dom_name ) & &
local_lookup_sid ( sid , name , name_type ) ;
}
}
if ( ! winbind_lookup_sid ( sid , dom_name , name , name_type ) ) {
fstring sid_str ;
DOM_SID tmp_sid ;
uint32 rid ;
DEBUG ( 10 , ( " lookup_sid: winbind lookup for SID %s failed - trying local. \n " , sid_to_string ( sid_str , sid ) ) ) ;
sid_copy ( & tmp_sid , sid ) ;
sid_split_rid ( & tmp_sid , & rid ) ;
return map_domain_sid_to_name ( & tmp_sid , dom_name ) & &
lookup_known_rid ( & tmp_sid , rid , name , name_type ) ;
}
return True ;
}
/*****************************************************************
Id mapping cache . This is to avoid Winbind mappings already
seen by smbd to be queried too frequently , keeping winbindd
busy , and blocking smbd while winbindd is busy with other
stuff . Written by Michael Steffens < michael . steffens @ hp . com > ,
modified to use linked lists by jra .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define MAX_UID_SID_CACHE_SIZE 100
# define TURNOVER_UID_SID_CACHE_SIZE 10
# define MAX_GID_SID_CACHE_SIZE 100
# define TURNOVER_GID_SID_CACHE_SIZE 10
static size_t n_uid_sid_cache = 0 ;
static size_t n_gid_sid_cache = 0 ;
static struct uid_sid_cache {
struct uid_sid_cache * next , * prev ;
uid_t uid ;
DOM_SID sid ;
enum SID_NAME_USE sidtype ;
} * uid_sid_cache_head ;
static struct gid_sid_cache {
struct gid_sid_cache * next , * prev ;
gid_t gid ;
DOM_SID sid ;
enum SID_NAME_USE sidtype ;
} * gid_sid_cache_head ;
/*****************************************************************
Find a SID given a uid .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL fetch_sid_from_uid_cache ( DOM_SID * psid , uid_t uid )
{
struct uid_sid_cache * pc ;
for ( pc = uid_sid_cache_head ; pc ; pc = pc - > next ) {
if ( pc - > uid = = uid ) {
fstring sid ;
* psid = pc - > sid ;
DEBUG ( 3 , ( " fetch sid from uid cache %u -> %s \n " ,
( unsigned int ) uid , sid_to_string ( sid , psid ) ) ) ;
DLIST_PROMOTE ( uid_sid_cache_head , pc ) ;
return True ;
}
}
return False ;
}
/*****************************************************************
Find a uid given a SID .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL fetch_uid_from_cache ( uid_t * puid , const DOM_SID * psid )
{
struct uid_sid_cache * pc ;
for ( pc = uid_sid_cache_head ; pc ; pc = pc - > next ) {
if ( sid_compare ( & pc - > sid , psid ) = = 0 ) {
fstring sid ;
* puid = pc - > uid ;
DEBUG ( 3 , ( " fetch uid from cache %u -> %s \n " ,
( unsigned int ) * puid , sid_to_string ( sid , psid ) ) ) ;
DLIST_PROMOTE ( uid_sid_cache_head , pc ) ;
return True ;
}
}
return False ;
}
/*****************************************************************
Store uid to SID mapping in cache .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void store_uid_sid_cache ( const DOM_SID * psid , uid_t uid )
{
struct uid_sid_cache * pc ;
if ( n_uid_sid_cache > = MAX_UID_SID_CACHE_SIZE & & n_uid_sid_cache > TURNOVER_UID_SID_CACHE_SIZE ) {
/* Delete the last TURNOVER_UID_SID_CACHE_SIZE entries. */
struct uid_sid_cache * pc_next ;
size_t i ;
for ( i = 0 , pc = uid_sid_cache_head ; i < ( n_uid_sid_cache - TURNOVER_UID_SID_CACHE_SIZE ) ; i + + , pc = pc - > next )
;
for ( ; pc ; pc = pc_next ) {
pc_next = pc - > next ;
DLIST_REMOVE ( uid_sid_cache_head , pc ) ;
SAFE_FREE ( pc ) ;
n_uid_sid_cache - - ;
}
}
pc = ( struct uid_sid_cache * ) malloc ( sizeof ( struct uid_sid_cache ) ) ;
if ( ! pc )
return ;
pc - > uid = uid ;
sid_copy ( & pc - > sid , psid ) ;
DLIST_ADD ( uid_sid_cache_head , pc ) ;
n_uid_sid_cache + + ;
}
/*****************************************************************
Find a SID given a gid .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL fetch_sid_from_gid_cache ( DOM_SID * psid , gid_t gid )
{
struct gid_sid_cache * pc ;
for ( pc = gid_sid_cache_head ; pc ; pc = pc - > next ) {
if ( pc - > gid = = gid ) {
fstring sid ;
* psid = pc - > sid ;
DEBUG ( 3 , ( " fetch sid from gid cache %u -> %s \n " ,
( unsigned int ) gid , sid_to_string ( sid , psid ) ) ) ;
DLIST_PROMOTE ( gid_sid_cache_head , pc ) ;
return True ;
}
}
return False ;
}
/*****************************************************************
Find a gid given a SID .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL fetch_gid_from_cache ( gid_t * pgid , const DOM_SID * psid )
{
struct gid_sid_cache * pc ;
for ( pc = gid_sid_cache_head ; pc ; pc = pc - > next ) {
if ( sid_compare ( & pc - > sid , psid ) = = 0 ) {
fstring sid ;
* pgid = pc - > gid ;
DEBUG ( 3 , ( " fetch uid from cache %u -> %s \n " ,
( unsigned int ) * pgid , sid_to_string ( sid , psid ) ) ) ;
DLIST_PROMOTE ( gid_sid_cache_head , pc ) ;
return True ;
}
}
return False ;
}
/*****************************************************************
Store gid to SID mapping in cache .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void store_gid_sid_cache ( const DOM_SID * psid , gid_t gid )
{
struct gid_sid_cache * pc ;
if ( n_gid_sid_cache > = MAX_GID_SID_CACHE_SIZE & & n_gid_sid_cache > TURNOVER_GID_SID_CACHE_SIZE ) {
/* Delete the last TURNOVER_GID_SID_CACHE_SIZE entries. */
struct gid_sid_cache * pc_next ;
size_t i ;
for ( i = 0 , pc = gid_sid_cache_head ; i < ( n_gid_sid_cache - TURNOVER_GID_SID_CACHE_SIZE ) ; i + + , pc = pc - > next )
;
for ( ; pc ; pc = pc_next ) {
pc_next = pc - > next ;
DLIST_REMOVE ( gid_sid_cache_head , pc ) ;
SAFE_FREE ( pc ) ;
n_gid_sid_cache - - ;
}
}
pc = ( struct gid_sid_cache * ) malloc ( sizeof ( struct gid_sid_cache ) ) ;
if ( ! pc )
return ;
pc - > gid = gid ;
sid_copy ( & pc - > sid , psid ) ;
DLIST_ADD ( gid_sid_cache_head , pc ) ;
n_gid_sid_cache + + ;
}
/*****************************************************************
* THE CANONICAL * convert uid_t to SID function .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
NTSTATUS uid_to_sid ( DOM_SID * psid , uid_t uid )
{
fstring sid ;
2003-11-07 17:39:47 +03:00
uid_t low , high ;
2003-08-28 00:52:56 +04:00
ZERO_STRUCTP ( psid ) ;
if ( fetch_sid_from_uid_cache ( psid , uid ) )
return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL ) ;
2003-11-07 17:39:47 +03:00
/* DC's never use winbindd to resolve users outside the
defined idmap range */
if ( lp_server_role ( ) = = ROLE_DOMAIN_MEMBER
| | ( lp_idmap_uid ( & low , & high ) & & uid > = low & & uid < = high ) )
{
2003-08-28 00:52:56 +04:00
if ( winbind_uid_to_sid ( psid , uid ) ) {
DEBUG ( 10 , ( " uid_to_sid: winbindd %u -> %s \n " ,
( unsigned int ) uid , sid_to_string ( sid , psid ) ) ) ;
if ( psid )
store_uid_sid_cache ( psid , uid ) ;
return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL ) ;
}
}
if ( ! local_uid_to_sid ( psid , uid ) ) {
DEBUG ( 10 , ( " uid_to_sid: local %u failed to map to sid \n " , ( unsigned int ) uid ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
DEBUG ( 10 , ( " uid_to_sid: local %u -> %s \n " , ( unsigned int ) uid , sid_to_string ( sid , psid ) ) ) ;
store_uid_sid_cache ( psid , uid ) ;
return NT_STATUS_OK ;
}
/*****************************************************************
* THE CANONICAL * convert gid_t to SID function .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
NTSTATUS gid_to_sid ( DOM_SID * psid , gid_t gid )
{
fstring sid ;
2003-11-07 17:39:47 +03:00
gid_t low , high ;
2003-08-28 00:52:56 +04:00
ZERO_STRUCTP ( psid ) ;
if ( fetch_sid_from_gid_cache ( psid , gid ) )
return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL ) ;
2003-11-07 17:39:47 +03:00
/* DC's never use winbindd to resolve groups outside the
defined idmap range */
if ( lp_server_role ( ) = = ROLE_DOMAIN_MEMBER
| | ( lp_idmap_gid ( & low , & high ) & & gid > = low & & gid < = high ) )
{
2003-08-28 00:52:56 +04:00
if ( winbind_gid_to_sid ( psid , gid ) ) {
DEBUG ( 10 , ( " gid_to_sid: winbindd %u -> %s \n " ,
( unsigned int ) gid , sid_to_string ( sid , psid ) ) ) ;
if ( psid )
store_gid_sid_cache ( psid , gid ) ;
return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL ) ;
}
}
if ( ! local_gid_to_sid ( psid , gid ) ) {
DEBUG ( 10 , ( " gid_to_sid: local %u failed to map to sid \n " , ( unsigned int ) gid ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
DEBUG ( 10 , ( " gid_to_sid: local %u -> %s \n " , ( unsigned int ) gid , sid_to_string ( sid , psid ) ) ) ;
store_gid_sid_cache ( psid , gid ) ;
return NT_STATUS_OK ;
}
/*****************************************************************
* THE CANONICAL * convert SID to uid function .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
NTSTATUS sid_to_uid ( const DOM_SID * psid , uid_t * puid )
{
fstring dom_name , name , sid_str ;
enum SID_NAME_USE name_type ;
if ( fetch_uid_from_cache ( puid , psid ) )
return NT_STATUS_OK ;
/* if this is our SID then go straight to a local lookup */
if ( sid_compare_domain ( get_global_sam_sid ( ) , psid ) = = 0 ) {
DEBUG ( 10 , ( " sid_to_uid: my domain (%s) - trying local. \n " ,
sid_string_static ( psid ) ) ) ;
if ( local_sid_to_uid ( puid , psid , & name_type ) )
goto success ;
DEBUG ( 10 , ( " sid_to_uid: local lookup failed \n " ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
/* If it is not our local domain, only hope is winbindd */
if ( ! winbind_lookup_sid ( psid , dom_name , name , & name_type ) ) {
DEBUG ( 10 , ( " sid_to_uid: winbind lookup for non-local sid %s failed \n " ,
sid_string_static ( psid ) ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
/* If winbindd does know the SID, ensure this is a user */
if ( name_type ! = SID_NAME_USER ) {
DEBUG ( 10 , ( " sid_to_uid: winbind lookup succeeded but SID is not a user (%u) \n " ,
( unsigned int ) name_type ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
/* get the uid. Has to work or else we are dead in the water */
if ( ! winbind_sid_to_uid ( puid , psid ) ) {
DEBUG ( 10 , ( " sid_to_uid: winbind failed to allocate a new uid for sid %s \n " ,
sid_to_string ( sid_str , psid ) ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
success :
DEBUG ( 10 , ( " sid_to_uid: %s -> %u \n " , sid_to_string ( sid_str , psid ) ,
( unsigned int ) * puid ) ) ;
store_uid_sid_cache ( psid , * puid ) ;
return NT_STATUS_OK ;
}
/*****************************************************************
* THE CANONICAL * convert SID to gid function .
Group mapping is used for gids that maps to Wellknown SIDs
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
NTSTATUS sid_to_gid ( const DOM_SID * psid , gid_t * pgid )
{
fstring dom_name , name , sid_str ;
enum SID_NAME_USE name_type ;
if ( fetch_gid_from_cache ( pgid , psid ) )
return NT_STATUS_OK ;
/*
* First we must look up the name and decide if this is a group sid .
* Group mapping can deal with foreign SIDs
*/
2004-05-28 12:57:00 +04:00
if ( local_sid_to_gid ( pgid , psid , & name_type ) )
goto success ;
2003-08-28 00:52:56 +04:00
if ( ! winbind_lookup_sid ( psid , dom_name , name , & name_type ) ) {
2004-05-28 12:57:00 +04:00
DEBUG ( 10 , ( " sid_to_gid: no one knows the SID %s (tried local, then winbind) \n " , sid_to_string ( sid_str , psid ) ) ) ;
2003-08-28 00:52:56 +04:00
return NT_STATUS_UNSUCCESSFUL ;
}
/* winbindd knows it; Ensure this is a group sid */
2003-11-07 17:39:47 +03:00
if ( ( name_type ! = SID_NAME_DOM_GRP ) & & ( name_type ! = SID_NAME_ALIAS )
& & ( name_type ! = SID_NAME_WKN_GRP ) )
{
2003-08-28 00:52:56 +04:00
DEBUG ( 10 , ( " sid_to_gid: winbind lookup succeeded but SID is not a known group (%u) \n " ,
( unsigned int ) name_type ) ) ;
/* winbindd is running and knows about this SID. Just the wrong type.
Don ' t fallback to a local lookup here */
return NT_STATUS_INVALID_PARAMETER ;
}
/* winbindd knows it and it is a type of group; sid_to_gid must succeed
or we are dead in the water */
if ( ! winbind_sid_to_gid ( pgid , psid ) ) {
2004-11-09 02:34:28 +03:00
DEBUG ( 10 , ( " sid_to_gid: winbind failed to allocate a new gid for sid %s \n " ,
2003-08-28 00:52:56 +04:00
sid_to_string ( sid_str , psid ) ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
success :
DEBUG ( 10 , ( " sid_to_gid: %s -> %u \n " , sid_to_string ( sid_str , psid ) ,
( unsigned int ) * pgid ) ) ;
store_gid_sid_cache ( psid , * pgid ) ;
return NT_STATUS_OK ;
}