* strtoul;
* memcpy, memset, memcpy;
* realloc(0,size) must work and be equivalent to malloc(size).
+* free(0) must work and do nothing
* <stdarg.h> (not varargs) and v[sf][n]printf.
System interfaces:
overlord.o: config.h common.h pcsum.h daemon.h
-servexec.o: config.h common.h pcsum.h daemon.h lib.h
+servexec.o: config.h common.h pcsum.h daemon.h lib.h version.h
lib.o: config.h lib.h
enum signalsexitspecials { se_number=-100, se_numbernocore, se_highbit, se_stdout };
enum overridetypes { ot_none, ot_string, ot_file };
-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, builtin;
+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, *spoofuser=0;
static void usage(void) {
if (fprintf(stderr,
"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"
static void of_defvar(const struct optioninfo *oip, const char *value, char *key) {
int i;
+ 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<defvarsused && strcmp(defvarsarray[i][0],key); i++);
- if (defvarsused >= MAX_ARGSDEFVARS) usageerror("far too many --defvar or -D options");
- if (i>=defvarsavail) {
- defvarsavail+=10; defvarsavail<<=1;
- defvarsarray= xrealloc(defvarsarray,sizeof(const char*)*2*defvarsavail);
+ 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) {
hidecwd=1;
}
+static void of_builtin(const struct optioninfo *oip, const char *value, char *key) {
+ builtin=1;
+}
+
static void of_help(const struct optioninfo *oip, const char *value, char *key) {
usage();
exit(0);
{ '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 },
char *argp;
const struct optioninfo *oip;
struct sockaddr_un ssockname;
- int sfd, ngids, i, j, tempfd, l, c, reading, fd, r, status, ngidssize;
+ int sfd, ngids, i, tempfd, l, c, reading, fd, r, status, ngidssize;
sigset_t sset;
unsigned long ul;
size_t cwdbufsize;
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 (builtin) {
+ serviceuser= "-";
+ } else {
+ if (!*argpp) usageerror("no service user given after options");
+ serviceuser= *argpp++;
+ }
+ if (!*argpp) usageerror(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;
- if (argc > MAX_ARGSDEFVARS) usageerror("far too many arguments");
+ 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) {
+ 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;
+ }
+
+ if (!strcmp(serviceuser,"-")) serviceuser= logname;
pw= getpwnam(serviceuser);
if (!pw) miscerror("requested service user `%s' is not a user",serviceuser);
serviceuid= pw->pw_uid;
}
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;
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);
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);
#endif
#define MAX_ALLOW_FD 1024
-#define MAX_INCLUDE_NEST 40
#define MAX_GENERAL_STRING (1024*1024)
#define MAX_OVERRIDE_LEN MAX_GENERAL_STRING
-#define MAX_ARGSDEFVARS 4096
+#define MAX_ARGSDEFVAR 4096
#define MAX_GIDS 1024
#ifdef DEBUG
-# define BASE_MAGIC 0x5deb7567 /* "\x5d\xebug" */
+# define BASE_MAGIC 0x5deb7567UL /* "\x5d\xebug" */
#else
-# define BASE_MAGIC 0x755e7276 /* "u\x5erv" */
+# define BASE_MAGIC 0x755e7276UL /* "u\x5erv" */
#endif
enum {
#include <sys/types.h>
#define RESET_CONFIGURATION " \n\
- cd " USERDIRPREFIX " \n\
+ cd ~/ \n\
reject \n\
no-set-environment \n\
suppress-args \n\
#define DEFAULTINCLUDELOOKUP ":default"
#define EMPTYINCLUDELOOKUP ":empty"
-#define USERDIRPREFIX USERDIR "/"
#define USERCONFIGDIRBASE SYSTEMUSERVCONFIGDIR
#define USERCONFIGDIR HIDDENPREFIX USERCONFIGDIRBASE
#define USERUSERVCONFIGPATH USERDIR "/" USERCONFIGDIR
void senderrmsgstderr(const char *errmsg);
void disconnect(int exitstatus) NONRETURNING;
+void always_dumpparameter(const char *parm, char **values);
+void always_dumpexecsettings(void);
+
void debug_dumprequest(pid_t mypid);
void debug_dumpexecsettings(void);
void debug_dumpparameter(const char *parm, char **values);
pid_t nondebug_fork(void);
const char *nondebug_serviceuserdir(const char *ifnondebug);
+typedef void builtinserviceexec_fnt(const char *const *args);
+builtinserviceexec_fnt NONRETURNING bisexec_environment, bisexec_parameter;
+builtinserviceexec_fnt NONRETURNING bisexec_version;
+builtinserviceexec_fnt NONRETURNING bisexec_toplevel, bisexec_override, bisexec_reset;
+builtinserviceexec_fnt NONRETURNING bisexec_execute;
+
void execservice(const int synchsocket[], int clientfd) NONRETURNING;
void servicerequest(int sfd) NONRETURNING;
int synchread(int fd, int ch);
+const char *defaultpath(void);
struct fdstate {
int iswrite, realfd, holdfd;
/* tokv_word_read, tokv_word_write */
};
-extern char **argarray;
-extern char *((*defvararray)[2]);
+struct keyvaluepair { char *key, *value; };
+
+extern struct request_msg request_mbuf;
+extern struct keyvaluepair *defvararray;
extern struct fdstate *fdarray; /* indexed by nominal fd */
extern int fdarraysize, fdarrayused;
extern int restfdwantstate, restfdwantrw;
-extern struct request_msg request_mbuf;
+extern int service_ngids;
+extern char **argarray;
extern char *serviceuser, *service, *logname, *cwd;
extern char *overridedata, *userrcfile;
extern char *serviceuser_dir, *serviceuser_shell, *callinguser_shell;
-extern int service_ngids;
extern gid_t *calling_gids, *service_gids;
-extern const char **calling_groups, **service_groups;
extern uid_t serviceuser_uid;
+extern const char **calling_groups, **service_groups;
extern char *execpath, **execargs;
extern int execute; /* One of the execution modes tokt_execmode */
extern int setenvironment, suppressargs, disconnecthup;
+extern builtinserviceexec_fnt *execbuiltin;
extern int syslogopenfacility;
#endif
#include "lib.h"
#include "tokens.h"
-#ifdef DEBUG
-
-static const char *sl_ident= "UNSET";
-static int sl_option=0, sl_facility=0;
-
-void openlog(const char *ident, int option, int facility) {
- sl_ident= ident;
- sl_option= option;
- sl_facility= facility;
-}
-
-void syslog(int priority, const char *fmt, ...) {
- va_list al;
- fprintf(stderr,"syslog: %s<%d.%d>(%d): ",sl_ident,sl_facility,priority,sl_option);
- va_start(al,fmt);
- vfprintf(stderr,fmt,al);
- va_end(al);
- fputc('\n',stderr);
-}
-
-void closelog(void) {
- sl_ident= "CLOSED";
- sl_option= sl_facility= 0;
-}
-
static void fdwantdumprwhead(int *donehead, const char *whichstr, const char *rwstr) {
if (*donehead) return;
printf("fds %s%s%s:",whichstr,rwstr?" ":"",rwstr?rwstr:"");
printf("%s: %s\n",whichstr,val?"yes":"no");
}
+void always_dumpparameter(const char *parm, char **values) {
+ printf("config parameter `%s':",parm);
+ while (*values) printf(" `%s'",*values++);
+ printf("\n");
+}
+
+void always_dumpexecsettings(void) {
+ char **cpp;
+
+ if (userrcfile) printf("user-rcfile: `%s'\n",userrcfile);
+ else printf("user-rcfile: <none>\n");
+ fdwantdump("required",tokv_word_requirefd,"ERROR");
+ fdwantdump("allowed",tokv_word_allowfd,"either");
+ fdwantdump("ignored",tokv_word_ignorefd,0);
+ fdwantdump("null",tokv_word_nullfd,"both");
+ fdwantdump("rejected",tokv_word_rejectfd,0);
+ fputs("execute: ",stdout);
+ switch (execute) {
+ case tokv_word_reject: printf("reject"); break;
+ case tokv_word_execute: printf("`%s'",execpath); break;
+ case tokv_word_executefromdirectory: printf("from directory, `%s'",execpath); break;
+ case tokv_word_executefrompath: printf("from path"); break;
+ case tokv_word_executebuiltin: printf("builtin %s",execpath); break;
+ default: abort();
+ }
+ if (execargs) {
+ fputs("\n" "no exec arguments\n",stdout);
+ } else {
+ fputs("\n" "exec arguments:",stdout);
+ for (cpp= execargs; cpp; cpp++) printf(" `%s'",*cpp);
+ putchar('\n');
+ }
+ truefalsedump("set-environment",setenvironment);
+ truefalsedump("suppress-args",suppressargs);
+ truefalsedump("disconnect-hup",disconnecthup);
+ truefalsedump("set-environment",setenvironment);
+}
+
+#ifdef DEBUG
+
+static const char *sl_ident= "UNSET";
+static int sl_option=0, sl_facility=0;
+
+void openlog(const char *ident, int option, int facility) {
+ sl_ident= ident;
+ sl_option= option;
+ sl_facility= facility;
+}
+
+void syslog(int priority, const char *fmt, ...) {
+ va_list al;
+ fprintf(stderr,"syslog: %s<%d.%d>(%d): ",sl_ident,sl_facility,priority,sl_option);
+ va_start(al,fmt);
+ vfprintf(stderr,fmt,al);
+ va_end(al);
+ fputc('\n',stderr);
+}
+
+void closelog(void) {
+ sl_ident= "CLOSED";
+ sl_option= sl_facility= 0;
+}
+
static void groupsdump(int ngids, const gid_t *gids, const char *const *groups) {
int i;
for (i=0; i<request_mbuf.nargs; i++) printf(" `%s'",argarray[i]);
printf("\n" "variables:");
for (i=0; i<request_mbuf.nvars; i++)
- printf(" `%s'=`%s'",defvararray[i][0],defvararray[i][1]);
+ printf(" `%s'=`%s'",defvararray[i].key,defvararray[i].value);
printf("\n");
if (getenv("USERVD_SLEEP")) sleep(atoi(getenv("USERVD_SLEEP")));
}
void debug_dumpexecsettings(void) {
printf("configuration parsed\n");
- if (userrcfile) printf("user-rcfile: `%s'\n",userrcfile);
- else printf("user-rcfile: <none>\n");
- fdwantdump("required",tokv_word_requirefd,"ERROR");
- fdwantdump("allowed",tokv_word_allowfd,"either");
- fdwantdump("ignored",tokv_word_ignorefd,0);
- fdwantdump("null",tokv_word_nullfd,"both");
- fdwantdump("rejected",tokv_word_rejectfd,0);
- printf("execute: ");
- switch (execute) {
- case tokv_word_reject: printf("reject"); break;
- case tokv_word_execute: printf("`%s'",execpath); break;
- case tokv_word_executefromdirectory: printf("from directory, `%s'",execpath); break;
- case tokv_word_executefrompath: printf("from path"); break;
- default: abort();
- }
- printf("\n");
- truefalsedump("set-environment",setenvironment);
- truefalsedump("suppress-args",suppressargs);
- truefalsedump("disconnect-hup",disconnecthup);
- truefalsedump("set-environment",setenvironment);
+ always_dumpexecsettings();
}
void debug_dumpparameter(const char *parm, char **values) {
- printf("config parameter `%s':",parm);
- while (*values) printf(" `%s'",*values++);
- printf("\n");
+ always_dumpparameter(parm,values);
}
static int groupsallin(int na, const gid_t *lista,
autovaldeftype(`string')
autovaldeftype(`execmode')
autovaldeftype(`ehandlemode')
+autovaldeftype(`builtinservice')
autovaldeftype(`misc')
autovaldeftype(`internal')
`lr_flag= &'makename(`$1')`; lr_flagval= 1; ')
isdirectivefn(`no-$1',`dfg_setflag',`',
`lr_flag= &'makename(`$1')`; lr_flagval= 0; ')')
+dnl `reset' is also a builtin service
isexecmode(`reject')
-isexecmode(`execute')
+dnl `execute' is also a builtin service
isexecmode(`execute-from-directory')
isexecmode(`execute-from-path')
+isexecmode(`execute-builtin')
isehandlemode(`errors-to-stderr')
isehandlemode(`errors-to-syslog')
isehandlemode(`errors-to-file')
isflagpair(`suppress-args')
isflagpair(`disconnect-hup')
isdirective(`cd')
-isdirective(`reset')
isdirective(`user-rcfile')
isdirective(`include')
isdirectivefn(`include-ifexist',`df_include')
isparmcondition(`range')
isparmcondition(`grep')
+dnl builtin services
+define(`isbuiltinservice',`wordtypelexexec(`$1',`tokt_builtinservice',
+ `lr_bispa= bispa_'makename(`$2')`; lr_bisexec= bisexec_'makename(`$1')`; ')')
+isbuiltinservice(`environment',`none')
+isbuiltinservice(`parameter',`parameter')
+isbuiltinservice(`version',`none')
+isbuiltinservice(`toplevel',`none')
+isbuiltinservice(`override',`none')
+
+dnl builtin services that are also directive names
+define(`isdirectivebuiltinservice',
+ `wordtypelexexec(`$1',`tokt_directive|tokt_builtinservice$3',
+ `lr_dir= df_'makename(`$1')`; lr_bispa= bispa_'makename(`$2')`; lr_bisexec= bisexec_'makename(`$1')`; ')')
+isdirectivebuiltinservice(`reset',`none')
+isdirectivebuiltinservice(`execute',`none',`|tokt_execmode')
+
dnl parameters
define(`isparameter',`wordtypelexexec(`$1',`tokt_parameter',
`lr_parameter= pf_'makename(`$1')`; ')')
typedef int directive_fnt(int dtoken);
static directive_fnt df_reject, df_execute, df_executefrompath;
-static directive_fnt df_executefromdirectory;
+static directive_fnt df_executefromdirectory, df_executebuiltin;
static directive_fnt df_errorstostderr, df_errorstosyslog, df_errorstofile;
static directive_fnt dfg_fdwant, dfg_setflag;
static directive_fnt df_reset, df_cd, df_userrcfile, df_include;
* freeparm can be used to free such an array.
*/
+typedef int builtinserviceparse_fnt(char ***rnewargs);
+static builtinserviceparse_fnt bispa_none, bispa_parameter;
+/* These parse the arguments to a builtin service, including the
+ * newline at the end of the line. *rnewargs will initially be
+ * null, indicating that no arguments are to be set; the function
+ * may store a mallocd array of mallocd strings in it,
+ * containing the arguments it wishes to have set (null-pointer
+ * terminated).
+ */
+
static int yylex(void);
/* Returns a token (which may be an eof or error exception) */
static directive_fnt *lr_dir;
static parmcondition_fnt *lr_parmcond;
+static builtinserviceparse_fnt *lr_bispa;
+static builtinserviceexec_fnt *lr_bisexec;
static parameter_fnt *lr_parameter;
static int lr_loglevel, lr_logfacility, lr_min, lr_max, *lr_flag;
static int lr_flagval, lr_controlend;
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-static directive_fnt *lr_dir=0;
-static parmcondition_fnt *lr_parmcond=0;
-static parameter_fnt *lr_parameter=0;
-static int lr_loglevel, lr_logfacility, lr_min, lr_max, *lr_flag;
-static int lr_flagval, lr_controlend;
-static int lr_fdwant_readwrite;
-
static void useless(void) { (void)yyunput; (void)useless; /* to shut up GCC ! */ }
static void closeerrorfile(void) {
*rv= string2path(cp); return 0;
}
-static int pa_parameter(char ***rvalues) {
+static int pa_parameter(char ***rvalues, char **rname) {
/* Scans a single parameter token and calls the appropriate parameter
* function, returning tokv_error or 0 just like the parameter function.
+ * If rname is non-null then the name of the parameter (malloc'd) will
+ * be stored in it.
*/
int token, r, i;
+ char *name;
token= yylex(); if (token == tokv_error) return token;
+ name= xstrsave(yytext);
if ((token & tokm_repres) != tokr_nonstring &&
!memcmp(yytext,"u-",2) && strlen(yytext)>=3) {
for (i=0;
- i<request_mbuf.nvars && strcmp(yytext+2,defvararray[i][0]);
+ i<request_mbuf.nvars && strcmp(yytext+2,defvararray[i].key);
i++);
if (i>=request_mbuf.nvars) {
*rvalues= xmalloc(sizeof(char*));
**rvalues= 0;
} else {
- parm_1string(rvalues,defvararray[i][1]);
+ parm_1string(rvalues,defvararray[i].value);
}
} else {
- if (!(token & tokt_parameter)) return unexpected(token,-1,"parameter name");
+ if (!(token & tokt_parameter)) {
+ free(name);
+ return unexpected(token,-1,"parameter name");
+ }
r= (lr_parameter)(token,rvalues);
- if (r) return r;
+ if (r) { free(name); return r; }
}
- debug_dumpparameter(yytext,*rvalues);
+ debug_dumpparameter(name,*rvalues);
+ if (rname) *rname= name;
+ else free(name);
return 0;
}
*rtrue= actrue; return 0;
} else if (token & tokt_parmcondition) {
r= pa_mwsp(); if (r) return r;
- r= pa_parameter(&parmvalues); if (r) return r;
+ r= pa_parameter(&parmvalues,0); if (r) return r;
r= (lr_parmcond)(token,parmvalues,rtrue); freecharparray(parmvalues);
return r;
}
/* Directive functions and associated `common code' functions */
-int df_reject(int dtoken) {
- int r;
-
- r= pa_mnl(); if (r) return r;
- execute= tokv_word_reject;
- free(execpath); execpath= 0;
- freecharparray(execargs); execargs= 0;
- return 0;
-}
-
-int df_executefrompath(int dtoken) {
- int r;
-
- r= pa_mnl(); if (r) return r;
- execute= tokv_word_executefrompath;
- free(execpath); execpath= 0;
- freecharparray(execargs); execargs= 0;
- return 0;
-}
-
static int paa_pathargs(const char **rv, char ***newargs_r) {
/* Repeated calls do _not_ overwrite newargs_r; caller must free.
* Repeated calls _do_ overwrite string returned in rv.
return r;
}
+static void execreset(void) {
+ execute= 0;
+ execbuiltin= 0;
+ free(execpath); execpath= 0;
+ freecharparray(execargs); execargs= 0;
+}
+
+int df_reject(int dtoken) {
+ int r;
+
+ r= pa_mnl(); if (r) return r;
+ execreset();
+ execute= tokv_word_reject;
+ return 0;
+}
+
+int df_executefrompath(int dtoken) {
+ int r;
+
+ r= pa_mnl(); if (r) return r;
+ execreset();
+ execute= tokv_word_executefrompath;
+ return 0;
+}
+
int df_execute(int dtoken) {
const char *rv;
char **newargs;
int r;
r= paa_pathargs(&rv,&newargs); if (r) return r;
+ execreset();
execute= tokv_word_execute;
- freecharparray(execargs); execargs= newargs;
- free(execpath); execpath= xstrsave(rv);
+ execargs= newargs;
+ execpath= xstrsave(rv);
return 0;
}
" or link to one (mode=0%o)",fn,stab.st_mode);
free(fn); freecharparray(newargs); return tokv_error;
}
+ execreset();
execute= tokv_word_executefromdirectory;
- freecharparray(execargs); execargs= newargs;
- free(execpath); execpath= fn;
+ execargs= newargs;
+ execpath= fn;
+ return 0;
+}
+
+static int bispa_none(char ***rnewargs) {
+ return pa_mnl();
+}
+
+static int bispa_parameter(char ***rnewargs) {
+ int r, i;
+ char **parmvalues, *name, **newargs;
+
+ r= pa_mwsp(); if (r) return r;
+ r= pa_parameter(&parmvalues,&name); if (r) return r;
+ for (i=0; parmvalues[i]; i++);
+ newargs= xmalloc(sizeof(char*)*(i+2));
+ newargs[0]= name;
+ memcpy(newargs+1,parmvalues,sizeof(char*)*(i+1));
+ free(parmvalues);
+ r= pa_mnl(); if (r) { free(newargs); return r; }
+ *rnewargs= newargs;
+ return 0;
+}
+
+int df_executebuiltin(int dtoken) {
+ int r;
+ builtinserviceexec_fnt *bisexec;
+ char *newpath, **newargs;
+
+ r= pa_mwsp(); if (r) return r;
+ r= yylex(); if (r == tokv_error) return r;
+ if (!(r & tokt_builtinservice)) return unexpected(r,-1,"builtin service name");
+ bisexec= lr_bisexec;
+ newpath= xstrsave(yytext);
+ newargs= 0;
+ r= lr_bispa(&newargs); if (r) { free(newpath); return r; }
+
+ execreset();
+ execute= tokv_word_executebuiltin;
+ execbuiltin= bisexec;
+ execpath= newpath;
+ execargs= newargs;
return 0;
}
int r, done, thisdone, cpl, c;
r= pa_mwsp(); if (r) return r;
- r= pa_parameter(&parmvalues); if (r) return r;
+ r= pa_parameter(&parmvalues,0); if (r) return r;
r= paa_1path(&cp); if (r) { freecharparray(parmvalues); return r; }
if (stat(cp,&stab)) {
parseerrprint("unable to access directory `%s': %s",cp,strerror(errno));
/* NB: defaults for the execution state are not set here, but in
* the RESET_CONFIGURATION #define in daemon.h. */
-char **argarray;
-char *((*defvararray)[2]);
+struct request_msg request_mbuf;
+struct keyvaluepair *defvararray;
struct fdstate *fdarray;
int fdarraysize, fdarrayused;
int restfdwantstate= tokv_word_rejectfd, restfdwantrw;
-struct request_msg request_mbuf;
+int service_ngids;
+char **argarray;
char *serviceuser, *service, *logname, *cwd;
char *overridedata, *userrcfile;
char *serviceuser_dir, *serviceuser_shell, *callinguser_shell;
-int service_ngids;
gid_t *calling_gids, *service_gids;
-const char **calling_groups, **service_groups;
uid_t serviceuser_uid=-1;
+const char **calling_groups, **service_groups;
char *execpath, **execargs;
int execute;
int setenvironment, suppressargs, disconnecthup;
+builtinserviceexec_fnt *execbuiltin;
int syslogopenfacility=-1;
static FILE *swfile, *srfile;
return 0;
}
+const char *defaultpath(void) {
+ return serviceuser_uid ? DEFAULTPATH_USER : DEFAULTPATH_ROOT;
+}
+
/* General-purpose functions; these do nothing special about signals */
static void blocksignals(void) {
}
static void receive_request(void) {
- int i,j, fd;
+ int i, fd;
unsigned long ul;
xfread(&request_mbuf,sizeof(request_mbuf));
fdarray[fd].iswrite= (i>=request_mbuf.nreadfds);
}
- assert(request_mbuf.nargs <= MAX_ARGSDEFVARS);
+ assert(request_mbuf.nargs <= MAX_ARGSDEFVAR);
argarray= xmalloc(sizeof(char*)*(request_mbuf.nargs));
for (i=0; i<request_mbuf.nargs; i++) argarray[i]= xfreadstring();
- assert(request_mbuf.nvars <= MAX_ARGSDEFVARS);
- defvararray= xmalloc(sizeof(char*)*request_mbuf.nvars*2);
- for (i=0; i<request_mbuf.nvars; i++)
- for (j=0; j<2; j++) defvararray[i][j]= xfreadstring();
+ assert(request_mbuf.nvars <= MAX_ARGSDEFVAR);
+ defvararray= xmalloc(sizeof(struct keyvaluepair)*request_mbuf.nvars);
+ for (i=0; i<request_mbuf.nvars; i++) {
+ defvararray[i].key= xfreadstring();
+ assert(defvararray[i].key[0]);
+ defvararray[i].value= xfreadstring();
+ }
xfread(&ul,sizeof(ul));
assert(ul == REQUEST_END_MAGIC);
}
static void establish_pipes(void) {
int fd, tempfd;
- char pipepathbuf[PIPEMAXLEN];
+ char pipepathbuf[PIPEMAXLEN+2];
for (fd=0; fd<fdarrayused; fd++) {
if (fdarray[fd].iswrite == -1) continue;
- snyprintf(pipepathbuf,sizeof(pipepathbuf), PIPEFORMAT,
+ pipepathbuf[sizeof(pipepathbuf)-2]= 0;
+ snyprintf(pipepathbuf,sizeof(pipepathbuf),PIPEFORMAT,
(unsigned long)request_mbuf.clientpid,(unsigned long)mypid,fd);
+ assert(!pipepathbuf[sizeof(pipepathbuf)-2]);
tempfd= open(pipepathbuf,O_RDWR);
- if (tempfd == -1) syscallerror("prelim open pipe");
- if (!fdarray[fd].iswrite) {
- fdarray[fd].holdfd= open(pipepathbuf, O_WRONLY);
- if (fdarray[fd].holdfd == -1) syscallerror("hold open pipe");
- fdarray[fd].realfd= open(pipepathbuf, O_RDONLY);
- } else {
+ if (tempfd<0) syscallerror("prelim open pipe");
+ if (fdarray[fd].iswrite) {
fdarray[fd].holdfd= -1;
fdarray[fd].realfd= open(pipepathbuf, O_WRONLY);
+ } else {
+ fdarray[fd].holdfd= open(pipepathbuf, O_WRONLY);
+ if (fdarray[fd].holdfd<0) syscallerror("hold open pipe");
+ fdarray[fd].realfd= open(pipepathbuf, O_RDONLY);
}
- if (fdarray[fd].realfd == -1) syscallerror("real open pipe");
+ if (fdarray[fd].realfd<0) syscallerror("real open pipe");
if (unlink(pipepathbuf)) syscallerror("unlink pipe");
if (close(tempfd)) syscallerror("close prelim fd onto pipe");
}
serviceuser_dir= xstrsave(nondebug_serviceuserdir(pw->pw_dir));
serviceuser_shell= xstrsave(pw->pw_shell);
serviceuser_uid= pw->pw_uid;
+
if (initgroups(pw->pw_name,pw->pw_gid)) syscallerror("initgroups");
if (setreuid(pw->pw_uid,pw->pw_uid)) syscallerror("setreuid 1");
if (setreuid(pw->pw_uid,pw->pw_uid)) syscallerror("setreuid 2");
r= stat(execpath,&stab);
if (r) syscallfailure("checking for executable in directory, `%s'",execpath);
break;
+ case tokv_word_executebuiltin:
+ break;
case tokv_word_executefrompath:
if (strchr(service,'/')) {
r= stat(service,&stab);
execpath= service;
} else {
string= getenv("PATH");
- if (!string) failure("execute-from-path, but daemon inherited no PATH !");
+ if (!string) string= defaultpath();
while (string) {
delim= strchr(string,':');
if (delim) {
- if (delim-string > INT_MAX)
+ if (delim-string > MAX_GENERAL_STRING)
failure("execute-from-path, but PATH component too long");
partsize= delim-string;
nextstring= delim+1;
fdarray[fd].realfd= open("/dev/null",
fdarray[fd].iswrite == -1 ? O_RDWR :
fdarray[fd].iswrite ? O_WRONLY : O_RDONLY);
- if (fdarray[fd].realfd == -1)
+ if (fdarray[fd].realfd<0)
syscallfailure("cannot open /dev/null for null fd");
break;
case tokv_word_requirefd:
if (fdarray[fd].realfd == -1) {
fdarray[fd].iswrite= (fdarray[fd].wantrw == tokv_word_write);
fdarray[fd].realfd= open("/dev/null",fdarray[fd].iswrite ? O_WRONLY : O_RDONLY);
- if (fdarray[fd].realfd == -1)
+ if (fdarray[fd].realfd<0)
syscallfailure("cannot open /dev/null for allowed but not provided fd");
} else {
if (fdarray[fd].iswrite) {
"<builtin toplevel configuration>",1);
ensurelogopen(USERVD_LOGFACILITY);
- if (r == tokv_error) failure("error encountered while parsing configuration files");
+ if (r == tokv_error) failure("error encountered while parsing configuration");
assert(r == tokv_quit);
debug_dumpexecsettings();
#include "common.h"
#include "daemon.h"
#include "lib.h"
+#include "version.h"
static void NONRETURNING serv_syscallfail(const char *msg) {
fputs("uservd(service): ",stderr);
_exit(-1);
}
+static void NONRETURNING serv_checkstdoutexit(void) {
+ if (ferror(stdout) || fclose(stdout)) serv_syscallfail("write stdout");
+ _exit(0);
+}
+
+void bisexec_environment(const char *const *argv) {
+ execlp("env","env",(char*)0);
+ serv_syscallfail("execute `env'");
+}
+
+void bisexec_parameter(const char *const *argv) {
+ always_dumpparameter(execargs[0],execargs+1);
+ serv_checkstdoutexit();
+}
+
+void bisexec_version(const char *const *argv) {
+ const unsigned char *p;
+ int i;
+
+ printf("uservd version " VERSION "; copyright (C)1996-1997 Ian Jackson.\n"
+#ifdef DEBUG
+ "DEBUGGING VERSION"
+#else
+ "production version"
+#endif
+ " - protocol magic number %08lx\n"
+ "protocol checksum:",
+ BASE_MAGIC);
+ for (i=0, p=protocolchecksumversion; i<sizeof(protocolchecksumversion); i++, p++)
+ printf("%02x",*p);
+ printf("\n"
+ "rendezvous socket: `" RENDEZVOUSPATH "'\n"
+ "system config dir: `" SYSTEMCONFIGDIR "'\n"
+ "pipe filename format: `%s' (max length %d)\n"
+ "maximums: fd %-10d general string %d"
+ " gids %-10d override length %d\n\n"
+ " args or variables %-10d error message %d\n"
+ " nested inclusion %-10d errno string reserve %d\n",
+ PIPEFORMAT, PIPEMAXLEN,
+ MAX_ALLOW_FD, MAX_GENERAL_STRING,
+ MAX_GIDS, MAX_OVERRIDE_LEN,
+ MAX_ARGSDEFVAR, ERRMSG_RESERVE_ERRNO,
+ MAX_INCLUDE_NEST, MAX_ERRMSG_LEN);
+ serv_checkstdoutexit();
+}
+
+static void NONRETURNING dumpconfig(const char *string) {
+ int nspaces, c;
+
+ assert(*string);
+ nspaces= 0;
+ while ((c= *string++)) {
+ switch (c) {
+ case ' ': nspaces++; break;
+ case '\n': nspaces= 0; putchar('\n'); break;
+ default:
+ while (nspaces>0) { putchar(' '); nspaces--; }
+ putchar(c);
+ }
+ }
+ assert(*--string == '\n');
+ serv_checkstdoutexit();
+}
+
+void bisexec_toplevel(const char *const *argv) {
+ dumpconfig(TOPLEVEL_CONFIGURATION);
+}
+
+void bisexec_override(const char *const *argv) {
+ dumpconfig(TOPLEVEL_OVERRIDDEN_CONFIGURATION);
+}
+
+void bisexec_reset(const char *const *argv) {
+ dumpconfig(RESET_CONFIGURATION);
+}
+
+void bisexec_execute(const char *const *argv) {
+ always_dumpexecsettings();
+ serv_checkstdoutexit();
+}
+
static void serv_resetsignal(int signo) {
struct sigaction sig;
static const char *see_home(void) { return serviceuser_dir; }
static const char *see_shell(void) { return serviceuser_shell; }
-static const char *see_path(void) {
- return serviceuser_uid ? DEFAULTPATH_USER : DEFAULTPATH_ROOT;
-}
-
static const char *see_service(void) { return service; }
static const char *see_c_cwd(void) { return cwd; }
static const char *see_c_logname(void) { return logname; }
{ "LOGNAME", see_logname },
{ "HOME", see_home },
{ "SHELL", see_shell },
- { "PATH", see_path },
+ { "PATH", defaultpath },
{ "USERV_SERVICE", see_service },
{ "USERV_CWD", see_c_cwd },
{ "USERV_USER", see_c_logname },
for (sei= serv_envinfos; sei->name; sei++)
if (setenv(sei->name,sei->fn(),1)) serv_syscallfail("setenv standard");
for (i=0; i<request_mbuf.nvars; i++) {
- l= strlen(defvararray[i][0])+9;
+ l= strlen(defvararray[i].key)+9;
if (l>envvarbufsize) { envvarbufsize= l; envvarbuf= xrealloc(envvarbuf,l); }
- snyprintf(envvarbuf,l,"USERV_U_%s",defvararray[i][0]);
- if (setenv(envvarbuf,defvararray[i][1],1)) serv_syscallfail("setenv defvar");
+ snyprintf(envvarbuf,l,"USERV_U_%s",defvararray[i].key);
+ if (setenv(envvarbuf,defvararray[i].value,1)) serv_syscallfail("setenv defvar");
}
nargs= 0;
if (!suppressargs) for (i=0; i<request_mbuf.nargs; i++) args[targ++]= argarray[i];
args[targ]= 0;
- execv(args[0],(char* const*)args);
+ if (execbuiltin)
+ execbuiltin(args);
+ else
+ execv(args[0],(char* const*)args);
serv_syscallfail("exec service program");
_exit(-1);
<p>
<example>
userv <var/options/ [--] <var/service-user/ <var/service-name/ [<var/argument/ ...]
+userv <var/options/ -B|--builtin [--] <var/builtin-service/ [<var/info-argument/ ...]
</example>
<p>
<var/service-user/ specifies which user is to provide the service.
-The user may be a login name or a numeric uid.
+The user may be a login name or a numeric uid, or <tt/-/ to indicate
+that the service user is to be the same as the calling user.
<p>
The service name is interpreted by the userv<footnote><tt/userv/ is
the next.
<taglist>
+<tag/<tt/-B//
+<tag/<tt/--builtin//
+<item>
+Requests that a builtin service be provided. This is equivalent to
+using the <tt/--override/ option to specify a string consisting of
+<tt/--execute-builtin/ followed by the <var/builtin-service/
+requested, and requesting a service user of <tt/-/ (indicating the
+calling user). Note that the <var/info argument/s supplied will be
+ignored by most services; if the builtin service being requested
+requires a <var/service-argument/ then this must be supplied to the
+client in the same argument as the <var/builtin-service/. See <ref
+id="execdirectives"> for details of the builtin services available,
+and <ref id="optoverride"> for details of the <tt/--override/ options.
+
<tag/<tt/-f<var/fd/[<var/modifiers/]=<var/filename///
<tag/<tt/--file <var/fd/[<var/modifiers/]=<var/filename///
<item>
</taglist>
-<sect>Security-overriding options
+<sect id="optoverride">Security-overriding options
<p>
There are also some options which are available for debugging and to
</taglist>
-<sect1>Directives for changing execution settings
+<sect1 id="execdirectives">Directives for changing execution settings
<p>
The following directives modify the execution settings; the server
It is an error for the execution to fail when it is attempted (after
all the configuration has been parsed).
+<tag/<tt/execute-builtin <var/service-name/ <var/service-arguments//
+<item>
+Executes the builtin service <var/service-name/. These builtin
+services display information about the server and/or the request, and
+ignore any arguments passed from the service side except possibly to
+print them as part of their output. They write their results to their
+standard output (i.e., wherever file descriptor 1 is directed). The
+builtin services are:
+
+<taglist compact>
+<tag/<tt/execute//
+Displays the execution settings, defined variables,
+arguments, etc. with which the builtin service was invoked.
+
+<tag/<tt/environment//
+Displays the environment variable settings with which the builtin
+service was invoked.
+
+<tag/<tt/parameter <var/parameter///
+Displays the values of the service configuration language parameter
+specified.
+
+<tag/<tt/version//
+Displays the version string and compilation details of the uservd
+server program.
+
+<tag/<tt/reset//
+Displays the default reset configuration (evaluated when <tt/reset/ is
+found in a configuration file, or when an error is caught by
+<tt/catch-quit/).
+
+<tag/<tt/toplevel//
+Displays the top-level default configuration (the configuration data,
+evaluated by the server, which calls all the other configuration
+files).
+
+<tag/<tt/override//
+Displays the top-level override configuration (the configuration data,
+evaluated by the server, which causes all the other configuration data
+to be parsed).
+</taglist>
+
+In the future other builtin services may be defined which do more than
+just print information.
+
<tag/<tt/set-environment//
<tag/<tt/no-set-environment//
<item>