chiark / gitweb /
Proper version.h.
[userv.git] / daemon.c
1 /*
2  * userv - daemon.c
3  * daemon main program
4  *
5  * Copyright (C)1996-1997 Ian Jackson
6  *
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.
11  *
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.
16  *
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.
20  */
21
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <unistd.h>
25 #include <wait.h>
26 #include <assert.h>
27 #include <signal.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <syslog.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <sys/types.h>
35 #include <sys/fcntl.h>
36 #include <sys/socket.h>
37 #include <sys/stat.h>
38 #include <sys/un.h>
39 #include <limits.h>
40 #include <ctype.h>
41
42 #include "config.h"
43 #include "common.h"
44 #include "daemon.h"
45 #include "lib.h"
46 #include "tokens.h"
47
48 /* NB: defaults for the execution state are not set here, but in
49  * the RESET_CONFIGURATION #define in daemon.h. */
50 gid_t *gidarray=0;
51 char **argarray=0;
52 char *((*defvararray)[2])=0;
53 struct fdstate *fdarray=0;
54 int fdarraysize=0, fdarrayused=0;
55 int restfdwantstate= tokv_word_rejectfd, restfdwantrw= 0;
56 struct request_msg request_mbuf;
57 char *serviceuser=0, *service=0, *logname=0, *cwd=0;
58 char *overridedata=0, *userrcfile=0;
59 char *serviceuser_dir=0, *serviceuser_shell=0;
60 uid_t serviceuser_uid=-1;
61 gid_t serviceuser_gid=-1;
62 char *execpath=0, **execargs=0;
63 int execute, setenvironment, suppressargs, disconnecthup, ehandling;
64 int ehlogfacility=0, ehloglevel=0, ehfilekeep=0, syslogopenfacility=-1;
65 FILE *ehfile=0;
66 char *ehfilename=0;
67
68 static FILE *swfile= 0, *srfile= 0;
69 static pid_t child= -1, childtokill= -1;
70
71 static struct passwd *servicepw, *callingpw;
72 static const char **grouparray;
73
74 static void sigchildhandler(int x) {
75   pid_t r;
76   int status, es;
77
78   es= errno;
79   for (;;) {
80     r= waitpid((pid_t)-1,&status,WNOHANG);
81     if (!r || (r==-1 && errno==ECHILD)) break;
82     if (r==-1) { syslog(LOG_ERR,"wait in sigchild handler gave error: %m"); break; }
83     if (WIFSIGNALED(status))
84       if (WCOREDUMP(status))
85         syslog(LOG_ERR,"call pid %ld dumped core due to signal %s",(long)r,
86                strsignal(WTERMSIG(status)));
87       else
88         syslog(LOG_ERR,"call pid %ld died due to signal %s",
89                (long)r,strsignal(WTERMSIG(status)));
90     else if (!WIFEXITED(status))
91       syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %ld",
92              (long)r,status);
93     else if (WEXITSTATUS(status)>24)
94       syslog(LOG_ERR,"call pid %ld exited with status %ld >24",
95              (long)r,WEXITSTATUS(status));
96   }
97   errno= es;
98   return;
99 }
100
101 static void xfread(void *p, size_t sz) {
102   size_t nr;
103   nr= fread(p,1,sz,srfile); if (nr == sz) return;
104   if (ferror(srfile)) syscallerror("reading from client");
105   assert(feof(srfile));
106   syslog(LOG_DEBUG,"client went away (unexpected EOF)");
107   swfile= 0;
108   disconnect(12);
109 }
110
111 static void xfwrite(const void *p, size_t sz, FILE *file) {
112   size_t nr;
113   nr= fwrite(p,1,sz,file); if (nr == sz) return;
114   syscallerror("writing to client");
115 }
116
117 static char *xfreadsetstring(int l) {
118   char *s;
119   s= xmalloc(l+1);
120   xfread(s,sizeof(*s)*l);
121   s[l]= 0;
122   return s;
123 }
124
125 static char *xfreadstring(void) {
126   int l;
127   xfread(&l,sizeof(l));
128   return xfreadsetstring(l);
129 }
130
131 static void xfflush(FILE *file) {
132   if (fflush(file)) syscallerror("flush client socket");
133 }
134
135 void ensurefdarray(int fd) {
136   if (fd < fdarrayused) return;
137   if (fd >= fdarraysize) {
138     fdarraysize= ((fd+2)<<1);
139     fdarray= xrealloc(fdarray,sizeof(struct fdstate)*fdarraysize);
140   }
141   while (fd >= fdarrayused) {
142     fdarray[fdarrayused].iswrite= -1;
143     fdarray[fdarrayused].realfd= -1;
144     fdarray[fdarrayused].wantstate= restfdwantstate;
145     fdarray[fdarrayused].wantrw= restfdwantrw;
146     fdarrayused++;
147   }
148 }
149
150 void ensurelogopen(int wantfacility) {
151   if (syslogopenfacility==wantfacility) return;
152   if (syslogopenfacility!=-1) closelog();
153   openlog(USERVD_LOGIDENT,LOG_NDELAY|LOG_PID,wantfacility);
154   syslogopenfacility= wantfacility;
155 }
156
157 void senderrmsgstderr(const char *errmsg) {
158   struct progress_msg progress_mbuf;
159   unsigned long ul;
160   int l;
161
162   l= strlen(errmsg);
163   memset(&progress_mbuf,0,sizeof(progress_mbuf));
164   progress_mbuf.magic= PROGRESS_MAGIC;
165   progress_mbuf.type= pt_errmsg;
166   progress_mbuf.data.errmsg.messagelen= l;
167   xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
168   xfwrite(errmsg,l,swfile);
169   ul= PROGRESS_ERRMSG_END_MAGIC;
170   xfwrite(&ul,sizeof(ul),swfile);
171   xfflush(swfile);
172 }
173
174 void miscerror(const char *what) {
175   syslog(LOG_ERR,"failure: %s",what);
176   disconnect(16);
177 }
178
179 void syscallerror(const char *what) {
180   int e;
181
182   e= errno;
183   syslog(LOG_ERR,"system call failure: %s: %s",what,strerror(e));
184   disconnect(18);
185 }
186
187 static void NONRETURNING generalfailure(const char *prefix, int reserveerrno,
188                                         int errnoval, const char *fmt, va_list al) {
189   char errmsg[MAX_ERRMSG_LEN];
190
191   if (prefix) {
192     strnycpy(errmsg,prefix,sizeof(errmsg));
193     strnytcat(errmsg,": ",sizeof(errmsg));
194   } else {
195     errmsg[0]= 0;
196   }
197   vsnytprintfcat(errmsg,sizeof(errmsg)-reserveerrno,fmt,al);
198   if (reserveerrno) {
199     strnytcat(errmsg,": ",sizeof(errmsg));
200     strnytcat(errmsg,strerror(errnoval),sizeof(errmsg));
201   }
202   senderrmsgstderr(errmsg);
203   syslog(LOG_DEBUG,"service failed (%s)",errmsg);
204   disconnect(12);
205 }
206
207 static void NONRETURNPRINTFFORMAT(1,2) failure(const char *fmt, ...) {
208   va_list al;
209
210   va_start(al,fmt);
211   generalfailure(0,0,0,fmt,al);
212 }  
213
214 static void NONRETURNPRINTFFORMAT(1,2) syscallfailure(const char *fmt, ...) {
215   va_list al;
216   int e;
217
218   e= errno;
219   va_start(al,fmt);
220   generalfailure("system call failed",ERRMSG_RESERVE_ERRNO,e,fmt,al);
221 }
222
223 void NONRETURNING disconnect(int exitstatus) {
224   /* This function can sometimes indirectly call itself (eg,
225    * xfwrite, syscallerror can cause it to be called).  So, all
226    * the global variables indicating need for action are reset
227    * before the action is taken so that if it fails it isn't
228    * attempted again.
229    */
230   struct progress_msg progress_mbuf;
231   FILE *swfilereal;
232   pid_t orgtokill;
233   int r;
234   
235   if (childtokill!=-1 && disconnecthup) {
236     orgtokill= childtokill;
237     childtokill= -1;
238     if (disconnecthup) {
239       r= kill(-orgtokill,SIGHUP);
240       if (r && errno!=EPERM && errno!=ESRCH)
241         syscallerror("sending SIGHUP to service process group");
242     }
243     child= -1;
244   }
245   if (swfile) {
246     swfilereal= swfile;
247     swfile= 0;
248     memset(&progress_mbuf,0,sizeof(progress_mbuf));
249     progress_mbuf.magic= PROGRESS_MAGIC;
250     progress_mbuf.type= pt_failed;
251     xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfilereal);
252     xfflush(swfilereal);
253   }
254
255   _exit(exitstatus);
256 }
257
258 static void NONRETURNING syscallservfail(const char *msg) {
259   fputs("uservd(service): ",stderr);
260   perror(msg);
261   _exit(-1);
262 }
263
264 static void servresetsig(int signo) {
265   struct sigaction sig;
266   
267   sig.sa_handler= SIG_DFL;
268   sigemptyset(&sig.sa_mask);
269   sig.sa_flags= 0;
270   if (sigaction(signo,&sig,0)) syscallservfail("reset signal handler");
271 }
272
273 static int synchread(int fd, int ch) {
274   char synchmsg;
275   int r;
276   
277   for (;;) {
278     r= read(fd,&synchmsg,1);
279     if (r==1) break;
280     if (r==0) { errno= ECONNRESET; return -1; }
281     assert(r<0);
282     if (errno!=EINTR) return -1;
283   };
284   if (synchmsg != ch) { errno= EPROTO; return -1; }
285   return 0;
286 }
287
288 static const char *see_logname(void) { return servicepw->pw_name; }
289 static const char *see_home(void) { return servicepw->pw_dir; }
290 static const char *see_shell(void) { return servicepw->pw_shell; }
291
292 static const char *see_path(void) {
293   return servicepw->pw_uid ?
294     "/usr/local/bin:/bin:/usr/bin" :
295     "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin";
296 }
297
298 static const char *see_service(void) { return service; }
299 static const char *see_c_cwd(void) { return cwd; }
300 static const char *see_c_logname(void) { return logname; }
301 static const char *see_c_uid(void) {
302   static char buf[CHAR_BIT*sizeof(uid_t)/3+4];
303   snyprintf(buf,sizeof(buf),"%lu",(unsigned long)callingpw->pw_uid);
304   return buf;
305 }
306
307 static const char *see_c_list(int n, const char *(*fn)(int i)) {
308   int l, i;
309   char *r;
310   
311   for (i=0, l=1; i<n; i++) l+= strlen(fn(i))+1;
312   r= xmalloc(l); r[l-1]= '*';
313   for (i=0, *r=0; i<n; i++) snytprintfcat(r,l,"%s ",fn(i));
314   assert(!r[l-1] && r[l-2]==' ');
315   r[l-2]= 0;
316   return r;
317 }
318
319 static const char *seei_group(int i) {
320   return grouparray[i];
321 }
322 static const char *see_c_group(void) {
323   return see_c_list(request_mbuf.ngids,seei_group);
324 }
325
326 static const char *seei_gid(int i) {
327   static char buf[CHAR_BIT*sizeof(gid_t)/3+4];
328   snyprintf(buf,sizeof(buf),"%d",gidarray[i]);
329   return buf;
330 }
331 static const char *see_c_gid(void) {
332   return see_c_list(request_mbuf.ngids,seei_gid);
333 }
334
335 static const struct servenvinfo {
336   const char *name;
337   const char *(*fn)(void);
338 } servenvinfos[]= {
339   { "USER",           see_logname    },
340   { "LOGNAME",        see_logname    },
341   { "HOME",           see_home       },
342   { "SHELL",          see_shell      },
343   { "PATH",           see_path       },
344   { "USERV_SERVICE",  see_service    },
345   { "USERV_CWD",      see_c_cwd      },
346   { "USERV_USER",     see_c_logname  },
347   { "USERV_UID",      see_c_uid      },
348   { "USERV_GROUP",    see_c_group    },
349   { "USERV_GID",      see_c_gid      },
350   {  0                               }
351 };
352
353 static void NONRETURNING execservice(const int synchsocket[]) {
354   static const char *const setenvpfargs[]= {
355     "/bin/sh",
356     "-c",
357     ". " SYSTEMCONFIGDIR "/environment; exec \"$@\"",
358     "-",
359     0
360   };
361   int fd, realfd, holdfd, newfd, r, envvarbufsize=0, targ, nargs, i, l;
362   char *envvarbuf=0;
363   const char **args, *const *cpp;
364   char *const *pp;
365   char synchmsg;
366   const struct servenvinfo *sei;
367
368   if (dup2(fdarray[2].realfd,2)<0) {
369     static const char duperrmsg[]= "uservd(service): cannot dup2 for stderr\n";
370     write(fdarray[2].realfd,duperrmsg,sizeof(duperrmsg)-1);
371     _exit(-1);
372   }
373   if (close(synchsocket[0])) syscallservfail("close parent synch socket");
374
375   if (setpgid(0,0)) syscallservfail("set process group");
376   synchmsg= 'y';
377   r= write(synchsocket[1],&synchmsg,1);
378   if (r!=1) syscallservfail("write synch byte to parent");
379   r= synchread(synchsocket[1],'g');
380   if (r) syscallservfail("reach synch byte from parent");
381
382   if (close(fileno(swfile))) syscallservfail("close client socket fd");
383   for (fd=0; fd<fdarrayused; fd++) {
384     if (fdarray[fd].holdfd == -1) continue;
385     if (close(fdarray[fd].holdfd)) syscallservfail("close pipe hold fd");
386     fdarray[fd].holdfd= -1;
387   }
388   for (fd=0; fd<fdarrayused; fd++) {
389     if (fdarray[fd].realfd < fdarrayused) fdarray[fdarray[fd].realfd].holdfd= fd;
390   }
391   for (fd=0; fd<fdarrayused; fd++) {
392     realfd= fdarray[fd].realfd;
393     if (realfd == -1) continue;
394     holdfd= fdarray[fd].holdfd;
395     if (holdfd == fd) {
396       assert(realfd == fd);
397       fdarray[fd].holdfd= -1;
398       continue;
399     } else if (holdfd != -1) {
400       assert(fdarray[holdfd].realfd == fd);
401       newfd= dup(fd); if (newfd<0) syscallservfail("dup out of the way");
402       fdarray[holdfd].realfd= newfd;
403       if (newfd<fdarrayused) fdarray[newfd].holdfd= holdfd;
404       fdarray[fd].holdfd= -1;
405     }
406     if (dup2(fdarray[fd].realfd,fd)<0) syscallservfail("dup2 set up fd");
407     if (close(fdarray[fd].realfd)) syscallservfail("close old fd");
408     if (fcntl(fd,F_SETFD,0)<0) syscallservfail("set no-close-on-exec on fd");
409     fdarray[fd].realfd= fd;
410   }
411   servresetsig(SIGPIPE);
412   servresetsig(SIGCHLD);
413
414   for (sei= servenvinfos; sei->name; sei++)
415     if (setenv(sei->name,sei->fn(),1)) syscallservfail("setenv standard");
416   for (i=0; i<request_mbuf.nvars; i++) {
417     l= strlen(defvararray[i][0])+9;
418     if (l>envvarbufsize) { envvarbufsize= l; envvarbuf= xrealloc(envvarbuf,l); }
419     snyprintf(envvarbuf,l,"USERV_U_%s",defvararray[i][0]);
420     if (setenv(envvarbuf,defvararray[i][1],1)) syscallservfail("setenv defvar");
421   }
422
423   nargs= 0;
424   if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) nargs++;
425   nargs++;
426   if (execargs) for (pp= execargs; *pp; pp++) nargs++;
427   if (!suppressargs) nargs+= request_mbuf.nargs;
428   args= xmalloc(sizeof(char*)*(nargs+1));
429   targ= 0;
430   if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) args[targ++]= *cpp;
431   args[targ++]= execpath;
432   if (execargs) for (pp= execargs; *pp; pp++) args[targ++]= *pp;
433   if (!suppressargs) for (i=0; i<request_mbuf.nargs; i++) args[targ++]= argarray[i];
434   args[targ++]= 0;
435
436   execv(args[0],(char* const*)args);
437
438   syscallservfail("exec service program");
439   _exit(-1);
440 }
441
442 static void NONRETURNING sighandler_pipe(int ignored) {
443   swfile= 0;
444   ensurelogopen(USERVD_LOGFACILITY);
445   syslog(LOG_DEBUG,"client went away (server got sigpipe)");
446   disconnect(8);
447 }
448
449 static void NONRETURNING sighandler_chld(int ignored) {
450   struct progress_msg progress_mbuf;
451   int status;
452   pid_t returned;
453
454   returned= wait3(&status,WNOHANG,0);
455   if (returned==-1) syscallerror("wait for child failed");
456   if (!returned) syscallfailure("spurious sigchld");
457   if (returned!=child) syscallfailure("spurious child process (pid %ld)",(long)returned);
458   child= childtokill= -1;
459
460   memset(&progress_mbuf,0,sizeof(progress_mbuf));
461   progress_mbuf.magic= PROGRESS_MAGIC;
462   progress_mbuf.type= pt_terminated;
463   progress_mbuf.data.terminated.status= status;
464   xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
465   xfflush(swfile);
466
467   syslog(LOG_DEBUG,"service completed (status %d %d)",(status>>8)&0x0ff,status&0x0ff);
468   _exit(0);
469 }
470
471 static void getevent(struct event_msg *event_r) {
472   int fd;
473   
474   for (;;) {
475     xfread(event_r,sizeof(struct event_msg));
476     switch (event_r->type) {
477     case et_closereadfd:
478       fd= event_r->data.closereadfd.fd;
479       assert(fd<fdarrayused);
480       assert(fdarray[fd].holdfd!=-1);
481       if (close(fdarray[fd].holdfd)) syscallfailure("cannot close holding fd %d",fd);
482       break;
483     case et_disconnect:
484       syslog(LOG_DEBUG,"client disconnected");
485       disconnect(4);
486     default:
487       return;
488     }
489   }
490 }
491
492 static void NONRETURNING servicerequest(int sfd) {
493   struct opening_msg opening_mbuf;
494   struct progress_msg progress_mbuf;
495   struct event_msg event_mbuf;
496   pid_t mypid, newchild;
497   unsigned long ul;
498   int i,j, r, tempfd, fd, partsize, synchsocket[2];
499   char pipepathbuf[PIPEPATHMAXLEN];
500   const char *string, *delim, *nextstring;
501   char *part, *exectry;
502   char synchmsg;
503   struct stat stab;
504   struct sigaction sig;
505   struct group *cgrp;
506
507   ensurelogopen(USERVD_LOGFACILITY);
508   syslog(LOG_DEBUG,"call connected");
509
510   mypid= getpid(); if (mypid == -1) syscallerror("getpid");
511
512   srfile= fdopen(sfd,"r");
513   if (!srfile) syscallerror("turn socket fd into reading FILE*");
514   if (setvbuf(srfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket reads");
515
516   swfile= fdopen(sfd,"w");
517   if (!swfile) syscallerror("turn socket fd into writing FILE*");
518   if (setvbuf(swfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket writes");
519
520   opening_mbuf.magic= OPENING_MAGIC;
521   memcpy(opening_mbuf.protocolchecksumversion,protocolchecksumversion,PCSUMSIZE);
522   opening_mbuf.serverpid= mypid;
523   xfwrite(&opening_mbuf,sizeof(opening_mbuf),swfile);
524   xfflush(swfile);
525
526   xfread(&request_mbuf,sizeof(request_mbuf));
527   serviceuser= xfreadsetstring(request_mbuf.serviceuserlen);
528   service= xfreadsetstring(request_mbuf.servicelen);
529   logname= xfreadsetstring(request_mbuf.lognamelen);
530   cwd= xfreadsetstring(request_mbuf.cwdlen);
531   if (request_mbuf.overridelen >= 0) {
532     assert(request_mbuf.overridelen <= MAX_OVERRIDE_LEN);
533     overridedata= xfreadsetstring(request_mbuf.overridelen);
534   } else {
535     overridedata= 0;
536   }
537   gidarray= xmalloc(sizeof(gid_t)*request_mbuf.ngids);
538   xfread(gidarray,sizeof(gid_t)*request_mbuf.ngids);
539
540   fdarraysize= 4; fdarray= xmalloc(sizeof(struct fdstate)*fdarraysize);
541   fdarrayused= 1; fdarray[0].iswrite= -1;
542   fdarray[0].wantstate= tokv_word_rejectfd;
543   for (i=0; i<request_mbuf.nreadfds+request_mbuf.nwritefds; i++) {
544     xfread(&fd,sizeof(int));
545     ensurefdarray(fd);
546     assert(fdarray[fd].iswrite == -1);
547     fdarray[fd].iswrite= (i>=request_mbuf.nreadfds);
548   }
549
550   argarray= xmalloc(sizeof(char*)*(request_mbuf.nargs));
551   for (i=0; i<request_mbuf.nargs; i++) argarray[i]= xfreadstring();
552   defvararray= xmalloc(sizeof(char*)*request_mbuf.nvars*2);
553   for (i=0; i<request_mbuf.nvars; i++)
554     for (j=0; j<2; j++) defvararray[i][j]= xfreadstring();
555   xfread(&ul,sizeof(ul));
556   assert(ul == REQUEST_END_MAGIC);
557
558   for (fd=0; fd<fdarrayused; fd++) {
559     if (fdarray[fd].iswrite == -1) continue;
560     sprintf(pipepathbuf, PIPEPATHFORMAT, (unsigned long)request_mbuf.clientpid,
561             (unsigned long)mypid, fd);
562     tempfd= open(pipepathbuf,O_RDWR);
563     if (tempfd == -1) syscallerror("prelim open pipe");
564     if (!fdarray[fd].iswrite) {
565       fdarray[fd].holdfd= open(pipepathbuf, O_WRONLY);
566       if (fdarray[fd].holdfd == -1) syscallerror("hold open pipe");
567       fdarray[fd].realfd= open(pipepathbuf, O_RDONLY);
568     } else {
569       fdarray[fd].holdfd= -1;
570       fdarray[fd].realfd= open(pipepathbuf, O_WRONLY);
571     }
572     if (fdarray[fd].realfd == -1) syscallerror("real open pipe");
573     if (unlink(pipepathbuf)) syscallerror("unlink pipe");
574     if (close(tempfd)) syscallerror("close prelim fd onto pipe");
575   }
576
577   servicepw= getpwnam(serviceuser);
578   if (!servicepw) syscallerror("look up service user");
579   assert(!strcmp(servicepw->pw_name,serviceuser));
580   serviceuser_dir= xstrdup(nondebug_serviceuserdir(servicepw->pw_dir));
581   serviceuser_shell= xstrdup(servicepw->pw_shell);
582   serviceuser_uid= servicepw->pw_uid;
583   serviceuser_gid= servicepw->pw_gid;
584   if (initgroups(servicepw->pw_name,servicepw->pw_gid)) syscallerror("initgroups");
585   if (setreuid(servicepw->pw_uid,servicepw->pw_uid)) syscallerror("setreuid 1");
586   if (setreuid(servicepw->pw_uid,servicepw->pw_uid)) syscallerror("setreuid 2");
587   if (servicepw->pw_uid)
588     if (!setreuid(servicepw->pw_uid,0)) miscerror("setreuid 3 unexpectedly succeeded");
589   if (errno != EPERM) syscallerror("setreuid 3 failed in unexpected way");
590
591   debug_dumprequest(mypid);
592
593   callingpw= getpwnam(logname);
594   if (!callingpw) syscallerror("get passwd entry for calling user");
595   grouparray= xmalloc(sizeof(char*)*request_mbuf.ngids);
596   for (i=0; i<request_mbuf.ngids; i++) {
597     cgrp= getgrgid(gidarray[i]);
598     if (!cgrp) syscallerror("get group entry for calling group");
599     grouparray[i]= xmstrsave(cgrp->gr_name);
600   }
601
602   if (overridedata) {
603     r= parse_string(TOPLEVEL_OVERRIDDEN_CONFIGURATION,
604                     "<builtin toplevel override configuration>");
605   } else {
606     r= parse_string(TOPLEVEL_CONFIGURATION,
607                     "<builtin toplevel configuration>");
608   }
609
610   ensurelogopen(USERVD_LOGFACILITY);
611
612   if (r == tokv_error) failure("error encountered while parsing configuration files");
613   assert(r == tokv_quit);
614
615   debug_dumpexecsettings();
616   
617   switch (execute) {
618   case tokv_word_reject:
619     failure("request rejected");
620   case tokv_word_execute:
621     r= stat(execpath,&stab);
622     if (r) syscallfailure("checking for executable `%s'",execpath);
623     break;
624   case tokv_word_executefromdirectory:
625     r= stat(execpath,&stab);
626     if (r) syscallfailure("checking for executable in directory, `%s'",execpath);
627     break;
628   case tokv_word_executefrompath:
629     if (strchr(service,'/')) {
630       r= stat(service,&stab);
631       if (r) syscallfailure("execute-from-path (contains slash)"
632                             " cannot check for executable `%s'",service);
633       execpath= service;
634     } else {
635       string= getenv("PATH");
636       if (!string) failure("execute-from-path, but daemon inherited no PATH !");
637       while (string) {
638         delim= strchr(string,':');
639         if (delim) {
640           if (delim-string > INT_MAX)
641             failure("execute-from-path, but PATH component too long");
642           partsize= delim-string;
643           nextstring= delim+1;
644         } else {
645           partsize= strlen(string);
646           nextstring= 0;
647         }
648         part= xmstrsubsave(string,partsize);
649         exectry= part[0] ? xmstrcat3save(part,"/",service) : xmstrsave(service);
650         free(part);
651         r= stat(exectry,&stab);
652         if (!r) { execpath= exectry; break; }
653         free(exectry);
654         string= nextstring;
655       }
656       if (!execpath) failure("execute-from-path, but program `%s' not found",service);
657     }
658     break;
659   default:
660     abort();
661   }
662
663   assert(fdarrayused>=2);
664   if (!(fdarray[2].wantstate == tokv_word_requirefd ||
665         fdarray[2].wantstate == tokv_word_allowfd) ||
666       fdarray[2].wantrw != tokv_word_write)
667     failure("must have stderr (fd 2), but file descriptor setup in "
668             "configuration does not have it or not for writing");
669
670   for (fd=0; fd<fdarrayused; fd++) {
671     switch (fdarray[fd].wantstate) {
672     case tokv_word_rejectfd:
673       if (fdarray[fd].realfd != -1)
674         failure("file descriptor %d provided but rejected",fd);
675       break;
676     case tokv_word_ignorefd:
677       if (fdarray[fd].realfd != -1)
678         if (close(fdarray[fd].realfd))
679           syscallfailure("close unwanted file descriptor %d",fd);
680       fdarray[fd].realfd= -1;
681       break;
682     case tokv_word_nullfd:
683       if (fdarray[fd].realfd != -1) close(fdarray[fd].realfd);
684       fdarray[fd].realfd= open("/dev/null",
685                                fdarray[fd].iswrite == -1 ? O_RDWR :
686                                fdarray[fd].iswrite ? O_WRONLY : O_RDONLY);
687       if (fdarray[fd].realfd == -1)
688         syscallfailure("cannot open /dev/null for null fd");
689       break;
690     case tokv_word_requirefd:
691       if (fdarray[fd].realfd == -1)
692         failure("file descriptor %d not provided but required",fd);
693       /* fall through */
694     case tokv_word_allowfd:
695       if (fdarray[fd].realfd == -1) {
696         fdarray[fd].iswrite= (fdarray[fd].wantrw == tokv_word_write);
697         fdarray[fd].realfd= open("/dev/null",fdarray[fd].iswrite ? O_WRONLY : O_RDONLY);
698         if (fdarray[fd].realfd == -1)
699           syscallfailure("cannot open /dev/null for allowed but not provided fd");
700       } else {
701         if (fdarray[fd].iswrite) {
702           if (fdarray[fd].wantrw != tokv_word_write)
703             failure("file descriptor %d provided write, wanted read",fd);
704         } else {
705           if (fdarray[fd].wantrw != tokv_word_read)
706             failure("file descriptor %d provided read, wanted write",fd);
707         }
708       }
709     }
710   }
711
712   memset(&progress_mbuf,0,sizeof(progress_mbuf));
713   progress_mbuf.magic= PROGRESS_MAGIC;
714   progress_mbuf.type= pt_ok;
715   xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
716   xfflush(swfile);
717
718   r= socketpair(AF_UNIX,SOCK_STREAM,0,synchsocket);
719   if (r) syscallfailure("cannot create socket for synch");
720
721   sig.sa_handler= sighandler_pipe;
722   sigemptyset(&sig.sa_mask);
723   sigaddset(&sig.sa_mask,SIGPIPE);
724   sigaddset(&sig.sa_mask,SIGCHLD);
725   sig.sa_flags= 0;
726   if (sigaction(SIGPIPE,&sig,0)) syscallfailure("cannot set sigpipe handler");
727
728   sig.sa_handler= sighandler_chld;
729   if (sigaction(SIGCHLD,&sig,0)) syscallfailure("cannot set sigchld handler");
730
731   getevent(&event_mbuf);
732   assert(event_mbuf.type == et_confirm);
733
734   newchild= fork();
735   if (newchild == -1) syscallfailure("cannot fork to invoke service");
736   if (!newchild) execservice(synchsocket);
737   childtokill= child= newchild;
738
739   if (close(synchsocket[1])) syscallfailure("cannot close other end of synch socket");
740
741   r= synchread(synchsocket[0],'y');
742   if (r) syscallfailure("read synch byte from child");
743
744   childtokill= -child;
745
746   synchmsg= 'g';
747   r= write(synchsocket[0],&synchmsg,1);
748   if (r!=1) syscallerror("write synch byte to child");
749
750   if (close(synchsocket[0])) syscallfailure("cannot close my end of synch socket");
751
752   getevent(&event_mbuf);
753   abort();
754 }
755
756 int main(int argc, char *const *argv) {
757   int mfd, sfd, csocklen;
758   struct sigaction childact;
759   struct sockaddr_un ssockname, csockname;
760
761 #ifdef NDEBUG
762   abort(); /* Do not disable assertions in this security-critical code ! */
763 #endif
764
765   if (argc>1) { fputs("usage: uservd\n",stderr); exit(3); }
766
767   openlog(USERVD_LOGIDENT,LOG_NDELAY,USERVD_LOGFACILITY);
768   mfd= socket(AF_UNIX,SOCK_STREAM,0);
769   if (!mfd) { syslog(LOG_CRIT,"cannot create master socket: %m"); exit(4); }
770
771   assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUSPATH));
772   ssockname.sun_family= AF_UNIX;
773   strcpy(ssockname.sun_path,RENDEZVOUSPATH);
774   unlink(RENDEZVOUSPATH);
775   if (bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname)))
776     { syslog(LOG_CRIT,"cannot bind master socket: %m"); exit(4); }
777   if (listen(mfd,5))
778     { syslog(LOG_CRIT,"cannot listen on master socket: %m"); exit(4); }
779
780   childact.sa_handler= sigchildhandler;
781   sigemptyset(&childact.sa_mask);
782   childact.sa_flags= SA_NOCLDSTOP;
783   if (sigaction(SIGCHLD,&childact,0))
784     { syslog(LOG_CRIT,"cannot setup sigchld handler: %m"); exit(4); }
785   syslog(LOG_NOTICE,"started");
786   for (;;) {
787     csocklen= sizeof(csockname);
788     sfd= accept(mfd,(struct sockaddr*)&csockname,&csocklen);
789     if (sfd == -1) {
790       if (errno == EINTR) continue;
791       if (errno == ENOMEM) {
792         syslog(LOG_ERR,"unable to accept connection: %m"); continue;
793       }
794       syslog(LOG_CRIT,"unable to accept new connections: %m"); exit(5);
795     }
796     child= nondebug_fork();
797     if (child == (pid_t)-1) {
798       syslog(LOG_ERR,"unable to fork server: %m"); close(sfd); continue;
799     }
800     if (!child) {
801       close(mfd); closelog(); servicerequest(sfd);
802     }
803     close(sfd);
804   }
805 }