#include <unistd.h>
#include <ctype.h>
#include <pwd.h>
+#include <grp.h>
#include <signal.h>
#include <limits.h>
#include <sys/time.h>
#include "config.h"
#include "common.h"
+#include "version.h"
struct optioninfo;
};
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;
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);
}
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");
}
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) {
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;
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 },
{ '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 }
};
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;
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 */
}
}
}
- 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;
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) {
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 (;;) {
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:
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;
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);
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);
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;
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);
}