X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=userv.git;a=blobdiff_plain;f=process.c;h=c88c3c9bee277d2551f446f2fe7ad22889e2ab38;hp=109c5010dfd6deacd70e3fb68a2074f81890943e;hb=d9cedcfbd2194ff9046d71400cb7878085c26499;hpb=464d71c37246e556de9ec05f7b97af834a5224ee diff --git a/process.c b/process.c index 109c501..c88c3c9 100644 --- a/process.c +++ b/process.c @@ -19,7 +19,8 @@ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* We do some horrible asynchronous stuff with signals. +/* + * 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 @@ -40,7 +41,6 @@ #include #include #include -#include #include #include #include @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -178,7 +179,12 @@ static void getevent(struct event_msg *event_r) { switch (event_r->type) { case et_closereadfd: fd= event_r->data.closereadfd.fd; - assert(fd= fdarrayused) { + blocksignals(); + syslog(LOG_ERR,"client sent bad file descriptor %d to close (max %d)", + fd,fdarrayused-1); + disconnect(12); + } if (fdarray[fd].holdfd!=-1) { if (close(fdarray[fd].holdfd)) syscallerror("cannot close holding fd"); fdarray[fd].holdfd= -1; @@ -358,20 +364,6 @@ void senderrmsgstderr(const char *errmsg) { xfflush(swfile); } -static void getgroupnames(int ngids, gid_t *list, const char ***names_r) { - const char **names; - struct group *cgrp; - int i; - - names= xmalloc(sizeof(char*)*ngids); - for (i=0; igr_name); - } - *names_r= names; -} - /* The per-request main program and its subfunctions. */ static void setup_comms(int sfd) { @@ -484,6 +476,20 @@ static void establish_pipes(void) { } } +static void groupnames(int ngids, gid_t *gids, const char ***names_r) { + const char **names; + struct group *gr; + int i; + + names= xmalloc(sizeof(char*)*ngids); + for (i=0; igr_name); + } + *names_r= names; +} + static void lookup_uidsgids(void) { struct passwd *pw; @@ -513,22 +519,55 @@ static void lookup_uidsgids(void) { if (getgroups(service_ngids,service_gids+1) != service_ngids) syscallerror("getgroups(size,list)"); - getgroupnames(service_ngids,service_gids,&service_groups); - getgroupnames(request_mbuf.ngids,calling_gids,&calling_groups); + groupnames(request_mbuf.ngids,calling_gids,&calling_groups); + groupnames(service_ngids,service_gids,&service_groups); } -static void check_find_executable(void) { - int r, partsize; +static void findinpath(char *program) { char *part, *exectry; const char *string, *delim, *nextstring; struct stat stab; + int r, partsize; + + if (strchr(program,'/')) { + r= stat(program,&stab); + if (r) syscallfailure("failed check for program (containing slash) `%s'",program); + execpath= program; + } else { + string= getenv("PATH"); + if (!string) string= defaultpath(); + while (string) { + delim= strchr(string,':'); + if (delim) { + if (delim-string > MAX_GENERAL_STRING) + failure("execute-from-path, but PATH component too long"); + partsize= delim-string; + nextstring= delim+1; + } else { + partsize= strlen(string); + nextstring= 0; + } + part= xstrsubsave(string,partsize); + exectry= part[0] ? xstrcat3save(part,"/",program) : xstrsave(program); + free(part); + r= stat(exectry,&stab); + if (!r) { execpath= exectry; break; } + free(exectry); + string= nextstring; + } + if (!execpath) failure("program `%s' not found on default PATH",program); + } +} + +static void check_find_executable(void) { + struct stat stab; + int r; switch (execute) { case tokv_word_reject: failure("request rejected"); case tokv_word_execute: - r= stat(execpath,&stab); - if (r) syscallfailure("checking for executable `%s'",execpath); + findinpath(execpath); break; case tokv_word_executefromdirectory: r= stat(execpath,&stab); @@ -537,41 +576,37 @@ static void check_find_executable(void) { case tokv_word_executebuiltin: break; case tokv_word_executefrompath: - if (strchr(service,'/')) { - r= stat(service,&stab); - if (r) syscallfailure("execute-from-path (contains slash)" - " cannot check for executable `%s'",service); - execpath= service; - } else { - string= getenv("PATH"); - if (!string) string= defaultpath(); - while (string) { - delim= strchr(string,':'); - if (delim) { - if (delim-string > MAX_GENERAL_STRING) - failure("execute-from-path, but PATH component too long"); - partsize= delim-string; - nextstring= delim+1; - } else { - partsize= strlen(string); - nextstring= 0; - } - part= xstrsubsave(string,partsize); - exectry= part[0] ? xstrcat3save(part,"/",service) : xstrsave(service); - free(part); - r= stat(exectry,&stab); - if (!r) { execpath= exectry; break; } - free(exectry); - string= nextstring; - } - if (!execpath) failure("execute-from-path, but program `%s' not found",service); - } + findinpath(service); break; default: abort(); } } +static void makenonexistentfd(int fd) { + if (fdarray[fd].realfd == -1) { + assert(fdarray[fd].holdfd == -1); + } else { + if (close(fdarray[fd].realfd)) + syscallfailure("close unwanted file descriptor %d",fd); + fdarray[fd].realfd= -1; + + if (fdarray[fd].holdfd != -1) { + if (close(fdarray[fd].holdfd)) + syscallfailure("close unwanted hold descriptor for %d",fd); + } + } +} + +static void makenullfd(int fd) { + fdarray[fd].realfd= open("/dev/null", + fdarray[fd].wantrw == tokv_word_read ? O_RDONLY : + fdarray[fd].wantrw == tokv_word_write ? O_WRONLY : + 0); + if (fdarray[fd].realfd<0) + syscallfailure("cannot open /dev/null for null or allowed, unprovided fd"); +} + static void check_fds(void) { int fd; @@ -589,35 +624,27 @@ static void check_fds(void) { failure("file descriptor %d provided but rejected",fd); break; case tokv_word_ignorefd: - if (fdarray[fd].realfd != -1) - if (close(fdarray[fd].realfd)) - syscallfailure("close unwanted file descriptor %d",fd); - fdarray[fd].realfd= -1; + makenonexistentfd(fd); break; case tokv_word_nullfd: - if (fdarray[fd].realfd != -1) close(fdarray[fd].realfd); - fdarray[fd].realfd= open("/dev/null", - fdarray[fd].iswrite == -1 ? O_RDWR : - fdarray[fd].iswrite ? O_WRONLY : O_RDONLY); - if (fdarray[fd].realfd<0) - syscallfailure("cannot open /dev/null for null fd"); + makenonexistentfd(fd); + makenullfd(fd); break; case tokv_word_requirefd: if (fdarray[fd].realfd == -1) - failure("file descriptor %d not provided but required",fd); + failure("file descriptor %d required but not provided",fd); + assert(fdarray[fd].holdfd == -1); /* fall through */ case tokv_word_allowfd: if (fdarray[fd].realfd == -1) { - fdarray[fd].iswrite= (fdarray[fd].wantrw == tokv_word_write); - fdarray[fd].realfd= open("/dev/null",fdarray[fd].iswrite ? O_WRONLY : O_RDONLY); - if (fdarray[fd].realfd<0) - syscallfailure("cannot open /dev/null for allowed but not provided fd"); + assert(fdarray[fd].holdfd == -1); + makenullfd(fd); } else { if (fdarray[fd].iswrite) { - if (fdarray[fd].wantrw != tokv_word_write) + if (fdarray[fd].wantrw == tokv_word_read) failure("file descriptor %d provided write, wanted read",fd); } else { - if (fdarray[fd].wantrw != tokv_word_read) + if (fdarray[fd].wantrw == tokv_word_write) failure("file descriptor %d provided read, wanted write",fd); } } @@ -644,6 +671,13 @@ static void fork_service_synch(void) { r= socketpair(AF_UNIX,SOCK_STREAM,0,synchsocket); if (r) syscallerror("cannot create socket for synch"); + /* Danger here. Firstly, we start handling signals asynchronously. + * Secondly after we fork the service we want it to put + * itself in a separate process group so that we can kill it and all + * its children - but, we mustn't kill the whole pgrp before it has + * done that (or we kill ourselves) and it mustn't fork until it + * knows that we are going to kill it the right way ... + */ sig.sa_handler= sighandler_chld; sigemptyset(&sig.sa_mask); sigaddset(&sig.sa_mask,SIGCHLD);