X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=userv.git;a=blobdiff_plain;f=client.c;h=4987073a3de76dad9d9dcbe55e85ab7cb5b99a37;hp=02d77a9340057e8da024a92aa777c39e6c0fe44e;hb=b6c671fd90134d458ad4722ec3a99742bced1a34;hpb=26444956110f28281bef77dfc28be75e25ae85a2 diff --git a/client.c b/client.c index 02d77a9..4987073 100644 --- a/client.c +++ b/client.c @@ -19,6 +19,36 @@ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* + * Here too, we do some horrible asynchronous stuff with signals. + * + * The following objects &c. are used in signal handlers and so + * must be protected by calls to blocksignals if they are used in + * the main program: + * stderr + * swfile + * + * The following objects are used in the main program unprotected + * and so must not be used in signal handlers: + * srfile + * fdsetup[].copyfd + * fdsetup[].pipefd + * + * The following objects/functions are not modified/called after the + * asynchronicity starts: + * malloc + * fdsetupsize + * fdsetup[].mods + * fdsetup[].filename + * fdsetup[].oflags + * results of argument parsing + * + * systemerror, swfile, fdsetup[].catpid and fdsetup[].killed are used + * for communication between the main thread and the signal handlers. + * + * All the signal handlers save errno so that is OK too. + */ + #include #include #include @@ -42,19 +72,9 @@ #include "config.h" #include "common.h" +#include "both.h" #include "version.h" -struct optioninfo; - -typedef void optionfunction(const struct optioninfo*, const char *value, char *key); - -struct optioninfo { - int abbrev; - const char *full; - int values; /* 0: no value; 1: single value; 2: key and value */ - optionfunction *fn; -}; - enum fdmodifiervalues { fdm_read= 00001, fdm_write= 00002, @@ -76,60 +96,11 @@ struct fdmodifierinfo { int oflags; }; -const struct fdmodifierinfo fdmodifierinfos[]= { - { "read", fdm_read, - fdm_write, - O_RDONLY }, - { "write", fdm_write, - fdm_read, - O_WRONLY }, - { "overwrite", fdm_write|fdm_create|fdm_truncate, - fdm_read|fdm_fd|fdm_exclusive, - O_WRONLY|O_CREAT|O_TRUNC }, - { "create", fdm_write|fdm_create, - fdm_read|fdm_fd, - O_WRONLY|O_CREAT }, - { "creat", fdm_write|fdm_create, - fdm_read|fdm_fd, - O_WRONLY|O_CREAT }, - { "exclusive", fdm_write|fdm_create|fdm_exclusive, - fdm_read|fdm_fd|fdm_truncate, - O_WRONLY|O_CREAT|O_EXCL }, - { "excl", fdm_write|fdm_create|fdm_exclusive, - fdm_read|fdm_fd|fdm_truncate, - O_WRONLY|O_CREAT|O_EXCL }, - { "truncate", fdm_write|fdm_truncate, - fdm_read|fdm_fd|fdm_exclusive, - O_WRONLY|O_CREAT|O_EXCL }, - { "trunc", fdm_write|fdm_truncate, - fdm_read|fdm_fd|fdm_exclusive, - O_WRONLY|O_CREAT|O_EXCL }, - { "append", fdm_write|fdm_append, - fdm_read|fdm_fd, - O_WRONLY|O_CREAT|O_APPEND }, - { "sync", fdm_write|fdm_sync, - fdm_read|fdm_fd, - O_WRONLY|O_CREAT|O_SYNC }, - { "wait", fdm_wait, - fdm_nowait|fdm_close, - 0 }, - { "nowait", fdm_nowait, - fdm_wait|fdm_close, - 0 }, - { "close", fdm_close, - fdm_wait|fdm_nowait, - 0 }, - { "fd", fdm_fd, - fdm_create|fdm_exclusive|fdm_truncate|fdm_append|fdm_sync, - 0 }, - { 0 } -}; - struct fdsetupstate { - const char *filename; - int copyfd; - int mods, oflags, pipefd, killed; - pid_t catpid; + const char *filename; /* non-null iff this fd has been specified */ + int copyfd; /* fd to copy, -1 unless mods & fdm_fd */ + int mods, oflags, pipefd, killed; /* 0,0,-1,0 unless otherwise set */ + pid_t catpid; /* -1 indicates no cat process */ }; enum signalsexitspecials { se_number=-100, se_numbernocore, se_highbit, se_stdout }; @@ -137,8 +108,9 @@ enum overridetypes { ot_none, ot_string, ot_file, ot_builtin }; struct constkeyvaluepair { const char *key, *value; }; +/* Variables from command-line arguments */ static const char *serviceuser; -static uid_t serviceuid, myuid; +static uid_t serviceuid; static struct fdsetupstate *fdsetup; static int fdsetupsize; static struct constkeyvaluepair *defvararray; @@ -149,7 +121,18 @@ static int sigpipeok, hidecwd; static int overridetype= ot_none; static const char *overridevalue, *spoofuser=0; +/* Other state variables */ static FILE *srfile, *swfile; +static pid_t mypid; +static uid_t myuid, spoofuid; +static gid_t mygid, spoofgid, *gidarray; +static int ngids; +static struct opening_msg opening_mbuf; +static const char *logname; +static char *cwdbuf; +static size_t cwdbufsize; +static char *ovbuf; +static int ovused, systemerror; static void blocksignals(int how) { sigset_t set; @@ -168,6 +151,12 @@ static void blocksignals(int how) { } } +/* Functions which may be called either from signal handlers or from + * the main thread. They block signals in case they are on the main + * thread, and may only use signal handler objects. None of them + * return. If they did they'd have to restore the signal mask. + */ + static void NONRETURNPRINTFFORMAT(1,2) miscerror(const char *fmt, ...) { va_list al; @@ -217,6 +206,33 @@ static void NONRETURNPRINTFFORMAT(1,2) protoerror(const char *fmt, ...) { exit(-1); } +/* + * General-purpose functions; these do nothing special about signals, + * except that they can call error-handlers which may block them + * to print error messages. + */ + +static void xfread(void *p, size_t sz, FILE *file) { + size_t nr; + nr= working_fread(p,sz,file); + if (nr != sz) protoreaderror(file,"in data"); +} + +static void xfwrite(const void *p, size_t sz, FILE *file) { + size_t nr; + nr= fwrite(p,1,sz,file); if (nr == sz) return; + syscallerror("writing to server"); +} + +static void xfflush(FILE *file) { + if (fflush(file)) syscallerror("flush server socket"); +} + +/* Functions which may be called only from the main thread. These may + * use main-thread objects and must block signals before using signal + * handler objects. + */ + #ifdef DEBUG static void priv_suspend(void) { } static void priv_resume(void) { } @@ -238,6 +254,52 @@ static void priv_permanentlyrevokesuspended(void) { } #endif +static void checkmagic(unsigned long was, unsigned long should, const char *when) { + if (was != should) + protoerror("magic number %s was %08lx, expected %08lx",when,was,should); +} + +static void getprogress(struct progress_msg *progress_r, FILE *file) { + int i, c; + unsigned long ul; + + for (;;) { + xfread(progress_r,sizeof(struct progress_msg),file); + checkmagic(progress_r->magic,PROGRESS_MAGIC,"in progress message"); + switch (progress_r->type) { + case pt_failed: + blocksignals(SIG_BLOCK); + fputs("userv: uservd reports that service failed\n",stderr); + exit(-1); + case pt_errmsg: + blocksignals(SIG_BLOCK); + fputs("uservd: ",stderr); + if (progress_r->data.errmsg.messagelen>MAX_ERRMSG_STRING) + protoerror("stderr message length %d is far too long", + progress_r->data.errmsg.messagelen); + for (i=0; idata.errmsg.messagelen; i++) { + c= working_getc(file); + if (c==EOF) protoreaderror(file,"in error message"); + if (isprint(c)) putc(c,stderr); + else fprintf(stderr,"\\x%02x",(unsigned char)c); + } + putc('\n',stderr); + if (ferror(stderr)) syscallerror("printing error message"); + xfread(&ul,sizeof(ul),file); + checkmagic(ul,PROGRESS_ERRMSG_END_MAGIC,"after error message"); + blocksignals(SIG_UNBLOCK); + break; + default: + return; + } + } +} + +/* + * Functions which are called only during setup, before + * the signal asynchronicity starts. They can do anything they like. + */ + static void *xmalloc(size_t s) { void *p; p= malloc(s?s:1); @@ -251,18 +313,6 @@ static void *xrealloc(void *p, size_t s) { return p; } -static void xfread(void *p, size_t sz, FILE *file) { - size_t nr; - nr= fread(p,1,sz,file); - if (nr != sz) protoreaderror(file,"in data"); -} - -static void xfwrite(const void *p, size_t sz, FILE *file) { - size_t nr; - nr= fwrite(p,1,sz,file); if (nr == sz) return; - syscallerror("writing to server"); -} - static void xfwritestring(const char *s, FILE *file) { int l; l= strlen(s); @@ -271,12 +321,110 @@ static void xfwritestring(const char *s, FILE *file) { xfwrite(s,sizeof(*s)*l,file); } -static void xfflush(FILE *file) { - if (fflush(file)) syscallerror("flush server socket"); +static void xfwritefds(int modifier, int expected, FILE *file) { + int i, fdcount; + + for (i=0, fdcount=0; i=fdsetupsize) continue; /* perhaps the caller gave us children */ + if ((WIFEXITED(status) && WEXITSTATUS(status)==0) || + (WIFSIGNALED(status) && WTERMSIG(status)==SIGPIPE) || + (fdsetup[fd].killed && WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL)) { + if (swfile && fdsetup[fd].mods & fdm_read) { + memset(&event_mbuf,0,sizeof(event_mbuf)); + event_mbuf.magic= EVENT_MAGIC; + event_mbuf.type= et_closereadfd; + event_mbuf.data.closereadfd.fd= fd; + r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile); + if (r != sizeof(event_mbuf) || fflush(swfile)) + if (errno != EPIPE) syscallerror("inform service of closed read fd"); + } + } else { + if (WIFEXITED(status)) + fprintf(stderr,"userv: cat for fd %d exited with error exit status %d\n", + fd,WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + if (WCOREDUMP(status)) + fprintf(stderr,"userv: cat for fd %d dumped core due to signal %s (%d)\n", + fd,strsignal(WTERMSIG(status)),WTERMSIG(status)); + else + fprintf(stderr,"userv: cat for fd %d terminated by signal %s (%d)\n", + fd,strsignal(WTERMSIG(status)),WTERMSIG(status)); + else + fprintf(stderr,"userv: cat for fd %d gave unknown wait status %d\n", + fd,status); + disconnect(); + } + fdsetup[fd].catpid= -1; + } + errno= es; +} + +/* + * Argument parsing. These functions which are called only during + * setup, before the signal asynchronicity starts. + */ + +struct optioninfo; + +typedef void optionfunction(const struct optioninfo*, const char *value, char *key); + +struct optioninfo { + int abbrev; + const char *full; + int values; /* 0: no value; 1: single value; 2: key and value */ + optionfunction *fn; +}; + static void usage(void) { - if (fprintf(stderr, + if (fputs( "usage: userv [--] [ ...]\n" "usage: userv -B|--builtin [--] [ ...]\n" "options: -f|--file []=\n" @@ -290,22 +438,72 @@ static void usage(void) { " --spoof-user } or same user\n" "fdmodifiers: read write overwrite trunc[ate]\n" "(separate with commas) append sync excl[usive] creat[e] fd\n\n" - "userv and uservd version " VERSION "; copyright (C)1996-1997 Ian Jackson.\n" - "there is NO WARRANTY; type `userv --copyright' for details.\n") - == EOF) syscallerror("write usage to stderr"); + "userv and uservd version " VERSION VEREXT "; copyright (C)1996-1997 Ian Jackson.\n" + "there is NO WARRANTY; type `userv --copyright' for details.\n", + stderr) < 0) + syscallerror("write usage to stderr"); } static void NONRETURNPRINTFFORMAT(1,2) usageerror(const char *fmt, ...) { va_list al; va_start(al,fmt); - fprintf(stderr,"userv: "); + fputs("userv: ",stderr); vfprintf(stderr,fmt,al); - fprintf(stderr,"\n\n"); + fputs("\n\n",stderr); usage(); exit(-1); } -static void addfdmodifier(struct fdsetupstate *fdsus, int fd, const char *key) { +static const struct fdmodifierinfo fdmodifierinfos[]= { + { "read", fdm_read, + fdm_write, + O_RDONLY }, + { "write", fdm_write, + fdm_read, + O_WRONLY }, + { "overwrite", fdm_write|fdm_create|fdm_truncate, + fdm_read|fdm_fd|fdm_exclusive, + O_WRONLY|O_CREAT|O_TRUNC }, + { "create", fdm_write|fdm_create, + fdm_read|fdm_fd, + O_WRONLY|O_CREAT }, + { "creat", fdm_write|fdm_create, + fdm_read|fdm_fd, + O_WRONLY|O_CREAT }, + { "exclusive", fdm_write|fdm_create|fdm_exclusive, + fdm_read|fdm_fd|fdm_truncate, + O_WRONLY|O_CREAT|O_EXCL }, + { "excl", fdm_write|fdm_create|fdm_exclusive, + fdm_read|fdm_fd|fdm_truncate, + O_WRONLY|O_CREAT|O_EXCL }, + { "truncate", fdm_write|fdm_truncate, + fdm_read|fdm_fd|fdm_exclusive, + O_WRONLY|O_CREAT|O_EXCL }, + { "trunc", fdm_write|fdm_truncate, + fdm_read|fdm_fd|fdm_exclusive, + O_WRONLY|O_CREAT|O_EXCL }, + { "append", fdm_write|fdm_append, + fdm_read|fdm_fd, + O_WRONLY|O_CREAT|O_APPEND }, + { "sync", fdm_write|fdm_sync, + fdm_read|fdm_fd, + O_WRONLY|O_CREAT|O_SYNC }, + { "wait", fdm_wait, + fdm_nowait|fdm_close, + 0 }, + { "nowait", fdm_nowait, + fdm_wait|fdm_close, + 0 }, + { "close", fdm_close, + fdm_wait|fdm_nowait, + 0 }, + { "fd", fdm_fd, + fdm_create|fdm_exclusive|fdm_truncate|fdm_append|fdm_sync, + 0 }, + { 0 } +}; + +static void addfdmodifier(int fd, const char *key) { const struct fdmodifierinfo *fdmip; if (!*key) return; @@ -348,11 +546,13 @@ static void of_file(const struct optioninfo *oip, const char *value, char *key) fdsetup= xrealloc(fdsetup,sizeof(struct fdsetupstate)*fdsetupsize); while (oldarraysize < fdsetupsize) { fdsetup[oldarraysize].filename= 0; + fdsetup[oldarraysize].pipefd= -1; fdsetup[oldarraysize].copyfd= -1; fdsetup[oldarraysize].mods= 0; + fdsetup[oldarraysize].oflags= 0; fdsetup[oldarraysize].catpid= -1; fdsetup[oldarraysize].killed= 0; - fdsetup[oldarraysize++].filename= 0; + fdsetup[oldarraysize].filename= 0; oldarraysize++; } } @@ -364,15 +564,15 @@ static void of_file(const struct optioninfo *oip, const char *value, char *key) key= delim; delim= strchr(key,','); if (delim) *delim++= 0; - addfdmodifier(&fdsetup[fd],fd,key); + addfdmodifier(fd,key); } if (!(fdsetup[fd].mods & (fdm_read|fdm_write))) { - if (fd != 1 && fd != 2) { - addfdmodifier(&fdsetup[fd],fd,"read"); + if (fd == 0) { + addfdmodifier(fd,"read"); } else if (fdsetup[fd].mods & fdm_fd) { - addfdmodifier(&fdsetup[fd],fd,"write"); + addfdmodifier(fd,"write"); } else { - addfdmodifier(&fdsetup[fd],fd,"overwrite"); + addfdmodifier(fd,"overwrite"); } } if (fdsetup[fd].mods & fdm_fd) { @@ -381,13 +581,13 @@ static void of_file(const struct optioninfo *oip, const char *value, char *key) copyfd= strtoul(value,&delim,0); if (*delim) usageerror("value part of argument to --file with fd modifier must be " - "numeric or fd name- `%s' is not recognised",value); + "numeric or fd name - `%s' is not recognised",value); else if (copyfd > MAX_ALLOW_FD) usageerror("file descriptor %lu named as target of file descriptor redirection" " (for file descriptor %lu) is larger than maximum allowed (%d)", copyfd,fd,MAX_ALLOW_FD); } - do { r= fstat(copyfd,&stab); } while (r && errno==EINTR); + r= fstat(copyfd,&stab); if (r) { if (oip) syscallerror("check filedescriptor %lu (named as target of file " "descriptor redirection for %lu)",copyfd,fd); @@ -408,7 +608,7 @@ static void of_fdwait(const struct optioninfo *oip, const char *value, char *key ul= strtoul(key,&delim,0); if (*delim) usageerror("first part of argument to --fdwait must be " "numeric or fd name - `%s' is not recognised",key); - if (ul>INT_MAX) usageerror("first part of argument to --fdwait is far too large"); + if (ul>MAX_ALLOW_FD) usageerror("first part of argument to --fdwait is too large"); fd= ul; } if (fd >= fdsetupsize || !fdsetup[fd].filename) @@ -442,14 +642,18 @@ static void of_defvar(const struct optioninfo *oip, const char *value, char *key static void of_timeout(const struct optioninfo *oip, const char *value, char *key) { char *endp; - timeout= strtoul(value,&endp,0); + unsigned long ul; + + ul= strtoul(value,&endp,0); if (*endp) usageerror("timeout value `%s' must be a plain decimal string",value); - if (timeout>INT_MAX) usageerror("timeout value %lu too large",timeout); + if (ul>INT_MAX) usageerror("timeout value %lu too large",ul); + timeout= ul; } static void of_signals(const struct optioninfo *oip, const char *value, char *key) { unsigned long numvalue; char *endp; + numvalue= strtoul(value,&endp,0); if (*endp) { if (!strcmp(value,"number")) signalsexit= se_number; @@ -478,7 +682,7 @@ static void of_help(const struct optioninfo *oip, const char *value, char *key) } static void of_copyright(const struct optioninfo *oip, const char *value, char *key) { - if (fprintf(stdout, + if (fputs( " userv - user service daemon and client; copyright (C)1996-1997 Ian Jackson\n\n" " This is free software; you can redistribute it and/or modify it under the\n" " terms of the GNU General Public License as published by the Free Software\n" @@ -491,8 +695,8 @@ static void of_copyright(const struct optioninfo *oip, const char *value, char * " You should have received a copy of the GNU General Public License along\n" " with userv; if not, write to Ian Jackson or\n" " to the Free Software Foundation, 59 Temple Place - Suite 330, Boston,\n" -" MA 02111-1307, USA.\n" - ) == EOF) syscallerror("write usage to stderr"); +" MA 02111-1307, USA.\n", + stdout) < 0) syscallerror("write usage to stderr"); exit(0); } @@ -537,13 +741,14 @@ static void callvalueoption(const struct optioninfo *oip, char *arg) { char *equals; if (oip->values == 2) { equals= strchr(arg,'='); - if (!equals) + if (!equals) { if (oip->abbrev) usageerror("option --%s (-%c) passed argument `%s' with no `='", oip->full,oip->abbrev,arg); else usageerror("option --%s passed argument `%s' with no `='", oip->full,arg); + } *equals++= 0; (oip->fn)(oip,equals,arg); } else { @@ -551,180 +756,41 @@ static void callvalueoption(const struct optioninfo *oip, char *arg) { } } -static void checkmagic(unsigned long was, unsigned long should, const char *when) { - if (was != should) - protoerror("magic number %s was %08lx, expected %08lx",when,was,should); -} - -static void getprogress(struct progress_msg *progress_r, FILE *file) { - int i, c; - unsigned long ul; - - for (;;) { - xfread(progress_r,sizeof(struct progress_msg),file); - switch (progress_r->type) { - case pt_failed: - blocksignals(SIG_BLOCK); - fputs("userv: uservd reports that service failed\n",stderr); - exit(-1); - case pt_errmsg: - blocksignals(SIG_BLOCK); - fputs("uservd: ",stderr); - if (progress_r->data.errmsg.messagelen>4096) - protoerror("stderr message length %d is far too long", - progress_r->data.errmsg.messagelen); - for (i=0; idata.errmsg.messagelen; i++) { - c= getc(file); if (c==EOF) protoreaderror(file,"in error message"); - if (isprint(c)) putc(c,stderr); - else fprintf(stderr,"\\x%02x",(unsigned char)c); - } - putc('\n',stderr); - if (ferror(stderr)) syscallerror("printing error message"); - xfread(&ul,sizeof(ul),file); - checkmagic(ul,PROGRESS_ERRMSG_END_MAGIC,"after error message"); - blocksignals(SIG_UNBLOCK); - break; - default: - return; - } - } -} - -static void xfwritefds(int modifier, int expected, FILE *file) { - int i, fdcount; - - for (i=0, fdcount=0; i=fdsetupsize) continue; /* perhaps the invoker gave us children */ - if ((WIFEXITED(status) && WEXITSTATUS(status)==0) || - (WIFSIGNALED(status) && WTERMSIG(status)==SIGPIPE) || - (fdsetup[fd].killed && WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL)) { - if (swfile && fdsetup[fd].mods & fdm_read) { - memset(&event_mbuf,0,sizeof(event_mbuf)); - event_mbuf.magic= EVENT_MAGIC; - event_mbuf.type= et_closereadfd; - r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile); - if (r != sizeof(event_mbuf) || fflush(swfile)) - if (errno != EPIPE) syscallerror("inform service of closed read fd"); - } - } else { - if (WIFEXITED(status)) - fprintf(stderr,"userv: cat for fd %d exited with error exit status %d\n", - fd,WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - if (WCOREDUMP(status)) - fprintf(stderr,"userv: cat for fd %d dumped core due to signal %s (%d)\n", - fd,strsignal(WTERMSIG(status)),WTERMSIG(status)); - else - fprintf(stderr,"userv: cat for fd %d terminated by signal %s (%d)\n", - fd,strsignal(WTERMSIG(status)),WTERMSIG(status)); - else - fprintf(stderr,"userv: cat for fd %d gave unknown wait status %d\n", - fd,status); - disconnect(); - } - fdsetup[fd].catpid= -1; - } - errno= es; -} +static void security_init(void) { + /* May not open any file descriptors. */ + + mypid= getpid(); if (mypid == (pid_t)-1) syscallerror("getpid"); + myuid= getuid(); if (myuid == (uid_t)-1) syscallerror("getuid"); + mygid= getgid(); if (mygid == (gid_t)-1) syscallerror("getgid"); + ngids= getgroups(0,0); if (ngids == (gid_t)-1) syscallerror("getgroups(0,0)"); + gidarray= xmalloc(sizeof(gid_t)*ngids); + if (getgroups(ngids,gidarray) != ngids) syscallerror("getgroups(ngids,)"); + + priv_suspend(); -static void catdup(const char *which, int from, int to) { - if (dup2(from,to)<0) { - blocksignals(SIG_BLOCK); - fprintf(stderr,"userv: %s: cannot dup for %s: %s\n",which, - to?"stdout":"stdin", strerror(errno)); - exit(-1); - } + if (ngids > MAX_GIDS) miscerror("caller is in far too many gids"); } -int main(int argc, char *const *argv) { +static void parse_arguments(int *argcp, char *const **argvp) { static char fd0key[]= "stdin,fd,read"; static char fd1key[]= "stdout,fd,write"; static char fd2key[]= "stderr,fd,write"; - static char stderrbuf[BUFSIZ], stdoutbuf[1024]; - + char *const *argpp; char *argp; const struct optioninfo *oip; - struct sockaddr_un ssockname; - int sfd, ngids, i, tempfd, l, c, reading, fd, r, status, ngidssize; - sigset_t sset; - unsigned long ul; - size_t cwdbufsize; - char *cwdbuf, **mem; - struct opening_msg opening_mbuf; - struct request_msg request_mbuf; - struct progress_msg progress_mbuf; - struct event_msg event_mbuf; - struct passwd *pw; - struct group *gr; - gid_t mygid, spoofgid, *gidarray; - uid_t spoofuid; - pid_t mypid; - const char *logname; - FILE *ovfile; - char *ovbuf; - int ovavail, ovused; - char pipepathbuf[PIPEPATHMAXLEN], catnamebuf[sizeof(int)*3+30]; - struct sigaction sig; - -#ifdef NDEBUG -# error Do not disable assertions in this security-critical code ! -#endif - - mypid= getpid(); if (mypid == (pid_t)-1) syscallerror("getpid"); - myuid= getuid(); if (myuid == (uid_t)-1) syscallerror("getuid"); - mygid= getgid(); if (mygid == (gid_t)-1) syscallerror("getgid"); - ngids= getgroups(0,0); if (ngids == (gid_t)-1) syscallerror("getgroups(0,0)"); - gidarray= xmalloc(sizeof(gid_t)*ngids); - if (getgroups(ngids,gidarray) != ngids) syscallerror("getgroups(ngids,)"); - priv_suspend(); + int fd; - assert(argv[0]); + assert((*argvp)[0]); of_file(0,"stdin",fd0key); of_file(0,"stdout",fd1key); of_file(0,"stderr",fd2key); - for (argpp= argv+1; + for (argpp= *argvp+1; (argp= *argpp) && *argp == '-' && argp[1]; argpp++) { if (!*++argp) usageerror("unknown option/argument `%s'",*argpp); @@ -768,6 +834,11 @@ int main(int argc, char *const *argv) { if (!*argpp) usageerror(overridetype == ot_builtin ? "no service name given after options and service user" : "no builtin service given after options"); + + *argcp-= (argpp-*argvp); + *argvp= argpp; + + if (*argcp > MAX_ARGSDEFVAR) usageerror("far too many arguments"); for (fd=0; fd MAX_ARGSDEFVAR) usageerror("far too many arguments"); - if (ngids > MAX_GIDS) miscerror("caller is in far too many gids"); +static void determine_users(void) { + int ngidssize; + char **mem; + struct passwd *pw; + struct group *gr; spoofuid= myuid; spoofgid= mygid; @@ -824,24 +892,33 @@ int main(int argc, char *const *argv) { for (mem= gr->gr_mem; *mem && strcmp(*mem,logname); mem++); if (!*mem) continue; if (ngids>=ngidssize) { + if (ngids>=MAX_GIDS) miscerror("spoofed user is member of too many groups"); ngidssize= (ngids+5)<<1; gidarray= xrealloc(gidarray,sizeof(gid_t)*ngidssize); } gidarray[ngids++]= gr->gr_gid; } } +} +static void determine_cwd(void) { cwdbufsize= 0; cwdbuf= 0; - if (!hidecwd) { - for (;;) { - assert(cwdbufsize < INT_MAX/3); - cwdbufsize <<= 1; cwdbufsize+= 100; - cwdbuf= xrealloc(cwdbuf,cwdbufsize); - cwdbuf[cwdbufsize-1]= 0; - if (getcwd(cwdbuf,cwdbufsize-1)) { cwdbufsize= strlen(cwdbuf); break; } - if (errno != ERANGE) { cwdbufsize= 0; break; } - } + + if (hidecwd) return; + + for (;;) { + if (cwdbufsize > MAX_GENERAL_STRING) { cwdbufsize= 0; free(cwdbuf); break; } + cwdbufsize <<= 1; cwdbufsize+= 100; + cwdbuf= xrealloc(cwdbuf,cwdbufsize); + cwdbuf[cwdbufsize-1]= 0; + if (getcwd(cwdbuf,cwdbufsize-1)) { cwdbufsize= strlen(cwdbuf); break; } + if (errno != ERANGE) { cwdbufsize= 0; free(cwdbuf); break; } } +} + +static void process_override(const char *servicename) { + FILE *ovfile; + int ovavail, l, c; switch (overridetype) { case ot_none: @@ -849,13 +926,13 @@ int main(int argc, char *const *argv) { ovbuf= 0; break; case ot_builtin: - l= strlen(argv[0]); + l= strlen(servicename); if (l >= MAX_OVERRIDE_LEN-20) miscerror("builtin service string is too long (%d, max is %d)", l,MAX_OVERRIDE_LEN-21); l+= 20; ovbuf= xmalloc(l); - snprintf(ovbuf,l,"execute-builtin %s\n",argv[0]); + snprintf(ovbuf,l,"execute-builtin %s\n",servicename); ovused= strlen(ovbuf); break; case ot_string: @@ -887,6 +964,19 @@ int main(int argc, char *const *argv) { default: abort(); } +} + +static int server_connect(void) { + struct sockaddr_un ssockname; + int sfd; + struct sigaction sig; + sigset_t sset; + + sigemptyset(&sset); + sigaddset(&sset,SIGCHLD); + sigaddset(&sset,SIGALRM); + sigaddset(&sset,SIGPIPE); + if (sigprocmask(SIG_UNBLOCK,&sset,0)) syscallerror("preliminarily unblock signals"); sig.sa_handler= SIG_IGN; sigemptyset(&sig.sa_mask); @@ -903,10 +993,14 @@ int main(int argc, char *const *argv) { while (connect(sfd,(struct sockaddr*)&ssockname,sizeof(ssockname))) { if (errno == ECONNREFUSED || errno == ENOENT) syscallerror("uservd daemon is not running - service not available"); - syscallerror("unable to connect to uservd daemon"); + if (errno != EINTR) + syscallerror("unable to connect to uservd daemon: %m"); } - priv_suspend(); + return sfd; +} + +static void server_handshake(int sfd) { srfile= fdopen(sfd,"r"); if (!srfile) syscallerror("turn socket fd into FILE* for read"); if (setvbuf(srfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket reads"); @@ -919,32 +1013,46 @@ int main(int argc, char *const *argv) { checkmagic(opening_mbuf.magic,OPENING_MAGIC,"in opening message"); if (memcmp(protocolchecksumversion,opening_mbuf.protocolchecksumversion,PCSUMSIZE)) protoerror("protocol version checksum mismatch - server not same as client"); +} +static void server_preparepipes(void) { + char pipepathbuf[PIPEPATHMAXLEN+2]; + int fd, tempfd; + for (fd=0; fd