/* * set uid and gid, then run something, with a time limit * * invoked as: * * ./run [--setuidgid uid gid] [--chdir dir] [-] prog [args...] * * for example: * * ./run --setuidgid 3012 3012 --chdir /home/frodo/a3 \ * -10 gdb -x /local-bin/gdb.batch -batch ./p1 * * where - optionally specifies the time limit (default 1800 seconds) * * R. Perry, Nov. 2004 * * Updated June 2012 for Linux * * Updated October 2012 to use execvpe() with restricted environment, * and to set HOME from the --chdir argument */ // for www.ece.villanova.edu: // #define execvpe execve #include #include #include #include #include #include #include #include #include #include int putenv(char *string); static char *progname; /* my name */ static pid_t pid; /* child pid */ static int limit = 1800; /* time limit */ void report_signal( const char *what, int sig) { const char *desc = ""; switch( sig) { case SIGHUP: desc = "SIGHUP - Hangup"; break; case SIGINT: desc = "SIGINT - Interrupt"; break; case SIGQUIT: desc = "SIGQUIT - Quit"; break; case SIGILL: desc = "SIGILL - Illegal instruction"; break; case SIGTRAP: desc = "SIGTRAP - Trace trap"; break; case SIGABRT: desc = "SIGABRT - Abort"; break; case SIGBUS: desc = "SIGBUS - BUS error"; break; case SIGFPE: desc = "SIGFPE - Floating-point exception"; break; case SIGKILL: desc = "SIGKILL - Kill"; break; case SIGUSR1: desc = "SIGUSR1 - User-defined signal 1"; break; case SIGSEGV: desc = "SIGSEGV - Segmentation violation"; break; case SIGUSR2: desc = "SIGUSR2 - User-defined signal 2"; break; case SIGPIPE: desc = "SIGPIPE - Broken pipe"; break; case SIGALRM: desc = "SIGALRM - Alarm clock"; break; case SIGTERM: desc = "SIGTERM - Termination"; break; case SIGSTKFLT: desc = "SIGSTKFLT - Stack fault"; break; case SIGCHLD: desc = "SIGCHLD - Child status has changed"; break; case SIGCONT: desc = "SIGCONT - Continue"; break; case SIGSTOP: desc = "SIGSTOP - Stop"; break; case SIGTSTP: desc = "SIGTSTP - Keyboard stop"; break; case SIGTTIN: desc = "SIGTTIN - Background read from tty"; break; case SIGTTOU: desc = "SIGTTOU - Background write to tty"; break; case SIGURG: desc = "SIGURG - Urgent condition on socket"; break; case SIGXCPU: desc = "SIGXCPU - CPU limit exceeded"; break; case SIGXFSZ: desc = "SIGXFSZ - File size limit exceeded"; break; case SIGVTALRM: desc = "SIGVTALRM - Virtual alarm clock"; break; case SIGPROF: desc = "SIGPROF - Profiling alarm clock"; break; case SIGWINCH: desc = "SIGWINCH - Window size change"; break; case SIGPOLL: desc = "SIGPOLL - Pollable event occurred"; break; case SIGPWR: desc = "SIGPWR - Power failure restart"; break; case SIGSYS: desc = "SIGSYS - Bad system call"; break; } fprintf( stderr, "\n%s by signal %d - %s\n", what, sig, desc); } void cleanup( int sig) { if( sig == SIGALRM) { fprintf( stderr, "\n---\n%s: time limit of %d seconds has been exceeded\n", progname, limit); fflush( stderr); } if( geteuid() != 0) kill( -1, SIGKILL); /* kill all, we better not be running as root! */ else kill( pid, SIGKILL); exit(1); } // enviroment for execvpe() // char home[BUFSIZ] = "/"; char *envp[] = { "PATH=/vecr/bin:/opt/jdk/bin:/opt/bin:/bin:/usr/bin:/sbin:/usr/sbin:.", "LD_PRELOAD=/vecr/IO/setvbuf.so", home, 0 }; int main( int argc, char *argv[]) { uid_t uid; gid_t gid; gid_t grouplist[1]; /* get my name */ progname = strrchr( argv[0], '/'); progname = progname ? progname + 1 : argv[0]; /*** Must be the first command-line option: --setuidgid uid gid ***/ if( argc >= 4 && strcmp( argv[1], "--setuidgid") == 0) { uid = atoi( argv[2]); gid = atoi( argv[3]); grouplist[0] = gid; if( setgroups( 1, grouplist)) { fprintf( stderr, "%s: setgroups() failed: %s\n", progname, strerror(errno)); exit(1); } if( setgid( gid)) { fprintf( stderr, "%s: setgid() failed: %s\n", progname, strerror(errno)); exit(1); } if( setuid( uid)) { fprintf( stderr, "%s: setuid() failed: %s\n", progname, strerror(errno)); exit(1); } argc -= 3; argv += 3; } /*** Must be the first remaining command-line option: --chdir dir ***/ if( argc >= 3 && strcmp( argv[1], "--chdir") == 0) { if( chdir( argv[2])) { fprintf( stderr, "%s: chdir() failed: %s\n", progname, strerror(errno)); exit(1); } strcpy( home, "HOME="); strncat( home, argv[2], BUFSIZ-10); argc -= 2; argv += 2; } /* check for - option */ if( argc > 1 && argv[1][0] == '-') { limit = atoi( argv[1]+1); --argc, ++argv; } /* check usage */ if( argc < 2) { fprintf( stderr, "Usage: %s [--setuidgid uid gid] [--chdir dir] " "[-] prog [args...]\n", progname); return 1; } /* reset errno */ errno = 0; /* try to fork */ if( (pid = fork()) < 0) { fprintf( stderr, "%s: fork: %s\n", progname, strerror(errno)); return 1; } if( pid == 0) /* child */ { int e = 0; struct rlimit rl; // set limits // // ulimit setrlimit // ----------------------------- --------- // time(seconds) 60 RLIMIT_CPU (seconds) // file(blocks) 1024 RLIMIT_FSIZE (bytes) // data(kbytes) 8192 RLIMIT_DATA (bytes) // stack(kbytes) 8192 RLIMIT_STACK (bytes) // coredump(blocks) 0 RLIMIT_CORE (bytes) // memory(kbytes) 65536 RLIMIT_RSS (bytes) // locked memory(kbytes) 64 RLIMIT_MEMLOCK (bytes) // process 64 RLIMIT_NPROC // nofiles 64 RLIMIT_NOFILE // vmemory(kbytes) unlimited RLIMIT_AS (bytes?) // locks 64 RLIMIT_LOCKS // rl.rlim_cur = rl.rlim_max = 60; if( setrlimit( RLIMIT_CPU, &rl)) { ++e; perror( "setrlimit cpu"); } rl.rlim_cur = rl.rlim_max = 1024*512; if( setrlimit( RLIMIT_FSIZE, &rl)) { ++e; perror( "setrlimit fsize"); } rl.rlim_cur = rl.rlim_max = 8192*1024; if( setrlimit( RLIMIT_DATA, &rl)) { ++e; perror( "setrlimit data"); } rl.rlim_cur = rl.rlim_max = 8192*1024; if( setrlimit( RLIMIT_STACK, &rl)) { ++e; perror( "setrlimit stack"); } rl.rlim_cur = rl.rlim_max = 0; if( setrlimit( RLIMIT_CORE, &rl)) { ++e; perror( "setrlimit core"); } rl.rlim_cur = rl.rlim_max = 65536*1024; if( setrlimit( RLIMIT_RSS, &rl)) { ++e; perror( "setrlimit memory"); } #if 0 // doesn't work on www.ece.villanova.edu rl.rlim_cur = rl.rlim_max = 64*1024; if( setrlimit( RLIMIT_MEMLOCK, &rl)) { ++e; perror( "setrlimit memlock"); } #endif rl.rlim_cur = rl.rlim_max = 64; if( setrlimit( RLIMIT_NPROC, &rl)) { ++e; perror( "setrlimit process"); } rl.rlim_cur = rl.rlim_max = 64; if( setrlimit( RLIMIT_NOFILE, &rl)) { ++e; perror( "setrlimit nfiles"); } rl.rlim_cur = rl.rlim_max = 64; if( setrlimit( RLIMIT_LOCKS, &rl)) { ++e; perror( "setrlimit locks"); } if( putenv( "PATH=/vecr/bin:/opt/jdk/bin:/opt/bin:/bin:/usr/bin:/sbin:/usr/sbin:.")) { ++e; perror( "putenv PATH"); } if( e) { fflush( stderr); _exit(1); } execvpe( argv[1], argv+1, envp); /* should not return */ fprintf( stderr, "%s: execvp: %s: %s\n", progname, argv[1], strerror(errno)); fflush( stderr); _exit(0); } /* parent */ int status; pid_t wait_result; signal( SIGALRM, cleanup); /* set alarm */ alarm( limit); /* wait for child to exit */ wait_result = waitpid( pid, &status, WUNTRACED); alarm(0); /* cancel alarm */ if( wait_result == (pid_t) -1) { fprintf( stderr, "%s: pid = %d, wait_result = %d, error = %s\n", progname, (int) pid, (int) wait_result, strerror(errno)); fflush( stderr); return 1; } /* wait_result == pid (of some child) */ if( WIFSTOPPED(status)) /* child has stopped */ { report_signal( "stopped", WSTOPSIG(status)); fflush( stderr); cleanup( WSTOPSIG(status)); return 1; } if( WIFSIGNALED(status)) /* child was terminated by a signal */ { report_signal( "terminated", WTERMSIG(status)); fflush( stderr); return 1; } if( WIFEXITED(status)) /* child has exited */ { return WEXITSTATUS(status); /* return child exit status */ } return 0; /* shouldn't get here */ }