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

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

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