#include #include #include #include #include #include #include "uint16.h" #include "str.h" #include "byte.h" #include "fmt.h" #include "scan.h" #include "ip4.h" #include "fd.h" #include "exit.h" #include "env.h" #include "prot.h" #include "open.h" #include "wait.h" #include "stralloc.h" #include "alloc.h" #include "buffer.h" #include "error.h" #include "strerr.h" #include "sgetopt.h" #include "ipc.h" #include "ipcpath.h" #include "ndelay.h" #include "rules.h" #include "sig.h" #include "iopause.h" #include "coe.h" #include "lock.h" extern void server(int argcs,char * const *argvs); const char *self; int verbosity = 1; const char *banner = ""; char remotepath[IPCPATH_MAX + 1]; char localname[IPCPATH_MAX + 1]; const char *forcelocal = 0; const char *localpath = 0; char remoteeuidstr[FMT_ULONG]; char remoteegidstr[FMT_ULONG]; int remoteeuid; int remoteegid; char strnum[FMT_ULONG]; char strnum2[FMT_ULONG]; int selfpipe[2]; int flagexit = 0; stralloc tmp = {0}; stralloc envplus; stralloc envtmp; char bspace[16]; buffer b; const char *lockfile = 0; int fdlock; int lop; static char **e; static char **e1; /* ---------------------------- child */ #define DROP ": warning: dropping connection, " int flagdelete = 1; int flagdeny = 0; int flagallownorules = 0; int flagpeereid = 1; const char *fnrules = 0; void drop_nomem(void) { strerr_die3sys(111,self,DROP,"out of memory"); } void drop_eid(void) { strerr_die3sys(111,self,DROP,"unable to obtain client eid: "); } void cats(const char *s) { if (!stralloc_cats(&tmp,s)) drop_nomem(); } void append(const char *ch) { if (!stralloc_append(&tmp,ch)) drop_nomem(); } void safecats(const char *s) { char ch; int i; for (i = 0;i < 100;++i) { ch = s[i]; if (!ch) return; if (ch < 33) ch = '?'; if (ch > 126) ch = '?'; if (ch == '%') ch = '?'; /* logger stupidity */ if (ch == ':') ch = '?'; append(&ch); } cats("..."); } void env(const char *s,const char *t) { if (!s) return; if (!stralloc_copys(&envtmp,s)) drop_nomem(); if (t) { if (!stralloc_cats(&envtmp,"=")) drop_nomem(); if (!stralloc_cats(&envtmp,t)) drop_nomem(); } if (!stralloc_0(&envtmp)) drop_nomem(); if (!stralloc_cat(&envplus,&envtmp)) drop_nomem(); } void env_set(void) { unsigned int elen; unsigned int i; unsigned int j; unsigned int split; unsigned int t; if (!stralloc_cats(&envplus,"")) return; elen = 0; for (i = 0;environ[i];++i) ++elen; for (i = 0;i < envplus.len;++i) if (!envplus.s[i]) ++elen; e = (char **) alloc((elen + 1) * sizeof(char *)); if (!e) return; elen = 0; for (i = 0;environ[i];++i) e[elen++] = environ[i]; j = 0; for (i = 0;i < envplus.len;++i) if (!envplus.s[i]) { split = str_chr(envplus.s + j,'='); for (t = 0;t < elen;++t) if (byte_equal(envplus.s + j,split,e[t])) if (e[t][split] == '=') { --elen; e[t] = e[elen]; break; } if (envplus.s[j + split]) e[elen++] = envplus.s + j; j = i + 1; } e[elen] = 0; e1 = environ; environ = e; } void env_reset(void) { if (e) { if (e != environ) strerr_die3x(111,self,DROP,"environ changed"); alloc_free((char *)e); } environ = e1; envplus.len = 0; } void drop_rules(void) { strerr_die5sys(111,self,DROP,"unable to read ",fnrules,": "); } void found(char *data,unsigned int datalen) { unsigned int next0; unsigned int split; while ((next0 = byte_chr(data,datalen,0)) < datalen) { switch(data[0]) { case 'D': flagdeny = 1; break; case '+': split = str_chr(data + 1,'='); if (data[1 + split] == '=') { data[1 + split] = 0; env(data + 1,data + 1 + split + 1); } break; } ++next0; data += next0; datalen -= next0; } } int doit(int t) { if (verbosity >= 2) { strnum[fmt_ulong(strnum,getpid())] = 0; strerr_warn5(self,": pid ",strnum," from ",remotepath,0); } if (*banner) { buffer_init(&b,buffer_unixwrite,t,bspace,sizeof bspace); if (buffer_putsflush(&b,banner) == -1) strerr_die3sys(111,self,DROP,"unable to print banner: "); } localpath = forcelocal; if (!localpath) if (ipc_local(t,localname,sizeof(localname),&lop) == 0) { localpath = localname; } env("PROTO","IPC"); env("IPCLOCALPATH",localpath); env("IPCREMOTEPATH",remotepath); if (flagpeereid) { if (ipc_eid(t,&remoteeuid,&remoteegid) == -1) drop_eid(); remoteeuidstr[fmt_ulong(remoteeuidstr,remoteeuid)] = 0; remoteegidstr[fmt_ulong(remoteegidstr,remoteegid)] = 0; env("IPCREMOTEEUID",remoteeuidstr); env("IPCREMOTEEGID",remoteegidstr); } else { remoteeuid = geteuid(); remoteegid = getegid(); remoteeuidstr[fmt_ulong(remoteeuidstr,remoteeuid)] = 0; remoteegidstr[fmt_ulong(remoteegidstr,remoteegid)] = 0; env("IPCREMOTEEUID",0); env("IPCREMOTEEGID",0); } if (fnrules) { int fdrules; flagdeny = 0; fdrules = open_read(fnrules); if (fdrules == -1) { if (errno != error_noent) drop_rules(); if (!flagallownorules) drop_rules(); } else { if (rules(found,fdrules,remoteeuidstr,remoteegidstr) == -1) drop_rules(); close(fdrules); } } if (verbosity >= 2) { strnum[fmt_ulong(strnum,getpid())] = 0; if (!stralloc_copys(&tmp,"ipcserver: ")) drop_nomem(); safecats(flagdeny ? "deny" : "ok"); cats(" "); safecats(strnum); cats(" "); safecats(localpath); cats(" "); safecats(remotepath); cats(":"); safecats(remoteeuidstr); cats(","); safecats(remoteegidstr); cats("\n"); buffer_putflush(buffer_2,tmp.s,tmp.len); } if (flagdeny) return(0); env_set(); return 1; } void done(void) { if (verbosity >= 2) { strnum[fmt_ulong(strnum,getpid())] = 0; if (!stralloc_copys(&tmp,self)) drop_nomem(); if (!stralloc_cats(&tmp,": ")) drop_nomem(); cats("done "); safecats(strnum); cats("\n"); buffer_putflush(buffer_2,tmp.s,tmp.len); } } /* ---------------------------- parent */ #define FATAL ": fatal: " void usage(void) { strerr_warn4(self,"\ : usage: ",self," \ [ -UXhHoOdDqQvpP ] \ [ -c limit ] \ [ -x rules.cdb ] \ [ -B banner ] \ [ -g gid ] \ [ -u uid ] \ [ -b backlog ] \ [ -l localname ] \ [ -f lockfile ] \ path",0); _exit(100); } unsigned long limit = 40; unsigned long numchildren = 0; unsigned long backlog = 20; unsigned long uid = 0; unsigned long gid = 0; void printstatus(void) { if (verbosity < 2) return; strnum[fmt_ulong(strnum,numchildren)] = 0; strnum2[fmt_ulong(strnum2,limit)] = 0; strerr_warn5(self,": status: ",strnum,"/",strnum2,0); } void trigger(void) { write(selfpipe[1],"",1); } void sigterm(void) { int pid; flagexit = 1; pid = getpid(); if (pid < 0) strerr_die2sys(111,FATAL,"cannot get pid: "); kill(-pid,SIGTERM); trigger(); } void sigchld(void) { int wstat; int pid; while ((pid = wait_nohang(&wstat)) > 0) { if (verbosity >= 2) { strnum[fmt_ulong(strnum,pid)] = 0; strnum2[fmt_ulong(strnum2,wstat)] = 0; strerr_warn5(self,": end ",strnum," status ",strnum2,0); } if (numchildren) --numchildren; printstatus(); if (flagexit && !numchildren) _exit(0); } trigger(); } void spawn(int s,int argc,char * const *argv) { int t; while (numchildren >= limit) sig_pause(); while (numchildren < limit) { ++numchildren; printstatus(); switch(fork()) { case 0: sig_uncatch(sig_child); sig_unblock(sig_child); sig_uncatch(sig_term); sig_uncatch(sig_pipe); for (;;) { if (lockfile) { if (lock_ex(fdlock) == -1) strerr_die5sys(111,self,FATAL,"unable to lock ",lockfile,": "); t = ipc_accept(s,remotepath,sizeof(remotepath),&lop); lock_un(fdlock); } else t = ipc_accept(s,remotepath,sizeof(remotepath),&lop); if (t == -1) continue; if (!doit(t)) continue; if ((fd_move(0,t) == -1) || (fd_copy(1,0) == -1)) strerr_die3sys(111,self,DROP,"unable to set up descriptors: "); server(argc,argv); close(0); close(1); env_reset(); done(); } break; case -1: strerr_warn3(self,DROP,"unable to fork: ",&strerr_sys); --numchildren; printstatus(); } } } int main(int argc,char *const *argv) { const char *path; int opt; const char *x; int s; iopause_fd io[2]; char ch; struct taia deadline; struct taia stamp; mode_t m; self = argv[0]; while ((opt = getopt(argc,argv,"vqQdDUXx:u:g:l:b:B:c:f:pP")) != opteof) switch(opt) { case 'b': scan_ulong(optarg,&backlog); break; case 'c': scan_ulong(optarg,&limit); break; case 'X': flagallownorules = 1; break; case 'x': fnrules = optarg; break; case 'B': banner = optarg; break; case 'v': verbosity = 2; break; case 'q': verbosity = 0; break; case 'Q': verbosity = 1; break; case 'd': flagdelete = 1; break; case 'D': flagdelete = 0; break; case 'U': x = env_get("UID"); if (x) scan_ulong(x,&uid); x = env_get("GID"); if (x) scan_ulong(x,&gid); break; case 'u': scan_ulong(optarg,&uid); break; case 'g': scan_ulong(optarg,&gid); break; case 'l': forcelocal = optarg; break; case 'f': lockfile = optarg; break; case 'p': flagpeereid = 1; break; case 'P': flagpeereid = 0; break; default: usage(); } argc -= optind; argv += optind; if (!verbosity) buffer_2->fd = -1; path = *argv++; --argc; if (!path) usage(); if (str_equal(path,"")) usage(); if (setsid() == -1) if (getpgrp() != getpid()) strerr_die3sys(111,self,FATAL,"unable to create process group: "); if (lockfile) { fdlock = open_append(lockfile); if (fdlock == -1) strerr_die5sys(111,self,FATAL,"unable to open ",lockfile,": "); } if (pipe(selfpipe) == -1) strerr_die3sys(111,self,FATAL,"unable to create pipe: "); coe(selfpipe[0]); coe(selfpipe[1]); ndelay_on(selfpipe[0]); ndelay_on(selfpipe[1]); sig_block(sig_child); sig_catch(sig_child,sigchld); sig_catch(sig_term,sigterm); sig_ignore(sig_pipe); s = ipc_stream(); if (s == -1) strerr_die3sys(111,self,FATAL,"unable to create socket: "); m = umask(0); if (flagdelete) { if (ipc_bind_reuse(s,path) == -1) strerr_die3sys(111,self,FATAL,"unable to bind: "); } else { if (ipc_bind(s,path) == -1) strerr_die3sys(111,self,FATAL,"unable to bind: "); } umask(m); if (ipc_local(s,0,0,&lop) == -1) strerr_die2sys(111,FATAL,"unable to get local address: "); if (ipc_listen(s,backlog) == -1) strerr_die2sys(111,FATAL,"unable to listen: "); ndelay_off(s); if (gid) if (prot_gid(gid) == -1) strerr_die3sys(111,self,FATAL,"unable to set gid: "); if (uid) if (prot_uid(uid) == -1) strerr_die3sys(111,self,FATAL,"unable to set uid: "); close(0); close(1); printstatus(); for (;;) { if (!flagexit) spawn(s,argc,argv); sig_unblock(sig_child); io[0].fd = selfpipe[0]; io[0].events = IOPAUSE_READ; taia_now(&stamp); taia_uint(&deadline,3600); taia_add(&deadline,&stamp,&deadline); iopause(io,1,&deadline,&stamp); sig_block(sig_child); if (flagexit && !numchildren) _exit(0); while (buffer_unixread(selfpipe[0],&ch,1) == 1) ; } }