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

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

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