[BACK]Return to env.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sudo

Annotation of src/usr.bin/sudo/env.c, Revision 1.10

1.1       millert     1: /*
1.9       millert     2:  * Copyright (c) 2000-2004 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       millert     3:  *
1.9       millert     4:  * Permission to use, copy, modify, and distribute this software for any
                      5:  * purpose with or without fee is hereby granted, provided that the above
                      6:  * copyright notice and this permission notice appear in all copies.
1.1       millert     7:  *
1.9       millert     8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                      9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.7       millert    15:  *
                     16:  * Sponsored in part by the Defense Advanced Research Projects
                     17:  * Agency (DARPA) and Air Force Research Laboratory, Air Force
                     18:  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
1.1       millert    19:  */
                     20:
                     21: #include "config.h"
                     22:
                     23: #include <sys/types.h>
                     24: #include <sys/param.h>
                     25: #include <sys/stat.h>
                     26: #include <stdio.h>
                     27: #ifdef STDC_HEADERS
                     28: # include <stdlib.h>
                     29: # include <stddef.h>
                     30: #else
                     31: # ifdef HAVE_STDLIB_H
                     32: #  include <stdlib.h>
                     33: # endif
                     34: #endif /* STDC_HEADERS */
                     35: #ifdef HAVE_STRING_H
                     36: # include <string.h>
                     37: #else
                     38: # ifdef HAVE_STRINGS_H
                     39: #  include <strings.h>
                     40: # endif
                     41: #endif /* HAVE_STRING_H */
                     42: #ifdef HAVE_UNISTD_H
                     43: # include <unistd.h>
                     44: #endif /* HAVE_UNISTD_H */
1.6       millert    45: #ifdef HAVE_ERR_H
                     46: # include <err.h>
                     47: #else
                     48: # include "emul/err.h"
                     49: #endif /* HAVE_ERR_H */
1.1       millert    50: #include <pwd.h>
                     51:
                     52: #include "sudo.h"
                     53:
                     54: #ifndef lint
1.9       millert    55: static const char rcsid[] = "$Sudo: env.c,v 1.42 2004/09/08 15:57:49 millert Exp $";
1.1       millert    56: #endif /* lint */
                     57:
                     58: /*
1.5       millert    59:  * Flags used in rebuild_env()
1.1       millert    60:  */
                     61: #undef DID_TERM
                     62: #define DID_TERM       0x01
                     63: #undef DID_PATH
                     64: #define DID_PATH       0x02
                     65: #undef DID_HOME
                     66: #define DID_HOME       0x04
                     67: #undef DID_SHELL
                     68: #define DID_SHELL      0x08
                     69: #undef DID_LOGNAME
                     70: #define DID_LOGNAME    0x10
1.9       millert    71: #undef DID_USER
1.1       millert    72: #define DID_USER       0x12
                     73:
1.9       millert    74: #undef VNULL
                     75: #define        VNULL   (VOID *)NULL
                     76:
1.1       millert    77: /*
                     78:  * Prototypes
                     79:  */
1.9       millert    80: char **rebuild_env             __P((char **, int, int));
1.1       millert    81: char **zero_env                        __P((char **));
1.5       millert    82: static void insert_env         __P((char *, int));
1.9       millert    83: static char *format_env                __P((char *, ...));
1.1       millert    84:
                     85: /*
                     86:  * Default table of "bad" variables to remove from the environment.
                     87:  * XXX - how to omit TERMCAP if it starts with '/'?
                     88:  */
1.5       millert    89: static const char *initial_badenv_table[] = {
1.1       millert    90:     "IFS",
                     91:     "LOCALDOMAIN",
                     92:     "RES_OPTIONS",
                     93:     "HOSTALIASES",
                     94:     "NLSPATH",
                     95:     "PATH_LOCALE",
                     96:     "LD_*",
                     97:     "_RLD*",
                     98: #ifdef __hpux
                     99:     "SHLIB_PATH",
                    100: #endif /* __hpux */
                    101: #ifdef _AIX
                    102:     "LIBPATH",
                    103: #endif /* _AIX */
1.5       millert   104: #ifdef __APPLE__
                    105:     "DYLD_*",
                    106: #endif
1.1       millert   107: #ifdef HAVE_KERB4
                    108:     "KRB_CONF*",
1.6       millert   109:     "KRBCONFDIR",
1.1       millert   110:     "KRBTKFILE",
                    111: #endif /* HAVE_KERB4 */
                    112: #ifdef HAVE_KERB5
                    113:     "KRB5_CONFIG*",
                    114: #endif /* HAVE_KERB5 */
                    115: #ifdef HAVE_SECURID
                    116:     "VAR_ACE",
                    117:     "USR_ACE",
                    118:     "DLC_ACE",
                    119: #endif /* HAVE_SECURID */
                    120:     "TERMINFO",
                    121:     "TERMINFO_DIRS",
                    122:     "TERMPATH",
                    123:     "TERMCAP",                 /* XXX - only if it starts with '/' */
                    124:     "ENV",
                    125:     "BASH_ENV",
                    126:     NULL
                    127: };
                    128:
                    129: /*
                    130:  * Default table of variables to check for '%' and '/' characters.
                    131:  */
1.5       millert   132: static const char *initial_checkenv_table[] = {
1.1       millert   133:     "LC_*",
                    134:     "LANG",
                    135:     "LANGUAGE",
                    136:     NULL
                    137: };
                    138:
1.5       millert   139: static char **new_environ;     /* Modified copy of the environment */
                    140: static size_t env_size;                /* size of new_environ in char **'s */
                    141: static size_t env_len;         /* number of slots used, not counting NULL */
                    142:
1.1       millert   143: /*
                    144:  * Zero out environment and replace with a minimal set of
                    145:  * USER, LOGNAME, HOME, TZ, PATH (XXX - should just set path to default)
                    146:  * May set user_path, user_shell, and/or user_prompt as side effects.
                    147:  */
                    148: char **
                    149: zero_env(envp)
                    150:     char **envp;
                    151: {
1.9       millert   152:     static char *newenv[8];
                    153:     char **ep, **nep = newenv;
                    154:     extern char *prev_user;
1.1       millert   155:
                    156:     for (ep = envp; *ep; ep++) {
                    157:        switch (**ep) {
                    158:            case 'H':
                    159:                if (strncmp("HOME=", *ep, 5) == 0)
                    160:                    break;
1.2       millert   161:                continue;
1.1       millert   162:            case 'L':
                    163:                if (strncmp("LOGNAME=", *ep, 8) == 0)
                    164:                    break;
1.2       millert   165:                continue;
1.1       millert   166:            case 'P':
                    167:                if (strncmp("PATH=", *ep, 5) == 0) {
                    168:                    user_path = *ep + 5;
                    169:                    /* XXX - set to sane default instead of user's? */
                    170:                    break;
                    171:                }
1.2       millert   172:                continue;
1.1       millert   173:            case 'S':
1.2       millert   174:                if (strncmp("SHELL=", *ep, 6) == 0)
1.1       millert   175:                    user_shell = *ep + 6;
1.9       millert   176:                else if (!user_prompt && strncmp("SUDO_PROMPT=", *ep, 12) == 0)
1.1       millert   177:                    user_prompt = *ep + 12;
1.9       millert   178:                else if (strncmp("SUDO_USER=", *ep, 10) == 0)
                    179:                    prev_user = *ep + 10;
1.2       millert   180:                continue;
1.1       millert   181:            case 'T':
                    182:                if (strncmp("TZ=", *ep, 3) == 0)
                    183:                    break;
1.2       millert   184:                continue;
1.1       millert   185:            case 'U':
                    186:                if (strncmp("USER=", *ep, 5) == 0)
                    187:                    break;
1.2       millert   188:                continue;
1.1       millert   189:            default:
                    190:                continue;
                    191:        }
                    192:
                    193:        /* Deal with multiply defined variables (take first instance) */
                    194:        for (nep = newenv; *nep; nep++) {
                    195:            if (**nep == **ep)
                    196:                break;
                    197:        }
                    198:        if (*nep == NULL)
                    199:            *nep++ = *ep;
                    200:     }
1.9       millert   201:
                    202: #ifdef HAVE_LDAP
                    203:     /*
                    204:      * Prevent OpenLDAP from reading any user dotfiles
                    205:      * or files in the current directory.
                    206:      *
                    207:      */
                    208:     *nep++ = "LDAPNOINIT=1";
                    209: #endif
                    210:
1.1       millert   211:     return(&newenv[0]);
                    212: }
                    213:
                    214: /*
                    215:  * Given a variable and value, allocate and format an environment string.
                    216:  */
                    217: static char *
1.9       millert   218: #ifdef __STDC__
                    219: format_env(char *var, ...)
                    220: #else
                    221: format_env(var, va_alist)
1.1       millert   222:     char *var;
1.9       millert   223:     va_dcl
                    224: #endif
1.1       millert   225: {
1.5       millert   226:     char *estring;
1.9       millert   227:     char *val;
1.5       millert   228:     size_t esize;
1.9       millert   229:     va_list ap;
1.1       millert   230:
1.9       millert   231: #ifdef __STDC__
                    232:     va_start(ap, var);
                    233: #else
                    234:     va_start(ap);
                    235: #endif
                    236:     esize = strlen(var) + 2;
                    237:     while ((val = va_arg(ap, char *)) != NULL)
                    238:        esize += strlen(val);
                    239:     va_end(ap);
1.5       millert   240:     estring = (char *) emalloc(esize);
                    241:
1.9       millert   242:     /* Store variable name and the '=' separator.  */
1.5       millert   243:     if (strlcpy(estring, var, esize) >= esize ||
1.9       millert   244:        strlcat(estring, "=", esize) >= esize) {
1.6       millert   245:
                    246:        errx(1, "internal error, format_env() overflow");
1.5       millert   247:     }
1.1       millert   248:
1.9       millert   249:     /* Now store the variable's value (if any) */
                    250: #ifdef __STDC__
                    251:     va_start(ap, var);
                    252: #else
                    253:     va_start(ap);
                    254: #endif
                    255:     while ((val = va_arg(ap, char *)) != NULL) {
                    256:        if (strlcat(estring, val, esize) >= esize)
                    257:            errx(1, "internal error, format_env() overflow");
                    258:     }
                    259:     va_end(ap);
                    260:
1.1       millert   261:     return(estring);
                    262: }
                    263:
                    264: /*
1.5       millert   265:  * Insert str into new_environ, assumes str has an '=' in it.
                    266:  * NOTE: no other routines may modify new_environ, env_size, or env_len.
1.1       millert   267:  */
                    268: static void
1.5       millert   269: insert_env(str, dupcheck)
1.1       millert   270:     char *str;
1.5       millert   271:     int dupcheck;
1.1       millert   272: {
1.5       millert   273:     char **nep;
1.1       millert   274:     size_t varlen;
                    275:
1.8       millert   276:     /* Make sure there is room for the new entry plus a NULL. */
                    277:     if (env_len + 2 > env_size) {
1.5       millert   278:        env_size += 128;
                    279:        new_environ = erealloc3(new_environ, env_size, sizeof(char *));
                    280:     }
                    281:
                    282:     if (dupcheck) {
                    283:            varlen = (strchr(str, '=') - str) + 1;
1.1       millert   284:
1.5       millert   285:            for (nep = new_environ; *nep; nep++) {
                    286:                if (strncmp(str, *nep, varlen) == 0) {
                    287:                    *nep = str;
                    288:                    return;
                    289:                }
                    290:            }
                    291:     } else
                    292:        nep = &new_environ[env_len];
                    293:
                    294:     env_len++;
                    295:     *nep++ = str;
                    296:     *nep = NULL;
1.1       millert   297: }
                    298:
                    299: /*
                    300:  * Build a new environment and ether clear potentially dangerous
                    301:  * variables from the old one or start with a clean slate.
                    302:  * Also adds sudo-specific variables (SUDO_*).
                    303:  */
                    304: char **
1.9       millert   305: rebuild_env(envp, sudo_mode, noexec)
                    306:     char **envp;
1.1       millert   307:     int sudo_mode;
1.9       millert   308:     int noexec;
1.1       millert   309: {
1.5       millert   310:     char **ep, *cp, *ps1;
1.1       millert   311:     int okvar, iswild, didvar;
1.5       millert   312:     size_t len;
1.1       millert   313:     struct list_member *cur;
                    314:
                    315:     /*
                    316:      * Either clean out the environment or reset to a safe default.
                    317:      */
                    318:     ps1 = NULL;
                    319:     didvar = 0;
1.9       millert   320:     if (def_env_reset) {
1.1       millert   321:        int keepit;
                    322:
                    323:        /* Pull in vars we want to keep from the old environment. */
                    324:        for (ep = envp; *ep; ep++) {
                    325:            keepit = 0;
1.10    ! millert   326:
        !           327:            /* Skip variables with values beginning with () (bash functions) */
        !           328:            if ((cp = strchr(*ep, '=')) != NULL) {
        !           329:                if (strncmp(cp, "=() ", 3) == 0)
        !           330:                    continue;
        !           331:            }
        !           332:
1.9       millert   333:            for (cur = def_env_keep; cur; cur = cur->next) {
1.1       millert   334:                len = strlen(cur->value);
                    335:                /* Deal with '*' wildcard */
                    336:                if (cur->value[len - 1] == '*') {
                    337:                    len--;
                    338:                    iswild = 1;
                    339:                } else
                    340:                    iswild = 0;
                    341:                if (strncmp(cur->value, *ep, len) == 0 &&
                    342:                    (iswild || (*ep)[len] == '=')) {
                    343:                    /* We always preserve TERM, no special treatment needed. */
                    344:                    if (strncmp(*ep, "TERM=", 5) != 0)
                    345:                        keepit = 1;
                    346:                    break;
                    347:                }
                    348:            }
                    349:
                    350:            /* For SUDO_PS1 -> PS1 conversion. */
                    351:            if (strncmp(*ep, "SUDO_PS1=", 8) == 0)
                    352:                ps1 = *ep + 5;
                    353:
                    354:            if (keepit) {
                    355:                /* Preserve variable. */
                    356:                switch (**ep) {
                    357:                    case 'H':
                    358:                        if (strncmp(*ep, "HOME=", 5) == 0)
1.9       millert   359:                            SET(didvar, DID_HOME);
1.5       millert   360:                        break;
1.1       millert   361:                    case 'S':
                    362:                        if (strncmp(*ep, "SHELL=", 6) == 0)
1.9       millert   363:                            SET(didvar, DID_SHELL);
1.5       millert   364:                        break;
1.1       millert   365:                    case 'L':
                    366:                        if (strncmp(*ep, "LOGNAME=", 8) == 0)
1.9       millert   367:                            SET(didvar, DID_LOGNAME);
1.5       millert   368:                        break;
1.1       millert   369:                    case 'U':
                    370:                        if (strncmp(*ep, "USER=", 5) == 0)
1.9       millert   371:                            SET(didvar, DID_USER);
1.5       millert   372:                        break;
1.1       millert   373:                }
1.5       millert   374:                insert_env(*ep, 0);
1.1       millert   375:            } else {
                    376:                /* Preserve TERM and PATH, ignore anything else. */
1.9       millert   377:                if (!ISSET(didvar, DID_TERM) && strncmp(*ep, "TERM=", 5) == 0) {
1.5       millert   378:                    insert_env(*ep, 0);
1.9       millert   379:                    SET(didvar, DID_TERM);
                    380:                } else if (!ISSET(didvar, DID_PATH) && strncmp(*ep, "PATH=", 5) == 0) {
1.5       millert   381:                    insert_env(*ep, 0);
1.9       millert   382:                    SET(didvar, DID_PATH);
1.1       millert   383:                }
                    384:            }
                    385:        }
                    386:
                    387:        /*
1.9       millert   388:         * Add in defaults.  In -i mode these come from the runas user,
                    389:         * otherwise they may be from the user's environment (depends
                    390:         * on sudoers options).
1.1       millert   391:         */
1.9       millert   392:        if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
                    393:            insert_env(format_env("HOME", runas_pw->pw_dir, VNULL), 0);
                    394:            insert_env(format_env("SHELL", runas_pw->pw_shell, VNULL), 0);
                    395:            insert_env(format_env("LOGNAME", runas_pw->pw_name, VNULL), 0);
                    396:            insert_env(format_env("USER", runas_pw->pw_name, VNULL), 0);
                    397:        } else {
                    398:            if (!ISSET(didvar, DID_HOME))
                    399:                insert_env(format_env("HOME", user_dir, VNULL), 0);
                    400:            if (!ISSET(didvar, DID_SHELL))
                    401:                insert_env(format_env("SHELL", sudo_user.pw->pw_shell, VNULL), 0);
                    402:            if (!ISSET(didvar, DID_LOGNAME))
                    403:                insert_env(format_env("LOGNAME", user_name, VNULL), 0);
                    404:            if (!ISSET(didvar, DID_USER))
                    405:                insert_env(format_env("USER", user_name, VNULL), 0);
                    406:        }
1.1       millert   407:     } else {
                    408:        /*
                    409:         * Copy envp entries as long as they don't match env_delete or
                    410:         * env_check.
                    411:         */
                    412:        for (ep = envp; *ep; ep++) {
                    413:            okvar = 1;
1.10    ! millert   414:
        !           415:            /* Skip variables with values beginning with () (bash functions) */
        !           416:            if ((cp = strchr(*ep, '=')) != NULL) {
        !           417:                if (strncmp(cp, "=() ", 3) == 0)
        !           418:                    continue;
        !           419:            }
1.1       millert   420:
                    421:            /* Skip anything listed in env_delete. */
1.9       millert   422:            for (cur = def_env_delete; cur && okvar; cur = cur->next) {
1.1       millert   423:                len = strlen(cur->value);
                    424:                /* Deal with '*' wildcard */
                    425:                if (cur->value[len - 1] == '*') {
                    426:                    len--;
                    427:                    iswild = 1;
                    428:                } else
                    429:                    iswild = 0;
                    430:                if (strncmp(cur->value, *ep, len) == 0 &&
                    431:                    (iswild || (*ep)[len] == '=')) {
                    432:                    okvar = 0;
                    433:                }
                    434:            }
                    435:
                    436:            /* Check certain variables for '%' and '/' characters. */
1.9       millert   437:            for (cur = def_env_check; cur && okvar; cur = cur->next) {
1.1       millert   438:                len = strlen(cur->value);
                    439:                /* Deal with '*' wildcard */
                    440:                if (cur->value[len - 1] == '*') {
                    441:                    len--;
                    442:                    iswild = 1;
                    443:                } else
                    444:                    iswild = 0;
                    445:                if (strncmp(cur->value, *ep, len) == 0 &&
                    446:                    (iswild || (*ep)[len] == '=') &&
                    447:                    strpbrk(*ep, "/%")) {
                    448:                    okvar = 0;
                    449:                }
                    450:            }
                    451:
                    452:            if (okvar) {
                    453:                if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
                    454:                    ps1 = *ep + 5;
                    455:                else if (strncmp(*ep, "PATH=", 5) == 0)
1.9       millert   456:                    SET(didvar, DID_PATH);
1.1       millert   457:                else if (strncmp(*ep, "TERM=", 5) == 0)
1.9       millert   458:                    SET(didvar, DID_TERM);
1.5       millert   459:                insert_env(*ep, 0);
1.1       millert   460:            }
                    461:        }
                    462:     }
                    463:     /* Provide default values for $TERM and $PATH if they are not set. */
1.9       millert   464:     if (!ISSET(didvar, DID_TERM))
1.5       millert   465:        insert_env("TERM=unknown", 0);
1.9       millert   466:     if (!ISSET(didvar, DID_PATH))
                    467:        insert_env(format_env("PATH", _PATH_DEFPATH, VNULL), 0);
1.1       millert   468:
                    469: #ifdef SECURE_PATH
                    470:     /* Replace the PATH envariable with a secure one. */
1.9       millert   471:     insert_env(format_env("PATH", SECURE_PATH, VNULL), 1);
1.1       millert   472: #endif
                    473:
                    474:     /* Set $USER and $LOGNAME to target if "set_logname" is true. */
1.9       millert   475:     if (def_set_logname && runas_pw->pw_name) {
                    476:        insert_env(format_env("LOGNAME", runas_pw->pw_name, VNULL), 1);
                    477:        insert_env(format_env("USER", runas_pw->pw_name, VNULL), 1);
1.1       millert   478:     }
                    479:
1.9       millert   480:     /* Set $HOME for `sudo -H'.  Only valid at PERM_FULL_RUNAS. */
                    481:     if (ISSET(sudo_mode, MODE_RESET_HOME) && runas_pw->pw_dir)
                    482:        insert_env(format_env("HOME", runas_pw->pw_dir, VNULL), 1);
                    483:
                    484:     /*
                    485:      * Preload a noexec file?  For a list of LD_PRELOAD-alikes, see
                    486:      * http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html
                    487:      * XXX - should prepend to original value, if any
                    488:      */
                    489:     if (noexec && def_noexec_file != NULL)
                    490: #if defined(__darwin__) || defined(__APPLE__)
                    491:        insert_env(format_env("DYLD_INSERT_LIBRARIES", def_noexec_file, VNULL), 1);
                    492:        insert_env(format_env("DYLD_FORCE_FLAT_NAMESPACE", VNULL), 1);
                    493: #else
                    494: # if defined(__osf__) || defined(__sgi)
                    495:        insert_env(format_env("_RLD_LIST", def_noexec_file, ":DEFAULT", VNULL), 1);
                    496: # else
                    497:        insert_env(format_env("LD_PRELOAD", def_noexec_file, VNULL), 1);
                    498: # endif
                    499: #endif
1.1       millert   500:
                    501:     /* Set PS1 if SUDO_PS1 is set. */
                    502:     if (ps1)
1.5       millert   503:        insert_env(ps1, 1);
1.1       millert   504:
                    505:     /* Add the SUDO_COMMAND envariable (cmnd + args). */
1.9       millert   506:     if (user_args)
                    507:        insert_env(format_env("SUDO_COMMAND", user_cmnd, " ", user_args, VNULL), 1);
                    508:     else
                    509:        insert_env(format_env("SUDO_COMMAND", user_cmnd, VNULL), 1);
1.1       millert   510:
                    511:     /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
1.9       millert   512:     insert_env(format_env("SUDO_USER", user_name, VNULL), 1);
1.5       millert   513:     easprintf(&cp, "SUDO_UID=%lu", (unsigned long) user_uid);
                    514:     insert_env(cp, 1);
                    515:     easprintf(&cp, "SUDO_GID=%lu", (unsigned long) user_gid);
                    516:     insert_env(cp, 1);
1.1       millert   517:
1.5       millert   518:     return(new_environ);
1.1       millert   519: }
                    520:
                    521: void
                    522: init_envtables()
                    523: {
                    524:     struct list_member *cur;
1.5       millert   525:     const char **p;
1.1       millert   526:
                    527:     /* Fill in "env_delete" variable. */
                    528:     for (p = initial_badenv_table; *p; p++) {
                    529:        cur = emalloc(sizeof(struct list_member));
                    530:        cur->value = estrdup(*p);
1.9       millert   531:        cur->next = def_env_delete;
                    532:        def_env_delete = cur;
1.1       millert   533:     }
                    534:
                    535:     /* Fill in "env_check" variable. */
                    536:     for (p = initial_checkenv_table; *p; p++) {
                    537:        cur = emalloc(sizeof(struct list_member));
                    538:        cur->value = estrdup(*p);
1.9       millert   539:        cur->next = def_env_check;
                    540:        def_env_check = cur;
1.1       millert   541:     }
                    542: }