chiark / gitweb /
Add --spoof-user. Tidy up servicepw &c in daemon.
[userv.git] / client.c
index 68ec6a4d1b9ff68dafbd821f3bb3c3078016ba94..9d12491be10c38eefc8bff5f22cd72fc3f90e833 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;
 
@@ -143,7 +145,7 @@ static unsigned long timeout=0;
 static int signalsexit=254;
 static int sigpipeok=0, hidecwd=0;
 static int overridetype= ot_none;
-static const char *overridevalue;
+static const char *overridevalue, *spoofuser=0;
 
 static FILE *srfile, *swfile;
 
@@ -272,19 +274,20 @@ 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"
+    "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");
 }
 
@@ -493,6 +496,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       },
@@ -505,6 +513,7 @@ const struct optioninfo optioninfos[]= {
   {  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 +670,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, j, 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;
@@ -755,19 +766,44 @@ int main(int argc, char *const *argv) {
   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"
+  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");
 
-  logname= getenv("LOGNAME");
-  if (!logname) logname= getenv("USER");
-  if (logname) {
+  if (spoofuser) {
+    logname= spoofuser;
     pw= getpwnam(logname);
-    if (!pw || pw->pw_uid != myuid) logname= 0;
-  }
-  if (!logname) {
-    pw= getpwuid(myuid); if (!pw) syscallerror("cannot determine your login name");
-    logname= pw->pw_name;
+    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;
+    }
+  } else {
+    spoofuid= myuid;
+    spoofgid= mygid;
+    logname= getenv("LOGNAME");
+    if (!logname) logname= getenv("USER");
+    if (logname) {
+      pw= getpwnam(logname);
+      if (!pw || pw->pw_uid != myuid) logname= 0;
+    }
+    if (!logname) {
+      pw= getpwuid(myuid); if (!pw) miscerror("cannot determine your login name");
+      logname= pw->pw_name;
+    }
   }
 
   cwdbufsize= 0; cwdbuf= 0;
@@ -876,7 +912,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;
@@ -895,7 +931,7 @@ 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);