Annotation of embedaddon/sudo/plugins/sudoers/ldap.c, revision 1.1.1.3

1.1       misho       1: /*
                      2:  * Copyright (c) 2003-2011 Todd C. Miller <Todd.Miller@courtesan.com>
                      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: #endif /* HAVE_STRING_H */
                     37: #ifdef HAVE_STRINGS_H
                     38: # include <strings.h>
                     39: #endif /* HAVE_STRINGS_H */
                     40: #ifdef HAVE_UNISTD_H
                     41: # include <unistd.h>
                     42: #endif /* HAVE_UNISTD_H */
                     43: #if TIME_WITH_SYS_TIME
                     44: # include <time.h>
                     45: #endif
                     46: #include <ctype.h>
                     47: #include <pwd.h>
                     48: #include <grp.h>
                     49: #include <netinet/in.h>
                     50: #include <arpa/inet.h>
                     51: #include <netdb.h>
                     52: #ifdef HAVE_LBER_H
                     53: # include <lber.h>
                     54: #endif
                     55: #include <ldap.h>
                     56: #if defined(HAVE_LDAP_SSL_H)
                     57: # include <ldap_ssl.h>
                     58: #elif defined(HAVE_MPS_LDAP_SSL_H)
                     59: # include <mps/ldap_ssl.h>
                     60: #endif
                     61: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
                     62: # ifdef HAVE_SASL_SASL_H
                     63: #  include <sasl/sasl.h>
                     64: # else
                     65: #  include <sasl.h>
                     66: # endif
                     67: # if HAVE_GSS_KRB5_CCACHE_NAME
                     68: #  if defined(HAVE_GSSAPI_GSSAPI_KRB5_H)
                     69: #   include <gssapi/gssapi.h>
                     70: #   include <gssapi/gssapi_krb5.h>
                     71: #  elif defined(HAVE_GSSAPI_GSSAPI_H)
                     72: #   include <gssapi/gssapi.h>
                     73: #  else
                     74: #   include <gssapi.h>
                     75: #  endif
                     76: # endif
                     77: #endif
                     78: 
                     79: #include "sudoers.h"
                     80: #include "parse.h"
                     81: #include "lbuf.h"
                     82: 
1.1.1.2   misho      83: /* Older Netscape LDAP SDKs don't prototype ldapssl_set_strength() */
                     84: #if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(HAVE_LDAP_SSL_H) && !defined(HAVE_MPS_LDAP_SSL_H)
                     85: extern int ldapssl_set_strength(LDAP *ldap, int strength);
                     86: #endif
                     87: 
1.1.1.3 ! misho      88: #if !defined(LDAP_OPT_NETWORK_TIMEOUT) && defined(LDAP_OPT_CONNECT_TIMEOUT)
        !            89: # define LDAP_OPT_NETWORK_TIMEOUT LDAP_OPT_CONNECT_TIMEOUT
        !            90: #endif
        !            91: 
1.1       misho      92: #ifndef LDAP_OPT_SUCCESS
                     93: # define LDAP_OPT_SUCCESS LDAP_SUCCESS
                     94: #endif
                     95: 
                     96: #ifndef LDAPS_PORT
                     97: # define LDAPS_PORT 636
                     98: #endif
                     99: 
                    100: #if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && !defined(LDAP_SASL_QUIET)
                    101: # define LDAP_SASL_QUIET       0
                    102: #endif
                    103: 
                    104: #ifndef HAVE_LDAP_UNBIND_EXT_S
                    105: #define ldap_unbind_ext_s(a, b, c)     ldap_unbind_s(a)
                    106: #endif
                    107: 
                    108: #ifndef HAVE_LDAP_SEARCH_EXT_S
                    109: # ifdef HAVE_LDAP_SEARCH_ST
                    110: #  define ldap_search_ext_s(a, b, c, d, e, f, g, h, i, j, k)           \
                    111:        ldap_search_st(a, b, c, d, e, f, i, k)
                    112: # else
                    113: #  define ldap_search_ext_s(a, b, c, d, e, f, g, h, i, j, k)           \
                    114:        ldap_search_s(a, b, c, d, e, f, k)
                    115: # endif
                    116: #endif
                    117: 
                    118: #define LDAP_FOREACH(var, ld, res)                                     \
                    119:     for ((var) = ldap_first_entry((ld), (res));                                \
                    120:        (var) != NULL;                                                  \
                    121:        (var) = ldap_next_entry((ld), (var)))
                    122: 
                    123: #define        DPRINTF(args, level)    if (ldap_conf.debug >= level) warningx args
                    124: 
                    125: #define CONF_BOOL      0
                    126: #define CONF_INT       1
                    127: #define CONF_STR       2
                    128: #define CONF_LIST_STR  4
                    129: #define CONF_DEREF_VAL 5
                    130: 
1.1.1.2   misho     131: #define SUDO_LDAP_CLEAR                0
1.1       misho     132: #define SUDO_LDAP_SSL          1
                    133: #define SUDO_LDAP_STARTTLS     2
                    134: 
1.1.1.3 ! misho     135: /* The TIMEFILTER_LENGTH is the length of the filter when timed entries
1.1       misho     136:    are used. The length is computed as follows:
1.1.1.3 ! misho     137:        81       for the filter itself
        !           138:        + 2 * 17 for the now timestamp
1.1       misho     139: */
1.1.1.3 ! misho     140: #define TIMEFILTER_LENGTH      115
1.1       misho     141: 
                    142: /*
                    143:  * The ldap_search structure implements a linked list of ldap and
                    144:  * search result pointers, which allows us to remove them after
                    145:  * all search results have been combined in memory.
                    146:  * XXX - should probably be a tailq since we do appends
                    147:  */
                    148: struct ldap_search_list {
                    149:     LDAP *ldap;
                    150:     LDAPMessage *searchresult;
                    151:     struct ldap_search_list *next;
                    152: };
                    153: 
                    154: /*
                    155:  * The ldap_entry_wrapper structure is used to implement sorted result entries.
                    156:  * A double is used for the order to allow for insertion of new entries
                    157:  * without having to renumber everything.
                    158:  * Note: there is no standard floating point type in LDAP.
                    159:  *       As a result, some LDAP servers will only allow an integer.
                    160:  */
                    161: struct ldap_entry_wrapper {
                    162:     LDAPMessage        *entry;
                    163:     double order;
                    164: };
                    165: 
                    166: /*
                    167:  * The ldap_result structure contains the list of matching searches as
                    168:  * well as an array of all result entries sorted by the sudoOrder attribute.
                    169:  */
                    170: struct ldap_result {
                    171:     struct ldap_search_list *searches;
                    172:     struct ldap_entry_wrapper *entries;
                    173:     int allocated_entries;
                    174:     int nentries;
                    175:     int user_matches;
                    176:     int host_matches;
                    177: };
                    178: #define        ALLOCATION_INCREMENT    100
                    179: 
                    180: struct ldap_config_table {
                    181:     const char *conf_str;      /* config file string */
1.1.1.2   misho     182:     int type;                  /* CONF_BOOL, CONF_INT, CONF_STR */
1.1       misho     183:     int opt_val;               /* LDAP_OPT_* (or -1 for sudo internal) */
                    184:     void *valp;                        /* pointer into ldap_conf */
                    185: };
                    186: 
                    187: struct ldap_config_list_str {
                    188:     struct ldap_config_list_str *next;
                    189:     char val[1];
                    190: };
                    191: 
                    192: /* LDAP configuration structure */
                    193: static struct ldap_config {
                    194:     int port;
                    195:     int version;
                    196:     int debug;
                    197:     int ldap_debug;
                    198:     int tls_checkpeer;
                    199:     int timelimit;
                    200:     int timeout;
                    201:     int bind_timelimit;
                    202:     int use_sasl;
                    203:     int rootuse_sasl;
                    204:     int ssl_mode;
                    205:     int timed;
                    206:     int deref;
                    207:     char *host;
                    208:     struct ldap_config_list_str *uri;
                    209:     char *binddn;
                    210:     char *bindpw;
                    211:     char *rootbinddn;
                    212:     struct ldap_config_list_str *base;
                    213:     char *search_filter;
                    214:     char *ssl;
                    215:     char *tls_cacertfile;
                    216:     char *tls_cacertdir;
                    217:     char *tls_random_file;
                    218:     char *tls_cipher_suite;
                    219:     char *tls_certfile;
                    220:     char *tls_keyfile;
1.1.1.3 ! misho     221:     char *tls_keypw;
1.1       misho     222:     char *sasl_auth_id;
                    223:     char *rootsasl_auth_id;
                    224:     char *sasl_secprops;
                    225:     char *krb5_ccname;
                    226: } ldap_conf;
                    227: 
1.1.1.2   misho     228: static struct ldap_config_table ldap_conf_global[] = {
                    229:     { "sudoers_debug", CONF_INT, -1, &ldap_conf.debug },
                    230:     { "host", CONF_STR, -1, &ldap_conf.host },
                    231:     { "port", CONF_INT, -1, &ldap_conf.port },
                    232:     { "ssl", CONF_STR, -1, &ldap_conf.ssl },
                    233:     { "sslpath", CONF_STR, -1, &ldap_conf.tls_certfile },
                    234:     { "uri", CONF_LIST_STR, -1, &ldap_conf.uri },
1.1       misho     235: #ifdef LDAP_OPT_DEBUG_LEVEL
1.1.1.2   misho     236:     { "debug", CONF_INT, LDAP_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug },
1.1       misho     237: #endif
                    238: #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
1.1.1.2   misho     239:     { "tls_checkpeer", CONF_BOOL, LDAP_OPT_X_TLS_REQUIRE_CERT,
1.1       misho     240:        &ldap_conf.tls_checkpeer },
                    241: #else
1.1.1.2   misho     242:     { "tls_checkpeer", CONF_BOOL, -1, &ldap_conf.tls_checkpeer },
1.1       misho     243: #endif
                    244: #ifdef LDAP_OPT_X_TLS_CACERTFILE
1.1.1.2   misho     245:     { "tls_cacertfile", CONF_STR, LDAP_OPT_X_TLS_CACERTFILE,
1.1       misho     246:        &ldap_conf.tls_cacertfile },
1.1.1.2   misho     247:     { "tls_cacert", CONF_STR, LDAP_OPT_X_TLS_CACERTFILE,
1.1       misho     248:        &ldap_conf.tls_cacertfile },
                    249: #endif
                    250: #ifdef LDAP_OPT_X_TLS_CACERTDIR
1.1.1.2   misho     251:     { "tls_cacertdir", CONF_STR, LDAP_OPT_X_TLS_CACERTDIR,
1.1       misho     252:        &ldap_conf.tls_cacertdir },
                    253: #endif
                    254: #ifdef LDAP_OPT_X_TLS_RANDOM_FILE
1.1.1.2   misho     255:     { "tls_randfile", CONF_STR, LDAP_OPT_X_TLS_RANDOM_FILE,
1.1       misho     256:        &ldap_conf.tls_random_file },
                    257: #endif
                    258: #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
1.1.1.2   misho     259:     { "tls_ciphers", CONF_STR, LDAP_OPT_X_TLS_CIPHER_SUITE,
1.1       misho     260:        &ldap_conf.tls_cipher_suite },
1.1.1.3 ! misho     261: #elif defined(LDAP_OPT_SSL_CIPHER)
        !           262:     { "tls_ciphers", CONF_STR, LDAP_OPT_SSL_CIPHER,
        !           263:        &ldap_conf.tls_cipher_suite },
1.1       misho     264: #endif
                    265: #ifdef LDAP_OPT_X_TLS_CERTFILE
1.1.1.2   misho     266:     { "tls_cert", CONF_STR, LDAP_OPT_X_TLS_CERTFILE,
1.1       misho     267:        &ldap_conf.tls_certfile },
                    268: #else
1.1.1.2   misho     269:     { "tls_cert", CONF_STR, -1, &ldap_conf.tls_certfile },
1.1       misho     270: #endif
                    271: #ifdef LDAP_OPT_X_TLS_KEYFILE
1.1.1.2   misho     272:     { "tls_key", CONF_STR, LDAP_OPT_X_TLS_KEYFILE,
1.1       misho     273:        &ldap_conf.tls_keyfile },
                    274: #else
1.1.1.2   misho     275:     { "tls_key", CONF_STR, -1, &ldap_conf.tls_keyfile },
                    276: #endif
1.1.1.3 ! misho     277: #ifdef HAVE_LDAP_SSL_CLIENT_INIT
        !           278:     { "tls_keypw", CONF_STR, -1, &ldap_conf.tls_keypw },
        !           279: #endif
1.1.1.2   misho     280:     { "binddn", CONF_STR, -1, &ldap_conf.binddn },
                    281:     { "bindpw", CONF_STR, -1, &ldap_conf.bindpw },
                    282:     { "rootbinddn", CONF_STR, -1, &ldap_conf.rootbinddn },
                    283:     { "sudoers_base", CONF_LIST_STR, -1, &ldap_conf.base },
                    284:     { "sudoers_timed", CONF_BOOL, -1, &ldap_conf.timed },
                    285:     { "sudoers_search_filter", CONF_STR, -1, &ldap_conf.search_filter },
                    286: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
                    287:     { "use_sasl", CONF_BOOL, -1, &ldap_conf.use_sasl },
                    288:     { "sasl_auth_id", CONF_STR, -1, &ldap_conf.sasl_auth_id },
                    289:     { "rootuse_sasl", CONF_BOOL, -1, &ldap_conf.rootuse_sasl },
                    290:     { "rootsasl_auth_id", CONF_STR, -1, &ldap_conf.rootsasl_auth_id },
                    291:     { "krb5_ccname", CONF_STR, -1, &ldap_conf.krb5_ccname },
                    292: #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
                    293:     { NULL }
                    294: };
                    295: 
                    296: static struct ldap_config_table ldap_conf_conn[] = {
                    297: #ifdef LDAP_OPT_PROTOCOL_VERSION
                    298:     { "ldap_version", CONF_INT, LDAP_OPT_PROTOCOL_VERSION,
                    299:        &ldap_conf.version },
1.1       misho     300: #endif
                    301: #ifdef LDAP_OPT_NETWORK_TIMEOUT
1.1.1.2   misho     302:     { "bind_timelimit", CONF_INT, -1 /* needs timeval, set manually */,
1.1       misho     303:        &ldap_conf.bind_timelimit },
1.1.1.2   misho     304:     { "network_timeout", CONF_INT, -1 /* needs timeval, set manually */,
1.1       misho     305:        &ldap_conf.bind_timelimit },
                    306: #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
1.1.1.2   misho     307:     { "bind_timelimit", CONF_INT, LDAP_X_OPT_CONNECT_TIMEOUT,
1.1       misho     308:        &ldap_conf.bind_timelimit },
1.1.1.2   misho     309:     { "network_timeout", CONF_INT, LDAP_X_OPT_CONNECT_TIMEOUT,
1.1       misho     310:        &ldap_conf.bind_timelimit },
                    311: #endif
1.1.1.2   misho     312:     { "timelimit", CONF_INT, LDAP_OPT_TIMELIMIT, &ldap_conf.timelimit },
1.1       misho     313: #ifdef LDAP_OPT_TIMEOUT
1.1.1.2   misho     314:     { "timeout", CONF_INT, -1 /* needs timeval, set manually */,
1.1       misho     315:        &ldap_conf.timeout },
                    316: #endif
                    317: #ifdef LDAP_OPT_DEREF
1.1.1.2   misho     318:     { "deref", CONF_DEREF_VAL, LDAP_OPT_DEREF, &ldap_conf.deref },
1.1       misho     319: #endif
1.1.1.2   misho     320: #ifdef LDAP_OPT_X_SASL_SECPROPS
                    321:     { "sasl_secprops", CONF_STR, LDAP_OPT_X_SASL_SECPROPS,
1.1       misho     322:        &ldap_conf.sasl_secprops },
1.1.1.2   misho     323: #endif
1.1       misho     324:     { NULL }
                    325: };
                    326: 
                    327: /* sudo_nss implementation */
                    328: static int sudo_ldap_open(struct sudo_nss *nss);
                    329: static int sudo_ldap_close(struct sudo_nss *nss);
                    330: static int sudo_ldap_parse(struct sudo_nss *nss);
                    331: static int sudo_ldap_setdefs(struct sudo_nss *nss);
                    332: static int sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag);
                    333: static int sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw);
                    334: static int sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw,
                    335:     struct lbuf *lbuf);
                    336: static int sudo_ldap_display_bound_defaults(struct sudo_nss *nss,
                    337:     struct passwd *pw, struct lbuf *lbuf);
                    338: static int sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
                    339:     struct lbuf *lbuf);
                    340: static struct ldap_result *sudo_ldap_result_get(struct sudo_nss *nss,
                    341:     struct passwd *pw);
                    342: 
                    343: /*
                    344:  * LDAP sudo_nss handle.
                    345:  * We store the connection to the LDAP server, the cached ldap_result object
                    346:  * (if any), and the name of the user the query was performed for.
                    347:  * If a new query is launched with sudo_ldap_result_get() that specifies a
                    348:  * different user, the old cached result is freed before the new query is run.
                    349:  */
                    350: struct sudo_ldap_handle {
                    351:     LDAP *ld;
                    352:     struct ldap_result *result;
                    353:     char *username;
                    354:     struct group_list *grlist;
                    355: };
                    356: 
                    357: struct sudo_nss sudo_nss_ldap = {
                    358:     &sudo_nss_ldap,
                    359:     NULL,
                    360:     sudo_ldap_open,
                    361:     sudo_ldap_close,
                    362:     sudo_ldap_parse,
                    363:     sudo_ldap_setdefs,
                    364:     sudo_ldap_lookup,
                    365:     sudo_ldap_display_cmnd,
                    366:     sudo_ldap_display_defaults,
                    367:     sudo_ldap_display_bound_defaults,
                    368:     sudo_ldap_display_privs
                    369: };
                    370: 
                    371: #ifdef HAVE_LDAP_CREATE
                    372: /*
                    373:  * Rebuild the hosts list and include a specific port for each host.
                    374:  * ldap_create() does not take a default port parameter so we must
                    375:  * append one if we want something other than LDAP_PORT.
                    376:  */
                    377: static void
                    378: sudo_ldap_conf_add_ports(void)
                    379: {
                    380: 
                    381:     char *host, *port, defport[13];
                    382:     char hostbuf[LINE_MAX * 2];
1.1.1.2   misho     383:     debug_decl(sudo_ldap_conf_add_ports, SUDO_DEBUG_LDAP)
1.1       misho     384: 
                    385:     hostbuf[0] = '\0';
                    386:     if (snprintf(defport, sizeof(defport), ":%d", ldap_conf.port) >= sizeof(defport))
                    387:        errorx(1, _("sudo_ldap_conf_add_ports: port too large"));
                    388: 
                    389:     for ((host = strtok(ldap_conf.host, " \t")); host; (host = strtok(NULL, " \t"))) {
                    390:        if (hostbuf[0] != '\0') {
                    391:            if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
                    392:                goto toobig;
                    393:        }
                    394: 
                    395:        if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
                    396:            goto toobig;
                    397:        /* Append port if there is not one already. */
                    398:        if ((port = strrchr(host, ':')) == NULL ||
                    399:            !isdigit((unsigned char)port[1])) {
                    400:            if (strlcat(hostbuf, defport, sizeof(hostbuf)) >= sizeof(hostbuf))
                    401:                goto toobig;
                    402:        }
                    403:     }
                    404: 
                    405:     efree(ldap_conf.host);
                    406:     ldap_conf.host = estrdup(hostbuf);
1.1.1.2   misho     407:     debug_return;
1.1       misho     408: 
                    409: toobig:
                    410:     errorx(1, _("sudo_ldap_conf_add_ports: out of space expanding hostbuf"));
                    411: }
                    412: #endif
                    413: 
                    414: #ifndef HAVE_LDAP_INITIALIZE
                    415: /*
                    416:  * For each uri, convert to host:port pairs.  For ldaps:// enable SSL
                    417:  * Accepts: uris of the form ldap:/// or ldap://hostname:portnum/
                    418:  * where the trailing slash is optional.
                    419:  */
                    420: static int
                    421: sudo_ldap_parse_uri(const struct ldap_config_list_str *uri_list)
                    422: {
                    423:     char *buf, *uri, *host, *cp, *port;
                    424:     char hostbuf[LINE_MAX];
                    425:     int nldap = 0, nldaps = 0;
                    426:     int rc = -1;
1.1.1.2   misho     427:     debug_decl(sudo_ldap_parse_uri, SUDO_DEBUG_LDAP)
1.1       misho     428: 
                    429:     do {
                    430:        buf = estrdup(uri_list->val);
                    431:        hostbuf[0] = '\0';
                    432:        for ((uri = strtok(buf, " \t")); uri != NULL; (uri = strtok(NULL, " \t"))) {
                    433:            if (strncasecmp(uri, "ldap://", 7) == 0) {
                    434:                nldap++;
                    435:                host = uri + 7;
                    436:            } else if (strncasecmp(uri, "ldaps://", 8) == 0) {
                    437:                nldaps++;
                    438:                host = uri + 8;
                    439:            } else {
                    440:                warningx(_("unsupported LDAP uri type: %s"), uri);
                    441:                goto done;
                    442:            }
                    443: 
                    444:            /* trim optional trailing slash */
                    445:            if ((cp = strrchr(host, '/')) != NULL && cp[1] == '\0') {
                    446:                *cp = '\0';
                    447:            }
                    448: 
                    449:            if (hostbuf[0] != '\0') {
                    450:                if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
                    451:                    goto toobig;
                    452:            }
                    453: 
                    454:            if (*host == '\0')
                    455:                host = "localhost";             /* no host specified, use localhost */
                    456: 
                    457:            if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
                    458:                goto toobig;
                    459: 
                    460:            /* If using SSL and no port specified, add port 636 */
                    461:            if (nldaps) {
                    462:                if ((port = strrchr(host, ':')) == NULL ||
                    463:                    !isdigit((unsigned char)port[1]))
                    464:                    if (strlcat(hostbuf, ":636", sizeof(hostbuf)) >= sizeof(hostbuf))
                    465:                        goto toobig;
                    466:            }
                    467:        }
                    468:        if (hostbuf[0] == '\0') {
1.1.1.2   misho     469:            warningx(_("invalid uri: %s"), uri_list->val);
1.1       misho     470:            goto done;
                    471:        }
                    472: 
                    473:        if (nldaps != 0) {
                    474:            if (nldap != 0) {
                    475:                warningx(_("unable to mix ldap and ldaps URIs"));
                    476:                goto done;
                    477:            }
                    478:            if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
                    479:                warningx(_("unable to mix ldaps and starttls"));
                    480:                goto done;
                    481:            }
                    482:            ldap_conf.ssl_mode = SUDO_LDAP_SSL;
                    483:        }
                    484: 
                    485:        efree(ldap_conf.host);
                    486:        ldap_conf.host = estrdup(hostbuf);
                    487:        efree(buf);
                    488:     } while ((uri_list = uri_list->next));
                    489: 
                    490:     buf = NULL;
                    491:     rc = 0;
                    492: 
                    493: done:
                    494:     efree(buf);
1.1.1.2   misho     495:     debug_return_int(rc);
1.1       misho     496: 
                    497: toobig:
                    498:     errorx(1, _("sudo_ldap_parse_uri: out of space building hostbuf"));
                    499: }
                    500: #else
                    501: static char *
                    502: sudo_ldap_join_uri(struct ldap_config_list_str *uri_list)
                    503: {
                    504:     struct ldap_config_list_str *uri;
                    505:     size_t len = 0;
                    506:     char *buf, *cp;
1.1.1.2   misho     507:     debug_decl(sudo_ldap_join_uri, SUDO_DEBUG_LDAP)
1.1       misho     508: 
                    509:     /* Usually just a single entry. */
                    510:     if (uri_list->next == NULL)
1.1.1.2   misho     511:        debug_return_str(estrdup(uri_list->val));
1.1       misho     512: 
                    513:     for (uri = uri_list; uri != NULL; uri = uri->next) {
                    514:        len += strlen(uri->val) + 1;
                    515:     }
                    516:     buf = cp = emalloc(len);
                    517:     buf[0] = '\0';
                    518:     for (uri = uri_list; uri != NULL; uri = uri->next) {
                    519:        cp += strlcpy(cp, uri->val, len - (cp - buf));
                    520:        *cp++ = ' ';
                    521:     }
                    522:     cp[-1] = '\0';
1.1.1.2   misho     523:     debug_return_str(buf);
1.1       misho     524: }
                    525: #endif /* HAVE_LDAP_INITIALIZE */
                    526: 
                    527: static int
                    528: sudo_ldap_init(LDAP **ldp, const char *host, int port)
                    529: {
                    530:     LDAP *ld = NULL;
                    531:     int rc = LDAP_CONNECT_ERROR;
1.1.1.2   misho     532:     debug_decl(sudo_ldap_init, SUDO_DEBUG_LDAP)
1.1       misho     533: 
                    534: #ifdef HAVE_LDAPSSL_INIT
1.1.1.2   misho     535:     if (ldap_conf.ssl_mode != SUDO_LDAP_CLEAR) {
                    536:        const int defsecure = ldap_conf.ssl_mode == SUDO_LDAP_SSL;
1.1       misho     537:        DPRINTF(("ldapssl_clientauth_init(%s, %s)",
                    538:            ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
                    539:            ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
                    540:        rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
                    541:            ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
                    542:        /*
1.1.1.2   misho     543:         * Starting with version 5.0, Mozilla-derived LDAP SDKs require
                    544:         * the cert and key paths to be a directory, not a file.
                    545:         * If the user specified a file and it fails, try the parent dir.
1.1       misho     546:         */
                    547:        if (rc != LDAP_SUCCESS) {
1.1.1.2   misho     548:            bool retry = false;
                    549:            if (ldap_conf.tls_certfile != NULL) {
                    550:                char *cp = strrchr(ldap_conf.tls_certfile, '/');
                    551:                if (cp != NULL && strncmp(cp + 1, "cert", 4) == 0) {
1.1       misho     552:                    *cp = '\0';
1.1.1.2   misho     553:                    retry = true;
                    554:                }
1.1       misho     555:            }
1.1.1.2   misho     556:            if (ldap_conf.tls_keyfile != NULL) {
                    557:                char *cp = strrchr(ldap_conf.tls_keyfile, '/');
                    558:                if (cp != NULL && strncmp(cp + 1, "key", 3) == 0) {
1.1       misho     559:                    *cp = '\0';
1.1.1.2   misho     560:                    retry = true;
                    561:                }
1.1       misho     562:            }
1.1.1.2   misho     563:            if (retry) {
                    564:                DPRINTF(("ldapssl_clientauth_init(%s, %s)",
                    565:                    ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
                    566:                    ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
                    567:                rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
                    568:                    ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
1.1       misho     569:            }
                    570:        }
1.1.1.2   misho     571:        if (rc != LDAP_SUCCESS) {
                    572:            warningx(_("unable to initialize SSL cert and key db: %s"),
                    573:                ldapssl_err2string(rc));
                    574:            if (ldap_conf.tls_certfile == NULL)
                    575:                warningx(_("you must set TLS_CERT in %s to use SSL"),
                    576:                    _PATH_LDAP_CONF);
                    577:            goto done;
                    578:        }
1.1       misho     579: 
1.1.1.2   misho     580:        DPRINTF(("ldapssl_init(%s, %d, %d)", host, port, defsecure), 2);
                    581:        if ((ld = ldapssl_init(host, port, defsecure)) != NULL)
1.1       misho     582:            rc = LDAP_SUCCESS;
                    583:     } else
1.1.1.3 ! misho     584: #elif defined(HAVE_LDAP_SSL_INIT) && defined(HAVE_LDAP_SSL_CLIENT_INIT)
        !           585:     if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
        !           586:        if (ldap_ssl_client_init(ldap_conf.tls_keyfile, ldap_conf.tls_keypw, 0, &rc) != LDAP_SUCCESS) {
        !           587:            warningx("ldap_ssl_client_init(): %s", ldap_err2string(rc));
        !           588:            debug_return_int(-1);
        !           589:        }
        !           590:        DPRINTF(("ldap_ssl_init(%s, %d, NULL)", host, port), 2);
        !           591:        if ((ld = ldap_ssl_init((char *)host, port, NULL)) != NULL)
        !           592:            rc = LDAP_SUCCESS;
        !           593:     } else
1.1       misho     594: #endif
                    595:     {
                    596: #ifdef HAVE_LDAP_CREATE
                    597:        DPRINTF(("ldap_create()"), 2);
                    598:        if ((rc = ldap_create(&ld)) != LDAP_SUCCESS)
                    599:            goto done;
                    600:        DPRINTF(("ldap_set_option(LDAP_OPT_HOST_NAME, %s)", host), 2);
                    601:        rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, host);
                    602: #else
                    603:        DPRINTF(("ldap_init(%s, %d)", host, port), 2);
1.1.1.3 ! misho     604:        if ((ld = ldap_init((char *)host, port)) != NULL)
1.1       misho     605:            rc = LDAP_SUCCESS;
                    606: #endif
                    607:     }
                    608: 
                    609: done:
                    610:     *ldp = ld;
1.1.1.2   misho     611:     debug_return_int(rc);
1.1       misho     612: }
                    613: 
                    614: /*
1.1.1.2   misho     615:  * Walk through search results and return true if we have a matching
                    616:  * netgroup, else false.
1.1       misho     617:  */
1.1.1.2   misho     618: static bool
1.1       misho     619: sudo_ldap_check_user_netgroup(LDAP *ld, LDAPMessage *entry, char *user)
                    620: {
                    621:     struct berval **bv, **p;
                    622:     char *val;
1.1.1.2   misho     623:     int ret = false;
                    624:     debug_decl(sudo_ldap_check_user_netgroup, SUDO_DEBUG_LDAP)
1.1       misho     625: 
                    626:     if (!entry)
1.1.1.2   misho     627:        debug_return_bool(ret);
1.1       misho     628: 
                    629:     /* get the values from the entry */
                    630:     bv = ldap_get_values_len(ld, entry, "sudoUser");
                    631:     if (bv == NULL)
1.1.1.2   misho     632:        debug_return_bool(ret);
1.1       misho     633: 
                    634:     /* walk through values */
                    635:     for (p = bv; *p != NULL && !ret; p++) {
                    636:        val = (*p)->bv_val;
                    637:        /* match any */
                    638:        if (netgr_matches(val, NULL, NULL, user))
1.1.1.2   misho     639:            ret = true;
1.1       misho     640:        DPRINTF(("ldap sudoUser netgroup '%s' ... %s", val,
                    641:            ret ? "MATCH!" : "not"), 2 + ((ret) ? 0 : 1));
                    642:     }
                    643: 
                    644:     ldap_value_free_len(bv);   /* cleanup */
                    645: 
1.1.1.2   misho     646:     debug_return_bool(ret);
1.1       misho     647: }
                    648: 
                    649: /*
1.1.1.2   misho     650: * Walk through search results and return true if we have a
                    651: * host match, else false.
                    652: */
                    653: static bool
1.1       misho     654: sudo_ldap_check_host(LDAP *ld, LDAPMessage *entry)
                    655: {
                    656:     struct berval **bv, **p;
                    657:     char *val;
1.1.1.2   misho     658:     bool ret = false;
                    659:     debug_decl(sudo_ldap_check_host, SUDO_DEBUG_LDAP)
1.1       misho     660: 
                    661:     if (!entry)
1.1.1.2   misho     662:        debug_return_bool(ret);
1.1       misho     663: 
                    664:     /* get the values from the entry */
                    665:     bv = ldap_get_values_len(ld, entry, "sudoHost");
                    666:     if (bv == NULL)
1.1.1.2   misho     667:        debug_return_bool(ret);
1.1       misho     668: 
                    669:     /* walk through values */
                    670:     for (p = bv; *p != NULL && !ret; p++) {
                    671:        val = (*p)->bv_val;
                    672:        /* match any or address or netgroup or hostname */
                    673:        if (!strcmp(val, "ALL") || addr_matches(val) ||
                    674:            netgr_matches(val, user_host, user_shost, NULL) ||
                    675:            hostname_matches(user_shost, user_host, val))
1.1.1.2   misho     676:            ret = true;
1.1       misho     677:        DPRINTF(("ldap sudoHost '%s' ... %s", val,
                    678:            ret ? "MATCH!" : "not"), 2);
                    679:     }
                    680: 
                    681:     ldap_value_free_len(bv);   /* cleanup */
                    682: 
1.1.1.2   misho     683:     debug_return_bool(ret);
1.1       misho     684: }
                    685: 
                    686: static int
                    687: sudo_ldap_check_runas_user(LDAP *ld, LDAPMessage *entry)
                    688: {
                    689:     struct berval **bv, **p;
                    690:     char *val;
1.1.1.2   misho     691:     bool ret = false;
                    692:     debug_decl(sudo_ldap_check_runas_user, SUDO_DEBUG_LDAP)
1.1       misho     693: 
                    694:     if (!runas_pw)
1.1.1.2   misho     695:        debug_return_bool(UNSPEC);
1.1       misho     696: 
                    697:     /* get the runas user from the entry */
                    698:     bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
                    699:     if (bv == NULL)
                    700:        bv = ldap_get_values_len(ld, entry, "sudoRunAs"); /* old style */
                    701: 
                    702:     /*
                    703:      * BUG:
                    704:      * 
                    705:      * if runas is not specified on the command line, the only information
                    706:      * as to which user to run as is in the runas_default option.  We should
                    707:      * check to see if we have the local option present.  Unfortunately we
                    708:      * don't parse these options until after this routine says yes or no.
                    709:      * The query has already returned, so we could peek at the attribute
                    710:      * values here though.
                    711:      * 
                    712:      * For now just require users to always use -u option unless its set
                    713:      * in the global defaults. This behaviour is no different than the global
                    714:      * /etc/sudoers.
                    715:      * 
                    716:      * Sigh - maybe add this feature later
                    717:      */
                    718: 
                    719:     /*
                    720:      * If there are no runas entries, match runas_default against
                    721:      * what the user specified on the command line.
                    722:      */
                    723:     if (bv == NULL)
1.1.1.2   misho     724:        debug_return_bool(!strcasecmp(runas_pw->pw_name, def_runas_default));
1.1       misho     725: 
                    726:     /* walk through values returned, looking for a match */
                    727:     for (p = bv; *p != NULL && !ret; p++) {
                    728:        val = (*p)->bv_val;
                    729:        switch (val[0]) {
                    730:        case '+':
                    731:            if (netgr_matches(val, NULL, NULL, runas_pw->pw_name))
1.1.1.2   misho     732:                ret = true;
1.1       misho     733:            break;
                    734:        case '%':
                    735:            if (usergr_matches(val, runas_pw->pw_name, runas_pw))
1.1.1.2   misho     736:                ret = true;
1.1       misho     737:            break;
                    738:        case 'A':
                    739:            if (strcmp(val, "ALL") == 0) {
1.1.1.2   misho     740:                ret = true;
1.1       misho     741:                break;
                    742:            }
                    743:            /* FALLTHROUGH */
                    744:        default:
                    745:            if (strcasecmp(val, runas_pw->pw_name) == 0)
1.1.1.2   misho     746:                ret = true;
1.1       misho     747:            break;
                    748:        }
                    749:        DPRINTF(("ldap sudoRunAsUser '%s' ... %s", val,
                    750:            ret ? "MATCH!" : "not"), 2);
                    751:     }
                    752: 
                    753:     ldap_value_free_len(bv);   /* cleanup */
                    754: 
1.1.1.2   misho     755:     debug_return_bool(ret);
1.1       misho     756: }
                    757: 
                    758: static int
                    759: sudo_ldap_check_runas_group(LDAP *ld, LDAPMessage *entry)
                    760: {
                    761:     struct berval **bv, **p;
                    762:     char *val;
1.1.1.2   misho     763:     bool ret = false;
                    764:     debug_decl(sudo_ldap_check_runas_group, SUDO_DEBUG_LDAP)
1.1       misho     765: 
                    766:     /* runas_gr is only set if the user specified the -g flag */
                    767:     if (!runas_gr)
1.1.1.2   misho     768:        debug_return_bool(UNSPEC);
1.1       misho     769: 
                    770:     /* get the values from the entry */
                    771:     bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
                    772:     if (bv == NULL)
1.1.1.2   misho     773:        debug_return_bool(ret);
1.1       misho     774: 
                    775:     /* walk through values returned, looking for a match */
                    776:     for (p = bv; *p != NULL && !ret; p++) {
                    777:        val = (*p)->bv_val;
                    778:        if (strcmp(val, "ALL") == 0 || group_matches(val, runas_gr))
1.1.1.2   misho     779:            ret = true;
1.1       misho     780:        DPRINTF(("ldap sudoRunAsGroup '%s' ... %s", val,
                    781:            ret ? "MATCH!" : "not"), 2);
                    782:     }
                    783: 
                    784:     ldap_value_free_len(bv);   /* cleanup */
                    785: 
1.1.1.2   misho     786:     debug_return_bool(ret);
1.1       misho     787: }
                    788: 
                    789: /*
1.1.1.2   misho     790:  * Walk through search results and return true if we have a runas match,
                    791:  * else false.  RunAs info is optional.
1.1       misho     792:  */
1.1.1.2   misho     793: static bool
1.1       misho     794: sudo_ldap_check_runas(LDAP *ld, LDAPMessage *entry)
                    795: {
1.1.1.2   misho     796:     bool ret;
                    797:     debug_decl(sudo_ldap_check_runas, SUDO_DEBUG_LDAP)
1.1       misho     798: 
                    799:     if (!entry)
1.1.1.2   misho     800:        debug_return_bool(false);
1.1       misho     801: 
1.1.1.2   misho     802:     ret = sudo_ldap_check_runas_user(ld, entry) != false &&
                    803:        sudo_ldap_check_runas_group(ld, entry) != false;
1.1       misho     804: 
1.1.1.2   misho     805:     debug_return_bool(ret);
1.1       misho     806: }
                    807: 
                    808: /*
1.1.1.2   misho     809:  * Walk through search results and return true if we have a command match,
                    810:  * false if disallowed and UNSPEC if not matched.
1.1       misho     811:  */
                    812: static int
                    813: sudo_ldap_check_command(LDAP *ld, LDAPMessage *entry, int *setenv_implied)
                    814: {
                    815:     struct berval **bv, **p;
                    816:     char *allowed_cmnd, *allowed_args, *val;
1.1.1.2   misho     817:     bool foundbang;
                    818:     int ret = UNSPEC;
                    819:     debug_decl(sudo_ldap_check_command, SUDO_DEBUG_LDAP)
1.1       misho     820: 
                    821:     if (!entry)
1.1.1.2   misho     822:        debug_return_bool(ret);
1.1       misho     823: 
                    824:     bv = ldap_get_values_len(ld, entry, "sudoCommand");
                    825:     if (bv == NULL)
1.1.1.2   misho     826:        debug_return_bool(ret);
1.1       misho     827: 
1.1.1.2   misho     828:     for (p = bv; *p != NULL && ret != false; p++) {
1.1       misho     829:        val = (*p)->bv_val;
                    830:        /* Match against ALL ? */
                    831:        if (!strcmp(val, "ALL")) {
1.1.1.2   misho     832:            ret = true;
1.1       misho     833:            if (setenv_implied != NULL)
1.1.1.2   misho     834:                *setenv_implied = true;
1.1       misho     835:            DPRINTF(("ldap sudoCommand '%s' ... MATCH!", val), 2);
                    836:            continue;
                    837:        }
                    838: 
                    839:        /* check for !command */
                    840:        if (*val == '!') {
1.1.1.2   misho     841:            foundbang = true;
1.1       misho     842:            allowed_cmnd = estrdup(1 + val);    /* !command */
                    843:        } else {
1.1.1.2   misho     844:            foundbang = false;
1.1       misho     845:            allowed_cmnd = estrdup(val);        /* command */
                    846:        }
                    847: 
                    848:        /* split optional args away from command */
                    849:        allowed_args = strchr(allowed_cmnd, ' ');
                    850:        if (allowed_args)
                    851:            *allowed_args++ = '\0';
                    852: 
                    853:        /* check the command like normal */
                    854:        if (command_matches(allowed_cmnd, allowed_args)) {
                    855:            /*
                    856:             * If allowed (no bang) set ret but keep on checking.
                    857:             * If disallowed (bang), exit loop.
                    858:             */
1.1.1.2   misho     859:            ret = foundbang ? false : true;
1.1       misho     860:        }
                    861:        DPRINTF(("ldap sudoCommand '%s' ... %s", val,
1.1.1.2   misho     862:            ret == true ? "MATCH!" : "not"), 2);
1.1       misho     863: 
                    864:        efree(allowed_cmnd);    /* cleanup */
                    865:     }
                    866: 
                    867:     ldap_value_free_len(bv);   /* more cleanup */
                    868: 
1.1.1.2   misho     869:     debug_return_bool(ret);
1.1       misho     870: }
                    871: 
                    872: /*
                    873:  * Search for boolean "option" in sudoOption.
1.1.1.2   misho     874:  * Returns true if found and allowed, false if negated, else UNSPEC.
1.1       misho     875:  */
                    876: static int
                    877: sudo_ldap_check_bool(LDAP *ld, LDAPMessage *entry, char *option)
                    878: {
                    879:     struct berval **bv, **p;
                    880:     char ch, *var;
                    881:     int ret = UNSPEC;
1.1.1.2   misho     882:     debug_decl(sudo_ldap_check_bool, SUDO_DEBUG_LDAP)
1.1       misho     883: 
                    884:     if (entry == NULL)
1.1.1.2   misho     885:        debug_return_bool(ret);
1.1       misho     886: 
                    887:     bv = ldap_get_values_len(ld, entry, "sudoOption");
                    888:     if (bv == NULL)
1.1.1.2   misho     889:        debug_return_bool(ret);
1.1       misho     890: 
                    891:     /* walk through options */
                    892:     for (p = bv; *p != NULL; p++) {
                    893:        var = (*p)->bv_val;;
                    894:        DPRINTF(("ldap sudoOption: '%s'", var), 2);
                    895: 
                    896:        if ((ch = *var) == '!')
                    897:            var++;
                    898:        if (strcmp(var, option) == 0)
                    899:            ret = (ch != '!');
                    900:     }
                    901: 
                    902:     ldap_value_free_len(bv);
                    903: 
1.1.1.2   misho     904:     debug_return_bool(ret);
1.1       misho     905: }
                    906: 
                    907: /*
                    908:  * Read sudoOption and modify the defaults as we go.  This is used once
                    909:  * from the cn=defaults entry and also once when a final sudoRole is matched.
                    910:  */
                    911: static void
                    912: sudo_ldap_parse_options(LDAP *ld, LDAPMessage *entry)
                    913: {
                    914:     struct berval **bv, **p;
                    915:     char op, *var, *val;
1.1.1.2   misho     916:     debug_decl(sudo_ldap_parse_options, SUDO_DEBUG_LDAP)
1.1       misho     917: 
                    918:     if (entry == NULL)
1.1.1.2   misho     919:        debug_return;
1.1       misho     920: 
                    921:     bv = ldap_get_values_len(ld, entry, "sudoOption");
                    922:     if (bv == NULL)
1.1.1.2   misho     923:        debug_return;
1.1       misho     924: 
                    925:     /* walk through options */
                    926:     for (p = bv; *p != NULL; p++) {
                    927:        var = estrdup((*p)->bv_val);
                    928:        DPRINTF(("ldap sudoOption: '%s'", var), 2);
                    929: 
                    930:        /* check for equals sign past first char */
                    931:        val = strchr(var, '=');
                    932:        if (val > var) {
                    933:            *val++ = '\0';      /* split on = and truncate var */
                    934:            op = *(val - 2);    /* peek for += or -= cases */
                    935:            if (op == '+' || op == '-') {
                    936:                *(val - 2) = '\0';      /* found, remove extra char */
                    937:                /* case var+=val or var-=val */
                    938:                set_default(var, val, (int) op);
                    939:            } else {
                    940:                /* case var=val */
1.1.1.2   misho     941:                set_default(var, val, true);
1.1       misho     942:            }
                    943:        } else if (*var == '!') {
                    944:            /* case !var Boolean False */
1.1.1.2   misho     945:            set_default(var + 1, NULL, false);
1.1       misho     946:        } else {
                    947:            /* case var Boolean True */
1.1.1.2   misho     948:            set_default(var, NULL, true);
1.1       misho     949:        }
                    950:        efree(var);
                    951:     }
                    952: 
                    953:     ldap_value_free_len(bv);
1.1.1.2   misho     954: 
                    955:     debug_return;
1.1       misho     956: }
                    957: 
                    958: /*
                    959:  * Build an LDAP timefilter.
                    960:  *
                    961:  * Stores a filter in the buffer that makes sure only entries
                    962:  * are selected that have a sudoNotBefore in the past and a
                    963:  * sudoNotAfter in the future, i.e. a filter of the following
                    964:  * structure (spaced out a little more for better readability:
                    965:  *
                    966:  * (&
                    967:  *   (|
                    968:  *     (!(sudoNotAfter=*))
                    969:  *     (sudoNotAfter>__now__)
                    970:  *   )
                    971:  *   (|
                    972:  *     (!(sudoNotBefore=*))
                    973:  *     (sudoNotBefore<__now__)
                    974:  *   )
                    975:  * )
                    976:  *
                    977:  * If either the sudoNotAfter or sudoNotBefore attributes are missing,
                    978:  * no time restriction shall be imposed.
                    979:  */
                    980: static int
                    981: sudo_ldap_timefilter(char *buffer, size_t buffersize)
                    982: {
                    983:     struct tm *tp;
                    984:     time_t now;
1.1.1.3 ! misho     985:     char timebuffer[sizeof("20120727121554.0Z")];
1.1       misho     986:     int bytes = 0;
1.1.1.2   misho     987:     debug_decl(sudo_ldap_timefilter, SUDO_DEBUG_LDAP)
1.1       misho     988: 
                    989:     /* Make sure we have a formatted timestamp for __now__. */
                    990:     time(&now);
                    991:     if ((tp = gmtime(&now)) == NULL) {
                    992:        warning(_("unable to get GMT time"));
                    993:        goto done;
                    994:     }
                    995: 
                    996:     /* Format the timestamp according to the RFC. */
1.1.1.3 ! misho     997:     if (strftime(timebuffer, sizeof(timebuffer), "%Y%m%d%H%M%S.0Z", tp) == 0) {
        !           998:        warningx(_("unable to format timestamp"));
1.1       misho     999:        goto done;
                   1000:     }
                   1001: 
                   1002:     /* Build filter. */
                   1003:     bytes = snprintf(buffer, buffersize, "(&(|(!(sudoNotAfter=*))(sudoNotAfter>=%s))(|(!(sudoNotBefore=*))(sudoNotBefore<=%s)))",
                   1004:        timebuffer, timebuffer);
                   1005:     if (bytes < 0 || bytes >= buffersize) {
                   1006:        warning(_("unable to build time filter"));
                   1007:        bytes = 0;
                   1008:     }
                   1009: 
                   1010: done:
1.1.1.2   misho    1011:     debug_return_int(bytes);
1.1       misho    1012: }
                   1013: 
                   1014: /*
                   1015:  * Builds up a filter to search for default settings
                   1016:  */
                   1017: static char *
1.1.1.2   misho    1018: sudo_ldap_build_default_filter(void)
1.1       misho    1019: {
                   1020:     char *filt;
1.1.1.2   misho    1021:     debug_decl(sudo_ldap_build_default_filter, SUDO_DEBUG_LDAP)
1.1       misho    1022: 
                   1023:     if (ldap_conf.search_filter)
                   1024:        easprintf(&filt, "(&%s(cn=defaults))", ldap_conf.search_filter);
                   1025:     else
                   1026:        filt = estrdup("cn=defaults");
1.1.1.2   misho    1027:     debug_return_str(filt);
                   1028: }
                   1029: 
                   1030: /*
                   1031:  * Determine length of query value after escaping characters
                   1032:  * as per RFC 4515.
                   1033:  */
                   1034: static size_t
                   1035: sudo_ldap_value_len(const char *value)
                   1036: {
                   1037:     const char *s;
                   1038:     size_t len = 0;
                   1039: 
                   1040:     for (s = value; *s != '\0'; s++) {
                   1041:        switch (*s) {
                   1042:        case '\\':
                   1043:        case '(':
                   1044:        case ')':
                   1045:        case '*':
                   1046:            len += 2;
                   1047:            break;
                   1048:        }
                   1049:     }
                   1050:     len += (size_t)(s - value);
                   1051:     return len;
                   1052: }
                   1053: 
                   1054: /*
                   1055:  * Like strlcat() but escapes characters as per RFC 4515.
                   1056:  */
                   1057: static size_t
                   1058: sudo_ldap_value_cat(char *dst, const char *src, size_t size)
                   1059: {
                   1060:     char *d = dst;
                   1061:     const char *s = src;
                   1062:     size_t n = size;
                   1063:     size_t dlen;
                   1064: 
                   1065:     /* Find the end of dst and adjust bytes left but don't go past end */
                   1066:     while (n-- != 0 && *d != '\0')
                   1067:        d++;
                   1068:     dlen = d - dst;
                   1069:     n = size - dlen;
                   1070: 
                   1071:     if (n == 0)
                   1072:        return dlen + strlen(s);
                   1073:     while (*s != '\0') {
                   1074:        switch (*s) {
                   1075:        case '\\':
                   1076:            if (n < 3)
                   1077:                goto done;
                   1078:            *d++ = '\\';
                   1079:            *d++ = '5';
                   1080:            *d++ = 'c';
                   1081:            n -= 3;
                   1082:            break;
                   1083:        case '(':
                   1084:            if (n < 3)
                   1085:                goto done;
                   1086:            *d++ = '\\';
                   1087:            *d++ = '2';
                   1088:            *d++ = '8';
                   1089:            n -= 3;
                   1090:            break;
                   1091:        case ')':
                   1092:            if (n < 3)
                   1093:                goto done;
                   1094:            *d++ = '\\';
                   1095:            *d++ = '2';
                   1096:            *d++ = '9';
                   1097:            n -= 3;
                   1098:            break;
                   1099:        case '*':
                   1100:            if (n < 3)
                   1101:                goto done;
                   1102:            *d++ = '\\';
                   1103:            *d++ = '2';
                   1104:            *d++ = 'a';
                   1105:            n -= 3;
                   1106:            break;
                   1107:        default:
                   1108:            if (n < 1)
                   1109:                goto done;
                   1110:            *d++ = *s;
                   1111:            n--;
                   1112:            break;
                   1113:        }
                   1114:        s++;
                   1115:     }
                   1116: done:
                   1117:     *d = '\0';
                   1118:     while (*s != '\0')
                   1119:        s++;
                   1120:     return dlen + (s - src);   /* count does not include NUL */
1.1       misho    1121: }
                   1122: 
                   1123: /*
                   1124:  * Builds up a filter to check against LDAP.
                   1125:  */
                   1126: static char *
                   1127: sudo_ldap_build_pass1(struct passwd *pw)
                   1128: {
                   1129:     struct group *grp;
1.1.1.3 ! misho    1130:     char *buf, timebuffer[TIMEFILTER_LENGTH + 1], gidbuf[MAX_UID_T_LEN + 1];
1.1       misho    1131:     struct group_list *grlist;
                   1132:     size_t sz = 0;
                   1133:     int i;
1.1.1.2   misho    1134:     debug_decl(sudo_ldap_build_pass1, SUDO_DEBUG_LDAP)
1.1       misho    1135: 
1.1.1.3 ! misho    1136:     /* If there is a filter, allocate space for the global AND. */
        !          1137:     if (ldap_conf.timed || ldap_conf.search_filter)
        !          1138:        sz += 3;
        !          1139: 
        !          1140:     /* Add LDAP search filter if present. */
1.1       misho    1141:     if (ldap_conf.search_filter)
1.1.1.3 ! misho    1142:        sz += strlen(ldap_conf.search_filter);
1.1       misho    1143: 
                   1144:     /* Then add (|(sudoUser=USERNAME)(sudoUser=ALL)) + NUL */
1.1.1.2   misho    1145:     sz += 29 + sudo_ldap_value_len(pw->pw_name);
1.1       misho    1146: 
1.1.1.2   misho    1147:     /* Add space for primary and supplementary groups and gids */
1.1       misho    1148:     if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
1.1.1.2   misho    1149:        sz += 12 + sudo_ldap_value_len(grp->gr_name);
1.1       misho    1150:     }
1.1.1.2   misho    1151:     sz += 13 + MAX_UID_T_LEN;
1.1.1.3 ! misho    1152:     if ((grlist = sudo_get_grlist(pw)) != NULL) {
1.1       misho    1153:        for (i = 0; i < grlist->ngroups; i++) {
                   1154:            if (grp != NULL && strcasecmp(grlist->groups[i], grp->gr_name) == 0)
                   1155:                continue;
1.1.1.2   misho    1156:            sz += 12 + sudo_ldap_value_len(grlist->groups[i]);
                   1157:        }
                   1158:        for (i = 0; i < grlist->ngids; i++) {
                   1159:            if (pw->pw_gid == grlist->gids[i])
                   1160:                continue;
                   1161:            sz += 13 + MAX_UID_T_LEN;
1.1       misho    1162:        }
                   1163:     }
                   1164: 
                   1165:     /* If timed, add space for time limits. */
                   1166:     if (ldap_conf.timed)
                   1167:        sz += TIMEFILTER_LENGTH;
                   1168:     buf = emalloc(sz);
                   1169:     *buf = '\0';
                   1170: 
                   1171:     /*
                   1172:      * If timed or using a search filter, start a global AND clause to
                   1173:      * contain the search filter, search criteria, and time restriction.
                   1174:      */
                   1175:     if (ldap_conf.timed || ldap_conf.search_filter)
                   1176:        (void) strlcpy(buf, "(&", sz);
                   1177: 
                   1178:     if (ldap_conf.search_filter)
                   1179:        (void) strlcat(buf, ldap_conf.search_filter, sz);
                   1180: 
                   1181:     /* Global OR + sudoUser=user_name filter */
                   1182:     (void) strlcat(buf, "(|(sudoUser=", sz);
1.1.1.2   misho    1183:     (void) sudo_ldap_value_cat(buf, pw->pw_name, sz);
1.1       misho    1184:     (void) strlcat(buf, ")", sz);
                   1185: 
1.1.1.2   misho    1186:     /* Append primary group and gid */
1.1       misho    1187:     if (grp != NULL) {
                   1188:        (void) strlcat(buf, "(sudoUser=%", sz);
1.1.1.2   misho    1189:        (void) sudo_ldap_value_cat(buf, grp->gr_name, sz);
1.1       misho    1190:        (void) strlcat(buf, ")", sz);
                   1191:     }
1.1.1.2   misho    1192:     (void) snprintf(gidbuf, sizeof(gidbuf), "%u", (unsigned int)pw->pw_gid);
                   1193:     (void) strlcat(buf, "(sudoUser=%#", sz);
                   1194:     (void) strlcat(buf, gidbuf, sz);
                   1195:     (void) strlcat(buf, ")", sz);
1.1       misho    1196: 
1.1.1.2   misho    1197:     /* Append supplementary groups and gids */
1.1       misho    1198:     if (grlist != NULL) {
                   1199:        for (i = 0; i < grlist->ngroups; i++) {
                   1200:            if (grp != NULL && strcasecmp(grlist->groups[i], grp->gr_name) == 0)
                   1201:                continue;
                   1202:            (void) strlcat(buf, "(sudoUser=%", sz);
1.1.1.2   misho    1203:            (void) sudo_ldap_value_cat(buf, grlist->groups[i], sz);
                   1204:            (void) strlcat(buf, ")", sz);
                   1205:        }
                   1206:        for (i = 0; i < grlist->ngids; i++) {
                   1207:            if (pw->pw_gid == grlist->gids[i])
                   1208:                continue;
                   1209:            (void) snprintf(gidbuf, sizeof(gidbuf), "%u",
                   1210:                (unsigned int)grlist->gids[i]);
                   1211:            (void) strlcat(buf, "(sudoUser=%#", sz);
                   1212:            (void) strlcat(buf, gidbuf, sz);
1.1       misho    1213:            (void) strlcat(buf, ")", sz);
                   1214:        }
                   1215:     }
                   1216: 
                   1217:     /* Done with groups. */
                   1218:     if (grlist != NULL)
1.1.1.3 ! misho    1219:        sudo_grlist_delref(grlist);
1.1       misho    1220:     if (grp != NULL)
1.1.1.3 ! misho    1221:        sudo_gr_delref(grp);
1.1       misho    1222: 
                   1223:     /* Add ALL to list and end the global OR */
                   1224:     if (strlcat(buf, "(sudoUser=ALL)", sz) >= sz)
                   1225:        errorx(1, _("sudo_ldap_build_pass1 allocation mismatch"));
                   1226: 
                   1227:     /* Add the time restriction, or simply end the global OR. */
                   1228:     if (ldap_conf.timed) {
                   1229:        strlcat(buf, ")", sz); /* closes the global OR */
                   1230:        sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
                   1231:        strlcat(buf, timebuffer, sz);
                   1232:     } else if (ldap_conf.search_filter) {
                   1233:        strlcat(buf, ")", sz); /* closes the global OR */
                   1234:     }
                   1235:     strlcat(buf, ")", sz); /* closes the global OR or the global AND */
                   1236: 
1.1.1.2   misho    1237:     debug_return_str(buf);
1.1       misho    1238: }
                   1239: 
                   1240: /*
                   1241:  * Builds up a filter to check against netgroup entries in LDAP.
                   1242:  */
                   1243: static char *
                   1244: sudo_ldap_build_pass2(void)
                   1245: {
1.1.1.3 ! misho    1246:     char *filt, timebuffer[TIMEFILTER_LENGTH + 1];
1.1.1.2   misho    1247:     debug_decl(sudo_ldap_build_pass2, SUDO_DEBUG_LDAP)
1.1       misho    1248: 
                   1249:     if (ldap_conf.timed)
                   1250:        sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
                   1251: 
                   1252:     /*
                   1253:      * Match all sudoUsers beginning with a '+'.
                   1254:      * If a search filter or time restriction is specified, 
                   1255:      * those get ANDed in to the expression.
                   1256:      */
                   1257:     easprintf(&filt, "%s%s(sudoUser=+*)%s%s",
                   1258:        (ldap_conf.timed || ldap_conf.search_filter) ? "(&" : "",
                   1259:        ldap_conf.search_filter ? ldap_conf.search_filter : "",
                   1260:        ldap_conf.timed ? timebuffer : "",
                   1261:        (ldap_conf.timed || ldap_conf.search_filter) ? ")" : "");
                   1262: 
1.1.1.2   misho    1263:     debug_return_str(filt);
1.1       misho    1264: }
                   1265: 
                   1266: static void
                   1267: sudo_ldap_read_secret(const char *path)
                   1268: {
                   1269:     FILE *fp;
                   1270:     char buf[LINE_MAX], *cp;
1.1.1.2   misho    1271:     debug_decl(sudo_ldap_read_secret, SUDO_DEBUG_LDAP)
1.1       misho    1272: 
                   1273:     if ((fp = fopen(_PATH_LDAP_SECRET, "r")) != NULL) {
                   1274:        if (fgets(buf, sizeof(buf), fp) != NULL) {
                   1275:            if ((cp = strchr(buf, '\n')) != NULL)
                   1276:                *cp = '\0';
                   1277:            /* copy to bindpw and binddn */
                   1278:            efree(ldap_conf.bindpw);
                   1279:            ldap_conf.bindpw = estrdup(buf);
                   1280:            efree(ldap_conf.binddn);
                   1281:            ldap_conf.binddn = ldap_conf.rootbinddn;
                   1282:            ldap_conf.rootbinddn = NULL;
                   1283:        }
                   1284:        fclose(fp);
                   1285:     }
1.1.1.2   misho    1286:     debug_return;
1.1       misho    1287: }
                   1288: 
1.1.1.2   misho    1289: /*
                   1290:  * Look up keyword in config tables.
                   1291:  * Returns true if found, else false.
                   1292:  */
                   1293: static bool
                   1294: sudo_ldap_parse_keyword(const char *keyword, const char *value,
                   1295:     struct ldap_config_table *table)
                   1296: {
                   1297:     struct ldap_config_table *cur;
                   1298:     debug_decl(sudo_ldap_parse_keyword, SUDO_DEBUG_LDAP)
                   1299: 
                   1300:     /* Look up keyword in config tables */
                   1301:     for (cur = table; cur->conf_str != NULL; cur++) {
                   1302:        if (strcasecmp(keyword, cur->conf_str) == 0) {
                   1303:            switch (cur->type) {
                   1304:            case CONF_DEREF_VAL:
                   1305:                if (strcasecmp(value, "searching") == 0)
                   1306:                    *(int *)(cur->valp) = LDAP_DEREF_SEARCHING;
                   1307:                else if (strcasecmp(value, "finding") == 0)
                   1308:                    *(int *)(cur->valp) = LDAP_DEREF_FINDING;
                   1309:                else if (strcasecmp(value, "always") == 0)
                   1310:                    *(int *)(cur->valp) = LDAP_DEREF_ALWAYS;
                   1311:                else
                   1312:                    *(int *)(cur->valp) = LDAP_DEREF_NEVER;
                   1313:                break;
                   1314:            case CONF_BOOL:
                   1315:                *(int *)(cur->valp) = atobool(value) == true;
                   1316:                break;
                   1317:            case CONF_INT:
                   1318:                *(int *)(cur->valp) = atoi(value);
                   1319:                break;
                   1320:            case CONF_STR:
                   1321:                efree(*(char **)(cur->valp));
                   1322:                *(char **)(cur->valp) = estrdup(value);
                   1323:                break;
                   1324:            case CONF_LIST_STR:
                   1325:                {
                   1326:                    struct ldap_config_list_str **p;
                   1327:                    size_t len = strlen(value);
                   1328: 
                   1329:                    if (len > 0) {
                   1330:                        p = (struct ldap_config_list_str **)cur->valp;
                   1331:                        while (*p != NULL)
                   1332:                            p = &(*p)->next;
                   1333:                        *p = emalloc(sizeof(struct ldap_config_list_str) + len);
                   1334:                        memcpy((*p)->val, value, len + 1);
                   1335:                        (*p)->next = NULL;
                   1336:                    }
                   1337:                }
                   1338:                break;
                   1339:            }
                   1340:            debug_return_bool(true);
                   1341:        }
                   1342:     }
                   1343:     debug_return_bool(false);
                   1344: }
                   1345: 
                   1346: static bool
1.1       misho    1347: sudo_ldap_read_config(void)
                   1348: {
                   1349:     FILE *fp;
                   1350:     char *cp, *keyword, *value;
1.1.1.2   misho    1351:     debug_decl(sudo_ldap_read_config, SUDO_DEBUG_LDAP)
1.1       misho    1352: 
                   1353:     /* defaults */
                   1354:     ldap_conf.version = 3;
                   1355:     ldap_conf.port = -1;
                   1356:     ldap_conf.tls_checkpeer = -1;
                   1357:     ldap_conf.timelimit = -1;
                   1358:     ldap_conf.timeout = -1;
                   1359:     ldap_conf.bind_timelimit = -1;
                   1360:     ldap_conf.use_sasl = -1;
                   1361:     ldap_conf.rootuse_sasl = -1;
                   1362:     ldap_conf.deref = -1;
                   1363: 
                   1364:     if ((fp = fopen(_PATH_LDAP_CONF, "r")) == NULL)
1.1.1.2   misho    1365:        debug_return_bool(false);
1.1       misho    1366: 
                   1367:     while ((cp = sudo_parseln(fp)) != NULL) {
                   1368:        if (*cp == '\0')
                   1369:            continue;           /* skip empty line */
                   1370: 
                   1371:        /* split into keyword and value */
                   1372:        keyword = cp;
                   1373:        while (*cp && !isblank((unsigned char) *cp))
                   1374:            cp++;
                   1375:        if (*cp)
                   1376:            *cp++ = '\0';       /* terminate keyword */
                   1377: 
                   1378:        /* skip whitespace before value */
                   1379:        while (isblank((unsigned char) *cp))
                   1380:            cp++;
                   1381:        value = cp;
                   1382: 
1.1.1.2   misho    1383:        /* Look up keyword in config tables */
                   1384:        if (!sudo_ldap_parse_keyword(keyword, value, ldap_conf_global))
                   1385:            sudo_ldap_parse_keyword(keyword, value, ldap_conf_conn);
1.1       misho    1386:     }
                   1387:     fclose(fp);
                   1388: 
                   1389:     if (!ldap_conf.host)
                   1390:        ldap_conf.host = estrdup("localhost");
                   1391: 
                   1392:     if (ldap_conf.debug > 1) {
                   1393:        sudo_printf(SUDO_CONV_ERROR_MSG, "LDAP Config Summary\n");
                   1394:        sudo_printf(SUDO_CONV_ERROR_MSG, "===================\n");
                   1395:        if (ldap_conf.uri) {
                   1396:            struct ldap_config_list_str *uri = ldap_conf.uri;
                   1397: 
                   1398:            do {
                   1399:                sudo_printf(SUDO_CONV_ERROR_MSG, "uri              %s\n",
                   1400:                    uri->val);
                   1401:            } while ((uri = uri->next) != NULL);
                   1402:        } else {
                   1403:            sudo_printf(SUDO_CONV_ERROR_MSG, "host             %s\n",
                   1404:                ldap_conf.host ?  ldap_conf.host : "(NONE)");
                   1405:            sudo_printf(SUDO_CONV_ERROR_MSG, "port             %d\n",
                   1406:                ldap_conf.port);
                   1407:        }
                   1408:        sudo_printf(SUDO_CONV_ERROR_MSG, "ldap_version     %d\n",
                   1409:            ldap_conf.version);
                   1410: 
                   1411:        if (ldap_conf.base) {
                   1412:            struct ldap_config_list_str *base = ldap_conf.base;
                   1413:            do {
                   1414:                sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base     %s\n",
                   1415:                    base->val);
                   1416:            } while ((base = base->next) != NULL);
                   1417:        } else {
                   1418:            sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base     %s\n",
                   1419:                "(NONE: LDAP disabled)");
                   1420:        }
                   1421:        if (ldap_conf.search_filter) {
                   1422:            sudo_printf(SUDO_CONV_ERROR_MSG, "search_filter    %s\n",
                   1423:                ldap_conf.search_filter);
                   1424:        }
                   1425:        sudo_printf(SUDO_CONV_ERROR_MSG, "binddn           %s\n",
                   1426:            ldap_conf.binddn ?  ldap_conf.binddn : "(anonymous)");
                   1427:        sudo_printf(SUDO_CONV_ERROR_MSG, "bindpw           %s\n",
                   1428:            ldap_conf.bindpw ?  ldap_conf.bindpw : "(anonymous)");
                   1429:        if (ldap_conf.bind_timelimit > 0) {
                   1430:            sudo_printf(SUDO_CONV_ERROR_MSG, "bind_timelimit   %d\n",
                   1431:                ldap_conf.bind_timelimit);
                   1432:        }
                   1433:        if (ldap_conf.timelimit > 0) {
                   1434:            sudo_printf(SUDO_CONV_ERROR_MSG, "timelimit        %d\n",
                   1435:                ldap_conf.timelimit);
                   1436:        }
                   1437:        if (ldap_conf.deref != -1) {
                   1438:            sudo_printf(SUDO_CONV_ERROR_MSG, "deref            %d\n",
                   1439:                ldap_conf.deref);
                   1440:        }
                   1441:        sudo_printf(SUDO_CONV_ERROR_MSG, "ssl              %s\n",
                   1442:            ldap_conf.ssl ?  ldap_conf.ssl : "(no)");
                   1443:        if (ldap_conf.tls_checkpeer != -1) {
                   1444:            sudo_printf(SUDO_CONV_ERROR_MSG, "tls_checkpeer    %s\n",
                   1445:                ldap_conf.tls_checkpeer ?  "(yes)" : "(no)");
                   1446:        }
                   1447:        if (ldap_conf.tls_cacertfile != NULL) {
                   1448:            sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cacertfile   %s\n",
                   1449:                ldap_conf.tls_cacertfile);
                   1450:        }
                   1451:        if (ldap_conf.tls_cacertdir != NULL) {
                   1452:            sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cacertdir    %s\n",
                   1453:                ldap_conf.tls_cacertdir);
                   1454:        }
                   1455:        if (ldap_conf.tls_random_file != NULL) {
                   1456:            sudo_printf(SUDO_CONV_ERROR_MSG, "tls_random_file  %s\n",
                   1457:                ldap_conf.tls_random_file);
                   1458:        }
                   1459:        if (ldap_conf.tls_cipher_suite != NULL) {
                   1460:            sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cipher_suite %s\n",
                   1461:                ldap_conf.tls_cipher_suite);
                   1462:        }
                   1463:        if (ldap_conf.tls_certfile != NULL) {
                   1464:            sudo_printf(SUDO_CONV_ERROR_MSG, "tls_certfile     %s\n",
                   1465:                ldap_conf.tls_certfile);
                   1466:        }
                   1467:        if (ldap_conf.tls_keyfile != NULL) {
                   1468:            sudo_printf(SUDO_CONV_ERROR_MSG, "tls_keyfile      %s\n",
                   1469:                ldap_conf.tls_keyfile);
                   1470:        }
                   1471: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
                   1472:        if (ldap_conf.use_sasl != -1) {
                   1473:            sudo_printf(SUDO_CONV_ERROR_MSG, "use_sasl         %s\n",
                   1474:                ldap_conf.use_sasl ? "yes" : "no");
                   1475:            sudo_printf(SUDO_CONV_ERROR_MSG, "sasl_auth_id     %s\n",
                   1476:                ldap_conf.sasl_auth_id ?  ldap_conf.sasl_auth_id : "(NONE)");
                   1477:            sudo_printf(SUDO_CONV_ERROR_MSG, "rootuse_sasl     %d\n",
                   1478:                ldap_conf.rootuse_sasl);
                   1479:            sudo_printf(SUDO_CONV_ERROR_MSG, "rootsasl_auth_id %s\n",
                   1480:                ldap_conf.rootsasl_auth_id ?  ldap_conf.rootsasl_auth_id : "(NONE)");
                   1481:            sudo_printf(SUDO_CONV_ERROR_MSG, "sasl_secprops    %s\n",
                   1482:                ldap_conf.sasl_secprops ?  ldap_conf.sasl_secprops : "(NONE)");
                   1483:            sudo_printf(SUDO_CONV_ERROR_MSG, "krb5_ccname      %s\n",
                   1484:                ldap_conf.krb5_ccname ?  ldap_conf.krb5_ccname : "(NONE)");
                   1485:        }
                   1486: #endif
                   1487:        sudo_printf(SUDO_CONV_ERROR_MSG, "===================\n");
                   1488:     }
                   1489:     if (!ldap_conf.base)
1.1.1.2   misho    1490:        debug_return_bool(false);       /* if no base is defined, ignore LDAP */
1.1       misho    1491: 
                   1492:     if (ldap_conf.bind_timelimit > 0)
                   1493:        ldap_conf.bind_timelimit *= 1000;       /* convert to ms */
                   1494: 
                   1495:     /*
                   1496:      * Interpret SSL option
                   1497:      */
                   1498:     if (ldap_conf.ssl != NULL) {
                   1499:        if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
                   1500:            ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
1.1.1.2   misho    1501:        else if (atobool(ldap_conf.ssl) == true)
1.1       misho    1502:            ldap_conf.ssl_mode = SUDO_LDAP_SSL;
                   1503:     }
                   1504: 
                   1505: #if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
                   1506:     if (ldap_conf.tls_checkpeer != -1) {
                   1507:        ldapssl_set_strength(NULL,
                   1508:            ldap_conf.tls_checkpeer ? LDAPSSL_AUTH_CERT : LDAPSSL_AUTH_WEAK);
                   1509:     }
                   1510: #endif
                   1511: 
                   1512: #ifndef HAVE_LDAP_INITIALIZE
                   1513:     /* Convert uri list to host list if no ldap_initialize(). */
                   1514:     if (ldap_conf.uri) {
                   1515:        struct ldap_config_list_str *uri = ldap_conf.uri;
                   1516:        if (sudo_ldap_parse_uri(uri) != 0)
1.1.1.2   misho    1517:            debug_return_bool(false);
1.1       misho    1518:        do {
                   1519:            ldap_conf.uri = uri->next;
                   1520:            efree(uri);
                   1521:        } while ((uri = ldap_conf.uri));
                   1522:        ldap_conf.port = LDAP_PORT;
                   1523:     }
                   1524: #endif
                   1525: 
                   1526:     if (!ldap_conf.uri) {
                   1527:        /* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */
                   1528:        if (ldap_conf.port < 0)
                   1529:            ldap_conf.port =
                   1530:                ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
                   1531: 
                   1532: #ifdef HAVE_LDAP_CREATE
                   1533:        /*
                   1534:         * Cannot specify port directly to ldap_create(), each host must
                   1535:         * include :port to override the default.
                   1536:         */
                   1537:        if (ldap_conf.port != LDAP_PORT)
                   1538:            sudo_ldap_conf_add_ports();
                   1539: #endif
                   1540:     }
                   1541: 
                   1542:     /* If search filter is not parenthesized, make it so. */
                   1543:     if (ldap_conf.search_filter && ldap_conf.search_filter[0] != '(') {
                   1544:        size_t len = strlen(ldap_conf.search_filter);
                   1545:        cp = ldap_conf.search_filter;
                   1546:        ldap_conf.search_filter = emalloc(len + 3);
                   1547:        ldap_conf.search_filter[0] = '(';
                   1548:        memcpy(ldap_conf.search_filter + 1, cp, len);
                   1549:        ldap_conf.search_filter[len + 1] = ')';
                   1550:        ldap_conf.search_filter[len + 2] = '\0';
                   1551:        efree(cp);
                   1552:     }
                   1553: 
                   1554:     /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
                   1555:     if (ldap_conf.rootbinddn)
                   1556:        sudo_ldap_read_secret(_PATH_LDAP_SECRET);
                   1557: 
                   1558: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
                   1559:     /*
                   1560:      * Make sure we can open the file specified by krb5_ccname.
                   1561:      */
                   1562:     if (ldap_conf.krb5_ccname != NULL) {
                   1563:        if (strncasecmp(ldap_conf.krb5_ccname, "FILE:", 5) == 0 ||
                   1564:            strncasecmp(ldap_conf.krb5_ccname, "WRFILE:", 7) == 0) {
                   1565:            value = ldap_conf.krb5_ccname +
                   1566:                (ldap_conf.krb5_ccname[4] == ':' ? 5 : 7);
                   1567:            if ((fp = fopen(value, "r")) != NULL) {
                   1568:                DPRINTF(("using krb5 credential cache: %s", value), 1);
                   1569:                fclose(fp);
                   1570:            } else {
                   1571:                /* Can't open it, just ignore the entry. */
                   1572:                DPRINTF(("unable to open krb5 credential cache: %s", value), 1);
                   1573:                efree(ldap_conf.krb5_ccname);
                   1574:                ldap_conf.krb5_ccname = NULL;
                   1575:            }
                   1576:        }
                   1577:     }
                   1578: #endif
1.1.1.2   misho    1579:     debug_return_bool(true);
1.1       misho    1580: }
                   1581: 
                   1582: /*
                   1583:  * Extract the dn from an entry and return the first rdn from it.
                   1584:  */
                   1585: static char *
                   1586: sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry)
                   1587: {
                   1588: #ifdef HAVE_LDAP_STR2DN
                   1589:     char *dn, *rdn = NULL;
                   1590:     LDAPDN tmpDN;
1.1.1.2   misho    1591:     debug_decl(sudo_ldap_get_first_rdn, SUDO_DEBUG_LDAP)
1.1       misho    1592: 
                   1593:     if ((dn = ldap_get_dn(ld, entry)) == NULL)
1.1.1.2   misho    1594:        debug_return_str(NULL);
1.1       misho    1595:     if (ldap_str2dn(dn, &tmpDN, LDAP_DN_FORMAT_LDAP) == LDAP_SUCCESS) {
                   1596:        ldap_rdn2str(tmpDN[0], &rdn, LDAP_DN_FORMAT_UFN);
                   1597:        ldap_dnfree(tmpDN);
                   1598:     }
                   1599:     ldap_memfree(dn);
1.1.1.2   misho    1600:     debug_return_str(rdn);
1.1       misho    1601: #else
                   1602:     char *dn, **edn;
1.1.1.2   misho    1603:     debug_decl(sudo_ldap_get_first_rdn, SUDO_DEBUG_LDAP)
1.1       misho    1604: 
                   1605:     if ((dn = ldap_get_dn(ld, entry)) == NULL)
                   1606:        return NULL;
                   1607:     edn = ldap_explode_dn(dn, 1);
                   1608:     ldap_memfree(dn);
1.1.1.2   misho    1609:     debug_return_str(edn ? edn[0] : NULL);
1.1       misho    1610: #endif
                   1611: }
                   1612: 
                   1613: /*
                   1614:  * Fetch and display the global Options.
                   1615:  */
                   1616: static int
                   1617: sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw,
                   1618:     struct lbuf *lbuf)
                   1619: {
                   1620:     struct berval **bv, **p;
                   1621:     struct timeval tv, *tvp = NULL;
                   1622:     struct ldap_config_list_str *base;
                   1623:     struct sudo_ldap_handle *handle = nss->handle;
                   1624:     LDAP *ld;
                   1625:     LDAPMessage *entry, *result;
                   1626:     char *prefix, *filt;
                   1627:     int rc, count = 0;
1.1.1.2   misho    1628:     debug_decl(sudo_ldap_display_defaults, SUDO_DEBUG_LDAP)
1.1       misho    1629: 
                   1630:     if (handle == NULL || handle->ld == NULL)
                   1631:        goto done;
                   1632:     ld = handle->ld;
                   1633: 
                   1634:     filt = sudo_ldap_build_default_filter();
                   1635:     for (base = ldap_conf.base; base != NULL; base = base->next) {
                   1636:        if (ldap_conf.timeout > 0) {
                   1637:            tv.tv_sec = ldap_conf.timeout;
                   1638:            tv.tv_usec = 0;
                   1639:            tvp = &tv;
                   1640:        }
                   1641:        result = NULL;
                   1642:        rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
                   1643:            filt, NULL, 0, NULL, NULL, tvp, 0, &result);
                   1644:        if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
                   1645:            bv = ldap_get_values_len(ld, entry, "sudoOption");
                   1646:            if (bv != NULL) {
                   1647:                if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
                   1648:                    prefix = "    ";
                   1649:                else
                   1650:                    prefix = ", ";
                   1651:                for (p = bv; *p != NULL; p++) {
                   1652:                    lbuf_append(lbuf, "%s%s", prefix, (*p)->bv_val);
                   1653:                    prefix = ", ";
                   1654:                    count++;
                   1655:                }
                   1656:                ldap_value_free_len(bv);
                   1657:            }
                   1658:        }
                   1659:        if (result)
                   1660:            ldap_msgfree(result);
                   1661:     }
                   1662:     efree(filt);
                   1663: done:
1.1.1.2   misho    1664:     debug_return_int(count);
1.1       misho    1665: }
                   1666: 
                   1667: /*
                   1668:  * STUB
                   1669:  */
                   1670: static int
                   1671: sudo_ldap_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw,
                   1672:     struct lbuf *lbuf)
                   1673: {
1.1.1.2   misho    1674:     debug_decl(sudo_ldap_display_bound_defaults, SUDO_DEBUG_LDAP)
                   1675:     debug_return_int(0);
1.1       misho    1676: }
                   1677: 
                   1678: /*
                   1679:  * Print a record in the short form, ala file sudoers.
                   1680:  */
                   1681: static int
                   1682: sudo_ldap_display_entry_short(LDAP *ld, LDAPMessage *entry, struct lbuf *lbuf)
                   1683: {
                   1684:     struct berval **bv, **p;
                   1685:     int count = 0;
1.1.1.2   misho    1686:     debug_decl(sudo_ldap_display_entry_short, SUDO_DEBUG_LDAP)
1.1       misho    1687: 
                   1688:     lbuf_append(lbuf, "    (");
                   1689: 
                   1690:     /* get the RunAsUser Values from the entry */
                   1691:     bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
                   1692:     if (bv == NULL)
                   1693:        bv = ldap_get_values_len(ld, entry, "sudoRunAs");
                   1694:     if (bv != NULL) {
                   1695:        for (p = bv; *p != NULL; p++) {
                   1696:            lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
                   1697:        }
                   1698:        ldap_value_free_len(bv);
                   1699:     } else
                   1700:        lbuf_append(lbuf, "%s", def_runas_default);
                   1701: 
                   1702:     /* get the RunAsGroup Values from the entry */
                   1703:     bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
                   1704:     if (bv != NULL) {
                   1705:        lbuf_append(lbuf, " : ");
                   1706:        for (p = bv; *p != NULL; p++) {
                   1707:            lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
                   1708:        }
                   1709:        ldap_value_free_len(bv);
                   1710:     }
                   1711:     lbuf_append(lbuf, ") ");
                   1712: 
                   1713:     /* get the Option Values from the entry */
                   1714:     bv = ldap_get_values_len(ld, entry, "sudoOption");
                   1715:     if (bv != NULL) {
                   1716:        for (p = bv; *p != NULL; p++) {
                   1717:            char *cp = (*p)->bv_val;
                   1718:            if (*cp == '!')
                   1719:                cp++;
                   1720:            if (strcmp(cp, "authenticate") == 0)
                   1721:                lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
                   1722:                    "NOPASSWD: " : "PASSWD: ");
                   1723:            else if (strcmp(cp, "noexec") == 0)
                   1724:                lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
                   1725:                    "EXEC: " : "NOEXEC: ");
                   1726:            else if (strcmp(cp, "setenv") == 0)
                   1727:                lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
                   1728:                    "NOSETENV: " : "SETENV: ");
                   1729:        }
                   1730:        ldap_value_free_len(bv);
                   1731:     }
                   1732: 
                   1733:     /* get the Command Values from the entry */
                   1734:     bv = ldap_get_values_len(ld, entry, "sudoCommand");
                   1735:     if (bv != NULL) {
                   1736:        for (p = bv; *p != NULL; p++) {
                   1737:            lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
                   1738:            count++;
                   1739:        }
                   1740:        ldap_value_free_len(bv);
                   1741:     }
                   1742:     lbuf_append(lbuf, "\n");
                   1743: 
1.1.1.2   misho    1744:     debug_return_int(count);
1.1       misho    1745: }
                   1746: 
                   1747: /*
                   1748:  * Print a record in the long form.
                   1749:  */
                   1750: static int
                   1751: sudo_ldap_display_entry_long(LDAP *ld, LDAPMessage *entry, struct lbuf *lbuf)
                   1752: {
                   1753:     struct berval **bv, **p;
                   1754:     char *rdn;
                   1755:     int count = 0;
1.1.1.2   misho    1756:     debug_decl(sudo_ldap_display_entry_long, SUDO_DEBUG_LDAP)
1.1       misho    1757: 
                   1758:     /* extract the dn, only show the first rdn */
                   1759:     rdn = sudo_ldap_get_first_rdn(ld, entry);
                   1760:     if (rdn != NULL)
                   1761:        lbuf_append(lbuf, _("\nLDAP Role: %s\n"), rdn);
                   1762:     else
                   1763:        lbuf_append(lbuf, _("\nLDAP Role: UNKNOWN\n"));
                   1764:     if (rdn)
                   1765:        ldap_memfree(rdn);
                   1766: 
                   1767:     /* get the RunAsUser Values from the entry */
                   1768:     lbuf_append(lbuf, "    RunAsUsers: ");
                   1769:     bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
                   1770:     if (bv == NULL)
                   1771:        bv = ldap_get_values_len(ld, entry, "sudoRunAs");
                   1772:     if (bv != NULL) {
                   1773:        for (p = bv; *p != NULL; p++) {
                   1774:            lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
                   1775:        }
                   1776:        ldap_value_free_len(bv);
                   1777:     } else
                   1778:        lbuf_append(lbuf, "%s", def_runas_default);
                   1779:     lbuf_append(lbuf, "\n");
                   1780: 
                   1781:     /* get the RunAsGroup Values from the entry */
                   1782:     bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
                   1783:     if (bv != NULL) {
                   1784:        lbuf_append(lbuf, "    RunAsGroups: ");
                   1785:        for (p = bv; *p != NULL; p++) {
                   1786:            lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
                   1787:        }
                   1788:        ldap_value_free_len(bv);
                   1789:        lbuf_append(lbuf, "\n");
                   1790:     }
                   1791: 
                   1792:     /* get the Option Values from the entry */
                   1793:     bv = ldap_get_values_len(ld, entry, "sudoOption");
                   1794:     if (bv != NULL) {
                   1795:        lbuf_append(lbuf, "    Options: ");
                   1796:        for (p = bv; *p != NULL; p++) {
                   1797:            lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
                   1798:        }
                   1799:        ldap_value_free_len(bv);
                   1800:        lbuf_append(lbuf, "\n");
                   1801:     }
                   1802: 
                   1803:     /*
                   1804:      * Display order attribute if present.  This attribute is single valued,
                   1805:      * so there is no need for a loop.
                   1806:      */
                   1807:     bv = ldap_get_values_len(ld, entry, "sudoOrder");
                   1808:     if (bv != NULL) {
                   1809:        if (*bv != NULL) {
                   1810:            lbuf_append(lbuf, _("    Order: %s\n"), (*bv)->bv_val);
                   1811:        }
                   1812:        ldap_value_free_len(bv);
                   1813:     }
                   1814: 
                   1815:     /* Get the command values from the entry. */
                   1816:     bv = ldap_get_values_len(ld, entry, "sudoCommand");
                   1817:     if (bv != NULL) {
                   1818:        lbuf_append(lbuf, _("    Commands:\n"));
                   1819:        for (p = bv; *p != NULL; p++) {
                   1820:            lbuf_append(lbuf, "\t%s\n", (*p)->bv_val);
                   1821:            count++;
                   1822:        }
                   1823:        ldap_value_free_len(bv);
                   1824:     }
                   1825: 
1.1.1.2   misho    1826:     debug_return_int(count);
1.1       misho    1827: }
                   1828: 
                   1829: /*
                   1830:  * Like sudo_ldap_lookup(), except we just print entries.
                   1831:  */
                   1832: static int
                   1833: sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
                   1834:     struct lbuf *lbuf)
                   1835: {
                   1836:     struct sudo_ldap_handle *handle = nss->handle;
                   1837:     LDAP *ld;
                   1838:     struct ldap_result *lres;
                   1839:     LDAPMessage *entry;
                   1840:     int i, count = 0;
1.1.1.2   misho    1841:     debug_decl(sudo_ldap_display_privs, SUDO_DEBUG_LDAP)
1.1       misho    1842: 
                   1843:     if (handle == NULL || handle->ld == NULL)
                   1844:        goto done;
                   1845:     ld = handle->ld;
                   1846: 
                   1847:     DPRINTF(("ldap search for command list"), 1);
                   1848:     lres = sudo_ldap_result_get(nss, pw);
                   1849: 
                   1850:     /* Display all matching entries. */
                   1851:     for (i = 0; i < lres->nentries; i++) {
                   1852:        entry = lres->entries[i].entry;
                   1853:        if (long_list)
                   1854:            count += sudo_ldap_display_entry_long(ld, entry, lbuf);
                   1855:        else
                   1856:            count += sudo_ldap_display_entry_short(ld, entry, lbuf);
                   1857:     }
                   1858: 
                   1859: done:
1.1.1.2   misho    1860:     debug_return_int(count);
1.1       misho    1861: }
                   1862: 
                   1863: static int
                   1864: sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw)
                   1865: {
                   1866:     struct sudo_ldap_handle *handle = nss->handle;
                   1867:     LDAP *ld;
                   1868:     struct ldap_result *lres;
                   1869:     LDAPMessage *entry;
1.1.1.2   misho    1870:     bool found = false;
                   1871:     int i;
                   1872:     debug_decl(sudo_ldap_display_cmnd, SUDO_DEBUG_LDAP)
1.1       misho    1873: 
                   1874:     if (handle == NULL || handle->ld == NULL)
                   1875:        goto done;
                   1876:     ld = handle->ld;
                   1877: 
                   1878:     /*
                   1879:      * The sudo_ldap_result_get() function returns all nodes that match
                   1880:      * the user and the host.
                   1881:      */
                   1882:     DPRINTF(("ldap search for command list"), 1);
                   1883:     lres = sudo_ldap_result_get(nss, pw);
                   1884:     for (i = 0; i < lres->nentries; i++) {
                   1885:        entry = lres->entries[i].entry;
                   1886:        if (sudo_ldap_check_command(ld, entry, NULL) &&
                   1887:            sudo_ldap_check_runas(ld, entry)) {
1.1.1.2   misho    1888:            found = true;
1.1       misho    1889:            goto done;
                   1890:        }
                   1891:     }
                   1892: 
                   1893: done:
                   1894:     if (found)
                   1895:        printf("%s%s%s\n", safe_cmnd ? safe_cmnd : user_cmnd,
                   1896:            user_args ? " " : "", user_args ? user_args : "");
1.1.1.2   misho    1897:    debug_return_bool(!found);
1.1       misho    1898: }
                   1899: 
                   1900: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
                   1901: static int
                   1902: sudo_ldap_sasl_interact(LDAP *ld, unsigned int flags, void *_auth_id,
                   1903:     void *_interact)
                   1904: {
                   1905:     char *auth_id = (char *)_auth_id;
                   1906:     sasl_interact_t *interact = (sasl_interact_t *)_interact;
1.1.1.2   misho    1907:     debug_decl(sudo_ldap_sasl_interact, SUDO_DEBUG_LDAP)
1.1       misho    1908: 
                   1909:     for (; interact->id != SASL_CB_LIST_END; interact++) {
                   1910:        if (interact->id != SASL_CB_USER)
1.1.1.2   misho    1911:            debug_return_int(LDAP_PARAM_ERROR);
1.1       misho    1912: 
                   1913:        if (auth_id != NULL)
                   1914:            interact->result = auth_id;
                   1915:        else if (interact->defresult != NULL)
                   1916:            interact->result = interact->defresult;
                   1917:        else
                   1918:            interact->result = "";
                   1919: 
                   1920:        interact->len = strlen(interact->result);
                   1921: #if SASL_VERSION_MAJOR < 2
                   1922:        interact->result = estrdup(interact->result);
                   1923: #endif /* SASL_VERSION_MAJOR < 2 */
                   1924:     }
1.1.1.2   misho    1925:     debug_return_int(LDAP_SUCCESS);
1.1       misho    1926: }
                   1927: #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
                   1928: 
1.1.1.2   misho    1929: 
1.1       misho    1930: /*
1.1.1.2   misho    1931:  * Set LDAP options from the specified options table
1.1       misho    1932:  */
                   1933: static int
1.1.1.2   misho    1934: sudo_ldap_set_options_table(LDAP *ld, struct ldap_config_table *table)
1.1       misho    1935: {
                   1936:     struct ldap_config_table *cur;
1.1.1.3 ! misho    1937:     int ival, rc, errors = 0;
1.1.1.2   misho    1938:     char *sval;
                   1939:     debug_decl(sudo_ldap_set_options_table, SUDO_DEBUG_LDAP)
1.1       misho    1940: 
1.1.1.2   misho    1941:     for (cur = table; cur->conf_str != NULL; cur++) {
1.1       misho    1942:        if (cur->opt_val == -1)
                   1943:            continue;
                   1944: 
                   1945:        switch (cur->type) {
                   1946:        case CONF_BOOL:
                   1947:        case CONF_INT:
                   1948:            ival = *(int *)(cur->valp);
                   1949:            if (ival >= 0) {
1.1.1.3 ! misho    1950:                DPRINTF(("ldap_set_option: %s -> %d", cur->conf_str, ival), 1);
1.1.1.2   misho    1951:                rc = ldap_set_option(ld, cur->opt_val, &ival);
1.1       misho    1952:                if (rc != LDAP_OPT_SUCCESS) {
                   1953:                    warningx("ldap_set_option: %s -> %d: %s",
                   1954:                        cur->conf_str, ival, ldap_err2string(rc));
1.1.1.3 ! misho    1955:                    errors++;
1.1       misho    1956:                }
                   1957:            }
                   1958:            break;
                   1959:        case CONF_STR:
                   1960:            sval = *(char **)(cur->valp);
                   1961:            if (sval != NULL) {
1.1.1.3 ! misho    1962:                DPRINTF(("ldap_set_option: %s -> %s", cur->conf_str, sval), 1);
1.1.1.2   misho    1963:                rc = ldap_set_option(ld, cur->opt_val, sval);
1.1       misho    1964:                if (rc != LDAP_OPT_SUCCESS) {
                   1965:                    warningx("ldap_set_option: %s -> %s: %s",
                   1966:                        cur->conf_str, sval, ldap_err2string(rc));
1.1.1.3 ! misho    1967:                    errors++;
1.1       misho    1968:                }
                   1969:            }
                   1970:            break;
                   1971:        }
                   1972:     }
1.1.1.3 ! misho    1973:     debug_return_int(errors ? -1 : 0);
1.1.1.2   misho    1974: }
                   1975: 
                   1976: /*
                   1977:  * Set LDAP options based on the global config table.
                   1978:  */
                   1979: static int
                   1980: sudo_ldap_set_options_global(void)
                   1981: {
                   1982:     int rc;
                   1983:     debug_decl(sudo_ldap_set_options_global, SUDO_DEBUG_LDAP)
                   1984: 
                   1985:     /* Set ber options */
                   1986: #ifdef LBER_OPT_DEBUG_LEVEL
                   1987:     if (ldap_conf.ldap_debug)
                   1988:        ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
                   1989: #endif
                   1990: 
                   1991:     /* Parse global LDAP options table. */
                   1992:     rc = sudo_ldap_set_options_table(NULL, ldap_conf_global);
                   1993:     if (rc == -1)
                   1994:        debug_return_int(-1);
                   1995:     debug_return_int(0);
                   1996: }
                   1997: 
                   1998: /*
                   1999:  * Set LDAP options based on the per-connection config table.
                   2000:  */
                   2001: static int
                   2002: sudo_ldap_set_options_conn(LDAP *ld)
                   2003: {
                   2004:     int rc;
                   2005:     debug_decl(sudo_ldap_set_options_conn, SUDO_DEBUG_LDAP)
                   2006: 
                   2007:     /* Parse per-connection LDAP options table. */
                   2008:     rc = sudo_ldap_set_options_table(ld, ldap_conf_conn);
                   2009:     if (rc == -1)
                   2010:        debug_return_int(-1);
1.1       misho    2011: 
                   2012: #ifdef LDAP_OPT_TIMEOUT
                   2013:     /* Convert timeout to a timeval */
                   2014:     if (ldap_conf.timeout > 0) {
                   2015:        struct timeval tv;
                   2016:        tv.tv_sec = ldap_conf.timeout;
                   2017:        tv.tv_usec = 0;
1.1.1.3 ! misho    2018:        DPRINTF(("ldap_set_option(LDAP_OPT_TIMEOUT, %ld)",
        !          2019:            (long)tv.tv_sec), 1);
1.1       misho    2020:        rc = ldap_set_option(ld, LDAP_OPT_TIMEOUT, &tv);
                   2021:        if (rc != LDAP_OPT_SUCCESS) {
                   2022:            warningx("ldap_set_option(TIMEOUT, %ld): %s",
                   2023:                (long)tv.tv_sec, ldap_err2string(rc));
                   2024:        }
                   2025:     }
                   2026: #endif
                   2027: #ifdef LDAP_OPT_NETWORK_TIMEOUT
                   2028:     /* Convert bind_timelimit to a timeval */
                   2029:     if (ldap_conf.bind_timelimit > 0) {
                   2030:        struct timeval tv;
                   2031:        tv.tv_sec = ldap_conf.bind_timelimit / 1000;
                   2032:        tv.tv_usec = 0;
1.1.1.3 ! misho    2033:        DPRINTF(("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %ld)",
        !          2034:            (long)tv.tv_sec), 1);
1.1       misho    2035:        rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
1.1.1.3 ! misho    2036: # if !defined(LDAP_OPT_CONNECT_TIMEOUT) || LDAP_VENDOR_VERSION != 510
        !          2037:        /* Tivoli Directory Server 6.3 libs always return a (bogus) error. */
1.1       misho    2038:        if (rc != LDAP_OPT_SUCCESS) {
                   2039:            warningx("ldap_set_option(NETWORK_TIMEOUT, %ld): %s",
                   2040:                (long)tv.tv_sec, ldap_err2string(rc));
                   2041:        }
1.1.1.3 ! misho    2042: # endif
1.1       misho    2043:     }
                   2044: #endif
                   2045: 
                   2046: #if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
                   2047:     if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
                   2048:        int val = LDAP_OPT_X_TLS_HARD;
1.1.1.3 ! misho    2049:        DPRINTF(("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD)"), 1);
1.1       misho    2050:        rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
                   2051:        if (rc != LDAP_SUCCESS) {
                   2052:            warningx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
                   2053:                ldap_err2string(rc));
1.1.1.2   misho    2054:            debug_return_int(-1);
1.1       misho    2055:        }
                   2056:     }
                   2057: #endif
1.1.1.2   misho    2058:     debug_return_int(0);
1.1       misho    2059: }
                   2060: 
                   2061: /*
                   2062:  * Create a new sudo_ldap_result structure.
                   2063:  */
                   2064: static struct ldap_result *
                   2065: sudo_ldap_result_alloc(void)
                   2066: {
                   2067:     struct ldap_result *result;
1.1.1.2   misho    2068:     debug_decl(sudo_ldap_result_alloc, SUDO_DEBUG_LDAP)
1.1       misho    2069: 
1.1.1.2   misho    2070:     debug_return_ptr(ecalloc(1, sizeof(*result)));
1.1       misho    2071: }
                   2072: 
                   2073: /*
                   2074:  * Free the ldap result structure
                   2075:  */
                   2076: static void
                   2077: sudo_ldap_result_free(struct ldap_result *lres)
                   2078: {
                   2079:     struct ldap_search_list *s;
1.1.1.2   misho    2080:     debug_decl(sudo_ldap_result_free, SUDO_DEBUG_LDAP)
1.1       misho    2081: 
                   2082:     if (lres != NULL) {
                   2083:        if (lres->nentries) {
                   2084:            efree(lres->entries);
                   2085:            lres->entries = NULL;
                   2086:        }
                   2087:        if (lres->searches) {
                   2088:            while ((s = lres->searches) != NULL) {
                   2089:                ldap_msgfree(s->searchresult);
                   2090:                lres->searches = s->next;
                   2091:                efree(s);
                   2092:            }
                   2093:        }
                   2094:        efree(lres);
                   2095:     }
1.1.1.2   misho    2096:     debug_return;
1.1       misho    2097: }
                   2098: 
                   2099: /*
                   2100:  * Add a search result to the ldap_result structure.
                   2101:  */
                   2102: static struct ldap_search_list *
                   2103: sudo_ldap_result_add_search(struct ldap_result *lres, LDAP *ldap,
                   2104:     LDAPMessage *searchresult)
                   2105: {
                   2106:     struct ldap_search_list *s, *news;
1.1.1.2   misho    2107:     debug_decl(sudo_ldap_result_add_search, SUDO_DEBUG_LDAP)
1.1       misho    2108: 
1.1.1.2   misho    2109:     news = ecalloc(1, sizeof(struct ldap_search_list));
1.1       misho    2110:     news->ldap = ldap;
                   2111:     news->searchresult = searchresult;
1.1.1.2   misho    2112:     /* news->next = NULL; */
1.1       misho    2113: 
                   2114:     /* Add entry to the end of the chain (XXX - tailq instead?). */
                   2115:     if (lres->searches) {
                   2116:        for (s = lres->searches; s->next != NULL; s = s->next)
                   2117:            continue;
                   2118:        s->next = news;
                   2119:     } else {
                   2120:        lres->searches = news;
                   2121:     }
1.1.1.2   misho    2122:     debug_return_ptr(news);
1.1       misho    2123: }
                   2124: 
                   2125: /*
                   2126:  * Connect to the LDAP server specified by ld
                   2127:  */
                   2128: static int
                   2129: sudo_ldap_bind_s(LDAP *ld)
                   2130: {
                   2131:     int rc;
                   2132: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
                   2133:     const char *old_ccname = user_ccname;
                   2134: # ifdef HAVE_GSS_KRB5_CCACHE_NAME
                   2135:     unsigned int status;
                   2136: # endif
                   2137: #endif
1.1.1.2   misho    2138:     debug_decl(sudo_ldap_bind_s, SUDO_DEBUG_LDAP)
1.1       misho    2139: 
                   2140: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1.1.1.2   misho    2141:     if (ldap_conf.rootuse_sasl == true ||
                   2142:        (ldap_conf.rootuse_sasl != false && ldap_conf.use_sasl == true)) {
1.1       misho    2143:        void *auth_id = ldap_conf.rootsasl_auth_id ?
                   2144:            ldap_conf.rootsasl_auth_id : ldap_conf.sasl_auth_id;
                   2145: 
                   2146:        if (ldap_conf.krb5_ccname != NULL) {
                   2147: # ifdef HAVE_GSS_KRB5_CCACHE_NAME
                   2148:            if (gss_krb5_ccache_name(&status, ldap_conf.krb5_ccname, &old_ccname)
                   2149:                != GSS_S_COMPLETE) {
                   2150:                old_ccname = NULL;
                   2151:                DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
                   2152:            }
                   2153: # else
1.1.1.2   misho    2154:            sudo_setenv("KRB5CCNAME", ldap_conf.krb5_ccname, true);
1.1       misho    2155: # endif
                   2156:        }
                   2157:        rc = ldap_sasl_interactive_bind_s(ld, ldap_conf.binddn, "GSSAPI",
                   2158:            NULL, NULL, LDAP_SASL_QUIET, sudo_ldap_sasl_interact, auth_id);
                   2159:        if (ldap_conf.krb5_ccname != NULL) {
                   2160: # ifdef HAVE_GSS_KRB5_CCACHE_NAME
                   2161:            if (gss_krb5_ccache_name(&status, old_ccname, NULL) != GSS_S_COMPLETE)
                   2162:                    DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
                   2163: # else
                   2164:            if (old_ccname != NULL)
1.1.1.2   misho    2165:                sudo_setenv("KRB5CCNAME", old_ccname, true);
1.1       misho    2166:            else
1.1.1.2   misho    2167:                sudo_unsetenv("KRB5CCNAME");
1.1       misho    2168: # endif
                   2169:        }
                   2170:        if (rc != LDAP_SUCCESS) {
                   2171:            warningx("ldap_sasl_interactive_bind_s(): %s",
                   2172:                ldap_err2string(rc));
1.1.1.2   misho    2173:            debug_return_int(-1);
1.1       misho    2174:        }
                   2175:        DPRINTF(("ldap_sasl_interactive_bind_s() ok"), 1);
                   2176:     } else
                   2177: #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
                   2178: #ifdef HAVE_LDAP_SASL_BIND_S
                   2179:     {
                   2180:        struct berval bv;
                   2181: 
                   2182:        bv.bv_val = ldap_conf.bindpw ? ldap_conf.bindpw : "";
                   2183:        bv.bv_len = strlen(bv.bv_val);
                   2184: 
                   2185:        rc = ldap_sasl_bind_s(ld, ldap_conf.binddn, LDAP_SASL_SIMPLE, &bv,
                   2186:            NULL, NULL, NULL);
                   2187:        if (rc != LDAP_SUCCESS) {
                   2188:            warningx("ldap_sasl_bind_s(): %s", ldap_err2string(rc));
1.1.1.2   misho    2189:            debug_return_int(-1);
1.1       misho    2190:        }
                   2191:        DPRINTF(("ldap_sasl_bind_s() ok"), 1);
                   2192:     }
                   2193: #else
                   2194:     {
                   2195:        rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw);
                   2196:        if (rc != LDAP_SUCCESS) {
                   2197:            warningx("ldap_simple_bind_s(): %s", ldap_err2string(rc));
1.1.1.2   misho    2198:            debug_return_int(-1);
1.1       misho    2199:        }
                   2200:        DPRINTF(("ldap_simple_bind_s() ok"), 1);
                   2201:     }
                   2202: #endif
1.1.1.2   misho    2203:     debug_return_int(0);
1.1       misho    2204: }
                   2205: 
                   2206: /*
                   2207:  * Open a connection to the LDAP server.
                   2208:  * Returns 0 on success and non-zero on failure.
                   2209:  */
                   2210: static int
                   2211: sudo_ldap_open(struct sudo_nss *nss)
                   2212: {
                   2213:     LDAP *ld;
1.1.1.2   misho    2214:     int rc;
                   2215:     bool ldapnoinit = false;
                   2216:     struct sudo_ldap_handle *handle;
                   2217:     debug_decl(sudo_ldap_open, SUDO_DEBUG_LDAP)
1.1       misho    2218: 
                   2219:     if (!sudo_ldap_read_config())
1.1.1.2   misho    2220:        debug_return_int(-1);
1.1       misho    2221: 
                   2222:     /* Prevent reading of user ldaprc and system defaults. */
1.1.1.2   misho    2223:     if (sudo_getenv("LDAPNOINIT") == NULL) {
                   2224:        ldapnoinit = true;
                   2225:        sudo_setenv("LDAPNOINIT", "1", true);
1.1       misho    2226:     }
                   2227: 
1.1.1.2   misho    2228:     /* Set global LDAP options */
                   2229:     if (sudo_ldap_set_options_global() < 0)
                   2230:        debug_return_int(-1);
                   2231: 
1.1       misho    2232:     /* Connect to LDAP server */
                   2233: #ifdef HAVE_LDAP_INITIALIZE
                   2234:     if (ldap_conf.uri != NULL) {
                   2235:        char *buf = sudo_ldap_join_uri(ldap_conf.uri);
                   2236:        DPRINTF(("ldap_initialize(ld, %s)", buf), 2);
                   2237:        rc = ldap_initialize(&ld, buf);
                   2238:        efree(buf);
1.1.1.2   misho    2239:        if (rc != LDAP_SUCCESS)
                   2240:            warningx(_("unable to initialize LDAP: %s"), ldap_err2string(rc));
1.1       misho    2241:     } else
                   2242: #endif
                   2243:        rc = sudo_ldap_init(&ld, ldap_conf.host, ldap_conf.port);
1.1.1.2   misho    2244:     if (rc != LDAP_SUCCESS)
                   2245:        debug_return_int(-1);
1.1       misho    2246: 
1.1.1.2   misho    2247:     /* Set LDAP per-connection options */
                   2248:     if (sudo_ldap_set_options_conn(ld) < 0)
                   2249:        debug_return_int(-1);
1.1       misho    2250: 
1.1.1.2   misho    2251:     if (ldapnoinit)
                   2252:        sudo_unsetenv("LDAPNOINIT");
1.1       misho    2253: 
                   2254:     if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
                   2255: #if defined(HAVE_LDAP_START_TLS_S)
                   2256:        rc = ldap_start_tls_s(ld, NULL, NULL);
                   2257:        if (rc != LDAP_SUCCESS) {
                   2258:            warningx("ldap_start_tls_s(): %s", ldap_err2string(rc));
1.1.1.2   misho    2259:            debug_return_int(-1);
1.1       misho    2260:        }
                   2261:        DPRINTF(("ldap_start_tls_s() ok"), 1);
                   2262: #elif defined(HAVE_LDAP_SSL_CLIENT_INIT) && defined(HAVE_LDAP_START_TLS_S_NP)
1.1.1.3 ! misho    2263:        if (ldap_ssl_client_init(ldap_conf.tls_keyfile, ldap_conf.tls_keypw, 0, &rc) != LDAP_SUCCESS) {
1.1       misho    2264:            warningx("ldap_ssl_client_init(): %s", ldap_err2string(rc));
1.1.1.2   misho    2265:            debug_return_int(-1);
1.1       misho    2266:        }
                   2267:        rc = ldap_start_tls_s_np(ld, NULL);
                   2268:        if (rc != LDAP_SUCCESS) {
                   2269:            warningx("ldap_start_tls_s_np(): %s", ldap_err2string(rc));
1.1.1.2   misho    2270:            debug_return_int(-1);
1.1       misho    2271:        }
                   2272:        DPRINTF(("ldap_start_tls_s_np() ok"), 1);
                   2273: #else
                   2274:        warningx(_("start_tls specified but LDAP libs do not support ldap_start_tls_s() or ldap_start_tls_s_np()"));
                   2275: #endif /* !HAVE_LDAP_START_TLS_S && !HAVE_LDAP_START_TLS_S_NP */
                   2276:     }
                   2277: 
                   2278:     /* Actually connect */
                   2279:     if (sudo_ldap_bind_s(ld) != 0)
1.1.1.2   misho    2280:        debug_return_int(-1);
1.1       misho    2281: 
                   2282:     /* Create a handle container. */
1.1.1.2   misho    2283:     handle = ecalloc(1, sizeof(struct sudo_ldap_handle));
1.1       misho    2284:     handle->ld = ld;
1.1.1.2   misho    2285:     /* handle->result = NULL; */
                   2286:     /* handle->username = NULL; */
                   2287:     /* handle->grlist = NULL; */
1.1       misho    2288:     nss->handle = handle;
                   2289: 
1.1.1.2   misho    2290:     debug_return_int(0);
1.1       misho    2291: }
                   2292: 
                   2293: static int
                   2294: sudo_ldap_setdefs(struct sudo_nss *nss)
                   2295: {
                   2296:     struct ldap_config_list_str *base;
                   2297:     struct sudo_ldap_handle *handle = nss->handle;
                   2298:     struct timeval tv, *tvp = NULL;
                   2299:     LDAP *ld;
                   2300:     LDAPMessage *entry, *result;
                   2301:     char *filt;
                   2302:     int rc;
1.1.1.2   misho    2303:     debug_decl(sudo_ldap_setdefs, SUDO_DEBUG_LDAP)
1.1       misho    2304: 
                   2305:     if (handle == NULL || handle->ld == NULL)
1.1.1.2   misho    2306:        debug_return_int(-1);
1.1       misho    2307:     ld = handle->ld;
                   2308: 
                   2309:     filt = sudo_ldap_build_default_filter();
                   2310:     DPRINTF(("Looking for cn=defaults: %s", filt), 1);
                   2311: 
                   2312:     for (base = ldap_conf.base; base != NULL; base = base->next) {
                   2313:        if (ldap_conf.timeout > 0) {
                   2314:            tv.tv_sec = ldap_conf.timeout;
                   2315:            tv.tv_usec = 0;
                   2316:            tvp = &tv;
                   2317:        }
                   2318:        result = NULL;
                   2319:        rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
                   2320:            filt, NULL, 0, NULL, NULL, tvp, 0, &result);
                   2321:        if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
                   2322:            DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
                   2323:            sudo_ldap_parse_options(ld, entry);
                   2324:        } else
                   2325:            DPRINTF(("no default options found in %s", base->val), 1);
                   2326: 
                   2327:        if (result)
                   2328:            ldap_msgfree(result);
                   2329:     }
                   2330:     efree(filt);
                   2331: 
1.1.1.2   misho    2332:     debug_return_int(0);
1.1       misho    2333: }
                   2334: 
                   2335: /*
                   2336:  * like sudoers_lookup() - only LDAP style
                   2337:  */
                   2338: static int
                   2339: sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
                   2340: {
                   2341:     struct sudo_ldap_handle *handle = nss->handle;
                   2342:     LDAP *ld;
                   2343:     LDAPMessage *entry;
                   2344:     int i, rc, setenv_implied;
                   2345:     struct ldap_result *lres = NULL;
1.1.1.2   misho    2346:     debug_decl(sudo_ldap_lookup, SUDO_DEBUG_LDAP)
1.1       misho    2347: 
                   2348:     if (handle == NULL || handle->ld == NULL)
1.1.1.2   misho    2349:        debug_return_int(ret);
1.1       misho    2350:     ld = handle->ld;
                   2351: 
                   2352:     /* Fetch list of sudoRole entries that match user and host. */
                   2353:     lres = sudo_ldap_result_get(nss, sudo_user.pw);
                   2354: 
                   2355:     /*
                   2356:      * The following queries are only determine whether or not a
                   2357:      * password is required, so the order of the entries doesn't matter.
                   2358:      */
                   2359:     if (pwflag) {
                   2360:        int doauth = UNSPEC;
                   2361:        int matched = UNSPEC;
                   2362:        enum def_tuple pwcheck = 
                   2363:            (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
                   2364: 
                   2365:        DPRINTF(("perform search for pwflag %d", pwflag), 1);
                   2366:        for (i = 0; i < lres->nentries; i++) {
                   2367:            entry = lres->entries[i].entry;
1.1.1.2   misho    2368:            if ((pwcheck == any && doauth != false) ||
                   2369:                (pwcheck == all && doauth == false)) {
1.1       misho    2370:                doauth = sudo_ldap_check_bool(ld, entry, "authenticate");
                   2371:            }
                   2372:            /* Only check the command when listing another user. */
                   2373:            if (user_uid == 0 || list_pw == NULL ||
                   2374:                user_uid == list_pw->pw_uid ||
                   2375:                sudo_ldap_check_command(ld, entry, NULL)) {
1.1.1.2   misho    2376:                matched = true;
1.1       misho    2377:                break;
                   2378:            }
                   2379:        }
                   2380:        if (matched || user_uid == 0) {
                   2381:            SET(ret, VALIDATE_OK);
                   2382:            CLR(ret, VALIDATE_NOT_OK);
                   2383:            if (def_authenticate) {
                   2384:                switch (pwcheck) {
                   2385:                    case always:
                   2386:                        SET(ret, FLAG_CHECK_USER);
                   2387:                        break;
                   2388:                    case all:
                   2389:                    case any:
1.1.1.2   misho    2390:                        if (doauth == false)
                   2391:                            def_authenticate = false;
1.1       misho    2392:                        break;
                   2393:                    case never:
1.1.1.2   misho    2394:                        def_authenticate = false;
1.1       misho    2395:                        break;
                   2396:                    default:
                   2397:                        break;
                   2398:                }
                   2399:            }
                   2400:        }
                   2401:        goto done;
                   2402:     }
                   2403: 
                   2404:     DPRINTF(("searching LDAP for sudoers entries"), 1);
                   2405: 
1.1.1.2   misho    2406:     setenv_implied = false;
1.1       misho    2407:     for (i = 0; i < lres->nentries; i++) {
                   2408:        entry = lres->entries[i].entry;
                   2409:        if (!sudo_ldap_check_runas(ld, entry))
                   2410:            continue;
                   2411:        rc = sudo_ldap_check_command(ld, entry, &setenv_implied);
                   2412:        if (rc != UNSPEC) {
                   2413:            /* We have a match. */
1.1.1.2   misho    2414:            DPRINTF(("Command %sallowed", rc == true ? "" : "NOT "), 1);
                   2415:            if (rc == true) {
1.1       misho    2416:                DPRINTF(("LDAP entry: %p", entry), 1);
                   2417:                /* Apply entry-specific options. */
                   2418:                if (setenv_implied)
1.1.1.2   misho    2419:                    def_setenv = true;
1.1       misho    2420:                sudo_ldap_parse_options(ld, entry);
                   2421: #ifdef HAVE_SELINUX
                   2422:                /* Set role and type if not specified on command line. */
                   2423:                if (user_role == NULL)
                   2424:                    user_role = def_role;
                   2425:                if (user_type == NULL)
                   2426:                    user_type = def_type;
                   2427: #endif /* HAVE_SELINUX */
                   2428:                SET(ret, VALIDATE_OK);
                   2429:                CLR(ret, VALIDATE_NOT_OK);
                   2430:            } else {
                   2431:                SET(ret, VALIDATE_NOT_OK);
                   2432:                CLR(ret, VALIDATE_OK);
                   2433:            }
                   2434:            break;
                   2435:        }
                   2436:     }
                   2437: 
                   2438: done:
                   2439:     DPRINTF(("done with LDAP searches"), 1);
                   2440:     DPRINTF(("user_matches=%d", lres->user_matches), 1);
                   2441:     DPRINTF(("host_matches=%d", lres->host_matches), 1);
                   2442: 
                   2443:     if (!ISSET(ret, VALIDATE_OK)) {
                   2444:        /* No matching entries. */
                   2445:        if (pwflag && list_pw == NULL)
                   2446:            SET(ret, FLAG_NO_CHECK);
                   2447:     }
                   2448:     if (lres->user_matches)
                   2449:        CLR(ret, FLAG_NO_USER);
                   2450:     if (lres->host_matches)
                   2451:        CLR(ret, FLAG_NO_HOST);
                   2452:     DPRINTF(("sudo_ldap_lookup(%d)=0x%02x", pwflag, ret), 1);
                   2453: 
1.1.1.2   misho    2454:     debug_return_int(ret);
1.1       misho    2455: }
                   2456: 
                   2457: /*
                   2458:  * Comparison function for ldap_entry_wrapper structures, descending order.
                   2459:  */
                   2460: static int
                   2461: ldap_entry_compare(const void *a, const void *b)
                   2462: {
                   2463:     const struct ldap_entry_wrapper *aw = a;
                   2464:     const struct ldap_entry_wrapper *bw = b;
1.1.1.2   misho    2465:     debug_decl(ldap_entry_compare, SUDO_DEBUG_LDAP)
1.1       misho    2466: 
1.1.1.2   misho    2467:     debug_return_int(bw->order < aw->order ? -1 :
                   2468:        (bw->order > aw->order ? 1 : 0));
1.1       misho    2469: }
                   2470: 
                   2471: /*
                   2472:  * Find the last entry in the list of searches, usually the
                   2473:  * one currently being used to add entries.
                   2474:  * XXX - use a tailq instead?
                   2475:  */
                   2476: static struct ldap_search_list *
                   2477: sudo_ldap_result_last_search(struct ldap_result *lres)
                   2478: {
                   2479:     struct ldap_search_list *result = lres->searches;
1.1.1.2   misho    2480:     debug_decl(sudo_ldap_result_last_search, SUDO_DEBUG_LDAP)
1.1       misho    2481: 
                   2482:     if (result) {
                   2483:        while (result->next)
                   2484:            result = result->next;
                   2485:     }
1.1.1.2   misho    2486:     debug_return_ptr(result);
1.1       misho    2487: }
                   2488: 
                   2489: /*
                   2490:  * Add an entry to the result structure.
                   2491:  */
                   2492: static struct ldap_entry_wrapper *
                   2493: sudo_ldap_result_add_entry(struct ldap_result *lres, LDAPMessage *entry)
                   2494: {
                   2495:     struct ldap_search_list *last;
                   2496:     struct berval **bv;
                   2497:     double order = 0.0;
                   2498:     char *ep;
1.1.1.2   misho    2499:     debug_decl(sudo_ldap_result_add_entry, SUDO_DEBUG_LDAP)
1.1       misho    2500: 
                   2501:     /* Determine whether the entry has the sudoOrder attribute. */
                   2502:     last = sudo_ldap_result_last_search(lres);
                   2503:     bv = ldap_get_values_len(last->ldap, entry, "sudoOrder");
                   2504:     if (bv != NULL) {
                   2505:        if (ldap_count_values_len(bv) > 0) {
                   2506:            /* Get the value of this attribute, 0 if not present. */
                   2507:            DPRINTF(("order attribute raw: %s", (*bv)->bv_val), 1);
                   2508:            order = strtod((*bv)->bv_val, &ep);
                   2509:            if (ep == (*bv)->bv_val || *ep != '\0') {
                   2510:                warningx(_("invalid sudoOrder attribute: %s"), (*bv)->bv_val);
                   2511:                order = 0.0;
                   2512:            }
                   2513:            DPRINTF(("order attribute: %f", order), 1);
                   2514:        }
                   2515:        ldap_value_free_len(bv);
                   2516:     }
                   2517: 
                   2518:     /*
                   2519:      * Enlarge the array of entry wrappers as needed, preallocating blocks
                   2520:      * of 100 entries to save on allocation time.
                   2521:      */
                   2522:     if (++lres->nentries > lres->allocated_entries) {
                   2523:        lres->allocated_entries += ALLOCATION_INCREMENT;
                   2524:        lres->entries = erealloc3(lres->entries, lres->allocated_entries,
                   2525:            sizeof(lres->entries[0]));
                   2526:     }
                   2527: 
                   2528:     /* Fill in the new entry and return it. */
                   2529:     lres->entries[lres->nentries - 1].entry = entry;
                   2530:     lres->entries[lres->nentries - 1].order = order;
                   2531: 
1.1.1.2   misho    2532:     debug_return_ptr(&lres->entries[lres->nentries - 1]);
1.1       misho    2533: }
                   2534: 
                   2535: /*
                   2536:  * Free the ldap result structure in the sudo_nss handle.
                   2537:  */
                   2538: static void
                   2539: sudo_ldap_result_free_nss(struct sudo_nss *nss)
                   2540: {
                   2541:     struct sudo_ldap_handle *handle = nss->handle;
1.1.1.2   misho    2542:     debug_decl(sudo_ldap_result_free_nss, SUDO_DEBUG_LDAP)
1.1       misho    2543: 
                   2544:     if (handle->result != NULL) {
                   2545:        DPRINTF(("removing reusable search result"), 1);
                   2546:        sudo_ldap_result_free(handle->result);
                   2547:        if (handle->username) {
                   2548:            efree(handle->username);
                   2549:            handle->username = NULL;
                   2550:        }
                   2551:        handle->grlist = NULL;
                   2552:        handle->result = NULL;
                   2553:     }
1.1.1.2   misho    2554:     debug_return;
1.1       misho    2555: }
                   2556: 
                   2557: /*
                   2558:  * Perform the LDAP query for the user or return a cached query if
                   2559:  * there is one for this user.
                   2560:  */
                   2561: static struct ldap_result *
                   2562: sudo_ldap_result_get(struct sudo_nss *nss, struct passwd *pw)
                   2563: {
                   2564:     struct sudo_ldap_handle *handle = nss->handle;
                   2565:     struct ldap_config_list_str *base;
                   2566:     struct ldap_result *lres;
                   2567:     struct timeval tv, *tvp = NULL;
                   2568:     LDAPMessage *entry, *result;
                   2569:     LDAP *ld = handle->ld;
                   2570:     int do_netgr, rc;
                   2571:     char *filt;
1.1.1.2   misho    2572:     debug_decl(sudo_ldap_result_get, SUDO_DEBUG_LDAP)
1.1       misho    2573: 
                   2574:     /*
                   2575:      * If we already have a cached result, return it so we don't have to
                   2576:      * have to contact the LDAP server again.
                   2577:      */
                   2578:     if (handle->result) {
                   2579:        if (handle->grlist == user_group_list &&
                   2580:            strcmp(pw->pw_name, handle->username) == 0) {
                   2581:            DPRINTF(("reusing previous result (user %s) with %d entries",
                   2582:                handle->username, handle->result->nentries), 1);
1.1.1.2   misho    2583:            debug_return_ptr(handle->result);
1.1       misho    2584:        }
                   2585:        /* User mismatch, cached result cannot be used. */
                   2586:        DPRINTF(("removing result (user %s), new search (user %s)",
                   2587:            handle->username, pw->pw_name), 1);
                   2588:        sudo_ldap_result_free_nss(nss);
                   2589:     }
                   2590: 
                   2591:     /*
                   2592:      * Okay - time to search for anything that matches this user
                   2593:      * Lets limit it to only two queries of the LDAP server
                   2594:      *
                   2595:      * The first pass will look by the username, groups, and
                   2596:      * the keyword ALL.  We will then inspect the results that
                   2597:      * came back from the query.  We don't need to inspect the
                   2598:      * sudoUser in this pass since the LDAP server already scanned
                   2599:      * it for us.
                   2600:      *
                   2601:      * The second pass will return all the entries that contain
                   2602:      * user netgroups.  Then we take the netgroups returned and
                   2603:      * try to match them against the username.
                   2604:      *
                   2605:      * Since we have to sort the possible entries before we make a
                   2606:      * decision, we perform the queries and store all of the results in
                   2607:      * an ldap_result object.  The results are then sorted by sudoOrder.
                   2608:      */
                   2609:     lres = sudo_ldap_result_alloc();
                   2610:     for (do_netgr = 0; do_netgr < 2; do_netgr++) {
                   2611:        filt = do_netgr ? sudo_ldap_build_pass2() : sudo_ldap_build_pass1(pw);
                   2612:        DPRINTF(("ldap search '%s'", filt), 1);
                   2613:        for (base = ldap_conf.base; base != NULL; base = base->next) {
                   2614:            DPRINTF(("searching from base '%s'", base->val), 1);
                   2615:            if (ldap_conf.timeout > 0) {
                   2616:                tv.tv_sec = ldap_conf.timeout;
                   2617:                tv.tv_usec = 0;
                   2618:                tvp = &tv;
                   2619:            }
                   2620:            result = NULL;
                   2621:            rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, filt,
                   2622:                NULL, 0, NULL, NULL, tvp, 0, &result);
                   2623:            if (rc != LDAP_SUCCESS) {
                   2624:                DPRINTF(("nothing found for '%s'", filt), 1);
                   2625:                continue;
                   2626:            }
1.1.1.2   misho    2627:            lres->user_matches = true;
1.1       misho    2628: 
                   2629:            /* Add the seach result to list of search results. */
                   2630:            DPRINTF(("adding search result"), 1);
                   2631:            sudo_ldap_result_add_search(lres, ld, result);
                   2632:            LDAP_FOREACH(entry, ld, result) {
                   2633:                if ((!do_netgr ||
                   2634:                    sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) &&
                   2635:                    sudo_ldap_check_host(ld, entry)) {
1.1.1.2   misho    2636:                    lres->host_matches = true;
1.1       misho    2637:                    sudo_ldap_result_add_entry(lres, entry);
                   2638:                }
                   2639:            }
                   2640:            DPRINTF(("result now has %d entries", lres->nentries), 1);
                   2641:        }
                   2642:        efree(filt);
                   2643:     }
                   2644: 
                   2645:     /* Sort the entries by the sudoOrder attribute. */
                   2646:     DPRINTF(("sorting remaining %d entries", lres->nentries), 1);
                   2647:     qsort(lres->entries, lres->nentries, sizeof(lres->entries[0]),
                   2648:        ldap_entry_compare);
                   2649: 
                   2650:     /* Store everything in the sudo_nss handle. */
                   2651:     handle->result = lres;
                   2652:     handle->username = estrdup(pw->pw_name);
                   2653:     handle->grlist = user_group_list;
                   2654: 
1.1.1.2   misho    2655:     debug_return_ptr(lres);
1.1       misho    2656: }
                   2657: 
                   2658: /*
                   2659:  * Shut down the LDAP connection.
                   2660:  */
                   2661: static int
                   2662: sudo_ldap_close(struct sudo_nss *nss)
                   2663: {
                   2664:     struct sudo_ldap_handle *handle = nss->handle;
1.1.1.2   misho    2665:     debug_decl(sudo_ldap_close, SUDO_DEBUG_LDAP)
1.1       misho    2666: 
                   2667:     if (handle != NULL) {
                   2668:        /* Free the result before unbinding; it may use the LDAP connection. */
                   2669:        sudo_ldap_result_free_nss(nss);
                   2670: 
                   2671:        /* Unbind and close the LDAP connection. */
                   2672:        if (handle->ld != NULL) {
                   2673:            ldap_unbind_ext_s(handle->ld, NULL, NULL);
                   2674:            handle->ld = NULL;
                   2675:        }
                   2676: 
                   2677:        /* Free the handle container. */
                   2678:        efree(nss->handle);
                   2679:        nss->handle = NULL;
                   2680:     }
1.1.1.2   misho    2681:     debug_return_int(0);
1.1       misho    2682: }
                   2683: 
                   2684: /*
                   2685:  * STUB
                   2686:  */
                   2687: static int
                   2688: sudo_ldap_parse(struct sudo_nss *nss)
                   2689: {
                   2690:     return 0;
                   2691: }
                   2692: 
                   2693: #if 0
                   2694: /*
                   2695:  * Create an ldap_result from an LDAP search result.
                   2696:  *
                   2697:  * This function is currently not used anywhere, it is left here as
                   2698:  * an example of how to use the cached searches.
                   2699:  */
                   2700: static struct ldap_result *
                   2701: sudo_ldap_result_from_search(LDAP *ldap, LDAPMessage *searchresult)
                   2702: {
                   2703:     /*
                   2704:      * An ldap_result is built from several search results, which are
                   2705:      * organized in a list. The head of the list is maintained in the
                   2706:      * ldap_result structure, together with the wrappers that point
                   2707:      * to individual entries, this has to be initialized first.
                   2708:      */
                   2709:     struct ldap_result *result = sudo_ldap_result_alloc();
                   2710: 
                   2711:     /*
                   2712:      * Build a new list node for the search result, this creates the
                   2713:      * list node.
                   2714:      */
                   2715:     struct ldap_search_list *last = sudo_ldap_result_add_search(result,
                   2716:        ldap, searchresult);
                   2717: 
                   2718:     /*
                   2719:      * Now add each entry in the search result to the array of of entries
                   2720:      * in the ldap_result object.
                   2721:      */
                   2722:     LDAPMessage        *entry;
                   2723:     LDAP_FOREACH(entry, last->ldap, last->searchresult) {
                   2724:        sudo_ldap_result_add_entry(result, entry);
                   2725:     }
                   2726:     DPRINTF(("sudo_ldap_result_from_search: %d entries found",
                   2727:        result->nentries), 2);
                   2728:     return result;
                   2729: }
                   2730: #endif

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