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

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

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