5 * Copyright (C)1996-1997 Ian Jackson
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with userv; if not, write to the Free Software
19 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 * Here too, we do some horrible asynchronous stuff with signals.
25 * The following objects &c. are used in signal handlers and so
26 * must be protected by calls to blocksignals if they are used in
31 * The following objects are used in the main program unprotected
32 * and so must not be used in signal handlers:
37 * The following objects/functions are not modified/called after the
38 * asynchronicity starts:
44 * results of argument parsing
46 * systemerror, swfile, fdsetup[].catpid and fdsetup[].killed are used
47 * for communication between the main thread and the signal handlers.
49 * All the signal handlers save errno so that is OK too.
66 #include <sys/types.h>
68 #include <sys/socket.h>
70 #include <sys/resource.h>
78 enum fdmodifiervalues {
92 struct fdmodifierinfo {
100 const char *filename; /* non-null iff this fd has been specified */
101 int copyfd; /* fd to copy, -1 unless mods & fdm_fd */
102 int mods, oflags, pipefd, killed; /* 0,0,-1,0 unless otherwise set */
103 pid_t catpid; /* -1 indicates no cat process */
106 enum signalsexitspecials { se_number=-100, se_numbernocore, se_highbit, se_stdout };
107 enum overridetypes { ot_none, ot_string, ot_file, ot_builtin };
109 struct constkeyvaluepair { const char *key, *value; };
111 /* Variables from command-line arguments */
112 static const char *serviceuser;
113 static uid_t serviceuid;
114 static struct fdsetupstate *fdsetup;
115 static int fdsetupsize;
116 static struct constkeyvaluepair *defvararray;
117 static int defvaravail, defvarused;
118 static unsigned long timeout;
119 static int signalsexit=254;
120 static int sigpipeok, hidecwd;
121 static int overridetype= ot_none;
122 static const char *overridevalue, *spoofuser=0;
124 /* Other state variables */
125 static FILE *srfile, *swfile;
127 static uid_t myuid, spoofuid;
128 static gid_t mygid, spoofgid, *gidarray;
130 static struct opening_msg opening_mbuf;
131 static const char *logname;
133 static size_t cwdbufsize;
135 static int ovused, systemerror;
137 static void blocksignals(int how) {
139 static const char blockerrmsg[]= "userv: failed to [un]block signals: ";
143 sigaddset(&set,SIGCHLD);
144 sigaddset(&set,SIGALRM);
145 if (sigprocmask(how,&set,0)) {
146 str= strerror(errno);
147 write(2,blockerrmsg,sizeof(blockerrmsg)-1);
148 write(2,str,strlen(str));
154 /* Functions which may be called either from signal handlers or from
155 * the main thread. They block signals in case they are on the main
156 * thread, and may only use signal handler objects. None of them
157 * return. If they did they'd have to restore the signal mask.
160 static void NONRETURNPRINTFFORMAT(1,2) miscerror(const char *fmt, ...) {
163 blocksignals(SIG_BLOCK);
165 fprintf(stderr,"userv: failure: ");
166 vfprintf(stderr,fmt,al);
167 fprintf(stderr,"\n");
171 static void NONRETURNPRINTFFORMAT(1,2) syscallerror(const char *fmt, ...) {
176 blocksignals(SIG_BLOCK);
178 fprintf(stderr,"userv: system call failure: ");
179 vfprintf(stderr,fmt,al);
180 fprintf(stderr,": %s\n",strerror(e));
184 static void NONRETURNING protoreaderror(FILE *file, const char *where) {
188 blocksignals(SIG_BLOCK);
190 fprintf(stderr,"userv: failure: read error %s: %s\n",where,strerror(e));
193 fprintf(stderr,"userv: internal failure: EOF from server %s\n",where);
198 static void NONRETURNPRINTFFORMAT(1,2) protoerror(const char *fmt, ...) {
201 blocksignals(SIG_BLOCK);
203 fprintf(stderr,"userv: internal failure: protocol error: ");
204 vfprintf(stderr,fmt,al);
205 fprintf(stderr,"\n");
210 * General-purpose functions; these do nothing special about signals,
211 * except that they can call error-handlers which may block them
212 * to print error messages.
215 static void xfread(void *p, size_t sz, FILE *file) {
217 nr= working_fread(p,sz,file);
218 if (nr != sz) protoreaderror(file,"in data");
221 static void xfwrite(const void *p, size_t sz, FILE *file) {
223 nr= fwrite(p,1,sz,file); if (nr == sz) return;
224 syscallerror("writing to server");
227 static void xfflush(FILE *file) {
228 if (fflush(file)) syscallerror("flush server socket");
231 /* Functions which may be called only from the main thread. These may
232 * use main-thread objects and must block signals before using signal
237 static void priv_suspend(void) { }
238 static void priv_resume(void) { }
239 static void priv_permanentlyrevokesuspended(void) { }
241 static void priv_suspend(void) {
242 if (setreuid(0,myuid) != 0) syscallerror("suspend root setreuid(0,myuid)");
244 static void priv_resume(void) {
245 if (setreuid(myuid,0) != 0) syscallerror("resume root setreuid(myuid,0)");
247 static void priv_permanentlyrevokesuspended(void) {
248 if (setreuid(myuid,myuid) != 0) syscallerror("revoke root setreuid(myuid,myuid)");
249 if (setreuid(myuid,myuid) != 0) syscallerror("rerevoke root setreuid(myuid,myuid)");
251 if (!setreuid(myuid,0)) miscerror("revoked root but setreuid(0,0) succeeded !");
252 if (errno != EPERM) syscallerror("revoked and setreuid(myuid,0) unexpected error");
257 static void checkmagic(unsigned long was, unsigned long should, const char *when) {
259 protoerror("magic number %s was %08lx, expected %08lx",when,was,should);
262 static void getprogress(struct progress_msg *progress_r, FILE *file) {
267 xfread(progress_r,sizeof(struct progress_msg),file);
268 checkmagic(progress_r->magic,PROGRESS_MAGIC,"in progress message");
269 switch (progress_r->type) {
271 blocksignals(SIG_BLOCK);
272 fputs("userv: uservd reports that service failed\n",stderr);
275 blocksignals(SIG_BLOCK);
276 fputs("uservd: ",stderr);
277 if (progress_r->data.errmsg.messagelen>MAX_ERRMSG_STRING)
278 protoerror("stderr message length %d is far too long",
279 progress_r->data.errmsg.messagelen);
280 for (i=0; i<progress_r->data.errmsg.messagelen; i++) {
281 c= working_getc(file);
282 if (c==EOF) protoreaderror(file,"in error message");
283 if (isprint(c)) putc(c,stderr);
284 else fprintf(stderr,"\\x%02x",(unsigned char)c);
287 if (ferror(stderr)) syscallerror("printing error message");
288 xfread(&ul,sizeof(ul),file);
289 checkmagic(ul,PROGRESS_ERRMSG_END_MAGIC,"after error message");
290 blocksignals(SIG_UNBLOCK);
299 * Functions which are called only during setup, before
300 * the signal asynchronicity starts. They can do anything they like.
303 static void *xmalloc(size_t s) {
306 if (!p) syscallerror("malloc (%lu bytes)",(unsigned long)s);
310 static void *xrealloc(void *p, size_t s) {
312 if (!p) syscallerror("realloc (%lu bytes)",(unsigned long)s);
316 static void xfwritestring(const char *s, FILE *file) {
319 assert(l<=MAX_GENERAL_STRING);
320 xfwrite(&l,sizeof(l),file);
321 xfwrite(s,sizeof(*s)*l,file);
324 static void xfwritefds(int modifier, int expected, FILE *file) {
327 for (i=0, fdcount=0; i<fdsetupsize; i++) {
328 if (!(fdsetup[i].filename && (fdsetup[i].mods & modifier)))
330 xfwrite(&i,sizeof(int),file); fdcount++;
332 assert(fdcount == expected);
335 /* Functions which may be called from signal handlers. These
336 * may use signal-handler objects. The main program may only
337 * call them with signals blocked, and they may not use any
338 * main-thread objects.
341 static void disconnect(void) /* DOES return, unlike in daemon */ {
342 struct event_msg event_mbuf;
346 memset(&event_mbuf,0,sizeof(event_mbuf));
347 event_mbuf.magic= EVENT_MAGIC;
348 event_mbuf.type= et_disconnect;
349 r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile);
350 if ((r != sizeof(event_mbuf) || fflush(swfile)) && errno != EPIPE)
351 syscallerror("write to server when disconnecting\n");
356 static void sighandler_alrm(int ignored) /* DOES return, unlike in daemon */ {
359 fputs("userv: timeout\n",stderr);
364 static void sighandler_chld(int ignored) /* DOES return, unlike in daemon */ {
365 struct event_msg event_mbuf;
367 int status, fd, r, es;
371 child= wait3(&status,WNOHANG,0);
372 if (child == 0 || (child == -1 && errno == ECHILD)) break;
373 if (child == -1) syscallerror("wait for child process (in sigchld handler)");
374 for (fd=0; fd<fdsetupsize && fdsetup[fd].catpid != child; fd++);
375 fprintf(stderr,"chld found pid=%ld fd=%d setupsize=%d\n",(long)child,fd,fdsetupsize);
376 if (fd>=fdsetupsize) continue; /* perhaps the caller gave us children */
377 if ((WIFEXITED(status) && WEXITSTATUS(status)==0) ||
378 (WIFSIGNALED(status) && WTERMSIG(status)==SIGPIPE) ||
379 (fdsetup[fd].killed && WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL)) {
380 if (swfile && fdsetup[fd].mods & fdm_read) {
381 memset(&event_mbuf,0,sizeof(event_mbuf));
382 event_mbuf.magic= EVENT_MAGIC;
383 event_mbuf.type= et_closereadfd;
384 event_mbuf.data.closereadfd.fd= fd;
385 r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile);
386 if (r != sizeof(event_mbuf) || fflush(swfile))
387 if (errno != EPIPE) syscallerror("inform service of closed read fd");
390 if (WIFEXITED(status))
391 fprintf(stderr,"userv: cat for fd %d exited with error exit status %d\n",
392 fd,WEXITSTATUS(status));
393 else if (WIFSIGNALED(status))
394 if (WCOREDUMP(status))
395 fprintf(stderr,"userv: cat for fd %d dumped core due to signal %s (%d)\n",
396 fd,strsignal(WTERMSIG(status)),WTERMSIG(status));
398 fprintf(stderr,"userv: cat for fd %d terminated by signal %s (%d)\n",
399 fd,strsignal(WTERMSIG(status)),WTERMSIG(status));
401 fprintf(stderr,"userv: cat for fd %d gave unknown wait status %d\n",
405 fdsetup[fd].catpid= -1;
411 * Argument parsing. These functions which are called only during
412 * setup, before the signal asynchronicity starts.
417 typedef void optionfunction(const struct optioninfo*, const char *value, char *key);
422 int values; /* 0: no value; 1: single value; 2: key and value */
426 static void usage(void) {
428 "usage: userv <options> [--] <service-user> <service-name> [<argument> ...]\n"
429 "usage: userv <options> -B|--builtin [--] <builtin-service> [<info-argument> ...]\n"
430 "options: -f|--file <fd>[<fdmodifiers>]=<filename>\n"
431 " -D|--defvar <name>=<value>\n"
432 " -t|--timeout <seconds>\n"
433 " -S|--signals <status>|number|number-nocore|highbit|stdout\n"
434 " -w|--fdwait <fd>=wait|nowait|close\n"
435 " -P|--sigpipe -H|--hidecwd -h|--help --copyright\n"
436 " --override <configuration-data> } available only\n"
437 " --override-file <filename> } to root\n"
438 " --spoof-user <username> } or same user\n"
439 "fdmodifiers: read write overwrite trunc[ate]\n"
440 "(separate with commas) append sync excl[usive] creat[e] fd\n\n"
441 "userv and uservd version " VERSION VEREXT "; copyright (C)1996-1997 Ian Jackson.\n"
442 "there is NO WARRANTY; type `userv --copyright' for details.\n",
444 syscallerror("write usage to stderr");
447 static void NONRETURNPRINTFFORMAT(1,2) usageerror(const char *fmt, ...) {
450 fputs("userv: ",stderr);
451 vfprintf(stderr,fmt,al);
452 fputs("\n\n",stderr);
457 static const struct fdmodifierinfo fdmodifierinfos[]= {
461 { "write", fdm_write,
464 { "overwrite", fdm_write|fdm_create|fdm_truncate,
465 fdm_read|fdm_fd|fdm_exclusive,
466 O_WRONLY|O_CREAT|O_TRUNC },
467 { "create", fdm_write|fdm_create,
470 { "creat", fdm_write|fdm_create,
473 { "exclusive", fdm_write|fdm_create|fdm_exclusive,
474 fdm_read|fdm_fd|fdm_truncate,
475 O_WRONLY|O_CREAT|O_EXCL },
476 { "excl", fdm_write|fdm_create|fdm_exclusive,
477 fdm_read|fdm_fd|fdm_truncate,
478 O_WRONLY|O_CREAT|O_EXCL },
479 { "truncate", fdm_write|fdm_truncate,
480 fdm_read|fdm_fd|fdm_exclusive,
481 O_WRONLY|O_CREAT|O_EXCL },
482 { "trunc", fdm_write|fdm_truncate,
483 fdm_read|fdm_fd|fdm_exclusive,
484 O_WRONLY|O_CREAT|O_EXCL },
485 { "append", fdm_write|fdm_append,
487 O_WRONLY|O_CREAT|O_APPEND },
488 { "sync", fdm_write|fdm_sync,
490 O_WRONLY|O_CREAT|O_SYNC },
492 fdm_nowait|fdm_close,
494 { "nowait", fdm_nowait,
497 { "close", fdm_close,
501 fdm_create|fdm_exclusive|fdm_truncate|fdm_append|fdm_sync,
506 static void addfdmodifier(int fd, const char *key) {
507 const struct fdmodifierinfo *fdmip;
510 for (fdmip= fdmodifierinfos; fdmip->string && strcmp(fdmip->string,key); fdmip++);
511 if (!fdmip->string) usageerror("unknown fdmodifer `%s' for fd %d",key,fd);
512 if (fdmip->conflicts & fdsetup[fd].mods)
513 usageerror("fdmodifier `%s' conflicts with another for fd %d",key,fd);
514 fdsetup[fd].mods |= fdmip->implies;
515 fdsetup[fd].oflags |= fdmip->oflags;
518 static int fdstdnumber(const char *string) {
519 if (!strcmp(string,"stdin")) return 0;
520 else if (!strcmp(string,"stdout")) return 1;
521 else if (!strcmp(string,"stderr")) return 2;
525 static void of_file(const struct optioninfo *oip, const char *value, char *key) {
526 unsigned long fd, copyfd;
531 fd= strtoul(key,&delim,10);
533 delim= strchr(key,',');
534 if (delim) *delim++= 0;
535 fd= fdstdnumber(key);
536 if (fd<0) usageerror("first part of argument to -f or --file must be numeric "
537 "file descriptor or `stdin', `stdout' or `stderr' - `%s' "
538 "is not recognized",key);
540 if (fd > MAX_ALLOW_FD)
541 usageerror("file descriptor specified (%lu) is larger than maximum allowed (%d)",
543 if (fd >= fdsetupsize) {
544 oldarraysize= fdsetupsize;
545 fdsetupsize+=2; fdsetupsize<<=1;
546 fdsetup= xrealloc(fdsetup,sizeof(struct fdsetupstate)*fdsetupsize);
547 while (oldarraysize < fdsetupsize) {
548 fdsetup[oldarraysize].filename= 0;
549 fdsetup[oldarraysize].pipefd= -1;
550 fdsetup[oldarraysize].copyfd= -1;
551 fdsetup[oldarraysize].mods= 0;
552 fdsetup[oldarraysize].oflags= 0;
553 fdsetup[oldarraysize].catpid= -1;
554 fdsetup[oldarraysize].killed= 0;
555 fdsetup[oldarraysize].filename= 0;
559 fdsetup[fd].filename= value;
560 fdsetup[fd].oflags= 0;
562 fdsetup[fd].copyfd= -1;
563 while (delim && *delim) {
565 delim= strchr(key,',');
566 if (delim) *delim++= 0;
567 addfdmodifier(fd,key);
569 if (!(fdsetup[fd].mods & (fdm_read|fdm_write))) {
571 addfdmodifier(fd,"read");
572 } else if (fdsetup[fd].mods & fdm_fd) {
573 addfdmodifier(fd,"write");
575 addfdmodifier(fd,"overwrite");
578 if (fdsetup[fd].mods & fdm_fd) {
579 copyfd= fdstdnumber(value);
581 copyfd= strtoul(value,&delim,0);
583 usageerror("value part of argument to --file with fd modifier must be "
584 "numeric or fd name - `%s' is not recognised",value);
585 else if (copyfd > MAX_ALLOW_FD)
586 usageerror("file descriptor %lu named as target of file descriptor redirection"
587 " (for file descriptor %lu) is larger than maximum allowed (%d)",
588 copyfd,fd,MAX_ALLOW_FD);
590 r= fstat(copyfd,&stab);
592 if (oip) syscallerror("check filedescriptor %lu (named as target of file "
593 "descriptor redirection for %lu)",copyfd,fd);
594 else syscallerror("check basic filedescriptor %lu at program start",copyfd);
596 fdsetup[fd].copyfd= copyfd;
600 static void of_fdwait(const struct optioninfo *oip, const char *value, char *key) {
601 const struct fdmodifierinfo *fdmip;
606 fd= fdstdnumber(key);
608 ul= strtoul(key,&delim,0);
609 if (*delim) usageerror("first part of argument to --fdwait must be "
610 "numeric or fd name - `%s' is not recognised",key);
611 if (ul>MAX_ALLOW_FD) usageerror("first part of argument to --fdwait is too large");
614 if (fd >= fdsetupsize || !fdsetup[fd].filename)
615 usageerror("file descriptor %d specified in --fdwait option is not open",fd);
616 for (fdmip= fdmodifierinfos; fdmip->string && strcmp(fdmip->string,value); fdmip++);
617 if (!fdmip->string || !(fdmip->implies & (fdm_wait|fdm_nowait|fdm_close)))
618 usageerror("value for --fdwait must be `wait', `nowait' or `close', not `%s'",value);
619 fdsetup[fd].mods &= ~(fdm_wait|fdm_nowait|fdm_close);
620 fdsetup[fd].mods |= fdmip->implies;
623 static void of_defvar(const struct optioninfo *oip, const char *value, char *key) {
627 usageerror("empty string not allowed as variable name");
628 if (strlen(key)>MAX_GENERAL_STRING)
629 usageerror("variable name `%s' is far too long",key);
630 if (strlen(value)>MAX_GENERAL_STRING)
631 usageerror("variable `%s' has value `%s' which is far too long",key,value);
632 for (i=0; i<defvarused && strcmp(defvararray[i].key,key); i++);
633 if (defvarused >= MAX_ARGSDEFVAR) usageerror("far too many --defvar or -D options");
634 if (i>=defvaravail) {
635 defvaravail+=10; defvaravail<<=1;
636 defvararray= xrealloc(defvararray,sizeof(struct constkeyvaluepair)*defvaravail);
638 if (i==defvarused) defvarused++;
639 defvararray[i].key= key;
640 defvararray[i].value= value;
643 static void of_timeout(const struct optioninfo *oip, const char *value, char *key) {
647 ul= strtoul(value,&endp,0);
648 if (*endp) usageerror("timeout value `%s' must be a plain decimal string",value);
649 if (ul>INT_MAX) usageerror("timeout value %lu too large",ul);
653 static void of_signals(const struct optioninfo *oip, const char *value, char *key) {
654 unsigned long numvalue;
657 numvalue= strtoul(value,&endp,0);
659 if (!strcmp(value,"number")) signalsexit= se_number;
660 else if (!strcmp(value,"number-nocore")) signalsexit= se_numbernocore;
661 else if (!strcmp(value,"highbit")) signalsexit= se_highbit;
662 else if (!strcmp(value,"stdout")) signalsexit= se_stdout;
663 else usageerror("value `%s' for --signals not understood",value);
665 if (numvalue<0 || numvalue>255)
666 usageerror("value %lu for --signals not 0...255",numvalue);
667 signalsexit= numvalue;
671 static void of_sigpipe(const struct optioninfo *oip, const char *value, char *key) {
675 static void of_hidecwd(const struct optioninfo *oip, const char *value, char *key) {
679 static void of_help(const struct optioninfo *oip, const char *value, char *key) {
684 static void of_copyright(const struct optioninfo *oip, const char *value, char *key) {
686 " userv - user service daemon and client; copyright (C)1996-1997 Ian Jackson\n\n"
687 " This is free software; you can redistribute it and/or modify it under the\n"
688 " terms of the GNU General Public License as published by the Free Software\n"
689 " Foundation; either version 2 of the License, or (at your option) any\n"
690 " later version.\n\n"
691 " This program is distributed in the hope that it will be useful, but\n"
692 " WITHOUT ANY WARRANTY; without even the implied warranty of\n"
693 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General\n"
694 " Public License for more details.\n\n"
695 " You should have received a copy of the GNU General Public License along\n"
696 " with userv; if not, write to Ian Jackson <ian@chiark.greenend.org.uk> or\n"
697 " to the Free Software Foundation, 59 Temple Place - Suite 330, Boston,\n"
698 " MA 02111-1307, USA.\n",
699 stdout) < 0) syscallerror("write usage to stderr");
703 static void of_builtin(const struct optioninfo *oip, const char *value, char *key) {
704 overridetype= ot_builtin;
707 static void of_override(const struct optioninfo *oip, const char *value, char *key) {
708 overridetype= ot_string;
709 overridevalue= value;
712 static void of_overridefile(const struct optioninfo *oip,
713 const char *value, char *key) {
714 overridetype= ot_file;
715 overridevalue= value;
718 static void of_spoofuser(const struct optioninfo *oip,
719 const char *value, char *key) {
723 const struct optioninfo optioninfos[]= {
724 { 'f', "file", 2, of_file },
725 { 'w', "fdwait", 2, of_fdwait },
726 { 'D', "defvar", 2, of_defvar },
727 { 't', "timeout", 1, of_timeout },
728 { 'S', "signals", 1, of_signals },
729 { 'P', "sigpipe", 0, of_sigpipe },
730 { 'H', "hidecwd", 0, of_hidecwd },
731 { 'B', "builtin", 0, of_builtin },
732 { 'h', "help", 0, of_help },
733 { 0, "copyright", 0, of_copyright },
734 { 0, "override", 1, of_override },
735 { 0, "override-file", 1, of_overridefile },
736 { 0, "spoof-user", 1, of_spoofuser },
740 static void callvalueoption(const struct optioninfo *oip, char *arg) {
742 if (oip->values == 2) {
743 equals= strchr(arg,'=');
746 usageerror("option --%s (-%c) passed argument `%s' with no `='",
747 oip->full,oip->abbrev,arg);
749 usageerror("option --%s passed argument `%s' with no `='",
753 (oip->fn)(oip,equals,arg);
755 (oip->fn)(oip,arg,0);
760 * Main thread main processing functions - in order of execution.
763 static void security_init(void) {
764 /* May not open any file descriptors. */
766 mypid= getpid(); if (mypid == (pid_t)-1) syscallerror("getpid");
767 myuid= getuid(); if (myuid == (uid_t)-1) syscallerror("getuid");
768 mygid= getgid(); if (mygid == (gid_t)-1) syscallerror("getgid");
769 ngids= getgroups(0,0); if (ngids == (gid_t)-1) syscallerror("getgroups(0,0)");
770 gidarray= xmalloc(sizeof(gid_t)*ngids);
771 if (getgroups(ngids,gidarray) != ngids) syscallerror("getgroups(ngids,)");
775 if (ngids > MAX_GIDS) miscerror("caller is in far too many gids");
778 static void parse_arguments(int *argcp, char *const **argvp) {
779 static char fd0key[]= "stdin,fd,read";
780 static char fd1key[]= "stdout,fd,write";
781 static char fd2key[]= "stderr,fd,write";
785 const struct optioninfo *oip;
789 of_file(0,"stdin",fd0key);
790 of_file(0,"stdout",fd1key);
791 of_file(0,"stderr",fd2key);
793 for (argpp= *argvp+1;
794 (argp= *argpp) && *argp == '-' && argp[1];
796 if (!*++argp) usageerror("unknown option/argument `%s'",*argpp);
797 if (*argp == '-') { /* Two hyphens */
798 if (!*++argp) { argpp++; break; /* End of options. */ }
799 for (oip= optioninfos; oip->full && strcmp(oip->full,argp); oip++);
800 if (!oip->full) usageerror("unknown long option `%s'",*argpp);
802 if (!argpp[1]) usageerror("long option `%s' needs a value",*argpp);
803 callvalueoption(oip,*++argpp);
808 for (; *argp; argp++) {
809 for (oip= optioninfos; oip->full && oip->abbrev != *argp; oip++);
810 if (!oip->full) usageerror("unknown short option `-%c' in argument `%s'",
816 if (!argpp[1]) usageerror("short option `-%c' in argument `%s' needs"
817 " a value",*argp,*argpp);
820 callvalueoption(oip,argp);
821 break; /* No more options in this argument, go on to the next one. */
828 if (overridetype == ot_builtin) {
831 if (!*argpp) usageerror("no service user given after options");
832 serviceuser= *argpp++;
834 if (!*argpp) usageerror(overridetype == ot_builtin ?
835 "no service name given after options and service user" :
836 "no builtin service given after options");
838 *argcp-= (argpp-*argvp);
841 if (*argcp > MAX_ARGSDEFVAR) usageerror("far too many arguments");
843 for (fd=0; fd<fdsetupsize; fd++) {
844 if (!fdsetup[fd].filename) continue;
845 if (fdsetup[fd].mods & (fdm_wait|fdm_nowait|fdm_close)) continue;
846 assert(fdsetup[fd].mods & (fdm_read|fdm_write));
847 fdsetup[fd].mods |= (fdsetup[fd].mods & fdm_read) ? fdm_close : fdm_wait;
851 static void determine_users(void) {
859 logname= getenv("LOGNAME");
860 if (!logname) logname= getenv("USER");
862 pw= getpwnam(logname);
863 if (!pw || pw->pw_uid != myuid) logname= 0;
866 pw= getpwuid(myuid); if (!pw) miscerror("cannot determine your login name");
867 logname= pw->pw_name;
870 if (!strcmp(serviceuser,"-")) serviceuser= logname;
871 pw= getpwnam(serviceuser);
872 if (!pw) miscerror("requested service user `%s' is not a user",serviceuser);
873 serviceuid= pw->pw_uid;
875 if ((overridetype != ot_none || spoofuser) && myuid != 0 && myuid != serviceuid)
876 miscerror("--override and --spoof options only available to root or to"
877 " the user who will be providing the service");
881 pw= getpwnam(logname);
882 if (!pw) miscerror("spoofed login name `%s' is not valid",logname);
883 spoofuid= pw->pw_uid;
884 spoofgid= pw->pw_gid;
885 ngidssize= ngids; ngids= 0;
888 gidarray= xrealloc(gidarray,sizeof(gid_t)*ngidssize);
890 gidarray[ngids++]= spoofgid;
891 while ((gr= getgrent())) { /* ouch! getgrent has no error behaviour! */
892 for (mem= gr->gr_mem; *mem && strcmp(*mem,logname); mem++);
894 if (ngids>=ngidssize) {
895 if (ngids>=MAX_GIDS) miscerror("spoofed user is member of too many groups");
896 ngidssize= (ngids+5)<<1;
897 gidarray= xrealloc(gidarray,sizeof(gid_t)*ngidssize);
899 gidarray[ngids++]= gr->gr_gid;
904 static void determine_cwd(void) {
905 cwdbufsize= 0; cwdbuf= 0;
910 if (cwdbufsize > MAX_GENERAL_STRING) { cwdbufsize= 0; free(cwdbuf); break; }
911 cwdbufsize <<= 1; cwdbufsize+= 100;
912 cwdbuf= xrealloc(cwdbuf,cwdbufsize);
913 cwdbuf[cwdbufsize-1]= 0;
914 if (getcwd(cwdbuf,cwdbufsize-1)) { cwdbufsize= strlen(cwdbuf); break; }
915 if (errno != ERANGE) { cwdbufsize= 0; free(cwdbuf); break; }
919 static void process_override(const char *servicename) {
923 switch (overridetype) {
929 l= strlen(servicename);
930 if (l >= MAX_OVERRIDE_LEN-20)
931 miscerror("builtin service string is too long (%d, max is %d)",
932 l,MAX_OVERRIDE_LEN-21);
935 snprintf(ovbuf,l,"execute-builtin %s\n",servicename);
936 ovused= strlen(ovbuf);
939 l= strlen(overridevalue);
940 if (l >= MAX_OVERRIDE_LEN)
941 miscerror("override string is too long (%d, max is %d)",l,MAX_OVERRIDE_LEN-1);
943 snprintf(ovbuf,l+2,"%s\n",overridevalue);
947 ovfile= fopen(overridevalue,"r");
948 if (!ovfile) syscallerror("open overriding configuration file `%s'",overridevalue);
949 ovbuf= 0; ovavail= ovused= 0;
950 while ((c= getc(ovfile)) != EOF) {
951 if (!c) miscerror("overriding config file `%s' contains null(s)",overridevalue);
952 if (ovused >= MAX_OVERRIDE_LEN)
953 miscerror("override file is too long (max is %d)",MAX_OVERRIDE_LEN);
954 if (ovused >= ovavail) {
955 ovavail+=80; ovavail<<=2; ovbuf= xrealloc(ovbuf,ovavail);
959 if (ferror(ovfile) || fclose(ovfile))
960 syscallerror("read overriding configuration file `%s'",overridevalue);
961 ovbuf= xrealloc(ovbuf,ovused+1);
969 static int server_connect(void) {
970 struct sockaddr_un ssockname;
972 struct sigaction sig;
976 sigaddset(&sset,SIGCHLD);
977 sigaddset(&sset,SIGALRM);
978 sigaddset(&sset,SIGPIPE);
979 if (sigprocmask(SIG_UNBLOCK,&sset,0)) syscallerror("preliminarily unblock signals");
981 sig.sa_handler= SIG_IGN;
982 sigemptyset(&sig.sa_mask);
984 if (sigaction(SIGPIPE,&sig,0)) syscallerror("ignore sigpipe");
986 sfd= socket(AF_UNIX,SOCK_STREAM,0);
987 if (!sfd) syscallerror("create client socket");
989 assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUSPATH));
990 ssockname.sun_family= AF_UNIX;
991 strcpy(ssockname.sun_path,RENDEZVOUSPATH);
993 while (connect(sfd,(struct sockaddr*)&ssockname,sizeof(ssockname))) {
994 if (errno == ECONNREFUSED || errno == ENOENT)
995 syscallerror("uservd daemon is not running - service not available");
997 syscallerror("unable to connect to uservd daemon: %m");
1003 static void server_handshake(int sfd) {
1004 srfile= fdopen(sfd,"r");
1005 if (!srfile) syscallerror("turn socket fd into FILE* for read");
1006 if (setvbuf(srfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket reads");
1008 swfile= fdopen(sfd,"w");
1009 if (!swfile) syscallerror("turn socket fd into FILE* for write");
1010 if (setvbuf(swfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket writes");
1012 xfread(&opening_mbuf,sizeof(opening_mbuf),srfile);
1013 checkmagic(opening_mbuf.magic,OPENING_MAGIC,"in opening message");
1014 if (memcmp(protocolchecksumversion,opening_mbuf.protocolchecksumversion,PCSUMSIZE))
1015 protoerror("protocol version checksum mismatch - server not same as client");
1018 static void server_preparepipes(void) {
1019 char pipepathbuf[PIPEPATHMAXLEN+2];
1022 for (fd=0; fd<fdsetupsize; fd++) {
1023 if (!fdsetup[fd].filename) continue;
1024 pipepathbuf[PIPEPATHMAXLEN]= 0;
1025 sprintf(pipepathbuf, PIPEPATHFORMAT,
1026 (unsigned long)mypid, (unsigned long)opening_mbuf.serverpid, fd);
1027 assert(!pipepathbuf[PIPEPATHMAXLEN]);
1029 if (unlink(pipepathbuf) && errno != ENOENT)
1030 syscallerror("remove any old pipe `%s'",pipepathbuf);
1031 if (mkfifo(pipepathbuf,0600)) /* permissions are irrelevant */
1032 syscallerror("create pipe `%s'",pipepathbuf);
1033 tempfd= open(pipepathbuf,O_RDWR);
1034 if (tempfd<0) syscallerror("prelim open pipe `%s' for read+write",pipepathbuf);
1035 assert(fdsetup[fd].mods & (fdm_read|fdm_write));
1037 open(pipepathbuf, (fdsetup[fd].mods & fdm_read) ? O_WRONLY : O_RDONLY);
1038 if (fdsetup[fd].pipefd<0) syscallerror("real open pipe `%s'",pipepathbuf);
1039 if (close(tempfd)) syscallerror("close prelim fd onto pipe `%s'",pipepathbuf);
1044 static void server_sendrequest(int argc, char *const *argv) {
1045 struct request_msg request_mbuf;
1049 memset(&request_mbuf,0,sizeof(request_mbuf));
1050 request_mbuf.magic= REQUEST_MAGIC;
1051 request_mbuf.clientpid= getpid();
1052 request_mbuf.serviceuserlen= strlen(serviceuser);
1053 request_mbuf.servicelen= strlen(argv[0]);
1054 request_mbuf.lognamelen= strlen(logname);
1055 request_mbuf.spoofed= spoofuser ? 1 : 0;
1056 request_mbuf.cwdlen= cwdbufsize;
1057 request_mbuf.callinguid= spoofuid;
1058 request_mbuf.ngids= ngids+1;
1059 request_mbuf.nreadfds= 0;
1060 request_mbuf.nwritefds= 0;
1061 for (fd=0; fd<fdsetupsize; fd++) {
1062 if (!fdsetup[fd].filename) continue;
1063 assert(fdsetup[fd].mods & (fdm_write|fdm_read));
1064 if (fdsetup[fd].mods & fdm_write) request_mbuf.nwritefds++;
1065 else request_mbuf.nreadfds++;
1067 request_mbuf.nargs= argc-1;
1068 request_mbuf.nvars= defvarused;
1069 request_mbuf.overridelen= ovused;
1070 xfwrite(&request_mbuf,sizeof(request_mbuf),swfile);
1071 xfwrite(serviceuser,sizeof(*serviceuser)*request_mbuf.serviceuserlen,swfile);
1072 xfwrite(argv[0],sizeof(*argv[0])*request_mbuf.servicelen,swfile);
1073 xfwrite(logname,sizeof(*logname)*request_mbuf.lognamelen,swfile);
1074 xfwrite(cwdbuf,sizeof(*cwdbuf)*request_mbuf.cwdlen,swfile);
1075 if (ovused>=0) xfwrite(ovbuf,sizeof(*ovbuf)*ovused,swfile);
1076 xfwrite(&spoofgid,sizeof(gid_t),swfile);
1077 xfwrite(gidarray,sizeof(gid_t)*ngids,swfile);
1078 xfwritefds(fdm_read,request_mbuf.nreadfds,swfile);
1079 xfwritefds(fdm_write,request_mbuf.nwritefds,swfile);
1080 for (i=1; i<argc; i++)
1081 xfwritestring(argv[i],swfile);
1082 for (i=0; i<defvarused; i++) {
1083 xfwritestring(defvararray[i].key,swfile);
1084 xfwritestring(defvararray[i].value,swfile);
1086 ul= REQUEST_END_MAGIC; xfwrite(&ul,sizeof(ul),swfile);
1090 static void server_awaitconfirm(void) {
1091 struct progress_msg progress_mbuf;
1093 getprogress(&progress_mbuf,srfile);
1094 if (progress_mbuf.type != pt_ok)
1095 protoerror("progress message during configuration phase"
1096 " unexpected type %d",progress_mbuf.type);
1099 static void prepare_asynchsignals(void) {
1100 static char stderrbuf[BUFSIZ], stdoutbuf[BUFSIZ];
1102 struct sigaction sig;
1104 if (setvbuf(stderr,stderrbuf,_IOLBF,sizeof(stderrbuf)))
1105 syscallerror("set buffering on stderr");
1106 if (setvbuf(stdout,stdoutbuf,_IOFBF,sizeof(stdoutbuf)))
1107 syscallerror("set buffering on stdout");
1109 sig.sa_handler= sighandler_chld;
1110 sigemptyset(&sig.sa_mask);
1111 sigaddset(&sig.sa_mask,SIGCHLD);
1112 sigaddset(&sig.sa_mask,SIGALRM);
1114 if (sigaction(SIGCHLD,&sig,0)) syscallerror("set up sigchld handler");
1116 sig.sa_handler= sighandler_alrm;
1117 if (sigaction(SIGALRM,&sig,0)) syscallerror("set up sigalrm handler");
1119 if (!timeout) return;
1120 if (alarm(timeout)<0) syscallerror("set up timeout alarm");
1123 static void catdup(const char *which, int from, int to) {
1124 if (dup2(from,to)<0) {
1125 blocksignals(SIG_BLOCK);
1126 fprintf(stderr,"userv: %s: cannot dup for %s: %s\n",which,
1127 to?"stdout":"stdin", strerror(errno));
1132 static void connect_pipes(void) {
1133 struct sigaction sig;
1134 char catnamebuf[sizeof(int)*3+30];
1138 for (fd=0; fd<fdsetupsize; fd++) {
1139 if (!fdsetup[fd].filename) continue;
1140 if (!(fdsetup[fd].mods & fdm_fd)) {
1142 open(fdsetup[fd].filename,fdsetup[fd].oflags|O_NOCTTY,0777);
1143 if (fdsetup[fd].copyfd<0)
1144 syscallerror("open file `%s' for fd %d",fdsetup[fd].filename,fd);
1146 blocksignals(SIG_BLOCK);
1148 fdsetup[fd].catpid= child;
1149 blocksignals(SIG_UNBLOCK);
1150 if (child==-1) syscallerror("fork for cat for fd %d",fd);
1152 snprintf(catnamebuf,sizeof(catnamebuf),"cat fd%d",fd);
1153 catnamebuf[sizeof(catnamebuf)-1]= 0;
1154 sig.sa_handler= SIG_DFL;
1155 sigemptyset(&sig.sa_mask);
1157 if (sigaction(SIGPIPE,&sig,0)) {
1158 fprintf(stderr,"userv: %s: reset sigpipe handler for cat: %s",
1159 catnamebuf,strerror(errno));
1162 reading= fdsetup[fd].mods & fdm_read;
1163 catdup(catnamebuf, fdsetup[fd].copyfd, reading ? 0 : 1);
1164 catdup(catnamebuf, fdsetup[fd].pipefd, reading ? 1 : 0);
1165 execl("/bin/cat",catnamebuf,(char*)0);
1166 fprintf(stderr,"userv: %s: cannot exec `cat': %s\n",catnamebuf,strerror(errno));
1169 if (fdsetup[fd].copyfd>2)
1170 if (close(fdsetup[fd].copyfd)) syscallerror("close real fd for %d",fd);
1171 if (close(fdsetup[fd].pipefd)) syscallerror("close pipe fd for %d",fd);
1175 static void server_sendconfirm(void) {
1176 struct event_msg event_mbuf;
1178 blocksignals(SIG_BLOCK);
1179 memset(&event_mbuf,0,sizeof(event_mbuf));
1180 event_mbuf.magic= EVENT_MAGIC;
1181 event_mbuf.type= et_confirm;
1182 xfwrite(&event_mbuf,sizeof(event_mbuf),swfile);
1184 blocksignals(SIG_UNBLOCK);
1187 static int server_awaitcompletion(void) {
1188 struct progress_msg progress_mbuf;
1190 getprogress(&progress_mbuf,srfile);
1191 if (progress_mbuf.type != pt_terminated)
1192 protoerror("progress message during execution phase"
1193 " unexpected type %d",progress_mbuf.type);
1196 return progress_mbuf.data.terminated.status;
1199 static void dispose_remaining_pipes(void) {
1203 blocksignals(SIG_BLOCK);
1204 for (fd=0; fd<fdsetupsize; fd++) {
1205 if (!(fdsetup[fd].catpid!=-1 && (fdsetup[fd].mods & fdm_close))) continue;
1206 if (kill(fdsetup[fd].catpid,SIGKILL)) syscallerror("kill cat for %d",fd);
1207 fdsetup[fd].killed= 1;
1209 blocksignals(SIG_UNBLOCK);
1212 blocksignals(SIG_BLOCK);
1214 fd<fdsetupsize && !(fdsetup[fd].catpid!=-1 && (fdsetup[fd].mods & fdm_wait));
1216 if (fd>=fdsetupsize) break;
1218 r= sigsuspend(&sset);
1219 if (r && errno != EINTR) syscallerror("sigsuspend failed in unexpected way");
1220 blocksignals(SIG_UNBLOCK);
1224 static void NONRETURNING process_exitstatus(int status) {
1225 blocksignals(SIG_BLOCK);
1227 if (systemerror) _exit(255);
1229 if (sigpipeok && signalsexit != se_stdout && WIFSIGNALED(status) &&
1230 WTERMSIG(status)==SIGPIPE && !WCOREDUMP(status)) status= 0;
1232 switch (signalsexit) {
1234 case se_numbernocore:
1235 if (WIFEXITED(status))
1236 _exit(WEXITSTATUS(status));
1237 else if (WIFSIGNALED(status))
1238 _exit(WTERMSIG(status) + (signalsexit==se_number && WCOREDUMP(status) ? 128 : 0));
1241 if (WIFEXITED(status))
1242 _exit(WEXITSTATUS(status)<=127 ? WEXITSTATUS(status) : 127);
1243 else if (WIFSIGNALED(status) && WTERMSIG(status)<=126)
1244 _exit(WTERMSIG(status)+128);
1247 printf("\n%d %d ",(status>>8)&0x0ff,status&0x0ff);
1248 if (WIFEXITED(status))
1249 printf("exited with code %d",WEXITSTATUS(status));
1250 else if (WIFSIGNALED(status))
1251 printf("killed by %s (signal %d)%s",
1252 strsignal(WTERMSIG(status)),WTERMSIG(status),
1253 WCOREDUMP(status) ? ", core dumped " : "");
1255 printf("unknown wait status");
1257 if (ferror(stdout) || fflush(stdout)) syscallerror("write exit status to stdout");
1260 if (WIFEXITED(status))
1261 _exit(WEXITSTATUS(status));
1262 else if (WIFSIGNALED(status))
1267 fprintf(stderr,"userv: unknown wait status %d\n",status);
1271 int main(int argc, char *const *argv) {
1272 int status, socketfd;
1275 # error Do not disable assertions in this security-critical code !
1279 parse_arguments(&argc,&argv);
1282 process_override(argv[0]);
1284 socketfd= server_connect();
1286 server_handshake(socketfd);
1287 server_preparepipes();
1288 server_sendrequest(argc,argv);
1290 priv_permanentlyrevokesuspended();
1292 server_awaitconfirm();
1293 prepare_asynchsignals();
1295 server_sendconfirm();
1296 status= server_awaitcompletion();
1298 dispose_remaining_pipes();
1299 process_exitstatus(status);