/* * 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 120 seconds) * * R. Perry, Nov. 2004 */ #include #include #include #include #include #include #include #include #include static char *progname; /* my name */ static pid_t pid; /* child pid */ static int limit = 120; /* time limit */ void report_signal( 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 SIGEMT: desc = "SIGEMT - EMT instruction"; break; case SIGFPE: desc = "SIGFPE - floating point exception"; break; case SIGKILL: desc = "SIGKILL - kill"; break; case SIGBUS: desc = "SIGBUS - bus error"; break; case SIGSEGV: desc = "SIGSEGV - segmentation violation"; break; case SIGSYS: desc = "SIGSYS - bad argument to system call"; break; case SIGPIPE: desc = "SIGPIPE - write on a pipe with no one to read it"; break; case SIGALRM: desc = "SIGALRM - alarm clock"; break; case SIGTERM: desc = "SIGTERM - terminate"; break; case SIGUSR1: desc = "SIGUSR1 - user defined signal 1"; break; case SIGUSR2: desc = "SIGUSR2 - user defined signal 2"; break; case SIGCHLD: desc = "SIGCHLD - child status change"; break; case SIGPWR: desc = "SIGPWR - power-fail restart"; break; case SIGWINCH: desc = "SIGWINCH - window size change"; break; case SIGURG: desc = "SIGURG - urgent socket condition"; break; case SIGPOLL: desc = "SIGPOLL - pollable event occured"; break; case SIGSTOP: desc = "SIGSTOP - stop"; break; case SIGTSTP: desc = "SIGTSTP - user stop requested from tty"; break; case SIGCONT: desc = "SIGCONT - stopped process has been continued"; break; case SIGTTIN: desc = "SIGTTIN - background tty read attempted"; break; case SIGTTOU: desc = "SIGTTOU - background tty write attempted"; break; case SIGVTALRM: desc = "SIGVTALRM - virtual timer expired"; break; case SIGPROF: desc = "SIGPROF - profiling timer expired"; break; case SIGXCPU: desc = "SIGXCPU - exceeded cpu limit"; break; case SIGXFSZ: desc = "SIGXFSZ - exceeded file size limit"; break; case SIGWAITING: desc = "SIGWAITING - process's lwps are blocked"; break; case SIGLWP: desc = "SIGLWP - thread library"; break; case SIGFREEZE: desc = "SIGFREEZE - CPR freeze"; break; case SIGTHAW: desc = "SIGTHAW - CPR thaw"; break; case SIGCANCEL: desc = "SIGCANCEL - thread cancellation"; break; case SIGLOST: desc = "SIGLOST - resource lost"; break; } fprintf( stderr, "\nterminated by signal %d - %s\n", 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); } 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); } 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; } else if( pid == 0) /* child */ { int e = 0; struct rlimit rl; rl.rlim_cur = rl.rlim_max = 0; if( setrlimit( RLIMIT_CORE, &rl)) { ++e; perror( "setrlimit core"); } rl.rlim_cur = rl.rlim_max = 8192*1024; if( setrlimit( RLIMIT_DATA, &rl)) { ++e; perror( "setrlimit data"); } rl.rlim_cur = rl.rlim_max = 1024*512; if( setrlimit( RLIMIT_FSIZE, &rl)) { ++e; perror( "setrlimit fsize"); } rl.rlim_cur = rl.rlim_max = 64; if( setrlimit( RLIMIT_NOFILE, &rl)) { ++e; perror( "setrlimit nfiles"); } rl.rlim_cur = rl.rlim_max = 8192*1024; if( setrlimit( RLIMIT_STACK, &rl)) { ++e; perror( "setrlimit stack"); } rl.rlim_cur = rl.rlim_max = 10; if( setrlimit( RLIMIT_CPU, &rl)) { ++e; perror( "setrlimit cpu"); } if( putenv( "PATH=/vecr/local-bin:/opt/bin:/usr/bin:/usr/ccs/bin:.")) { ++e; perror( "putenv PATH"); } if( e) { fflush( stderr); _exit(1); } execvp( argv[1], argv+1); /* should not return */ fprintf( stderr, "%s: execvp: %s: %s\n", progname, argv[1], strerror(errno)); fflush( stderr); _exit(0); } else /* parent */ { int status; pid_t wait_result; signal( SIGALRM, cleanup); /* set alarm */ alarm( limit); wait_result = wait( &status); /* wait for child to exit */ alarm(0); /* cancel alarm */ if( wait_result == pid) { int high = (status >> 8) & 0xff; int low = status & 0xff; if( high && low) /* child has stopped */ { fprintf( stderr, "\nstopped.\n"); fflush( stderr); cleanup(0); } else if( high == 0 && low) /* child was terminated by a signal */ { report_signal( low); fflush( stderr); high = 1; } /* else low == 0, child has exited */ fprintf( stderr, "\n---\nExit status = %d\n", high); return high; } else { fprintf( stderr, "%s: wait: %s\n", progname, strerror(errno)); fflush( stderr); return 1; } } return 0; } /* from man -s2 wait: o If the child process stopped, the high order 8 bits of status will contain the number of the signal that caused the process to stop and the low order 8 bits will be set equal to WSTOPFLG. o If the child process terminated due to an _exit() call, the low order 8 bits of status will be 0 and the high order 8 bits will contain the low order 8 bits of the argument that the child process passed to _exit(); see exit(2). o If the child process terminated due to a signal, the high order 8 bits of status will be 0 and the low order 8 bits will contain the number of the signal that caused the termination. In addition, if WCOREFLG is set, a "core image" will have been produced; see signal(3C). */