chiark / gitweb /
Fix builtins.
[userv.git] / client.c
index 68ec6a4d1b9ff68dafbd821f3bb3c3078016ba94..02d77a9340057e8da024a92aa777c39e6c0fe44e 100644 (file)
--- a/client.c
+++ b/client.c
@@ -29,6 +29,7 @@
 #include <unistd.h>
 #include <ctype.h>
 #include <pwd.h>
+#include <grp.h>
 #include <signal.h>
 #include <limits.h>
 #include <sys/time.h>
@@ -41,6 +42,7 @@
 
 #include "config.h"
 #include "common.h"
+#include "version.h"
 
 struct optioninfo;
 
@@ -131,19 +133,21 @@ struct fdsetupstate {
 };
 
 enum signalsexitspecials { se_number=-100, se_numbernocore, se_highbit, se_stdout };
-enum overridetypes { ot_none, ot_string, ot_file };
+enum overridetypes { ot_none, ot_string, ot_file, ot_builtin };
 
-static const char *serviceuser=0;
+struct constkeyvaluepair { const char *key, *value; };
+
+static const char *serviceuser;
 static uid_t serviceuid, myuid;
-static struct fdsetupstate *fdsetup=0;
-static int fdsetupsize=0;
-static const char *(*defvarsarray)[2];
-static int defvarsavail=0, defvarsused=0;
-static unsigned long timeout=0;
+static struct fdsetupstate *fdsetup;
+static int fdsetupsize;
+static struct constkeyvaluepair *defvararray;
+static int defvaravail, defvarused;
+static unsigned long timeout;
 static int signalsexit=254;
-static int sigpipeok=0, hidecwd=0;
+static int sigpipeok, hidecwd;
 static int overridetype= ot_none;
-static const char *overridevalue;
+static const char *overridevalue, *spoofuser=0;
 
 static FILE *srfile, *swfile;
 
@@ -262,6 +266,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);
 }
@@ -272,19 +277,21 @@ static void xfflush(FILE *file) {
 
 static void usage(void) {
   if (fprintf(stderr,
-              "usage: userv <options> [--] <service-user> <service-name> [<argument> ...]\n"
-              "options: -f|--file <fd>[<fdmodifiers>]=<filename>\n"
-              "         -D|--defvar <name>=<value>\n"
-              "         -t|--timeout <seconds>\n"
-              "         -S|--signals <status>|number|number-nocore|highbit|stdout\n"
-              "         -w|--fdwait <fd>=wait|nowait|close\n"
-              "         -P|--sigpipe  -H|--hidecwd  -h|--help  --copyright\n"
-              "         --override <configuration-data> } available only to\n"
-              "         --override-file <filename>      } root or same user\n"
-              "fdmodifiers:            read    write  overwrite    trunc[ate]\n"
-              "(separate with commas)  append  sync   excl[usive]  creat[e]  fd\n\n"
-             "userv and uservd are copyright (C)1996-1997 Ian Jackson.\n"
-              "They come with NO WARRANTY; type `userv --copyright' for details.\n")
+    "usage: userv <options> [--] <service-user> <service-name> [<argument> ...]\n"
+    "usage: userv <options> -B|--builtin [--] <builtin-service> [<info-argument> ...]\n"
+    "options: -f|--file <fd>[<fdmodifiers>]=<filename>\n"
+    "         -D|--defvar <name>=<value>\n"
+    "         -t|--timeout <seconds>\n"
+    "         -S|--signals <status>|number|number-nocore|highbit|stdout\n"
+    "         -w|--fdwait <fd>=wait|nowait|close\n"
+    "         -P|--sigpipe  -H|--hidecwd  -h|--help  --copyright\n"
+    "         --override <configuration-data> } available only\n"
+    "         --override-file <filename>      }  to root\n"
+    "         --spoof-user <username>         }  or same user\n"
+    "fdmodifiers:            read    write  overwrite    trunc[ate]\n"
+    "(separate with commas)  append  sync   excl[usive]  creat[e]  fd\n\n"
+    "userv and uservd version " VERSION "; copyright (C)1996-1997 Ian Jackson.\n"
+    "there is NO WARRANTY; type `userv --copyright' for details.\n")
       == EOF) syscallerror("write usage to stderr");
 }
 
@@ -416,14 +423,21 @@ 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;
 
-  for (i=0; i<defvarsused && strcmp(defvarsarray[i][0],key); i++);
-  if (i>=defvarsavail) {
-    defvarsavail+=10; defvarsavail<<=1;
-    defvarsarray= xrealloc(defvarsarray,sizeof(const char*)*2*defvarsavail);
+  if (!key[0])
+    usageerror("empty string not allowed as variable name");
+  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<defvarused && strcmp(defvararray[i].key,key); i++);
+  if (defvarused >= MAX_ARGSDEFVAR) usageerror("far too many --defvar or -D options");
+  if (i>=defvaravail) {
+    defvaravail+=10; defvaravail<<=1;
+    defvararray= xrealloc(defvararray,sizeof(struct constkeyvaluepair)*defvaravail);
   }
-  if (i==defvarsused) defvarsused++;
-  defvarsarray[i][0]= key;
-  defvarsarray[i][1]= value;
+  if (i==defvarused) defvarused++;
+  defvararray[i].key= key;
+  defvararray[i].value= value;
 }
 
 static void of_timeout(const struct optioninfo *oip, const char *value, char *key) {
@@ -482,6 +496,10 @@ static void of_copyright(const struct optioninfo *oip, const char *value, char *
   exit(0);
 }
 
+static void of_builtin(const struct optioninfo *oip, const char *value, char *key) {
+  overridetype= ot_builtin;
+}
+
 static void of_override(const struct optioninfo *oip, const char *value, char *key) {
   overridetype= ot_string;
   overridevalue= value;
@@ -493,6 +511,11 @@ static void of_overridefile(const struct optioninfo *oip,
   overridevalue= value;
 }
 
+static void of_spoofuser(const struct optioninfo *oip,
+                        const char *value, char *key) {
+  spoofuser= value;
+}
+
 const struct optioninfo optioninfos[]= {
   { 'f', "file",          2, of_file         },
   { 'w', "fdwait",        2, of_fdwait       },
@@ -501,10 +524,12 @@ const struct optioninfo optioninfos[]= {
   { 'S', "signals",       1, of_signals      },
   { 'P', "sigpipe",       0, of_sigpipe      },
   { 'H', "hidecwd",       0, of_hidecwd      },
+  { 'B', "builtin",       0, of_builtin      },
   { 'h', "help",          0, of_help         },
   {  0,  "copyright",     0, of_copyright    },
   {  0,  "override",      1, of_override     },
   {  0,  "override-file", 1, of_overridefile },
+  {  0,  "spoof-user",    1, of_spoofuser    },
   {  0,   0                                  }
 };
 
@@ -661,17 +686,19 @@ int main(int argc, char *const *argv) {
   char *argp;
   const struct optioninfo *oip;
   struct sockaddr_un ssockname;
-  int sfd, ngids, i, j, tempfd, l, c, reading, fd, r, status;
+  int sfd, ngids, i, tempfd, l, c, reading, fd, r, status, ngidssize;
   sigset_t sset;
   unsigned long ul;
   size_t cwdbufsize;
-  char *cwdbuf;
+  char *cwdbuf, **mem;
   struct opening_msg opening_mbuf;
   struct request_msg request_mbuf;
   struct progress_msg progress_mbuf;
   struct event_msg event_mbuf;
   struct passwd *pw;
-  gid_t mygid, *gidarray;
+  struct group *gr;
+  gid_t mygid, spoofgid, *gidarray;
+  uid_t spoofuid;
   pid_t mypid;
   const char *logname;
   FILE *ovfile;
@@ -698,7 +725,7 @@ int main(int argc, char *const *argv) {
   of_file(0,"stderr",fd2key);
 
   for (argpp= argv+1;
-       (argp= *argpp) && *argp == '-';
+       (argp= *argpp) && *argp == '-' && argp[1];
        argpp++) {
     if (!*++argp) usageerror("unknown option/argument `%s'",*argpp);
     if (*argp == '-') { /* Two hyphens */
@@ -732,9 +759,15 @@ int main(int argc, char *const *argv) {
       }
     }
   }
-  if (!*argpp) usageerror("no service user given after options");
-  serviceuser= *argpp++;
-  if (!*argpp) usageerror("no service name given after options and service user");
+  if (overridetype == ot_builtin) {
+    serviceuser= "-";
+  } else {
+    if (!*argpp) usageerror("no service user given after options");
+    serviceuser= *argpp++;
+  }
+  if (!*argpp) usageerror(overridetype == ot_builtin ?
+                         "no service name given after options and service user" :
+                         "no builtin service given after options");
   
   for (fd=0; fd<fdsetupsize; fd++) {
     if (!fdsetup[fd].filename) continue;
@@ -750,15 +783,11 @@ int main(int argc, char *const *argv) {
 
   argc-= (argpp-argv);
   argv= argpp;
-
-  pw= getpwnam(serviceuser);
-  if (!pw) miscerror("requested service user `%s' is not a user",serviceuser);
-  serviceuid= pw->pw_uid;
-
-  if (overridetype != ot_none && myuid != 0 && myuid != serviceuid)
-    miscerror("--override options only available to root or to"
-              " the user who will be providing the service");
-
+  if (argc > MAX_ARGSDEFVAR) usageerror("far too many arguments");
+  if (ngids > MAX_GIDS) miscerror("caller is in far too many gids");
+  
+  spoofuid= myuid;
+  spoofgid= mygid;
   logname= getenv("LOGNAME");
   if (!logname) logname= getenv("USER");
   if (logname) {
@@ -766,10 +795,42 @@ int main(int argc, char *const *argv) {
     if (!pw || pw->pw_uid != myuid) logname= 0;
   }
   if (!logname) {
-    pw= getpwuid(myuid); if (!pw) syscallerror("cannot determine your login name");
+    pw= getpwuid(myuid); if (!pw) miscerror("cannot determine your login name");
     logname= pw->pw_name;
   }
 
+  if (!strcmp(serviceuser,"-")) serviceuser= logname;
+  pw= getpwnam(serviceuser);
+  if (!pw) miscerror("requested service user `%s' is not a user",serviceuser);
+  serviceuid= pw->pw_uid;
+
+  if ((overridetype != ot_none || spoofuser) && myuid != 0 && myuid != serviceuid)
+    miscerror("--override and --spoof options only available to root or to"
+              " the user who will be providing the service");
+
+  if (spoofuser) {
+    logname= spoofuser;
+    pw= getpwnam(logname);
+    if (!pw) miscerror("spoofed login name `%s' is not valid",logname);
+    spoofuid= pw->pw_uid;
+    spoofgid= pw->pw_gid;
+    ngidssize= ngids; ngids= 0;
+    if (ngidssize<5) {
+      ngidssize= 5;
+      gidarray= xrealloc(gidarray,sizeof(gid_t)*ngidssize);
+    }
+    gidarray[ngids++]= spoofgid;
+    while ((gr= getgrent())) { /* ouch! getgrent has no error behaviour! */
+      for (mem= gr->gr_mem; *mem && strcmp(*mem,logname); mem++);
+      if (!*mem) continue;
+      if (ngids>=ngidssize) {
+       ngidssize= (ngids+5)<<1;
+       gidarray= xrealloc(gidarray,sizeof(gid_t)*ngidssize);
+      }
+      gidarray[ngids++]= gr->gr_gid;
+    }
+  }
+
   cwdbufsize= 0; cwdbuf= 0;
   if (!hidecwd) {
     for (;;) {
@@ -787,13 +848,22 @@ int main(int argc, char *const *argv) {
     ovused= -1;
     ovbuf= 0;
     break;
+  case ot_builtin:
+    l= strlen(argv[0]);
+    if (l >= MAX_OVERRIDE_LEN-20)
+      miscerror("builtin service string is too long (%d, max is %d)",
+               l,MAX_OVERRIDE_LEN-21);
+    l+= 20;
+    ovbuf= xmalloc(l);
+    snprintf(ovbuf,l,"execute-builtin %s\n",argv[0]);
+    ovused= strlen(ovbuf);
+    break;
   case ot_string:
     l= strlen(overridevalue);
     if (l >= MAX_OVERRIDE_LEN)
       miscerror("override string is too long (%d, max is %d)",l,MAX_OVERRIDE_LEN-1);
     ovbuf= xmalloc(l+2);
-    strcpy(ovbuf,overridevalue);
-    strcat(ovbuf,"\n");
+    snprintf(ovbuf,l+2,"%s\n",overridevalue);
     ovused= l+1;
     break;
   case ot_file:
@@ -876,7 +946,7 @@ int main(int argc, char *const *argv) {
   request_mbuf.servicelen= strlen(argv[0]);
   request_mbuf.lognamelen= strlen(logname);
   request_mbuf.cwdlen= cwdbufsize;
-  request_mbuf.callinguid= myuid;
+  request_mbuf.callinguid= spoofuid;
   request_mbuf.ngids= ngids+1;
   request_mbuf.nreadfds= 0;
   request_mbuf.nwritefds= 0;
@@ -887,7 +957,7 @@ int main(int argc, char *const *argv) {
     else request_mbuf.nreadfds++;
   }
   request_mbuf.nargs= argc-1;
-  request_mbuf.nvars= defvarsused;
+  request_mbuf.nvars= defvarused;
   request_mbuf.overridelen= ovused;
   xfwrite(&request_mbuf,sizeof(request_mbuf),swfile);
   xfwrite(serviceuser,sizeof(*serviceuser)*request_mbuf.serviceuserlen,swfile);
@@ -895,15 +965,16 @@ int main(int argc, char *const *argv) {
   xfwrite(logname,sizeof(*logname)*request_mbuf.lognamelen,swfile);
   xfwrite(cwdbuf,sizeof(*cwdbuf)*request_mbuf.cwdlen,swfile);
   if (ovused>=0) xfwrite(ovbuf,sizeof(*ovbuf)*ovused,swfile);
-  xfwrite(&mygid,sizeof(gid_t),swfile);
+  xfwrite(&spoofgid,sizeof(gid_t),swfile);
   xfwrite(gidarray,sizeof(gid_t)*ngids,swfile);
   xfwritefds(fdm_read,request_mbuf.nreadfds,swfile);
   xfwritefds(fdm_write,request_mbuf.nwritefds,swfile);
   for (i=1; i<argc; i++)
     xfwritestring(argv[i],swfile);
-  for (i=0; i<defvarsused; i++)
-    for (j=0; j<2; j++)
-      xfwritestring(defvarsarray[i][j],swfile);
+  for (i=0; i<defvarused; i++) {
+    xfwritestring(defvararray[i].key,swfile);
+    xfwritestring(defvararray[i].value,swfile);
+  }
   ul= REQUEST_END_MAGIC; xfwrite(&ul,sizeof(ul),swfile);
   xfflush(swfile);
 
@@ -936,6 +1007,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;
@@ -944,11 +1016,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);
     }