+ binary = todo[0];
+ }
+
+ else switch (style) {
+
+ /* --- An unadorned becoming requires little work --- */
+
+ case l_preserve:
+ shell[0] = getenv("SHELL");
+ if (!shell[0])
+ shell[0] = from_pw->pw_shell;
+ shell[1] = 0;
+ todo = shell;
+ binary = todo[0];
+ break;
+
+ /* --- An su-like login needs slightly less effort --- */
+
+ case l_setuser:
+ shell[0] = to_pw->pw_shell;
+ shell[1] = 0;
+ todo = shell;
+ binary = todo[0];
+ break;
+
+ /* --- A login request needs a little bit of work --- */
+
+ case l_login: {
+ const char *p = strrchr(to_pw->pw_shell, '/');
+
+ if (p)
+ p++;
+ else
+ p = to_pw->pw_shell;
+ shell[0] = xmalloc(strlen(p) + 2);
+ shell[0][0] = '-';
+ strcpy(shell[0] + 1, p);
+ shell[1] = 0;
+ todo = shell;
+ binary = to_pw->pw_shell;
+ chdir(to_pw->pw_dir);
+ } break;
+ }
+
+ /* --- Mangle the environment --- *
+ *
+ * This keeps getting more complicated all the time. (How true. Now I've
+ * got all sorts of nasty environment mangling to do.)
+ *
+ * The environment stuff now happens in seven phases:
+ *
+ * 1. Mark very special variables to be preserved. Currently only TERM
+ * and DISPLAY are treated in this way.
+ *
+ * 2. Set and preserve Become's own environment variables.
+ *
+ * 3. Set and preserve the user identity variables (USER, LOGNAME, HOME,
+ * SHELL and MAIL) if we're being `su'-like or `login'-like.
+ *
+ * 4. If we're preserving the environment or being `su'-like, process the
+ * PATH variable a little. Otherwise reset it to something
+ * appropriate.
+ *
+ * 5. If we're being `login'-like, expunge all unpreserved variables.
+ *
+ * 6. Expunge any security-critical variables.
+ *
+ * 7. Build a new environment table to pass to child processes.
+ */
+
+ {
+ /* --- Variables to be preserved always --- *
+ *
+ * A user can explicitly expunge a variable in this list, in which case
+ * we never get to see it here.
+ */
+
+ static char *preserve[] = {
+ "TERM", "DISPLAY", 0
+ };
+
+ /* --- Variables to be expunged --- *
+ *
+ * Any environment string which has one of the following as a prefix will
+ * be expunged from the environment passed to the called process. The
+ * first line lists variables which have been used to list search paths
+ * for shared libraries: by manipulating these, an attacker could replace
+ * a standard library with one of his own. The second line lists other
+ * well-known dangerous environment variables.
+ */
+
+ static char *banned[] = {
+ "-LD_", "SHLIB_PATH", "LIBPATH", "-_RLD_",
+ "IFS", "ENV", "BASH_ENV", "KRB_CONF",
+ 0
+ };
+
+ /* --- Other useful variables --- */
+
+ sym_env *e;
+ char *p, *q, *r;
+ char **pp, **qq;
+ size_t sz;
+ unsigned f;
+ sym_iter i;
+
+ /* --- Stage one. Preserve display-specific variables --- */
+
+ for (pp = preserve; *pp; pp++) {
+ if ((e = sym_find(&bc__env, *pp, -1, 0, 0)) != 0)
+ e->f |= envFlag_preserve;
+ }
+
+ /* --- Stage two. Set Become's own variables --- */
+
+ e = sym_find(&bc__env, "BECOME_ORIGINAL_USER", -1, sizeof(*e), &f);
+ if (!f)
+ bc__setenv(e, from_pw->pw_name);
+ e->f |= envFlag_preserve;
+
+ e = sym_find(&bc__env, "BECOME_ORIGINAL_HOME", -1, sizeof(*e), &f);
+ if (!f)
+ bc__setenv(e, from_pw->pw_dir);
+ e->f |= envFlag_preserve;
+
+ bc__putenv("BECOME_OLD_USER", from_pw->pw_name, envFlag_preserve, 0);
+ bc__putenv("BECOME_OLD_HOME", from_pw->pw_dir, envFlag_preserve, 0);
+ bc__putenv("BECOME_USER", to_pw->pw_name, envFlag_preserve, 0);
+ bc__putenv("BECOME_HOME", to_pw->pw_dir, envFlag_preserve, 0);
+
+ /* --- Stage three. Set user identity --- */
+
+ switch (style) {
+ case l_login: {
+ static char *maildirs[] = {
+ "/var/spool/mail", "/var/mail",
+ "/usr/spool/mail", "/usr/mail",
+ 0
+ };
+ struct stat s;
+ char b[128];
+
+ for (pp = maildirs; *pp; pp++) {
+ if (stat(*pp, &s) == 0 && S_ISDIR(s.st_mode)) {
+ sprintf(b, "%s/%s", *pp, to_pw->pw_name);
+ bc__putenv("MAIL", b, envFlag_preserve, 0);
+ break;
+ }
+ }
+ } /* Fall through */
+
+ 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);
+ bc__putenv("SHELL", to_pw->pw_shell, envFlag_preserve, 0);
+ break;
+ }
+
+ /* --- Stage four. Set the user's PATH properly --- */
+
+ {
+ /* --- Find an existing path --- *
+ *
+ * If there's no path, or this is a login, then set a default path,
+ * unless we're meant to preserve the existing one. Whew!
+ */
+
+ e = sym_find(&bc__env, "PATH", -1, sizeof(*e), &f);
+
+ if (!f || (style == l_login && ~e->f & envFlag_preserve)) {
+ bc__putenv("PATH",
+ rq.to ? "/usr/bin:/bin" : "/usr/bin:/usr/sbin:/bin:/sbin",
+ envFlag_preserve, 0);
+ } else {
+
+ /* --- Find the string --- */
+
+ e->f = envFlag_preserve;
+ p = strchr(e->val, '=') + 1;
+ r = e->val;
+
+ /* --- Write the new version to a dynamically allocated buffer --- */
+
+ e->val = xmalloc(4 + 1 + strlen(p) + 1);
+ strcpy(e->val, "PATH=");
+ q = e->val + 5;
+
+ for (p = strtok(p, ":"); p; p = strtok(0, ":")) {
+ if (p[0] != '/')
+ continue;
+ while (*p)
+ *q++ = *p++;
+ *q++ = ':';
+ }
+ q[-1] = 0;
+
+ /* --- Done! --- */
+
+ free(r);
+ }
+ }
+
+ /* --- Stages five and six. Expunge variables and count numbers --- *
+ *
+ * Folded together, so I only need one pass through the table. Also
+ * count the number of variables needed at this time.
+ */
+
+ sz = 0;
+
+ for (sym_createIter(&i, &bc__env); (e = sym_next(&i)) != 0; ) {
+
+ /* --- Login style expunges all unpreserved variables --- */
+
+ if (style == l_login && ~e->f & envFlag_preserve)
+ goto expunge;
+
+ /* --- Otherwise just check the name against the list --- */
+
+ for (pp = banned; *pp; pp++) {
+ if (**pp == '-') {
+ p = *pp + 1;
+ if (memcmp(e->_base.name, p, strlen(p)) == 0)
+ goto expunge;
+ } else if (strcmp(e->_base.name, *pp) == 0)
+ goto expunge;
+ }
+
+ sz++;
+ continue;
+
+ expunge:
+ sym_remove(&bc__env, e);
+ }
+
+ /* --- Stage seven. Build the new environment block --- */
+
+ env = qq = xmalloc((sz + 1) * sizeof(*qq));
+
+ for (sym_createIter(&i, &bc__env); (e = sym_next(&i)) != 0; )
+ *qq++ = e->val;
+ *qq++ = 0;