Annotation of embedaddon/sudo/plugins/sudoers/env.c, revision 1.1.1.2

1.1       misho       1: /*
                      2:  * Copyright (c) 2000-2005, 2007-2011
                      3:  *     Todd C. Miller <Todd.Miller@courtesan.com>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     16:  *
                     17:  * Sponsored in part by the Defense Advanced Research Projects
                     18:  * Agency (DARPA) and Air Force Research Laboratory, Air Force
                     19:  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
                     20:  */
                     21: 
                     22: #include <config.h>
                     23: 
                     24: #include <sys/types.h>
                     25: #include <sys/param.h>
                     26: #include <sys/stat.h>
                     27: #include <stdio.h>
                     28: #ifdef STDC_HEADERS
                     29: # include <stdlib.h>
                     30: # include <stddef.h>
                     31: #else
                     32: # ifdef HAVE_STDLIB_H
                     33: #  include <stdlib.h>
                     34: # endif
                     35: #endif /* STDC_HEADERS */
                     36: #ifdef HAVE_STRING_H
                     37: # include <string.h>
                     38: #endif /* HAVE_STRING_H */
                     39: #ifdef HAVE_STRINGS_H
                     40: # include <strings.h>
                     41: #endif /* HAVE_STRINGS_H */
                     42: #ifdef HAVE_UNISTD_H
                     43: # include <unistd.h>
                     44: #endif /* HAVE_UNISTD_H */
1.1.1.2 ! misho      45: #ifdef HAVE_LOGIN_CAP_H
        !            46: # include <login_cap.h>
        !            47: # ifndef LOGIN_SETENV
        !            48: #  define LOGIN_SETENV 0
        !            49: # endif
        !            50: #endif /* HAVE_LOGIN_CAP_H */
1.1       misho      51: #include <ctype.h>
                     52: #include <errno.h>
                     53: #include <pwd.h>
                     54: 
                     55: #include "sudoers.h"
                     56: 
                     57: /*
                     58:  * Flags used in rebuild_env()
                     59:  */
                     60: #undef DID_TERM
                     61: #define DID_TERM       0x0001
                     62: #undef DID_PATH
                     63: #define DID_PATH       0x0002
                     64: #undef DID_HOME
                     65: #define DID_HOME       0x0004
                     66: #undef DID_SHELL
                     67: #define DID_SHELL      0x0008
                     68: #undef DID_LOGNAME
                     69: #define DID_LOGNAME    0x0010
                     70: #undef DID_USER
                     71: #define DID_USER       0x0020
                     72: #undef DID_USERNAME
                     73: #define DID_USERNAME           0x0040
                     74: #undef DID_MAIL
                     75: #define DID_MAIL       0x0080
                     76: #undef DID_MAX
                     77: #define DID_MAX        0x00ff
                     78: 
                     79: #undef KEPT_TERM
                     80: #define KEPT_TERM      0x0100
                     81: #undef KEPT_PATH
                     82: #define KEPT_PATH      0x0200
                     83: #undef KEPT_HOME
                     84: #define KEPT_HOME      0x0400
                     85: #undef KEPT_SHELL
                     86: #define KEPT_SHELL     0x0800
                     87: #undef KEPT_LOGNAME
                     88: #define KEPT_LOGNAME   0x1000
                     89: #undef KEPT_USER
                     90: #define KEPT_USER      0x2000
                     91: #undef KEPT_USERNAME
                     92: #define KEPT_USERNAME  0x4000
                     93: #undef KEPT_MAIL
                     94: #define KEPT_MAIL      0x8000
                     95: #undef KEPT_MAX
                     96: #define KEPT_MAX       0xff00
                     97: 
                     98: struct environment {
1.1.1.2 ! misho      99:     char * const *old_envp;    /* pointer the environment we passed back */
1.1       misho     100:     char **envp;               /* pointer to the new environment */
                    101:     size_t env_size;           /* size of new_environ in char **'s */
                    102:     size_t env_len;            /* number of slots used, not counting NULL */
                    103: };
                    104: 
                    105: /*
                    106:  * Copy of the sudo-managed environment.
                    107:  */
                    108: static struct environment env;
                    109: 
                    110: /*
                    111:  * Default table of "bad" variables to remove from the environment.
                    112:  * XXX - how to omit TERMCAP if it starts with '/'?
                    113:  */
                    114: static const char *initial_badenv_table[] = {
                    115:     "IFS",
                    116:     "CDPATH",
                    117:     "LOCALDOMAIN",
                    118:     "RES_OPTIONS",
                    119:     "HOSTALIASES",
                    120:     "NLSPATH",
                    121:     "PATH_LOCALE",
                    122:     "LD_*",
                    123:     "_RLD*",
                    124: #ifdef __hpux
                    125:     "SHLIB_PATH",
                    126: #endif /* __hpux */
                    127: #ifdef _AIX
                    128:     "LDR_*",
                    129:     "LIBPATH",
                    130:     "AUTHSTATE",
                    131: #endif
                    132: #ifdef __APPLE__
                    133:     "DYLD_*",
                    134: #endif
                    135: #ifdef HAVE_KERB5
                    136:     "KRB5_CONFIG*",
                    137:     "KRB5_KTNAME",
                    138: #endif /* HAVE_KERB5 */
                    139: #ifdef HAVE_SECURID
                    140:     "VAR_ACE",
                    141:     "USR_ACE",
                    142:     "DLC_ACE",
                    143: #endif /* HAVE_SECURID */
                    144:     "TERMINFO",                        /* terminfo, exclusive path to terminfo files */
                    145:     "TERMINFO_DIRS",           /* terminfo, path(s) to terminfo files */
                    146:     "TERMPATH",                        /* termcap, path(s) to termcap files */
                    147:     "TERMCAP",                 /* XXX - only if it starts with '/' */
                    148:     "ENV",                     /* ksh, file to source before script runs */
                    149:     "BASH_ENV",                        /* bash, file to source before script runs */
                    150:     "PS4",                     /* bash, prefix for lines in xtrace mode */
                    151:     "GLOBIGNORE",              /* bash, globbing patterns to ignore */
                    152:     "SHELLOPTS",               /* bash, extra command line options */
                    153:     "JAVA_TOOL_OPTIONS",       /* java, extra command line options */
                    154:     "PERLIO_DEBUG ",           /* perl, debugging output file */
                    155:     "PERLLIB",                 /* perl, search path for modules/includes */
                    156:     "PERL5LIB",                        /* perl 5, search path for modules/includes */
                    157:     "PERL5OPT",                        /* perl 5, extra command line options */
                    158:     "PERL5DB",                 /* perl 5, command used to load debugger */
                    159:     "FPATH",                   /* ksh, search path for functions */
                    160:     "NULLCMD",                 /* zsh, command for null file redirection */
                    161:     "READNULLCMD",             /* zsh, command for null file redirection */
                    162:     "ZDOTDIR",                 /* zsh, search path for dot files */
                    163:     "TMPPREFIX",               /* zsh, prefix for temporary files */
                    164:     "PYTHONHOME",              /* python, module search path */
                    165:     "PYTHONPATH",              /* python, search path */
                    166:     "PYTHONINSPECT",           /* python, allow inspection */
                    167:     "PYTHONUSERBASE",          /* python, per user site-packages directory */
                    168:     "RUBYLIB",                 /* ruby, library load path */
                    169:     "RUBYOPT",                 /* ruby, extra command line options */
                    170:     NULL
                    171: };
                    172: 
                    173: /*
                    174:  * Default table of variables to check for '%' and '/' characters.
                    175:  */
                    176: static const char *initial_checkenv_table[] = {
                    177:     "COLORTERM",
                    178:     "LANG",
                    179:     "LANGUAGE",
                    180:     "LC_*",
                    181:     "LINGUAS",
                    182:     "TERM",
                    183:     NULL
                    184: };
                    185: 
                    186: /*
                    187:  * Default table of variables to preserve in the environment.
                    188:  */
                    189: static const char *initial_keepenv_table[] = {
                    190:     "COLORS",
                    191:     "DISPLAY",
                    192:     "HOSTNAME",
                    193:     "KRB5CCNAME",
                    194:     "LS_COLORS",
                    195:     "PATH",
                    196:     "PS1",
                    197:     "PS2",
                    198:     "TZ",
                    199:     "XAUTHORITY",
                    200:     "XAUTHORIZATION",
                    201:     NULL
                    202: };
                    203: 
                    204: /*
                    205:  * Initialize env based on envp.
                    206:  */
                    207: void
                    208: env_init(char * const envp[])
                    209: {
                    210:     char * const *ep;
                    211:     size_t len;
1.1.1.2 ! misho     212:     debug_decl(env_init, SUDO_DEBUG_ENV)
1.1       misho     213: 
1.1.1.2 ! misho     214:     if (envp == NULL) {
        !           215:        /* Reset to initial state but keep a pointer to what we allocated. */
        !           216:        envp = env.envp;
        !           217:        memset(&env, 0, sizeof(env));
        !           218:        env.old_envp = envp;
        !           219:     } else {
        !           220:        /* Make private copy of envp. */
        !           221:        for (ep = envp; *ep != NULL; ep++)
        !           222:            continue;
        !           223:        len = (size_t)(ep - envp);
1.1       misho     224: 
1.1.1.2 ! misho     225:        env.env_len = len;
        !           226:        env.env_size = len + 1 + 128;
        !           227:        env.envp = emalloc2(env.env_size, sizeof(char *));
1.1       misho     228: #ifdef ENV_DEBUG
1.1.1.2 ! misho     229:        memset(env.envp, 0, env.env_size * sizeof(char *));
1.1       misho     230: #endif
1.1.1.2 ! misho     231:        memcpy(env.envp, envp, len * sizeof(char *));
        !           232:        env.envp[len] = '\0';
1.1       misho     233: 
1.1.1.2 ! misho     234:        /* Free the old envp we allocated, if any. */
        !           235:        if (env.old_envp != NULL)
        !           236:            efree((void *)env.old_envp);
        !           237:     }
        !           238: 
        !           239:     debug_return;
1.1       misho     240: }
                    241: 
                    242: /*
1.1.1.2 ! misho     243:  * Getter for private copy of the environment.
1.1       misho     244:  */
1.1.1.2 ! misho     245: char **
        !           246: env_get(void)
1.1       misho     247: {
1.1.1.2 ! misho     248:     return env.envp;
1.1       misho     249: }
                    250: 
                    251: /*
                    252:  * Similar to putenv(3) but operates on sudo's private copy of the
                    253:  * environment (not environ) and it always overwrites.  The dupcheck param
                    254:  * determines whether we need to verify that the variable is not already set.
                    255:  * Will only overwrite an existing variable if overwrite is set.
1.1.1.2 ! misho     256:  * Does not include warnings or debugging to avoid recursive calls.
1.1       misho     257:  */
1.1.1.2 ! misho     258: static int
        !           259: sudo_putenv_nodebug(char *str, bool dupcheck, bool overwrite)
1.1       misho     260: {
                    261:     char **ep;
                    262:     size_t len;
1.1.1.2 ! misho     263:     bool found = false;
1.1       misho     264: 
                    265:     /* Make sure there is room for the new entry plus a NULL. */
                    266:     if (env.env_len + 2 > env.env_size) {
1.1.1.2 ! misho     267:        char **nenvp;
        !           268:        size_t nsize = env.env_size + 128;
        !           269:        nenvp = env.envp ? realloc(env.envp, nsize * sizeof(char *)) :
        !           270:            malloc(nsize * sizeof(char *));
        !           271:        if (nenvp == NULL) {
        !           272:            errno = ENOMEM;
        !           273:            return -1;
        !           274:        }
        !           275:        env.envp = nenvp;
        !           276:        env.env_size = nsize;
1.1       misho     277: #ifdef ENV_DEBUG
                    278:        memset(env.envp + env.env_len, 0,
                    279:            (env.env_size - env.env_len) * sizeof(char *));
                    280: #endif
                    281:     }
                    282: 
                    283: #ifdef ENV_DEBUG
1.1.1.2 ! misho     284:     if (env.envp[env.env_len] != NULL) {
        !           285:        errno = EINVAL;
        !           286:        return -1;
        !           287:     }
1.1       misho     288: #endif
                    289: 
                    290:     if (dupcheck) {
                    291:        len = (strchr(str, '=') - str) + 1;
                    292:        for (ep = env.envp; !found && *ep != NULL; ep++) {
                    293:            if (strncmp(str, *ep, len) == 0) {
                    294:                if (overwrite)
                    295:                    *ep = str;
1.1.1.2 ! misho     296:                found = true;
1.1       misho     297:            }
                    298:        }
                    299:        /* Prune out duplicate variables. */
                    300:        if (found && overwrite) {
                    301:            while (*ep != NULL) {
                    302:                if (strncmp(str, *ep, len) == 0) {
                    303:                    char **cur = ep;
                    304:                    while ((*cur = *(cur + 1)) != NULL)
                    305:                        cur++;
                    306:                } else {
                    307:                    ep++;
                    308:                }
                    309:            }
                    310:            env.env_len = ep - env.envp;
                    311:        }
                    312:     }
                    313: 
                    314:     if (!found) {
                    315:        ep = env.envp + env.env_len;
                    316:        env.env_len++;
                    317:        *ep++ = str;
                    318:        *ep = NULL;
                    319:     }
1.1.1.2 ! misho     320:     return 0;
1.1       misho     321: }
                    322: 
                    323: /*
1.1.1.2 ! misho     324:  * Similar to putenv(3) but operates on sudo's private copy of the
        !           325:  * environment (not environ) and it always overwrites.  The dupcheck param
        !           326:  * determines whether we need to verify that the variable is not already set.
        !           327:  * Will only overwrite an existing variable if overwrite is set.
1.1       misho     328:  */
                    329: static int
1.1.1.2 ! misho     330: sudo_putenv(char *str, bool dupcheck, bool overwrite)
        !           331: {
        !           332:     int rval;
        !           333:     debug_decl(sudo_putenv, SUDO_DEBUG_ENV)
        !           334: 
        !           335:     rval = sudo_putenv_nodebug(str, dupcheck, overwrite);
        !           336:     if (rval == -1) {
        !           337: #ifdef ENV_DEBUG
        !           338:        if (env.envp[env.env_len] != NULL)
        !           339:            errorx(1, _("sudo_putenv: corrupted envp, length mismatch"));
        !           340: #endif
        !           341:        errorx(1, _("unable to allocate memory"));
        !           342:     }
        !           343:     debug_return_int(rval);
        !           344: }
        !           345: 
        !           346: /*
        !           347:  * Similar to setenv(3) but operates on a private copy of the environment.
        !           348:  * The dupcheck param determines whether we need to verify that the variable
        !           349:  * is not already set.
        !           350:  */
        !           351: static int
        !           352: sudo_setenv2(const char *var, const char *val, bool dupcheck, bool overwrite)
        !           353: {
        !           354:     char *estring;
        !           355:     size_t esize;
        !           356:     debug_decl(sudo_setenv2, SUDO_DEBUG_ENV)
        !           357: 
        !           358:     esize = strlen(var) + 1 + strlen(val) + 1;
        !           359:     estring = emalloc(esize);
        !           360: 
        !           361:     /* Build environment string and insert it. */
        !           362:     if (strlcpy(estring, var, esize) >= esize ||
        !           363:        strlcat(estring, "=", esize) >= esize ||
        !           364:        strlcat(estring, val, esize) >= esize) {
        !           365: 
        !           366:        errorx(1, _("internal error, sudo_setenv2() overflow"));
        !           367:     }
        !           368:     debug_return_int(sudo_putenv(estring, dupcheck, overwrite));
        !           369: }
        !           370: 
        !           371: /*
        !           372:  * Similar to setenv(3) but operates on a private copy of the environment.
        !           373:  * Does not include warnings or debugging to avoid recursive calls.
        !           374:  */
        !           375: static int
        !           376: sudo_setenv_nodebug(const char *var, const char *val, int overwrite)
        !           377: {
        !           378:     char *estring;
        !           379:     size_t esize;
        !           380: 
        !           381:     esize = strlen(var) + 1 + strlen(val) + 1;
        !           382:     if ((estring = malloc(esize)) == NULL) {
        !           383:        errno = ENOMEM;
        !           384:        return -1;
        !           385:     }
        !           386: 
        !           387:     /* Build environment string and insert it. */
        !           388:     if (strlcpy(estring, var, esize) >= esize ||
        !           389:        strlcat(estring, "=", esize) >= esize ||
        !           390:        strlcat(estring, val, esize) >= esize) {
        !           391: 
        !           392:        errno = EINVAL;
        !           393:        return -1;
        !           394:     }
        !           395:     return sudo_putenv_nodebug(estring, true, overwrite);
        !           396: }
        !           397: 
        !           398: /*
        !           399:  * Similar to setenv(3) but operates on a private copy of the environment.
        !           400:  */
        !           401: int
        !           402: sudo_setenv(const char *var, const char *val, int overwrite)
        !           403: {
        !           404:     int rval;
        !           405:     debug_decl(sudo_setenv, SUDO_DEBUG_ENV)
        !           406: 
        !           407:     rval = sudo_setenv_nodebug(var, val, overwrite);
        !           408:     if (rval == -1) {
        !           409:        if (errno == EINVAL)
        !           410:            errorx(1, _("internal error, sudo_setenv() overflow"));
        !           411:        errorx(1, _("unable to allocate memory"));
        !           412:     }
        !           413:     debug_return_int(rval);
        !           414: }
        !           415: 
        !           416: /*
        !           417:  * Similar to unsetenv(3) but operates on a private copy of the environment.
        !           418:  * Does not include warnings or debugging to avoid recursive calls.
        !           419:  */
        !           420: static int
        !           421: sudo_unsetenv_nodebug(const char *var)
        !           422: {
        !           423:     char **ep = env.envp;
        !           424:     size_t len;
        !           425: 
        !           426:     if (ep == NULL || var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
        !           427:        errno = EINVAL;
        !           428:        return -1;
        !           429:     }
        !           430: 
        !           431:     len = strlen(var);
        !           432:     while (*ep != NULL) {
        !           433:        if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
        !           434:            /* Found it; shift remainder + NULL over by one. */
        !           435:            char **cur = ep;
        !           436:            while ((*cur = *(cur + 1)) != NULL)
        !           437:                cur++;
        !           438:            /* Keep going, could be multiple instances of the var. */
        !           439:        } else {
        !           440:            ep++;
        !           441:        }
        !           442:     }
        !           443:     return 0;
        !           444: }
        !           445: 
        !           446: /*
        !           447:  * Similar to unsetenv(3) but operates on a private copy of the environment.
        !           448:  */
        !           449: int
        !           450: sudo_unsetenv(const char *name)
        !           451: {
        !           452:     int rval;
        !           453:     debug_decl(sudo_unsetenv, SUDO_DEBUG_ENV)
        !           454: 
        !           455:     rval = sudo_unsetenv_nodebug(name);
        !           456: 
        !           457:     debug_return_int(rval);
        !           458: }
        !           459: 
        !           460: /*
        !           461:  * Similar to getenv(3) but operates on a private copy of the environment.
        !           462:  * Does not include warnings or debugging to avoid recursive calls.
        !           463:  */
        !           464: static char *
        !           465: sudo_getenv_nodebug(const char *name)
        !           466: {
        !           467:     char **ep, *val = NULL;
        !           468:     size_t namelen = 0;
        !           469: 
        !           470:     if (env.env_len != 0) {
        !           471:        /* For BSD compatibility, treat '=' in name like end of string. */
        !           472:        while (name[namelen] != '\0' && name[namelen] != '=')
        !           473:            namelen++;
        !           474:        for (ep = env.envp; *ep != NULL; ep++) {
        !           475:            if (strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') {
        !           476:                val = *ep + namelen + 1;
        !           477:                break;
        !           478:            }
        !           479:        }
        !           480:     }
        !           481:     return val;
        !           482: }
        !           483: 
        !           484: /*
        !           485:  * Similar to getenv(3) but operates on a private copy of the environment.
        !           486:  */
        !           487: char *
        !           488: sudo_getenv(const char *name)
        !           489: {
        !           490:     char *val;
        !           491:     debug_decl(sudo_getenv, SUDO_DEBUG_ENV)
        !           492: 
        !           493:     val = sudo_getenv_nodebug(name);
        !           494: 
        !           495:     debug_return_str(val);
        !           496: }
        !           497: 
        !           498: /*
        !           499:  * Merge another environment with our private copy.
        !           500:  */
        !           501: void
        !           502: env_merge(char * const envp[], bool overwrite)
        !           503: {
        !           504:     char * const *ep;
        !           505:     debug_decl(env_merge, SUDO_DEBUG_ENV)
        !           506: 
        !           507:     for (ep = envp; *ep != NULL; ep++)
        !           508:        sudo_putenv(*ep, true, overwrite);
        !           509: 
        !           510:     debug_return;
        !           511: }
        !           512: 
        !           513: /*
        !           514:  * Check the env_delete blacklist.
        !           515:  * Returns true if the variable was found, else false.
        !           516:  */
        !           517: static bool
1.1       misho     518: matches_env_delete(const char *var)
                    519: {
                    520:     struct list_member *cur;
                    521:     size_t len;
1.1.1.2 ! misho     522:     bool iswild;
        !           523:     bool match = false;
        !           524:     debug_decl(matches_env_delete, SUDO_DEBUG_ENV)
1.1       misho     525: 
                    526:     /* Skip anything listed in env_delete. */
                    527:     for (cur = def_env_delete; cur; cur = cur->next) {
                    528:        len = strlen(cur->value);
                    529:        /* Deal with '*' wildcard */
                    530:        if (cur->value[len - 1] == '*') {
                    531:            len--;
1.1.1.2 ! misho     532:            iswild = true;
1.1       misho     533:        } else
1.1.1.2 ! misho     534:            iswild = false;
1.1       misho     535:        if (strncmp(cur->value, var, len) == 0 &&
                    536:            (iswild || var[len] == '=')) {
1.1.1.2 ! misho     537:            match = true;
1.1       misho     538:            break;
                    539:        }
                    540:     }
1.1.1.2 ! misho     541:     debug_return_bool(match);
1.1       misho     542: }
                    543: 
                    544: /*
                    545:  * Apply the env_check list.
1.1.1.2 ! misho     546:  * Returns true if the variable is allowed, false if denied
1.1       misho     547:  * or -1 if no match.
                    548:  */
                    549: static int
                    550: matches_env_check(const char *var)
                    551: {
                    552:     struct list_member *cur;
                    553:     size_t len;
1.1.1.2 ! misho     554:     bool iswild;
        !           555:     int keepit = -1;
        !           556:     debug_decl(matches_env_check, SUDO_DEBUG_ENV)
1.1       misho     557: 
                    558:     for (cur = def_env_check; cur; cur = cur->next) {
                    559:        len = strlen(cur->value);
                    560:        /* Deal with '*' wildcard */
                    561:        if (cur->value[len - 1] == '*') {
                    562:            len--;
1.1.1.2 ! misho     563:            iswild = true;
1.1       misho     564:        } else
1.1.1.2 ! misho     565:            iswild = false;
1.1       misho     566:        if (strncmp(cur->value, var, len) == 0 &&
                    567:            (iswild || var[len] == '=')) {
                    568:            keepit = !strpbrk(var, "/%");
                    569:            break;
                    570:        }
                    571:     }
1.1.1.2 ! misho     572:     debug_return_bool(keepit);
1.1       misho     573: }
                    574: 
                    575: /*
                    576:  * Check the env_keep list.
1.1.1.2 ! misho     577:  * Returns true if the variable is allowed else false.
1.1       misho     578:  */
1.1.1.2 ! misho     579: static bool
1.1       misho     580: matches_env_keep(const char *var)
                    581: {
                    582:     struct list_member *cur;
                    583:     size_t len;
1.1.1.2 ! misho     584:     bool iswild, keepit = false;
        !           585:     debug_decl(matches_env_keep, SUDO_DEBUG_ENV)
1.1       misho     586: 
                    587:     /* Preserve SHELL variable for "sudo -s". */
1.1.1.2 ! misho     588:     if (ISSET(sudo_mode, MODE_SHELL) && strncmp(var, "SHELL=", 6) == 0) {
        !           589:        keepit = true;
        !           590:        goto done;
        !           591:     }
1.1       misho     592: 
                    593:     for (cur = def_env_keep; cur; cur = cur->next) {
                    594:        len = strlen(cur->value);
                    595:        /* Deal with '*' wildcard */
                    596:        if (cur->value[len - 1] == '*') {
                    597:            len--;
1.1.1.2 ! misho     598:            iswild = true;
1.1       misho     599:        } else
1.1.1.2 ! misho     600:            iswild = false;
1.1       misho     601:        if (strncmp(cur->value, var, len) == 0 &&
                    602:            (iswild || var[len] == '=')) {
1.1.1.2 ! misho     603:            keepit = true;
1.1       misho     604:            break;
                    605:        }
                    606:     }
1.1.1.2 ! misho     607: done:
        !           608:     debug_return_bool(keepit);
        !           609: }
        !           610: 
        !           611: /*
        !           612:  * Look up var in the env_delete and env_check.
        !           613:  * Returns true if we should delete the variable, else false.
        !           614:  */
        !           615: static bool
        !           616: env_should_delete(const char *var)
        !           617: {
        !           618:     int delete_it;
        !           619:     debug_decl(env_should_delete, SUDO_DEBUG_ENV);
        !           620: 
        !           621:     delete_it = matches_env_delete(var);
        !           622:     if (!delete_it)
        !           623:        delete_it = matches_env_check(var) == false;
        !           624:     debug_return_bool(delete_it);
        !           625: }
        !           626: 
        !           627: /*
        !           628:  * Lookup var in the env_check and env_keep lists.
        !           629:  * Returns true if the variable is allowed else false.
        !           630:  */
        !           631: static bool
        !           632: env_should_keep(const char *var)
        !           633: {
        !           634:     int keepit;
        !           635:     debug_decl(env_should_keep, SUDO_DEBUG_ENV)
        !           636: 
        !           637:     keepit = matches_env_check(var);
        !           638:     if (keepit == -1)
        !           639:        keepit = matches_env_keep(var);
        !           640: 
        !           641:     debug_return_bool(keepit == true);
        !           642: }
        !           643: 
        !           644: static void
        !           645: env_update_didvar(const char *ep, unsigned int *didvar)
        !           646: {
        !           647:     switch (*ep) {
        !           648:        case 'H':
        !           649:            if (strncmp(ep, "HOME=", 5) == 0)
        !           650:                SET(*didvar, DID_HOME);
        !           651:            break;
        !           652:        case 'L':
        !           653:            if (strncmp(ep, "LOGNAME=", 8) == 0)
        !           654:                SET(*didvar, DID_LOGNAME);
        !           655:            break;
        !           656:        case 'M':
        !           657:            if (strncmp(ep, "MAIL=", 5) == 0)
        !           658:                SET(*didvar, DID_MAIL);
        !           659:            break;
        !           660:        case 'P':
        !           661:            if (strncmp(ep, "PATH=", 5) == 0)
        !           662:                SET(*didvar, DID_PATH);
        !           663:            break;
        !           664:        case 'S':
        !           665:            if (strncmp(ep, "SHELL=", 6) == 0)
        !           666:                SET(*didvar, DID_SHELL);
        !           667:            break;
        !           668:        case 'T':
        !           669:            if (strncmp(ep, "TERM=", 5) == 0)
        !           670:                SET(*didvar, DID_TERM);
        !           671:            break;
        !           672:        case 'U':
        !           673:            if (strncmp(ep, "USER=", 5) == 0)
        !           674:                SET(*didvar, DID_USER);
        !           675:            if (strncmp(ep, "USERNAME=", 5) == 0)
        !           676:                SET(*didvar, DID_USERNAME);
        !           677:            break;
        !           678:     }
1.1       misho     679: }
                    680: 
                    681: /*
                    682:  * Build a new environment and ether clear potentially dangerous
                    683:  * variables from the old one or start with a clean slate.
                    684:  * Also adds sudo-specific variables (SUDO_*).
                    685:  */
                    686: void
                    687: rebuild_env(void)
                    688: {
                    689:     char **old_envp, **ep, *cp, *ps1;
                    690:     char idbuf[MAX_UID_T_LEN];
                    691:     unsigned int didvar;
1.1.1.2 ! misho     692:     bool reset_home = false;
1.1       misho     693: 
                    694:     /*
                    695:      * Either clean out the environment or reset to a safe default.
                    696:      */
                    697:     ps1 = NULL;
                    698:     didvar = 0;
                    699:     env.env_len = 0;
                    700:     env.env_size = 128;
                    701:     old_envp = env.envp;
                    702:     env.envp = emalloc2(env.env_size, sizeof(char *));
                    703: #ifdef ENV_DEBUG
                    704:     memset(env.envp, 0, env.env_size * sizeof(char *));
1.1.1.2 ! misho     705: #else
        !           706:     env.envp[0] = NULL;
1.1       misho     707: #endif
                    708: 
                    709:     /* Reset HOME based on target user if configured to. */
                    710:     if (ISSET(sudo_mode, MODE_RUN)) {
                    711:        if (def_always_set_home ||
                    712:            ISSET(sudo_mode, MODE_RESET_HOME | MODE_LOGIN_SHELL) || 
                    713:            (ISSET(sudo_mode, MODE_SHELL) && def_set_home))
1.1.1.2 ! misho     714:            reset_home = true;
1.1       misho     715:     }
                    716: 
                    717:     if (def_env_reset || ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
1.1.1.2 ! misho     718:        /*
        !           719:         * If starting with a fresh environment, initialize it based on
        !           720:         * /etc/environment or login.conf.  For "sudo -i" we want those
        !           721:         * variables to override the invoking user's environment, so we
        !           722:         * defer reading them until later.
        !           723:         */
        !           724:        if (!ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
        !           725: #ifdef HAVE_LOGIN_CAP_H
        !           726:            /* Insert login class environment variables. */
        !           727:            if (login_class) {
        !           728:                login_cap_t *lc = login_getclass(login_class);
        !           729:                if (lc != NULL) {
        !           730:                    setusercontext(lc, runas_pw, runas_pw->pw_uid,
        !           731:                        LOGIN_SETPATH|LOGIN_SETENV);
        !           732:                    login_close(lc);
        !           733:                }
        !           734:            }
        !           735: #endif /* HAVE_LOGIN_CAP_H */
        !           736: #if defined(_AIX) || (defined(__linux__) && !defined(HAVE_PAM))
        !           737:            /* Insert system-wide environment variables. */
        !           738:            read_env_file(_PATH_ENVIRONMENT, true);
        !           739: #endif
        !           740:            for (ep = env.envp; *ep; ep++)
        !           741:                env_update_didvar(*ep, &didvar);
        !           742:        }
        !           743: 
1.1       misho     744:        /* Pull in vars we want to keep from the old environment. */
                    745:        for (ep = old_envp; *ep; ep++) {
1.1.1.2 ! misho     746:            bool keepit;
1.1       misho     747: 
                    748:            /* Skip variables with values beginning with () (bash functions) */
                    749:            if ((cp = strchr(*ep, '=')) != NULL) {
                    750:                if (strncmp(cp, "=() ", 3) == 0)
                    751:                    continue;
                    752:            }
                    753: 
                    754:            /*
1.1.1.2 ! misho     755:             * Look up the variable in the env_check and env_keep lists.
1.1       misho     756:             */
1.1.1.2 ! misho     757:            keepit = env_should_keep(*ep);
1.1       misho     758: 
1.1.1.2 ! misho     759:            /*
        !           760:             * Do SUDO_PS1 -> PS1 conversion.
        !           761:             * This must happen *after* env_should_keep() is called.
        !           762:             */
1.1       misho     763:            if (strncmp(*ep, "SUDO_PS1=", 8) == 0)
                    764:                ps1 = *ep + 5;
                    765: 
                    766:            if (keepit) {
                    767:                /* Preserve variable. */
1.1.1.2 ! misho     768:                sudo_putenv(*ep, false, false);
        !           769:                env_update_didvar(*ep, &didvar);
1.1       misho     770:            }
                    771:        }
                    772:        didvar |= didvar << 8;          /* convert DID_* to KEPT_* */
                    773: 
                    774:        /*
                    775:         * Add in defaults.  In -i mode these come from the runas user,
                    776:         * otherwise they may be from the user's environment (depends
                    777:         * on sudoers options).
                    778:         */
                    779:        if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
1.1.1.2 ! misho     780:            sudo_setenv2("SHELL", runas_pw->pw_shell,
        !           781:                ISSET(didvar, DID_SHELL), true);
        !           782:            sudo_setenv2("LOGNAME", runas_pw->pw_name,
        !           783:                ISSET(didvar, DID_LOGNAME), true);
        !           784:            sudo_setenv2("USER", runas_pw->pw_name,
        !           785:                ISSET(didvar, DID_USER), true);
        !           786:            sudo_setenv2("USERNAME", runas_pw->pw_name,
        !           787:                ISSET(didvar, DID_USERNAME), true);
1.1       misho     788:        } else {
                    789:            if (!ISSET(didvar, DID_SHELL))
1.1.1.2 ! misho     790:                sudo_setenv2("SHELL", sudo_user.pw->pw_shell, false, true);
1.1       misho     791:            if (!ISSET(didvar, DID_LOGNAME))
1.1.1.2 ! misho     792:                sudo_setenv2("LOGNAME", user_name, false, true);
1.1       misho     793:            if (!ISSET(didvar, DID_USER))
1.1.1.2 ! misho     794:                sudo_setenv2("USER", user_name, false, true);
1.1       misho     795:            if (!ISSET(didvar, DID_USERNAME))
1.1.1.2 ! misho     796:                sudo_setenv2("USERNAME", user_name, false, true);
1.1       misho     797:        }
                    798: 
                    799:        /* If we didn't keep HOME, reset it based on target user. */
                    800:        if (!ISSET(didvar, KEPT_HOME))
1.1.1.2 ! misho     801:            reset_home = true;
1.1       misho     802: 
                    803:        /*
                    804:         * Set MAIL to target user in -i mode or if MAIL is not preserved
                    805:         * from user's environment.
                    806:         */
                    807:        if (ISSET(sudo_mode, MODE_LOGIN_SHELL) || !ISSET(didvar, KEPT_MAIL)) {
                    808:            cp = _PATH_MAILDIR;
                    809:            if (cp[sizeof(_PATH_MAILDIR) - 2] == '/')
                    810:                easprintf(&cp, "MAIL=%s%s", _PATH_MAILDIR, runas_pw->pw_name);
                    811:            else
                    812:                easprintf(&cp, "MAIL=%s/%s", _PATH_MAILDIR, runas_pw->pw_name);
1.1.1.2 ! misho     813:            sudo_putenv(cp, ISSET(didvar, DID_MAIL), true);
1.1       misho     814:        }
                    815:     } else {
                    816:        /*
                    817:         * Copy environ entries as long as they don't match env_delete or
                    818:         * env_check.
                    819:         */
                    820:        for (ep = old_envp; *ep; ep++) {
                    821:            /* Skip variables with values beginning with () (bash functions) */
                    822:            if ((cp = strchr(*ep, '=')) != NULL) {
                    823:                if (strncmp(cp, "=() ", 3) == 0)
                    824:                    continue;
                    825:            }
                    826: 
1.1.1.2 ! misho     827:            /* Add variable unless it matches a black list. */
        !           828:            if (!env_should_delete(*ep)) {
1.1       misho     829:                if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
                    830:                    ps1 = *ep + 5;
                    831:                else if (strncmp(*ep, "PATH=", 5) == 0)
                    832:                    SET(didvar, DID_PATH);
                    833:                else if (strncmp(*ep, "TERM=", 5) == 0)
                    834:                    SET(didvar, DID_TERM);
1.1.1.2 ! misho     835:                sudo_putenv(*ep, false, false);
1.1       misho     836:            }
                    837:        }
                    838:     }
                    839:     /* Replace the PATH envariable with a secure one? */
                    840:     if (def_secure_path && !user_is_exempt()) {
1.1.1.2 ! misho     841:        sudo_setenv2("PATH", def_secure_path, true, true);
1.1       misho     842:        SET(didvar, DID_PATH);
                    843:     }
                    844: 
                    845:     /*
                    846:      * Set $USER, $LOGNAME and $USERNAME to target if "set_logname" is not
                    847:      * disabled.  We skip this if we are running a login shell (because
                    848:      * they have already been set them) or sudoedit (because we want the
                    849:      * editor to find the user's startup files).
                    850:      */
                    851:     if (def_set_logname && !ISSET(sudo_mode, MODE_LOGIN_SHELL|MODE_EDIT)) {
                    852:        if (!ISSET(didvar, KEPT_LOGNAME))
1.1.1.2 ! misho     853:            sudo_setenv2("LOGNAME", runas_pw->pw_name, true, true);
1.1       misho     854:        if (!ISSET(didvar, KEPT_USER))
1.1.1.2 ! misho     855:            sudo_setenv2("USER", runas_pw->pw_name, true, true);
1.1       misho     856:        if (!ISSET(didvar, KEPT_USERNAME))
1.1.1.2 ! misho     857:            sudo_setenv2("USERNAME", runas_pw->pw_name, true, true);
1.1       misho     858:     }
                    859: 
                    860:     /* Set $HOME to target user if not preserving user's value. */
                    861:     if (reset_home)
1.1.1.2 ! misho     862:        sudo_setenv2("HOME", runas_pw->pw_dir, true, true);
1.1       misho     863: 
                    864:     /* Provide default values for $TERM and $PATH if they are not set. */
                    865:     if (!ISSET(didvar, DID_TERM))
1.1.1.2 ! misho     866:        sudo_putenv("TERM=unknown", false, false);
1.1       misho     867:     if (!ISSET(didvar, DID_PATH))
1.1.1.2 ! misho     868:        sudo_setenv2("PATH", _PATH_STDPATH, false, true);
1.1       misho     869: 
                    870:     /* Set PS1 if SUDO_PS1 is set. */
                    871:     if (ps1 != NULL)
1.1.1.2 ! misho     872:        sudo_putenv(ps1, true, true);
1.1       misho     873: 
                    874:     /* Add the SUDO_COMMAND envariable (cmnd + args). */
                    875:     if (user_args) {
                    876:        easprintf(&cp, "%s %s", user_cmnd, user_args);
1.1.1.2 ! misho     877:        sudo_setenv2("SUDO_COMMAND", cp, true, true);
1.1       misho     878:        efree(cp);
                    879:     } else {
1.1.1.2 ! misho     880:        sudo_setenv2("SUDO_COMMAND", user_cmnd, true, true);
1.1       misho     881:     }
                    882: 
                    883:     /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
1.1.1.2 ! misho     884:     sudo_setenv2("SUDO_USER", user_name, true, true);
1.1       misho     885:     snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_uid);
1.1.1.2 ! misho     886:     sudo_setenv2("SUDO_UID", idbuf, true, true);
1.1       misho     887:     snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_gid);
1.1.1.2 ! misho     888:     sudo_setenv2("SUDO_GID", idbuf, true, true);
1.1       misho     889: 
                    890:     /* Free old environment. */
                    891:     efree(old_envp);
                    892: }
                    893: 
                    894: void
                    895: insert_env_vars(char * const envp[])
                    896: {
                    897:     char * const *ep;
                    898: 
                    899:     if (envp == NULL)
                    900:        return;
                    901: 
                    902:     /* Add user-specified environment variables. */
                    903:     for (ep = envp; *ep != NULL; ep++)
1.1.1.2 ! misho     904:        sudo_putenv(*ep, true, true);
1.1       misho     905: }
                    906: 
                    907: /*
                    908:  * Validate the list of environment variables passed in on the command
                    909:  * line against env_delete, env_check, and env_keep.
1.1.1.2 ! misho     910:  * Calls log_fatal() if any specified variables are not allowed.
1.1       misho     911:  */
                    912: void
                    913: validate_env_vars(char * const env_vars[])
                    914: {
                    915:     char * const *ep;
                    916:     char *eq, *bad = NULL;
                    917:     size_t len, blen = 0, bsize = 0;
1.1.1.2 ! misho     918:     bool okvar;
1.1       misho     919: 
                    920:     if (env_vars == NULL)
                    921:        return;
                    922: 
                    923:     /* Add user-specified environment variables. */
                    924:     for (ep = env_vars; *ep != NULL; ep++) {
                    925:        if (def_secure_path && !user_is_exempt() &&
                    926:            strncmp(*ep, "PATH=", 5) == 0) {
1.1.1.2 ! misho     927:            okvar = false;
1.1       misho     928:        } else if (def_env_reset) {
1.1.1.2 ! misho     929:            okvar = env_should_keep(*ep);
1.1       misho     930:        } else {
1.1.1.2 ! misho     931:            okvar = !env_should_delete(*ep);
1.1       misho     932:        }
1.1.1.2 ! misho     933:        if (okvar == false) {
1.1       misho     934:            /* Not allowed, add to error string, allocating as needed. */
                    935:            if ((eq = strchr(*ep, '=')) != NULL)
                    936:                *eq = '\0';
                    937:            len = strlen(*ep) + 2;
                    938:            if (blen + len >= bsize) {
                    939:                do {
                    940:                    bsize += 1024;
                    941:                } while (blen + len >= bsize);
                    942:                bad = erealloc(bad, bsize);
                    943:                bad[blen] = '\0';
                    944:            }
                    945:            strlcat(bad, *ep, bsize);
                    946:            strlcat(bad, ", ", bsize);
                    947:            blen += len;
                    948:            if (eq != NULL)
                    949:                *eq = '=';
                    950:        }
                    951:     }
                    952:     if (bad != NULL) {
                    953:        bad[blen - 2] = '\0';           /* remove trailing ", " */
1.1.1.2 ! misho     954:        log_fatal(NO_MAIL,
1.1       misho     955:            _("sorry, you are not allowed to set the following environment variables: %s"), bad);
                    956:        /* NOTREACHED */
                    957:        efree(bad);
                    958:     }
                    959: }
                    960: 
                    961: /*
                    962:  * Read in /etc/environment ala AIX and Linux.
                    963:  * Lines may be in either of three formats:
                    964:  *  NAME=VALUE
                    965:  *  NAME="VALUE"
                    966:  *  NAME='VALUE'
                    967:  * with an optional "export" prefix so the shell can source the file.
                    968:  * Invalid lines, blank lines, or lines consisting solely of a comment
                    969:  * character are skipped.
                    970:  */
                    971: void
                    972: read_env_file(const char *path, int overwrite)
                    973: {
                    974:     FILE *fp;
                    975:     char *cp, *var, *val;
                    976:     size_t var_len, val_len;
                    977: 
                    978:     if ((fp = fopen(path, "r")) == NULL)
                    979:        return;
                    980: 
                    981:     while ((var = sudo_parseln(fp)) != NULL) {
                    982:        /* Skip blank or comment lines */
                    983:        if (*var == '\0')
                    984:            continue;
                    985: 
                    986:        /* Skip optional "export " */
                    987:        if (strncmp(var, "export", 6) == 0 && isspace((unsigned char) var[6])) {
                    988:            var += 7;
                    989:            while (isspace((unsigned char) *var)) {
                    990:                var++;
                    991:            }
                    992:        }
                    993: 
                    994:        /* Must be of the form name=["']value['"] */
                    995:        for (val = var; *val != '\0' && *val != '='; val++)
                    996:            ;
                    997:        if (var == val || *val != '=')
                    998:            continue;
                    999:        var_len = (size_t)(val - var);
                   1000:        val_len = strlen(++val);
                   1001: 
                   1002:        /* Strip leading and trailing single/double quotes */
                   1003:        if ((val[0] == '\'' || val[0] == '\"') && val[0] == val[val_len - 1]) {
                   1004:            val[val_len - 1] = '\0';
                   1005:            val++;
                   1006:            val_len -= 2;
                   1007:        }
                   1008: 
                   1009:        cp = emalloc(var_len + 1 + val_len + 1);
                   1010:        memcpy(cp, var, var_len + 1); /* includes '=' */
                   1011:        memcpy(cp + var_len + 1, val, val_len + 1); /* includes NUL */
                   1012: 
1.1.1.2 ! misho    1013:        sudo_putenv(cp, true, overwrite);
1.1       misho    1014:     }
                   1015:     fclose(fp);
                   1016: }
                   1017: 
                   1018: void
                   1019: init_envtables(void)
                   1020: {
                   1021:     struct list_member *cur;
                   1022:     const char **p;
                   1023: 
                   1024:     /* Fill in the "env_delete" list. */
                   1025:     for (p = initial_badenv_table; *p; p++) {
1.1.1.2 ! misho    1026:        cur = ecalloc(1, sizeof(struct list_member));
1.1       misho    1027:        cur->value = estrdup(*p);
                   1028:        cur->next = def_env_delete;
                   1029:        def_env_delete = cur;
                   1030:     }
                   1031: 
                   1032:     /* Fill in the "env_check" list. */
                   1033:     for (p = initial_checkenv_table; *p; p++) {
1.1.1.2 ! misho    1034:        cur = ecalloc(1, sizeof(struct list_member));
1.1       misho    1035:        cur->value = estrdup(*p);
                   1036:        cur->next = def_env_check;
                   1037:        def_env_check = cur;
                   1038:     }
                   1039: 
                   1040:     /* Fill in the "env_keep" list. */
                   1041:     for (p = initial_keepenv_table; *p; p++) {
1.1.1.2 ! misho    1042:        cur = ecalloc(1, sizeof(struct list_member));
1.1       misho    1043:        cur->value = estrdup(*p);
                   1044:        cur->next = def_env_keep;
                   1045:        def_env_keep = cur;
                   1046:     }
                   1047: }
1.1.1.2 ! misho    1048: 
        !          1049: int
        !          1050: sudoers_hook_getenv(const char *name, char **value, void *closure)
        !          1051: {
        !          1052:     static bool in_progress = false; /* avoid recursion */
        !          1053: 
        !          1054:     if (in_progress || env.envp == NULL)
        !          1055:        return SUDO_HOOK_RET_NEXT;
        !          1056: 
        !          1057:     in_progress = true;
        !          1058:     *value = sudo_getenv_nodebug(name);
        !          1059:     in_progress = false;
        !          1060:     return SUDO_HOOK_RET_STOP;
        !          1061: }
        !          1062: 
        !          1063: int
        !          1064: sudoers_hook_putenv(char *string, void *closure)
        !          1065: {
        !          1066:     static bool in_progress = false; /* avoid recursion */
        !          1067: 
        !          1068:     if (in_progress || env.envp == NULL)
        !          1069:        return SUDO_HOOK_RET_NEXT;
        !          1070: 
        !          1071:     in_progress = true;
        !          1072:     sudo_putenv_nodebug(string, true, true);
        !          1073:     in_progress = false;
        !          1074:     return SUDO_HOOK_RET_STOP;
        !          1075: }
        !          1076: 
        !          1077: int
        !          1078: sudoers_hook_setenv(const char *name, const char *value, int overwrite, void *closure)
        !          1079: {
        !          1080:     static bool in_progress = false; /* avoid recursion */
        !          1081: 
        !          1082:     if (in_progress || env.envp == NULL)
        !          1083:        return SUDO_HOOK_RET_NEXT;
        !          1084: 
        !          1085:     in_progress = true;
        !          1086:     sudo_setenv_nodebug(name, value, overwrite);
        !          1087:     in_progress = false;
        !          1088:     return SUDO_HOOK_RET_STOP;
        !          1089: }
        !          1090: 
        !          1091: int
        !          1092: sudoers_hook_unsetenv(const char *name, void *closure)
        !          1093: {
        !          1094:     static bool in_progress = false; /* avoid recursion */
        !          1095: 
        !          1096:     if (in_progress || env.envp == NULL)
        !          1097:        return SUDO_HOOK_RET_NEXT;
        !          1098: 
        !          1099:     in_progress = true;
        !          1100:     sudo_unsetenv_nodebug(name);
        !          1101:     in_progress = false;
        !          1102:     return SUDO_HOOK_RET_STOP;
        !          1103: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>