From: mdw Date: Wed, 10 Sep 1997 10:28:05 +0000 (+0000) Subject: Allow default port to be given as a service name or port number. Handle X-Git-Tag: 1.3.3~95 X-Git-Url: https://git.distorted.org.uk/~mdw/become/commitdiff_plain/a508927318d30c986067ed83ec8d57fe85d8598d Allow default port to be given as a service name or port number. Handle groups properly (lots of options here). --- diff --git a/src/become.c b/src/become.c index d43bb36..2154e4b 100644 --- a/src/become.c +++ b/src/become.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: become.c,v 1.8 1997/09/08 13:56:24 mdw Exp $ + * $Id: become.c,v 1.9 1997/09/10 10:28:05 mdw Exp $ * * Main code for `become' * @@ -29,7 +29,11 @@ /*----- Revision history --------------------------------------------------* * * $Log: become.c,v $ - * Revision 1.8 1997/09/08 13:56:24 mdw + * 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 `/'. * @@ -66,6 +70,7 @@ #include #include +#include #include #include #include @@ -83,6 +88,7 @@ #include #include +#include #include #include #include @@ -124,10 +130,26 @@ enum { enum { l_preserve, /* Preserve the environment */ - l_user, /* Update who I am */ + l_setuser, /* Update who I am */ l_login /* Do a full login */ }; +/* --- Group behaviour types --- * + * + * Note that these make a handy bitfield. + */ + +#ifdef HAVE_SETGROUPS + +enum { + g_unset = 0, /* Nobody's set a preference */ + g_keep = 1, /* Leave the group memberships */ + g_replace = 2, /* Replace group memberships */ + g_merge = (g_keep | g_replace) /* Merge the group memberships */ +}; + +#endif + /*----- Static variables --------------------------------------------------*/ static sym_table bc__env; @@ -230,7 +252,7 @@ static sym_env *bc__putenv(const char *var, const char *val, if (val) { e = sym_find(&bc__env, var, -1, sizeof(*e), &f); - if (~e->f & envFlag_preserve || force) { + if (!f || ~e->f & envFlag_preserve || force) { if (f) free(e->val); bc__setenv(e, val); @@ -251,6 +273,50 @@ static sym_env *bc__putenv(const char *var, const char *val, return (e); } +/* --- @bc__addGroups@ --- * + * + * Arguments: @gid_t *g@ = pointer to a group array + * @int *png@ = pointer to number of entries in the array + * @const gid_t *a@ = pointer to groups to add + * @int na@ = number of groups to add + * + * Returns: Zero if it was OK, nonzero if we should stop now. + * + * Use: Adds groups to a groups array. + */ + +static int bc__addGroups(gid_t *g, int *png, const gid_t *a, int na) +{ + int i, j; + int ng = *png; + + for (i = 0; i < na; i++) { + + /* --- Ensure this group isn't already in the list --- */ + + for (j = 0; j < ng; j++) { + if (a[i] == g[j]) + goto next_group; + } + + /* --- See if there's room for more --- */ + + if (ng > NGROUPS_MAX) { + moan("too many groups (system limit exceeded -- some have been lost"); + *png = ng; + return (-1); + } + + /* --- Add the group --- */ + + g[ng++] = a[i]; + next_group:; + } + + *png = ng; + return (0); +} + /* --- @bc__banner@ --- * * * Arguments: @FILE *fp@ = stream to write on @@ -321,6 +387,13 @@ static void bc__help(FILE *fp, int suid) "-s, --su, --set-user Set environment variables to reflect USER\n" "-l, --login Really log in as USER\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" +"-m, --merge-groups Merge the lists of groups\n" +"-r, --replace-groups Replace the list of groups\n" +#endif +"\n" "-c CMD, --command=CMD Run the (Bourne) shell command CMD\n" "\n" "-d, --daemon Start a daemon\n" @@ -370,6 +443,13 @@ int main(int argc, char *argv[]) unsigned flags = 0; /* Various useful flags */ int style = DEFAULT_LOGIN_STYLE; /* Login style */ + gid_t group = -1; /* Default group to set */ + int gstyle = g_unset; /* No group style set yet */ + +#ifdef HAVE_SETGROUPS + gid_t groups[NGROUPS_MAX]; /* Set of groups */ + int ngroups; /* Number of groups in the set */ +#endif /* --- Default argument list executes a shell command --- */ @@ -387,7 +467,8 @@ int main(int argc, char *argv[]) f_duff = 2, /* Fault in arguments */ f_login = 4, /* Execute as a login shell */ f_dummy = 8, /* Don't actually do anything */ - f_setuid = 16 /* We're running setuid */ + f_setuid = 16, /* We're running setuid */ + f_havegroup = 32 /* Set a default group */ }; /* --- Set up the program name --- */ @@ -426,6 +507,15 @@ int main(int argc, char *argv[]) { "set-user", 0, 0, 's' }, { "login", 0, 0, 'l' }, + /* --- Group style options --- */ + + { "group", gFlag_argReq, 0, 'g' }, +#ifdef HAVE_SETGROUPS + { "keep-groups", 0, 0, 'k' }, + { "merge-groups", 0, 0, 'm' }, + { "replace-groups", 0, 0, 'r' }, +#endif + /* --- Command to run options --- */ { "command", gFlag_argReq, 0, 'c' }, @@ -448,7 +538,20 @@ int main(int argc, char *argv[]) }; i = mdwopt(argc, argv, - "-" "huv" "esl" "c:" "dp:f:" T("I:T::L::"), + "-" /* Return non-options as options */ + "huv" /* Asking for help */ + "esl" /* Login style options */ +#ifdef HAVE_SETGROUPS + "g:kmr" /* Group style options */ +#else + "g:" /* Group (without @setgroups@) */ +#endif + "c:" /* Command to run options */ + "dp:f:" /* Server options */ +#ifdef TRACING + "I:T::L::" /* Tracing options */ +#endif + , opts, 0, 0, gFlag_envVar); if (i < 0) goto done_options; @@ -476,12 +579,36 @@ int main(int argc, char *argv[]) style = l_preserve; break; case 's': - style = l_user; + style = l_setuser; break; case 'l': style = l_login; break; + /* --- Group style options --- */ + + case 'g': + if (isdigit((unsigned char)optarg[0])) + group = atoi(optarg); + else { + struct group *gr = getgrnam(optarg); + if (!gr) + die("unknown group `%s'", optarg); + group = gr->gr_gid; + } + flags |= f_havegroup; + break; + + case 'k': + gstyle = g_keep; + break; + case 'm': + gstyle = g_merge; + break; + case 'r': + gstyle = g_replace; + break; + /* --- Command to run options --- */ case 'c': @@ -491,7 +618,14 @@ int main(int argc, char *argv[]) /* --- Server options --- */ case 'p': - port = atoi(optarg); + 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); + port = s->s_port; + } break; case 'd': flags |= f_daemon; @@ -766,6 +900,131 @@ done_options: memcpy(&rq.host, he->h_addr, sizeof(struct in_addr)); } + /* --- Fiddle with group ownerships a bit --- */ + + { +#ifdef HAVE_SETGROUPS + gid_t from_gr[NGROUPS_MAX], to_gr[NGROUPS_MAX]; + int n_fgr, n_tgr; +#endif + + /* --- Set the default login group, if there is one --- */ + + if (~flags & f_havegroup) + group = (style == l_preserve) ? from_pw->pw_gid : to_pw->pw_gid; + +#ifndef HAVE_SETGROUPS + + /* --- Check that it's valid --- */ + + if (group != from_pw->pw_gid && group != to_pw->pw_gid) + die("invalid default group"); + +#else + + /* --- Set the default group style --- */ + + if (gstyle == g_unset) + gstyle = (style == l_login) ? g_replace : g_merge; + + /* --- Read in my current set of groups --- */ + + n_fgr = getgroups(NGROUPS_MAX, from_gr); + + /* --- Now read the groups for the target user --- * + * + * Do this even if I'm using the @g_keep@ style -- I'll use it for + * checking that @group@ is valid. + */ + + { + struct group *gr; + char **p; + + n_tgr = 0; + to_gr[n_tgr++] = to_pw->pw_gid; + + setgrent(); + while ((gr = getgrent()) != 0) { + if (gr->gr_gid == to_gr[0]) + continue; + for (p = gr->gr_mem; *p; p++) { + if (strcmp(to_pw->pw_name, *p) == 0) { + to_gr[n_tgr++] = gr->gr_gid; + if (n_tgr >= NGROUPS_MAX) + goto done_groups; + break; + } + } + } + + done_groups: + endgrent(); + } + + /* --- Check that @group@ is reasonable --- */ + + { + int i; + + if (group == getgid() || group == from_pw->pw_gid) + goto group_ok; + for (i = 0; i < n_fgr; i++) { + if (group == from_gr[i]) + goto group_ok; + } + for (i = 0; i < n_tgr; i++) { + if (group == to_gr[i]) + goto group_ok; + } + die("invalid default group"); + group_ok:; + } + + /* --- Phew. Now comes the hard bit --- */ + + { + gid_t ga[4]; + int i; + + i = 0; + ga[i++] = group; + if (gstyle & g_keep) { + ga[i++] = getgid(); + ga[i++] = from_pw->pw_gid; + } + if (gstyle & g_replace) + ga[i++] = to_pw->pw_gid; + + /* --- Style authorities will become apoplectic if shown this --- * + * + * As far as I can see, it's the neatest way of writing it. + */ + + ngroups = 0; + (void)(bc__addGroups(groups, &ngroups, ga, i) || + ((gstyle & g_keep) && + bc__addGroups(groups, &ngroups, from_gr,n_fgr)) || + ((gstyle & g_replace) && + bc__addGroups(groups, &ngroups, to_gr, n_tgr))); + } + +#endif + + /* --- Trace the results of all this --- */ + + T( trace(TRACE_SETUP, "setup: default group == %i", (int)group); ) + +#ifdef HAVE_SETGROUPS + IF_TRACING(TRACE_SETUP, { + int i; + + for (i = 1; i < ngroups; i++) + trace(TRACE_SETUP, "setup: subsidiary group %i", (int)groups[i]); + }) +#endif + } + /* --- Shell commands are easy --- */ if (cmd) { @@ -795,7 +1054,7 @@ done_options: /* --- An su-like login needs slightly less effort --- */ - case l_user: + case l_setuser: shell[0] = to_pw->pw_shell; shell[1] = 0; todo = shell; @@ -928,7 +1187,7 @@ done_options: } } /* Fall through */ - case l_user: + case l_setuser: bc__putenv("USER", to_pw->pw_name, envFlag_preserve, 0); bc__putenv("LOGNAME", to_pw->pw_name, envFlag_preserve, 0); bc__putenv("HOME", to_pw->pw_dir, envFlag_preserve, 0); @@ -1002,7 +1261,7 @@ done_options: p = *pp + 1; if (memcmp(e->_base.name, p, strlen(p)) == 0) goto expunge; - } else if (strcmp(e->_base.name, p) == 0) + } else if (strcmp(e->_base.name, *pp) == 0) goto expunge; } @@ -1149,7 +1408,10 @@ done_options: * * If the user is already what she wants to be, then print a warning. * Then, if I was just going to spawn a shell, quit, to reduce user - * confusion. Otherwise, do what was wanted anyway. + * confusion. Otherwise, do what was wanted anyway. Also, don't bother + * checking if we're already root -- root can do anything anyway, and at + * least this way we get some logging done, and offer a more friendly + * front-end. */ if (rq.from == rq.to) { @@ -1159,7 +1421,7 @@ done_options: exit(0); } } else { - int a = check(&rq); + int a = (rq.from == 0) || check(&rq); syslog(LOG_INFO, "permission %s for %s to become %s to run `%s'", @@ -1177,14 +1439,24 @@ done_options: if (flags & f_dummy) { puts("permission granted"); return (0); - } else { - if (setuid(rq.to) == -1) - die("couldn't set uid: %s", strerror(errno)); - fflush(0); - execve(rq.cmd, todo, env); - die("couldn't exec `%s': %s", rq.cmd, strerror(errno)); - return (127); } + +#ifdef HAVE_SETGROUPS + if (setgroups(ngroups, groups) < 0) + die("couldn't set groups: %s", strerror(errno)); +#endif + + if (setgid(group) < 0) + die("couldn't set default group: %s", strerror(errno)); + if (setuid(rq.to) < 0) + die("couldn't set uid: %s", strerror(errno)); + + /* --- Finally, call the program --- */ + + fflush(0); + execve(rq.cmd, todo, env); + die("couldn't exec `%s': %s", rq.cmd, strerror(errno)); + return (127); } /*----- That's all, folks -------------------------------------------------*/