File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / plugins / sudoers / ldap.c
Revision 1.1.1.6 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 16:12:54 2014 UTC (10 years, 2 months ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_10p3_0, v1_8_10p3, HEAD
sudo v 1.8.10p3

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

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