X-Git-Url: https://git.distorted.org.uk/~mdw/become/blobdiff_plain/45df69ab1437a0e5ab3d0d0411ebdf127430d8a5..af4f4d6a77aceba8e2d6f58d15e894df320e7c24:/src/become.c diff --git a/src/become.c b/src/become.c index 54e5551..b4956ff 100644 --- a/src/become.c +++ b/src/become.c @@ -1,10 +1,10 @@ /* -*-c-*- * - * $Id: become.c,v 1.12 1997/09/25 16:04:48 mdw Exp $ + * $Id: become.c,v 1.26 2004/04/08 01:36:20 mdw Exp $ * * Main code for `become' * - * (c) 1997 EBI + * (c) 1998 EBI */ /*----- Licensing notice --------------------------------------------------* @@ -26,54 +26,6 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/*----- Revision history --------------------------------------------------* - * - * $Log: become.c,v $ - * Revision 1.12 1997/09/25 16:04:48 mdw - * Change directory after becoming someone else, instead of before. This - * avoids problems with root-squashed NFS mounts. - * - * Revision 1.11 1997/09/24 09:48:45 mdw - * Fix (scary) overrun bug in group allocation stuff. - * - * Revision 1.10 1997/09/17 10:14:10 mdw - * Fix a typo. Support service names in `--port' option. - * - * Revision 1.9 1997/09/10 10:28:05 mdw - * Allow default port to be given as a service name or port number. Handle - * groups properly (lots of options here). - * - * Revision 1.8 1997/09/08 13:56:24 mdw - * Change criteria for expunging items from the user's PATH: instead of - * removing things starting with `.', remove things not starting with `/'. - * - * Revision 1.7 1997/09/08 13:43:20 mdw - * Change userid when creating tracefiles rather than fiddling with - * `access': it works rather better. Also, insert some stdio buffer - * flushing to ensure tracedumps are completely written. - * - * Revision 1.6 1997/09/05 13:47:44 mdw - * Make the `-L' (trace-level) option's argument optional, like the long - * version is. - * - * Revision 1.5 1997/09/05 11:45:19 mdw - * Add support for different login styles, and environment variable - * manipulation in a safe and useful way. - * - * Revision 1.4 1997/08/20 16:15:13 mdw - * Overhaul of environment handling. Fix daft bug in path search code. - * - * Revision 1.3 1997/08/07 16:28:59 mdw - * Do something useful when users attempt to become themselves. - * - * Revision 1.2 1997/08/04 10:24:20 mdw - * Sources placed under CVS control. - * - * Revision 1.1 1997/07/21 13:47:54 mdw - * Initial revision - * - */ - /*----- Header files ------------------------------------------------------*/ /* --- ANSI headers --- */ @@ -102,9 +54,19 @@ #include #include #include +#include extern char **environ; +/* --- mLib --- */ + +#include +#include +#include +#include +#include +#include + /* --- Local headers --- */ #include "become.h" @@ -112,12 +74,9 @@ extern char **environ; #include "check.h" #include "daemon.h" #include "lexer.h" -#include "mdwopt.h" #include "name.h" -#include "parser.h" +#include "parse.h" #include "rule.h" -#include "sym.h" -#include "utils.h" #include "userdb.h" /*----- Type definitions --------------------------------------------------*/ @@ -138,11 +97,9 @@ enum { /* --- Login behaviour types --- */ -enum { - l_preserve, /* Preserve the environment */ - l_setuser, /* Update who I am */ - l_login /* Do a full login */ -}; +#define l_preserve 0 /* Preserve the environment */ +#define l_setuser 1 /* Update who I am */ +#define l_login 2 /* Do a full login */ /* --- Group behaviour types --- * * @@ -356,7 +313,10 @@ static void bc__usage(FILE *fp) "Usage: \n" " $ -c \n" " $ [] [ []...]\n" - " $ -d [-p ] [-f ]\n"); +#ifndef NONETWORK + " $ -d [-p ] [-f ]\n" +#endif + ); } /* --- @bc__help@ --- * @@ -396,7 +356,17 @@ static void bc__help(FILE *fp, int suid) "-e, --preserve-environment Try to preserve the current environment\n" "-s, --su, --set-user Set environment variables to reflect USER\n" "-l, --login Really log in as USER\n" -"\n" +" [Default is " +#if DEFAULT_LOGIN_STYLE == l_preserve + "preserve-environment" +#elif DEFAULT_LOGIN_STYLE == l_setuser + "set-user" +#elif DEFAULT_LOGIN_STYLE == l_login + "login" +#else + "poorly configured" +#endif +"]\n\n" "-g GROUP, --group=GROUP Set primary group-id to be GROUP\n" #ifdef HAVE_SETGROUPS "-k, --keep-groups Keep your current set of groups\n" @@ -405,11 +375,15 @@ static void bc__help(FILE *fp, int suid) #endif "\n" "-c CMD, --command=CMD Run the (Bourne) shell command CMD\n" +#ifndef NONETWORK "\n" "-d, --daemon Start a daemon\n" +"-n, --nofork In daemon mode, don't fork into background\n" "-p PORT, --port=PORT In daemon mode, listen on PORT\n" -"-f FILE, --config-file=FILE In daemon mode, read config from FILE\n"); -#ifdef TRACING +"-f FILE, --config-file=FILE In daemon mode, read config from FILE\n" +#endif + ); +#ifndef NTRACE bc__write(fp, "\n"); if (!suid) { bc__write(fp, @@ -446,8 +420,10 @@ int main(int argc, char *argv[]) /* --- Become server setup parameters --- */ +#ifndef NONETWORK char *conffile = file_RULES; /* Default config file for daemon */ int port = 0; /* Default port for daemon */ +#endif /* --- Miscellanous shared variables --- */ @@ -472,14 +448,13 @@ int main(int argc, char *argv[]) /* --- Definitions for the various flags --- */ - enum { - f_daemon = 1, /* Start up in daemon mode */ - f_duff = 2, /* Fault in arguments */ - f_shell = 4, /* Run a default shell */ - f_dummy = 8, /* Don't actually do anything */ - f_setuid = 16, /* We're running setuid */ - f_havegroup = 32 /* Set a default group */ - }; +#define f_daemon 1u /* Start up in daemon mode */ +#define f_duff 2u /* Fault in arguments */ +#define f_shell 4u /* Run a default shell */ +#define f_dummy 8u /* Don't actually do anything */ +#define f_setuid 16u /* We're running setuid */ +#define f_havegroup 32u /* Set a default group */ +#define f_nofork 64u /* Don't fork into background */ /* --- Set up the program name --- */ @@ -488,12 +463,23 @@ int main(int argc, char *argv[]) if (getuid() != geteuid()) flags |= f_setuid; + /* --- Make sure standard file descriptors are open --- */ + + { + int fd; + do { + if ((fd = open("/dev/null", O_RDWR)) < 0) + die(1, "couldn't open /dev/null: %s", strerror(errno)); + } while (fd <= STDERR_FILENO); + close(fd); + } + /* --- Read the environment into a hashtable --- */ { char **p; - sym_createTable(&bc__env); + sym_create(&bc__env); for (p = environ; *p; p++) bc__putenv(0, *p, 0, 0); } @@ -519,7 +505,7 @@ int main(int argc, char *argv[]) /* --- Group style options --- */ - { "group", gFlag_argReq, 0, 'g' }, + { "group", OPTF_ARGREQ, 0, 'g' }, #ifdef HAVE_SETGROUPS { "keep-groups", 0, 0, 'k' }, { "merge-groups", 0, 0, 'm' }, @@ -528,20 +514,23 @@ int main(int argc, char *argv[]) /* --- Command to run options --- */ - { "command", gFlag_argReq, 0, 'c' }, + { "command", OPTF_ARGREQ, 0, 'c' }, /* --- Server options --- */ +#ifndef NONETWORK { "daemon", 0, 0, 'd' }, - { "port", gFlag_argReq, 0, 'p' }, - { "config-file", gFlag_argReq, 0, 'f' }, + { "nofork", 0, 0, 'n' }, + { "port", OPTF_ARGREQ, 0, 'p' }, + { "config-file", OPTF_ARGREQ, 0, 'f' }, +#endif /* --- Tracing options --- */ -#ifdef TRACING - { "impersonate", gFlag_argReq, 0, 'I' }, - { "trace", gFlag_argOpt, 0, 'T' }, - { "trace-level", gFlag_argOpt, 0, 'L' }, +#ifndef NTRACE + { "impersonate", OPTF_ARGREQ, 0, 'I' }, + { "trace", OPTF_ARGOPT, 0, 'T' }, + { "trace-level", OPTF_ARGOPT, 0, 'L' }, #endif { 0, 0, 0, 0 } @@ -557,8 +546,10 @@ int main(int argc, char *argv[]) "g:" /* Group (without @setgroups@) */ #endif "c:" /* Command to run options */ - "dp:f:" /* Server options */ -#ifdef TRACING +#ifndef NONETWORK + "dnp:f:" /* Server options */ +#endif +#ifndef NTRACE "I:T::L::" /* Tracing options */ #endif , @@ -603,7 +594,7 @@ int main(int argc, char *argv[]) else { struct group *gr = getgrnam(optarg); if (!gr) - die("unknown group `%s'", optarg); + die(1, "unknown group `%s'", optarg); group = gr->gr_gid; } flags |= f_havegroup; @@ -627,22 +618,27 @@ int main(int argc, char *argv[]) /* --- Server options --- */ +#ifndef NONETWORK case 'p': if (isdigit((unsigned char)optarg[0])) port = htons(atoi(optarg)); else { struct servent *s = getservbyname(optarg, "udp"); if (!s) - die("unknown service name `%s'", optarg); + die(1, "unknown service name `%s'", optarg); port = s->s_port; } break; case 'd': flags |= f_daemon; break; + case 'n': + flags |= f_nofork; + break; case 'f': conffile = optarg; break; +#endif /* --- Pretend to be a different user --- * * @@ -650,7 +646,7 @@ int main(int argc, char *argv[]) * allow it if we're running setuid. Disable the actual login anyway. */ -#ifdef TRACING +#ifndef NTRACE case 'I': if (flags & f_setuid) @@ -662,7 +658,7 @@ int main(int argc, char *argv[]) else pw = getpwnam(optarg); if (!pw) - die("can't impersonate unknown user `%s'", optarg); + die(1, "can't impersonate unknown user `%s'", optarg); from_pw = userdb_copyUser(pw); rq.from = from_pw->pw_uid; flags |= f_dummy; @@ -677,7 +673,7 @@ int main(int argc, char *argv[]) * to! */ -#ifdef TRACING +#ifndef TRACE case 'T': { FILE *fp; @@ -693,12 +689,12 @@ int main(int argc, char *argv[]) if (seteuid(ru)) #endif { - die("couldn't temporarily give up privileges: %s", + die(1, "couldn't temporarily give up privileges: %s", strerror(errno)); } if ((fp = fopen(optarg, "w")) == 0) { - die("couldn't open trace file `%s' for writing: %s", + die(1, "couldn't open trace file `%s' for writing: %s", optarg, strerror(errno)); } @@ -707,9 +703,9 @@ int main(int argc, char *argv[]) #else if (seteuid(eu)) #endif - die("couldn't regain privileges: %s", strerror(errno)); + die(1, "couldn't regain privileges: %s", strerror(errno)); } - traceon(fp, TRACE_DFL); + trace_on(fp, TRACE_DFL); trace(TRACE_MISC, "become: tracing enabled"); } break; @@ -717,80 +713,33 @@ int main(int argc, char *argv[]) /* --- Setting trace levels --- */ -#ifdef TRACING +#ifndef NTRACE case 'L': { - int sense = 1; - unsigned int lvl = 0, l; - const char *p = optarg; /* --- Table of tracing facilities --- */ - typedef struct tr { - char ch; - unsigned int l; - const char *help; - } tr; - - static tr lvltbl[] = { + static trace_opt lvltbl[] = { { 'm', TRACE_MISC, "miscellaneous messages" }, { 's', TRACE_SETUP, "building the request block" }, - { 'd', TRACE_DAEMON, "server process" }, { 'r', TRACE_RULE, "ruleset scanning" }, { 'c', TRACE_CHECK, "request checking" }, +#ifndef NONETWORK + { 'd', TRACE_DAEMON, "server process" }, { 'l', TRACE_CLIENT, "client process" }, { 'R', TRACE_RAND, "random number generator" }, { 'C', TRACE_CRYPTO, "cryptographic processing of requests" }, +#endif { 'y', TRACE_YACC, "parsing configuration file" }, { 'D', TRACE_DFL, "default tracing options" }, { 'A', TRACE_ALL, "all tracing options" }, { 0, 0, 0 } }; - tr *tp; /* --- Output some help if there's no arguemnt --- */ - if (!optarg) { - bc__banner(stdout); - bc__write(stdout, - "\n" - "Tracing options:\n" - "\n"); - for (tp = lvltbl; tp->l; tp++) { - if ((flags & f_setuid) == 0 || tp->l & ~TRACE_PRIV) - printf("%c -- %s\n", tp->ch, tp->help); - } - bc__write(stdout, -"\n" -"Also, `+' and `-' options are recognised to turn on and off various\n" -"tracing options. For example, `A-r' enables everything except ruleset\n" -"tracing, and `A-D+c' is everything except the defaults, but with request\n" -"check tracing.\n" -); - exit(0); - } - - while (*p) { - if (*p == '+') - sense = 1; - else if (*p == '-') - sense = 0; - else { - for (tp = lvltbl; tp->l && *p != tp->ch; tp++) - ; - l = tp->l; - if (flags & f_setuid) - l &= ~TRACE_PRIV; - if (l) - lvl = sense ? (lvl | l) : (lvl & ~l); - else - moan("unknown trace option `%c'", *p); - } - p++; - } - - tracesetlvl(lvl); - yydebug = ((lvl & TRACE_YACC) != 0); + trace_level(traceopt(lvltbl, optarg, TRACE_DFL, + (flags & f_setuid) ? TRACE_PRIV : 0)); } break; #endif @@ -858,11 +807,13 @@ done_options: /* --- Switch to daemon mode if requested --- */ +#ifndef NONETWORK if (flags & f_daemon) { T( trace(TRACE_MISC, "become: daemon mode requested"); ) - daemon_init(conffile, port); + daemon_init(conffile, port, (flags & f_nofork) ? df_nofork : 0); exit(0); } +#endif /* --- Open a syslog --- */ @@ -883,7 +834,7 @@ done_options: else pw = getpwnam(who); if (!pw) - die("unknown user `%s'", who); + die(1, "unknown user `%s'", who); to_pw = userdb_copyUser(pw); rq.to = pw->pw_uid; } @@ -896,7 +847,7 @@ done_options: rq.from = getuid(); pw = getpwuid(rq.from); if (!pw) - die("who are you? (can't find user %li)", (long)rq.from); + die(1, "who are you? (can't find user %li)", (long)rq.from); from_pw = userdb_copyUser(pw); } @@ -907,8 +858,8 @@ done_options: struct hostent *he; uname(&u); if ((he = gethostbyname(u.nodename)) == 0) - die("who am I? (can't resolve `%s')", u.nodename); - memcpy(&rq.host, he->h_addr, sizeof(struct in_addr)); + die(1, "who am I? (can't resolve `%s')", u.nodename); + memcpy(&rq.host, he->h_addr, sizeof(rq.host)); } /* --- Fiddle with group ownerships a bit --- */ @@ -929,7 +880,7 @@ done_options: /* --- Check that it's valid --- */ if (group != from_pw->pw_gid && group != to_pw->pw_gid) - die("invalid default group"); + die(1, "invalid default group"); #else @@ -988,7 +939,7 @@ done_options: if (group == to_gr[i]) goto group_ok; } - die("invalid default group"); + die(1, "invalid default group"); group_ok:; } @@ -1015,7 +966,7 @@ done_options: ngroups = 0; (void)(bc__addGroups(groups, &ngroups, ga, i) || ((gstyle & g_keep) && - bc__addGroups(groups, &ngroups, from_gr,n_fgr)) || + bc__addGroups(groups, &ngroups, from_gr, n_fgr)) || ((gstyle & g_replace) && bc__addGroups(groups, &ngroups, to_gr, n_tgr))); } @@ -1128,7 +1079,7 @@ done_options: */ static char *preserve[] = { - "TERM", "DISPLAY", 0 + "TERM", "DISPLAY", "TZ", 0 }; /* --- Variables to be expunged --- * @@ -1261,7 +1212,7 @@ done_options: sz = 0; - for (sym_createIter(&i, &bc__env); (e = sym_next(&i)) != 0; ) { + for (sym_mkiter(&i, &bc__env); (e = sym_next(&i)) != 0; ) { /* --- Login style expunges all unpreserved variables --- */ @@ -1273,7 +1224,7 @@ done_options: for (pp = banned; *pp; pp++) { if (**pp == '-') { p = *pp + 1; - if (memcmp(e->_base.name, p, strlen(p)) == 0) + if (strncmp(e->_base.name, p, strlen(p)) == 0) goto expunge; } else if (strcmp(e->_base.name, *pp) == 0) goto expunge; @@ -1290,7 +1241,7 @@ done_options: env = qq = xmalloc((sz + 1) * sizeof(*qq)); - for (sym_createIter(&i, &bc__env); (e = sym_next(&i)) != 0; ) + for (sym_mkiter(&i, &bc__env); (e = sym_next(&i)) != 0; ) *qq++ = e->val; *qq++ = 0; } @@ -1326,9 +1277,21 @@ done_options: if (strlen(p) + strlen(binary) + 2 > sizeof(rq.cmd)) continue; - /* --- Now build the pathname and check it --- */ + /* --- Now build the pathname and check it --- * + * + * Issue: user can take advantage of these privileges to decide whether + * a program with a given name exists. I'm not sure that's + * particularly significant: it only works on regular files with + * execute permissions, and if you're relying on the names of these + * being secret to keep your security up, then you're doing something + * deeply wrong anyway. On the other hand, it's useful to allow people + * to be able to execute programs and scripts which they wouldn't + * otherwise have access to. [This problem was brought up on + * Bugtraq, as a complaint against sudo.] + */ - sprintf(rq.cmd, "%s/%s", p, todo[0]); + if (!*p) p = "."; + sprintf(rq.cmd, "%s/%s", p, binary); if (stat(rq.cmd, &st) == 0 && /* Check it exists */ st.st_mode & 0111 && /* Check it's executable */ S_ISREG(st.st_mode)) /* Check it's a file */ @@ -1336,7 +1299,7 @@ done_options: } if (!p) - die("couldn't find `%s' in path", todo[0]); + die(1, "couldn't find `%s' in path", todo[0]); binary = rq.cmd; free(path); } @@ -1355,7 +1318,7 @@ done_options: q = binary; if (*q != '/') { if (!getcwd(b, sizeof(b))) - die("couldn't read current directory: %s", strerror(errno)); + die(1, "couldn't read current directory: %s", strerror(errno)); p += strlen(p); *p++ = '/'; } @@ -1371,7 +1334,7 @@ done_options: */ if (p >= b + sizeof(b) - 1) - die("internal error: buffer overflow while canonifying path"); + die(1, "internal error: buffer overflow while canonifying path"); /* --- Reduce multiple slashes to just one --- */ @@ -1443,7 +1406,7 @@ done_options: rq.cmd); if (!a) - die("permission denied"); + die(1, "permission denied"); } /* --- Now do the job --- */ @@ -1457,17 +1420,19 @@ done_options: #ifdef HAVE_SETGROUPS if (setgroups(ngroups, groups) < 0) - die("couldn't set groups: %s", strerror(errno)); + die(1, "couldn't set groups: %s", strerror(errno)); #endif if (setgid(group) < 0) - die("couldn't set default group: %s", strerror(errno)); + die(1, "couldn't set default group: %s", strerror(errno)); if (setuid(rq.to) < 0) - die("couldn't set uid: %s", strerror(errno)); + die(1, "couldn't set uid: %s", strerror(errno)); /* --- If this was a login, change current directory --- */ - if (flags & f_shell && style == l_login && chdir(to_pw->pw_dir) < 0) { + if ((flags & f_shell) && + style == l_login && + chdir(to_pw->pw_dir) < 0) { moan("couldn't change directory to `%s': %s", to_pw->pw_dir, strerror(errno)); } @@ -1475,8 +1440,9 @@ done_options: /* --- Finally, call the program --- */ fflush(0); + closelog(); execve(rq.cmd, todo, env); - die("couldn't exec `%s': %s", rq.cmd, strerror(errno)); + die(1, "couldn't exec `%s': %s", rq.cmd, strerror(errno)); return (127); }