1998-08-14 21:38:29 +04:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
1998-08-14 21:38:29 +04:00
run a command as a specified user
Copyright ( C ) Andrew Tridgell 1992 - 1998
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"
/* need to move this from here!! need some sleep ... */
struct current_user current_user ;
/****************************************************************************
2001-04-13 04:37:00 +04:00
This is a utility function of smbrun ( ) .
1998-08-14 21:38:29 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-04-13 23:12:06 +04:00
static int setup_out_fd ( void )
2001-04-13 04:37:00 +04:00
{
int fd ;
pstring path ;
1998-08-14 21:38:29 +04:00
2001-04-13 23:12:06 +04:00
slprintf ( path , sizeof ( path ) - 1 , " %s/smb.XXXXXX " , tmpdir ( ) ) ;
1998-08-14 21:38:29 +04:00
2001-04-13 04:37:00 +04:00
/* now create the file */
fd = smb_mkstemp ( path ) ;
2001-04-05 23:17:54 +04:00
2001-04-13 04:37:00 +04:00
if ( fd = = - 1 ) {
DEBUG ( 0 , ( " setup_out_fd: Failed to create file %s. (%s) \n " ,
path , strerror ( errno ) ) ) ;
return - 1 ;
}
1998-08-14 21:38:29 +04:00
2001-04-13 04:37:00 +04:00
DEBUG ( 10 , ( " setup_out_fd: Created tmp file %s \n " , path ) ) ;
1998-08-14 21:38:29 +04:00
2001-04-13 04:37:00 +04:00
/* Ensure file only kept around by open fd. */
unlink ( path ) ;
return fd ;
1998-08-14 21:38:29 +04:00
}
/****************************************************************************
run a command being careful about uid / gid handling and putting the output in
2001-04-13 04:37:00 +04:00
outfd ( or discard it if outfd is NULL ) .
1998-08-14 21:38:29 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-04-13 04:37:00 +04:00
2005-09-15 03:58:14 +04:00
int smbrun ( const char * cmd , int * outfd )
1998-08-14 21:38:29 +04:00
{
1999-12-13 16:27:58 +03:00
pid_t pid ;
2006-02-02 23:44:50 +03:00
uid_t uid = current_user . ut . uid ;
gid_t gid = current_user . ut . gid ;
2000-06-09 10:58:06 +04:00
/*
* Lose any kernel oplock capabilities we may have .
*/
oplock_set_capability ( False , False ) ;
1998-09-26 03:40:49 +04:00
2001-04-13 04:37:00 +04:00
/* point our stdout at the file we want output to go into */
1998-08-14 21:38:29 +04:00
2001-04-13 23:12:06 +04:00
if ( outfd & & ( ( * outfd = setup_out_fd ( ) ) = = - 1 ) ) {
2001-04-13 04:37:00 +04:00
return - 1 ;
2000-05-08 22:24:26 +04:00
}
2001-04-13 04:37:00 +04:00
/* in this method we will exec /bin/sh with the correct
1998-08-14 21:38:29 +04:00
arguments , after first setting stdout to point at the file */
1999-12-13 16:27:58 +03:00
/*
* We need to temporarily stop CatchChild from eating
* SIGCLD signals as it also eats the exit status code . JRA .
*/
CatchChildLeaveStatus ( ) ;
2000-05-02 06:23:41 +04:00
if ( ( pid = sys_fork ( ) ) < 0 ) {
1999-12-13 16:27:58 +03:00
DEBUG ( 0 , ( " smbrun: fork failed with error %s \n " , strerror ( errno ) ) ) ;
CatchChild ( ) ;
2001-04-13 04:37:00 +04:00
if ( outfd ) {
close ( * outfd ) ;
* outfd = - 1 ;
}
1999-12-13 16:27:58 +03:00
return errno ;
2004-09-14 04:21:11 +04:00
}
1999-12-13 16:27:58 +03:00
if ( pid ) {
/*
* Parent .
*/
1998-08-14 21:38:29 +04:00
int status = 0 ;
1999-12-13 16:27:58 +03:00
pid_t wpid ;
1998-08-14 21:38:29 +04:00
/* the parent just waits for the child to exit */
1999-12-13 16:27:58 +03:00
while ( ( wpid = sys_waitpid ( pid , & status , 0 ) ) < 0 ) {
if ( errno = = EINTR ) {
errno = 0 ;
continue ;
}
break ;
}
CatchChild ( ) ;
if ( wpid ! = pid ) {
DEBUG ( 2 , ( " waitpid(%d) : %s \n " , ( int ) pid , strerror ( errno ) ) ) ;
2001-04-13 04:37:00 +04:00
if ( outfd ) {
close ( * outfd ) ;
* outfd = - 1 ;
}
1998-08-14 21:38:29 +04:00
return - 1 ;
}
2001-04-13 04:37:00 +04:00
/* Reset the seek pointer. */
if ( outfd ) {
sys_lseek ( * outfd , 0 , SEEK_SET ) ;
}
1999-12-13 16:27:58 +03:00
# if defined(WIFEXITED) && defined(WEXITSTATUS)
if ( WIFEXITED ( status ) ) {
return WEXITSTATUS ( status ) ;
}
# endif
2001-04-13 04:37:00 +04:00
1998-08-14 21:38:29 +04:00
return status ;
}
1999-12-13 16:27:58 +03:00
CatchChild ( ) ;
1998-08-14 21:38:29 +04:00
/* we are in the child. we exec /bin/sh to do the work for us. we
don ' t directly exec the command we want because it may be a
pipeline or anything else the config file specifies */
/* point our stdout at the file we want output to go into */
2001-04-13 04:37:00 +04:00
if ( outfd ) {
close ( 1 ) ;
2002-08-17 21:00:51 +04:00
if ( sys_dup2 ( * outfd , 1 ) ! = 1 ) {
2001-04-13 04:37:00 +04:00
DEBUG ( 2 , ( " Failed to create stdout file descriptor \n " ) ) ;
close ( * outfd ) ;
exit ( 80 ) ;
}
1998-08-14 21:38:29 +04:00
}
2001-04-13 04:37:00 +04:00
1999-12-13 16:27:58 +03:00
/* now completely lose our privileges. This is a fairly paranoid
1998-08-14 21:38:29 +04:00
way of doing it , but it does work on all systems that I know of */
1999-12-13 16:27:58 +03:00
become_user_permanently ( uid , gid ) ;
1998-08-14 21:38:29 +04:00
if ( getuid ( ) ! = uid | | geteuid ( ) ! = uid | |
getgid ( ) ! = gid | | getegid ( ) ! = gid ) {
1999-12-13 16:27:58 +03:00
/* we failed to lose our privileges - do not execute
1998-08-14 21:38:29 +04:00
the command */
exit ( 81 ) ; /* we can't print stuff at this stage,
instead use exit codes for debugging */
}
2000-04-23 12:45:21 +04:00
# ifndef __INSURE__
1998-08-14 21:38:29 +04:00
/* close all other file descriptors, leaving only 0, 1 and 2. 0 and
2 point to / dev / null from the startup code */
2001-04-13 04:37:00 +04:00
{
int fd ;
1998-08-14 21:38:29 +04:00
for ( fd = 3 ; fd < 256 ; fd + + ) close ( fd ) ;
2001-04-13 04:37:00 +04:00
}
2000-04-23 12:45:21 +04:00
# endif
1998-08-14 21:38:29 +04:00
execl ( " /bin/sh " , " sh " , " -c " , cmd , NULL ) ;
/* not reached */
exit ( 82 ) ;
return 1 ;
}
2004-09-14 04:21:11 +04:00
/****************************************************************************
run a command being careful about uid / gid handling and putting the output in
outfd ( or discard it if outfd is NULL ) .
sends the provided secret to the child stdin .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-09-15 03:58:14 +04:00
int smbrunsecret ( const char * cmd , const char * secret )
2004-09-14 04:21:11 +04:00
{
pid_t pid ;
2006-02-02 23:44:50 +03:00
uid_t uid = current_user . ut . uid ;
gid_t gid = current_user . ut . gid ;
2004-09-14 04:21:11 +04:00
int ifd [ 2 ] ;
/*
* Lose any kernel oplock capabilities we may have .
*/
oplock_set_capability ( False , False ) ;
/* build up an input pipe */
if ( pipe ( ifd ) ) {
return - 1 ;
}
/* in this method we will exec /bin/sh with the correct
arguments , after first setting stdout to point at the file */
/*
* We need to temporarily stop CatchChild from eating
* SIGCLD signals as it also eats the exit status code . JRA .
*/
CatchChildLeaveStatus ( ) ;
if ( ( pid = sys_fork ( ) ) < 0 ) {
DEBUG ( 0 , ( " smbrunsecret: fork failed with error %s \n " , strerror ( errno ) ) ) ;
CatchChild ( ) ;
return errno ;
}
if ( pid ) {
/*
* Parent .
*/
int status = 0 ;
pid_t wpid ;
2005-09-30 21:13:37 +04:00
size_t towrite ;
ssize_t wrote ;
2004-09-14 04:21:11 +04:00
close ( ifd [ 0 ] ) ;
/* send the secret */
2005-09-30 21:13:37 +04:00
towrite = strlen ( secret ) ;
wrote = write ( ifd [ 1 ] , secret , towrite ) ;
if ( wrote ! = towrite ) {
DEBUG ( 0 , ( " smbrunsecret: wrote %ld of %lu bytes \n " , ( long ) wrote , ( unsigned long ) towrite ) ) ;
}
2004-09-14 04:21:11 +04:00
fsync ( ifd [ 1 ] ) ;
close ( ifd [ 1 ] ) ;
/* the parent just waits for the child to exit */
while ( ( wpid = sys_waitpid ( pid , & status , 0 ) ) < 0 ) {
if ( errno = = EINTR ) {
errno = 0 ;
continue ;
}
break ;
}
CatchChild ( ) ;
if ( wpid ! = pid ) {
DEBUG ( 2 , ( " waitpid(%d) : %s \n " , ( int ) pid , strerror ( errno ) ) ) ;
return - 1 ;
}
# if defined(WIFEXITED) && defined(WEXITSTATUS)
if ( WIFEXITED ( status ) ) {
return WEXITSTATUS ( status ) ;
}
# endif
return status ;
}
CatchChild ( ) ;
/* we are in the child. we exec /bin/sh to do the work for us. we
don ' t directly exec the command we want because it may be a
pipeline or anything else the config file specifies */
close ( ifd [ 1 ] ) ;
close ( 0 ) ;
if ( sys_dup2 ( ifd [ 0 ] , 0 ) ! = 0 ) {
DEBUG ( 2 , ( " Failed to create stdin file descriptor \n " ) ) ;
close ( ifd [ 0 ] ) ;
exit ( 80 ) ;
}
/* now completely lose our privileges. This is a fairly paranoid
way of doing it , but it does work on all systems that I know of */
become_user_permanently ( uid , gid ) ;
if ( getuid ( ) ! = uid | | geteuid ( ) ! = uid | |
getgid ( ) ! = gid | | getegid ( ) ! = gid ) {
/* we failed to lose our privileges - do not execute
the command */
exit ( 81 ) ; /* we can't print stuff at this stage,
instead use exit codes for debugging */
}
# ifndef __INSURE__
/* close all other file descriptors, leaving only 0, 1 and 2. 0 and
2 point to / dev / null from the startup code */
{
int fd ;
for ( fd = 3 ; fd < 256 ; fd + + ) close ( fd ) ;
}
# endif
execl ( " /bin/sh " , " sh " , " -c " , cmd , NULL ) ;
/* not reached */
exit ( 82 ) ;
return 1 ;
}