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

1.1       millert     1: /*
1.11    ! millert     2:  * Copyright (c) 2003-2009 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>
                     48: #include <pwd.h>
                     49: #include <grp.h>
                     50: #include <netinet/in.h>
                     51: #include <arpa/inet.h>
                     52: #include <netdb.h>
                     53: #ifdef HAVE_LBER_H
                     54: # include <lber.h>
                     55: #endif
                     56: #include <ldap.h>
1.7       millert    57: #if defined(HAVE_LDAP_SSL_H)
                     58: # include <ldap_ssl.h>
                     59: #elif defined(HAVE_MPS_LDAP_SSL_H)
                     60: # include <mps/ldap_ssl.h>
                     61: #endif
1.9       millert    62: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
                     63: # ifdef HAVE_SASL_SASL_H
                     64: #  include <sasl/sasl.h>
                     65: # else
                     66: #  include <sasl.h>
                     67: # endif
                     68: # if HAVE_GSS_KRB5_CCACHE_NAME
                     69: #  if defined(HAVE_GSSAPI_GSSAPI_KRB5_H)
                     70: #   include <gssapi/gssapi.h>
                     71: #   include <gssapi/gssapi_krb5.h>
                     72: #  elif defined(HAVE_GSSAPI_GSSAPI_H)
                     73: #   include <gssapi/gssapi.h>
                     74: #  else
                     75: #   include <gssapi.h>
                     76: #  endif
                     77: # endif
                     78: #endif
1.1       millert    79:
                     80: #include "sudo.h"
                     81: #include "parse.h"
1.9       millert    82: #include "lbuf.h"
1.1       millert    83:
                     84: #ifndef lint
1.11    ! millert    85: __unused static const char rcsid[] = "$Sudo: ldap.c,v 1.108 2009/05/29 13:43:12 millert Exp $";
1.1       millert    86: #endif /* lint */
                     87:
                     88: #ifndef LDAP_OPT_SUCCESS
                     89: # define LDAP_OPT_SUCCESS LDAP_SUCCESS
                     90: #endif
                     91:
1.8       millert    92: #ifndef LDAPS_PORT
                     93: # define LDAPS_PORT 636
                     94: #endif
                     95:
1.9       millert    96: #if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && !defined(LDAP_SASL_QUIET)
                     97: # define LDAP_SASL_QUIET       0
                     98: #endif
                     99:
                    100: #ifndef HAVE_LDAP_UNBIND_EXT_S
                    101: #define ldap_unbind_ext_s(a, b, c)     ldap_unbind_s(a)
                    102: #endif
                    103:
                    104: #ifndef HAVE_LDAP_SEARCH_EXT_S
                    105: #define ldap_search_ext_s(a, b, c, d, e, f, g, h, i, j, k)             \
                    106:        ldap_search_s(a, b, c, d, e, f, k)
                    107: #endif
                    108:
                    109: #define LDAP_FOREACH(var, ld, res)                                     \
                    110:     for ((var) = ldap_first_entry((ld), (res));                                \
                    111:        (var) != NULL;                                                  \
                    112:        (var) = ldap_next_entry((ld), (var)))
                    113:
                    114: #define        DPRINTF(args, level)    if (ldap_conf.debug >= level) warningx args
1.1       millert   115:
1.5       millert   116: #define CONF_BOOL      0
                    117: #define CONF_INT       1
                    118: #define CONF_STR       2
                    119:
                    120: #define SUDO_LDAP_SSL          1
                    121: #define SUDO_LDAP_STARTTLS     2
                    122:
                    123: struct ldap_config_table {
                    124:     const char *conf_str;      /* config file string */
                    125:     short type;                        /* CONF_BOOL, CONF_INT, CONF_STR */
                    126:     short connected;           /* connection-specific value? */
                    127:     int opt_val;               /* LDAP_OPT_* (or -1 for sudo internal) */
                    128:     void *valp;                        /* pointer into ldap_conf */
                    129: };
1.1       millert   130:
                    131: /* ldap configuration structure */
1.9       millert   132: static struct ldap_config {
1.1       millert   133:     int port;
                    134:     int version;
                    135:     int debug;
1.5       millert   136:     int ldap_debug;
1.1       millert   137:     int tls_checkpeer;
                    138:     int timelimit;
                    139:     int bind_timelimit;
1.9       millert   140:     int use_sasl;
                    141:     int rootuse_sasl;
1.5       millert   142:     int ssl_mode;
1.1       millert   143:     char *host;
                    144:     char *uri;
                    145:     char *binddn;
                    146:     char *bindpw;
                    147:     char *rootbinddn;
                    148:     char *base;
                    149:     char *ssl;
                    150:     char *tls_cacertfile;
                    151:     char *tls_cacertdir;
                    152:     char *tls_random_file;
                    153:     char *tls_cipher_suite;
                    154:     char *tls_certfile;
                    155:     char *tls_keyfile;
1.9       millert   156:     char *sasl_auth_id;
                    157:     char *rootsasl_auth_id;
                    158:     char *sasl_secprops;
                    159:     char *krb5_ccname;
1.1       millert   160: } ldap_conf;
                    161:
1.9       millert   162: static struct ldap_config_table ldap_conf_table[] = {
1.5       millert   163:     { "sudoers_debug", CONF_INT, FALSE, -1, &ldap_conf.debug },
                    164:     { "host", CONF_STR, FALSE, -1, &ldap_conf.host },
                    165:     { "port", CONF_INT, FALSE, -1, &ldap_conf.port },
                    166:     { "ssl", CONF_STR, FALSE, -1, &ldap_conf.ssl },
                    167:     { "sslpath", CONF_STR, FALSE, -1, &ldap_conf.tls_certfile },
                    168:     { "uri", CONF_STR, FALSE, -1, &ldap_conf.uri },
                    169: #ifdef LDAP_OPT_DEBUG_LEVEL
                    170:     { "debug", CONF_INT, FALSE, LDAP_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug },
                    171: #endif
                    172: #ifdef LDAP_OPT_PROTOCOL_VERSION
                    173:     { "ldap_version", CONF_INT, TRUE, LDAP_OPT_PROTOCOL_VERSION,
                    174:        &ldap_conf.version },
                    175: #endif
                    176: #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
                    177:     { "tls_checkpeer", CONF_BOOL, FALSE, LDAP_OPT_X_TLS_REQUIRE_CERT,
                    178:        &ldap_conf.tls_checkpeer },
1.7       millert   179: #else
                    180:     { "tls_checkpeer", CONF_BOOL, FALSE, -1, &ldap_conf.tls_checkpeer },
1.5       millert   181: #endif
                    182: #ifdef LDAP_OPT_X_TLS_CACERTFILE
                    183:     { "tls_cacertfile", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTFILE,
                    184:        &ldap_conf.tls_cacertfile },
                    185: #endif
                    186: #ifdef LDAP_OPT_X_TLS_CACERTDIR
                    187:     { "tls_cacertdir", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTDIR,
                    188:        &ldap_conf.tls_cacertdir },
                    189: #endif
                    190: #ifdef LDAP_OPT_X_TLS_RANDOM_FILE
                    191:     { "tls_randfile", CONF_STR, FALSE, LDAP_OPT_X_TLS_RANDOM_FILE,
                    192:        &ldap_conf.tls_random_file },
                    193: #endif
                    194: #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
                    195:     { "tls_ciphers", CONF_STR, FALSE, LDAP_OPT_X_TLS_CIPHER_SUITE,
                    196:        &ldap_conf.tls_cipher_suite },
                    197: #endif
                    198: #ifdef LDAP_OPT_X_TLS_CERTFILE
                    199:     { "tls_cert", CONF_STR, FALSE, LDAP_OPT_X_TLS_CERTFILE,
                    200:        &ldap_conf.tls_certfile },
                    201: #else
                    202:     { "tls_cert", CONF_STR, FALSE, -1, &ldap_conf.tls_certfile },
                    203: #endif
                    204: #ifdef LDAP_OPT_X_TLS_KEYFILE
                    205:     { "tls_key", CONF_STR, FALSE, LDAP_OPT_X_TLS_KEYFILE,
                    206:        &ldap_conf.tls_keyfile },
                    207: #else
                    208:     { "tls_key", CONF_STR, FALSE, -1, &ldap_conf.tls_keyfile },
                    209: #endif
                    210: #ifdef LDAP_OPT_NETWORK_TIMEOUT
                    211:     { "bind_timelimit", CONF_INT, TRUE, -1 /* needs timeval, set manually */,
                    212:        &ldap_conf.bind_timelimit },
                    213: #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
                    214:     { "bind_timelimit", CONF_INT, TRUE, LDAP_X_OPT_CONNECT_TIMEOUT,
                    215:        &ldap_conf.bind_timelimit },
                    216: #endif
                    217:     { "timelimit", CONF_INT, TRUE, LDAP_OPT_TIMELIMIT, &ldap_conf.timelimit },
                    218:     { "binddn", CONF_STR, FALSE, -1, &ldap_conf.binddn },
                    219:     { "bindpw", CONF_STR, FALSE, -1, &ldap_conf.bindpw },
                    220:     { "rootbinddn", CONF_STR, FALSE, -1, &ldap_conf.rootbinddn },
                    221:     { "sudoers_base", CONF_STR, FALSE, -1, &ldap_conf.base },
1.9       millert   222: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
                    223:     { "use_sasl", CONF_BOOL, FALSE, -1, &ldap_conf.use_sasl },
                    224:     { "sasl_auth_id", CONF_STR, FALSE, -1, &ldap_conf.sasl_auth_id },
                    225:     { "rootuse_sasl", CONF_BOOL, FALSE, -1, &ldap_conf.rootuse_sasl },
                    226:     { "rootsasl_auth_id", CONF_STR, FALSE, -1, &ldap_conf.rootsasl_auth_id },
                    227: # ifdef LDAP_OPT_X_SASL_SECPROPS
                    228:     { "sasl_secprops", CONF_STR, TRUE, LDAP_OPT_X_SASL_SECPROPS,
                    229:        &ldap_conf.sasl_secprops },
                    230: # endif
                    231:     { "krb5_ccname", CONF_STR, FALSE, -1, &ldap_conf.krb5_ccname },
                    232: #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
1.5       millert   233:     { NULL }
                    234: };
                    235:
1.9       millert   236: struct sudo_nss sudo_nss_ldap = {
                    237:     &sudo_nss_ldap,
                    238:     NULL,
                    239:     sudo_ldap_open,
                    240:     sudo_ldap_close,
                    241:     sudo_ldap_parse,
                    242:     sudo_ldap_setdefs,
                    243:     sudo_ldap_lookup,
                    244:     sudo_ldap_display_cmnd,
                    245:     sudo_ldap_display_defaults,
                    246:     sudo_ldap_display_bound_defaults,
                    247:     sudo_ldap_display_privs
                    248: };
                    249:
                    250: #ifdef HAVE_LDAP_CREATE
                    251: /*
                    252:  * Rebuild the hosts list and include a specific port for each host.
                    253:  * ldap_create() does not take a default port parameter so we must
                    254:  * append one if we want something other than LDAP_PORT.
                    255:  */
                    256: static void
                    257: sudo_ldap_conf_add_ports()
                    258: {
                    259:
                    260:     char *host, *port, defport[13];
                    261:     char hostbuf[LINE_MAX * 2];
                    262:
                    263:     hostbuf[0] = '\0';
                    264:     if (snprintf(defport, sizeof(defport), ":%d", ldap_conf.port) >= sizeof(defport))
                    265:        errorx(1, "sudo_ldap_conf_add_ports: port too large");
                    266:
                    267:     for ((host = strtok(ldap_conf.host, " \t")); host; (host = strtok(NULL, " \t"))) {
                    268:        if (hostbuf[0] != '\0') {
                    269:            if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
                    270:                goto toobig;
                    271:        }
                    272:
                    273:        if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
                    274:            goto toobig;
                    275:        /* Append port if there is not one already. */
                    276:        if ((port = strrchr(host, ':')) == NULL || !isdigit(port[1])) {
                    277:            if (strlcat(hostbuf, defport, sizeof(hostbuf)) >= sizeof(hostbuf))
                    278:                goto toobig;
                    279:        }
                    280:     }
                    281:
                    282:     free(ldap_conf.host);
                    283:     ldap_conf.host = estrdup(hostbuf);
                    284:     return;
                    285:
                    286: toobig:
                    287:     errorx(1, "sudo_ldap_conf_add_ports: out of space expanding hostbuf");
                    288: }
                    289: #endif
1.1       millert   290:
1.6       millert   291: #ifndef HAVE_LDAP_INITIALIZE
                    292: /*
                    293:  * For each uri, convert to host:port pairs.  For ldaps:// enable SSL
                    294:  * Accepts: uris of the form ldap:/// or ldap://hostname:portnum/
                    295:  * where the trailing slash is optional.
                    296:  */
                    297: static int
                    298: sudo_ldap_parse_uri(uri_list)
                    299:     const char *uri_list;
                    300: {
                    301:     char *buf, *uri, *host, *cp, *port;
                    302:     char hostbuf[LINE_MAX];
                    303:     int nldap = 0, nldaps = 0;
                    304:     int rc = -1;
                    305:
                    306:     buf = estrdup(uri_list);
                    307:     hostbuf[0] = '\0';
                    308:     for ((uri = strtok(buf, " \t")); uri != NULL; (uri = strtok(NULL, " \t"))) {
                    309:        if (strncasecmp(uri, "ldap://", 7) == 0) {
                    310:            nldap++;
                    311:            host = uri + 7;
                    312:        } else if (strncasecmp(uri, "ldaps://", 8) == 0) {
                    313:            nldaps++;
                    314:            host = uri + 8;
                    315:        } else {
1.9       millert   316:            warningx("unsupported LDAP uri type: %s", uri);
1.6       millert   317:            goto done;
                    318:        }
                    319:
                    320:        /* trim optional trailing slash */
                    321:        if ((cp = strrchr(host, '/')) != NULL && cp[1] == '\0') {
                    322:            *cp = '\0';
                    323:        }
                    324:
                    325:        if (hostbuf[0] != '\0') {
                    326:            if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
                    327:                goto toobig;
                    328:        }
                    329:
                    330:        if (*host == '\0')
                    331:            host = "localhost";         /* no host specified, use localhost */
                    332:
                    333:        if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
                    334:            goto toobig;
                    335:
                    336:        /* If using SSL and no port specified, add port 636 */
                    337:        if (nldaps) {
                    338:            if ((port = strrchr(host, ':')) == NULL || !isdigit(port[1]))
                    339:                if (strlcat(hostbuf, ":636", sizeof(hostbuf)) >= sizeof(hostbuf))
                    340:                    goto toobig;
                    341:        }
                    342:     }
                    343:     if (hostbuf[0] == '\0') {
1.9       millert   344:        warningx("invalid uri: %s", uri_list);
1.6       millert   345:        goto done;
                    346:     }
                    347:
                    348:     if (nldaps != 0) {
                    349:        if (nldap != 0) {
1.9       millert   350:            warningx("cannot mix ldap and ldaps URIs");
1.6       millert   351:            goto done;
                    352:        }
                    353:        if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
1.9       millert   354:            warningx("cannot mix ldaps and starttls");
1.6       millert   355:            goto done;
                    356:        }
                    357:        ldap_conf.ssl_mode = SUDO_LDAP_SSL;
                    358:     }
                    359:
                    360:     free(ldap_conf.host);
                    361:     ldap_conf.host = estrdup(hostbuf);
                    362:     rc = 0;
                    363:
                    364: done:
                    365:     efree(buf);
                    366:     return(rc);
                    367:
                    368: toobig:
1.9       millert   369:     errorx(1, "sudo_ldap_parse_uri: out of space building hostbuf");
1.6       millert   370: }
                    371: #endif /* HAVE_LDAP_INITIALIZE */
                    372:
                    373: static int
                    374: sudo_ldap_init(ldp, host, port)
                    375:     LDAP **ldp;
                    376:     const char *host;
                    377:     int port;
                    378: {
                    379:     LDAP *ld = NULL;
                    380:     int rc = LDAP_CONNECT_ERROR;
                    381:
                    382: #ifdef HAVE_LDAPSSL_INIT
                    383:     if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
                    384:        DPRINTF(("ldapssl_clientauth_init(%s, %s)",
                    385:            ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
                    386:            ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
                    387:        rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
                    388:            ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
1.11    ! millert   389:        /*
        !           390:         * Mozilla-derived SDKs have a bug starting with version 5.0
        !           391:         * where the path can no longer be a file name and must be a dir.
        !           392:         */
1.6       millert   393:        if (rc != LDAP_SUCCESS) {
1.11    ! millert   394:            char *cp;
        !           395:            if (ldap_conf.tls_certfile) {
        !           396:                cp = strrchr(ldap_conf.tls_certfile, '/');
        !           397:                if (cp != NULL && strncmp(cp + 1, "cert", 4) == 0)
        !           398:                    *cp = '\0';
        !           399:            }
        !           400:            if (ldap_conf.tls_keyfile) {
        !           401:                cp = strrchr(ldap_conf.tls_keyfile, '/');
        !           402:                if (cp != NULL && strncmp(cp + 1, "key", 3) == 0)
        !           403:                    *cp = '\0';
        !           404:            }
        !           405:            DPRINTF(("ldapssl_clientauth_init(%s, %s)",
        !           406:                ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
        !           407:                ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
        !           408:            rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
        !           409:                ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
        !           410:            if (rc != LDAP_SUCCESS) {
        !           411:                warningx("unable to initialize SSL cert and key db: %s",
        !           412:                    ldapssl_err2string(rc));
        !           413:                goto done;
        !           414:            }
1.6       millert   415:        }
                    416:
                    417:        DPRINTF(("ldapssl_init(%s, %d, 1)", host, port), 2);
1.9       millert   418:        if ((ld = ldapssl_init(host, port, 1)) != NULL)
                    419:            rc = LDAP_SUCCESS;
1.6       millert   420:     } else
                    421: #endif
                    422:     {
1.9       millert   423: #ifdef HAVE_LDAP_CREATE
                    424:        DPRINTF(("ldap_create()"), 2);
                    425:        if ((rc = ldap_create(&ld)) != LDAP_SUCCESS)
                    426:            goto done;
                    427:        DPRINTF(("ldap_set_option(LDAP_OPT_HOST_NAME, %s)", host), 2);
                    428:        rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, host);
                    429: #else
1.6       millert   430:        DPRINTF(("ldap_init(%s, %d)", host, port), 2);
1.9       millert   431:        if ((ld = ldap_init(host, port)) != NULL)
                    432:            rc = LDAP_SUCCESS;
                    433: #endif
1.6       millert   434:     }
                    435:
                    436: done:
                    437:     *ldp = ld;
                    438:     return(rc);
                    439: }
                    440:
1.1       millert   441: /*
                    442:  * Walk through search results and return TRUE if we have a matching
                    443:  * netgroup, else FALSE.
                    444:  */
                    445: int
1.9       millert   446: sudo_ldap_check_user_netgroup(ld, entry, user)
1.1       millert   447:     LDAP *ld;
                    448:     LDAPMessage *entry;
1.9       millert   449:     char *user;
1.1       millert   450: {
1.9       millert   451:     struct berval **bv, **p;
                    452:     char *val;
1.1       millert   453:     int ret = FALSE;
                    454:
                    455:     if (!entry)
                    456:        return(ret);
                    457:
                    458:     /* get the values from the entry */
1.9       millert   459:     bv = ldap_get_values_len(ld, entry, "sudoUser");
                    460:     if (bv == NULL)
                    461:        return(ret);
1.1       millert   462:
                    463:     /* walk through values */
1.9       millert   464:     for (p = bv; *p != NULL && !ret; p++) {
                    465:        val = (*p)->bv_val;
1.1       millert   466:        /* match any */
1.9       millert   467:        if (netgr_matches(val, NULL, NULL, user))
1.1       millert   468:            ret = TRUE;
1.9       millert   469:        DPRINTF(("ldap sudoUser netgroup '%s' ... %s", val,
1.1       millert   470:            ret ? "MATCH!" : "not"), 2);
                    471:     }
                    472:
1.9       millert   473:     ldap_value_free_len(bv);   /* cleanup */
1.1       millert   474:
                    475:     return(ret);
                    476: }
                    477:
                    478: /*
                    479:  * Walk through search results and return TRUE if we have a
                    480:  * host match, else FALSE.
                    481:  */
                    482: int
                    483: sudo_ldap_check_host(ld, entry)
                    484:     LDAP *ld;
                    485:     LDAPMessage *entry;
                    486: {
1.9       millert   487:     struct berval **bv, **p;
                    488:     char *val;
1.1       millert   489:     int ret = FALSE;
                    490:
                    491:     if (!entry)
                    492:        return(ret);
                    493:
                    494:     /* get the values from the entry */
1.9       millert   495:     bv = ldap_get_values_len(ld, entry, "sudoHost");
                    496:     if (bv == NULL)
                    497:        return(ret);
1.1       millert   498:
                    499:     /* walk through values */
1.9       millert   500:     for (p = bv; *p != NULL && !ret; p++) {
                    501:        val = (*p)->bv_val;
1.1       millert   502:        /* match any or address or netgroup or hostname */
1.9       millert   503:        if (!strcmp(val, "ALL") || addr_matches(val) ||
                    504:            netgr_matches(val, user_host, user_shost, NULL) ||
                    505:            hostname_matches(user_shost, user_host, val))
1.1       millert   506:            ret = TRUE;
1.9       millert   507:        DPRINTF(("ldap sudoHost '%s' ... %s", val,
1.1       millert   508:            ret ? "MATCH!" : "not"), 2);
                    509:     }
                    510:
1.9       millert   511:     ldap_value_free_len(bv);   /* cleanup */
1.1       millert   512:
                    513:     return(ret);
                    514: }
                    515:
                    516: int
1.9       millert   517: sudo_ldap_check_runas_user(ld, entry)
1.1       millert   518:     LDAP *ld;
                    519:     LDAPMessage *entry;
                    520: {
1.9       millert   521:     struct berval **bv, **p;
                    522:     char *val;
1.1       millert   523:     int ret = FALSE;
                    524:
1.9       millert   525:     if (!runas_pw)
                    526:        return(UNSPEC);
1.1       millert   527:
1.9       millert   528:     /* get the runas user from the entry */
                    529:     bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
                    530:     if (bv == NULL)
                    531:        bv = ldap_get_values_len(ld, entry, "sudoRunAs"); /* old style */
1.1       millert   532:
                    533:     /*
                    534:      * BUG:
                    535:      *
                    536:      * if runas is not specified on the command line, the only information
                    537:      * as to which user to run as is in the runas_default option.  We should
                    538:      * check to see if we have the local option present.  Unfortunately we
                    539:      * don't parse these options until after this routine says yes or no.
                    540:      * The query has already returned, so we could peek at the attribute
                    541:      * values here though.
                    542:      *
                    543:      * For now just require users to always use -u option unless its set
                    544:      * in the global defaults. This behaviour is no different than the global
                    545:      * /etc/sudoers.
1.9       millert   546:      *
1.1       millert   547:      * Sigh - maybe add this feature later
                    548:      */
                    549:
                    550:     /*
                    551:      * If there are no runas entries, match runas_default against
                    552:      * what the user specified on the command line.
                    553:      */
1.9       millert   554:     if (bv == NULL)
                    555:        return(!strcasecmp(runas_pw->pw_name, def_runas_default));
1.1       millert   556:
                    557:     /* walk through values returned, looking for a match */
1.9       millert   558:     for (p = bv; *p != NULL && !ret; p++) {
                    559:        val = (*p)->bv_val;
                    560:        switch (val[0]) {
1.2       millert   561:        case '+':
1.9       millert   562:            if (netgr_matches(val, NULL, NULL, runas_pw->pw_name))
1.2       millert   563:                ret = TRUE;
                    564:            break;
                    565:        case '%':
1.9       millert   566:            if (usergr_matches(val, runas_pw->pw_name, runas_pw))
1.2       millert   567:                ret = TRUE;
                    568:            break;
                    569:        case 'A':
1.9       millert   570:            if (strcmp(val, "ALL") == 0) {
1.2       millert   571:                ret = TRUE;
                    572:                break;
                    573:            }
                    574:            /* FALLTHROUGH */
                    575:        default:
1.9       millert   576:            if (strcasecmp(val, runas_pw->pw_name) == 0)
1.2       millert   577:                ret = TRUE;
                    578:            break;
                    579:        }
1.9       millert   580:        DPRINTF(("ldap sudoRunAsUser '%s' ... %s", val,
                    581:            ret ? "MATCH!" : "not"), 2);
                    582:     }
                    583:
                    584:     ldap_value_free_len(bv);   /* cleanup */
                    585:
                    586:     return(ret);
                    587: }
                    588:
                    589: int
                    590: sudo_ldap_check_runas_group(ld, entry)
                    591:     LDAP *ld;
                    592:     LDAPMessage *entry;
                    593: {
                    594:     struct berval **bv, **p;
                    595:     char *val;
                    596:     int ret = FALSE;
                    597:
                    598:     /* runas_gr is only set if the user specified the -g flag */
                    599:     if (!runas_gr)
                    600:        return(UNSPEC);
                    601:
                    602:     /* get the values from the entry */
                    603:     bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
                    604:     if (bv == NULL)
                    605:        return(ret);
                    606:
                    607:     /* walk through values returned, looking for a match */
                    608:     for (p = bv; *p != NULL && !ret; p++) {
                    609:        val = (*p)->bv_val;
                    610:        if (strcmp(val, "ALL") == 0 || group_matches(val, runas_gr))
                    611:            ret = TRUE;
                    612:        DPRINTF(("ldap sudoRunAsGroup '%s' ... %s", val,
1.1       millert   613:            ret ? "MATCH!" : "not"), 2);
                    614:     }
                    615:
1.9       millert   616:     ldap_value_free_len(bv);   /* cleanup */
1.1       millert   617:
                    618:     return(ret);
                    619: }
                    620:
                    621: /*
1.9       millert   622:  * Walk through search results and return TRUE if we have a runas match,
                    623:  * else FALSE.  RunAs info is optional.
                    624:  */
                    625: int
                    626: sudo_ldap_check_runas(ld, entry)
                    627:     LDAP *ld;
                    628:     LDAPMessage *entry;
                    629: {
                    630:     int ret;
                    631:
                    632:     if (!entry)
                    633:        return(FALSE);
                    634:
                    635:     ret = sudo_ldap_check_runas_user(ld, entry) != FALSE &&
                    636:        sudo_ldap_check_runas_group(ld, entry) != FALSE;
                    637:
                    638:     return(ret);
                    639: }
                    640:
                    641: /*
                    642:  * Walk through search results and return TRUE if we have a command match,
                    643:  * FALSE if disallowed and UNSPEC if not matched.
1.1       millert   644:  */
                    645: int
1.4       millert   646: sudo_ldap_check_command(ld, entry, setenv_implied)
1.1       millert   647:     LDAP *ld;
                    648:     LDAPMessage *entry;
1.4       millert   649:     int *setenv_implied;
1.1       millert   650: {
1.9       millert   651:     struct berval **bv, **p;
                    652:     char *allowed_cmnd, *allowed_args, *val;
                    653:     int foundbang, ret = UNSPEC;
1.1       millert   654:
                    655:     if (!entry)
                    656:        return(ret);
                    657:
1.9       millert   658:     bv = ldap_get_values_len(ld, entry, "sudoCommand");
                    659:     if (bv == NULL)
                    660:        return(ret);
1.1       millert   661:
1.9       millert   662:     for (p = bv; *p != NULL && ret != FALSE; p++) {
                    663:        val = (*p)->bv_val;
1.1       millert   664:        /* Match against ALL ? */
1.9       millert   665:        if (!strcmp(val, "ALL")) {
1.1       millert   666:            ret = TRUE;
1.4       millert   667:            if (setenv_implied != NULL)
                    668:                *setenv_implied = TRUE;
1.9       millert   669:            DPRINTF(("ldap sudoCommand '%s' ... MATCH!", val), 2);
1.1       millert   670:            continue;
                    671:        }
                    672:
                    673:        /* check for !command */
1.9       millert   674:        if (*val == '!') {
1.1       millert   675:            foundbang = TRUE;
1.9       millert   676:            allowed_cmnd = estrdup(1 + val);    /* !command */
1.1       millert   677:        } else {
                    678:            foundbang = FALSE;
1.9       millert   679:            allowed_cmnd = estrdup(val);        /* command */
1.1       millert   680:        }
                    681:
                    682:        /* split optional args away from command */
                    683:        allowed_args = strchr(allowed_cmnd, ' ');
                    684:        if (allowed_args)
                    685:            *allowed_args++ = '\0';
                    686:
                    687:        /* check the command like normal */
                    688:        if (command_matches(allowed_cmnd, allowed_args)) {
                    689:            /*
                    690:             * If allowed (no bang) set ret but keep on checking.
                    691:             * If disallowed (bang), exit loop.
                    692:             */
1.9       millert   693:            ret = foundbang ? FALSE : TRUE;
1.1       millert   694:        }
1.9       millert   695:        DPRINTF(("ldap sudoCommand '%s' ... %s", val,
1.1       millert   696:            ret == TRUE ? "MATCH!" : "not"), 2);
                    697:
                    698:        efree(allowed_cmnd);    /* cleanup */
                    699:     }
                    700:
1.9       millert   701:     ldap_value_free_len(bv);   /* more cleanup */
1.1       millert   702:
1.9       millert   703:     return(ret);
                    704: }
                    705:
                    706: /*
                    707:  * Search for boolean "option" in sudoOption.
                    708:  * Returns TRUE if found and allowed, FALSE if negated, else UNSPEC.
                    709:  */
                    710: int
                    711: sudo_ldap_check_bool(ld, entry, option)
                    712:     LDAP *ld;
                    713:     LDAPMessage *entry;
                    714:     char *option;
                    715: {
                    716:     struct berval **bv, **p;
                    717:     char ch, *var;
                    718:     int ret = UNSPEC;
                    719:
                    720:     if (entry == NULL)
                    721:        return(UNSPEC);
                    722:
                    723:     bv = ldap_get_values_len(ld, entry, "sudoOption");
                    724:     if (bv == NULL)
                    725:        return(ret);
                    726:
                    727:     /* walk through options */
                    728:     for (p = bv; *p != NULL; p++) {
                    729:        var = (*p)->bv_val;;
                    730:        DPRINTF(("ldap sudoOption: '%s'", var), 2);
                    731:
                    732:        if ((ch = *var) == '!')
                    733:            var++;
                    734:        if (strcmp(var, option) == 0)
                    735:            ret = (ch != '!');
                    736:     }
                    737:
                    738:     ldap_value_free_len(bv);
                    739:
                    740:     return(ret);
1.1       millert   741: }
                    742:
                    743: /*
                    744:  * Read sudoOption and modify the defaults as we go.  This is used once
                    745:  * from the cn=defaults entry and also once when a final sudoRole is matched.
                    746:  */
                    747: void
                    748: sudo_ldap_parse_options(ld, entry)
                    749:     LDAP *ld;
                    750:     LDAPMessage *entry;
                    751: {
1.9       millert   752:     struct berval **bv, **p;
                    753:     char op, *var, *val;
1.1       millert   754:
1.9       millert   755:     if (entry == NULL)
1.1       millert   756:        return;
                    757:
1.9       millert   758:     bv = ldap_get_values_len(ld, entry, "sudoOption");
                    759:     if (bv == NULL)
                    760:        return;
1.1       millert   761:
                    762:     /* walk through options */
1.9       millert   763:     for (p = bv; *p != NULL; p++) {
                    764:        var = estrdup((*p)->bv_val);
                    765:        DPRINTF(("ldap sudoOption: '%s'", var), 2);
1.1       millert   766:
                    767:        /* check for equals sign past first char */
                    768:        val = strchr(var, '=');
                    769:        if (val > var) {
                    770:            *val++ = '\0';      /* split on = and truncate var */
                    771:            op = *(val - 2);    /* peek for += or -= cases */
                    772:            if (op == '+' || op == '-') {
                    773:                *(val - 2) = '\0';      /* found, remove extra char */
                    774:                /* case var+=val or var-=val */
                    775:                set_default(var, val, (int) op);
                    776:            } else {
                    777:                /* case var=val */
                    778:                set_default(var, val, TRUE);
                    779:            }
                    780:        } else if (*var == '!') {
                    781:            /* case !var Boolean False */
                    782:            set_default(var + 1, NULL, FALSE);
                    783:        } else {
                    784:            /* case var Boolean True */
                    785:            set_default(var, NULL, TRUE);
                    786:        }
                    787:        efree(var);
                    788:     }
                    789:
1.9       millert   790:     ldap_value_free_len(bv);
1.1       millert   791: }
                    792:
                    793: /*
                    794:  * builds together a filter to check against ldap
                    795:  */
                    796: char *
1.9       millert   797: sudo_ldap_build_pass1(pw)
                    798:     struct passwd *pw;
1.1       millert   799: {
                    800:     struct group *grp;
                    801:     size_t sz;
1.9       millert   802:     char *buf;
1.1       millert   803:     int i;
                    804:
1.9       millert   805:     /* Start with (|(sudoUser=USERNAME)(sudoUser=ALL)) + NUL */
                    806:     sz = 29 + strlen(pw->pw_name);
                    807:
                    808:     /* Add space for groups */
                    809:     if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL)
                    810:        sz += 12 + strlen(grp->gr_name);        /* primary group */
                    811:     for (i = 0; i < user_ngroups; i++) {
                    812:        if (user_groups[i] == pw->pw_gid)
                    813:            continue;
                    814:        if ((grp = sudo_getgrgid(user_groups[i])) != NULL)
                    815:            sz += 12 + strlen(grp->gr_name);    /* supplementary group */
                    816:     }
                    817:     buf = emalloc(sz);
1.1       millert   818:
1.9       millert   819:     /* Global OR + sudoUser=user_name filter */
                    820:     (void) strlcpy(buf, "(|(sudoUser=", sz);
                    821:     (void) strlcat(buf, pw->pw_name, sz);
                    822:     (void) strlcat(buf, ")", sz);
1.1       millert   823:
                    824:     /* Append primary group */
1.9       millert   825:     if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
                    826:        (void) strlcat(buf, "(sudoUser=%", sz);
                    827:        (void) strlcat(buf, grp->gr_name, sz);
                    828:        (void) strlcat(buf, ")", sz);
1.1       millert   829:     }
                    830:
                    831:     /* Append supplementary groups */
                    832:     for (i = 0; i < user_ngroups; i++) {
1.9       millert   833:        if (user_groups[i] == pw->pw_gid)
1.6       millert   834:            continue;
1.9       millert   835:        if ((grp = sudo_getgrgid(user_groups[i])) != NULL) {
                    836:            (void) strlcat(buf, "(sudoUser=%", sz);
                    837:            (void) strlcat(buf, grp->gr_name, sz);
                    838:            (void) strlcat(buf, ")", sz);
1.1       millert   839:        }
                    840:     }
                    841:
1.9       millert   842:     /* Add ALL to list and end the global OR */
                    843:     if (strlcat(buf, "(sudoUser=ALL))", sz) >= sz)
                    844:        errorx(1, "sudo_ldap_build_pass1 allocation mismatch");
1.1       millert   845:
1.9       millert   846:     return(buf);
1.1       millert   847: }
                    848:
                    849: /*
                    850:  * Map yes/true/on to TRUE, no/false/off to FALSE, else -1
                    851:  */
                    852: int
                    853: _atobool(s)
                    854:     const char *s;
                    855: {
                    856:     switch (*s) {
                    857:        case 'y':
                    858:        case 'Y':
                    859:            if (strcasecmp(s, "yes") == 0)
                    860:                return(TRUE);
                    861:            break;
                    862:        case 't':
                    863:        case 'T':
                    864:            if (strcasecmp(s, "true") == 0)
                    865:                return(TRUE);
                    866:            break;
                    867:        case 'o':
                    868:        case 'O':
                    869:            if (strcasecmp(s, "on") == 0)
                    870:                return(TRUE);
                    871:            if (strcasecmp(s, "off") == 0)
                    872:                return(FALSE);
                    873:            break;
                    874:        case 'n':
                    875:        case 'N':
                    876:            if (strcasecmp(s, "no") == 0)
                    877:                return(FALSE);
                    878:            break;
                    879:        case 'f':
                    880:        case 'F':
                    881:            if (strcasecmp(s, "false") == 0)
                    882:                return(FALSE);
                    883:            break;
                    884:     }
                    885:     return(-1);
                    886: }
                    887:
1.9       millert   888: static void
                    889: sudo_ldap_read_secret(path)
                    890:     const char *path;
                    891: {
                    892:     FILE *fp;
                    893:     char buf[LINE_MAX], *cp;
                    894:
                    895:     if ((fp = fopen(_PATH_LDAP_SECRET, "r")) != NULL) {
                    896:        if (fgets(buf, sizeof(buf), fp) != NULL) {
                    897:            if ((cp = strchr(buf, '\n')) != NULL)
                    898:                *cp = '\0';
                    899:            /* copy to bindpw and binddn */
                    900:            efree(ldap_conf.bindpw);
                    901:            ldap_conf.bindpw = estrdup(buf);
                    902:            efree(ldap_conf.binddn);
                    903:            ldap_conf.binddn = ldap_conf.rootbinddn;
                    904:            ldap_conf.rootbinddn = NULL;
                    905:        }
                    906:        fclose(fp);
                    907:     }
                    908: }
                    909:
1.1       millert   910: int
                    911: sudo_ldap_read_config()
                    912: {
1.9       millert   913:     FILE *fp;
                    914:     char *cp, *keyword, *value;
1.5       millert   915:     struct ldap_config_table *cur;
1.1       millert   916:
                    917:     /* defaults */
                    918:     ldap_conf.version = 3;
1.5       millert   919:     ldap_conf.port = -1;
1.1       millert   920:     ldap_conf.tls_checkpeer = -1;
                    921:     ldap_conf.timelimit = -1;
                    922:     ldap_conf.bind_timelimit = -1;
1.9       millert   923:     ldap_conf.use_sasl = -1;
                    924:     ldap_conf.rootuse_sasl = -1;
1.1       millert   925:
1.9       millert   926:     if ((fp = fopen(_PATH_LDAP_CONF, "r")) == NULL)
1.1       millert   927:        return(FALSE);
1.5       millert   928:
1.9       millert   929:     while ((cp = sudo_parseln(fp)) != NULL) {
                    930:        if (*cp == '\0')
1.1       millert   931:            continue;           /* skip empty line */
                    932:
1.9       millert   933:        /* split into keyword and value */
                    934:        keyword = cp;
                    935:        while (*cp && !isblank((unsigned char) *cp))
                    936:            cp++;
                    937:        if (*cp)
                    938:            *cp++ = '\0';       /* terminate keyword */
1.1       millert   939:
                    940:        /* skip whitespace before value */
1.9       millert   941:        while (isblank((unsigned char) *cp))
                    942:            cp++;
                    943:        value = cp;
1.1       millert   944:
1.5       millert   945:        /* Look up keyword in config table. */
                    946:        for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
                    947:            if (strcasecmp(keyword, cur->conf_str) == 0) {
                    948:                switch (cur->type) {
                    949:                case CONF_BOOL:
                    950:                    *(int *)(cur->valp) = _atobool(value);
                    951:                    break;
                    952:                case CONF_INT:
                    953:                    *(int *)(cur->valp) = atoi(value);
                    954:                    break;
                    955:                case CONF_STR:
                    956:                    efree(*(char **)(cur->valp));
                    957:                    *(char **)(cur->valp) = estrdup(value);
                    958:                    break;
                    959:                }
                    960:                break;
                    961:            }
1.1       millert   962:        }
                    963:     }
1.9       millert   964:     fclose(fp);
1.1       millert   965:
                    966:     if (!ldap_conf.host)
1.9       millert   967:        ldap_conf.host = estrdup("localhost");
1.1       millert   968:
                    969:     if (ldap_conf.bind_timelimit > 0)
                    970:        ldap_conf.bind_timelimit *= 1000;       /* convert to ms */
                    971:
                    972:     if (ldap_conf.debug > 1) {
                    973:        fprintf(stderr, "LDAP Config Summary\n");
                    974:        fprintf(stderr, "===================\n");
                    975:        if (ldap_conf.uri) {
1.9       millert   976:            fprintf(stderr, "uri              %s\n", ldap_conf.uri);
1.6       millert   977:        } else {
1.9       millert   978:            fprintf(stderr, "host             %s\n", ldap_conf.host ?
1.1       millert   979:                ldap_conf.host : "(NONE)");
1.9       millert   980:            fprintf(stderr, "port             %d\n", ldap_conf.port);
1.1       millert   981:        }
1.9       millert   982:        fprintf(stderr, "ldap_version     %d\n", ldap_conf.version);
1.1       millert   983:
1.9       millert   984:        fprintf(stderr, "sudoers_base     %s\n", ldap_conf.base ?
1.1       millert   985:            ldap_conf.base : "(NONE) <---Sudo will ignore ldap)");
1.9       millert   986:        fprintf(stderr, "binddn           %s\n", ldap_conf.binddn ?
1.1       millert   987:            ldap_conf.binddn : "(anonymous)");
1.9       millert   988:        fprintf(stderr, "bindpw           %s\n", ldap_conf.bindpw ?
1.1       millert   989:            ldap_conf.bindpw : "(anonymous)");
1.5       millert   990:        if (ldap_conf.bind_timelimit > 0)
1.9       millert   991:            fprintf(stderr, "bind_timelimit   %d\n", ldap_conf.bind_timelimit);
1.5       millert   992:        if (ldap_conf.timelimit > 0)
1.9       millert   993:            fprintf(stderr, "timelimit        %d\n", ldap_conf.timelimit);
                    994:        fprintf(stderr, "ssl              %s\n", ldap_conf.ssl ?
1.1       millert   995:            ldap_conf.ssl : "(no)");
1.5       millert   996:        if (ldap_conf.tls_checkpeer != -1)
                    997:            fprintf(stderr, "tls_checkpeer    %s\n", ldap_conf.tls_checkpeer ?
                    998:                "(yes)" : "(no)");
                    999:        if (ldap_conf.tls_cacertfile != NULL)
                   1000:            fprintf(stderr, "tls_cacertfile   %s\n", ldap_conf.tls_cacertfile);
                   1001:        if (ldap_conf.tls_cacertdir != NULL)
                   1002:            fprintf(stderr, "tls_cacertdir    %s\n", ldap_conf.tls_cacertdir);
                   1003:        if (ldap_conf.tls_random_file != NULL)
                   1004:            fprintf(stderr, "tls_random_file  %s\n", ldap_conf.tls_random_file);
                   1005:        if (ldap_conf.tls_cipher_suite != NULL)
                   1006:            fprintf(stderr, "tls_cipher_suite %s\n", ldap_conf.tls_cipher_suite);
                   1007:        if (ldap_conf.tls_certfile != NULL)
                   1008:            fprintf(stderr, "tls_certfile     %s\n", ldap_conf.tls_certfile);
                   1009:        if (ldap_conf.tls_keyfile != NULL)
                   1010:            fprintf(stderr, "tls_keyfile      %s\n", ldap_conf.tls_keyfile);
1.9       millert  1011: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
                   1012:        if (ldap_conf.use_sasl != -1) {
                   1013:            fprintf(stderr, "use_sasl         %s\n",
                   1014:                ldap_conf.use_sasl ? "yes" : "no");
                   1015:            fprintf(stderr, "sasl_auth_id     %s\n", ldap_conf.sasl_auth_id ?
                   1016:                ldap_conf.sasl_auth_id : "(NONE)");
                   1017:            fprintf(stderr, "rootuse_sasl     %d\n", ldap_conf.rootuse_sasl);
                   1018:            fprintf(stderr, "rootsasl_auth_id %s\n", ldap_conf.rootsasl_auth_id ?
                   1019:                ldap_conf.rootsasl_auth_id : "(NONE)");
                   1020:            fprintf(stderr, "sasl_secprops    %s\n", ldap_conf.sasl_secprops ?
                   1021:                ldap_conf.sasl_secprops : "(NONE)");
                   1022:            fprintf(stderr, "krb5_ccname      %s\n", ldap_conf.krb5_ccname ?
                   1023:                ldap_conf.krb5_ccname : "(NONE)");
                   1024:        }
                   1025: #endif
1.1       millert  1026:        fprintf(stderr, "===================\n");
                   1027:     }
                   1028:     if (!ldap_conf.base)
                   1029:        return(FALSE);          /* if no base is defined, ignore LDAP */
                   1030:
1.5       millert  1031:     /*
                   1032:      * Interpret SSL option
                   1033:      */
                   1034:     if (ldap_conf.ssl != NULL) {
1.7       millert  1035:        if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
                   1036:            ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
                   1037:        else if (_atobool(ldap_conf.ssl))
                   1038:            ldap_conf.ssl_mode = SUDO_LDAP_SSL;
1.5       millert  1039:     }
1.7       millert  1040:
                   1041: #if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
                   1042:     if (ldap_conf.tls_checkpeer != -1) {
                   1043:        ldapssl_set_strength(NULL,
                   1044:            ldap_conf.tls_checkpeer ? LDAPSSL_AUTH_CERT : LDAPSSL_AUTH_WEAK);
                   1045:     }
                   1046: #endif
1.5       millert  1047:
1.6       millert  1048: #ifndef HAVE_LDAP_INITIALIZE
                   1049:     /* Convert uri list to host list if no ldap_initialize(). */
                   1050:     if (ldap_conf.uri) {
                   1051:        if (sudo_ldap_parse_uri(ldap_conf.uri) != 0)
                   1052:            return(FALSE);
                   1053:        free(ldap_conf.uri);
                   1054:        ldap_conf.uri = NULL;
                   1055:        ldap_conf.port = LDAP_PORT;
                   1056:     }
                   1057: #endif
                   1058:
1.9       millert  1059:     if (!ldap_conf.uri) {
                   1060:        /* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */
                   1061:        if (ldap_conf.port < 0)
                   1062:            ldap_conf.port =
                   1063:                ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
                   1064:
                   1065: #ifdef HAVE_LDAP_CREATE
                   1066:        /*
                   1067:         * Cannot specify port directly to ldap_create(), each host must
                   1068:         * include :port to override the default.
                   1069:         */
                   1070:        if (ldap_conf.port != LDAP_PORT)
                   1071:            sudo_ldap_conf_add_ports();
                   1072: #endif
                   1073:     }
1.5       millert  1074:
1.1       millert  1075:     /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
1.9       millert  1076:     if (ldap_conf.rootbinddn)
                   1077:        sudo_ldap_read_secret(_PATH_LDAP_SECRET);
                   1078:
                   1079: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
                   1080:     /*
                   1081:      * Make sure we can open the file specified by krb5_ccname.
                   1082:      */
                   1083:     if (ldap_conf.krb5_ccname != NULL) {
                   1084:        if (strncasecmp(ldap_conf.krb5_ccname, "FILE:", 5) == 0 ||
                   1085:            strncasecmp(ldap_conf.krb5_ccname, "WRFILE:", 7) == 0) {
                   1086:            value = ldap_conf.krb5_ccname +
                   1087:                (ldap_conf.krb5_ccname[4] == ':' ? 5 : 7);
                   1088:            if ((fp = fopen(value, "r")) != NULL) {
                   1089:                DPRINTF(("using krb5 credential cache: %s", value), 1);
                   1090:                fclose(fp);
                   1091:            } else {
                   1092:                /* Can't open it, just ignore the entry. */
                   1093:                DPRINTF(("unable to open krb5 credential cache: %s", value), 1);
                   1094:                efree(ldap_conf.krb5_ccname);
                   1095:                ldap_conf.krb5_ccname = NULL;
1.1       millert  1096:            }
                   1097:        }
                   1098:     }
1.9       millert  1099: #endif
1.1       millert  1100:     return(TRUE);
                   1101: }
                   1102:
                   1103: /*
1.9       millert  1104:  * Extract the dn from an entry and return the first rdn from it.
1.1       millert  1105:  */
1.9       millert  1106: static char *
                   1107: sudo_ldap_get_first_rdn(ld, entry)
                   1108:     LDAP *ld;
                   1109:     LDAPMessage *entry;
1.1       millert  1110: {
1.9       millert  1111: #ifdef HAVE_LDAP_STR2DN
                   1112:     char *dn, *rdn = NULL;
                   1113:     LDAPDN tmpDN;
1.1       millert  1114:
1.9       millert  1115:     if ((dn = ldap_get_dn(ld, entry)) == NULL)
                   1116:        return(NULL);
                   1117:     if (ldap_str2dn(dn, &tmpDN, LDAP_DN_FORMAT_LDAP) == LDAP_SUCCESS) {
                   1118:        ldap_rdn2str(tmpDN[0], &rdn, LDAP_DN_FORMAT_UFN);
                   1119:        ldap_dnfree(tmpDN);
1.1       millert  1120:     }
1.9       millert  1121:     ldap_memfree(dn);
                   1122:     return(rdn);
                   1123: #else
                   1124:     char *dn, **edn;
1.1       millert  1125:
1.9       millert  1126:     if ((dn = ldap_get_dn(ld, entry)) == NULL)
                   1127:        return(NULL);
                   1128:     edn = ldap_explode_dn(dn, 1);
                   1129:     ldap_memfree(dn);
                   1130:     return(edn ? edn[0] : NULL);
                   1131: #endif
                   1132: }
                   1133:
                   1134: /*
                   1135:  * Fetch and display the global Options.
                   1136:  */
                   1137: int
                   1138: sudo_ldap_display_defaults(nss, pw, lbuf)
                   1139:     struct sudo_nss *nss;
                   1140:     struct passwd *pw;
                   1141:     struct lbuf *lbuf;
                   1142: {
                   1143:     struct berval **bv, **p;
                   1144:     LDAP *ld = (LDAP *) nss->handle;
                   1145:     LDAPMessage *entry = NULL, *result = NULL;
                   1146:     char *prefix = NULL;
                   1147:     int rc, count = 0;
                   1148:
                   1149:     if (ld == NULL)
                   1150:        return(-1);
                   1151:
                   1152:     rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE,
1.10      millert  1153:        "cn=defaults", NULL, 0, NULL, NULL, NULL, 0, &result);
1.9       millert  1154:     if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
                   1155:        bv = ldap_get_values_len(ld, entry, "sudoOption");
                   1156:        if (bv != NULL) {
                   1157:            if (lbuf->len == 0)
                   1158:                prefix = "    ";
                   1159:            else
                   1160:                prefix = ", ";
                   1161:            for (p = bv; *p != NULL; p++) {
                   1162:                lbuf_append(lbuf, prefix, (*p)->bv_val, NULL);
                   1163:                prefix = ", ";
                   1164:                count++;
                   1165:            }
                   1166:            ldap_value_free_len(bv);
                   1167:        }
1.1       millert  1168:     }
1.9       millert  1169:     if (result)
                   1170:        ldap_msgfree(result);
                   1171:     return(count);
                   1172: }
1.1       millert  1173:
1.9       millert  1174: /*
                   1175:  * STUB
                   1176:  */
                   1177: int
                   1178: sudo_ldap_display_bound_defaults(nss, pw, lbuf)
                   1179:     struct sudo_nss *nss;
                   1180:     struct passwd *pw;
                   1181:     struct lbuf *lbuf;
                   1182: {
                   1183:     return(1);
1.1       millert  1184: }
                   1185:
1.9       millert  1186: /*
                   1187:  * Print a record in the short form, ala file sudoers.
                   1188:  */
                   1189: int
                   1190: sudo_ldap_display_entry_short(ld, entry, lbuf)
                   1191:     LDAP *ld;
                   1192:     LDAPMessage *entry;
                   1193:     struct lbuf *lbuf;
                   1194: {
                   1195:     struct berval **bv, **p;
                   1196:     int count = 0;
                   1197:
                   1198:     lbuf_append(lbuf, "    (", NULL);
                   1199:
                   1200:     /* get the RunAsUser Values from the entry */
                   1201:     bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
                   1202:     if (bv == NULL)
                   1203:        bv = ldap_get_values_len(ld, entry, "sudoRunAs");
                   1204:     if (bv != NULL) {
                   1205:        for (p = bv; *p != NULL; p++) {
                   1206:            if (p != bv)
                   1207:                lbuf_append(lbuf, ", ", NULL);
                   1208:            lbuf_append(lbuf, (*p)->bv_val, NULL);
                   1209:        }
                   1210:        ldap_value_free_len(bv);
                   1211:     } else
                   1212:        lbuf_append(lbuf, def_runas_default, NULL);
                   1213:
                   1214:     /* get the RunAsGroup Values from the entry */
                   1215:     bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
                   1216:     if (bv != NULL) {
                   1217:        lbuf_append(lbuf, " : ", NULL);
                   1218:        for (p = bv; *p != NULL; p++) {
                   1219:            if (p != bv)
                   1220:                lbuf_append(lbuf, ", ", NULL);
                   1221:            lbuf_append(lbuf, (*p)->bv_val, NULL);
                   1222:        }
                   1223:        ldap_value_free_len(bv);
                   1224:     }
                   1225:     lbuf_append(lbuf, ") ", NULL);
                   1226:
                   1227:     /* get the Option Values from the entry */
                   1228:     bv = ldap_get_values_len(ld, entry, "sudoOption");
                   1229:     if (bv != NULL) {
                   1230:        char *cp, *tag;
                   1231:
                   1232:        for (p = bv; *p != NULL; p++) {
                   1233:            cp = (*p)->bv_val;
                   1234:            if (*cp == '!')
                   1235:                cp++;
                   1236:            tag = NULL;
                   1237:            if (strcmp(cp, "authenticate") == 0)
                   1238:                tag = (*p)->bv_val[0] == '!' ?
                   1239:                    "NOPASSWD: " : "PASSWD: ";
                   1240:            else if (strcmp(cp, "noexec") == 0)
                   1241:                tag = (*p)->bv_val[0] == '!' ?
                   1242:                    "EXEC: " : "NOEXEC: ";
                   1243:            else if (strcmp(cp, "setenv") == 0)
                   1244:                tag = (*p)->bv_val[0] == '!' ?
                   1245:                    "NOSETENV: " : "SETENV: ";
                   1246:            if (tag != NULL)
                   1247:                lbuf_append(lbuf, tag, NULL);
                   1248:            /* XXX - ignores other options */
                   1249:        }
                   1250:        ldap_value_free_len(bv);
                   1251:     }
                   1252:
                   1253:     /* get the Command Values from the entry */
                   1254:     bv = ldap_get_values_len(ld, entry, "sudoCommand");
                   1255:     if (bv != NULL) {
                   1256:        for (p = bv; *p != NULL; p++) {
                   1257:            if (p != bv)
                   1258:                lbuf_append(lbuf, ", ", NULL);
                   1259:            lbuf_append(lbuf, (*p)->bv_val, NULL);
                   1260:            count++;
                   1261:        }
                   1262:        ldap_value_free_len(bv);
                   1263:     }
                   1264:
                   1265:     lbuf_print(lbuf);          /* forces a newline */
                   1266:     return(count);
                   1267: }
1.1       millert  1268:
                   1269: /*
1.9       millert  1270:  * Print a record in the long form.
1.1       millert  1271:  */
                   1272: int
1.9       millert  1273: sudo_ldap_display_entry_long(ld, entry, lbuf)
1.1       millert  1274:     LDAP *ld;
                   1275:     LDAPMessage *entry;
1.9       millert  1276:     struct lbuf *lbuf;
1.1       millert  1277: {
1.9       millert  1278:     struct berval **bv, **p;
                   1279:     char *rdn;
                   1280:     int count = 0;
                   1281:
                   1282:     /* extract the dn, only show the first rdn */
                   1283:     rdn = sudo_ldap_get_first_rdn(ld, entry);
                   1284:     lbuf_print(lbuf);  /* force a newline */
                   1285:     lbuf_append(lbuf, "LDAP Role: ", rdn ? rdn : "UNKNOWN", NULL);
                   1286:     lbuf_print(lbuf);
                   1287:     if (rdn)
                   1288:        ldap_memfree(rdn);
                   1289:
                   1290:     /* get the RunAsUser Values from the entry */
                   1291:     lbuf_append(lbuf, "    RunAsUsers: ", NULL);
                   1292:     bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
                   1293:     if (bv == NULL)
                   1294:        bv = ldap_get_values_len(ld, entry, "sudoRunAs");
                   1295:     if (bv != NULL) {
                   1296:        for (p = bv; *p != NULL; p++) {
                   1297:            if (p != bv)
                   1298:                lbuf_append(lbuf, ", ", NULL);
                   1299:            lbuf_append(lbuf, (*p)->bv_val, NULL);
                   1300:        }
                   1301:        ldap_value_free_len(bv);
                   1302:     } else
                   1303:        lbuf_append(lbuf, def_runas_default, NULL);
                   1304:     lbuf_print(lbuf);
1.1       millert  1305:
1.9       millert  1306:     /* get the RunAsGroup Values from the entry */
                   1307:     bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
                   1308:     if (bv != NULL) {
                   1309:        lbuf_append(lbuf, "    RunAsGroups: ", NULL);
                   1310:        for (p = bv; *p != NULL; p++) {
                   1311:            if (p != bv)
                   1312:                lbuf_append(lbuf, ", ", NULL);
                   1313:            lbuf_append(lbuf, (*p)->bv_val, NULL);
                   1314:        }
                   1315:        ldap_value_free_len(bv);
                   1316:        lbuf_print(lbuf);
                   1317:     }
                   1318:
                   1319:     /* get the Option Values from the entry */
                   1320:     bv = ldap_get_values_len(ld, entry, "sudoOption");
                   1321:     if (bv != NULL) {
                   1322:        lbuf_append(lbuf, "    Options: ", NULL);
                   1323:        for (p = bv; *p != NULL; p++) {
                   1324:            if (p != bv)
                   1325:                lbuf_append(lbuf, ", ", NULL);
                   1326:            lbuf_append(lbuf, (*p)->bv_val, NULL);
                   1327:        }
                   1328:        ldap_value_free_len(bv);
                   1329:        lbuf_print(lbuf);
1.1       millert  1330:     }
                   1331:
                   1332:     /* get the Command Values from the entry */
1.9       millert  1333:     bv = ldap_get_values_len(ld, entry, "sudoCommand");
                   1334:     if (bv != NULL) {
                   1335:        lbuf_append(lbuf, "    Commands:", NULL);
                   1336:        lbuf_print(lbuf);
                   1337:        for (p = bv; *p != NULL; p++) {
                   1338:            lbuf_append(lbuf, "\t", (*p)->bv_val, NULL);
                   1339:            lbuf_print(lbuf);
                   1340:            count++;
                   1341:        }
                   1342:        ldap_value_free_len(bv);
1.1       millert  1343:     }
                   1344:
1.9       millert  1345:     return(count);
                   1346: }
                   1347:
                   1348: /*
                   1349:  * Like sudo_ldap_lookup(), except we just print entries.
                   1350:  */
                   1351: int
                   1352: sudo_ldap_display_privs(nss, pw, lbuf)
                   1353:     struct sudo_nss *nss;
                   1354:     struct passwd *pw;
                   1355:     struct lbuf *lbuf;
                   1356: {
                   1357:     LDAP *ld = (LDAP *) nss->handle;
                   1358:     LDAPMessage *entry = NULL, *result = NULL;
                   1359:     char *filt;
                   1360:     int rc, do_netgr, count = 0;
                   1361:
                   1362:     if (ld == NULL)
                   1363:        return(-1);
                   1364:
                   1365:     /*
                   1366:      * Okay - time to search for anything that matches this user
                   1367:      * Lets limit it to only two queries of the LDAP server
                   1368:      *
                   1369:      * The first pass will look by the username, groups, and
                   1370:      * the keyword ALL.  We will then inspect the results that
                   1371:      * came back from the query.  We don't need to inspect the
                   1372:      * sudoUser in this pass since the LDAP server already scanned
                   1373:      * it for us.
                   1374:      *
                   1375:      * The second pass will return all the entries that contain
                   1376:      * user netgroups.  Then we take the netgroups returned and
                   1377:      * try to match them against the username.
                   1378:      */
                   1379:     for (do_netgr = 0; do_netgr < 2; do_netgr++) {
                   1380:        filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw);
                   1381:        DPRINTF(("ldap search '%s'", filt), 1);
                   1382:        rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt,
1.10      millert  1383:            NULL, 0, NULL, NULL, NULL, 0, &result);
1.9       millert  1384:        efree(filt);
                   1385:        if (rc != LDAP_SUCCESS)
                   1386:            continue;   /* no entries for this pass */
                   1387:
                   1388:        /* print each matching entry */
                   1389:        LDAP_FOREACH(entry, ld, result) {
                   1390:            if ((!do_netgr ||
                   1391:                sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) &&
                   1392:                sudo_ldap_check_host(ld, entry)) {
                   1393:
                   1394:                if (long_list)
                   1395:                    count += sudo_ldap_display_entry_long(ld, entry, lbuf);
                   1396:                else
                   1397:                    count += sudo_ldap_display_entry_short(ld, entry, lbuf);
                   1398:            }
                   1399:        }
                   1400:        ldap_msgfree(result);
                   1401:        result = NULL;
                   1402:     }
                   1403:     return(count);
1.1       millert  1404: }
                   1405:
1.9       millert  1406: int
                   1407: sudo_ldap_display_cmnd(nss, pw)
                   1408:     struct sudo_nss *nss;
                   1409:     struct passwd *pw;
                   1410: {
                   1411:     LDAP *ld = (LDAP *) nss->handle;
                   1412:     LDAPMessage *entry = NULL, *result = NULL; /* used for searches */
                   1413:     char *filt;                                        /* used to parse attributes */
                   1414:     int rc, found, do_netgr;                   /* temp/final return values */
                   1415:
                   1416:     if (ld == NULL)
                   1417:        return(1);
                   1418:
                   1419:     /*
                   1420:      * Okay - time to search for anything that matches this user
                   1421:      * Lets limit it to only two queries of the LDAP server
                   1422:      *
                   1423:      * The first pass will look by the username, groups, and
                   1424:      * the keyword ALL.  We will then inspect the results that
                   1425:      * came back from the query.  We don't need to inspect the
                   1426:      * sudoUser in this pass since the LDAP server already scanned
                   1427:      * it for us.
                   1428:      *
                   1429:      * The second pass will return all the entries that contain
                   1430:      * user netgroups.  Then we take the netgroups returned and
                   1431:      * try to match them against the username.
                   1432:      */
                   1433:     for (found = FALSE, do_netgr = 0; !found && do_netgr < 2; do_netgr++) {
                   1434:        filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw);
                   1435:        DPRINTF(("ldap search '%s'", filt), 1);
                   1436:        rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt,
1.10      millert  1437:            NULL, 0, NULL, NULL, NULL, 0, &result);
1.9       millert  1438:        efree(filt);
                   1439:        if (rc != LDAP_SUCCESS)
                   1440:            continue;   /* no entries for this pass */
                   1441:
                   1442:        LDAP_FOREACH(entry, ld, result) {
                   1443:            if ((!do_netgr ||
                   1444:                sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) &&
                   1445:                sudo_ldap_check_host(ld, entry) &&
                   1446:                sudo_ldap_check_command(ld, entry, NULL) &&
                   1447:                sudo_ldap_check_runas(ld, entry)) {
                   1448:
                   1449:                found = TRUE;
                   1450:                break;
                   1451:            }
                   1452:        }
                   1453:        ldap_msgfree(result);
                   1454:        result = NULL;
                   1455:     }
                   1456:
                   1457:     if (found)
                   1458:        printf("%s%s%s\n", safe_cmnd ? safe_cmnd : user_cmnd,
                   1459:            user_args ? " " : "", user_args ? user_args : "");
                   1460:    return(!found);
                   1461: }
                   1462:
                   1463: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
                   1464: static int
                   1465: sudo_ldap_sasl_interact(ld, flags, _auth_id, _interact)
                   1466:     LDAP *ld;
                   1467:     unsigned int flags;
                   1468:     void *_auth_id;
                   1469:     void *_interact;
1.1       millert  1470: {
1.9       millert  1471:     char *auth_id = (char *)_auth_id;
                   1472:     sasl_interact_t *interact = (sasl_interact_t *)_interact;
                   1473:
                   1474:     for (; interact->id != SASL_CB_LIST_END; interact++) {
                   1475:        if (interact->id != SASL_CB_USER)
                   1476:            return(LDAP_PARAM_ERROR);
                   1477:
                   1478:        if (auth_id != NULL)
                   1479:            interact->result = auth_id;
                   1480:        else if (interact->defresult != NULL)
                   1481:            interact->result = interact->defresult;
                   1482:        else
                   1483:            interact->result = "";
                   1484:
                   1485:        interact->len = strlen(interact->result);
                   1486: #if SASL_VERSION_MAJOR < 2
                   1487:        interact->result = estrdup(interact->result);
                   1488: #endif /* SASL_VERSION_MAJOR < 2 */
                   1489:     }
                   1490:     return(LDAP_SUCCESS);
1.1       millert  1491: }
1.9       millert  1492: #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
1.1       millert  1493:
                   1494: /*
1.5       millert  1495:  * Set LDAP options based on the config table.
1.1       millert  1496:  */
1.5       millert  1497: int
                   1498: sudo_ldap_set_options(ld)
                   1499:     LDAP *ld;
1.1       millert  1500: {
1.5       millert  1501:     struct ldap_config_table *cur;
1.1       millert  1502:     int rc;
                   1503:
1.5       millert  1504:     /* Set ber options */
                   1505: #ifdef LBER_OPT_DEBUG_LEVEL
                   1506:     if (ldap_conf.ldap_debug)
                   1507:        ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
                   1508: #endif
1.1       millert  1509:
1.5       millert  1510:     /* Set simple LDAP options */
                   1511:     for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
                   1512:        LDAP *conn;
                   1513:        int ival;
                   1514:        char *sval;
1.1       millert  1515:
1.5       millert  1516:        if (cur->opt_val == -1)
                   1517:            continue;
1.1       millert  1518:
1.5       millert  1519:        conn = cur->connected ? ld : NULL;
                   1520:        switch (cur->type) {
                   1521:        case CONF_BOOL:
                   1522:        case CONF_INT:
                   1523:            ival = *(int *)(cur->valp);
                   1524:            if (ival >= 0) {
                   1525:                rc = ldap_set_option(conn, cur->opt_val, &ival);
                   1526:                if (rc != LDAP_OPT_SUCCESS) {
1.9       millert  1527:                    warningx("ldap_set_option: %s -> %d: %s",
1.5       millert  1528:                        cur->conf_str, ival, ldap_err2string(rc));
                   1529:                    return(-1);
                   1530:                }
                   1531:                DPRINTF(("ldap_set_option: %s -> %d", cur->conf_str, ival), 1);
                   1532:            }
                   1533:            break;
                   1534:        case CONF_STR:
                   1535:            sval = *(char **)(cur->valp);
                   1536:            if (sval != NULL) {
                   1537:                rc = ldap_set_option(conn, cur->opt_val, sval);
                   1538:                if (rc != LDAP_OPT_SUCCESS) {
1.9       millert  1539:                    warningx("ldap_set_option: %s -> %s: %s",
1.5       millert  1540:                        cur->conf_str, sval, ldap_err2string(rc));
                   1541:                    return(-1);
                   1542:                }
                   1543:                DPRINTF(("ldap_set_option: %s -> %s", cur->conf_str, sval), 1);
                   1544:            }
                   1545:            break;
                   1546:        }
                   1547:     }
1.1       millert  1548:
                   1549: #ifdef LDAP_OPT_NETWORK_TIMEOUT
1.5       millert  1550:     /* Convert bind_timelimit to a timeval */
1.1       millert  1551:     if (ldap_conf.bind_timelimit > 0) {
                   1552:        struct timeval tv;
                   1553:        tv.tv_sec = ldap_conf.bind_timelimit / 1000;
                   1554:        tv.tv_usec = 0;
                   1555:        rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
                   1556:        if (rc != LDAP_OPT_SUCCESS) {
1.9       millert  1557:            warningx("ldap_set_option(NETWORK_TIMEOUT, %ld): %s",
1.5       millert  1558:                (long)tv.tv_sec, ldap_err2string(rc));
                   1559:            return(-1);
                   1560:        }
                   1561:        DPRINTF(("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %ld)\n",
                   1562:            (long)tv.tv_sec), 1);
                   1563:     }
                   1564: #endif
                   1565:
                   1566: #if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
                   1567:     if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
                   1568:        int val = LDAP_OPT_X_TLS_HARD;
                   1569:        rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
                   1570:        if (rc != LDAP_SUCCESS) {
1.9       millert  1571:            warningx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
1.5       millert  1572:                ldap_err2string(rc));
                   1573:            return(-1);
                   1574:        }
1.6       millert  1575:        DPRINTF(("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD)\n"), 1);
1.5       millert  1576:     }
                   1577: #endif
                   1578:     return(0);
                   1579: }
                   1580:
                   1581: /*
1.9       millert  1582:  * Connect to the LDAP server specified by ld
                   1583:  */
                   1584: static int
                   1585: sudo_ldap_bind_s(ld)
                   1586:     LDAP *ld;
                   1587: {
                   1588:     int rc;
                   1589:     const char *old_ccname = user_ccname;
                   1590: #ifdef HAVE_GSS_KRB5_CCACHE_NAME
                   1591:     unsigned int status;
                   1592: #endif
                   1593:
                   1594: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
                   1595:     if (ldap_conf.rootuse_sasl == TRUE ||
                   1596:        (ldap_conf.rootuse_sasl != FALSE && ldap_conf.use_sasl == TRUE)) {
                   1597:        void *auth_id = ldap_conf.rootsasl_auth_id ?
                   1598:            ldap_conf.rootsasl_auth_id : ldap_conf.sasl_auth_id;
                   1599:
                   1600:        if (ldap_conf.krb5_ccname != NULL) {
                   1601: #ifdef HAVE_GSS_KRB5_CCACHE_NAME
                   1602:            if (gss_krb5_ccache_name(&status, ldap_conf.krb5_ccname, &old_ccname)
                   1603:                != GSS_S_COMPLETE) {
                   1604:                old_ccname = NULL;
                   1605:                DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
                   1606:            }
                   1607: #else
1.10      millert  1608:            setenv("KRB5CCNAME", ldap_conf.krb5_ccname, TRUE);
1.9       millert  1609: #endif
                   1610:        }
                   1611:        rc = ldap_sasl_interactive_bind_s(ld, ldap_conf.binddn, "GSSAPI",
                   1612:            NULL, NULL, LDAP_SASL_QUIET, sudo_ldap_sasl_interact, auth_id);
                   1613:        if (ldap_conf.krb5_ccname != NULL) {
                   1614: #ifdef HAVE_GSS_KRB5_CCACHE_NAME
                   1615:            if (gss_krb5_ccache_name(&status, old_ccname, NULL) != GSS_S_COMPLETE)
                   1616:                    DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
                   1617: #else
                   1618:            if (old_ccname != NULL)
1.10      millert  1619:                setenv("KRB5CCNAME", old_ccname, TRUE);
1.9       millert  1620:            else
1.10      millert  1621:                unsetenv("KRB5CCNAME");
1.9       millert  1622: #endif
                   1623:        }
                   1624:        if (rc != LDAP_SUCCESS) {
                   1625:            warningx("ldap_sasl_interactive_bind_s(): %s", ldap_err2string(rc));
                   1626:            return(-1);
                   1627:        }
                   1628:        DPRINTF(("ldap_sasl_interactive_bind_s() ok"), 1);
                   1629:     } else
                   1630: #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
                   1631: #ifdef HAVE_LDAP_SASL_BIND_S
                   1632:     {
                   1633:        struct berval bv;
                   1634:
                   1635:        bv.bv_val = ldap_conf.bindpw ? ldap_conf.bindpw : "";
                   1636:        bv.bv_len = strlen(bv.bv_val);
                   1637:
                   1638:        rc = ldap_sasl_bind_s(ld, ldap_conf.binddn, LDAP_SASL_SIMPLE, &bv,
                   1639:            NULL, NULL, NULL);
                   1640:        if (rc != LDAP_SUCCESS) {
                   1641:            warningx("ldap_sasl_bind_s(): %s", ldap_err2string(rc));
                   1642:            return(-1);
                   1643:        }
                   1644:        DPRINTF(("ldap_sasl_bind_s() ok"), 1);
                   1645:     }
                   1646: #else
                   1647:     {
                   1648:        rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw);
                   1649:        if (rc != LDAP_SUCCESS) {
                   1650:            warningx("ldap_simple_bind_s(): %s", ldap_err2string(rc));
                   1651:            return(-1);
                   1652:        }
                   1653:        DPRINTF(("ldap_simple_bind_s() ok"), 1);
                   1654:     }
                   1655: #endif
                   1656:     return(0);
                   1657: }
                   1658:
                   1659: /*
1.5       millert  1660:  * Open a connection to the LDAP server.
1.9       millert  1661:  * Returns 0 on success and non-zero on failure.
1.5       millert  1662:  */
1.9       millert  1663: int
                   1664: sudo_ldap_open(nss)
                   1665:     struct sudo_nss *nss;
1.5       millert  1666: {
1.9       millert  1667:     LDAP *ld;
                   1668:     int rc, ldapnoinit = FALSE;
1.5       millert  1669:
                   1670:     if (!sudo_ldap_read_config())
1.9       millert  1671:        return(-1);
                   1672:
                   1673:     /* Prevent reading of user ldaprc and system defaults. */
                   1674:     if (getenv("LDAPNOINIT") == NULL) {
                   1675:        ldapnoinit = TRUE;
1.10      millert  1676:        setenv("LDAPNOINIT", "1", TRUE);
1.9       millert  1677:     }
1.5       millert  1678:
                   1679:     /* Connect to LDAP server */
1.1       millert  1680: #ifdef HAVE_LDAP_INITIALIZE
1.6       millert  1681:     if (ldap_conf.uri != NULL) {
1.5       millert  1682:        DPRINTF(("ldap_initialize(ld, %s)", ldap_conf.uri), 2);
1.1       millert  1683:        rc = ldap_initialize(&ld, ldap_conf.uri);
                   1684:     } else
1.9       millert  1685: #endif
1.6       millert  1686:        rc = sudo_ldap_init(&ld, ldap_conf.host, ldap_conf.port);
                   1687:     if (rc != LDAP_SUCCESS) {
1.9       millert  1688:        warningx("unable to initialize LDAP: %s", ldap_err2string(rc));
                   1689:        return(-1);
1.1       millert  1690:     }
                   1691:
1.9       millert  1692:     if (ldapnoinit)
1.10      millert  1693:        unsetenv("LDAPNOINIT");
1.9       millert  1694:
1.5       millert  1695:     /* Set LDAP options */
                   1696:     if (sudo_ldap_set_options(ld) < 0)
1.9       millert  1697:        return(-1);
1.1       millert  1698:
1.5       millert  1699:     if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
1.10      millert  1700: #if defined(HAVE_LDAP_START_TLS_S)
1.1       millert  1701:        rc = ldap_start_tls_s(ld, NULL, NULL);
                   1702:        if (rc != LDAP_SUCCESS) {
1.9       millert  1703:            warningx("ldap_start_tls_s(): %s", ldap_err2string(rc));
                   1704:            return(-1);
1.1       millert  1705:        }
                   1706:        DPRINTF(("ldap_start_tls_s() ok"), 1);
1.10      millert  1707: #elif defined(HAVE_LDAP_SSL_CLIENT_INIT) && defined(HAVE_LDAP_START_TLS_S_NP)
                   1708:        if (ldap_ssl_client_init(NULL, NULL, 0, &rc) != LDAP_SUCCESS) {
                   1709:            warningx("ldap_ssl_client_init(): %s", ldap_err2string(rc));
                   1710:            return(-1);
                   1711:        }
                   1712:        rc = ldap_start_tls_s_np(ld, NULL);
                   1713:        if (rc != LDAP_SUCCESS) {
                   1714:            warningx("ldap_start_tls_s_np(): %s", ldap_err2string(rc));
                   1715:            return(-1);
                   1716:        }
                   1717:        DPRINTF(("ldap_start_tls_s_np() ok"), 1);
1.5       millert  1718: #else
1.10      millert  1719:        warningx("start_tls specified but LDAP libs do not support ldap_start_tls_s() or ldap_start_tls_s_np()");
                   1720: #endif /* !HAVE_LDAP_START_TLS_S && !HAVE_LDAP_START_TLS_S_NP */
1.1       millert  1721:     }
                   1722:
                   1723:     /* Actually connect */
1.9       millert  1724:     if (sudo_ldap_bind_s(ld) != 0)
                   1725:        return(-1);
1.1       millert  1726:
1.9       millert  1727:     nss->handle = ld;
                   1728:     return(0);
1.1       millert  1729: }
                   1730:
1.9       millert  1731: int
                   1732: sudo_ldap_setdefs(nss)
                   1733:     struct sudo_nss *nss;
1.1       millert  1734: {
1.9       millert  1735:     LDAP *ld = (LDAP *) nss->handle;
1.1       millert  1736:     LDAPMessage *entry = NULL, *result = NULL;  /* used for searches */
                   1737:     int rc;                                     /* temp return value */
                   1738:
1.9       millert  1739:     if (ld == NULL)
                   1740:        return(-1);
                   1741:
                   1742:     rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE,
1.10      millert  1743:        "cn=defaults", NULL, 0, NULL, NULL, NULL, 0, &result);
1.9       millert  1744:     if (rc == 0 && (entry = ldap_first_entry(ld, result))) {
1.1       millert  1745:        DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
                   1746:        sudo_ldap_parse_options(ld, entry);
                   1747:     } else
                   1748:        DPRINTF(("no default options found!"), 1);
                   1749:
                   1750:     if (result)
                   1751:        ldap_msgfree(result);
1.9       millert  1752:
                   1753:     return(0);
1.1       millert  1754: }
                   1755:
                   1756: /*
                   1757:  * like sudoers_lookup() - only LDAP style
                   1758:  */
                   1759: int
1.9       millert  1760: sudo_ldap_lookup(nss, ret, pwflag)
                   1761:     struct sudo_nss *nss;
                   1762:     int ret;
1.1       millert  1763:     int pwflag;
                   1764: {
1.9       millert  1765:     LDAP *ld = (LDAP *) nss->handle;
                   1766:     LDAPMessage *entry = NULL, *result = NULL;
                   1767:     char *filt;
                   1768:     int do_netgr, rc, matched;
1.4       millert  1769:     int setenv_implied;
1.9       millert  1770:     int ldap_user_matches = FALSE, ldap_host_matches = FALSE;
                   1771:     struct passwd *pw = list_pw ? list_pw : sudo_user.pw;
1.1       millert  1772:
1.9       millert  1773:     if (ld == NULL)
                   1774:        return(ret);
1.1       millert  1775:
1.9       millert  1776:     if (pwflag) {
                   1777:        int doauth = UNSPEC;
                   1778:        enum def_tupple pwcheck =
                   1779:            (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
                   1780:
                   1781:        for (matched = 0, do_netgr = 0; !matched && do_netgr < 2; do_netgr++) {
                   1782:            filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw);
                   1783:            rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt,
1.10      millert  1784:                NULL, 0, NULL, NULL, NULL, 0, &result);
1.9       millert  1785:            efree(filt);
                   1786:            if (rc != LDAP_SUCCESS)
                   1787:                continue;
                   1788:
                   1789:            LDAP_FOREACH(entry, ld, result) {
                   1790:                /* only verify netgroup matches in pass 2 */
                   1791:                if (do_netgr && !sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name))
                   1792:                    continue;
                   1793:
                   1794:                ldap_user_matches = TRUE;
                   1795:                if (sudo_ldap_check_host(ld, entry)) {
                   1796:                    ldap_host_matches = TRUE;
                   1797:                    if ((pwcheck == any && doauth != FALSE) ||
                   1798:                        (pwcheck == all && doauth == FALSE))
                   1799:                        doauth = sudo_ldap_check_bool(ld, entry, "authenticate");
                   1800:                    /* Only check the command when listing another user. */
                   1801:                    if (user_uid == 0 || list_pw == NULL ||
                   1802:                        user_uid == list_pw->pw_uid ||
                   1803:                        sudo_ldap_check_command(ld, entry, NULL)) {
                   1804:                        matched = 1;
                   1805:                        break;  /* end foreach */
                   1806:                    }
                   1807:                }
                   1808:            }
                   1809:            ldap_msgfree(result);
                   1810:            result = NULL;
                   1811:        }
                   1812:        if (matched || user_uid == 0) {
                   1813:            SET(ret, VALIDATE_OK);
                   1814:            CLR(ret, VALIDATE_NOT_OK);
                   1815:            if (def_authenticate) {
                   1816:                switch (pwcheck) {
                   1817:                    case always:
                   1818:                        SET(ret, FLAG_CHECK_USER);
                   1819:                        break;
                   1820:                    case all:
                   1821:                    case any:
                   1822:                        if (doauth == FALSE)
                   1823:                            def_authenticate = FALSE;
                   1824:                        break;
                   1825:                    case never:
                   1826:                        def_authenticate = FALSE;
                   1827:                        break;
                   1828:                    default:
                   1829:                        break;
                   1830:                }
                   1831:            }
                   1832:        }
                   1833:        goto done;
                   1834:     }
1.1       millert  1835:
                   1836:     /*
                   1837:      * Okay - time to search for anything that matches this user
                   1838:      * Lets limit it to only two queries of the LDAP server
                   1839:      *
                   1840:      * The first pass will look by the username, groups, and
                   1841:      * the keyword ALL.  We will then inspect the results that
                   1842:      * came back from the query.  We don't need to inspect the
                   1843:      * sudoUser in this pass since the LDAP server already scanned
                   1844:      * it for us.
                   1845:      *
                   1846:      * The second pass will return all the entries that contain
                   1847:      * user netgroups.  Then we take the netgroups returned and
                   1848:      * try to match them against the username.
                   1849:      */
1.4       millert  1850:     setenv_implied = FALSE;
1.9       millert  1851:     for (matched = 0, do_netgr = 0; !matched && do_netgr < 2; do_netgr++) {
                   1852:        filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw);
1.1       millert  1853:        DPRINTF(("ldap search '%s'", filt), 1);
1.9       millert  1854:        rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt,
1.10      millert  1855:            NULL, 0, NULL, NULL, NULL, 0, &result);
1.5       millert  1856:        if (rc != LDAP_SUCCESS)
1.1       millert  1857:            DPRINTF(("nothing found for '%s'", filt), 1);
                   1858:        efree(filt);
                   1859:
                   1860:        /* parse each entry returned from this most recent search */
1.9       millert  1861:        if (rc == LDAP_SUCCESS) {
                   1862:            LDAP_FOREACH(entry, ld, result) {
                   1863:                DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
                   1864:                if (
                   1865:                /* first verify user netgroup matches - only if in pass 2 */
                   1866:                    (!do_netgr || sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) &&
                   1867:                /* remember that user matched */
                   1868:                    (ldap_user_matches = TRUE) &&
                   1869:                /* verify host match */
                   1870:                    sudo_ldap_check_host(ld, entry) &&
                   1871:                /* remember that host matched */
                   1872:                    (ldap_host_matches = TRUE) &&
                   1873:                /* verify runas match */
                   1874:                    sudo_ldap_check_runas(ld, entry) &&
                   1875:                /* verify command match */
                   1876:                    (rc = sudo_ldap_check_command(ld, entry, &setenv_implied)) != UNSPEC
                   1877:                    ) {
                   1878:                    /* We have a match! */
                   1879:                    DPRINTF(("Command %sallowed", rc == TRUE ? "" : "NOT "), 1);
                   1880:                    matched = TRUE;
                   1881:                    if (rc == TRUE) {
                   1882:                        /* pick up any options */
                   1883:                        if (setenv_implied)
                   1884:                            def_setenv = TRUE;
                   1885:                        sudo_ldap_parse_options(ld, entry);
1.8       millert  1886: #ifdef HAVE_SELINUX
1.9       millert  1887:                        /* Set role and type if not specified on command line. */
                   1888:                        if (user_role == NULL)
                   1889:                            user_role = def_role;
                   1890:                        if (user_type == NULL)
                   1891:                            user_type = def_type;
1.8       millert  1892: #endif /* HAVE_SELINUX */
1.9       millert  1893:                        /* make sure we don't reenter loop */
                   1894:                        SET(ret, VALIDATE_OK);
                   1895:                        CLR(ret, VALIDATE_NOT_OK);
                   1896:                    } else {
                   1897:                        SET(ret, VALIDATE_NOT_OK);
                   1898:                        CLR(ret, VALIDATE_OK);
                   1899:                    }
                   1900:                    /* break from inside for loop */
                   1901:                    break;
                   1902:                }
1.1       millert  1903:            }
1.9       millert  1904:            ldap_msgfree(result);
                   1905:            result = NULL;
1.1       millert  1906:        }
                   1907:     }
                   1908:
1.9       millert  1909: done:
1.1       millert  1910:     DPRINTF(("user_matches=%d", ldap_user_matches), 1);
                   1911:     DPRINTF(("host_matches=%d", ldap_host_matches), 1);
                   1912:
1.9       millert  1913:     if (!ISSET(ret, VALIDATE_OK)) {
1.1       millert  1914:        /* we do not have a match */
1.9       millert  1915:        if (pwflag && list_pw == NULL)
1.1       millert  1916:            SET(ret, FLAG_NO_CHECK);
                   1917:     }
1.9       millert  1918:     if (ldap_user_matches)
                   1919:        CLR(ret, FLAG_NO_USER);
                   1920:     if (ldap_host_matches)
                   1921:        CLR(ret, FLAG_NO_HOST);
                   1922:     DPRINTF(("sudo_ldap_lookup(%d)=0x%02x", pwflag, ret), 1);
1.1       millert  1923:
                   1924:     return(ret);
                   1925: }
                   1926:
                   1927: /*
                   1928:  * shut down LDAP connection
                   1929:  */
1.9       millert  1930: int
                   1931: sudo_ldap_close(nss)
                   1932:     struct sudo_nss *nss;
                   1933: {
                   1934:     if (nss->handle != NULL) {
                   1935:        ldap_unbind_ext_s((LDAP *) nss->handle, NULL, NULL);
                   1936:        nss->handle = NULL;
                   1937:     }
                   1938:     return(0);
                   1939: }
                   1940:
                   1941: /*
                   1942:  * STUB
                   1943:  */
                   1944: int
                   1945: sudo_ldap_parse(nss)
                   1946:     struct sudo_nss *nss;
1.1       millert  1947: {
1.9       millert  1948:     return(0);
1.1       millert  1949: }