123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- #define _GNU_SOURCE //pipe2()
- #include <errno.h>
- #include <fcntl.h>
- #include <grp.h>
- #include <paths.h>
- #include <pwd.h>
- #include <signal.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/prctl.h>
- #include <sys/socket.h>
- #include <unistd.h>
- #include "launcherd.h"
- static int switchToUser( struct passwd* user ){
-
- /*** switch gid, groups and uid ***/
- if( setgid( user->pw_gid ) ) goto l_perror;
- if( initgroups(user->pw_name, user->pw_gid) ) goto l_perror;
- if( setuid( user->pw_uid ) ) goto l_perror;
-
- /*** check they really switched ***/
- if( getgid() != user->pw_gid || getegid() != user->pw_gid ||
- getuid() != user->pw_uid || geteuid() != user->pw_uid ){
- printf( "[launcherd] Failed switch uid or gid\n" );
- return -2;
- }
-
- /*** if target user is root, no more checks is needed ***/
- if( !user->pw_uid || !user->pw_gid ) return 0;
-
- /*** extra check if target user not root ***/
- if( !setuid(0) || !seteuid(0) ){
- printf( "[launcherd] Could not drop root privileges!\n" );
- return -3;
- }
-
- return 0;
-
- l_perror:
- perror( "[launcherd] switch user" );
- return -1;
- }
- /**************************************************
- func execute
- set environment, cd to home,
- route stdin, stdout, stderr to /dev/null
- and attempt to exec program from user
- input
- user - user to run as
- programm - path to executable
- return
- not return on success
- errno on error
- **************************************************/
- static int execute( struct passwd* user, char* programm, int pipe ){
-
- /*** args ***/
- char* execArgs[] = { programm, NULL };
-
- /*** env ***/
- char eUSER[64];
- char eLOGNAME[64];
- char eHOME[128];
- char eSHELL[128];
- char ePATH[] = "PATH=" _PATH_STDPATH;
- char* execEnv[] = { eUSER, eLOGNAME, eHOME, eSHELL, ePATH, NULL };
-
- if( strlen(user->pw_name) > 32 || strlen(user->pw_dir) > 120 || strlen(user->pw_shell) > 120 ){
- printf( "[launcherd] username or home or shell too long\n" );
- return ECANCELED;
- }
-
- sprintf( eUSER, "USER=%s", user->pw_name );
- sprintf( eLOGNAME, "LOGNAME=%s", user->pw_name );
- sprintf( eHOME, "HOME=%s", user->pw_dir );
- sprintf( eSHELL, "SHELL=%s", user->pw_shell );
-
- /*** pwd ***/
- if( chdir(user->pw_dir) ){
- printf( "[launcherd] Cannot cd to [%s], using [/] instead\n", user->pw_dir );
- if( chdir("/") ){
- printf( "[launcherd] chdir\n" );
- return ECANCELED;
- }
- }
-
- printf( "[launcherd] Trying to start [%s] from user [%s]\n", programm, user->pw_name );
-
- /*** route stdin, stdout and stderr to /dev/null ***/
- if( fileno( freopen(_PATH_DEVNULL, "rb", stdin ) ) != 0 ) return EBADF;
- if( fileno( freopen(_PATH_DEVNULL, "wb", stdout) ) != 1 ) return EBADF;
- if( fileno( freopen(_PATH_DEVNULL, "wb", stderr) ) != 2 ) return EBADF;
-
- /*** exec ***/
- execve( programm, execArgs, execEnv );
- return errno;
- }
- /**************************************
- func executeFromUser
- switch to user and execute prorgamm
- return
- not return on success
- if return then error
- **************************************/
- static int executeFromUser( struct launcherdRequest* execrq, int pipe ){
-
- int savedErrno;
- struct passwd* user;
-
- //get user data from /etc/passwd
- user = getpwnam( execrq->username );
- if( !user ){
- if( !errno ) printf( "[launcherd] User [%s] not found.\n", execrq->username );
- else perror( "[launcherd] getpwnam" );
- savedErrno = ECANCELED;
- goto l_error;
- }
-
- //switch to user
- if( switchToUser(user) ){
- savedErrno = ECANCELED;
- goto l_error;
- }
-
- //try to execute;
- savedErrno = execute( user, execrq->programm, pipe );
-
- l_error:
- return write( pipe, &savedErrno, sizeof(savedErrno) );
- }
- /**********************************************
- func launcherdListen
- listen control socket and handle connection
- input
- mySocket - socket to listen
- return
- 0 on exit if socket close from other side
- not 0 on error
- **********************************************/
- static int launcherdListen( int mySocket ){
-
- int res;
- int execRes;
- int pipes[2];
- struct launcherdRequest rq;
-
- printf( "[launcherd] Listen\n" );
-
- l_listen:
- //receive request from socket
- if( recv(mySocket, &rq, sizeof(rq), MSG_WAITALL) != sizeof(rq) ){
- if( errno ){
- perror( "[launcherd] recv" );
- return -1;
- }
- printf( "[launcherd] Socket seems has been closed. Exiting.\n" );
- return 0;
- }
-
- //strings must be null-terminated
- rq.username[sizeof(rq.username)-1] = 0x00;
- rq.programm[sizeof(rq.programm)-1] = 0x00;
-
- //create pipes to check execute result
- res = pipe2( pipes, O_CLOEXEC );
- if( res ){
- perror( "[launcherd] pipe2" );
- return -2;
- }
-
- //fork process to execute requested program
- res = fork();
- if( res < 0 ){
- perror( "[launcherd] fork" );
- return -3;
- }
-
- //prepare and exec in the child process
- if( !res ){//child
- close( mySocket );
- close( pipes[0] );
- executeFromUser( &rq, pipes[1] );
- exit( 1 );//exit if exec failed
- }
-
- //close child pipe
- close( pipes[1] );
-
- //read and print execute result from child over pipe
- res = read( pipes[0], &execRes, sizeof(execRes) );
- if( res != sizeof(execRes) ) printf( "[launcherd] Success.\n" );
- else printf( "[launcherd] Failed: %s.\n", strerror(execRes) );
-
- //close our pipe
- close( pipes[0] );
-
- //listen next request
- goto l_listen;
- }
- /********************************************
- func launcherdStart
- create control socket and start launcherd
- return
- negative on error
- positive as control socket
- ********************************************/
- int launcherdStart( void ){
-
- int res;
- int sockets[2];
-
- //create sockets
- res = socketpair( AF_UNIX, SOCK_STREAM, 0, sockets );
- if( res ){
- perror( "[launcherd] socketpair" );
- return -1;
- }
-
- //fork process
- res = fork();
- if( res < 0 ){
- perror( "[launcherd] fork" );
- return -2;
- }
-
- //start listen socket in the child process
- if( !res ){//child
- close( sockets[0] );
- prctl( PR_SET_NAME, "launcherd", 0, 0, 0 );
- signal( SIGCHLD, SIG_IGN );
- signal( SIGPIPE, SIG_IGN );
- res = launcherdListen( sockets[1] );
- printf( "[launcherd] Exiting with code: %i\n", res );
- close( sockets[1] );
- exit( res );
- }
-
- //return socket for communicate with child process
- close( sockets[1] );
- return sockets[0];
- }
|