[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.7

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