2007-09-15 02:14:39 +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
2007-09-15 02:14:39 +04:00
1998-08-14 21:38:29 +04:00
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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
1998-08-14 21:38:29 +04:00
( at your option ) any later version .
2007-09-15 02:14:39 +04:00
1998-08-14 21:38:29 +04:00
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 .
2007-09-15 02:14:39 +04:00
1998-08-14 21:38:29 +04:00
You should have received a copy of the GNU General Public License
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
1998-08-14 21:38:29 +04:00
*/
# include "includes.h"
2013-02-25 20:34:21 +04:00
# include "system/filesys.h"
1998-08-14 21:38:29 +04:00
/* 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 )
2007-09-15 02:14:39 +04:00
{
2001-04-13 04:37:00 +04:00
int fd ;
2007-09-15 02:14:39 +04:00
TALLOC_CTX * ctx = talloc_stackframe ( ) ;
char * path = NULL ;
2013-02-25 20:34:21 +04:00
mode_t mask ;
1998-08-14 21:38:29 +04:00
2007-09-15 02:14:39 +04:00
path = talloc_asprintf ( ctx ,
" %s/smb.XXXXXX " ,
tmpdir ( ) ) ;
if ( ! path ) {
TALLOC_FREE ( ctx ) ;
errno = ENOMEM ;
return - 1 ;
}
1998-08-14 21:38:29 +04:00
2001-04-13 04:37:00 +04:00
/* now create the file */
2013-02-25 20:34:21 +04:00
mask = umask ( S_IRWXO | S_IRWXG ) ;
2009-04-21 01:58:26 +04:00
fd = mkstemp ( path ) ;
2013-02-25 20:34:21 +04:00
umask ( mask ) ;
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 ) ) ) ;
2007-09-15 02:14:39 +04:00
TALLOC_FREE ( ctx ) ;
2001-04-13 04:37:00 +04:00
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 ) ;
2007-09-15 02:14:39 +04:00
TALLOC_FREE ( ctx ) ;
2001-04-13 04:37:00 +04:00
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
2007-10-19 04:40:25 +04:00
static int smbrun_internal ( const char * cmd , int * outfd , bool sanitize )
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 ;
2014-09-24 01:51:18 +04:00
void ( * saved_handler ) ( int ) ;
2007-09-15 02:14:39 +04:00
2000-06-09 10:58:06 +04:00
/*
2006-03-23 02:49:09 +03:00
* Lose any elevated privileges .
2000-06-09 10:58:06 +04:00
*/
2006-03-21 05:56:49 +03:00
drop_effective_capability ( KERNEL_OPLOCK_CAPABILITY ) ;
2006-03-23 02:49:09 +03:00
drop_effective_capability ( DMAPI_ACCESS_CAPABILITY ) ;
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 .
*/
2014-09-24 01:51:18 +04:00
saved_handler = CatchChildLeaveStatus ( ) ;
1999-12-13 16:27:58 +03:00
2012-03-24 23:17:08 +04:00
if ( ( pid = fork ( ) ) < 0 ) {
1999-12-13 16:27:58 +03:00
DEBUG ( 0 , ( " smbrun: fork failed with error %s \n " , strerror ( errno ) ) ) ;
2014-09-24 01:51:18 +04:00
( void ) CatchSignal ( SIGCLD , saved_handler ) ;
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 */
2016-02-16 17:46:06 +03:00
while ( ( wpid = waitpid ( pid , & status , 0 ) ) < 0 ) {
1999-12-13 16:27:58 +03:00
if ( errno = = EINTR ) {
errno = 0 ;
continue ;
}
break ;
}
2014-09-24 01:51:18 +04:00
( void ) CatchSignal ( SIGCLD , saved_handler ) ;
1999-12-13 16:27:58 +03:00
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 ) {
2012-03-28 05:37:04 +04:00
lseek ( * outfd , 0 , SEEK_SET ) ;
2001-04-13 04:37:00 +04:00
}
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 ;
}
2014-09-24 01:51:18 +04:00
( void ) 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 ) ;
2008-11-01 05:58:14 +03:00
if ( 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 ) ;
2009-05-11 20:42:08 +04:00
if ( ! non_root_mode ( ) ) {
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 */
}
1998-08-14 21:38:29 +04:00
}
2009-05-11 20:42:08 +04:00
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
2007-05-14 18:23:51 +04:00
{
2009-02-10 00:25:59 +03:00
char * newcmd = NULL ;
if ( sanitize ) {
newcmd = escape_shell_string ( cmd ) ;
if ( ! newcmd )
exit ( 82 ) ;
2007-05-14 18:23:51 +04:00
}
2009-02-10 00:25:59 +03:00
execl ( " /bin/sh " , " sh " , " -c " ,
newcmd ? ( const char * ) newcmd : cmd , NULL ) ;
SAFE_FREE ( newcmd ) ;
2007-05-14 18:23:51 +04:00
}
1998-08-14 21:38:29 +04:00
/* not reached */
2007-05-14 18:23:51 +04:00
exit ( 83 ) ;
1998-08-14 21:38:29 +04:00
return 1 ;
}
2004-09-14 04:21:11 +04:00
2007-05-14 18:23:51 +04:00
/****************************************************************************
Use only in known safe shell calls ( printing ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int smbrun_no_sanitize ( const char * cmd , int * outfd )
{
return smbrun_internal ( cmd , outfd , False ) ;
}
/****************************************************************************
By default this now sanitizes shell expansion .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int smbrun ( const char * cmd , int * outfd )
{
return smbrun_internal ( cmd , outfd , True ) ;
}
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 ] ;
2014-09-24 01:51:18 +04:00
void ( * saved_handler ) ( int ) ;
2004-09-14 04:21:11 +04:00
/*
2006-03-23 02:49:09 +03:00
* Lose any elevated privileges .
2004-09-14 04:21:11 +04:00
*/
2006-03-21 05:56:49 +03:00
drop_effective_capability ( KERNEL_OPLOCK_CAPABILITY ) ;
2006-03-23 02:49:09 +03:00
drop_effective_capability ( DMAPI_ACCESS_CAPABILITY ) ;
2004-09-14 04:21:11 +04:00
/* 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 .
*/
2014-09-24 01:51:18 +04:00
saved_handler = CatchChildLeaveStatus ( ) ;
2004-09-14 04:21:11 +04:00
2012-03-24 23:17:08 +04:00
if ( ( pid = fork ( ) ) < 0 ) {
2004-09-14 04:21:11 +04:00
DEBUG ( 0 , ( " smbrunsecret: fork failed with error %s \n " , strerror ( errno ) ) ) ;
2014-09-24 01:51:18 +04:00
( void ) CatchSignal ( SIGCLD , saved_handler ) ;
2004-09-14 04:21:11 +04:00
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 */
2016-02-16 17:46:06 +03:00
while ( ( wpid = waitpid ( pid , & status , 0 ) ) < 0 ) {
2004-09-14 04:21:11 +04:00
if ( errno = = EINTR ) {
errno = 0 ;
continue ;
}
break ;
}
2014-09-24 01:51:18 +04:00
( void ) CatchSignal ( SIGCLD , saved_handler ) ;
2004-09-14 04:21:11 +04:00
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 ;
}
2014-09-24 01:51:18 +04:00
( void ) CatchChild ( ) ;
2004-09-14 04:21:11 +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 */
close ( ifd [ 1 ] ) ;
close ( 0 ) ;
2008-11-01 05:58:14 +03:00
if ( dup2 ( ifd [ 0 ] , 0 ) ! = 0 ) {
2004-09-14 04:21:11 +04:00
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 ) ;
2009-05-11 20:42:08 +04:00
if ( ! non_root_mode ( ) ) {
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 */
}
2004-09-14 04:21:11 +04:00
}
2009-05-11 20:42:08 +04:00
2004-09-14 04:21:11 +04:00
# 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 ) ;
2007-05-14 18:23:51 +04:00
2004-09-14 04:21:11 +04:00
/* not reached */
exit ( 82 ) ;
return 1 ;
}