From 9f56f874416db295bdb50d448bd99cdd34db969d Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 2 Sep 1997 02:46:23 +0000 Subject: [PATCH] Internal review up to end of p11. --- INSTALL | 81 +++++ Makefile.in | 16 +- client.c | 14 +- common.h | 24 +- configure.in | 1 - daemon.h | 45 ++- ddebug.c => debug.c | 57 ++- language.i4 | 41 +-- lexer.l.m4 | 116 ++++++- lib.c | 12 +- lib.h | 7 +- overlord.c | 181 ++++++++++ parser.c | 231 ++++++------- daemon.c => process.c | 786 ++++++++++++++++++------------------------ servexec.c | 216 ++++++++++++ spec.sgml | 6 +- tokens.h.m4 | 61 +--- version.h | 2 +- 18 files changed, 1159 insertions(+), 738 deletions(-) create mode 100644 INSTALL rename ddebug.c => debug.c (77%) create mode 100644 overlord.c rename daemon.c => process.c (55%) create mode 100644 servexec.c diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..d7211b6 --- /dev/null +++ b/INSTALL @@ -0,0 +1,81 @@ +INSTALLATION INSTRUCTIONS: + + $ ./configure + $ make + # make install + +This will not install the documentation, which is shipped as +pre-prepared HTML and PostScript as well as debiandoc-sgml source. +Put that (spec.html/ and spec.ps) where you will. + +SYSTEM REQUIREMENTS: + +Programs: + +* md5sum (Colin Plumb's or the GNU version) +* GNU m4 +* GNU make +* GNU flex +* GCC is preferred but other compilers ought to work (though + no portability testing has yet been done). ANSI C only. + ints must be at least 32 bits. +* A sensible /bin/cat which notices write errors (ie not + SunOS4, BSD 4.3, or many others.) + +C Library: + +* [v]snprintf - a real version, not just [v]sprintf with a wrapper + that throws the argument away. +* strsignal; +* fnmatch; +* BSD syslog(3); +* strtoul; +* memcpy, memset, memcpy; +* realloc(0,size) must work and be equivalent to malloc(size). +* (not varargs) and v[sf][n]printf. + +System interfaces: + +* setreuid(2), getreuid(2), getgroups(2), initgroups(3), with + the ability for root to (a) swap euid and ruid and + (b) give away all privilege by calling setreuid(ruid,ruid) + twice. +* wait3 and waitpid, with WNOHANG, WIFSIGNALED, + WIFEXITEED, WTERMSIG, WEXITSTATUS and WCOREDUMP. +* gid_t, uid_t, pid_t. +* Unix-domain (AF_UNIX) stream sockets, for use with: + * BSD sockets - socket(), bind(), listen(), accept(), connect(); + * socketpair(2); +* lstat(2) (though stat(2) will be safe on systems without symlinks). +* Pipes: + * creating using pipe(2) and mkfifo(2); + * proper interaction between open(O_RDWR), open(O_RDONLY), + open(O_WRONLY), close(), dup2, EPIPE, SIGPIPE, &c.; +* POSIX signal handling - sigaction(2), sigprocmask(2), sigsuspend(2); + +To format the documentation: + +* debiandoc-sgml, and hence sp (aka nsgmls) and sgmlspm. +* For PostScript output, Lout and possibly psutils. + +For debugging version (./configure --enable-debug): + +* initgroups(3) must use setgroups(2) and dynamic + linking must allow overriding setgroups(2) for initgroups(3); + +REENTRANCY IN THE LIBC: + +We assume, both in the client and server, that it is safe to use one +stdio stream in a signal handler which interrupts use of a _different_ +stdio stream in another. We make sure using setvbuf that we have +pre-allocated buffers so that stdio doesn't need to use malloc() when +we actually read or write. stdio had better not do anything else +weird. + +Furthermore, we assume that it is safe to use syslog in a signal +handler which has interrupted a stdio operation (but we don't require +that it be safe to invoke when the signal has interrupted a call to +malloc, unless stdio makes gratuitous mallocs). openlog will already +have been called (but syslog will not necessarily have been called). + +We assume that strerror is completely reentrant. diff --git a/Makefile.in b/Makefile.in index 2e7f5c5..0df4f95 100644 --- a/Makefile.in +++ b/Makefile.in @@ -39,11 +39,12 @@ etcsubdir=$(etcdir)/userv SOURCES= Makefile.in configure.in acconfig.h \ client.c common.h version.h \ - daemon.c daemon.h ddebug.c parser.c lib.c lib.h \ + overlord.c process.c servexec.c \ + daemon.h debug.c parser.c lib.c lib.h \ language.i4 lexer.l.m4 tokens.h.m4 ALSOSHIP= system.default system.override \ spec.sgml overview.fig overview.ps \ - COPYING buildship install-sh .cvsignore + COPYING INSTALL buildship install-sh .cvsignore GENSHIP= lexer.l lexer.c tokens.h configure config.h.in \ spec.html spec.ps overview.ps @@ -60,17 +61,22 @@ install: all if ! test -f $(etcsubdir)/system.override; then \ $(INSTALL_DATA) -o root -g root system.override $(etcsubdir); fi -daemon: daemon.o parserlexer.o ddebug.o lib.o +daemon: overlord.o process.o servexec.o parserlexer.o debug.o lib.o + $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) lexer.l: language.i4 client.o: config.h common.h pcsum.h version.h -daemon.o: config.h common.h pcsum.h daemon.h lib.h tokens.h +process.o: config.h common.h pcsum.h daemon.h lib.h tokens.h + +overlord.o: config.h common.h pcsum.h daemon.h + +servexec.o: config.h common.h pcsum.h daemon.h lib.h lib.o: config.h lib.h -ddebug.o: config.h common.h pcsum.h daemon.h lib.h tokens.h +debug.o: config.h common.h pcsum.h daemon.h lib.h tokens.h parserlexer.o: lexer.c parser.c config.h common.h pcsum.h daemon.h lib.h tokens.h # lexer.c #include's parser.c at the end. Blame flex. diff --git a/client.c b/client.c index 9d12491..bee10a1 100644 --- a/client.c +++ b/client.c @@ -264,6 +264,7 @@ static void xfwrite(const void *p, size_t sz, FILE *file) { static void xfwritestring(const char *s, FILE *file) { int l; l= strlen(s); + assert(l<=MAX_GENERAL_STRING); xfwrite(&l,sizeof(l),file); xfwrite(s,sizeof(*s)*l,file); } @@ -419,7 +420,12 @@ static void of_fdwait(const struct optioninfo *oip, const char *value, char *key static void of_defvar(const struct optioninfo *oip, const char *value, char *key) { int i; + if (strlen(key)>MAX_GENERAL_STRING) + usageerror("variable name `%s' is far too long",key); + if (strlen(value)>MAX_GENERAL_STRING) + usageerror("variable `%s' has value `%s' which is far too long",key,value); for (i=0; i= MAX_ARGSDEFVARS) usageerror("far too many --defvar or -D options"); if (i>=defvarsavail) { defvarsavail+=10; defvarsavail<<=1; defvarsarray= xrealloc(defvarsarray,sizeof(const char*)*2*defvarsavail); @@ -761,7 +767,9 @@ int main(int argc, char *const *argv) { argc-= (argpp-argv); argv= argpp; - + if (argc > MAX_ARGSDEFVARS) usageerror("far too many arguments"); + if (ngids > MAX_GIDS) miscerror("caller is in far too many gids"); + pw= getpwnam(serviceuser); if (!pw) miscerror("requested service user `%s' is not a user",serviceuser); serviceuid= pw->pw_uid; @@ -972,6 +980,7 @@ int main(int argc, char *const *argv) { if (fdsetup[fd].catpid==-1) syscallerror("fork for cat for fd %d",fd); if (!fdsetup[fd].catpid) { snprintf(catnamebuf,sizeof(catnamebuf),"cat fd%d",fd); + catnamebuf[sizeof(catnamebuf)-1]= 0; sig.sa_handler= SIG_DFL; sigemptyset(&sig.sa_mask); sig.sa_flags= 0; @@ -980,11 +989,10 @@ int main(int argc, char *const *argv) { catnamebuf,strerror(errno)); exit(-1); } - catnamebuf[sizeof(catnamebuf)-1]= 0; reading= fdsetup[fd].mods & fdm_read; catdup(catnamebuf, fdsetup[fd].copyfd, reading ? 0 : 1); catdup(catnamebuf, fdsetup[fd].pipefd, reading ? 1 : 0); - execlp("cat",catnamebuf,(char*)0); + execl("/bin/cat",catnamebuf,(char*)0); fprintf(stderr,"userv: %s: cannot exec `cat': %s\n",catnamebuf,strerror(errno)); exit(-1); } diff --git a/common.h b/common.h index 539ecf0..5fdfd76 100644 --- a/common.h +++ b/common.h @@ -32,34 +32,32 @@ static const unsigned char protocolchecksumversion[PCSUMSIZE]= { # define VARDIR "/var/run/userv" #endif -#define DIRSEP "/" - #ifndef RENDEZVOUS # define RENDEZVOUS "socket" #endif #ifndef RENDEZVOUSPATH -# define RENDEZVOUSPATH VARDIR DIRSEP RENDEZVOUS +# define RENDEZVOUSPATH VARDIR "/" RENDEZVOUS #endif #ifndef PIPEFORMAT -# ifdef AC_SYS_LONG_FILENAMES -# define PIPEFORMAT "pipe.%lu.%lu.%d" -# define PIPEFORMATEXTEND (sizeof(long)*3*2+sizeof(int)*3+1) -# else -# define PIPEFORMAT "%lx.%lx.%x" -# define PIPEFORMATEXTEND (sizeof(long)*2*2+sizeof(int)*2+1) -# endif +# define PIPEFORMAT "%lx.%lx.%x" +# define PIPEPATTERN "[0-9a-f]*.[0-9a-f]*.*[0-9a-f]" +# define PIPEFORMATEXTEND (sizeof(long)*2*2+sizeof(int)*2+1) +# define PIPEMAXLEN (sizeof(PIPEFORMAT)+PIPEFORMATEXTEND) #endif #ifndef PIPEPATHFORMAT -# define PIPEPATHFORMAT VARDIR DIRSEP PIPEFORMAT +# define PIPEPATHFORMAT VARDIR "/" PIPEFORMAT # define PIPEPATHMAXLEN (sizeof(PIPEPATHFORMAT)+PIPEFORMATEXTEND) #endif -#define MAX_ALLOW_FD 255 +#define MAX_ALLOW_FD 1024 #define MAX_INCLUDE_NEST 40 -#define MAX_OVERRIDE_LEN (1024*1024) +#define MAX_GENERAL_STRING (1024*1024) +#define MAX_OVERRIDE_LEN MAX_GENERAL_STRING +#define MAX_ARGSDEFVARS 4096 +#define MAX_GIDS 1024 #ifdef DEBUG # define BASE_MAGIC 0x5deb7567 /* "\x5d\xebug" */ diff --git a/configure.in b/configure.in index 4d8ab26..b42a728 100644 --- a/configure.in +++ b/configure.in @@ -39,7 +39,6 @@ AC_ARG_ENABLE(debug, AC_PROG_CC AC_PROG_INSTALL -AC_SYS_LONG_FILE_NAMES AC_SUBST(OPTIMISE) if test "${GCC-no}" = yes; then diff --git a/daemon.h b/daemon.h index e37b862..25b6f71 100644 --- a/daemon.h +++ b/daemon.h @@ -43,6 +43,18 @@ # endif #endif +#ifndef DEFAULTPATH_USER +# define DEFAULTPATH_USER "/usr/local/bin:/bin:/usr/bin" +#endif + +#ifndef DEFAULTPATH_ROOT +# define DEFAULTPATH_ROOT "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin" +#endif + +#ifndef SETENVIRONMENT +# define SETENVIRONMENT "environment" +#endif + #define USERRCFILE "rc" #define SYSTEMUSERVCONFIGDIR "userv" #define SHELLLIST "shells" @@ -52,15 +64,16 @@ #define DEFAULTINCLUDELOOKUP ":default" #define EMPTYINCLUDELOOKUP ":empty" -#define USERDIRPREFIX USERDIR DIRSEP +#define USERDIRPREFIX USERDIR "/" #define USERCONFIGDIRBASE SYSTEMUSERVCONFIGDIR #define USERCONFIGDIR HIDDENPREFIX USERCONFIGDIRBASE -#define USERUSERVCONFIGPATH USERDIR DIRSEP USERCONFIGDIR -#define USERRCFILEPATH USERUSERVCONFIGPATH DIRSEP USERRCFILE -#define SYSTEMUSERVCONFIGPATH SYSTEMCONFIGDIR DIRSEP SYSTEMUSERVCONFIGDIR -#define SYSTEMRCFILEDEFAULTPATH SYSTEMUSERVCONFIGPATH DIRSEP SYSTEMRCFILEDEFAULT -#define SYSTEMRCFILEOVERRIDEPATH SYSTEMUSERVCONFIGPATH DIRSEP SYSTEMRCFILEOVERRIDE -#define SHELLLISTPATH SYSTEMCONFIGDIR DIRSEP SHELLLIST +#define USERUSERVCONFIGPATH USERDIR "/" USERCONFIGDIR +#define USERRCFILEPATH USERUSERVCONFIGPATH "/" USERRCFILE +#define SYSTEMUSERVCONFIGPATH SYSTEMCONFIGDIR "/" SYSTEMUSERVCONFIGDIR +#define SYSTEMRCFILEDEFAULTPATH SYSTEMUSERVCONFIGPATH "/" SYSTEMRCFILEDEFAULT +#define SYSTEMRCFILEOVERRIDEPATH SYSTEMUSERVCONFIGPATH "/" SYSTEMRCFILEOVERRIDE +#define SHELLLISTPATH SYSTEMCONFIGDIR "/" SHELLLIST +#define SETENVIRONMENTPATH SYSTEMCONFIGDIR "/" SETENVIRONMENT #define USERDIR "~" #define HIDDENPREFIX "." @@ -97,7 +110,7 @@ #define MAX_ERRMSG_LEN 2048 #define ERRMSG_RESERVE_ERRNO 128 -int parse_string(const char *string, const char *descrip); +int parse_string(const char *string, const char *descrip, int isinternal); void parseerrprint(const char *fmt, ...) PRINTFFORMAT(1,2); void ensurelogopen(int wantfacility); void ensurefdarray(int fd); @@ -111,6 +124,10 @@ void debug_dumpparameter(const char *parm, char **values); pid_t nondebug_fork(void); const char *nondebug_serviceuserdir(const char *ifnondebug); +void execservice(const int synchsocket[], int clientfd) NONRETURNING; +void servicerequest(int sfd) NONRETURNING; +int synchread(int fd, int ch); + struct fdstate { int iswrite, realfd, holdfd; int wantstate; @@ -120,7 +137,6 @@ struct fdstate { /* tokv_word_read, tokv_word_write */ }; -extern gid_t *gidarray; extern char **argarray; extern char *((*defvararray)[2]); extern struct fdstate *fdarray; /* indexed by nominal fd */ @@ -130,14 +146,13 @@ extern struct request_msg request_mbuf; extern char *serviceuser, *service, *logname, *cwd; extern char *overridedata, *userrcfile; extern char *serviceuser_dir, *serviceuser_shell, *callinguser_shell; -extern uid_t serviceuser_uid, callinguser_uid; -extern gid_t serviceuser_gid; +extern int service_ngids; +extern gid_t *calling_gids, *service_gids; +extern const char **calling_groups, **service_groups; +extern uid_t serviceuser_uid; extern char *execpath, **execargs; extern int execute; /* One of the execution modes tokt_execmode */ extern int setenvironment, suppressargs, disconnecthup; -extern int ehandling; /* One of the error handling modes tokt_ehandlemode */ -extern int ehlogfacility, ehloglevel, syslogopenfacility, ehfilekeep; -extern FILE *ehfile; -extern char *ehfilename; +extern int syslogopenfacility; #endif diff --git a/ddebug.c b/debug.c similarity index 77% rename from ddebug.c rename to debug.c index 1ea8a06..9174d83 100644 --- a/ddebug.c +++ b/debug.c @@ -96,6 +96,12 @@ static void truefalsedump(const char *whichstr, int val) { printf("%s: %s\n",whichstr,val?"yes":"no"); } +static void groupsdump(int ngids, const gid_t *gids, const char *const *groups) { + int i; + + for (i=0; i max"); return tokv_error; - } - assert(!*ep); }; ') -nametypelexpatexec(`fdstoend',`tokt_fdrange|tokr_punct',`[0-9]{1,8}-', -`{ char *ep; - lr_min= (int)strtoul(yytext,&ep,10); lr_max=-1; - assert(*ep == HYPHEN); assert(!*++ep); }; ') +dnl some of these have two tokt_ bits set, because they can be several things. +autovalistype(`ordinal', `tokt_number|tokt_fdrange|tokr_word') +autovalistype(`fdrange', `tokt_fdrange|tokr_punct') +autovalistype(`fdstoend', `tokt_fdrange|tokr_punct') nametypelexpatexec(`dollar',`tokt_misc|tokr_punct',`\$',`') dnl non-word things diff --git a/lexer.l.m4 b/lexer.l.m4 index 2f9dee6..0337686 100644 --- a/lexer.l.m4 +++ b/lexer.l.m4 @@ -44,8 +44,75 @@ include(language.i4) #define HYPHEN '-' -static int lineno, atnewline, notedreferer; +typedef int directive_fnt(int dtoken); +static directive_fnt df_reject, df_execute, df_executefrompath; +static directive_fnt df_executefromdirectory; +static directive_fnt df_errorstostderr, df_errorstosyslog, df_errorstofile; +static directive_fnt dfg_fdwant, dfg_setflag; +static directive_fnt df_reset, df_cd, df_userrcfile, df_include; +static directive_fnt df_includelookup, df_includedirectory; +static directive_fnt df_message, df_error, df_quit, df_eof; +static directive_fnt df_if, df_catchquit, df_errorspush; +static directive_fnt dfi_includeuserrcfile, dfi_includeclientconfig; +/* directive functions return: + * 0 for success having scanned up to and including end of line but not beyond, + * or tokv_error or tokv_quit. + */ + +typedef int parmcondition_fnt(int ctoken, char **parmvalues, int *rtrue); +static parmcondition_fnt pcf_glob, pcf_range, pcf_grep; +/* all conditional functions return tokv_error for failure or 0 for success + * at parsing and testing, in which case *rtrue is set to 0 or 1. + * On success they have scanned up to and including the condition's + * terminating newline. + * The parameter-based conditionals take a list of parameter values + * as obtained from the parameter functions and pa_parameter, + * and do _not_ free it. + */ + +typedef int parameter_fnt(int ptoken, char ***rvalues); +static parameter_fnt pf_service; +static parameter_fnt pf_callinguser, pf_serviceuser; +static parameter_fnt pf_callinggroup, pf_servicegroup; +static parameter_fnt pf_callingusershell, pf_serviceusershell; +/* Parameter functions return tokv_error or 0 for success at parsing + * and determining the value, in which case *rvalues is made to be + * a mallocd null-terminated array of pointers to mallocd strings. + * freeparm can be used to free such an array. + */ + +static int yylex(void); +/* Returns a token (which may be an eof or error exception) */ + +static directive_fnt *lr_dir; +static parmcondition_fnt *lr_parmcond; +static parameter_fnt *lr_parameter; +static int lr_loglevel, lr_logfacility, lr_min, lr_max, *lr_flag; +static int lr_flagval, lr_controlend; +static int lr_fdwant_readwrite; /* -1=never, 0=opt, 1=always */ + +/* Forward declarations of things used in lexer and parser */ + +struct parser_state { + int lineno, reportlineno, notedreferer, isinternal; + const char *filename; + YY_BUFFER_STATE ybuf; + struct parser_state *upstate; +}; + +static struct parser_state *cstate; + +struct error_handling { + int handling; /* One of the error handling modes tokt_ehandlemode */ + int logfacility, loglevel, filekeep; + FILE *file; + char *filename; +}; + +static struct error_handling eh = { tokv_word_errorstostderr, 0,0,0,0,0 }; + static int dequote(char *inplace); + %} %option noyywrap %% @@ -54,14 +121,49 @@ dnl simple words undivert(3) changequote({*,*}) {* +[0-9]{1,8} { + char *ep; + lr_min=lr_max= (int)strtoul(yytext,&ep,10); + assert(!*ep); + return tokv_ordinal; + } +[0-9]{1,8}-[0-9]{1,8} { + char *ep; + lr_min=(int)strtoul(yytext,&ep,10); + assert(*ep == HYPHEN); + assert(*++ep); + lr_max=(int)strtoul(ep,&ep,10); + assert(!*ep); + if (lr_max < lr_min) { + parseerrprint("fd range has min > max"); + return tokv_error; + } + return tokv_fdrange; + } +[0-9]{1,8}- { + char *ep; + lr_min= (int)strtoul(yytext,&ep,10); + assert(*ep == HYPHEN); + assert(!*++ep); + lr_max=-1; + return tokv_fdstoend; + } [\ \t]+ return tokv_lwsp; -[\ \t]*\n atnewline= 1; lineno++; return tokv_newline; -[\ \t]*\#[^\n]*\n atnewline= 1; lineno++; return tokv_newline; -[\ \t]*\#[^\n]* atnewline= 0; parseerrprint("missing newline at eof after comment"); return tokv_error; -[^\ \t\n]+ atnewline= 0; return tokv_barestring; +[\ \t]*\n cstate->lineno++; return tokv_newline; +[\ \t]*\#[^\n]*\n cstate->lineno++; return tokv_newline; +[\ \t]*\#[^\n]* { + parseerrprint("missing newline at eof after comment"); + return tokv_error; + } +[^\ \t\n]+ return tokv_barestring; +\"([^\\\"\n]|\\[a-z]|\\[0-9]{3}|\\x[0-9A-Fa-f]{2}|\\[:punct:]|\\[ \t]*\n)*\" { + return dequote(yytext); + } +\".* { + parseerrprint("misquoted or unterminated string"); + return tokv_error; + } <> return tokv_eof; -\"([^\\\"\n]|\\[a-z]|\\[0-9]{3}|\\x[0-9a-f]{2}|\\[:punct:]|\\[ \t]*\n)*\" return dequote(yytext); -\".* atnewline= 0; parseerrprint("misquoted or unterminated string"); return tokv_error; *} changequote(`,') %% diff --git a/lib.c b/lib.c index 4af9b80..6c3cd0a 100644 --- a/lib.c +++ b/lib.c @@ -29,7 +29,7 @@ #include "config.h" #include "lib.h" -char *xmstrcat3save(const char *a, const char *b, const char *c) { +char *xstrcat3save(const char *a, const char *b, const char *c) { char *r; r= xmalloc(strlen(a)+strlen(b)+strlen(c)+1); @@ -39,7 +39,7 @@ char *xmstrcat3save(const char *a, const char *b, const char *c) { return r; } -char *xmstrsave(const char *s) { +char *xstrsave(const char *s) { char *r; r= xmalloc(strlen(s)+1); @@ -47,7 +47,7 @@ char *xmstrsave(const char *s) { return r; } -char *xmstrsubsave(const char *begin, int len) { +char *xstrsubsave(const char *begin, int len) { char *r; r= xmalloc(len+1); @@ -67,12 +67,6 @@ void *xrealloc(void *p, size_t s) { return p; } -char *xstrdup(const char *str) { - char *r; - r= xmalloc(strlen(str)+1); - strcpy(r,str); return r; -} - void makeroom(char **buffer, int *size, int needed) { if (*size >= needed) return; *buffer= xrealloc(*buffer,needed); diff --git a/lib.h b/lib.h index 8a9c348..fc9d016 100644 --- a/lib.h +++ b/lib.h @@ -22,15 +22,14 @@ #ifndef LIB_H #define LIB_H -char *xmstrcat3save(const char *a, const char *b, const char *c); -char *xmstrsave(const char *s); -char *xmstrsubsave(const char *begin, int len); +char *xstrcat3save(const char *a, const char *b, const char *c); +char *xstrsave(const char *s); +char *xstrsubsave(const char *begin, int len); void miscerror(const char *what) NONRETURNING; void syscallerror(const char *what) NONRETURNING; void *xmalloc(size_t s); void *xrealloc(void *p, size_t s); -char *xstrdup(const char *str); void makeroom(char **buffer, int *size, int needed); /* It doesn't appear to be documented whether [v]snprintf put a null diff --git a/overlord.c b/overlord.c new file mode 100644 index 0000000..be2553a --- /dev/null +++ b/overlord.c @@ -0,0 +1,181 @@ +/* + * userv - overlord.c + * daemon main program, collects request and forks handlers + * + * Copyright (C)1996-1997 Ian Jackson + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with userv; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "common.h" +#include "daemon.h" + +static void checkstalepipes(void) { + /* There is an unimportant race here. If there is a stale pipe but + * another pair of processes with the same pids is about to create a + * new one we can check that the pipe is stale before they recreate + * it but then only remove it afterwards; then we remove the pipe + * they're actually using causing that invocation to fail with + * ENOENT on the pipe. However, this can only happen if things are + * already shafted, because we check for stale pipes at startup + * before any children have been started, and then only when a child + * dies unhelpfully - and we actually have to have some stale pipes + * for the race to exist. + */ + DIR *dir; + struct dirent *de; + struct stat stab; + time_t now; + unsigned long timediff; + int r; + + if (time(&now) == -1) { syslog(LOG_ERR,"get current time: %m"); return; } + dir= opendir("."); + if (!dir) { syslog(LOG_ERR,"open directory " VARDIR ": %m"); return; } + while ((de= readdir(dir))) { + if (fnmatch(PIPEPATTERN,de->d_name,FNM_PATHNAME|FNM_PERIOD)) continue; + r= lstat(de->d_name,&stab); if (r && errno==ENOENT) continue; + if (r) { syslog(LOG_ERR,"could not stat `" VARDIR "/%s': %m",de->d_name); continue; } + timediff= (unsigned long)now - (unsigned long)stab.st_ctime; + if (timediff >= (~0UL>>1) || timediff < 3600) continue; + if (unlink(de->d_name) && errno!=ENOENT) + syslog(LOG_ERR,"could not remove stale pipe `%s': %m",de->d_name); + } + if (closedir(dir)) syslog(LOG_ERR,"close directory " VARDIR ": %m"); +} + +static void sighandler_chld(int x) { + pid_t r; + int status, es; + + es= errno; + for (;;) { + r= waitpid((pid_t)-1,&status,WNOHANG); + if (!r || (r==-1 && errno==ECHILD)) break; + if (r==-1) { syslog(LOG_ERR,"wait in sigchild handler gave error: %m"); break; } + if (WIFSIGNALED(status)) { + if (WCOREDUMP(status)) + syslog(LOG_ERR,"call pid %ld dumped core due to signal %s",(long)r, + strsignal(WTERMSIG(status))); + else + syslog(LOG_ERR,"call pid %ld died due to signal %s", + (long)r,strsignal(WTERMSIG(status))); + } else if (!WIFEXITED(status)) { + syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %ld", + (long)r,status); + } else if (WEXITSTATUS(status)>12) { + if (WEXITSTATUS(status)>24) + syslog(LOG_ERR,"call pid %ld exited with status %ld >24", + (long)r,WEXITSTATUS(status)); + checkstalepipes(); + } + } + errno= es; + return; +} + +static void blocksignals(int how) { + int r; + sigset_t set; + + sigemptyset(&set); + sigaddset(&set,SIGCHLD); + r= sigprocmask(how,&set,0); assert(!r); +} + +int main(int argc, char *const *argv) { + int mfd, sfd, csocklen, e; + struct sigaction childact; + struct sockaddr_un ssockname, csockname; + pid_t child; + +#ifdef NDEBUG + abort(); /* Do not disable assertions in this security-critical code ! */ +#endif + + if (argc>1) { fputs("usage: uservd\n",stderr); exit(3); } + + openlog(USERVD_LOGIDENT,LOG_NDELAY,USERVD_LOGFACILITY); + + if (chdir(VARDIR)) { syslog(LOG_CRIT,"cannot change to " VARDIR ": %m"); exit(4); } + checkstalepipes(); + + mfd= socket(AF_UNIX,SOCK_STREAM,0); + if (!mfd) { syslog(LOG_CRIT,"cannot create master socket: %m"); exit(4); } + + assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUS)); + ssockname.sun_family= AF_UNIX; + strcpy(ssockname.sun_path,RENDEZVOUS); + unlink(RENDEZVOUS); + if (bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname))) + { syslog(LOG_CRIT,"cannot bind master socket: %m"); exit(4); } + if (listen(mfd,5)) + { syslog(LOG_CRIT,"cannot listen on master socket: %m"); exit(4); } + + childact.sa_handler= sighandler_chld; + sigemptyset(&childact.sa_mask); + childact.sa_flags= SA_NOCLDSTOP; + if (sigaction(SIGCHLD,&childact,0)) + { syslog(LOG_CRIT,"cannot setup sigchld handler: %m"); exit(4); } + syslog(LOG_NOTICE,"started"); + for (;;) { + csocklen= sizeof(csockname); + blocksignals(SIG_UNBLOCK); + sfd= accept(mfd,(struct sockaddr*)&csockname,&csocklen); + e= errno; + blocksignals(SIG_BLOCK); + if (sfd<0) { + errno= e; + if (errno == EINTR) continue; + if (errno == ENOMEM) { + syslog(LOG_ERR,"unable to accept connection: %m"); + continue; + } else { + syslog(LOG_CRIT,"unable to accept new connections: %m"); + exit(5); + } + } + child= nondebug_fork(); + if (child == (pid_t)-1) { + syslog(LOG_ERR,"unable to fork server: %m"); + close(sfd); + continue; + } + if (!child) { + close(mfd); + closelog(); + blocksignals(SIG_UNBLOCK); + servicerequest(sfd); + } + close(sfd); + } +} diff --git a/parser.c b/parser.c index 2395837..0f8e195 100644 --- a/parser.c +++ b/parser.c @@ -22,16 +22,6 @@ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -struct parser_state { - int lineno, atnewline, notedreferer; - const char *filename; - YY_BUFFER_STATE ybuf; - struct parser_state *upstate; -}; - -static const char *currentfile= 0; -static struct parser_state *parser_topstate= 0; - static directive_fnt *lr_dir=0; static parmcondition_fnt *lr_parmcond=0; static parameter_fnt *lr_parameter=0; @@ -42,8 +32,16 @@ static int lr_fdwant_readwrite; static void useless(void) { (void)yyunput; (void)useless; /* to shut up GCC ! */ } static void closeerrorfile(void) { - if (ehfile && !ehfilekeep) { fclose(ehfile); free(ehfilename); } - ehfile= 0; ehfilename= 0; ehfilekeep= 0; + if (eh.file && !eh.filekeep) { + if (fclose(eh.file)) { + eh.handling= tokv_word_errorstostderr; + parseerrprint("error writing to error log file `%s': %s", + eh.filename,strerror(errno)); + } + free(eh.filename); + } + eh.handling= 0; + eh.file= 0; eh.filename= 0; eh.filekeep= 0; } static void senderrmsg(const char *errmsg, int useehandling) { @@ -57,24 +55,24 @@ static void senderrmsg(const char *errmsg, int useehandling) { senderrmsgstderr(errmsg); break; case tokv_word_errorstosyslog: - ensurelogopen(ehlogfacility); - syslog(ehloglevel,"%s",errmsg); + ensurelogopen(eh.logfacility); + syslog(eh.loglevel,"%s",errmsg); break; case tokv_word_errorstofile: if (time(&now)==-1) syscallerror("get current time"); lt= localtime(&now); if (!lt) syscallerror("convert current time"); - if (fprintf(ehfile,"%d-%02d-%02d %02d:%02d:%02d: uservd: %s\n", + if (fprintf(eh.file,"%d-%02d-%02d %02d:%02d:%02d: uservd: %s\n", lt->tm_year+1900, lt->tm_mon+1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, errmsg) == EOF) { e= errno; - closeerrorfile(); ehandling= tokv_word_errorstofile; + closeerrorfile(); eh.handling= tokv_word_errorstofile; snyprintf(suberrmsg,sizeof(suberrmsg), "error writing to error log file `%.*s': %s;" " reverting to errors-to-stderr", - (int)(sizeof(suberrmsg)>>1),ehfilename,strerror(e)); - senderrmsg(suberrmsg,ehandling); - senderrmsg(errmsg,ehandling); + (int)(sizeof(suberrmsg)>>1),eh.filename,strerror(e)); + senderrmsg(suberrmsg,eh.handling); + senderrmsg(errmsg,eh.handling); } break; default: @@ -82,22 +80,22 @@ static void senderrmsg(const char *errmsg, int useehandling) { } } -static void errwhere(int iflineno, const char *filename, int *ifnotedreferer, - struct parser_state *upstate, char *bufput, int bufputlen) { +static void errwhere(struct parser_state *tstate, char *bufput, int bufputlen) { static const char suffix[]= "references ..."; char errmsg[MAX_ERRMSG_LEN]; - if (!upstate) { - filename= ""; - } else if (!*ifnotedreferer && upstate->upstate && upstate->upstate->upstate) { - errwhere(upstate->lineno-upstate->atnewline,upstate->filename, - &upstate->notedreferer,upstate->upstate, - errmsg,sizeof(errmsg)-sizeof(suffix)); + if (!tstate) { + strnycpy(bufput,": ",bufputlen); + return; + } + if (!tstate->notedreferer && tstate->upstate && !tstate->upstate->isinternal) { + errwhere(tstate->upstate,errmsg,sizeof(errmsg)-sizeof(suffix)); strcat(errmsg,suffix); - senderrmsg(errmsg,ehandling); - *ifnotedreferer= 1; + senderrmsg(errmsg,eh.handling); + tstate->notedreferer= 1; } - snyprintf(bufput,bufputlen,"%.*s:%d: ",bufputlen-10,filename,iflineno); + snyprintf(bufput,bufputlen,"%.*s:%d: ",bufputlen-10, + tstate->filename,tstate->reportlineno); } void parseerrprint(const char *fmt, ...) { @@ -105,10 +103,9 @@ void parseerrprint(const char *fmt, ...) { char errmsg[MAX_ERRMSG_LEN]; va_start(al,fmt); - errwhere(lineno-atnewline,currentfile,¬edreferer,parser_topstate, - errmsg,sizeof(errmsg)>>1); + errwhere(cstate,errmsg,sizeof(errmsg)>>1); vsnytprintfcat(errmsg,sizeof(errmsg),fmt,al); - senderrmsg(errmsg,ehandling); + senderrmsg(errmsg,eh.handling); va_end(al); } @@ -209,44 +206,35 @@ static const char *string2path(const char *in) { static char *p=0; static int pl=0; - int l; - - if (strncmp(in,USERDIRPREFIX,sizeof(USERDIRPREFIX)-1)) return in; - l= strlen(serviceuser_dir)+strlen(in)+1-(sizeof(USERDIRPREFIX)-1)+ - sizeof(DIRSEP)-1; - if (l>pl) { p= realloc(p,l); pl=l; } - strcpy(p,serviceuser_dir); strcat(p,DIRSEP); - strcat(p,in+(sizeof(USERDIRPREFIX)-1)); + if (strncmp(in,"~/",2)) return in; + makeroom(&p,&pl,strlen(serviceuser_dir)+1+strlen(in+2)+1); + snyprintf(p,pl,"%s/%s",serviceuser_dir,in+2); return p; } -static void parser_push(struct parser_state *saveinto, - const char *newfile) { - saveinto->lineno= lineno; - saveinto->atnewline= atnewline; - saveinto->filename= currentfile; - saveinto->notedreferer= notedreferer; - saveinto->ybuf= YY_CURRENT_BUFFER; - saveinto->upstate= parser_topstate; - - lineno= 1; - notedreferer= 0; - currentfile= newfile; - parser_topstate= saveinto; +static void parser_push(struct parser_state *usestate, + const char *newfile, + YY_BUFFER_STATE ybuf, + int isinternal) { + usestate->lineno= 1; + usestate->reportlineno= 1; + usestate->filename= newfile; + usestate->notedreferer= 0; + usestate->isinternal= isinternal; + usestate->ybuf= ybuf; + usestate->upstate= cstate; + + cstate= usestate; + yy_switch_to_buffer(ybuf); } static void parser_pop(void) { - YY_BUFFER_STATE ybuf; - ybuf= YY_CURRENT_BUFFER; - - lineno= parser_topstate->lineno; - atnewline= parser_topstate->atnewline; - currentfile= parser_topstate->filename; - notedreferer= parser_topstate->notedreferer; - if (parser_topstate->ybuf) yy_switch_to_buffer(parser_topstate->ybuf); - parser_topstate= parser_topstate->upstate; - - yy_delete_buffer(ybuf); + struct parser_state *oldstate; + + oldstate= cstate; + cstate= cstate->upstate; + if (cstate) yy_switch_to_buffer(cstate->ybuf); + yy_delete_buffer(oldstate->ybuf); } /* parser component functions pa_ return tokv_error or 0, @@ -276,7 +264,7 @@ static int pa_mwsp(void) { static void parm_1string(char ***rvalues, const char *tocopy) { char **a; a= xmalloc(sizeof(char*)*2); - a[0]= xstrdup(tocopy); + a[0]= xstrsave(tocopy); a[1]= 0; *rvalues= a; } @@ -361,6 +349,7 @@ static int pa_condition(int *rtrue) { } else if (token == tokv_openparen) { andor= 0; actrue= -1; for (;;) { + cstate->reportlineno= cstate->lineno; r= pa_condition(&ctrue); if (r) return r; switch (andor) { case 0: assert(actrue==-1); actrue= ctrue; break; @@ -422,6 +411,7 @@ static int skip(int allowce) { int token, r; for (;;) { /* loop over lines */ + cstate->reportlineno= cstate->lineno; do { token= yylex(); } while (token == tokv_lwsp); if (token & tokt_exception) { return token; @@ -432,6 +422,7 @@ static int skip(int allowce) { r= token; while (r & tokt_controlstart) { r= skiptoeol(); if (r) return r; + cstate->reportlineno= cstate->lineno; r= skip(token); if (r & tokt_exception) return r; } } else if (!(token & tokt_directive) && !(token & tokt_condop)) { @@ -454,6 +445,7 @@ static int parser(int allowce) { int token, r; for (;;) { /* loop over lines */ + cstate->reportlineno= cstate->lineno; do { token= yylex(); } while (token == tokv_lwsp); if (token & tokt_exception) { return token; @@ -470,17 +462,16 @@ static int parser(int allowce) { } } -int parse_string(const char *string, const char *descrip) { +int parse_string(const char *string, const char *descrip, int isinternal) { /* Returns the same things as parser, except that tokv_eof * is turned into 0. */ - struct parser_state save; + struct parser_state usestate; YY_BUFFER_STATE ybuf; int r; - - parser_push(&save,descrip); + ybuf= yy_scan_string(string); if (!ybuf) syscallerror("unable to create flex buffer for internal string"); - yy_switch_to_buffer(ybuf); + parser_push(&usestate,descrip,ybuf,isinternal); r= parser(0); @@ -494,7 +485,7 @@ static int parse_file(const char *string, int *didexist) { * is turned into 0. */ static int fileparselevel= 0; - struct parser_state save; + struct parser_state usestate; YY_BUFFER_STATE ybuf; int r; FILE *file; @@ -513,10 +504,9 @@ static int parse_file(const char *string, int *didexist) { } if (didexist) *didexist= 1; - parser_push(&save,string); ybuf= yy_create_buffer(file,YY_BUF_SIZE); if (!ybuf) syscallerror("unable to create flex buffer for file"); - yy_switch_to_buffer(ybuf); + parser_push(&usestate,string,ybuf,0); fileparselevel++; r= parser(0); @@ -635,7 +625,7 @@ static char *parm_ulong(unsigned long ul) { static int parm_usernameuid(char ***rvalues, const char *name, uid_t id) { char **a; a= xmalloc(sizeof(char*)*3); - a[0]= xstrdup(name); + a[0]= xstrsave(name); a[1]= parm_ulong(id); a[2]= 0; *rvalues= a; return 0; @@ -659,7 +649,7 @@ static char *parm_gidname(gid_t id) { sprintf(ebuf,"look up group with id %lu",(unsigned long)id); syscallerror(ebuf); } - return xstrdup(ge->gr_name); + return xstrsave(ge->gr_name); } static int parm_gids(char ***rvalues, int size, gid_t *list) { @@ -677,20 +667,11 @@ static int parm_gids(char ***rvalues, int size, gid_t *list) { } int pf_callinggroup(int ptoken, char ***rvalues) { - return parm_gids(rvalues,request_mbuf.ngids,gidarray); + return parm_gids(rvalues,request_mbuf.ngids,calling_gids); } int pf_servicegroup(int ptoken, char ***rvalues) { - static int size=-1; - static gid_t *list; - - if (size == -1) { - size= getgroups(0,0); if (size == -1) syscallerror("getgroups(0,0)"); - list= xmalloc(sizeof(gid_t)*(size+1)); - if (getgroups(size,list+1) != size) syscallerror("getgroups(size,list)"); - list[0]= serviceuser_gid; - } - return parm_gids(rvalues,size,list); + return parm_gids(rvalues,service_ngids,service_gids); } int pf_callingusershell(int ptoken, char ***rvalues) { @@ -749,7 +730,7 @@ static int paa_pathargs(const char **rv, char ***newargs_r) { size= (used+5)<<2; newargs= xrealloc(newargs,sizeof(char*)*(size+1)); } - newargs[used++]= xmstrsave(yytext); + newargs[used++]= xstrsave(yytext); } newargs[used]= 0; *newargs_r= newargs; @@ -769,7 +750,7 @@ int df_execute(int dtoken) { r= paa_pathargs(&rv,&newargs); if (r) return r; execute= tokv_word_execute; freecharparray(execargs); execargs= newargs; - free(execpath); execpath= xmstrsave(rv); + free(execpath); execpath= xstrsave(rv); return 0; } @@ -797,8 +778,9 @@ int df_executefromdirectory(int dtoken) { return tokv_error; } } - l= strlen(rv)+sizeof(DIRSEP)+strlen(p); - fn= xmalloc(l); strcpy(fn,rv); strcat(fn,DIRSEP); strcat(fn,p); + l= strlen(rv)+1+strlen(p)+1; + fn= xmalloc(l); + snyprintf(fn,l,"%s/%s",rv,p); if (stat(fn,&stab)) { if (errno == ENOENT) { free(fn); freecharparray(newargs); return 0; } parseerrprint("failed to stat `%s' for execute-from-directory: %s", @@ -820,7 +802,7 @@ int df_errorstostderr(int dtoken) { int r; r= pa_mnl(); if (r) return r; - closeerrorfile(); ehandling= dtoken; return 0; + closeerrorfile(); eh.handling= dtoken; return 0; } int df_errorstosyslog(int dtoken) { @@ -845,8 +827,8 @@ int df_errorstosyslog(int dtoken) { } if (unexpected(token,tokv_newline,"end of line somewhere after errors-to-syslog")) return tokv_error; - closeerrorfile(); ehandling= tokv_word_errorstosyslog; - ehlogfacility= facility; ehloglevel= level; return 0; + closeerrorfile(); eh.handling= tokv_word_errorstosyslog; + eh.logfacility= facility; eh.loglevel= level; return 0; } int df_errorstofile(int dtoken) { @@ -864,8 +846,8 @@ int df_errorstofile(int dtoken) { parseerrprint("unable to set line buffering on errors file: %s",strerror(errno)); fclose(file); return tokv_error; } - closeerrorfile(); ehandling= tokv_word_errorstofile; - ehfile= file; ehfilename= xmstrsave(cp); ehfilekeep= 0; return 0; + closeerrorfile(); eh.handling= tokv_word_errorstofile; + eh.file= file; eh.filename= xstrsave(cp); return 0; } int dfg_setflag(int dtoken) { @@ -879,7 +861,7 @@ int df_reset(int dtoken) { int r; r= pa_mnl(); if (r) return r; - r= parse_string(RESET_CONFIGURATION,""); + r= parse_string(RESET_CONFIGURATION,"",1); assert(!r); return 0; } @@ -898,7 +880,7 @@ int df_userrcfile(int dtoken) { int r; r= paa_1path(&cp); if (r) return r; - free(userrcfile); userrcfile= xstrdup(cp); + free(userrcfile); userrcfile= xstrsave(cp); return 0; } @@ -920,7 +902,7 @@ int dfi_includeclientconfig(int dtoken) { "found but configuration not overridden"); return tokv_error; } - return parse_string(overridedata,""); + return parse_string(overridedata,"",0); } int df_include(int dtoken) { @@ -1001,10 +983,8 @@ int df_includedirectory(int dtoken) { if (!isalnum(*p)) continue; while ((c= *++p)) if (!(isalnum(c) || c=='-')) break; if (c) continue; - makeroom(&buildbuf,&buildbuflen,cpl+tel+sizeof(DIRSEP)); - strcpy(buildbuf,cp); - strcat(buildbuf,DIRSEP); - strcat(buildbuf,de->d_name); + makeroom(&buildbuf,&buildbuflen,cpl+1+tel+1); + snyprintf(buildbuf,buildbuflen,"%s/%s",cp,de->d_name); r= parse_file(buildbuf,&found); if (r) { closedir(d); return r; } if (!found) { parseerrprint("unable to open file `%s' in included directory `%s': %s", @@ -1042,9 +1022,8 @@ int df_includelookup(int dtoken) { done= 0; cpl= strlen(cp); if (!parmvalues[0]) { - makeroom(&buildbuf,&buildbuflen,cpl+sizeof(DIRSEP NONEINCLUDELOOKUP)); - strcpy(buildbuf,cp); - strcat(buildbuf,DIRSEP NONEINCLUDELOOKUP); + makeroom(&buildbuf,&buildbuflen,cpl+1+sizeof(NONEINCLUDELOOKUP)); + snyprintf(buildbuf,buildbuflen,"%s/" NONEINCLUDELOOKUP,cp); r= parse_file(buildbuf,&thisdone); if (r) { freecharparray(parmvalues); return r; } if (thisdone) done= 1; @@ -1053,10 +1032,10 @@ int df_includelookup(int dtoken) { *pp && (!done || dtoken == tokv_word_includelookupall); pp++) { makeroom(&buildbuf,&buildbuflen, - cpl+sizeof(DIRSEP)+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)); + cpl+1+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)+1); strcpy(buildbuf,cp); - strcat(buildbuf,DIRSEP); - p= *pp; q= buildbuf+cpl+sizeof(DIRSEP)-1; + p= *pp; q= buildbuf+cpl; + *q++= '/'; if (*p=='.') *q++= ':'; while ((c= *p++)) { if (c=='/') { *q++= ':'; c='-'; } @@ -1072,9 +1051,8 @@ int df_includelookup(int dtoken) { freecharparray(parmvalues); if (!done) { makeroom(&buildbuf,&buildbuflen, - cpl+sizeof(DIRSEP)+sizeof(DEFAULTINCLUDELOOKUP)); - strcpy(buildbuf,cp); - strcat(buildbuf,DIRSEP DEFAULTINCLUDELOOKUP); + cpl+1+sizeof(DEFAULTINCLUDELOOKUP)); + snyprintf(buildbuf,buildbuflen,"%s/" DEFAULTINCLUDELOOKUP,cp); r= parse_file(buildbuf,0); if (r) return r; } return 0; @@ -1091,11 +1069,8 @@ int df_catchquit(int dtoken) { } else if (r == tokv_quit || r == tokv_error) { if (r == tokv_error) { r= parse_string(RESET_CONFIGURATION, - ""); + "",1); assert(!r); - while (!atnewline) { - r= yylex(); if (r == tokv_error) return r; - } } r= skip(tokv_word_catchquit); if (r & tokt_controlend) { @@ -1118,6 +1093,7 @@ int df_if(int dtoken) { } while (r == tokv_word_elif); if (r == tokv_word_else) { r= pa_mnl(); if (r) return r; + cstate->reportlineno= cstate->lineno; if (done) r= skip(tokv_word_if); else r= parser(tokv_word_if); if (!(r & tokv_word_if)) return r; @@ -1182,29 +1158,18 @@ int df_eof(int dtoken) { } int df_errorspush(int dt) { - int saveehandling, saveehlogfacility, saveehloglevel, saveehfilekeep; - FILE *saveehfile; - char *saveehfilename; + struct error_handling save; int r; r= pa_mnl(); if (r) return r; - saveehandling= ehandling; - saveehlogfacility= ehlogfacility; - saveehloglevel= ehloglevel; - saveehfile= ehfile; - saveehfilekeep= ehfilekeep; - saveehfilename= ehfilename; - if (ehandling == tokv_word_errorstofile) ehfilekeep= 1; + save= eh; + eh.filekeep= 1; r= parser(tokv_word_errorspush); - - ehandling= saveehandling; - ehlogfacility= saveehlogfacility; - ehloglevel= saveehloglevel; - ehfile= saveehfile; - ehfilekeep= saveehfilekeep; - ehfilename= saveehfilename; + + closeerrorfile(); + eh= save; if (r & tokt_controlend) { assert(r == tokv_word_srorre); diff --git a/daemon.c b/process.c similarity index 55% rename from daemon.c rename to process.c index 97bffa5..8d20ce0 100644 --- a/daemon.c +++ b/process.c @@ -1,6 +1,6 @@ /* - * userv - daemon.c - * daemon main program + * userv - process.c + * daemon code to process one request (is parent of service process) * * Copyright (C)1996-1997 Ian Jackson * @@ -19,6 +19,24 @@ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* 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: + * the syslog() family of calls, and the associated + * syslogopenfacility variable + * swfile (stdio stream) + * + * The following objects are used in the main program unprotected + * and so must not be used in signal handlers: + * srfile + * + * child and childtokill are used for communication between the + * main thread and the signal handlers; none of the signal handlers + * return so errno is OK too. + */ + #include #include #include @@ -31,13 +49,13 @@ #include #include #include +#include +#include #include #include -#include #include +#include #include -#include -#include #include "config.h" #include "common.h" @@ -47,73 +65,94 @@ /* NB: defaults for the execution state are not set here, but in * the RESET_CONFIGURATION #define in daemon.h. */ -gid_t *gidarray=0; -char **argarray=0; -char *((*defvararray)[2])=0; -struct fdstate *fdarray=0; -int fdarraysize=0, fdarrayused=0; -int restfdwantstate= tokv_word_rejectfd, restfdwantrw= 0; +char **argarray; +char *((*defvararray)[2]); +struct fdstate *fdarray; +int fdarraysize, fdarrayused; +int restfdwantstate= tokv_word_rejectfd, restfdwantrw; struct request_msg request_mbuf; -char *serviceuser=0, *service=0, *logname=0, *cwd=0; -char *overridedata=0, *userrcfile=0; -char *serviceuser_dir=0, *serviceuser_shell=0, *callinguser_shell; +char *serviceuser, *service, *logname, *cwd; +char *overridedata, *userrcfile; +char *serviceuser_dir, *serviceuser_shell, *callinguser_shell; +int service_ngids; +gid_t *calling_gids, *service_gids; +const char **calling_groups, **service_groups; uid_t serviceuser_uid=-1; -gid_t serviceuser_gid=-1; -char *execpath=0, **execargs=0; -int execute, setenvironment, suppressargs, disconnecthup, ehandling; -int ehlogfacility=0, ehloglevel=0, ehfilekeep=0, syslogopenfacility=-1; -FILE *ehfile=0; -char *ehfilename=0; - -static FILE *swfile= 0, *srfile= 0; -static pid_t child= -1, childtokill= -1; -static const char **grouparray; - -static void sigchildhandler(int x) { - pid_t r; - int status, es; - - es= errno; +char *execpath, **execargs; +int execute; +int setenvironment, suppressargs, disconnecthup; +int syslogopenfacility=-1; + +static FILE *swfile, *srfile; +static pid_t child=-1, childtokill=-1; +static pid_t mypid; + +/* Function shared with servexec.c: */ + +int synchread(int fd, int ch) { + char synchmsg; + int r; + for (;;) { - r= waitpid((pid_t)-1,&status,WNOHANG); - if (!r || (r==-1 && errno==ECHILD)) break; - if (r==-1) { syslog(LOG_ERR,"wait in sigchild handler gave error: %m"); break; } - if (WIFSIGNALED(status)) - if (WCOREDUMP(status)) - syslog(LOG_ERR,"call pid %ld dumped core due to signal %s",(long)r, - strsignal(WTERMSIG(status))); - else - syslog(LOG_ERR,"call pid %ld died due to signal %s", - (long)r,strsignal(WTERMSIG(status))); - else if (!WIFEXITED(status)) - syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %ld", - (long)r,status); - else if (WEXITSTATUS(status)>24) - syslog(LOG_ERR,"call pid %ld exited with status %ld >24", - (long)r,WEXITSTATUS(status)); - } - errno= es; - return; + r= read(fd,&synchmsg,1); + if (r==1) break; + if (r==0) { errno= ECONNRESET; return -1; } + assert(r<0); + if (errno!=EINTR) return -1; + }; + assert(synchmsg==ch); + return 0; +} + +/* General-purpose functions; these do nothing special about signals */ + +static void blocksignals(void) { + int r; + sigset_t set; + + sigemptyset(&set); + sigaddset(&set,SIGCHLD); + sigaddset(&set,SIGPIPE); + r= sigprocmask(SIG_BLOCK,&set,0); assert(!r); } +static void xfwriteerror(void) { + if (errno != EPIPE) syscallerror("writing to client"); + blocksignals(); + ensurelogopen(USERVD_LOGFACILITY); + syslog(LOG_DEBUG,"client went away (broken pipe)"); + disconnect(8); +} + +static void xfwrite(const void *p, size_t sz, FILE *file) { + size_t nr; + nr= fwrite(p,1,sz,file); + if (nr != sz) xfwriteerror(); +} + +static void xfflush(FILE *file) { + if (fflush(file)) xfwriteerror(); +} + +/* 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. + */ + static void xfread(void *p, size_t sz) { size_t nr; nr= fread(p,1,sz,srfile); if (nr == sz) return; if (ferror(srfile)) syscallerror("reading from client"); + blocksignals(); assert(feof(srfile)); syslog(LOG_DEBUG,"client went away (unexpected EOF)"); swfile= 0; - disconnect(12); -} - -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 client"); + disconnect(8); } static char *xfreadsetstring(int l) { char *s; + assert(l<=MAX_GENERAL_STRING); s= xmalloc(l+1); xfread(s,sizeof(*s)*l); s[l]= 0; @@ -126,50 +165,38 @@ static char *xfreadstring(void) { return xfreadsetstring(l); } -static void xfflush(FILE *file) { - if (fflush(file)) syscallerror("flush client socket"); -} - -void ensurefdarray(int fd) { - if (fd < fdarrayused) return; - if (fd >= fdarraysize) { - fdarraysize= ((fd+2)<<1); - fdarray= xrealloc(fdarray,sizeof(struct fdstate)*fdarraysize); - } - while (fd >= fdarrayused) { - fdarray[fdarrayused].iswrite= -1; - fdarray[fdarrayused].realfd= -1; - fdarray[fdarrayused].wantstate= restfdwantstate; - fdarray[fdarrayused].wantrw= restfdwantrw; - fdarrayused++; +static void getevent(struct event_msg *event_r) { + int fd; + + for (;;) { + xfread(event_r,sizeof(struct event_msg)); + switch (event_r->type) { + case et_closereadfd: + fd= event_r->data.closereadfd.fd; + assert(fd>8)&0x0ff,status&0x0ff); + _exit(0); } -static const char *seei_group(int i) { - return grouparray[i]; -} -static const char *see_c_group(void) { - return see_c_list(request_mbuf.ngids,seei_group); -} +/* Functions which are called only during setup, before + * the signal asynchronicity starts. They can do anything they like. + */ -static const char *seei_gid(int i) { - static char buf[CHAR_BIT*sizeof(gid_t)/3+4]; - snyprintf(buf,sizeof(buf),"%d",gidarray[i]); - return buf; -} -static const char *see_c_gid(void) { - return see_c_list(request_mbuf.ngids,seei_gid); +void ensurefdarray(int fd) { + if (fd < fdarrayused) return; + if (fd >= fdarraysize) { + fdarraysize= ((fd+2)<<1); + fdarray= xrealloc(fdarray,sizeof(struct fdstate)*fdarraysize); + } + while (fd >= fdarrayused) { + fdarray[fdarrayused].iswrite= -1; + fdarray[fdarrayused].realfd= -1; + fdarray[fdarrayused].holdfd= -1; + fdarray[fdarrayused].wantstate= restfdwantstate; + fdarray[fdarrayused].wantrw= restfdwantrw; + fdarrayused++; + } } -static const struct servenvinfo { - const char *name; - const char *(*fn)(void); -} servenvinfos[]= { - { "USER", see_logname }, - { "LOGNAME", see_logname }, - { "HOME", see_home }, - { "SHELL", see_shell }, - { "PATH", see_path }, - { "USERV_SERVICE", see_service }, - { "USERV_CWD", see_c_cwd }, - { "USERV_USER", see_c_logname }, - { "USERV_UID", see_c_uid }, - { "USERV_GROUP", see_c_group }, - { "USERV_GID", see_c_gid }, - { 0 } -}; - -static void NONRETURNING execservice(const int synchsocket[]) { - static const char *const setenvpfargs[]= { - "/bin/sh", - "-c", - ". " SYSTEMCONFIGDIR "/environment; exec \"$@\"", - "-", - 0 - }; - int fd, realfd, holdfd, newfd, r, envvarbufsize=0, targ, nargs, i, l; - char *envvarbuf=0; - const char **args, *const *cpp; - char *const *pp; - char synchmsg; - const struct servenvinfo *sei; +static void NONRETURNING generalfailure(const char *prefix, int reserveerrno, + int errnoval, const char *fmt, va_list al) { + char errmsg[MAX_ERRMSG_LEN]; - if (dup2(fdarray[2].realfd,2)<0) { - static const char duperrmsg[]= "uservd(service): cannot dup2 for stderr\n"; - write(fdarray[2].realfd,duperrmsg,sizeof(duperrmsg)-1); - _exit(-1); + if (prefix) { + strnycpy(errmsg,prefix,sizeof(errmsg)); + strnytcat(errmsg,": ",sizeof(errmsg)); + } else { + errmsg[0]= 0; } - if (close(synchsocket[0])) syscallservfail("close parent synch socket"); + vsnytprintfcat(errmsg,sizeof(errmsg)-reserveerrno,fmt,al); + if (reserveerrno) { + strnytcat(errmsg,": ",sizeof(errmsg)); + strnytcat(errmsg,strerror(errnoval),sizeof(errmsg)); + } + senderrmsgstderr(errmsg); + syslog(LOG_DEBUG,"service failed (%s)",errmsg); + disconnect(12); +} - if (setpgid(0,0)) syscallservfail("set process group"); - synchmsg= 'y'; - r= write(synchsocket[1],&synchmsg,1); - if (r!=1) syscallservfail("write synch byte to parent"); - r= synchread(synchsocket[1],'g'); - if (r) syscallservfail("reach synch byte from parent"); +static void NONRETURNPRINTFFORMAT(1,2) failure(const char *fmt, ...) { + va_list al; - if (close(fileno(swfile))) syscallservfail("close client socket fd"); - for (fd=0; fdname; sei++) - if (setenv(sei->name,sei->fn(),1)) syscallservfail("setenv standard"); - for (i=0; ienvvarbufsize) { envvarbufsize= l; envvarbuf= xrealloc(envvarbuf,l); } - snyprintf(envvarbuf,l,"USERV_U_%s",defvararray[i][0]); - if (setenv(envvarbuf,defvararray[i][1],1)) syscallservfail("setenv defvar"); - } + va_start(al,fmt); + generalfailure(0,0,0,fmt,al); +} - nargs= 0; - if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) nargs++; - nargs++; - if (execargs) for (pp= execargs; *pp; pp++) nargs++; - if (!suppressargs) nargs+= request_mbuf.nargs; - args= xmalloc(sizeof(char*)*(nargs+1)); - targ= 0; - if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) args[targ++]= *cpp; - args[targ++]= execpath; - if (execargs) for (pp= execargs; *pp; pp++) args[targ++]= *pp; - if (!suppressargs) for (i=0; i>8)&0x0ff,status&0x0ff); - _exit(0); } -static void getevent(struct event_msg *event_r) { - int fd; +static void getgroupnames(int ngids, gid_t *list, const char ***names_r) { + const char **names; + struct group *cgrp; + int i; - for (;;) { - xfread(event_r,sizeof(struct event_msg)); - switch (event_r->type) { - case et_closereadfd: - fd= event_r->data.closereadfd.fd; - assert(fdgr_name); } + *names_r= names; } + +/* The per-request main program and its subfunctions. */ -static void NONRETURNING servicerequest(int sfd) { - struct opening_msg opening_mbuf; - struct progress_msg progress_mbuf; - struct event_msg event_mbuf; - pid_t mypid, newchild; - unsigned long ul; - int i,j, r, tempfd, fd, partsize, synchsocket[2]; - char pipepathbuf[PIPEPATHMAXLEN]; - const char *string, *delim, *nextstring; - char *part, *exectry; - char synchmsg; - struct stat stab; - struct sigaction sig; - struct group *cgrp; - struct passwd *pw; +static void setup_comms(int sfd) { + static char swbuf[BUFSIZ]; + static char srbuf[BUFSIZ]; + struct sigaction sig; + ensurelogopen(USERVD_LOGFACILITY); syslog(LOG_DEBUG,"call connected"); mypid= getpid(); if (mypid == -1) syscallerror("getpid"); + sig.sa_handler= SIG_IGN; + sigemptyset(&sig.sa_mask); + sig.sa_flags= 0; + if (sigaction(SIGPIPE,&sig,0)) syscallerror("cannot ignore sigpipe"); + srfile= fdopen(sfd,"r"); if (!srfile) syscallerror("turn socket fd into reading FILE*"); - if (setvbuf(srfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket reads"); + if (setvbuf(srfile,srbuf,_IOFBF,sizeof(srbuf))) + syscallerror("set buffering on socket reads"); swfile= fdopen(sfd,"w"); if (!swfile) syscallerror("turn socket fd into writing FILE*"); - if (setvbuf(swfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket writes"); + if (setvbuf(swfile,swbuf,_IOFBF,sizeof(swbuf))) + syscallerror("set buffering on socket writes"); +} +static void send_opening(void) { + struct opening_msg opening_mbuf; + + memset(&opening_mbuf,0,sizeof(opening_mbuf)); opening_mbuf.magic= OPENING_MAGIC; memcpy(opening_mbuf.protocolchecksumversion,protocolchecksumversion,PCSUMSIZE); opening_mbuf.serverpid= mypid; xfwrite(&opening_mbuf,sizeof(opening_mbuf),swfile); xfflush(swfile); +} + +static void receive_request(void) { + int i,j, fd; + unsigned long ul; xfread(&request_mbuf,sizeof(request_mbuf)); serviceuser= xfreadsetstring(request_mbuf.serviceuserlen); @@ -531,33 +420,44 @@ static void NONRETURNING servicerequest(int sfd) { assert(request_mbuf.overridelen <= MAX_OVERRIDE_LEN); overridedata= xfreadsetstring(request_mbuf.overridelen); } else { + assert(request_mbuf.overridelen == -1); overridedata= 0; } - gidarray= xmalloc(sizeof(gid_t)*request_mbuf.ngids); - xfread(gidarray,sizeof(gid_t)*request_mbuf.ngids); + assert(request_mbuf.ngids <= MAX_GIDS); + calling_gids= xmalloc(sizeof(gid_t)*request_mbuf.ngids); + xfread(calling_gids,sizeof(gid_t)*request_mbuf.ngids); fdarraysize= 4; fdarray= xmalloc(sizeof(struct fdstate)*fdarraysize); fdarrayused= 1; fdarray[0].iswrite= -1; fdarray[0].wantstate= tokv_word_rejectfd; + assert(request_mbuf.nreadfds+request_mbuf.nwritefds <= MAX_ALLOW_FD+1); for (i=0; i=request_mbuf.nreadfds); } + assert(request_mbuf.nargs <= MAX_ARGSDEFVARS); argarray= xmalloc(sizeof(char*)*(request_mbuf.nargs)); for (i=0; ipw_name,logname)); + callinguser_shell= xstrsave(pw->pw_shell); pw= getpwnam(serviceuser); if (!pw) miscerror("look up service user"); assert(!strcmp(pw->pw_name,serviceuser)); - serviceuser_dir= xstrdup(nondebug_serviceuserdir(pw->pw_dir)); - serviceuser_shell= xstrdup(pw->pw_shell); + serviceuser_dir= xstrsave(nondebug_serviceuserdir(pw->pw_dir)); + serviceuser_shell= xstrsave(pw->pw_shell); serviceuser_uid= pw->pw_uid; - serviceuser_gid= pw->pw_gid; if (initgroups(pw->pw_name,pw->pw_gid)) syscallerror("initgroups"); if (setreuid(pw->pw_uid,pw->pw_uid)) syscallerror("setreuid 1"); if (setreuid(pw->pw_uid,pw->pw_uid)) syscallerror("setreuid 2"); @@ -587,30 +495,23 @@ static void NONRETURNING servicerequest(int sfd) { if (!setreuid(pw->pw_uid,0)) miscerror("setreuid 3 unexpectedly succeeded"); if (errno != EPERM) syscallerror("setreuid 3 failed in unexpected way"); - debug_dumprequest(mypid); - - grouparray= xmalloc(sizeof(char*)*request_mbuf.ngids); - for (i=0; igr_name); - } + service_ngids= getgroups(0,0); if (service_ngids == -1) syscallerror("getgroups(0,0)"); + if (service_ngids > MAX_GIDS) miscerror("service user is in far too many groups"); + service_gids= xmalloc(sizeof(gid_t)*(service_ngids+1)); + service_gids[0]= pw->pw_gid; + if (getgroups(service_ngids,service_gids+1) != service_ngids) + syscallerror("getgroups(size,list)"); - if (overridedata) { - r= parse_string(TOPLEVEL_OVERRIDDEN_CONFIGURATION, - ""); - } else { - r= parse_string(TOPLEVEL_CONFIGURATION, - ""); - } - - ensurelogopen(USERVD_LOGFACILITY); + getgroupnames(service_ngids,service_gids,&service_groups); + getgroupnames(request_mbuf.ngids,calling_gids,&calling_groups); +} - if (r == tokv_error) failure("error encountered while parsing configuration files"); - assert(r == tokv_quit); +static void check_find_executable(void) { + int r, partsize; + char *part, *exectry; + const char *string, *delim, *nextstring; + struct stat stab; - debug_dumpexecsettings(); - switch (execute) { case tokv_word_reject: failure("request rejected"); @@ -642,8 +543,8 @@ static void NONRETURNING servicerequest(int sfd) { partsize= strlen(string); nextstring= 0; } - part= xmstrsubsave(string,partsize); - exectry= part[0] ? xmstrcat3save(part,"/",service) : xmstrsave(service); + part= xstrsubsave(string,partsize); + exectry= part[0] ? xstrcat3save(part,"/",service) : xstrsave(service); free(part); r= stat(exectry,&stab); if (!r) { execpath= exectry; break; } @@ -656,7 +557,11 @@ static void NONRETURNING servicerequest(int sfd) { default: abort(); } +} +static void check_fds(void) { + int fd; + assert(fdarrayused>=2); if (!(fdarray[2].wantstate == tokv_word_requirefd || fdarray[2].wantstate == tokv_word_allowfd) || @@ -705,38 +610,42 @@ static void NONRETURNING servicerequest(int sfd) { } } } +} + +static void send_progress_ok(void) { + struct progress_msg progress_mbuf; memset(&progress_mbuf,0,sizeof(progress_mbuf)); progress_mbuf.magic= PROGRESS_MAGIC; progress_mbuf.type= pt_ok; xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile); xfflush(swfile); +} + +static void fork_service_synch(void) { + pid_t newchild; + struct sigaction sig; + int r, synchsocket[2]; + char synchmsg; r= socketpair(AF_UNIX,SOCK_STREAM,0,synchsocket); - if (r) syscallfailure("cannot create socket for synch"); + if (r) syscallerror("cannot create socket for synch"); - sig.sa_handler= sighandler_pipe; + sig.sa_handler= sighandler_chld; sigemptyset(&sig.sa_mask); - sigaddset(&sig.sa_mask,SIGPIPE); sigaddset(&sig.sa_mask,SIGCHLD); sig.sa_flags= 0; - if (sigaction(SIGPIPE,&sig,0)) syscallfailure("cannot set sigpipe handler"); - - sig.sa_handler= sighandler_chld; - if (sigaction(SIGCHLD,&sig,0)) syscallfailure("cannot set sigchld handler"); - - getevent(&event_mbuf); - assert(event_mbuf.type == et_confirm); + if (sigaction(SIGCHLD,&sig,0)) syscallerror("cannot set sigchld handler"); newchild= fork(); - if (newchild == -1) syscallfailure("cannot fork to invoke service"); - if (!newchild) execservice(synchsocket); + if (newchild == -1) syscallerror("cannot fork to invoke service"); + if (!newchild) execservice(synchsocket,fileno(swfile)); childtokill= child= newchild; - if (close(synchsocket[1])) syscallfailure("cannot close other end of synch socket"); + if (close(synchsocket[1])) syscallerror("cannot close other end of synch socket"); r= synchread(synchsocket[0],'y'); - if (r) syscallfailure("read synch byte from child"); + if (r) syscallerror("read synch byte from child"); childtokill= -child; @@ -744,59 +653,42 @@ static void NONRETURNING servicerequest(int sfd) { r= write(synchsocket[0],&synchmsg,1); if (r!=1) syscallerror("write synch byte to child"); - if (close(synchsocket[0])) syscallfailure("cannot close my end of synch socket"); + if (close(synchsocket[0])) syscallerror("cannot close my end of synch socket"); +} + +void servicerequest(int sfd) { + struct event_msg event_mbuf; + int r; + + setup_comms(sfd); + send_opening(); + receive_request(); + establish_pipes(); + lookup_uidsgids(); + debug_dumprequest(mypid); + + if (overridedata) + r= parse_string(TOPLEVEL_OVERRIDDEN_CONFIGURATION, + "",1); + else + r= parse_string(TOPLEVEL_CONFIGURATION, + "",1); + + ensurelogopen(USERVD_LOGFACILITY); + if (r == tokv_error) failure("error encountered while parsing configuration files"); + assert(r == tokv_quit); + + debug_dumpexecsettings(); + + check_find_executable(); + check_fds(); + send_progress_ok(); getevent(&event_mbuf); - abort(); -} + assert(event_mbuf.type == et_confirm); -int main(int argc, char *const *argv) { - int mfd, sfd, csocklen; - struct sigaction childact; - struct sockaddr_un ssockname, csockname; - -#ifdef NDEBUG - abort(); /* Do not disable assertions in this security-critical code ! */ -#endif - - if (argc>1) { fputs("usage: uservd\n",stderr); exit(3); } - - openlog(USERVD_LOGIDENT,LOG_NDELAY,USERVD_LOGFACILITY); - mfd= socket(AF_UNIX,SOCK_STREAM,0); - if (!mfd) { syslog(LOG_CRIT,"cannot create master socket: %m"); exit(4); } - - assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUSPATH)); - ssockname.sun_family= AF_UNIX; - strcpy(ssockname.sun_path,RENDEZVOUSPATH); - unlink(RENDEZVOUSPATH); - if (bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname))) - { syslog(LOG_CRIT,"cannot bind master socket: %m"); exit(4); } - if (listen(mfd,5)) - { syslog(LOG_CRIT,"cannot listen on master socket: %m"); exit(4); } - - childact.sa_handler= sigchildhandler; - sigemptyset(&childact.sa_mask); - childact.sa_flags= SA_NOCLDSTOP; - if (sigaction(SIGCHLD,&childact,0)) - { syslog(LOG_CRIT,"cannot setup sigchld handler: %m"); exit(4); } - syslog(LOG_NOTICE,"started"); - for (;;) { - csocklen= sizeof(csockname); - sfd= accept(mfd,(struct sockaddr*)&csockname,&csocklen); - if (sfd == -1) { - if (errno == EINTR) continue; - if (errno == ENOMEM) { - syslog(LOG_ERR,"unable to accept connection: %m"); continue; - } - syslog(LOG_CRIT,"unable to accept new connections: %m"); exit(5); - } - child= nondebug_fork(); - if (child == (pid_t)-1) { - syslog(LOG_ERR,"unable to fork server: %m"); close(sfd); continue; - } - if (!child) { - close(mfd); closelog(); servicerequest(sfd); - } - close(sfd); - } + fork_service_synch(); + + getevent(&event_mbuf); + abort(); } diff --git a/servexec.c b/servexec.c new file mode 100644 index 0000000..48a6e29 --- /dev/null +++ b/servexec.c @@ -0,0 +1,216 @@ +/* + * userv - execserv.c + * daemon code which executes actual service (ie child process) + * + * Copyright (C)1996-1997 Ian Jackson + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with userv; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "common.h" +#include "daemon.h" +#include "lib.h" + +static void NONRETURNING serv_syscallfail(const char *msg) { + fputs("uservd(service): ",stderr); + perror(msg); + _exit(-1); +} + +static void serv_resetsignal(int signo) { + struct sigaction sig; + + sig.sa_handler= SIG_DFL; + sigemptyset(&sig.sa_mask); + sig.sa_flags= 0; + if (sigaction(signo,&sig,0)) serv_syscallfail("reset signal handler"); +} + +static const char *see_logname(void) { return serviceuser; } +static const char *see_home(void) { return serviceuser_dir; } +static const char *see_shell(void) { return serviceuser_shell; } + +static const char *see_path(void) { + return serviceuser_uid ? DEFAULTPATH_USER : DEFAULTPATH_ROOT; +} + +static const char *see_service(void) { return service; } +static const char *see_c_cwd(void) { return cwd; } +static const char *see_c_logname(void) { return logname; } +static const char *see_c_uid(void) { + static char buf[CHAR_BIT*sizeof(uid_t)/3+4]; + snyprintf(buf,sizeof(buf),"%lu",(unsigned long)request_mbuf.callinguid); + return buf; +} + +static const char *see_c_list(int n, const char *(*fn)(int i)) { + int l, i; + char *r; + + for (i=0, l=1; iname; sei++) + if (setenv(sei->name,sei->fn(),1)) serv_syscallfail("setenv standard"); + for (i=0; ienvvarbufsize) { envvarbufsize= l; envvarbuf= xrealloc(envvarbuf,l); } + snyprintf(envvarbuf,l,"USERV_U_%s",defvararray[i][0]); + if (setenv(envvarbuf,defvararray[i][1],1)) serv_syscallfail("setenv defvar"); + } + + nargs= 0; + if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) nargs++; + nargs++; + if (execargs) for (pp= execargs; *pp; pp++) nargs++; + if (!suppressargs) nargs+= request_mbuf.nargs; + args= xmalloc(sizeof(char*)*(nargs+1)); + targ= 0; + if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) args[targ++]= *cpp; + args[targ++]= execpath; + if (execargs) for (pp= execargs; *pp; pp++) args[targ++]= *pp; + if (!suppressargs) for (i=0; i -If a serious lexical or syntax error is detected in the same -configuration file as the diff --git a/tokens.h.m4 b/tokens.h.m4 index 21a3dbe..4755d9d 100644 --- a/tokens.h.m4 +++ b/tokens.h.m4 @@ -24,65 +24,18 @@ include(language.i4) #define TOKENS_H enum tokens { - tokm_instance= 000000000777, - tokm_repres= 000000007000, - tokm_type= 017777770000, - tokr_nonstring= 000000001000, - tokr_word= 000000002000, - tokr_punct= 000000003000, - tokr_string= 000000004000, + tokm_instance= 0x000000ff, + tokm_repres= 0x00000f00, + tokm_type= 0xfffff000, + tokr_nonstring= 0x00000100, + tokr_word= 0x00000200, + tokr_punct= 0x00000300, + tokr_string= 0x00000400, undivert(4) undivert(1) undivert(2) }; -typedef int directive_fnt(int dtoken); -directive_fnt df_reject, df_execute, df_executefrompath; -directive_fnt df_executefromdirectory; -directive_fnt df_errorstostderr, df_errorstosyslog, df_errorstofile; -directive_fnt dfg_fdwant, dfg_setflag; -directive_fnt df_reset, df_cd, df_userrcfile, df_include; -directive_fnt df_includelookup, df_includedirectory; -directive_fnt df_message, df_error, df_quit, df_eof; -directive_fnt df_if, df_catchquit, df_errorspush; -directive_fnt dfi_includeuserrcfile, dfi_includeclientconfig; -/* directive functions return: - * 0 for success - * having scanned up to and including end of line but not beyond - * an exception (eg tokv_error) for failure of some kind - */ - -typedef int parmcondition_fnt(int ctoken, char **parmvalues, int *rtrue); -parmcondition_fnt pcf_glob, pcf_range, pcf_grep; -/* all conditional functions return tokv_error or 0 for success at parsing - * and testing, in which case *rtrue is set to 0 or 1. On success they - * have scanned up to and including the condition's terminating newline. - * The parameter-based conditionals take a list of parameter values - * as obtained from the parameter functions and pa_parameter, - * and do _not_ free it. - */ - -typedef int parameter_fnt(int ptoken, char ***rvalues); -parameter_fnt pf_service; -parameter_fnt pf_callinguser, pf_serviceuser; -parameter_fnt pf_callinggroup, pf_servicegroup; -parameter_fnt pf_callingusershell, pf_serviceusershell; -/* Parameter functions return tokv_error or 0 for success at parsing - * and determining the value, in which case *rvalues is made to be - * a mallocd null-terminated array of pointers to mallocd strings. - * freeparm can be used to free such an array. - */ - -int yylex(void); -/* Returns a token (which may be a eof or error exception) */ - -extern directive_fnt *lr_dir; -extern parmcondition_fnt *lr_parmcond; -extern parameter_fnt *lr_parameter; -extern int lr_loglevel, lr_logfacility, lr_min, lr_max, *lr_flag; -extern int lr_flagval, lr_controlend; -extern int lr_fdwant_readwrite; /* -1=never, 0=opt, 1=always */ - undivert(4) #endif diff --git a/version.h b/version.h index 521c272..4894ee8 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define VERSION "0.50" +#define VERSION "0.55" -- 2.30.2