6 * Copyright 1996-2017 Ian Jackson <ian@davenant.greenend.org.uk>.
7 * Copyright 2000 Ben Harris <bjh21@cam.ac.uk>
8 * Copyright 2016-2017 Peter Benie <pjb1008@cam.ac.uk>
10 * This is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with userv; if not, see <http://www.gnu.org/licenses/>.
25 * Here too, we do some horrible asynchronous stuff with signals.
27 * The following objects &c. are used in signal handlers and so
28 * must be protected by calls to blocksignals if they are used in
33 * The following objects are used in the main program unprotected
34 * and so must not be used in signal handlers:
39 * The following objects/functions are not modified/called after the
40 * asynchronicity starts:
46 * results of argument parsing
48 * systemerror, swfile, fdsetup[].catpid and fdsetup[].killed are used
49 * for communication between the main thread and the signal handlers.
51 * All the signal handlers save errno so that is OK too.
68 #include <sys/types.h>
70 #include <sys/socket.h>
72 #include <sys/resource.h>
80 enum fdmodifiervalues {
94 struct fdmodifierinfo {
101 struct fdsetupstate {
102 const char *filename; /* non-null iff this fd has been specified */
103 int copyfd; /* fd to copy, -1 unless mods & fdm_fd */
104 int mods, oflags, pipefd, killed; /* 0,0,-1,0 unless otherwise set */
105 pid_t catpid; /* -1 indicates no cat process */
108 enum signalsexitspecials { se_number=-100, se_numbernocore, se_highbit, se_stdout };
109 enum overridetypes { ot_none, ot_string, ot_file, ot_builtin };
111 struct constkeyvaluepair { const char *key, *value; };
113 /* Variables from command-line arguments */
114 static const char *serviceuser;
115 static uid_t serviceuid;
116 static struct fdsetupstate *fdsetup;
117 static int fdsetupsize;
118 static struct constkeyvaluepair *defvararray;
119 static int defvaravail, defvarused;
120 static unsigned long timeout;
121 static int signalsexit=254;
122 static int sigpipeok, hidecwd;
123 static int overridetype= ot_none;
124 static const char *overridevalue, *spoofuser=0;
126 /* Other state variables */
127 static FILE *srfile, *swfile;
129 static uid_t myuid, spoofuid;
130 static gid_t mygid, spoofgid, *gidarray;
132 static struct opening_msg opening_mbuf;
133 static const char *loginname;
135 static size_t cwdbufsize;
137 static int ovused, systemerror, socketfd;
139 static void blocksignals(int how) {
141 static const char blockerrmsg[]= "userv: failed to [un]block signals: ";
146 sigaddset(&set,SIGCHLD);
147 sigaddset(&set,SIGALRM);
148 if (sigprocmask(how,&set,0)) {
149 str= strerror(errno);
150 unused= write(2,blockerrmsg,sizeof(blockerrmsg)-1);
151 unused= write(2,str,strlen(str));
152 unused= write(2,"\n",1);
158 /* Functions which may be called either from signal handlers or from
159 * the main thread. They block signals in case they are on the main
160 * thread, and may only use signal handler objects. None of them
161 * return. If they did they'd have to restore the signal mask.
164 static void NONRETURNPRINTFFORMAT(1,2) miscerror(const char *fmt, ...) {
167 blocksignals(SIG_BLOCK);
169 fprintf(stderr,"userv: failure: ");
170 vfprintf(stderr,fmt,al);
171 fprintf(stderr,"\n");
175 static void NONRETURNPRINTFFORMAT(1,2) fsyscallerror(const char *fmt, ...) {
180 blocksignals(SIG_BLOCK);
182 fprintf(stderr,"userv: system call failure: ");
183 vfprintf(stderr,fmt,al);
184 fprintf(stderr,": %s\n",strerror(e));
188 void syscallerror(const char *what) {
189 fsyscallerror("%s",what);
192 static void NONRETURNING protoreaderror(FILE *file, const char *where) {
196 blocksignals(SIG_BLOCK);
198 fprintf(stderr,"userv: failure: read error %s: %s\n",where,strerror(e));
201 fprintf(stderr,"userv: internal failure: EOF from server %s\n",where);
206 static void NONRETURNPRINTFFORMAT(1,2) protoerror(const char *fmt, ...) {
209 blocksignals(SIG_BLOCK);
211 fprintf(stderr,"userv: internal failure: protocol error: ");
212 vfprintf(stderr,fmt,al);
213 fprintf(stderr,"\n");
218 * General-purpose functions; these do nothing special about signals,
219 * except that they can call error-handlers which may block them
220 * to print error messages.
223 static void xfread(void *p, size_t sz, FILE *file) {
225 nr= working_fread(p,sz,file);
226 if (nr != sz) protoreaderror(file,"in data");
229 static void xfwrite(const void *p, size_t sz, FILE *file) {
231 nr= fwrite(p,1,sz,file); if (nr == sz) return;
232 syscallerror("writing to server");
235 static void xfflush(FILE *file) {
236 if (fflush(file)) syscallerror("flush server socket");
239 /* Functions which may be called only from the main thread. These may
240 * use main-thread objects and must block signals before using signal
245 static void priv_suspend(void) { }
246 static void priv_resume(void) { }
247 static void priv_permanentlyrevokesuspended(void) { }
249 static void priv_suspend(void) {
250 if (setreuid(0,myuid) != 0) syscallerror("suspend root setreuid(0,myuid)");
252 static void priv_resume(void) {
253 if (setreuid(myuid,0) != 0) syscallerror("resume root setreuid(myuid,0)");
255 static void priv_permanentlyrevokesuspended(void) {
256 if (setreuid(myuid,myuid) != 0) syscallerror("revoke root setreuid(myuid,myuid)");
257 if (setreuid(myuid,myuid) != 0) syscallerror("rerevoke root setreuid(myuid,myuid)");
259 if (!setreuid(myuid,0)) miscerror("revoked root but setreuid(0,0) succeeded !");
260 if (errno != EPERM) syscallerror("revoked and setreuid(myuid,0) unexpected error");
265 static void checkmagic(unsigned long was, unsigned long should, const char *when) {
267 protoerror("magic number %s was %08lx, expected %08lx",when,was,should);
270 static void getprogress(struct progress_msg *progress_r, FILE *file) {
275 xfread(progress_r,sizeof(struct progress_msg),file);
276 checkmagic(progress_r->magic,PROGRESS_MAGIC,"in progress message");
277 switch (progress_r->type) {
279 blocksignals(SIG_BLOCK);
280 fputs("userv: uservd reports that service failed\n",stderr);
283 blocksignals(SIG_BLOCK);
284 fputs("uservd: ",stderr);
285 if (progress_r->data.errmsg.messagelen>MAX_ERRMSG_STRING)
286 protoerror("stderr message length %d is far too long",
287 progress_r->data.errmsg.messagelen);
288 for (i=0; i<progress_r->data.errmsg.messagelen; i++) {
289 c= working_getc(file);
290 if (c==EOF) protoreaderror(file,"in error message");
291 if (ISCHAR(isprint,c)) putc(c,stderr);
292 else fprintf(stderr,"\\x%02x",(unsigned char)c);
295 if (ferror(stderr)) syscallerror("printing error message");
296 xfread(&ul,sizeof(ul),file);
297 checkmagic(ul,PROGRESS_ERRMSG_END_MAGIC,"after error message");
298 blocksignals(SIG_UNBLOCK);
307 * Functions which are called only during setup, before
308 * the signal asynchronicity starts. They can do anything they like.
311 /* This includes xmalloc and xrealloc from both.c */
313 static void xfwritestring(const char *s, FILE *file) {
316 assert(l<=MAX_GENERAL_STRING);
317 xfwrite(&l,sizeof(l),file);
318 xfwrite(s,sizeof(*s)*l,file);
321 static void xfwritefds(int modifier, int expected, FILE *file) {
324 for (i=0, fdcount=0; i<fdsetupsize; i++) {
325 if (!(fdsetup[i].filename && (fdsetup[i].mods & modifier)))
327 xfwrite(&i,sizeof(int),file); fdcount++;
329 assert(fdcount == expected);
332 /* Functions which may be called from signal handlers. These
333 * may use signal-handler objects. The main program may only
334 * call them with signals blocked, and they may not use any
335 * main-thread objects.
338 static void disconnect(void) /* DOES return, unlike in daemon */ {
339 struct event_msg event_mbuf;
343 memset(&event_mbuf,0,sizeof(event_mbuf));
344 event_mbuf.magic= EVENT_MAGIC;
345 event_mbuf.type= et_disconnect;
346 r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile);
347 if ((r != sizeof(event_mbuf) || fflush(swfile)) && errno != EPIPE)
348 syscallerror("write to server when disconnecting");
353 static void sighandler_alrm(int ignored) /* DOES return, unlike in daemon */ {
356 fputs("userv: timeout\n",stderr);
361 static void sighandler_chld(int ignored) /* DOES return, unlike in daemon */ {
362 struct event_msg event_mbuf;
364 int status, fd, r, es;
368 child= wait3(&status,WNOHANG,0);
369 if (child == 0 || (child == -1 && errno == ECHILD)) break;
370 if (child == -1) syscallerror("wait for child process (in sigchld handler)");
371 for (fd=0; fd<fdsetupsize && fdsetup[fd].catpid != child; fd++);
372 if (fd>=fdsetupsize) continue; /* perhaps the caller gave us children */
373 if ((WIFEXITED(status) && WEXITSTATUS(status)==0) ||
374 (WIFSIGNALED(status) && WTERMSIG(status)==SIGPIPE) ||
375 (fdsetup[fd].killed && WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL)) {
376 if (swfile && fdsetup[fd].mods & fdm_read) {
377 memset(&event_mbuf,0,sizeof(event_mbuf));
378 event_mbuf.magic= EVENT_MAGIC;
379 event_mbuf.type= et_closereadfd;
380 event_mbuf.data.closereadfd.fd= fd;
381 r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile);
382 if (r != sizeof(event_mbuf) || fflush(swfile))
383 if (errno != EPIPE) syscallerror("inform service of closed read fd");
386 if (WIFEXITED(status))
387 fprintf(stderr,"userv: cat for fd %d exited with error exit status %d\n",
388 fd,WEXITSTATUS(status));
389 else if (WIFSIGNALED(status))
390 if (WCOREDUMP(status))
391 fprintf(stderr,"userv: cat for fd %d dumped core due to signal %s (%d)\n",
392 fd,strsignal(WTERMSIG(status)),WTERMSIG(status));
394 fprintf(stderr,"userv: cat for fd %d terminated by signal %s (%d)\n",
395 fd,strsignal(WTERMSIG(status)),WTERMSIG(status));
397 fprintf(stderr,"userv: cat for fd %d gave unknown wait status %d\n",
401 fdsetup[fd].catpid= -1;
407 * Argument parsing. These functions which are called only during
408 * setup, before the signal asynchronicity starts.
413 typedef void optionfunction(const struct optioninfo*, const char *value, char *key);
418 int values; /* 0: no value; 1: single value; 2: key and value */
422 static void usage(FILE *stream) {
424 "usage: userv <options> [--] <service-user> <service-name> [<argument> ...]\n"
425 "usage: userv <options> -B|--builtin [--] <builtin-service> [<info-argument> ...]\n"
426 "options: -f|--file <fd>[<fdmodifiers>]=<filename>\n"
427 " -D|--defvar <name>=<value>\n"
428 " -t|--timeout <seconds>\n"
429 " -S|--signals <status>|number|number-nocore|highbit|stdout\n"
430 " -w|--fdwait <fd>=wait|nowait|close\n"
431 " -P|--sigpipe -H|--hidecwd -h|--help|--version --copyright\n"
432 " --override <configuration-data> } available only\n"
433 " --override-file <filename> } to root\n"
434 " --spoof-user <username> } or same user\n"
435 "fdmodifiers: read write overwrite trunc[ate]\n"
436 "(separate with commas) append sync excl[usive] creat[e] fd\n"
437 "userv -B 'X' ... is same as userv --override 'execute-builtin X' - 'X' ...\n"
438 " for help, type `userv -B help'; remember to quote multi-word X\n"
439 "userv and uservd version " VERSION VEREXT ".\n"
442 syscallerror("write usage message");
445 static void NONRETURNPRINTFFORMAT(1,2) usageerror(const char *fmt, ...) {
448 fputs("userv: ",stderr);
449 vfprintf(stderr,fmt,al);
450 fputs("\n\n",stderr);
455 static const struct fdmodifierinfo fdmodifierinfos[]= {
459 { "write", fdm_write,
462 { "overwrite", fdm_write|fdm_create|fdm_truncate,
463 fdm_read|fdm_fd|fdm_exclusive,
464 O_WRONLY|O_CREAT|O_TRUNC },
465 { "create", fdm_write|fdm_create,
468 { "creat", fdm_write|fdm_create,
471 { "exclusive", fdm_write|fdm_create|fdm_exclusive,
472 fdm_read|fdm_fd|fdm_truncate,
473 O_WRONLY|O_CREAT|O_EXCL },
474 { "excl", fdm_write|fdm_create|fdm_exclusive,
475 fdm_read|fdm_fd|fdm_truncate,
476 O_WRONLY|O_CREAT|O_EXCL },
477 { "truncate", fdm_write|fdm_truncate,
478 fdm_read|fdm_fd|fdm_exclusive,
479 O_WRONLY|O_CREAT|O_EXCL },
480 { "trunc", fdm_write|fdm_truncate,
481 fdm_read|fdm_fd|fdm_exclusive,
482 O_WRONLY|O_CREAT|O_EXCL },
483 { "append", fdm_write|fdm_append,
485 O_WRONLY|O_CREAT|O_APPEND },
486 { "sync", fdm_write|fdm_sync,
488 O_WRONLY|O_CREAT|O_SYNC },
490 fdm_nowait|fdm_close,
492 { "nowait", fdm_nowait,
495 { "close", fdm_close,
499 fdm_create|fdm_exclusive|fdm_truncate|fdm_append|fdm_sync,
504 static void addfdmodifier(int fd, const char *key, size_t key_len) {
505 const struct fdmodifierinfo *fdmip;
508 for (fdmip= fdmodifierinfos;
510 !(strlen(fdmip->string) == key_len &&
511 !memcmp(fdmip->string,key,key_len));
513 if (!fdmip->string) usageerror("unknown fdmodifer `%.*s' for fd %d",
514 (int)key_len,key,fd);
515 if (fdmip->conflicts & fdsetup[fd].mods)
516 usageerror("fdmodifier `%.*s' conflicts with another for fd %d",
517 (int)key_len,key,fd);
518 fdsetup[fd].mods |= fdmip->implies;
519 fdsetup[fd].oflags |= fdmip->oflags;
522 static void addfdmodifier_fixed(int fd, const char *key) {
523 addfdmodifier(fd, key, strlen(key));
526 static int fdstdnumber(const char *string, const char **delim_r) {
527 #define FN(v,s) do{ \
528 if (!memcmp(string,s,sizeof(s)-1)) { \
529 *delim_r= string+sizeof(s)-1; \
541 static int strtofd(const char *string,
542 const char **mods_r /* 0: no modifiers, must go to end */,
549 fd= fdstdnumber(string,&mods);
551 if (*mods && *mods != ',')
552 usageerror("%s, when it is `stdin', `stdout' or `stderr',"
553 " must be delimited with a comma from any following"
554 " modifiers - `%s' is not permitted",
560 ul= strtoul(string,&delim_v,10);
561 if (errno || delim_v == string || ul > INT_MAX)
562 usageerror("%s must be must be numeric file descriptor"
563 " or `stdin', `stdout' or `stderr'"
564 " - `%s' is not recognized",
576 usageerror("%s must be must be only file descriptor"
577 " - trailing portion or modifiers `%s' not permitted",
580 if (fd > MAX_ALLOW_FD)
581 usageerror("%s file descriptor specified (%d)"
582 " is larger than maximum allowed (%d)",
583 what, fd, MAX_ALLOW_FD);
588 static void of_file(const struct optioninfo *oip, const char *value, char *key) {
589 unsigned long fd, copyfd;
593 const char *mods, *delim;
595 fd= strtofd(key,&mods,"first part of argument to -f or --file");
597 if (fd >= fdsetupsize) {
598 oldarraysize= fdsetupsize;
599 fdsetupsize+=2; fdsetupsize<<=1;
600 fdsetup= xrealloc(fdsetup,sizeof(struct fdsetupstate)*fdsetupsize);
601 while (oldarraysize < fdsetupsize) {
602 fdsetup[oldarraysize].filename= 0;
603 fdsetup[oldarraysize].pipefd= -1;
604 fdsetup[oldarraysize].copyfd= -1;
605 fdsetup[oldarraysize].mods= 0;
606 fdsetup[oldarraysize].oflags= 0;
607 fdsetup[oldarraysize].catpid= -1;
608 fdsetup[oldarraysize].killed= 0;
609 fdsetup[oldarraysize].filename= 0;
613 fdsetup[fd].filename= value;
614 fdsetup[fd].oflags= 0;
616 fdsetup[fd].copyfd= -1;
617 while (mods && *mods) {
618 delim= strchr(mods,',');
619 mod_len= delim ? delim-mods : strlen(mods);
620 addfdmodifier(fd,mods,mod_len);
621 mods= delim ? delim+1 : 0;
623 if (!(fdsetup[fd].mods & (fdm_read|fdm_write))) {
625 addfdmodifier_fixed(fd,"read");
626 } else if (fdsetup[fd].mods & fdm_fd) {
627 addfdmodifier_fixed(fd,"write");
629 addfdmodifier_fixed(fd,"overwrite");
632 if (fdsetup[fd].mods & fdm_fd) {
633 copyfd= strtofd(value,0,
634 "value part of argument to --file with fd modifier");
635 r= fstat(copyfd,&stab);
637 if (oip) fsyscallerror("check filedescriptor %lu (named as target of file "
638 "descriptor redirection for %lu)",copyfd,fd);
639 else fsyscallerror("check basic filedescriptor %lu at program start",copyfd);
641 fdsetup[fd].copyfd= copyfd;
645 static void of_fdwait(const struct optioninfo *oip, const char *value, char *key) {
646 const struct fdmodifierinfo *fdmip;
649 fd= strtofd(key,0,"first part of argument to --fdwait");
650 if (fd >= fdsetupsize || !fdsetup[fd].filename)
651 usageerror("file descriptor %d specified in --fdwait option is not open",fd);
652 for (fdmip= fdmodifierinfos; fdmip->string && strcmp(fdmip->string,value); fdmip++);
653 if (!fdmip->string || !(fdmip->implies & (fdm_wait|fdm_nowait|fdm_close)))
654 usageerror("value for --fdwait must be `wait', `nowait' or `close', not `%s'",value);
655 fdsetup[fd].mods &= ~(fdm_wait|fdm_nowait|fdm_close);
656 fdsetup[fd].mods |= fdmip->implies;
659 static void of_defvar(const struct optioninfo *oip, const char *value, char *key) {
663 usageerror("empty string not allowed as variable name");
664 if (strlen(key)>MAX_GENERAL_STRING)
665 usageerror("variable name `%s' is far too long",key);
666 if (strlen(value)>MAX_GENERAL_STRING)
667 usageerror("variable `%s' has value `%s' which is far too long",key,value);
668 for (i=0; i<defvarused && strcmp(defvararray[i].key,key); i++);
669 if (defvarused >= MAX_ARGSDEFVAR) usageerror("far too many --defvar or -D options");
670 if (i>=defvaravail) {
671 defvaravail+=10; defvaravail<<=1;
672 defvararray= xrealloc(defvararray,sizeof(struct constkeyvaluepair)*defvaravail);
674 if (i==defvarused) defvarused++;
675 defvararray[i].key= key;
676 defvararray[i].value= value;
679 static void of_timeout(const struct optioninfo *oip, const char *value, char *key) {
684 ul= strtoul(value,&endp,10);
686 usageerror("timeout value `%s' must be a plain decimal string",value);
687 if (ul>INT_MAX) usageerror("timeout value %lu too large",ul);
691 static void of_signals(const struct optioninfo *oip, const char *value, char *key) {
692 unsigned long numvalue;
696 numvalue= strtoul(value,&endp,10);
697 if (errno || *endp) {
698 if (!strcmp(value,"number")) signalsexit= se_number;
699 else if (!strcmp(value,"number-nocore")) signalsexit= se_numbernocore;
700 else if (!strcmp(value,"highbit")) signalsexit= se_highbit;
701 else if (!strcmp(value,"stdout")) signalsexit= se_stdout;
702 else usageerror("value `%s' for --signals not understood",value);
704 if (numvalue<0 || numvalue>255)
705 usageerror("value %lu for --signals not 0...255",numvalue);
706 signalsexit= numvalue;
710 static void of_sigpipe(const struct optioninfo *oip, const char *value, char *key) {
714 static void of_hidecwd(const struct optioninfo *oip, const char *value, char *key) {
718 static void of_help(const struct optioninfo *oip, const char *value, char *key) {
720 if (fclose(stdout)) syscallerror("fclose stdout after writing usage message");
724 static void of_version(const struct optioninfo *oip, const char *value, char *key) {
725 if (puts(VERSION VEREXT) == EOF || fclose(stdout))
726 syscallerror("write version number");
730 static void of_copyright(const struct optioninfo *oip, const char *value, char *key) {
732 " userv - user service daemon and client\n\n"
735 " This is free software; you can redistribute it and/or modify it under the\n"
736 " terms of the GNU General Public License as published by the Free Software\n"
737 " Foundation; either version 3 of the License, or (at your option) any\n"
738 " later version.\n\n"
739 " This program is distributed in the hope that it will be useful, but\n"
740 " WITHOUT ANY WARRANTY; without even the implied warranty of\n"
741 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General\n"
742 " Public License for more details.\n\n"
743 " You should have received a copy of the GNU General Public License along\n"
744 " with userv; if not, see <http://www.gnu.org/licenses/>.\n",
745 stdout) < 0) syscallerror("write usage to stderr");
749 static void of_builtin(const struct optioninfo *oip, const char *value, char *key) {
750 overridetype= ot_builtin;
753 static void of_override(const struct optioninfo *oip, const char *value, char *key) {
754 overridetype= ot_string;
755 overridevalue= value;
758 static void of_overridefile(const struct optioninfo *oip,
759 const char *value, char *key) {
760 overridetype= ot_file;
761 overridevalue= value;
764 static void of_spoofuser(const struct optioninfo *oip,
765 const char *value, char *key) {
769 const struct optioninfo optioninfos[]= {
770 { 'f', "file", 2, of_file },
771 { 'w', "fdwait", 2, of_fdwait },
772 { 'D', "defvar", 2, of_defvar },
773 { 't', "timeout", 1, of_timeout },
774 { 'S', "signals", 1, of_signals },
775 { 'P', "sigpipe", 0, of_sigpipe },
776 { 'H', "hidecwd", 0, of_hidecwd },
777 { 'B', "builtin", 0, of_builtin },
778 { 'h', "help", 0, of_help },
779 { 0, "version", 0, of_version },
780 { 0, "copyright", 0, of_copyright },
781 { 0, "override", 1, of_override },
782 { 0, "override-file", 1, of_overridefile },
783 { 0, "spoof-user", 1, of_spoofuser },
787 static void callvalueoption(const struct optioninfo *oip, char *arg) {
789 if (oip->values == 2) {
790 equals= strchr(arg,'=');
793 usageerror("option --%s (-%c) passed argument `%s' with no `='",
794 oip->full,oip->abbrev,arg);
796 usageerror("option --%s passed argument `%s' with no `='",
800 (oip->fn)(oip,equals,arg);
802 (oip->fn)(oip,arg,0);
807 * Main thread main processing functions - in order of execution.
810 static void security_init(void) {
811 /* May not open any file descriptors. */
813 mypid= getpid(); if (mypid == (pid_t)-1) syscallerror("getpid");
814 myuid= getuid(); if (myuid == (uid_t)-1) syscallerror("getuid");
815 mygid= getgid(); if (mygid == (gid_t)-1) syscallerror("getgid");
816 ngids= getgroups(0,0); if (ngids == -1) syscallerror("getgroups(0,0)");
817 gidarray= xmalloc(sizeof(gid_t)*ngids);
818 if (getgroups(ngids,gidarray) != ngids) syscallerror("getgroups(ngids,)");
822 if (ngids > MAX_GIDS) miscerror("caller is in far too many gids");
825 static void parse_arguments(int *argcp, char *const **argvp) {
826 static char fd0key[]= "stdin,fd,read";
827 static char fd1key[]= "stdout,fd,write";
828 static char fd2key[]= "stderr,fd,write";
832 const struct optioninfo *oip;
836 of_file(0,"stdin",fd0key);
837 of_file(0,"stdout",fd1key);
838 of_file(0,"stderr",fd2key);
840 for (argpp= *argvp+1;
841 (argp= *argpp) && *argp == '-' && argp[1];
843 if (!*++argp) usageerror("unknown option/argument `%s'",*argpp);
844 if (*argp == '-') { /* Two hyphens */
845 if (!*++argp) { argpp++; break; /* End of options. */ }
846 for (oip= optioninfos; oip->full && strcmp(oip->full,argp); oip++);
847 if (!oip->full) usageerror("unknown long option `%s'",*argpp);
849 if (!argpp[1]) usageerror("long option `%s' needs a value",*argpp);
850 callvalueoption(oip,*++argpp);
855 for (; *argp; argp++) {
856 for (oip= optioninfos; oip->full && oip->abbrev != *argp; oip++);
857 if (!oip->full) usageerror("unknown short option `-%c' in argument `%s'",
863 if (!argpp[1]) usageerror("short option `-%c' in argument `%s' needs"
864 " a value",*argp,*argpp);
867 callvalueoption(oip,argp);
868 break; /* No more options in this argument, go on to the next one. */
875 if (overridetype == ot_builtin) {
878 if (!*argpp) usageerror("no service user given after options");
879 serviceuser= *argpp++;
881 if (!*argpp) usageerror(overridetype == ot_builtin ?
882 "no service name given after options and service user" :
883 "no builtin service given after options");
885 *argcp-= (argpp-*argvp);
888 if (*argcp > MAX_ARGSDEFVAR) usageerror("far too many arguments");
890 for (fd=0; fd<fdsetupsize; fd++) {
891 if (!fdsetup[fd].filename) continue;
892 if (fdsetup[fd].mods & (fdm_wait|fdm_nowait|fdm_close)) continue;
893 assert(fdsetup[fd].mods & (fdm_read|fdm_write));
894 fdsetup[fd].mods |= (fdsetup[fd].mods & fdm_read) ? fdm_close : fdm_wait;
898 static void determine_users(void) {
906 loginname= getenv("LOGNAME");
907 if (!loginname) loginname= getenv("USER");
909 pw= getpwnam(loginname);
910 if (!pw || pw->pw_uid != myuid) loginname= 0;
913 pw= getpwuid(myuid); if (!pw) miscerror("cannot determine your login name");
914 loginname= xstrsave(pw->pw_name);
917 if (!strcmp(serviceuser,"-")) serviceuser= loginname;
918 pw= getpwnam(serviceuser);
919 if (!pw) miscerror("requested service user `%s' is not a user",serviceuser);
920 serviceuid= pw->pw_uid;
922 if ((overridetype != ot_none || spoofuser) && myuid != 0 && myuid != serviceuid)
923 miscerror("--override and --spoof options only available to root or to"
924 " the user who will be providing the service");
927 loginname= spoofuser;
928 pw= getpwnam(loginname);
929 if (!pw) miscerror("spoofed login name `%s' is not valid",loginname);
930 spoofuid= pw->pw_uid;
931 spoofgid= pw->pw_gid;
932 ngidssize= ngids; ngids= 0;
935 gidarray= xrealloc(gidarray,sizeof(gid_t)*ngidssize);
937 gidarray[ngids++]= spoofgid;
938 while ((gr= getgrent())) { /* ouch! getgrent has no error behaviour! */
939 for (mem= gr->gr_mem; *mem && strcmp(*mem,loginname); mem++);
941 if (ngids>=ngidssize) {
942 if (ngids>=MAX_GIDS) miscerror("spoofed user is member of too many groups");
943 ngidssize= (ngids+5)<<1;
944 gidarray= xrealloc(gidarray,sizeof(gid_t)*ngidssize);
946 gidarray[ngids++]= gr->gr_gid;
951 static void determine_cwd(void) {
952 cwdbufsize= 0; cwdbuf= 0;
957 if (cwdbufsize > MAX_GENERAL_STRING) { cwdbufsize= 0; free(cwdbuf); break; }
958 cwdbufsize <<= 1; cwdbufsize+= 100;
959 cwdbuf= xrealloc(cwdbuf,cwdbufsize);
960 cwdbuf[cwdbufsize-1]= 0;
961 if (getcwd(cwdbuf,cwdbufsize-1)) { cwdbufsize= strlen(cwdbuf); break; }
962 if (errno != ERANGE) { cwdbufsize= 0; free(cwdbuf); break; }
966 static void process_override(const char *servicename) {
970 switch (overridetype) {
976 l= strlen(servicename);
977 if (l >= MAX_OVERRIDE_LEN-20)
978 miscerror("builtin service string is too long (%d, max is %d)",
979 l,MAX_OVERRIDE_LEN-21);
982 snprintf(ovbuf,l,"execute-builtin %s\n",servicename);
983 ovused= strlen(ovbuf);
986 l= strlen(overridevalue);
987 if (l >= MAX_OVERRIDE_LEN)
988 miscerror("override string is too long (%d, max is %d)",l,MAX_OVERRIDE_LEN-1);
990 snprintf(ovbuf,l+2,"%s\n",overridevalue);
994 ovfile= fopen(overridevalue,"r");
995 if (!ovfile) fsyscallerror("open overriding configuration file `%s'",overridevalue);
996 ovbuf= 0; ovavail= ovused= 0;
997 while ((c= getc(ovfile)) != EOF) {
998 if (!c) miscerror("overriding config file `%s' contains null(s)",overridevalue);
999 if (ovused >= MAX_OVERRIDE_LEN)
1000 miscerror("override file is too long (max is %d)",MAX_OVERRIDE_LEN);
1001 if (ovused >= ovavail) {
1002 ovavail+=80; ovavail<<=2; ovbuf= xrealloc(ovbuf,ovavail);
1006 if (ferror(ovfile) || fclose(ovfile))
1007 fsyscallerror("read overriding configuration file `%s'",overridevalue);
1008 ovbuf= xrealloc(ovbuf,ovused+1);
1016 static int server_connect(void) {
1017 struct sockaddr_un ssockname;
1019 struct sigaction sig;
1023 sigaddset(&sset,SIGCHLD);
1024 sigaddset(&sset,SIGALRM);
1025 sigaddset(&sset,SIGPIPE);
1026 if (sigprocmask(SIG_UNBLOCK,&sset,0)) syscallerror("preliminarily unblock signals");
1028 sig.sa_handler= SIG_IGN;
1029 sigemptyset(&sig.sa_mask);
1031 if (sigaction(SIGPIPE,&sig,0)) syscallerror("ignore sigpipe");
1033 sfd= socket(AF_UNIX,SOCK_STREAM,0);
1034 if (!sfd) syscallerror("create client socket");
1036 assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUSPATH));
1037 ssockname.sun_family= AF_UNIX;
1038 strcpy(ssockname.sun_path,RENDEZVOUSPATH);
1040 while (connect(sfd,(struct sockaddr*)&ssockname,sizeof(ssockname))) {
1041 if (errno == ECONNREFUSED || errno == ENOENT)
1042 syscallerror("uservd daemon is not running - service not available");
1044 fsyscallerror("unable to connect to uservd daemon: %m");
1050 static void server_handshake(int sfd) {
1051 srfile= fdopen(sfd,"r");
1052 if (!srfile) syscallerror("turn socket fd into FILE* for read");
1053 if (setvbuf(srfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket reads");
1055 swfile= fdopen(sfd,"w");
1056 if (!swfile) syscallerror("turn socket fd into FILE* for write");
1057 if (setvbuf(swfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket writes");
1059 xfread(&opening_mbuf,sizeof(opening_mbuf),srfile);
1060 checkmagic(opening_mbuf.magic,OPENING_MAGIC,"in opening message");
1061 if (memcmp(protocolchecksumversion,opening_mbuf.protocolchecksumversion,PCSUMSIZE))
1062 protoerror("protocol version checksum mismatch - server not same as client");
1065 static void server_preparepipes(void) {
1066 char pipepathbuf[PIPEPATHMAXLEN+2];
1069 for (fd=0; fd<fdsetupsize; fd++) {
1070 if (!fdsetup[fd].filename) continue;
1071 pipepathbuf[PIPEPATHMAXLEN]= 0;
1072 sprintf(pipepathbuf, PIPEPATHFORMAT,
1073 (unsigned long)mypid, (unsigned long)opening_mbuf.serverpid, fd);
1074 assert(!pipepathbuf[PIPEPATHMAXLEN]);
1076 if (unlink(pipepathbuf) && errno != ENOENT)
1077 fsyscallerror("remove any old pipe `%s'",pipepathbuf);
1078 if (mkfifo(pipepathbuf,0600)) /* permissions are irrelevant */
1079 fsyscallerror("create pipe `%s'",pipepathbuf);
1080 tempfd= open(pipepathbuf,O_RDWR);
1081 if (tempfd<0) fsyscallerror("prelim open pipe `%s' for read+write",pipepathbuf);
1082 assert(fdsetup[fd].mods & (fdm_read|fdm_write));
1084 open(pipepathbuf, (fdsetup[fd].mods & fdm_read) ? O_WRONLY : O_RDONLY);
1085 if (fdsetup[fd].pipefd<0) fsyscallerror("real open pipe `%s'",pipepathbuf);
1086 if (close(tempfd)) fsyscallerror("close prelim fd onto pipe `%s'",pipepathbuf);
1091 static void server_sendrequest(int argc, char *const *argv) {
1092 struct request_msg request_mbuf;
1096 memset(&request_mbuf,0,sizeof(request_mbuf));
1097 request_mbuf.magic= REQUEST_MAGIC;
1098 request_mbuf.clientpid= getpid();
1099 request_mbuf.serviceuserlen= strlen(serviceuser);
1100 request_mbuf.servicelen= strlen(argv[0]);
1101 request_mbuf.loginnamelen= strlen(loginname);
1102 request_mbuf.spoofed= spoofuser ? 1 : 0;
1103 request_mbuf.cwdlen= cwdbufsize;
1104 request_mbuf.callinguid= spoofuid;
1105 request_mbuf.ngids= ngids+1;
1106 request_mbuf.nreadfds= 0;
1107 request_mbuf.nwritefds= 0;
1108 for (fd=0; fd<fdsetupsize; fd++) {
1109 if (!fdsetup[fd].filename) continue;
1110 assert(fdsetup[fd].mods & (fdm_write|fdm_read));
1111 if (fdsetup[fd].mods & fdm_write) request_mbuf.nwritefds++;
1112 else request_mbuf.nreadfds++;
1114 request_mbuf.nargs= argc-1;
1115 request_mbuf.nvars= defvarused;
1116 request_mbuf.overridelen= ovused;
1117 xfwrite(&request_mbuf,sizeof(request_mbuf),swfile);
1118 xfwrite(serviceuser,sizeof(*serviceuser)*request_mbuf.serviceuserlen,swfile);
1119 xfwrite(argv[0],sizeof(*argv[0])*request_mbuf.servicelen,swfile);
1120 xfwrite(loginname,sizeof(*loginname)*request_mbuf.loginnamelen,swfile);
1121 xfwrite(cwdbuf,sizeof(*cwdbuf)*request_mbuf.cwdlen,swfile);
1122 if (ovused>=0) xfwrite(ovbuf,sizeof(*ovbuf)*ovused,swfile);
1123 xfwrite(&spoofgid,sizeof(gid_t),swfile);
1124 xfwrite(gidarray,sizeof(gid_t)*ngids,swfile);
1125 xfwritefds(fdm_read,request_mbuf.nreadfds,swfile);
1126 xfwritefds(fdm_write,request_mbuf.nwritefds,swfile);
1127 for (i=1; i<argc; i++)
1128 xfwritestring(argv[i],swfile);
1129 for (i=0; i<defvarused; i++) {
1130 xfwritestring(defvararray[i].key,swfile);
1131 xfwritestring(defvararray[i].value,swfile);
1133 ul= REQUEST_END_MAGIC; xfwrite(&ul,sizeof(ul),swfile);
1137 static void server_awaitconfirm(void) {
1138 struct progress_msg progress_mbuf;
1140 getprogress(&progress_mbuf,srfile);
1141 if (progress_mbuf.type != pt_ok)
1142 protoerror("progress message during configuration phase"
1143 " unexpected type %d",progress_mbuf.type);
1146 static void prepare_asynchsignals(void) {
1147 static char stderrbuf[BUFSIZ], stdoutbuf[BUFSIZ];
1149 struct sigaction sig;
1151 if (setvbuf(stderr,stderrbuf,_IOLBF,sizeof(stderrbuf)))
1152 syscallerror("set buffering on stderr");
1153 if (setvbuf(stdout,stdoutbuf,_IOFBF,sizeof(stdoutbuf)))
1154 syscallerror("set buffering on stdout");
1156 sig.sa_handler= sighandler_chld;
1157 sigemptyset(&sig.sa_mask);
1158 sigaddset(&sig.sa_mask,SIGCHLD);
1159 sigaddset(&sig.sa_mask,SIGALRM);
1161 if (sigaction(SIGCHLD,&sig,0)) syscallerror("set up sigchld handler");
1163 sig.sa_handler= sighandler_alrm;
1164 if (sigaction(SIGALRM,&sig,0)) syscallerror("set up sigalrm handler");
1166 if (!timeout) return;
1171 static void close_unwanted_pipes(void) {
1174 for (fd=0; fd<fdsetupsize; fd++) {
1175 if (!fdsetup[fd].filename) continue;
1176 if (close(fdsetup[fd].pipefd)) fsyscallerror("close pipe fd for %d",fd);
1177 if (fdsetup[fd].copyfd>2)
1178 if (close(fdsetup[fd].copyfd))
1180 /* EBADF can be induced if cmd line specifies same fd twice */
1181 fsyscallerror("close real fd for %d",fd);
1185 static void catdup(const char *which, int from, int to) {
1186 if (dup2(from,to)<0) {
1187 blocksignals(SIG_BLOCK);
1188 fprintf(stderr,"userv: %s: cannot dup for %s: %s\n",which,
1189 to?"stdout":"stdin", strerror(errno));
1194 static void connect_pipes(void) {
1195 struct sigaction sig;
1196 char catnamebuf[sizeof(int)*3+30];
1200 for (fd=0; fd<fdsetupsize; fd++) {
1201 if (!fdsetup[fd].filename) continue;
1202 if (!(fdsetup[fd].mods & fdm_fd)) {
1204 open(fdsetup[fd].filename,fdsetup[fd].oflags|O_NOCTTY,0777);
1205 if (fdsetup[fd].copyfd<0)
1206 fsyscallerror("open file `%s' for fd %d",fdsetup[fd].filename,fd);
1208 blocksignals(SIG_BLOCK);
1210 fdsetup[fd].catpid= child;
1211 blocksignals(SIG_UNBLOCK);
1212 if (child==-1) fsyscallerror("fork for cat for fd %d",fd);
1214 snprintf(catnamebuf,sizeof(catnamebuf),"cat fd%d",fd);
1215 catnamebuf[sizeof(catnamebuf)-1]= 0;
1216 sig.sa_handler= SIG_DFL;
1217 sigemptyset(&sig.sa_mask);
1219 if (sigaction(SIGPIPE,&sig,0)) {
1220 fprintf(stderr,"userv: %s: reset sigpipe handler for cat: %s",
1221 catnamebuf,strerror(errno));
1224 reading= fdsetup[fd].mods & fdm_read;
1225 catdup(catnamebuf, fdsetup[fd].copyfd, reading ? 0 : 1);
1226 catdup(catnamebuf, fdsetup[fd].pipefd, reading ? 1 : 0);
1227 if (close(socketfd))
1228 fsyscallerror("%s: close client socket for for cat",catnamebuf);
1229 close_unwanted_pipes();
1230 execl("/bin/cat",catnamebuf,(char*)0);
1231 fprintf(stderr,"userv: %s: cannot exec `cat': %s\n",catnamebuf,strerror(errno));
1235 close_unwanted_pipes();
1238 static void server_sendconfirm(void) {
1239 struct event_msg event_mbuf;
1241 blocksignals(SIG_BLOCK);
1242 memset(&event_mbuf,0,sizeof(event_mbuf));
1243 event_mbuf.magic= EVENT_MAGIC;
1244 event_mbuf.type= et_confirm;
1245 xfwrite(&event_mbuf,sizeof(event_mbuf),swfile);
1247 blocksignals(SIG_UNBLOCK);
1250 static int server_awaitcompletion(void) {
1251 struct progress_msg progress_mbuf;
1253 getprogress(&progress_mbuf,srfile);
1254 if (progress_mbuf.type != pt_terminated)
1255 protoerror("progress message during execution phase"
1256 " unexpected type %d",progress_mbuf.type);
1259 return progress_mbuf.data.terminated.status;
1262 static void dispose_remaining_pipes(void) {
1266 blocksignals(SIG_BLOCK);
1267 for (fd=0; fd<fdsetupsize; fd++) {
1268 if (!(fdsetup[fd].catpid!=-1 && (fdsetup[fd].mods & fdm_close))) continue;
1269 if (kill(fdsetup[fd].catpid,SIGKILL)) fsyscallerror("kill cat for %d",fd);
1270 fdsetup[fd].killed= 1;
1272 blocksignals(SIG_UNBLOCK);
1275 blocksignals(SIG_BLOCK);
1277 fd<fdsetupsize && !(fdsetup[fd].catpid!=-1 && (fdsetup[fd].mods & fdm_wait));
1279 if (fd>=fdsetupsize) break;
1281 r= sigsuspend(&sset);
1282 if (r && errno != EINTR) syscallerror("sigsuspend failed in unexpected way");
1283 blocksignals(SIG_UNBLOCK);
1287 static void NONRETURNING process_exitstatus(int status) {
1288 blocksignals(SIG_BLOCK);
1290 if (systemerror) _exit(255);
1292 if (sigpipeok && signalsexit != se_stdout && WIFSIGNALED(status) &&
1293 WTERMSIG(status)==SIGPIPE && !WCOREDUMP(status)) status= 0;
1295 switch (signalsexit) {
1297 case se_numbernocore:
1298 if (WIFEXITED(status))
1299 _exit(WEXITSTATUS(status));
1300 else if (WIFSIGNALED(status))
1301 _exit(WTERMSIG(status) + (signalsexit==se_number && WCOREDUMP(status) ? 128 : 0));
1304 if (WIFEXITED(status))
1305 _exit(WEXITSTATUS(status)<=127 ? WEXITSTATUS(status) : 127);
1306 else if (WIFSIGNALED(status) && WTERMSIG(status)<=126)
1307 _exit(WTERMSIG(status)+128);
1310 printf("\n%d %d ",(status>>8)&0x0ff,status&0x0ff);
1311 if (WIFEXITED(status))
1312 printf("exited with code %d",WEXITSTATUS(status));
1313 else if (WIFSIGNALED(status))
1314 printf("killed by %s (signal %d)%s",
1315 strsignal(WTERMSIG(status)),WTERMSIG(status),
1316 WCOREDUMP(status) ? ", core dumped " : "");
1318 printf("unknown wait status");
1320 if (ferror(stdout) || fflush(stdout)) syscallerror("write exit status to stdout");
1323 if (WIFEXITED(status))
1324 _exit(WEXITSTATUS(status));
1325 else if (WIFSIGNALED(status))
1330 fprintf(stderr,"userv: unknown wait status %d\n",status);
1334 int main(int argc, char *const *argv) {
1338 # error Do not disable assertions in this security-critical code !
1342 parse_arguments(&argc,&argv);
1345 process_override(argv[0]);
1347 socketfd= server_connect();
1349 server_handshake(socketfd);
1350 server_preparepipes();
1351 server_sendrequest(argc,argv);
1353 priv_permanentlyrevokesuspended();
1355 server_awaitconfirm();
1356 prepare_asynchsignals();
1358 server_sendconfirm();
1359 status= server_awaitcompletion();
1361 dispose_remaining_pipes();
1362 process_exitstatus(status);