Annotation of embedaddon/sudo/common/sudo_conf.c, revision 1.1.1.5

1.1       misho       1: /*
1.1.1.5 ! misho       2:  * Copyright (c) 2009-2014 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       misho       3:  *
                      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.
                      7:  *
                      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.
                     15:  */
                     16: 
                     17: #include <config.h>
                     18: 
                     19: #include <sys/types.h>
                     20: #include <sys/stat.h>
                     21: #include <stdio.h>
                     22: #ifdef STDC_HEADERS
                     23: # include <stdlib.h>
                     24: # include <stddef.h>
                     25: #else
                     26: # ifdef HAVE_STDLIB_H
                     27: #  include <stdlib.h>
                     28: # endif
                     29: #endif /* STDC_HEADERS */
                     30: #ifdef HAVE_STDBOOL_H
                     31: # include <stdbool.h>
                     32: #else
                     33: # include "compat/stdbool.h"
                     34: #endif
                     35: #ifdef HAVE_STRING_H
                     36: # include <string.h>
                     37: #endif /* HAVE_STRING_H */
                     38: #ifdef HAVE_STRINGS_H
                     39: # include <strings.h>
                     40: #endif /* HAVE_STRINGS_H */
                     41: #ifdef HAVE_UNISTD_H
                     42: # include <unistd.h>
                     43: #endif /* HAVE_UNISTD_H */
                     44: #include <ctype.h>
                     45: #include <errno.h>
1.1.1.3   misho      46: #include <limits.h>
1.1       misho      47: 
1.1.1.5 ! misho      48: #define DEFAULT_TEXT_DOMAIN    "sudo"
        !            49: #include "gettext.h"           /* must be included before missing.h */
        !            50: 
1.1       misho      51: #define SUDO_ERROR_WRAP        0
                     52: 
                     53: #include "missing.h"
                     54: #include "alloc.h"
1.1.1.4   misho      55: #include "fatal.h"
1.1       misho      56: #include "fileops.h"
                     57: #include "pathnames.h"
                     58: #include "sudo_plugin.h"
                     59: #include "sudo_conf.h"
                     60: #include "sudo_debug.h"
1.1.1.5 ! misho      61: #include "sudo_util.h"
1.1       misho      62: #include "secure_path.h"
1.1.1.3   misho      63: 
1.1       misho      64: #ifdef __TANDEM
                     65: # define ROOT_UID      65535
                     66: #else
                     67: # define ROOT_UID      0
                     68: #endif
                     69: 
                     70: struct sudo_conf_table {
                     71:     const char *name;
                     72:     unsigned int namelen;
1.1.1.3   misho      73:     void (*setter)(const char *entry, const char *conf_file);
1.1       misho      74: };
                     75: 
                     76: struct sudo_conf_paths {
                     77:     const char *pname;
                     78:     unsigned int pnamelen;
                     79:     const char *pval;
                     80: };
                     81: 
1.1.1.3   misho      82: static void set_debug(const char *entry, const char *conf_file);
                     83: static void set_path(const char *entry, const char *conf_file);
                     84: static void set_plugin(const char *entry, const char *conf_file);
                     85: static void set_variable(const char *entry, const char *conf_file);
                     86: static void set_var_disable_coredump(const char *entry, const char *conf_file);
                     87: static void set_var_group_source(const char *entry, const char *conf_file);
                     88: static void set_var_max_groups(const char *entry, const char *conf_file);
1.1.1.5 ! misho      89: static void set_var_probe_interfaces(const char *entry, const char *conf_file);
1.1.1.3   misho      90: 
                     91: static unsigned int conf_lineno;
1.1       misho      92: 
                     93: static struct sudo_conf_table sudo_conf_table[] = {
                     94:     { "Debug", sizeof("Debug") - 1, set_debug },
                     95:     { "Path", sizeof("Path") - 1, set_path },
                     96:     { "Plugin", sizeof("Plugin") - 1, set_plugin },
                     97:     { "Set", sizeof("Set") - 1, set_variable },
                     98:     { NULL }
                     99: };
                    100: 
1.1.1.3   misho     101: static struct sudo_conf_table sudo_conf_table_vars[] = {
                    102:     { "disable_coredump", sizeof("disable_coredump") - 1, set_var_disable_coredump },
                    103:     { "group_source", sizeof("group_source") - 1, set_var_group_source },
                    104:     { "max_groups", sizeof("max_groups") - 1, set_var_max_groups },
1.1.1.5 ! misho     105:     { "probe_interfaces", sizeof("probe_interfaces") - 1, set_var_probe_interfaces },
1.1.1.3   misho     106:     { NULL }
                    107: };
                    108: 
1.1       misho     109: static struct sudo_conf_data {
                    110:     bool disable_coredump;
1.1.1.5 ! misho     111:     bool probe_interfaces;
1.1.1.3   misho     112:     int group_source;
                    113:     int max_groups;
1.1       misho     114:     const char *debug_flags;
                    115:     struct plugin_info_list plugins;
1.1.1.5 ! misho     116:     struct sudo_conf_paths paths[5];
1.1       misho     117: } sudo_conf_data = {
                    118:     true,
1.1.1.5 ! misho     119:     true,
1.1.1.3   misho     120:     GROUP_SOURCE_ADAPTIVE,
                    121:     -1,
1.1       misho     122:     NULL,
1.1.1.5 ! misho     123:     TAILQ_HEAD_INITIALIZER(sudo_conf_data.plugins),
1.1       misho     124:     {
                    125: #define SUDO_CONF_ASKPASS_IDX  0
                    126:        { "askpass", sizeof("askpass") - 1, _PATH_SUDO_ASKPASS },
1.1.1.3   misho     127: #define SUDO_CONF_SESH_IDX     1
                    128:        { "sesh", sizeof("sesh") - 1, _PATH_SUDO_SESH },
1.1       misho     129: #ifdef _PATH_SUDO_NOEXEC
1.1.1.3   misho     130: #define SUDO_CONF_NOEXEC_IDX   2
1.1       misho     131:        { "noexec", sizeof("noexec") - 1, _PATH_SUDO_NOEXEC },
                    132: #endif
1.1.1.4   misho     133: #ifdef _PATH_SUDO_PLUGIN_DIR
                    134: #define SUDO_CONF_PLUGIN_IDX   3
                    135:        { "plugin", sizeof("plugin") - 1, _PATH_SUDO_PLUGIN_DIR },
                    136: #endif
1.1       misho     137:        { NULL }
                    138:     }
                    139: };
                    140: 
                    141: /*
                    142:  * "Set variable_name value"
                    143:  */
1.1.1.3   misho     144: static void
                    145: set_variable(const char *entry, const char *conf_file)
1.1       misho     146: {
1.1.1.3   misho     147:     struct sudo_conf_table *var;
                    148: 
                    149:     for (var = sudo_conf_table_vars; var->name != NULL; var++) {
                    150:        if (strncmp(entry, var->name, var->namelen) == 0 &&
                    151:            isblank((unsigned char)entry[var->namelen])) {
                    152:            entry += var->namelen + 1;
                    153:            while (isblank((unsigned char)*entry))
                    154:                entry++;
                    155:            var->setter(entry, conf_file);
                    156:            break;
                    157:        }
                    158:     }
                    159: }
                    160: 
                    161: static void
                    162: set_var_disable_coredump(const char *entry, const char *conf_file)
                    163: {
                    164:     int val = atobool(entry);
                    165: 
                    166:     if (val != -1)
                    167:        sudo_conf_data.disable_coredump = val;
                    168: }
                    169: 
                    170: static void
                    171: set_var_group_source(const char *entry, const char *conf_file)
                    172: {
                    173:     if (strcasecmp(entry, "adaptive") == 0) {
                    174:        sudo_conf_data.group_source = GROUP_SOURCE_ADAPTIVE;
                    175:     } else if (strcasecmp(entry, "static") == 0) {
                    176:        sudo_conf_data.group_source = GROUP_SOURCE_STATIC;
                    177:     } else if (strcasecmp(entry, "dynamic") == 0) {
                    178:        sudo_conf_data.group_source = GROUP_SOURCE_DYNAMIC;
                    179:     } else {
1.1.1.5 ! misho     180:        warningx(U_("unsupported group source `%s' in %s, line %d"), entry,
1.1.1.3   misho     181:            conf_file, conf_lineno);
                    182:     }
                    183: }
                    184: 
                    185: static void
                    186: set_var_max_groups(const char *entry, const char *conf_file)
                    187: {
1.1.1.5 ! misho     188:     int max_groups;
1.1.1.3   misho     189: 
1.1.1.5 ! misho     190:     max_groups = strtonum(entry, 1, INT_MAX, NULL);
        !           191:     if (max_groups > 0) {
        !           192:        sudo_conf_data.max_groups = max_groups;
1.1.1.3   misho     193:     } else {
1.1.1.5 ! misho     194:        warningx(U_("invalid max groups `%s' in %s, line %d"), entry,
        !           195:            conf_file, conf_lineno);
1.1       misho     196:     }
                    197: }
                    198: 
1.1.1.5 ! misho     199: static void
        !           200: set_var_probe_interfaces(const char *entry, const char *conf_file)
        !           201: {
        !           202:     int val = atobool(entry);
        !           203: 
        !           204:     if (val != -1)
        !           205:        sudo_conf_data.probe_interfaces = val;
        !           206: }
        !           207: 
1.1       misho     208: /*
                    209:  * "Debug progname debug_file debug_flags"
                    210:  */
1.1.1.3   misho     211: static void
                    212: set_debug(const char *entry, const char *conf_file)
1.1       misho     213: {
                    214:     size_t filelen, proglen;
                    215:     const char *progname;
                    216:     char *debug_file, *debug_flags;
                    217: 
                    218:     /* Is this debug setting for me? */
                    219:     progname = getprogname();
                    220:     if (strcmp(progname, "sudoedit") == 0)
                    221:        progname = "sudo";
                    222:     proglen = strlen(progname);
                    223:     if (strncmp(entry, progname, proglen) != 0 ||
                    224:        !isblank((unsigned char)entry[proglen]))
1.1.1.3   misho     225:        return;
1.1       misho     226:     entry += proglen + 1;
                    227:     while (isblank((unsigned char)*entry))
                    228:        entry++;
                    229: 
                    230:     debug_flags = strpbrk(entry, " \t");
                    231:     if (debug_flags == NULL)
1.1.1.3   misho     232:        return;
1.1       misho     233:     filelen = (size_t)(debug_flags - entry);
                    234:     while (isblank((unsigned char)*debug_flags))
                    235:        debug_flags++;
                    236: 
                    237:     /* Set debug file and parse the flags (init debug as soon as possible). */
                    238:     debug_file = estrndup(entry, filelen);
                    239:     debug_flags = estrdup(debug_flags);
                    240:     sudo_debug_init(debug_file, debug_flags);
                    241:     efree(debug_file);
                    242: 
                    243:     sudo_conf_data.debug_flags = debug_flags;
                    244: }
                    245: 
1.1.1.3   misho     246: static void
                    247: set_path(const char *entry, const char *conf_file)
1.1       misho     248: {
                    249:     const char *name, *path;
                    250:     struct sudo_conf_paths *cur;
                    251: 
                    252:     /* Parse Path line */
                    253:     name = entry;
                    254:     path = strpbrk(entry, " \t");
                    255:     if (path == NULL)
1.1.1.3   misho     256:        return;
1.1       misho     257:     while (isblank((unsigned char)*path))
                    258:        path++;
                    259: 
                    260:     /* Match supported paths, ignore the rest. */
                    261:     for (cur = sudo_conf_data.paths; cur->pname != NULL; cur++) {
                    262:        if (strncasecmp(name, cur->pname, cur->pnamelen) == 0 &&
                    263:            isblank((unsigned char)name[cur->pnamelen])) {
                    264:            cur->pval = estrdup(path);
                    265:            break;
                    266:        }
                    267:     }
                    268: }
                    269: 
1.1.1.3   misho     270: static void
                    271: set_plugin(const char *entry, const char *conf_file)
1.1       misho     272: {
                    273:     struct plugin_info *info;
                    274:     const char *name, *path, *cp, *ep;
                    275:     char **options = NULL;
                    276:     size_t namelen, pathlen;
                    277:     unsigned int nopts;
                    278: 
                    279:     /* Parse Plugin line */
                    280:     name = entry;
                    281:     path = strpbrk(entry, " \t");
                    282:     if (path == NULL)
1.1.1.3   misho     283:        return;
1.1       misho     284:     namelen = (size_t)(path - name);
                    285:     while (isblank((unsigned char)*path))
                    286:        path++;
                    287:     if ((cp = strpbrk(path, " \t")) != NULL) {
                    288:        /* Convert any options to an array. */
                    289:        pathlen = (size_t)(cp - path);
                    290:        while (isblank((unsigned char)*cp))
                    291:            cp++;
                    292:        /* Count number of options and allocate array. */
                    293:        for (ep = cp, nopts = 1; (ep = strpbrk(ep, " \t")) != NULL; nopts++) {
                    294:            while (isblank((unsigned char)*ep))
                    295:                ep++;
                    296:        }
                    297:        options = emalloc2(nopts + 1, sizeof(*options));
                    298:        /* Fill in options array, there is at least one element. */
                    299:        for (nopts = 0; (ep = strpbrk(cp, " \t")) != NULL; ) {
                    300:            options[nopts++] = estrndup(cp, (size_t)(ep - cp));
                    301:            while (isblank((unsigned char)*ep))
                    302:                ep++;
                    303:            cp = ep;
                    304:        }
                    305:        options[nopts++] = estrdup(cp);
                    306:        options[nopts] = NULL;
                    307:     } else {
                    308:        /* No extra options. */
                    309:        pathlen = strlen(path);
                    310:     }
                    311: 
                    312:     info = ecalloc(1, sizeof(*info));
                    313:     info->symbol_name = estrndup(name, namelen);
                    314:     info->path = estrndup(path, pathlen);
                    315:     info->options = options;
1.1.1.3   misho     316:     info->lineno = conf_lineno;
1.1.1.5 ! misho     317:     TAILQ_INSERT_TAIL(&sudo_conf_data.plugins, info, entries);
1.1       misho     318: }
                    319: 
                    320: const char *
                    321: sudo_conf_askpass_path(void)
                    322: {
                    323:     return sudo_conf_data.paths[SUDO_CONF_ASKPASS_IDX].pval;
                    324: }
                    325: 
1.1.1.3   misho     326: const char *
                    327: sudo_conf_sesh_path(void)
                    328: {
                    329:     return sudo_conf_data.paths[SUDO_CONF_SESH_IDX].pval;
                    330: }
                    331: 
1.1       misho     332: #ifdef _PATH_SUDO_NOEXEC
                    333: const char *
                    334: sudo_conf_noexec_path(void)
                    335: {
                    336:     return sudo_conf_data.paths[SUDO_CONF_NOEXEC_IDX].pval;
                    337: }
                    338: #endif
                    339: 
1.1.1.4   misho     340: #ifdef _PATH_SUDO_PLUGIN_DIR
                    341: const char *
                    342: sudo_conf_plugin_dir_path(void)
                    343: {
                    344:     return sudo_conf_data.paths[SUDO_CONF_PLUGIN_IDX].pval;
                    345: }
                    346: #endif
                    347: 
1.1       misho     348: const char *
                    349: sudo_conf_debug_flags(void)
                    350: {
                    351:     return sudo_conf_data.debug_flags;
                    352: }
                    353: 
1.1.1.3   misho     354: int
                    355: sudo_conf_group_source(void)
                    356: {
                    357:     return sudo_conf_data.group_source;
                    358: }
                    359: 
                    360: int
                    361: sudo_conf_max_groups(void)
                    362: {
                    363:     return sudo_conf_data.max_groups;
                    364: }
                    365: 
1.1       misho     366: struct plugin_info_list *
                    367: sudo_conf_plugins(void)
                    368: {
                    369:     return &sudo_conf_data.plugins;
                    370: }
                    371: 
                    372: bool
                    373: sudo_conf_disable_coredump(void)
                    374: {
                    375:     return sudo_conf_data.disable_coredump;
                    376: }
                    377: 
1.1.1.5 ! misho     378: bool
        !           379: sudo_conf_probe_interfaces(void)
        !           380: {
        !           381:     return sudo_conf_data.probe_interfaces;
        !           382: }
        !           383: 
1.1       misho     384: /*
                    385:  * Reads in /etc/sudo.conf and populates sudo_conf_data.
                    386:  */
                    387: void
1.1.1.3   misho     388: sudo_conf_read(const char *conf_file)
1.1       misho     389: {
                    390:     struct sudo_conf_table *cur;
                    391:     struct stat sb;
                    392:     FILE *fp;
1.1.1.3   misho     393:     char *cp, *line = NULL;
                    394:     char *prev_locale = estrdup(setlocale(LC_ALL, NULL));
                    395:     size_t linesize = 0;
                    396: 
                    397:     /* Parse sudo.conf in the "C" locale. */
                    398:     if (prev_locale[0] != 'C' || prev_locale[1] != '\0')
                    399:         setlocale(LC_ALL, "C");
                    400: 
                    401:     if (conf_file == NULL) {
                    402:        conf_file = _PATH_SUDO_CONF;
                    403:        switch (sudo_secure_file(conf_file, ROOT_UID, -1, &sb)) {
                    404:            case SUDO_PATH_SECURE:
                    405:                break;
                    406:            case SUDO_PATH_MISSING:
                    407:                /* Root should always be able to read sudo.conf. */
                    408:                if (errno != ENOENT && geteuid() == ROOT_UID)
1.1.1.5 ! misho     409:                    warning(U_("unable to stat %s"), conf_file);
1.1.1.3   misho     410:                goto done;
                    411:            case SUDO_PATH_BAD_TYPE:
1.1.1.5 ! misho     412:                warningx(U_("%s is not a regular file"), conf_file);
1.1.1.3   misho     413:                goto done;
                    414:            case SUDO_PATH_WRONG_OWNER:
1.1.1.5 ! misho     415:                warningx(U_("%s is owned by uid %u, should be %u"),
1.1.1.3   misho     416:                    conf_file, (unsigned int) sb.st_uid, ROOT_UID);
                    417:                goto done;
                    418:            case SUDO_PATH_WORLD_WRITABLE:
1.1.1.5 ! misho     419:                warningx(U_("%s is world writable"), conf_file);
1.1.1.3   misho     420:                goto done;
                    421:            case SUDO_PATH_GROUP_WRITABLE:
1.1.1.5 ! misho     422:                warningx(U_("%s is group writable"), conf_file);
1.1.1.3   misho     423:                goto done;
                    424:            default:
                    425:                /* NOTREACHED */
                    426:                goto done;
                    427:        }
1.1       misho     428:     }
                    429: 
1.1.1.3   misho     430:     if ((fp = fopen(conf_file, "r")) == NULL) {
1.1       misho     431:        if (errno != ENOENT && geteuid() == ROOT_UID)
1.1.1.5 ! misho     432:            warning(U_("unable to open %s"), conf_file);
1.1       misho     433:        goto done;
                    434:     }
                    435: 
1.1.1.3   misho     436:     conf_lineno = 0;
                    437:     while (sudo_parseln(&line, &linesize, &conf_lineno, fp) != -1) {
                    438:        if (*(cp = line) == '\0')
                    439:            continue;           /* empty line or comment */
1.1       misho     440: 
                    441:        for (cur = sudo_conf_table; cur->name != NULL; cur++) {
                    442:            if (strncasecmp(cp, cur->name, cur->namelen) == 0 &&
                    443:                isblank((unsigned char)cp[cur->namelen])) {
                    444:                cp += cur->namelen;
                    445:                while (isblank((unsigned char)*cp))
                    446:                    cp++;
1.1.1.3   misho     447:                cur->setter(cp, conf_file);
                    448:                break;
1.1       misho     449:            }
                    450:        }
                    451:     }
                    452:     fclose(fp);
1.1.1.3   misho     453:     free(line);
1.1       misho     454: done:
1.1.1.3   misho     455:     /* Restore locale if needed. */
                    456:     if (prev_locale[0] != 'C' || prev_locale[1] != '\0')
                    457:         setlocale(LC_ALL, prev_locale);
                    458:     efree(prev_locale);
1.1       misho     459: }

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