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

Annotation of src/usr.bin/sudo/ldap.c, Revision 1.8

1.1       millert     1: /*
1.6       millert     2:  * Copyright (c) 2003-2008 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       millert     3:  *
                      4:  * This code is derived from software contributed by Aaron Spangler.
                      5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     15:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     16:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
                     18:
                     19: #include <config.h>
                     20:
                     21: #include <sys/types.h>
                     22: #include <sys/time.h>
                     23: #include <sys/param.h>
                     24: #include <sys/stat.h>
                     25: #include <stdio.h>
                     26: #ifdef STDC_HEADERS
                     27: # include <stdlib.h>
                     28: # include <stddef.h>
                     29: #else
                     30: # ifdef HAVE_STDLIB_H
                     31: #  include <stdlib.h>
                     32: # endif
                     33: #endif /* STDC_HEADERS */
                     34: #ifdef HAVE_STRING_H
                     35: # include <string.h>
                     36: #else
                     37: # ifdef HAVE_STRINGS_H
                     38: #  include <strings.h>
                     39: # endif
                     40: #endif /* HAVE_STRING_H */
                     41: #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
                     42: # include <malloc.h>
                     43: #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
                     44: #ifdef HAVE_UNISTD_H
                     45: # include <unistd.h>
                     46: #endif /* HAVE_UNISTD_H */
                     47: #include <ctype.h>
1.6       millert    48: #include <limits.h>
1.1       millert    49: #include <pwd.h>
                     50: #include <grp.h>
                     51: #include <netinet/in.h>
                     52: #include <arpa/inet.h>
                     53: #include <netdb.h>
                     54: #ifdef HAVE_ERR_H
                     55: # include <err.h>
                     56: #else
                     57: # include "emul/err.h"
                     58: #endif /* HAVE_ERR_H */
                     59: #include <errno.h>
                     60: #ifdef HAVE_LBER_H
                     61: # include <lber.h>
                     62: #endif
                     63: #include <ldap.h>
1.7       millert    64: #if defined(HAVE_LDAP_SSL_H)
                     65: # include <ldap_ssl.h>
                     66: #elif defined(HAVE_MPS_LDAP_SSL_H)
                     67: # include <mps/ldap_ssl.h>
                     68: #endif
1.1       millert    69:
                     70: #include "sudo.h"
                     71: #include "parse.h"
                     72:
                     73: #ifndef lint
1.8     ! millert    74: __unused static const char rcsid[] = "$Sudo: ldap.c,v 1.11.2.38 2008/04/11 14:03:51 millert Exp $";
1.1       millert    75: #endif /* lint */
                     76:
                     77: #ifndef LINE_MAX
                     78: # define LINE_MAX 2048
                     79: #endif
                     80:
                     81: #ifndef LDAP_OPT_SUCCESS
                     82: # define LDAP_OPT_SUCCESS LDAP_SUCCESS
                     83: #endif
                     84:
1.8     ! millert    85: #ifndef LDAPS_PORT
        !            86: # define LDAPS_PORT 636
        !            87: #endif
        !            88:
1.5       millert    89: #define        DPRINTF(args, level)    if (ldap_conf.debug >= level) warnx args
1.1       millert    90:
1.5       millert    91: #define CONF_BOOL      0
                     92: #define CONF_INT       1
                     93: #define CONF_STR       2
                     94:
                     95: #define SUDO_LDAP_SSL          1
                     96: #define SUDO_LDAP_STARTTLS     2
                     97:
                     98: struct ldap_config_table {
                     99:     const char *conf_str;      /* config file string */
                    100:     short type;                        /* CONF_BOOL, CONF_INT, CONF_STR */
                    101:     short connected;           /* connection-specific value? */
                    102:     int opt_val;               /* LDAP_OPT_* (or -1 for sudo internal) */
                    103:     void *valp;                        /* pointer into ldap_conf */
                    104: };
1.1       millert   105:
                    106: /* ldap configuration structure */
                    107: struct ldap_config {
                    108:     int port;
                    109:     int version;
                    110:     int debug;
1.5       millert   111:     int ldap_debug;
1.1       millert   112:     int tls_checkpeer;
                    113:     int timelimit;
                    114:     int bind_timelimit;
1.5       millert   115:     int ssl_mode;
1.1       millert   116:     char *host;
                    117:     char *uri;
                    118:     char *binddn;
                    119:     char *bindpw;
                    120:     char *rootbinddn;
                    121:     char *base;
                    122:     char *ssl;
                    123:     char *tls_cacertfile;
                    124:     char *tls_cacertdir;
                    125:     char *tls_random_file;
                    126:     char *tls_cipher_suite;
                    127:     char *tls_certfile;
                    128:     char *tls_keyfile;
                    129: } ldap_conf;
                    130:
1.5       millert   131: struct ldap_config_table ldap_conf_table[] = {
                    132:     { "sudoers_debug", CONF_INT, FALSE, -1, &ldap_conf.debug },
                    133:     { "host", CONF_STR, FALSE, -1, &ldap_conf.host },
                    134:     { "port", CONF_INT, FALSE, -1, &ldap_conf.port },
                    135:     { "ssl", CONF_STR, FALSE, -1, &ldap_conf.ssl },
                    136:     { "sslpath", CONF_STR, FALSE, -1, &ldap_conf.tls_certfile },
                    137:     { "uri", CONF_STR, FALSE, -1, &ldap_conf.uri },
                    138: #ifdef LDAP_OPT_DEBUG_LEVEL
                    139:     { "debug", CONF_INT, FALSE, LDAP_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug },
                    140: #endif
                    141: #ifdef LDAP_OPT_PROTOCOL_VERSION
                    142:     { "ldap_version", CONF_INT, TRUE, LDAP_OPT_PROTOCOL_VERSION,
                    143:        &ldap_conf.version },
                    144: #endif
                    145: #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
                    146:     { "tls_checkpeer", CONF_BOOL, FALSE, LDAP_OPT_X_TLS_REQUIRE_CERT,
                    147:        &ldap_conf.tls_checkpeer },
1.7       millert   148: #else
                    149:     { "tls_checkpeer", CONF_BOOL, FALSE, -1, &ldap_conf.tls_checkpeer },
1.5       millert   150: #endif
                    151: #ifdef LDAP_OPT_X_TLS_CACERTFILE
                    152:     { "tls_cacertfile", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTFILE,
                    153:        &ldap_conf.tls_cacertfile },
                    154: #endif
                    155: #ifdef LDAP_OPT_X_TLS_CACERTDIR
                    156:     { "tls_cacertdir", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTDIR,
                    157:        &ldap_conf.tls_cacertdir },
                    158: #endif
                    159: #ifdef LDAP_OPT_X_TLS_RANDOM_FILE
                    160:     { "tls_randfile", CONF_STR, FALSE, LDAP_OPT_X_TLS_RANDOM_FILE,
                    161:        &ldap_conf.tls_random_file },
                    162: #endif
                    163: #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
                    164:     { "tls_ciphers", CONF_STR, FALSE, LDAP_OPT_X_TLS_CIPHER_SUITE,
                    165:        &ldap_conf.tls_cipher_suite },
                    166: #endif
                    167: #ifdef LDAP_OPT_X_TLS_CERTFILE
                    168:     { "tls_cert", CONF_STR, FALSE, LDAP_OPT_X_TLS_CERTFILE,
                    169:        &ldap_conf.tls_certfile },
                    170: #else
                    171:     { "tls_cert", CONF_STR, FALSE, -1, &ldap_conf.tls_certfile },
                    172: #endif
                    173: #ifdef LDAP_OPT_X_TLS_KEYFILE
                    174:     { "tls_key", CONF_STR, FALSE, LDAP_OPT_X_TLS_KEYFILE,
                    175:        &ldap_conf.tls_keyfile },
                    176: #else
                    177:     { "tls_key", CONF_STR, FALSE, -1, &ldap_conf.tls_keyfile },
                    178: #endif
                    179: #ifdef LDAP_OPT_NETWORK_TIMEOUT
                    180:     { "bind_timelimit", CONF_INT, TRUE, -1 /* needs timeval, set manually */,
                    181:        &ldap_conf.bind_timelimit },
                    182: #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
                    183:     { "bind_timelimit", CONF_INT, TRUE, LDAP_X_OPT_CONNECT_TIMEOUT,
                    184:        &ldap_conf.bind_timelimit },
                    185: #endif
                    186:     { "timelimit", CONF_INT, TRUE, LDAP_OPT_TIMELIMIT, &ldap_conf.timelimit },
                    187:     { "binddn", CONF_STR, FALSE, -1, &ldap_conf.binddn },
                    188:     { "bindpw", CONF_STR, FALSE, -1, &ldap_conf.bindpw },
                    189:     { "rootbinddn", CONF_STR, FALSE, -1, &ldap_conf.rootbinddn },
                    190:     { "sudoers_base", CONF_STR, FALSE, -1, &ldap_conf.base },
                    191:     { NULL }
                    192: };
                    193:
1.1       millert   194: static void sudo_ldap_update_defaults __P((LDAP *));
                    195: static void sudo_ldap_close __P((LDAP *));
                    196: static LDAP *sudo_ldap_open __P((void));
                    197:
1.6       millert   198: #ifndef HAVE_LDAP_INITIALIZE
                    199: /*
                    200:  * For each uri, convert to host:port pairs.  For ldaps:// enable SSL
                    201:  * Accepts: uris of the form ldap:/// or ldap://hostname:portnum/
                    202:  * where the trailing slash is optional.
                    203:  */
                    204: static int
                    205: sudo_ldap_parse_uri(uri_list)
                    206:     const char *uri_list;
                    207: {
                    208:     char *buf, *uri, *host, *cp, *port;
                    209:     char hostbuf[LINE_MAX];
                    210:     int nldap = 0, nldaps = 0;
                    211:     int rc = -1;
                    212:
                    213:     buf = estrdup(uri_list);
                    214:     hostbuf[0] = '\0';
                    215:     for ((uri = strtok(buf, " \t")); uri != NULL; (uri = strtok(NULL, " \t"))) {
                    216:        if (strncasecmp(uri, "ldap://", 7) == 0) {
                    217:            nldap++;
                    218:            host = uri + 7;
                    219:        } else if (strncasecmp(uri, "ldaps://", 8) == 0) {
                    220:            nldaps++;
                    221:            host = uri + 8;
                    222:        } else {
                    223:            warnx("unsupported LDAP uri type: %s", uri);
                    224:            goto done;
                    225:        }
                    226:
                    227:        /* trim optional trailing slash */
                    228:        if ((cp = strrchr(host, '/')) != NULL && cp[1] == '\0') {
                    229:            *cp = '\0';
                    230:        }
                    231:
                    232:        if (hostbuf[0] != '\0') {
                    233:            if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
                    234:                goto toobig;
                    235:        }
                    236:
                    237:        if (*host == '\0')
                    238:            host = "localhost";         /* no host specified, use localhost */
                    239:
                    240:        if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
                    241:            goto toobig;
                    242:
                    243:        /* If using SSL and no port specified, add port 636 */
                    244:        if (nldaps) {
                    245:            if ((port = strrchr(host, ':')) == NULL || !isdigit(port[1]))
                    246:                if (strlcat(hostbuf, ":636", sizeof(hostbuf)) >= sizeof(hostbuf))
                    247:                    goto toobig;
                    248:        }
                    249:     }
                    250:     if (hostbuf[0] == '\0') {
                    251:        warnx("invalid uri: %s", uri_list);
                    252:        goto done;
                    253:     }
                    254:
                    255:     if (nldaps != 0) {
                    256:        if (nldap != 0) {
                    257:            warnx("cannot mix ldap and ldaps URIs");
                    258:            goto done;
                    259:        }
                    260:        if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
                    261:            warnx("cannot mix ldaps and starttls");
                    262:            goto done;
                    263:        }
                    264:        ldap_conf.ssl_mode = SUDO_LDAP_SSL;
                    265:     }
                    266:
                    267:     free(ldap_conf.host);
                    268:     ldap_conf.host = estrdup(hostbuf);
                    269:     rc = 0;
                    270:
                    271: done:
                    272:     efree(buf);
                    273:     return(rc);
                    274:
                    275: toobig:
                    276:     errx(1, "sudo_ldap_parse_uri: out of space building hostbuf");
                    277: }
                    278: #endif /* HAVE_LDAP_INITIALIZE */
                    279:
                    280: static int
                    281: sudo_ldap_init(ldp, host, port)
                    282:     LDAP **ldp;
                    283:     const char *host;
                    284:     int port;
                    285: {
                    286:     LDAP *ld = NULL;
                    287:     int rc = LDAP_CONNECT_ERROR;
                    288:
                    289: #ifdef HAVE_LDAPSSL_INIT
                    290:     if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
                    291:        DPRINTF(("ldapssl_clientauth_init(%s, %s)",
                    292:            ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
                    293:            ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
                    294:        rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
                    295:            ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
                    296:        if (rc != LDAP_SUCCESS) {
                    297:            warnx("unable to initialize SSL cert and key db: %s",
                    298:                ldapssl_err2string(rc));
                    299:            goto done;
                    300:        }
                    301:
                    302:        DPRINTF(("ldapssl_init(%s, %d, 1)", host, port), 2);
                    303:        if ((ld = ldapssl_init(host, port, 1)) == NULL)
                    304:            goto done;
                    305:     } else
                    306: #endif
                    307:     {
                    308:        DPRINTF(("ldap_init(%s, %d)", host, port), 2);
                    309:        if ((ld = ldap_init(host, port)) == NULL)
                    310:            goto done;
                    311:     }
                    312:     rc = LDAP_SUCCESS;
                    313:
                    314: done:
                    315:     *ldp = ld;
                    316:     return(rc);
                    317: }
                    318:
1.1       millert   319: /*
                    320:  * Walk through search results and return TRUE if we have a matching
                    321:  * netgroup, else FALSE.
                    322:  */
                    323: int
                    324: sudo_ldap_check_user_netgroup(ld, entry)
                    325:     LDAP *ld;
                    326:     LDAPMessage *entry;
                    327: {
                    328:     char **v = NULL, **p = NULL;
                    329:     int ret = FALSE;
                    330:
                    331:     if (!entry)
                    332:        return(ret);
                    333:
                    334:     /* get the values from the entry */
                    335:     v = ldap_get_values(ld, entry, "sudoUser");
                    336:
                    337:     /* walk through values */
                    338:     for (p = v; p && *p && !ret; p++) {
                    339:        /* match any */
                    340:        if (netgr_matches(*p, NULL, NULL, user_name))
                    341:            ret = TRUE;
                    342:        DPRINTF(("ldap sudoUser netgroup '%s' ... %s", *p,
                    343:            ret ? "MATCH!" : "not"), 2);
                    344:     }
                    345:
                    346:     if (v)
                    347:        ldap_value_free(v);     /* cleanup */
                    348:
                    349:     return(ret);
                    350: }
                    351:
                    352: /*
                    353:  * Walk through search results and return TRUE if we have a
                    354:  * host match, else FALSE.
                    355:  */
                    356: int
                    357: sudo_ldap_check_host(ld, entry)
                    358:     LDAP *ld;
                    359:     LDAPMessage *entry;
                    360: {
                    361:     char **v = NULL, **p = NULL;
                    362:     int ret = FALSE;
                    363:
                    364:     if (!entry)
                    365:        return(ret);
                    366:
                    367:     /* get the values from the entry */
                    368:     v = ldap_get_values(ld, entry, "sudoHost");
                    369:
                    370:     /* walk through values */
                    371:     for (p = v; p && *p && !ret; p++) {
                    372:        /* match any or address or netgroup or hostname */
1.4       millert   373:        if (!strcmp(*p, "ALL") || addr_matches(*p) ||
1.1       millert   374:            netgr_matches(*p, user_host, user_shost, NULL) ||
                    375:            !hostname_matches(user_shost, user_host, *p))
                    376:            ret = TRUE;
                    377:        DPRINTF(("ldap sudoHost '%s' ... %s", *p,
                    378:            ret ? "MATCH!" : "not"), 2);
                    379:     }
                    380:
                    381:     if (v)
                    382:        ldap_value_free(v);     /* cleanup */
                    383:
                    384:     return(ret);
                    385: }
                    386:
                    387: /*
                    388:  * Walk through search results and return TRUE if we have a runas match,
                    389:  * else FALSE.
                    390:  * Since the runas directive in /etc/sudoers is optional, so is sudoRunAs.
                    391:  */
                    392: int
                    393: sudo_ldap_check_runas(ld, entry)
                    394:     LDAP *ld;
                    395:     LDAPMessage *entry;
                    396: {
                    397:     char **v = NULL, **p = NULL;
                    398:     int ret = FALSE;
                    399:
                    400:     if (!entry)
                    401:        return(ret);
                    402:
                    403:     /* get the values from the entry */
                    404:     v = ldap_get_values(ld, entry, "sudoRunAs");
                    405:
                    406:     /*
                    407:      * BUG:
                    408:      *
                    409:      * if runas is not specified on the command line, the only information
                    410:      * as to which user to run as is in the runas_default option.  We should
                    411:      * check to see if we have the local option present.  Unfortunately we
                    412:      * don't parse these options until after this routine says yes or no.
                    413:      * The query has already returned, so we could peek at the attribute
                    414:      * values here though.
                    415:      *
                    416:      * For now just require users to always use -u option unless its set
                    417:      * in the global defaults. This behaviour is no different than the global
                    418:      * /etc/sudoers.
                    419:      *
                    420:      * Sigh - maybe add this feature later
                    421:      *
                    422:      */
                    423:
                    424:     /*
                    425:      * If there are no runas entries, match runas_default against
                    426:      * what the user specified on the command line.
                    427:      */
                    428:     if (!v)
1.2       millert   429:        ret = !strcasecmp(runas_pw->pw_name, def_runas_default);
1.1       millert   430:
                    431:     /* walk through values returned, looking for a match */
                    432:     for (p = v; p && *p && !ret; p++) {
1.2       millert   433:        switch (*p[0]) {
                    434:        case '+':
                    435:            if (netgr_matches(*p, NULL, NULL, runas_pw->pw_name))
                    436:                ret = TRUE;
                    437:            break;
                    438:        case '%':
                    439:            if (usergr_matches(*p, runas_pw->pw_name, runas_pw))
                    440:                ret = TRUE;
                    441:            break;
                    442:        case 'A':
                    443:            if (strcmp(*p, "ALL") == 0) {
                    444:                ret = TRUE;
                    445:                break;
                    446:            }
                    447:            /* FALLTHROUGH */
                    448:        default:
                    449:            if (strcasecmp(*p, runas_pw->pw_name) == 0)
                    450:                ret = TRUE;
                    451:            break;
                    452:        }
1.1       millert   453:        DPRINTF(("ldap sudoRunAs '%s' ... %s", *p,
                    454:            ret ? "MATCH!" : "not"), 2);
                    455:     }
                    456:
                    457:     if (v)
                    458:        ldap_value_free(v);     /* cleanup */
                    459:
                    460:     return(ret);
                    461: }
                    462:
                    463: /*
                    464:  * Walk through search results and return TRUE if we have a command match.
                    465:  */
                    466: int
1.4       millert   467: sudo_ldap_check_command(ld, entry, setenv_implied)
1.1       millert   468:     LDAP *ld;
                    469:     LDAPMessage *entry;
1.4       millert   470:     int *setenv_implied;
1.1       millert   471: {
                    472:     char *allowed_cmnd, *allowed_args, **v = NULL, **p = NULL;
                    473:     int foundbang, ret = FALSE;
                    474:
                    475:     if (!entry)
                    476:        return(ret);
                    477:
                    478:     v = ldap_get_values(ld, entry, "sudoCommand");
                    479:
                    480:     /* get_first_entry */
                    481:     for (p = v; p && *p && ret >= 0; p++) {
                    482:        /* Match against ALL ? */
1.4       millert   483:        if (!strcmp(*p, "ALL")) {
1.1       millert   484:            ret = TRUE;
1.4       millert   485:            if (setenv_implied != NULL)
                    486:                *setenv_implied = TRUE;
1.1       millert   487:            DPRINTF(("ldap sudoCommand '%s' ... MATCH!", *p), 2);
                    488:            continue;
                    489:        }
                    490:
                    491:        /* check for !command */
                    492:        if (**p == '!') {
                    493:            foundbang = TRUE;
                    494:            allowed_cmnd = estrdup(1 + *p);     /* !command */
                    495:        } else {
                    496:            foundbang = FALSE;
                    497:            allowed_cmnd = estrdup(*p);         /* command */
                    498:        }
                    499:
                    500:        /* split optional args away from command */
                    501:        allowed_args = strchr(allowed_cmnd, ' ');
                    502:        if (allowed_args)
                    503:            *allowed_args++ = '\0';
                    504:
                    505:        /* check the command like normal */
                    506:        if (command_matches(allowed_cmnd, allowed_args)) {
                    507:            /*
                    508:             * If allowed (no bang) set ret but keep on checking.
                    509:             * If disallowed (bang), exit loop.
                    510:             */
                    511:            ret = foundbang ? -1 : TRUE;
                    512:        }
                    513:        DPRINTF(("ldap sudoCommand '%s' ... %s", *p,
                    514:            ret == TRUE ? "MATCH!" : "not"), 2);
                    515:
                    516:        efree(allowed_cmnd);    /* cleanup */
                    517:     }
                    518:
                    519:     if (v)
                    520:        ldap_value_free(v);     /* more cleanup */
                    521:
                    522:     /* return TRUE if we found at least one ALLOW and no DENY */
                    523:     return(ret > 0);
                    524: }
                    525:
                    526: /*
                    527:  * Read sudoOption and modify the defaults as we go.  This is used once
                    528:  * from the cn=defaults entry and also once when a final sudoRole is matched.
                    529:  */
                    530: void
                    531: sudo_ldap_parse_options(ld, entry)
                    532:     LDAP *ld;
                    533:     LDAPMessage *entry;
                    534: {
                    535:     char op, *var, *val, **v = NULL, **p = NULL;
                    536:
                    537:     if (!entry)
                    538:        return;
                    539:
                    540:     v = ldap_get_values(ld, entry, "sudoOption");
                    541:
                    542:     /* walk through options */
                    543:     for (p = v; p && *p; p++) {
                    544:
                    545:        DPRINTF(("ldap sudoOption: '%s'", *p), 2);
                    546:        var = estrdup(*p);
                    547:
                    548:        /* check for equals sign past first char */
                    549:        val = strchr(var, '=');
                    550:        if (val > var) {
                    551:            *val++ = '\0';      /* split on = and truncate var */
                    552:            op = *(val - 2);    /* peek for += or -= cases */
                    553:            if (op == '+' || op == '-') {
                    554:                *(val - 2) = '\0';      /* found, remove extra char */
                    555:                /* case var+=val or var-=val */
                    556:                set_default(var, val, (int) op);
                    557:            } else {
                    558:                /* case var=val */
                    559:                set_default(var, val, TRUE);
                    560:            }
                    561:        } else if (*var == '!') {
                    562:            /* case !var Boolean False */
                    563:            set_default(var + 1, NULL, FALSE);
                    564:        } else {
                    565:            /* case var Boolean True */
                    566:            set_default(var, NULL, TRUE);
                    567:        }
                    568:        efree(var);
                    569:     }
                    570:
                    571:     if (v)
                    572:        ldap_value_free(v);
                    573: }
                    574:
                    575: /*
                    576:  * Concatenate strings, dynamically growing them as necessary.
                    577:  * Strings can be arbitrarily long and are allocated/reallocated on
                    578:  * the fly.  Make sure to free them when you are done.
                    579:  *
                    580:  * Usage:
                    581:  *
                    582:  * char *s=NULL;
                    583:  * size_t sz;
                    584:  *
                    585:  * ncat(&s,&sz,"This ");
                    586:  * ncat(&s,&sz,"is ");
                    587:  * ncat(&s,&sz,"an ");
                    588:  * ncat(&s,&sz,"arbitrarily ");
                    589:  * ncat(&s,&sz,"long ");
                    590:  * ncat(&s,&sz,"string!");
                    591:  *
                    592:  * printf("String Value='%s', but has %d bytes allocated\n",s,sz);
                    593:  *
                    594:  */
                    595: void
                    596: ncat(s, sz, src)
                    597:     char **s;
                    598:     size_t *sz;
                    599:     char *src;
                    600: {
                    601:     size_t nsz;
                    602:
                    603:     /* handle initial alloc */
                    604:     if (*s == NULL) {
                    605:        *s = estrdup(src);
                    606:        *sz = strlen(src) + 1;
                    607:        return;
                    608:     }
                    609:     /* handle realloc */
                    610:     nsz = strlen(*s) + strlen(src) + 1;
                    611:     if (*sz < nsz)
                    612:        *s = erealloc((void *) *s, *sz = nsz * 2);
                    613:     strlcat(*s, src, *sz);
                    614: }
                    615:
                    616: /*
                    617:  * builds together a filter to check against ldap
                    618:  */
                    619: char *
                    620: sudo_ldap_build_pass1()
                    621: {
                    622:     struct group *grp;
                    623:     size_t sz;
                    624:     char *b = NULL;
                    625:     int i;
                    626:
                    627:     /* global OR */
                    628:     ncat(&b, &sz, "(|");
                    629:
                    630:     /* build filter sudoUser=user_name */
                    631:     ncat(&b, &sz, "(sudoUser=");
                    632:     ncat(&b, &sz, user_name);
                    633:     ncat(&b, &sz, ")");
                    634:
                    635:     /* Append primary group */
                    636:     grp = getgrgid(user_gid);
                    637:     if (grp != NULL) {
                    638:        ncat(&b, &sz, "(sudoUser=%");
                    639:        ncat(&b, &sz, grp -> gr_name);
                    640:        ncat(&b, &sz, ")");
                    641:     }
                    642:
                    643:     /* Append supplementary groups */
                    644:     for (i = 0; i < user_ngroups; i++) {
1.6       millert   645:        if (user_groups[i] == user_gid)
                    646:            continue;
1.1       millert   647:        if ((grp = getgrgid(user_groups[i])) != NULL) {
                    648:            ncat(&b, &sz, "(sudoUser=%");
                    649:            ncat(&b, &sz, grp -> gr_name);
                    650:            ncat(&b, &sz, ")");
                    651:        }
                    652:     }
                    653:
                    654:     /* Add ALL to list */
                    655:     ncat(&b, &sz, "(sudoUser=ALL)");
                    656:
                    657:     /* End of OR List */
                    658:     ncat(&b, &sz, ")");
                    659:
                    660:     return(b);
                    661: }
                    662:
                    663: /*
                    664:  * Map yes/true/on to TRUE, no/false/off to FALSE, else -1
                    665:  */
                    666: int
                    667: _atobool(s)
                    668:     const char *s;
                    669: {
                    670:     switch (*s) {
                    671:        case 'y':
                    672:        case 'Y':
                    673:            if (strcasecmp(s, "yes") == 0)
                    674:                return(TRUE);
                    675:            break;
                    676:        case 't':
                    677:        case 'T':
                    678:            if (strcasecmp(s, "true") == 0)
                    679:                return(TRUE);
                    680:            break;
                    681:        case 'o':
                    682:        case 'O':
                    683:            if (strcasecmp(s, "on") == 0)
                    684:                return(TRUE);
                    685:            if (strcasecmp(s, "off") == 0)
                    686:                return(FALSE);
                    687:            break;
                    688:        case 'n':
                    689:        case 'N':
                    690:            if (strcasecmp(s, "no") == 0)
                    691:                return(FALSE);
                    692:            break;
                    693:        case 'f':
                    694:        case 'F':
                    695:            if (strcasecmp(s, "false") == 0)
                    696:                return(FALSE);
                    697:            break;
                    698:     }
                    699:     return(-1);
                    700: }
                    701:
                    702: int
                    703: sudo_ldap_read_config()
                    704: {
                    705:     FILE *f;
                    706:     char buf[LINE_MAX], *c, *keyword, *value;
1.5       millert   707:     struct ldap_config_table *cur;
1.1       millert   708:
                    709:     /* defaults */
                    710:     ldap_conf.version = 3;
1.5       millert   711:     ldap_conf.port = -1;
1.1       millert   712:     ldap_conf.tls_checkpeer = -1;
                    713:     ldap_conf.timelimit = -1;
                    714:     ldap_conf.bind_timelimit = -1;
                    715:
                    716:     if ((f = fopen(_PATH_LDAP_CONF, "r")) == NULL)
                    717:        return(FALSE);
1.5       millert   718:
1.1       millert   719:     while (fgets(buf, sizeof(buf), f)) {
                    720:        /* ignore text after comment character */
                    721:        if ((c = strchr(buf, '#')) != NULL)
                    722:            *c = '\0';
                    723:
                    724:        /* skip leading whitespace */
                    725:        for (c = buf; isspace((unsigned char) *c); c++)
                    726:            /* nothing */;
                    727:
                    728:        if (*c == '\0' || *c == '\n')
                    729:            continue;           /* skip empty line */
                    730:
                    731:        /* properly terminate keyword string */
                    732:        keyword = c;
                    733:        while (*c && !isspace((unsigned char) *c))
                    734:            c++;
                    735:        if (*c)
                    736:            *c++ = '\0';        /* terminate keyword */
                    737:
                    738:        /* skip whitespace before value */
                    739:        while (isspace((unsigned char) *c))
                    740:            c++;
                    741:        value = c;
                    742:
                    743:        /* trim whitespace after value */
                    744:        while (*c)
                    745:            c++;                /* wind to end */
                    746:        while (--c > value && isspace((unsigned char) *c))
                    747:            *c = '\0';
                    748:
1.5       millert   749:        /* Look up keyword in config table. */
                    750:        for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
                    751:            if (strcasecmp(keyword, cur->conf_str) == 0) {
                    752:                switch (cur->type) {
                    753:                case CONF_BOOL:
                    754:                    *(int *)(cur->valp) = _atobool(value);
                    755:                    break;
                    756:                case CONF_INT:
                    757:                    *(int *)(cur->valp) = atoi(value);
                    758:                    break;
                    759:                case CONF_STR:
                    760:                    efree(*(char **)(cur->valp));
                    761:                    *(char **)(cur->valp) = estrdup(value);
                    762:                    break;
                    763:                }
                    764:                break;
                    765:            }
1.1       millert   766:        }
                    767:     }
                    768:     fclose(f);
                    769:
                    770:     if (!ldap_conf.host)
1.5       millert   771:        ldap_conf.host = "localhost";
1.1       millert   772:
                    773:     if (ldap_conf.bind_timelimit > 0)
                    774:        ldap_conf.bind_timelimit *= 1000;       /* convert to ms */
                    775:
                    776:     if (ldap_conf.debug > 1) {
                    777:        fprintf(stderr, "LDAP Config Summary\n");
                    778:        fprintf(stderr, "===================\n");
                    779:        if (ldap_conf.uri) {
                    780:            fprintf(stderr, "uri          %s\n", ldap_conf.uri);
1.6       millert   781:        } else {
1.1       millert   782:            fprintf(stderr, "host         %s\n", ldap_conf.host ?
                    783:                ldap_conf.host : "(NONE)");
                    784:            fprintf(stderr, "port         %d\n", ldap_conf.port);
                    785:        }
                    786:        fprintf(stderr, "ldap_version %d\n", ldap_conf.version);
                    787:
                    788:        fprintf(stderr, "sudoers_base %s\n", ldap_conf.base ?
                    789:            ldap_conf.base : "(NONE) <---Sudo will ignore ldap)");
                    790:        fprintf(stderr, "binddn       %s\n", ldap_conf.binddn ?
                    791:            ldap_conf.binddn : "(anonymous)");
                    792:        fprintf(stderr, "bindpw       %s\n", ldap_conf.bindpw ?
                    793:            ldap_conf.bindpw : "(anonymous)");
1.5       millert   794:        if (ldap_conf.bind_timelimit > 0)
                    795:            fprintf(stderr, "bind_timelimit  %d\n", ldap_conf.bind_timelimit);
                    796:        if (ldap_conf.timelimit > 0)
                    797:            fprintf(stderr, "timelimit    %d\n", ldap_conf.timelimit);
1.1       millert   798:        fprintf(stderr, "ssl          %s\n", ldap_conf.ssl ?
                    799:            ldap_conf.ssl : "(no)");
1.5       millert   800:        if (ldap_conf.tls_checkpeer != -1)
                    801:            fprintf(stderr, "tls_checkpeer    %s\n", ldap_conf.tls_checkpeer ?
                    802:                "(yes)" : "(no)");
                    803:        if (ldap_conf.tls_cacertfile != NULL)
                    804:            fprintf(stderr, "tls_cacertfile   %s\n", ldap_conf.tls_cacertfile);
                    805:        if (ldap_conf.tls_cacertdir != NULL)
                    806:            fprintf(stderr, "tls_cacertdir    %s\n", ldap_conf.tls_cacertdir);
                    807:        if (ldap_conf.tls_random_file != NULL)
                    808:            fprintf(stderr, "tls_random_file  %s\n", ldap_conf.tls_random_file);
                    809:        if (ldap_conf.tls_cipher_suite != NULL)
                    810:            fprintf(stderr, "tls_cipher_suite %s\n", ldap_conf.tls_cipher_suite);
                    811:        if (ldap_conf.tls_certfile != NULL)
                    812:            fprintf(stderr, "tls_certfile     %s\n", ldap_conf.tls_certfile);
                    813:        if (ldap_conf.tls_keyfile != NULL)
                    814:            fprintf(stderr, "tls_keyfile      %s\n", ldap_conf.tls_keyfile);
1.1       millert   815:        fprintf(stderr, "===================\n");
                    816:     }
                    817:     if (!ldap_conf.base)
                    818:        return(FALSE);          /* if no base is defined, ignore LDAP */
                    819:
1.5       millert   820:     /*
                    821:      * Interpret SSL option
                    822:      */
                    823:     if (ldap_conf.ssl != NULL) {
1.7       millert   824:        if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
                    825:            ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
                    826:        else if (_atobool(ldap_conf.ssl))
                    827:            ldap_conf.ssl_mode = SUDO_LDAP_SSL;
1.5       millert   828:     }
1.7       millert   829:
                    830: #if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
                    831:     if (ldap_conf.tls_checkpeer != -1) {
                    832:        ldapssl_set_strength(NULL,
                    833:            ldap_conf.tls_checkpeer ? LDAPSSL_AUTH_CERT : LDAPSSL_AUTH_WEAK);
                    834:     }
                    835: #endif
1.5       millert   836:
1.6       millert   837: #ifndef HAVE_LDAP_INITIALIZE
                    838:     /* Convert uri list to host list if no ldap_initialize(). */
                    839:     if (ldap_conf.uri) {
                    840:        if (sudo_ldap_parse_uri(ldap_conf.uri) != 0)
                    841:            return(FALSE);
                    842:        free(ldap_conf.uri);
                    843:        ldap_conf.uri = NULL;
                    844:        ldap_conf.port = LDAP_PORT;
                    845:     }
                    846: #endif
                    847:
1.5       millert   848:     /* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */
1.6       millert   849:     if (!ldap_conf.uri && ldap_conf.port < 0)
1.5       millert   850:        ldap_conf.port =
                    851:            ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
                    852:
1.1       millert   853:     /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
                    854:     if (ldap_conf.rootbinddn) {
                    855:        if ((f = fopen(_PATH_LDAP_SECRET, "r")) != NULL) {
                    856:            if (fgets(buf, sizeof(buf), f) != NULL) {
                    857:                /* removing trailing newlines */
                    858:                for (c = buf; *c != '\0'; c++)
                    859:                    continue;
                    860:                while (--c > buf && *c == '\n')
                    861:                    *c = '\0';
                    862:                /* copy to bindpw and binddn */
                    863:                efree(ldap_conf.bindpw);
                    864:                ldap_conf.bindpw = estrdup(buf);
                    865:                efree(ldap_conf.binddn);
                    866:                ldap_conf.binddn = ldap_conf.rootbinddn;
                    867:                ldap_conf.rootbinddn = NULL;
                    868:            }
                    869:            fclose(f);
                    870:        }
                    871:     }
                    872:     return(TRUE);
                    873: }
                    874:
                    875: /*
                    876:  * like perl's join(sep,@ARGS)
                    877:  */
                    878: char *
                    879:  _ldap_join_values(sep, v)
                    880:     char *sep;
                    881:     char **v;
                    882: {
                    883:     char *b = NULL, **p = NULL;
                    884:     size_t sz = 0;
                    885:
                    886:     /* paste values together */
                    887:     for (p = v; p && *p; p++) {
                    888:        if (p != v && sep != NULL)
1.3       martynas  889:            ncat(&b, &sz, sep); /* append separator */
1.1       millert   890:        ncat(&b, &sz, *p);      /* append value */
                    891:     }
                    892:
                    893:     /* sanity check */
                    894:     if (b[0] == '\0') {
                    895:        /* something went wrong, put something here */
                    896:        ncat(&b, &sz, "(empty list)");  /* append value */
                    897:     }
                    898:
                    899:     return(b);
                    900: }
                    901:
                    902: char *sudo_ldap_cm_list = NULL;
                    903: size_t sudo_ldap_cm_list_size;
                    904:
                    905: #define SAVE_LIST(x) ncat(&sudo_ldap_cm_list,&sudo_ldap_cm_list_size,(x))
                    906: /*
                    907:  * Walks through search result and returns TRUE if we have a
                    908:  * command match
                    909:  */
                    910: int
                    911: sudo_ldap_add_match(ld, entry, pwflag)
                    912:     LDAP *ld;
                    913:     LDAPMessage *entry;
                    914:     int pwflag;
                    915: {
                    916:     char *dn, **edn, **v = NULL;
                    917:
                    918:     /* if we are not collecting matches, then don't save them */
                    919:     if (pwflag != I_LISTPW)
                    920:        return(TRUE);
                    921:
                    922:     /* collect the dn, only show the rdn */
                    923:     dn = ldap_get_dn(ld, entry);
                    924:     edn = dn ? ldap_explode_dn(dn, 1) : NULL;
                    925:     SAVE_LIST("\nLDAP Role: ");
                    926:     SAVE_LIST((edn && *edn) ? *edn : "UNKNOWN");
                    927:     SAVE_LIST("\n");
                    928:     if (dn)
                    929:        ldap_memfree(dn);
                    930:     if (edn)
                    931:        ldap_value_free(edn);
                    932:
                    933:     /* get the Runas Values from the entry */
                    934:     v = ldap_get_values(ld, entry, "sudoRunAs");
                    935:     if (v && *v) {
                    936:        SAVE_LIST("  RunAs: (");
                    937:        SAVE_LIST(_ldap_join_values(", ", v));
                    938:        SAVE_LIST(")\n");
                    939:     }
                    940:     if (v)
                    941:        ldap_value_free(v);
                    942:
                    943:     /* get the Command Values from the entry */
                    944:     v = ldap_get_values(ld, entry, "sudoCommand");
                    945:     if (v && *v) {
                    946:        SAVE_LIST("  Commands:\n    ");
                    947:        SAVE_LIST(_ldap_join_values("\n    ", v));
                    948:        SAVE_LIST("\n");
                    949:     } else {
                    950:        SAVE_LIST("  Commands: NONE\n");
                    951:     }
                    952:     if (v)
                    953:        ldap_value_free(v);
                    954:
                    955:     return(FALSE);             /* Don't stop at the first match */
                    956: }
                    957: #undef SAVE_LIST
                    958:
                    959: void
                    960: sudo_ldap_list_matches()
                    961: {
                    962:     if (sudo_ldap_cm_list != NULL)
                    963:        printf("%s", sudo_ldap_cm_list);
                    964: }
                    965:
                    966: /*
1.5       millert   967:  * Set LDAP options based on the config table.
1.1       millert   968:  */
1.5       millert   969: int
                    970: sudo_ldap_set_options(ld)
                    971:     LDAP *ld;
1.1       millert   972: {
1.5       millert   973:     struct ldap_config_table *cur;
1.1       millert   974:     int rc;
                    975:
1.5       millert   976:     /* Set ber options */
                    977: #ifdef LBER_OPT_DEBUG_LEVEL
                    978:     if (ldap_conf.ldap_debug)
                    979:        ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
                    980: #endif
1.1       millert   981:
1.5       millert   982:     /* Set simple LDAP options */
                    983:     for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
                    984:        LDAP *conn;
                    985:        int ival;
                    986:        char *sval;
1.1       millert   987:
1.5       millert   988:        if (cur->opt_val == -1)
                    989:            continue;
1.1       millert   990:
1.5       millert   991:        conn = cur->connected ? ld : NULL;
                    992:        switch (cur->type) {
                    993:        case CONF_BOOL:
                    994:        case CONF_INT:
                    995:            ival = *(int *)(cur->valp);
                    996:            if (ival >= 0) {
                    997:                rc = ldap_set_option(conn, cur->opt_val, &ival);
                    998:                if (rc != LDAP_OPT_SUCCESS) {
                    999:                    warnx("ldap_set_option: %s -> %d: %s",
                   1000:                        cur->conf_str, ival, ldap_err2string(rc));
                   1001:                    return(-1);
                   1002:                }
                   1003:                DPRINTF(("ldap_set_option: %s -> %d", cur->conf_str, ival), 1);
                   1004:            }
                   1005:            break;
                   1006:        case CONF_STR:
                   1007:            sval = *(char **)(cur->valp);
                   1008:            if (sval != NULL) {
                   1009:                rc = ldap_set_option(conn, cur->opt_val, sval);
                   1010:                if (rc != LDAP_OPT_SUCCESS) {
                   1011:                    warnx("ldap_set_option: %s -> %s: %s",
                   1012:                        cur->conf_str, sval, ldap_err2string(rc));
                   1013:                    return(-1);
                   1014:                }
                   1015:                DPRINTF(("ldap_set_option: %s -> %s", cur->conf_str, sval), 1);
                   1016:            }
                   1017:            break;
                   1018:        }
                   1019:     }
1.1       millert  1020:
                   1021: #ifdef LDAP_OPT_NETWORK_TIMEOUT
1.5       millert  1022:     /* Convert bind_timelimit to a timeval */
1.1       millert  1023:     if (ldap_conf.bind_timelimit > 0) {
                   1024:        struct timeval tv;
                   1025:        tv.tv_sec = ldap_conf.bind_timelimit / 1000;
                   1026:        tv.tv_usec = 0;
                   1027:        rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
                   1028:        if (rc != LDAP_OPT_SUCCESS) {
1.5       millert  1029:            warnx("ldap_set_option(NETWORK_TIMEOUT, %ld): %s",
                   1030:                (long)tv.tv_sec, ldap_err2string(rc));
                   1031:            return(-1);
                   1032:        }
                   1033:        DPRINTF(("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %ld)\n",
                   1034:            (long)tv.tv_sec), 1);
                   1035:     }
                   1036: #endif
                   1037:
                   1038: #if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
                   1039:     if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
                   1040:        int val = LDAP_OPT_X_TLS_HARD;
                   1041:        rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
                   1042:        if (rc != LDAP_SUCCESS) {
                   1043:            warnx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
                   1044:                ldap_err2string(rc));
                   1045:            return(-1);
                   1046:        }
1.6       millert  1047:        DPRINTF(("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD)\n"), 1);
1.5       millert  1048:     }
                   1049: #endif
                   1050:     return(0);
                   1051: }
                   1052:
                   1053: /*
                   1054:  * Open a connection to the LDAP server.
                   1055:  */
                   1056: static LDAP *
                   1057: sudo_ldap_open()
                   1058: {
                   1059:     LDAP *ld = NULL;
                   1060:     int rc;
                   1061:
                   1062:     if (!sudo_ldap_read_config())
                   1063:        return(NULL);
                   1064:
                   1065:     /* Connect to LDAP server */
1.1       millert  1066: #ifdef HAVE_LDAP_INITIALIZE
1.6       millert  1067:     if (ldap_conf.uri != NULL) {
1.5       millert  1068:        DPRINTF(("ldap_initialize(ld, %s)", ldap_conf.uri), 2);
1.1       millert  1069:        rc = ldap_initialize(&ld, ldap_conf.uri);
                   1070:     } else
                   1071: #endif /* HAVE_LDAP_INITIALIZE */
1.6       millert  1072:        rc = sudo_ldap_init(&ld, ldap_conf.host, ldap_conf.port);
                   1073:     if (rc != LDAP_SUCCESS) {
                   1074:        warnx("unable to initialize LDAP: %s", ldap_err2string(rc));
                   1075:        return(NULL);
1.1       millert  1076:     }
                   1077:
1.5       millert  1078:     /* Set LDAP options */
                   1079:     if (sudo_ldap_set_options(ld) < 0)
                   1080:        return(NULL);
1.1       millert  1081:
1.5       millert  1082:     if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
1.1       millert  1083: #ifdef HAVE_LDAP_START_TLS_S
                   1084:        rc = ldap_start_tls_s(ld, NULL, NULL);
                   1085:        if (rc != LDAP_SUCCESS) {
1.5       millert  1086:            warnx("ldap_start_tls_s(): %s", ldap_err2string(rc));
1.1       millert  1087:            ldap_unbind(ld);
                   1088:            return(NULL);
                   1089:        }
                   1090:        DPRINTF(("ldap_start_tls_s() ok"), 1);
1.5       millert  1091: #else
                   1092:        warnx("start_tls specified but LDAP libs do not support ldap_start_tls_s()");
                   1093: #endif /* HAVE_LDAP_START_TLS_S */
1.1       millert  1094:     }
                   1095:
                   1096:     /* Actually connect */
                   1097:     if ((rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw))) {
1.5       millert  1098:        warnx("ldap_simple_bind_s: %s", ldap_err2string(rc));
1.1       millert  1099:        return(NULL);
                   1100:     }
1.5       millert  1101:     DPRINTF(("ldap_simple_bind_s() ok"), 1);
1.1       millert  1102:
                   1103:     return(ld);
                   1104: }
                   1105:
                   1106: static void
                   1107: sudo_ldap_update_defaults(ld)
                   1108:     LDAP *ld;
                   1109: {
                   1110:     LDAPMessage *entry = NULL, *result = NULL;  /* used for searches */
                   1111:     int rc;                                     /* temp return value */
                   1112:
                   1113:     rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE,
                   1114:        "cn=defaults", NULL, 0, &result);
1.5       millert  1115:     if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
1.1       millert  1116:        DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
                   1117:        sudo_ldap_parse_options(ld, entry);
                   1118:     } else
                   1119:        DPRINTF(("no default options found!"), 1);
                   1120:
                   1121:     if (result)
                   1122:        ldap_msgfree(result);
                   1123: }
                   1124:
                   1125: /*
                   1126:  * like sudoers_lookup() - only LDAP style
                   1127:  */
                   1128: int
                   1129: sudo_ldap_check(pwflag)
                   1130:     int pwflag;
                   1131: {
                   1132:     LDAP *ld;
                   1133:     LDAPMessage *entry = NULL, *result = NULL; /* used for searches */
                   1134:     char *filt;                                        /* used to parse attributes */
                   1135:     int rc, ret = FALSE, do_netgr;             /* temp/final return values */
1.4       millert  1136:     int setenv_implied;
1.1       millert  1137:     int ldap_user_matches = FALSE, ldap_host_matches = FALSE; /* flags */
                   1138:
                   1139:     /* Open a connection to the LDAP server. */
                   1140:     if ((ld = sudo_ldap_open()) == NULL)
                   1141:        return(VALIDATE_ERROR);
                   1142:
                   1143:     /* Parse Default options. */
                   1144:     sudo_ldap_update_defaults(ld);
                   1145:
                   1146:     /*
                   1147:      * Okay - time to search for anything that matches this user
                   1148:      * Lets limit it to only two queries of the LDAP server
                   1149:      *
                   1150:      * The first pass will look by the username, groups, and
                   1151:      * the keyword ALL.  We will then inspect the results that
                   1152:      * came back from the query.  We don't need to inspect the
                   1153:      * sudoUser in this pass since the LDAP server already scanned
                   1154:      * it for us.
                   1155:      *
                   1156:      * The second pass will return all the entries that contain
                   1157:      * user netgroups.  Then we take the netgroups returned and
                   1158:      * try to match them against the username.
                   1159:      */
1.4       millert  1160:     setenv_implied = FALSE;
1.1       millert  1161:     for (do_netgr = 0; !ret && do_netgr < 2; do_netgr++) {
                   1162:        filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1();
                   1163:        DPRINTF(("ldap search '%s'", filt), 1);
                   1164:        rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt,
                   1165:            NULL, 0, &result);
1.5       millert  1166:        if (rc != LDAP_SUCCESS)
1.1       millert  1167:            DPRINTF(("nothing found for '%s'", filt), 1);
                   1168:        efree(filt);
                   1169:
                   1170:        /* parse each entry returned from this most recent search */
                   1171:        entry = rc ? NULL : ldap_first_entry(ld, result);
                   1172:        while (entry != NULL) {
                   1173:            DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
                   1174:            if (
                   1175:            /* first verify user netgroup matches - only if in pass 2 */
                   1176:                (!do_netgr || sudo_ldap_check_user_netgroup(ld, entry)) &&
                   1177:            /* remember that user matched */
                   1178:                (ldap_user_matches = -1) &&
                   1179:            /* verify host match */
                   1180:                sudo_ldap_check_host(ld, entry) &&
                   1181:            /* remember that host matched */
                   1182:                (ldap_host_matches = -1) &&
                   1183:            /* add matches for listing later */
                   1184:                sudo_ldap_add_match(ld, entry, pwflag) &&
                   1185:            /* verify command match */
1.4       millert  1186:                sudo_ldap_check_command(ld, entry, &setenv_implied) &&
1.1       millert  1187:            /* verify runas match */
                   1188:                sudo_ldap_check_runas(ld, entry)
                   1189:                ) {
                   1190:                /* We have a match! */
                   1191:                DPRINTF(("Perfect Matched!"), 1);
                   1192:                /* pick up any options */
1.4       millert  1193:                if (setenv_implied)
                   1194:                    def_setenv = TRUE;
1.1       millert  1195:                sudo_ldap_parse_options(ld, entry);
1.8     ! millert  1196: #ifdef HAVE_SELINUX
        !          1197:                /* Set role and type if not specified on command line. */
        !          1198:                if (user_role == NULL)
        !          1199:                    user_role = def_role;
        !          1200:                if (user_type == NULL)
        !          1201:                    user_type = def_type;
        !          1202: #endif /* HAVE_SELINUX */
1.1       millert  1203:                /* make sure we don't reenter loop */
                   1204:                ret = VALIDATE_OK;
                   1205:                /* break from inside for loop */
                   1206:                break;
                   1207:            }
                   1208:            entry = ldap_next_entry(ld, entry);
                   1209:        }
                   1210:        if (result)
                   1211:            ldap_msgfree(result);
                   1212:        result = NULL;
                   1213:     }
                   1214:
                   1215:     sudo_ldap_close(ld);               /* shut down connection */
                   1216:
                   1217:     DPRINTF(("user_matches=%d", ldap_user_matches), 1);
                   1218:     DPRINTF(("host_matches=%d", ldap_host_matches), 1);
                   1219:
                   1220:     /* Check for special case for -v, -k, -l options */
                   1221:     if (pwflag && ldap_user_matches && ldap_host_matches) {
                   1222:        /*
                   1223:          * Handle verifypw & listpw
                   1224:          *
                   1225:          * To be extra paranoid, since we haven't read any NOPASSWD options
                   1226:          * in /etc/sudoers yet, but we have to make the decission now, lets
                   1227:          * assume the worst and prefer to prompt for password unless the setting
                   1228:          * is "never". (example verifypw=never or listpw=never)
                   1229:          *
                   1230:          */
                   1231:        ret = VALIDATE_OK;
                   1232:        if (pwflag == -1) {
                   1233:            SET(ret, FLAG_NOPASS);              /* -k or -K */
                   1234:        } else {
                   1235:            switch (sudo_defs_table[pwflag].sd_un.tuple) {
                   1236:            case never:
                   1237:                SET(ret, FLAG_NOPASS);
                   1238:                break;
                   1239:            case always:
                   1240:                if (def_authenticate)
                   1241:                    SET(ret, FLAG_CHECK_USER);
                   1242:                break;
                   1243:            default:
                   1244:                break;
                   1245:            }
                   1246:        }
                   1247:     }
                   1248:     if (ISSET(ret, VALIDATE_OK)) {
                   1249:        /* we have a match, should we check the password? */
                   1250:        if (!def_authenticate)
                   1251:            SET(ret, FLAG_NOPASS);
                   1252:        if (def_noexec)
                   1253:            SET(ret, FLAG_NOEXEC);
                   1254:        if (def_setenv)
                   1255:            SET(ret, FLAG_SETENV);
                   1256:     } else {
                   1257:        /* we do not have a match */
                   1258:        ret = VALIDATE_NOT_OK;
                   1259:        if (pwflag)
                   1260:            SET(ret, FLAG_NO_CHECK);
                   1261:        else if (!ldap_user_matches)
                   1262:            SET(ret, FLAG_NO_USER);
                   1263:        else if (!ldap_host_matches)
                   1264:            SET(ret, FLAG_NO_HOST);
                   1265:     }
                   1266:     DPRINTF(("sudo_ldap_check(%d)=0x%02x", pwflag, ret), 1);
                   1267:
                   1268:     return(ret);
                   1269: }
                   1270:
                   1271: /*
                   1272:  * shut down LDAP connection
                   1273:  */
                   1274: static void
                   1275: sudo_ldap_close(LDAP *ld)
                   1276: {
                   1277:     if (ld)
                   1278:        ldap_unbind_s(ld);
                   1279: }