2000-06-23 09:49:11 +04:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
2000-06-23 09:49:11 +04:00
uid / user handling
Copyright ( C ) Tim Potter 2000
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"
extern struct current_user current_user ;
struct sec_ctx {
uid_t uid ;
uid_t gid ;
int ngroups ;
gid_t * groups ;
2000-08-04 02:38:43 +04:00
NT_USER_TOKEN * token ;
2000-06-23 09:49:11 +04:00
} ;
/* A stack of security contexts. We include the current context as being
the first one , so there is room for another MAX_SEC_CTX_DEPTH more . */
static struct sec_ctx sec_ctx_stack [ MAX_SEC_CTX_DEPTH + 1 ] ;
static int sec_ctx_stack_ndx ;
2000-08-04 02:38:43 +04:00
/****************************************************************************
Become the specified uid .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-06-23 09:49:11 +04:00
static BOOL become_uid ( uid_t uid )
{
/* Check for dodgy uid values */
if ( uid = = ( uid_t ) - 1 | |
( ( sizeof ( uid_t ) = = 2 ) & & ( uid = = ( uid_t ) 65535 ) ) ) {
static int done ;
if ( ! done ) {
DEBUG ( 1 , ( " WARNING: using uid %d is a security risk \n " ,
( int ) uid ) ) ;
done = 1 ;
}
}
/* Set effective user id */
set_effective_uid ( uid ) ;
2000-10-28 23:38:39 +04:00
DO_PROFILE_INC ( uid_changes ) ;
2000-06-23 09:49:11 +04:00
return True ;
}
2000-08-04 02:38:43 +04:00
/****************************************************************************
Become the specified gid .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-06-23 09:49:11 +04:00
static BOOL become_gid ( gid_t gid )
{
/* Check for dodgy gid values */
if ( gid = = ( gid_t ) - 1 | | ( ( sizeof ( gid_t ) = = 2 ) & &
( gid = = ( gid_t ) 65535 ) ) ) {
static int done ;
if ( ! done ) {
DEBUG ( 1 , ( " WARNING: using gid %d is a security risk \n " ,
( int ) gid ) ) ;
done = 1 ;
}
}
/* Set effective group id */
set_effective_gid ( gid ) ;
return True ;
}
2000-08-04 02:38:43 +04:00
/****************************************************************************
Become the specified uid and gid .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-06-23 09:49:11 +04:00
static BOOL become_id ( uid_t uid , gid_t gid )
{
return become_gid ( gid ) & & become_uid ( uid ) ;
}
2000-08-04 02:38:43 +04:00
/****************************************************************************
Drop back to root privileges in order to change to another user .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-06-23 09:49:11 +04:00
static void gain_root ( void )
{
2001-06-26 07:45:45 +04:00
if ( non_root_mode ( ) ) {
return ;
}
2000-06-23 09:49:11 +04:00
if ( geteuid ( ) ! = 0 ) {
set_effective_uid ( 0 ) ;
if ( geteuid ( ) ! = 0 ) {
DEBUG ( 0 ,
( " Warning: You appear to have a trapdoor "
" uid system \n " ) ) ;
}
}
if ( getegid ( ) ! = 0 ) {
set_effective_gid ( 0 ) ;
if ( getegid ( ) ! = 0 ) {
DEBUG ( 0 ,
( " Warning: You appear to have a trapdoor "
" gid system \n " ) ) ;
}
}
}
2000-08-04 02:38:43 +04:00
/****************************************************************************
Get the list of current groups .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-06-23 09:49:11 +04:00
2002-07-15 14:35:28 +04:00
int get_current_groups ( gid_t gid , int * p_ngroups , gid_t * * p_groups )
2000-06-23 09:49:11 +04:00
{
2000-08-02 06:11:55 +04:00
int i ;
gid_t grp ;
2002-07-15 14:35:28 +04:00
int ngroups ;
gid_t * groups = NULL ;
2000-08-02 06:11:55 +04:00
( * p_ngroups ) = 0 ;
( * p_groups ) = NULL ;
2002-07-15 14:35:28 +04:00
/* this looks a little strange, but is needed to cope with
systems that put the current egid in the group list
returned from getgroups ( ) ( tridge ) */
save_re_gid ( ) ;
set_effective_gid ( gid ) ;
setgid ( gid ) ;
2000-08-02 06:11:55 +04:00
2002-07-15 14:35:28 +04:00
ngroups = sys_getgroups ( 0 , & grp ) ;
if ( ngroups < = 0 ) {
goto fail ;
}
if ( ( groups = ( gid_t * ) malloc ( sizeof ( gid_t ) * ( ngroups + 1 ) ) ) = = NULL ) {
2000-08-02 06:11:55 +04:00
DEBUG ( 0 , ( " setup_groups malloc fail ! \n " ) ) ;
2002-07-15 14:35:28 +04:00
goto fail ;
2000-08-02 06:11:55 +04:00
}
2001-04-28 04:05:11 +04:00
if ( ( ngroups = sys_getgroups ( ngroups , groups ) ) = = - 1 ) {
2002-07-15 14:35:28 +04:00
goto fail ;
2001-04-28 04:05:11 +04:00
}
2000-08-02 06:11:55 +04:00
2002-07-15 14:35:28 +04:00
restore_re_gid ( ) ;
2000-08-02 06:11:55 +04:00
( * p_ngroups ) = ngroups ;
( * p_groups ) = groups ;
2000-06-23 09:49:11 +04:00
2001-10-29 06:46:09 +03:00
DEBUG ( 3 , ( " get_current_groups: user is in %u groups: " , ngroups ) ) ;
2000-08-02 06:11:55 +04:00
for ( i = 0 ; i < ngroups ; i + + ) {
DEBUG ( 3 , ( " %s%d " , ( i ? " , " : " " ) , ( int ) groups [ i ] ) ) ;
2000-06-23 09:49:11 +04:00
}
2000-08-02 06:11:55 +04:00
DEBUG ( 3 , ( " \n " ) ) ;
2000-06-23 09:49:11 +04:00
2002-07-15 14:35:28 +04:00
return ngroups ;
fail :
SAFE_FREE ( groups ) ;
restore_re_gid ( ) ;
return - 1 ;
2000-06-23 09:49:11 +04:00
}
2000-08-28 10:50:45 +04:00
/****************************************************************************
Initialize the groups a user belongs to .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
BOOL initialise_groups ( char * user , uid_t uid , gid_t gid )
{
struct sec_ctx * prev_ctx_p ;
BOOL result = True ;
2001-09-19 07:30:20 +04:00
if ( non_root_mode ( ) ) {
return True ;
}
2000-08-28 10:50:45 +04:00
become_root ( ) ;
/* Call initgroups() to get user groups */
2002-01-27 15:12:22 +03:00
if ( initgroups ( user , gid ) = = - 1 ) {
2000-08-28 10:50:45 +04:00
DEBUG ( 0 , ( " Unable to initgroups. Error was %s \n " , strerror ( errno ) ) ) ;
if ( getuid ( ) = = 0 ) {
if ( gid < 0 | | gid > 32767 | | uid < 0 | | uid > 32767 ) {
DEBUG ( 0 , ( " This is probably a problem with the account %s \n " , user ) ) ;
}
}
result = False ;
goto done ;
}
/* Store groups in previous user's security context. This will
always work as the become_root ( ) call increments the stack
pointer . */
prev_ctx_p = & sec_ctx_stack [ sec_ctx_stack_ndx - 1 ] ;
2001-09-17 15:25:41 +04:00
SAFE_FREE ( prev_ctx_p - > groups ) ;
2000-08-28 10:50:45 +04:00
prev_ctx_p - > ngroups = 0 ;
2002-07-15 14:35:28 +04:00
get_current_groups ( gid , & prev_ctx_p - > ngroups , & prev_ctx_p - > groups ) ;
2000-08-28 10:50:45 +04:00
done :
unbecome_root ( ) ;
return result ;
}
2000-08-04 02:38:43 +04:00
/****************************************************************************
Create a new security context on the stack . It is the same as the old
one . User changes are done using the set_sec_ctx ( ) function .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-06-23 09:49:11 +04:00
BOOL push_sec_ctx ( void )
{
2000-06-23 23:57:42 +04:00
struct sec_ctx * ctx_p ;
2000-06-23 09:49:11 +04:00
/* Check we don't overflow our stack */
2000-08-09 22:40:48 +04:00
if ( sec_ctx_stack_ndx = = MAX_SEC_CTX_DEPTH ) {
2000-06-23 09:49:11 +04:00
DEBUG ( 0 , ( " Security context stack overflow! \n " ) ) ;
2000-08-09 22:40:48 +04:00
smb_panic ( " Security context stack overflow! \n " ) ;
2000-06-23 09:49:11 +04:00
}
/* Store previous user context */
sec_ctx_stack_ndx + + ;
2000-06-23 23:57:42 +04:00
ctx_p = & sec_ctx_stack [ sec_ctx_stack_ndx ] ;
2000-06-23 09:49:11 +04:00
2000-06-23 23:57:42 +04:00
ctx_p - > uid = geteuid ( ) ;
ctx_p - > gid = getegid ( ) ;
2000-06-23 09:49:11 +04:00
2001-04-28 01:14:18 +04:00
DEBUG ( 3 , ( " push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d \n " ,
( unsigned int ) ctx_p - > uid , ( unsigned int ) ctx_p - > gid , sec_ctx_stack_ndx ) ) ;
2000-10-05 07:28:58 +04:00
2000-08-04 02:38:43 +04:00
ctx_p - > token = dup_nt_token ( sec_ctx_stack [ sec_ctx_stack_ndx - 1 ] . token ) ;
2000-06-23 23:57:42 +04:00
ctx_p - > ngroups = sys_getgroups ( 0 , NULL ) ;
if ( ctx_p - > ngroups ! = 0 ) {
if ( ! ( ctx_p - > groups = malloc ( ctx_p - > ngroups * sizeof ( gid_t ) ) ) ) {
DEBUG ( 0 , ( " Out of memory in push_sec_ctx() \n " ) ) ;
2000-08-04 02:38:43 +04:00
delete_nt_token ( & ctx_p - > token ) ;
2000-06-23 23:57:42 +04:00
return False ;
}
2000-06-23 09:49:11 +04:00
2000-06-23 23:57:42 +04:00
sys_getgroups ( ctx_p - > ngroups , ctx_p - > groups ) ;
} else {
ctx_p - > groups = NULL ;
}
2000-06-23 09:49:11 +04:00
return True ;
}
2000-08-04 02:38:43 +04:00
/****************************************************************************
Set the current security context to a given user .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-06-23 09:49:11 +04:00
2000-08-04 02:38:43 +04:00
void set_sec_ctx ( uid_t uid , gid_t gid , int ngroups , gid_t * groups , NT_USER_TOKEN * token )
2000-06-23 09:49:11 +04:00
{
2000-06-23 23:57:42 +04:00
struct sec_ctx * ctx_p = & sec_ctx_stack [ sec_ctx_stack_ndx ] ;
2001-01-04 22:27:08 +03:00
2000-06-23 09:49:11 +04:00
/* Set the security context */
2001-04-28 01:14:18 +04:00
DEBUG ( 3 , ( " setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d \n " ,
( unsigned int ) uid , ( unsigned int ) gid , sec_ctx_stack_ndx ) ) ;
2000-06-23 09:49:11 +04:00
2002-09-25 19:19:00 +04:00
debug_nt_user_token ( DBGC_CLASS , 5 , token ) ;
debug_unix_user_token ( DBGC_CLASS , 5 , uid , gid , ngroups , groups ) ;
2001-01-04 22:27:08 +03:00
2000-06-23 09:49:11 +04:00
gain_root ( ) ;
# ifdef HAVE_SETGROUPS
sys_setgroups ( ngroups , groups ) ;
2000-06-23 21:31:38 +04:00
# endif
2000-06-23 09:49:11 +04:00
2000-06-23 23:57:42 +04:00
ctx_p - > ngroups = ngroups ;
2000-06-23 21:31:38 +04:00
2001-09-17 15:25:41 +04:00
SAFE_FREE ( ctx_p - > groups ) ;
2000-08-09 22:40:48 +04:00
if ( token & & ( token = = ctx_p - > token ) )
smb_panic ( " DUPLICATE_TOKEN " ) ;
2000-08-04 02:38:43 +04:00
delete_nt_token ( & ctx_p - > token ) ;
2000-06-23 23:57:42 +04:00
ctx_p - > groups = memdup ( groups , sizeof ( gid_t ) * ngroups ) ;
2000-08-04 02:38:43 +04:00
ctx_p - > token = dup_nt_token ( token ) ;
2000-06-23 09:49:11 +04:00
become_id ( uid , gid ) ;
2000-06-23 23:57:42 +04:00
ctx_p - > uid = uid ;
ctx_p - > gid = gid ;
2000-06-23 09:49:11 +04:00
/* Update current_user stuff */
current_user . uid = uid ;
current_user . gid = gid ;
current_user . ngroups = ngroups ;
current_user . groups = groups ;
2001-07-25 22:07:36 +04:00
current_user . nt_user_token = ctx_p - > token ;
2000-06-23 09:49:11 +04:00
}
2000-08-04 02:38:43 +04:00
/****************************************************************************
Become root context .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-06-23 09:49:11 +04:00
void set_root_sec_ctx ( void )
{
/* May need to worry about supplementary groups at some stage */
2000-08-04 02:38:43 +04:00
set_sec_ctx ( 0 , 0 , 0 , NULL , NULL ) ;
2000-06-23 09:49:11 +04:00
}
2000-08-04 02:38:43 +04:00
/****************************************************************************
Pop a security context from the stack .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-06-23 09:49:11 +04:00
BOOL pop_sec_ctx ( void )
{
2000-06-23 23:57:42 +04:00
struct sec_ctx * ctx_p ;
struct sec_ctx * prev_ctx_p ;
2000-06-23 09:49:11 +04:00
/* Check for stack underflow */
if ( sec_ctx_stack_ndx = = 0 ) {
DEBUG ( 0 , ( " Security context stack underflow! \n " ) ) ;
2000-08-09 22:40:48 +04:00
smb_panic ( " Security context stack underflow! \n " ) ;
2000-06-23 09:49:11 +04:00
}
2000-06-23 23:57:42 +04:00
ctx_p = & sec_ctx_stack [ sec_ctx_stack_ndx ] ;
2000-06-23 09:49:11 +04:00
/* Clear previous user info */
2000-06-23 23:57:42 +04:00
ctx_p - > uid = ( uid_t ) - 1 ;
ctx_p - > gid = ( gid_t ) - 1 ;
2000-06-23 09:49:11 +04:00
2001-09-17 15:25:41 +04:00
SAFE_FREE ( ctx_p - > groups ) ;
2000-06-23 23:57:42 +04:00
ctx_p - > ngroups = 0 ;
2000-06-23 09:49:11 +04:00
2000-08-04 02:38:43 +04:00
delete_nt_token ( & ctx_p - > token ) ;
2000-06-23 09:49:11 +04:00
/* Pop back previous user */
sec_ctx_stack_ndx - - ;
gain_root ( ) ;
2000-06-23 23:57:42 +04:00
prev_ctx_p = & sec_ctx_stack [ sec_ctx_stack_ndx ] ;
2000-06-23 09:49:11 +04:00
# ifdef HAVE_SETGROUPS
2000-06-23 23:57:42 +04:00
sys_setgroups ( prev_ctx_p - > ngroups , prev_ctx_p - > groups ) ;
2000-06-23 09:49:11 +04:00
# endif
2000-06-23 23:57:42 +04:00
become_id ( prev_ctx_p - > uid , prev_ctx_p - > gid ) ;
2000-06-23 09:49:11 +04:00
/* Update current_user stuff */
2000-06-23 23:57:42 +04:00
current_user . uid = prev_ctx_p - > uid ;
current_user . gid = prev_ctx_p - > gid ;
current_user . ngroups = prev_ctx_p - > ngroups ;
current_user . groups = prev_ctx_p - > groups ;
2000-08-04 02:38:43 +04:00
current_user . nt_user_token = prev_ctx_p - > token ;
2000-06-23 09:49:11 +04:00
2001-04-28 01:14:18 +04:00
DEBUG ( 3 , ( " pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d \n " ,
( unsigned int ) geteuid ( ) , ( unsigned int ) getegid ( ) , sec_ctx_stack_ndx ) ) ;
2000-06-23 09:49:11 +04:00
return True ;
}
/* Initialise the security context system */
void init_sec_ctx ( void )
{
int i ;
2000-06-23 23:57:42 +04:00
struct sec_ctx * ctx_p ;
2000-06-23 09:49:11 +04:00
/* Initialise security context stack */
memset ( sec_ctx_stack , 0 , sizeof ( struct sec_ctx ) * MAX_SEC_CTX_DEPTH ) ;
for ( i = 0 ; i < MAX_SEC_CTX_DEPTH ; i + + ) {
sec_ctx_stack [ i ] . uid = ( uid_t ) - 1 ;
sec_ctx_stack [ i ] . gid = ( gid_t ) - 1 ;
}
/* Initialise first level of stack. It is the current context */
2000-06-23 23:57:42 +04:00
ctx_p = & sec_ctx_stack [ 0 ] ;
2000-06-23 09:49:11 +04:00
2000-06-23 23:57:42 +04:00
ctx_p - > uid = geteuid ( ) ;
ctx_p - > gid = getegid ( ) ;
2000-06-23 09:49:11 +04:00
2002-07-15 14:35:28 +04:00
get_current_groups ( ctx_p - > gid , & ctx_p - > ngroups , & ctx_p - > groups ) ;
2000-06-23 09:49:11 +04:00
2000-08-04 02:38:43 +04:00
ctx_p - > token = NULL ; /* Maps to guest user. */
2000-06-23 09:49:11 +04:00
/* Initialise current_user global */
2000-06-23 23:57:42 +04:00
current_user . uid = ctx_p - > uid ;
current_user . gid = ctx_p - > gid ;
current_user . ngroups = ctx_p - > ngroups ;
current_user . groups = ctx_p - > groups ;
2000-06-23 09:49:11 +04:00
/* The conn and vuid are usually taken care of by other modules.
We initialise them here . */
current_user . conn = NULL ;
current_user . vuid = UID_FIELD_INVALID ;
2000-08-04 02:38:43 +04:00
current_user . nt_user_token = NULL ;
2000-06-23 09:49:11 +04:00
}