]> granicus.if.org Git - sudo/commitdiff
Move LDAP configuration bits into ldap_conf.c
authorTodd C. Miller <Todd.Miller@sudo.ws>
Sun, 11 Feb 2018 14:36:08 +0000 (07:36 -0700)
committerTodd C. Miller <Todd.Miller@sudo.ws>
Sun, 11 Feb 2018 14:36:08 +0000 (07:36 -0700)
MANIFEST
configure
configure.ac
mkdep.pl
plugins/sudoers/Makefile.in
plugins/sudoers/ldap.c
plugins/sudoers/ldap_conf.c [new file with mode: 0644]
plugins/sudoers/sudo_ldap.h
plugins/sudoers/sudo_ldap_conf.h [new file with mode: 0644]

index a0f1471db329c63bc373422e6444eed1bed074c4..ac7d0d66bb310aa6d143158357858d33c2159f59 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -305,6 +305,7 @@ plugins/sudoers/iolog.h
 plugins/sudoers/iolog_path.c
 plugins/sudoers/ldap.c
 plugins/sudoers/ldap_common.c
+plugins/sudoers/ldap_conf.c
 plugins/sudoers/linux_audit.c
 plugins/sudoers/linux_audit.h
 plugins/sudoers/locale.c
@@ -553,6 +554,7 @@ plugins/sudoers/sssd.c
 plugins/sudoers/starttime.c
 plugins/sudoers/stubs.c
 plugins/sudoers/sudo_ldap.h
+plugins/sudoers/sudo_ldap_conf.h
 plugins/sudoers/sudo_nss.c
 plugins/sudoers/sudo_nss.h
 plugins/sudoers/sudo_printf.c
index 900203b2ce464270c1cf286161459800582060f5..ba8da8f6c1ab0aec77798b71aea4c9d09de98d54 100755 (executable)
--- a/configure
+++ b/configure
@@ -24492,7 +24492,7 @@ fi
 
        with_ldap=yes
     fi
-    SUDOERS_OBJS="${SUDOERS_OBJS} ldap.lo"
+    SUDOERS_OBJS="${SUDOERS_OBJS} ldap.lo ldap_conf.lo"
     case "$SUDOERS_OBJS" in
        *ldap_common.lo*) ;;
        *) SUDOERS_OBJS="${SUDOERS_OBJS} ldap_common.lo";;
index 4731be4e7056671509730604f7dbaab547286dda..d0ca587117284db63541d618643c7c5addddaa03 100644 (file)
@@ -3730,7 +3730,7 @@ if test ${with_ldap-'no'} != "no"; then
        AX_APPEND_FLAG([-I${with_ldap}/include], [CPPFLAGS])
        with_ldap=yes
     fi
-    SUDOERS_OBJS="${SUDOERS_OBJS} ldap.lo"
+    SUDOERS_OBJS="${SUDOERS_OBJS} ldap.lo ldap_conf.lo"
     case "$SUDOERS_OBJS" in
        *ldap_common.lo*) ;;
        *) SUDOERS_OBJS="${SUDOERS_OBJS} ldap_common.lo";;
index 4b4d85fbd24113fd86ba8a02ef0aa94bd8925643..d0a6200537ca4bf9967cbb0c67ef570659b20136 100755 (executable)
--- a/mkdep.pl
+++ b/mkdep.pl
@@ -79,7 +79,7 @@ sub mkdep {
     $makefile =~ s:\@DEV\@::g;
     $makefile =~ s:\@COMMON_OBJS\@:aix.lo event_poll.lo event_select.lo:;
     $makefile =~ s:\@SUDO_OBJS\@:openbsd.o preload.o selinux.o sesh.o solaris.o:;
-    $makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo ldap_common.lo solaris_audit.lo sssd.lo:;
+    $makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo ldap_common.lo ldap_conf.lo solaris_audit.lo sssd.lo:;
     # XXX - fill in AUTH_OBJS from contents of the auth dir instead
     $makefile =~ s:\@AUTH_OBJS\@:afs.lo aix_auth.lo bsdauth.lo dce.lo fwtk.lo getspwuid.lo kerb5.lo pam.lo passwd.lo rfc1938.lo secureware.lo securid5.lo sia.lo:;
     $makefile =~ s:\@FILEDIGEST\@:filedigest.lo filedigest_openssl.lo filedigest_gcrypt.lo:;
index fb73b82a3ec4217b3abb433dfa737981dbf29a5a..e77cde03c29956757ad05d656a2b8ea0640853bf 100644 (file)
@@ -987,6 +987,17 @@ ldap_common.lo: $(srcdir)/ldap_common.c $(devdir)/def_data.h $(devdir)/gram.h \
                 $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
                 $(top_builddir)/pathnames.h
        $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/ldap_common.c
+ldap_conf.lo: $(srcdir)/ldap_conf.c $(devdir)/def_data.h $(devdir)/gram.h \
+              $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
+              $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
+              $(incdir)/sudo_dso.h $(incdir)/sudo_fatal.h \
+              $(incdir)/sudo_gettext.h $(incdir)/sudo_lbuf.h \
+              $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
+              $(incdir)/sudo_util.h $(srcdir)/defaults.h $(srcdir)/logging.h \
+              $(srcdir)/parse.h $(srcdir)/sudo_ldap.h $(srcdir)/sudo_nss.h \
+              $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \
+              $(top_builddir)/config.h $(top_builddir)/pathnames.h
+       $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/ldap_conf.c
 linux_audit.lo: $(srcdir)/linux_audit.c $(devdir)/def_data.h \
                 $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
                 $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
index 87f26ce27e890ef1df8205b31995249152e3f993..e34ad1147e2816117747496e1b2a24168a4c9d96 100644 (file)
@@ -36,9 +36,6 @@
 #include <fcntl.h>
 #include <pwd.h>
 #include <grp.h>
-#include <signal.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
 #ifdef HAVE_LBER_H
 # include <lber.h>
 #endif
 #include "gram.h"
 #include "sudo_lbuf.h"
 #include "sudo_ldap.h"
+#include "sudo_ldap_conf.h"
 #include "sudo_dso.h"
 
-/* Older Netscape LDAP SDKs don't prototype ldapssl_set_strength() */
-#if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(HAVE_LDAP_SSL_H) && !defined(HAVE_MPS_LDAP_SSL_H)
-extern int ldapssl_set_strength(LDAP *ldap, int strength);
-#endif
-
-#if !defined(LDAP_OPT_NETWORK_TIMEOUT) && defined(LDAP_OPT_CONNECT_TIMEOUT)
-# define LDAP_OPT_NETWORK_TIMEOUT LDAP_OPT_CONNECT_TIMEOUT
-#endif
-
-#ifndef LDAP_OPT_SUCCESS
-# define LDAP_OPT_SUCCESS LDAP_SUCCESS
-#endif
-
-#ifndef LDAPS_PORT
-# define LDAPS_PORT 636
-#endif
-
 #if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && !defined(LDAP_SASL_QUIET)
 # define LDAP_SASL_QUIET       0
 #endif
@@ -103,60 +84,6 @@ extern int ldapssl_set_strength(LDAP *ldap, int strength);
        (var) != NULL;                                                  \
        (var) = ldap_next_entry((ld), (var)))
 
-#if defined(__GNUC__) && __GNUC__ == 2
-# define DPRINTF1(fmt...) do {                                         \
-    sudo_debug_printf(SUDO_DEBUG_DIAG, fmt);                           \
-    if (ldap_conf.debug >= 1)                                          \
-       sudo_warnx_nodebug(fmt);                                        \
-} while (0)
-# define DPRINTF2(fmt...) do {                                         \
-    sudo_debug_printf(SUDO_DEBUG_INFO, fmt);                           \
-    if (ldap_conf.debug >= 2)                                          \
-       sudo_warnx_nodebug(fmt);                                        \
-} while (0)
-#else
-# define DPRINTF1(...) do {                                            \
-    sudo_debug_printf(SUDO_DEBUG_DIAG, __VA_ARGS__);                   \
-    if (ldap_conf.debug >= 1)                                          \
-       sudo_warnx_nodebug(__VA_ARGS__);                                \
-} while (0)
-# define DPRINTF2(...) do {                                            \
-    sudo_debug_printf(SUDO_DEBUG_INFO, __VA_ARGS__);                   \
-    if (ldap_conf.debug >= 2)                                          \
-       sudo_warnx_nodebug(__VA_ARGS__);                                \
-} while (0)
-#endif
-
-/* Macros for checking strlcpy/strlcat/sudo_ldap_value_cat return value. */
-#define CHECK_STRLCPY(d, s, l) do {                                           \
-       if (strlcpy((d), (s), (l)) >= (l))                                     \
-           goto overflow;                                                     \
-} while (0)
-#define CHECK_STRLCAT(d, s, l) do {                                           \
-       if (strlcat((d), (s), (l)) >= (l))                                     \
-           goto overflow;                                                     \
-} while (0)
-#define CHECK_LDAP_VCAT(d, s, l) do {                                         \
-       if (sudo_ldap_value_cat((d), (s), (l)) >= (l))                         \
-           goto overflow;                                                     \
-} while (0)
-
-#define CONF_BOOL      0
-#define CONF_INT       1
-#define CONF_STR       2
-#define CONF_LIST_STR  4
-#define CONF_DEREF_VAL 5
-
-#define SUDO_LDAP_CLEAR                0
-#define SUDO_LDAP_SSL          1
-#define SUDO_LDAP_STARTTLS     2
-
-/* Default search filter. */
-#define DEFAULT_SEARCH_FILTER  "(objectClass=sudoRole)"
-
-/* Default netgroup search filter. */
-#define DEFAULT_NETGROUP_SEARCH_FILTER "(objectClass=nisNetgroup)"
-
 /* The TIMEFILTER_LENGTH is the length of the filter when timed entries
    are used. The length is computed as follows:
        81       for the filter itself
@@ -212,160 +139,6 @@ struct ldap_netgroup {
 };
 STAILQ_HEAD(ldap_netgroup_list, ldap_netgroup);
 
-struct ldap_config_table {
-    const char *conf_str;      /* config file string */
-    int type;                  /* CONF_BOOL, CONF_INT, CONF_STR */
-    int opt_val;               /* LDAP_OPT_* (or -1 for sudo internal) */
-    void *valp;                        /* pointer into ldap_conf */
-};
-
-struct ldap_config_str {
-    STAILQ_ENTRY(ldap_config_str) entries;
-    char val[1];
-};
-STAILQ_HEAD(ldap_config_str_list, ldap_config_str);
-
-/* LDAP configuration structure */
-static struct ldap_config {
-    int port;
-    int version;
-    int debug;
-    int ldap_debug;
-    int tls_checkpeer;
-    int timelimit;
-    int timeout;
-    int bind_timelimit;
-    int use_sasl;
-    int rootuse_sasl;
-    int ssl_mode;
-    int timed;
-    int deref;
-    char *host;
-    struct ldap_config_str_list uri;
-    char *binddn;
-    char *bindpw;
-    char *rootbinddn;
-    struct ldap_config_str_list base;
-    struct ldap_config_str_list netgroup_base;
-    char *search_filter;
-    char *netgroup_search_filter;
-    char *ssl;
-    char *tls_cacertfile;
-    char *tls_cacertdir;
-    char *tls_random_file;
-    char *tls_cipher_suite;
-    char *tls_certfile;
-    char *tls_keyfile;
-    char *tls_keypw;
-    char *sasl_mech;
-    char *sasl_auth_id;
-    char *rootsasl_auth_id;
-    char *sasl_secprops;
-    char *krb5_ccname;
-} ldap_conf;
-
-static struct ldap_config_table ldap_conf_global[] = {
-    { "sudoers_debug", CONF_INT, -1, &ldap_conf.debug },
-    { "host", CONF_STR, -1, &ldap_conf.host },
-    { "port", CONF_INT, -1, &ldap_conf.port },
-    { "ssl", CONF_STR, -1, &ldap_conf.ssl },
-    { "sslpath", CONF_STR, -1, &ldap_conf.tls_certfile },
-    { "uri", CONF_LIST_STR, -1, &ldap_conf.uri },
-#ifdef LDAP_OPT_DEBUG_LEVEL
-    { "debug", CONF_INT, LDAP_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug },
-#endif
-#ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
-    { "tls_checkpeer", CONF_BOOL, LDAP_OPT_X_TLS_REQUIRE_CERT,
-       &ldap_conf.tls_checkpeer },
-#else
-    { "tls_checkpeer", CONF_BOOL, -1, &ldap_conf.tls_checkpeer },
-#endif
-#ifdef LDAP_OPT_X_TLS_CACERTFILE
-    { "tls_cacertfile", CONF_STR, LDAP_OPT_X_TLS_CACERTFILE,
-       &ldap_conf.tls_cacertfile },
-    { "tls_cacert", CONF_STR, LDAP_OPT_X_TLS_CACERTFILE,
-       &ldap_conf.tls_cacertfile },
-#endif
-#ifdef LDAP_OPT_X_TLS_CACERTDIR
-    { "tls_cacertdir", CONF_STR, LDAP_OPT_X_TLS_CACERTDIR,
-       &ldap_conf.tls_cacertdir },
-#endif
-#ifdef LDAP_OPT_X_TLS_RANDOM_FILE
-    { "tls_randfile", CONF_STR, LDAP_OPT_X_TLS_RANDOM_FILE,
-       &ldap_conf.tls_random_file },
-#endif
-#ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
-    { "tls_ciphers", CONF_STR, LDAP_OPT_X_TLS_CIPHER_SUITE,
-       &ldap_conf.tls_cipher_suite },
-#elif defined(LDAP_OPT_SSL_CIPHER)
-    { "tls_ciphers", CONF_STR, LDAP_OPT_SSL_CIPHER,
-       &ldap_conf.tls_cipher_suite },
-#endif
-#ifdef LDAP_OPT_X_TLS_CERTFILE
-    { "tls_cert", CONF_STR, LDAP_OPT_X_TLS_CERTFILE,
-       &ldap_conf.tls_certfile },
-#else
-    { "tls_cert", CONF_STR, -1, &ldap_conf.tls_certfile },
-#endif
-#ifdef LDAP_OPT_X_TLS_KEYFILE
-    { "tls_key", CONF_STR, LDAP_OPT_X_TLS_KEYFILE,
-       &ldap_conf.tls_keyfile },
-#else
-    { "tls_key", CONF_STR, -1, &ldap_conf.tls_keyfile },
-#endif
-#ifdef HAVE_LDAP_SSL_CLIENT_INIT
-    { "tls_keypw", CONF_STR, -1, &ldap_conf.tls_keypw },
-#endif
-    { "binddn", CONF_STR, -1, &ldap_conf.binddn },
-    { "bindpw", CONF_STR, -1, &ldap_conf.bindpw },
-    { "rootbinddn", CONF_STR, -1, &ldap_conf.rootbinddn },
-    { "sudoers_base", CONF_LIST_STR, -1, &ldap_conf.base },
-    { "sudoers_timed", CONF_BOOL, -1, &ldap_conf.timed },
-    { "sudoers_search_filter", CONF_STR, -1, &ldap_conf.search_filter },
-    { "netgroup_base", CONF_LIST_STR, -1, &ldap_conf.netgroup_base },
-    { "netgroup_search_filter", CONF_STR, -1, &ldap_conf.netgroup_search_filter },
-#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
-    { "use_sasl", CONF_BOOL, -1, &ldap_conf.use_sasl },
-    { "sasl_mech", CONF_STR, -1, &ldap_conf.sasl_mech },
-    { "sasl_auth_id", CONF_STR, -1, &ldap_conf.sasl_auth_id },
-    { "rootuse_sasl", CONF_BOOL, -1, &ldap_conf.rootuse_sasl },
-    { "rootsasl_auth_id", CONF_STR, -1, &ldap_conf.rootsasl_auth_id },
-    { "krb5_ccname", CONF_STR, -1, &ldap_conf.krb5_ccname },
-#endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
-    { NULL }
-};
-
-static struct ldap_config_table ldap_conf_conn[] = {
-#ifdef LDAP_OPT_PROTOCOL_VERSION
-    { "ldap_version", CONF_INT, LDAP_OPT_PROTOCOL_VERSION,
-       &ldap_conf.version },
-#endif
-#ifdef LDAP_OPT_NETWORK_TIMEOUT
-    { "bind_timelimit", CONF_INT, -1 /* needs timeval, set manually */,
-       &ldap_conf.bind_timelimit },
-    { "network_timeout", CONF_INT, -1 /* needs timeval, set manually */,
-       &ldap_conf.bind_timelimit },
-#elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
-    { "bind_timelimit", CONF_INT, LDAP_X_OPT_CONNECT_TIMEOUT,
-       &ldap_conf.bind_timelimit },
-    { "network_timeout", CONF_INT, LDAP_X_OPT_CONNECT_TIMEOUT,
-       &ldap_conf.bind_timelimit },
-#endif
-    { "timelimit", CONF_INT, LDAP_OPT_TIMELIMIT, &ldap_conf.timelimit },
-#ifdef LDAP_OPT_TIMEOUT
-    { "timeout", CONF_INT, -1 /* needs timeval, set manually */,
-       &ldap_conf.timeout },
-#endif
-#ifdef LDAP_OPT_DEREF
-    { "deref", CONF_DEREF_VAL, LDAP_OPT_DEREF, &ldap_conf.deref },
-#endif
-#ifdef LDAP_OPT_X_SASL_SECPROPS
-    { "sasl_secprops", CONF_STR, LDAP_OPT_X_SASL_SECPROPS,
-       &ldap_conf.sasl_secprops },
-#endif
-    { NULL }
-};
-
 /* sudo_nss implementation */
 static int sudo_ldap_open(struct sudo_nss *nss);
 static int sudo_ldap_close(struct sudo_nss *nss);
@@ -410,140 +183,7 @@ struct sudo_nss sudo_nss_ldap = {
     sudo_ldap_display_privs
 };
 
-#ifdef HAVE_LDAP_CREATE
-/*
- * Rebuild the hosts list and include a specific port for each host.
- * ldap_create() does not take a default port parameter so we must
- * append one if we want something other than LDAP_PORT.
- */
-static bool
-sudo_ldap_conf_add_ports(void)
-{
-    char *host, *last, *port, defport[13];
-    char hostbuf[LINE_MAX * 2];
-    int len;
-    debug_decl(sudo_ldap_conf_add_ports, SUDOERS_DEBUG_LDAP)
-
-    hostbuf[0] = '\0';
-    len = snprintf(defport, sizeof(defport), ":%d", ldap_conf.port);
-    if (len <= 0 || (size_t)len >= sizeof(defport)) {
-       sudo_warnx(U_("sudo_ldap_conf_add_ports: port too large"));
-       debug_return_bool(false);
-    }
-
-    for ((host = strtok_r(ldap_conf.host, " \t", &last)); host; (host = strtok_r(NULL, " \t", &last))) {
-       if (hostbuf[0] != '\0')
-           CHECK_STRLCAT(hostbuf, " ", sizeof(hostbuf));
-       CHECK_STRLCAT(hostbuf, host, sizeof(hostbuf));
-
-       /* Append port if there is not one already. */
-       if ((port = strrchr(host, ':')) == NULL ||
-           !isdigit((unsigned char)port[1])) {
-           CHECK_STRLCAT(hostbuf, defport, sizeof(hostbuf));
-       }
-    }
-
-    free(ldap_conf.host);
-    if ((ldap_conf.host = strdup(hostbuf)) == NULL)
-       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-    debug_return_bool(ldap_conf.host != NULL);
-
-overflow:
-    sudo_warnx(U_("internal error, %s overflow"), __func__);
-    debug_return_bool(false);
-}
-#endif
-
-#ifndef HAVE_LDAP_INITIALIZE
-/*
- * For each uri, convert to host:port pairs.  For ldaps:// enable SSL
- * Accepts: uris of the form ldap:/// or ldap://hostname:portnum/
- * where the trailing slash is optional.
- * Returns LDAP_SUCCESS on success, else non-zero.
- */
-static int
-sudo_ldap_parse_uri(const struct ldap_config_str_list *uri_list)
-{
-    const struct ldap_config_str *entry;
-    char *buf, hostbuf[LINE_MAX];
-    int nldap = 0, nldaps = 0;
-    int ret = -1;
-    debug_decl(sudo_ldap_parse_uri, SUDOERS_DEBUG_LDAP)
-
-    hostbuf[0] = '\0';
-    STAILQ_FOREACH(entry, uri_list, entries) {
-       char *cp, *host, *last, *port, *uri;
-
-       buf = strdup(entry->val);
-       if (buf == NULL) {
-           sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-           goto done;
-       }
-       for ((uri = strtok_r(buf, " \t", &last)); uri != NULL; (uri = strtok_r(NULL, " \t", &last))) {
-           if (strncasecmp(uri, "ldap://", 7) == 0) {
-               nldap++;
-               host = uri + 7;
-           } else if (strncasecmp(uri, "ldaps://", 8) == 0) {
-               nldaps++;
-               host = uri + 8;
-           } else {
-               sudo_warnx(U_("unsupported LDAP uri type: %s"), uri);
-               goto done;
-           }
-
-           /* trim optional trailing slash */
-           if ((cp = strrchr(host, '/')) != NULL && cp[1] == '\0') {
-               *cp = '\0';
-           }
-
-           if (hostbuf[0] != '\0')
-               CHECK_STRLCAT(hostbuf, " ", sizeof(hostbuf));
-
-           if (*host == '\0')
-               host = "localhost";             /* no host specified, use localhost */
-
-           CHECK_STRLCAT(hostbuf, host, sizeof(hostbuf));
-
-           /* If using SSL and no port specified, add port 636 */
-           if (nldaps) {
-               if ((port = strrchr(host, ':')) == NULL ||
-                   !isdigit((unsigned char)port[1]))
-                   CHECK_STRLCAT(hostbuf, ":636", sizeof(hostbuf));
-           }
-       }
-
-       if (nldaps != 0) {
-           if (nldap != 0) {
-               sudo_warnx(U_("unable to mix ldap and ldaps URIs"));
-               goto done;
-           }
-           if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS)
-               sudo_warnx(U_("starttls not supported when using ldaps"));
-           ldap_conf.ssl_mode = SUDO_LDAP_SSL;
-       }
-       free(buf);
-    }
-    buf = NULL;
-
-    /* Store parsed URI(s) in host for ldap_create() or ldap_init(). */
-    free(ldap_conf.host);
-    if ((ldap_conf.host = strdup(hostbuf)) == NULL) {
-       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-       goto done;
-    }
-
-    ret = LDAP_SUCCESS;
-
-done:
-    free(buf);
-    debug_return_int(ret);
-
-overflow:
-    sudo_warnx(U_("internal error, %s overflow"), __func__);
-    free(buf);
-    debug_return_int(-1);
-}
-#else
+#ifdef HAVE_LDAP_INITIALIZE
 static char *
 sudo_ldap_join_uri(struct ldap_config_str_list *uri_list)
 {
@@ -1746,464 +1386,6 @@ sudo_ldap_build_pass2(void)
     debug_return_str(filt);
 }
 
-/*
- * Decode a secret if it is base64 encoded, else return NULL.
- */
-static char *
-sudo_ldap_decode_secret(const char *secret)
-{
-    unsigned char *result = NULL;
-    size_t len, reslen;
-    debug_decl(sudo_ldap_decode_secret, SUDOERS_DEBUG_LDAP)
-
-    if (strncasecmp(secret, "base64:", sizeof("base64:") - 1) == 0) {
-       /*
-        * Decode a base64 secret.  The decoded length is 3/4 the encoded
-        * length but padding may be missing so round up to a multiple of 4.
-        */
-       secret += sizeof("base64:") - 1;
-       reslen = ((strlen(secret) + 3) / 4 * 3);
-       result = malloc(reslen + 1);
-       if (result == NULL) {
-           sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-       } else {
-           len = base64_decode(secret, result, reslen);
-           if (len == (size_t)-1) {
-               free(result);
-               result = NULL;
-           } else {
-               result[len] = '\0';
-           }
-       }
-    }
-    debug_return_str((char *)result);
-}
-
-static void
-sudo_ldap_read_secret(const char *path)
-{
-    FILE *fp;
-    char *line = NULL;
-    size_t linesize = 0;
-    ssize_t len;
-    debug_decl(sudo_ldap_read_secret, SUDOERS_DEBUG_LDAP)
-
-    if ((fp = fopen(path_ldap_secret, "r")) != NULL) {
-       len = getline(&line, &linesize, fp);
-       if (len != -1) {
-           /* trim newline */
-           while (len > 0 && line[len - 1] == '\n')
-               line[--len] = '\0';
-           /* copy to bindpw and binddn */
-           free(ldap_conf.bindpw);
-           ldap_conf.bindpw = sudo_ldap_decode_secret(line);
-           if (ldap_conf.bindpw == NULL) {
-               /* not base64 encoded, use directly */
-               ldap_conf.bindpw = line;
-               line = NULL;
-           }
-           free(ldap_conf.binddn);
-           ldap_conf.binddn = ldap_conf.rootbinddn;
-           ldap_conf.rootbinddn = NULL;
-       }
-       fclose(fp);
-       free(line);
-    }
-    debug_return;
-}
-
-/*
- * Look up keyword in config tables.
- * Returns true if found, else false.
- */
-static bool
-sudo_ldap_parse_keyword(const char *keyword, const char *value,
-    struct ldap_config_table *table)
-{
-    struct ldap_config_table *cur;
-    const char *errstr;
-    debug_decl(sudo_ldap_parse_keyword, SUDOERS_DEBUG_LDAP)
-
-    /* Look up keyword in config tables */
-    for (cur = table; cur->conf_str != NULL; cur++) {
-       if (strcasecmp(keyword, cur->conf_str) == 0) {
-           switch (cur->type) {
-           case CONF_DEREF_VAL:
-               if (strcasecmp(value, "searching") == 0)
-                   *(int *)(cur->valp) = LDAP_DEREF_SEARCHING;
-               else if (strcasecmp(value, "finding") == 0)
-                   *(int *)(cur->valp) = LDAP_DEREF_FINDING;
-               else if (strcasecmp(value, "always") == 0)
-                   *(int *)(cur->valp) = LDAP_DEREF_ALWAYS;
-               else
-                   *(int *)(cur->valp) = LDAP_DEREF_NEVER;
-               break;
-           case CONF_BOOL:
-               *(int *)(cur->valp) = sudo_strtobool(value) == true;
-               break;
-           case CONF_INT:
-               *(int *)(cur->valp) = strtonum(value, INT_MIN, INT_MAX, &errstr);
-               if (errstr != NULL) {
-                   sudo_warnx(U_("%s: %s: %s: %s"),
-                       path_ldap_conf, keyword, value, U_(errstr));
-               }
-               break;
-           case CONF_STR:
-               {
-                   char *cp = NULL;
-
-                   free(*(char **)(cur->valp));
-                   if (*value && (cp = strdup(value)) == NULL) {
-                       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-                       debug_return_bool(false);
-                   }
-                   *(char **)(cur->valp) = cp;
-                   break;
-               }
-           case CONF_LIST_STR:
-               {
-                   struct ldap_config_str_list *head;
-                   struct ldap_config_str *str;
-                   size_t len = strlen(value);
-
-                   if (len > 0) {
-                       head = (struct ldap_config_str_list *)cur->valp;
-                       if ((str = malloc(sizeof(*str) + len)) == NULL) {
-                           sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-                           debug_return_bool(false);
-                       }
-                       memcpy(str->val, value, len + 1);
-                       STAILQ_INSERT_TAIL(head, str, entries);
-                   }
-               }
-               break;
-           }
-           debug_return_bool(true);
-       }
-    }
-    debug_return_bool(false);
-}
-
-#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
-static const char *
-sudo_krb5_ccname_path(const char *old_ccname)
-{
-    const char *ccname = old_ccname;
-    debug_decl(sudo_krb5_ccname_path, SUDOERS_DEBUG_LDAP)
-
-    /* Strip off leading FILE: or WRFILE: prefix. */
-    switch (ccname[0]) {
-       case 'F':
-       case 'f':
-           if (strncasecmp(ccname, "FILE:", 5) == 0)
-               ccname += 5;
-           break;
-       case 'W':
-       case 'w':
-           if (strncasecmp(ccname, "WRFILE:", 7) == 0)
-               ccname += 7;
-           break;
-    }
-    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
-       "ccache %s -> %s", old_ccname, ccname);
-
-    /* Credential cache must be a fully-qualified path name. */
-    debug_return_const_str(*ccname == '/' ? ccname : NULL);
-}
-
-static bool
-sudo_check_krb5_ccname(const char *ccname)
-{
-    int fd = -1;
-    const char *ccname_path;
-    debug_decl(sudo_check_krb5_ccname, SUDOERS_DEBUG_LDAP)
-
-    /* Strip off prefix to get path name. */
-    ccname_path = sudo_krb5_ccname_path(ccname);
-    if (ccname_path == NULL) {
-       sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
-           "unsupported krb5 credential cache path: %s", ccname);
-       debug_return_bool(false);
-    }
-    /* Make sure credential cache is fully-qualified and exists. */
-    fd = open(ccname_path, O_RDONLY|O_NONBLOCK, 0);
-    if (fd == -1) {
-       sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
-           "unable to open krb5 credential cache: %s", ccname_path);
-       debug_return_bool(false);
-    }
-    close(fd);
-    sudo_debug_printf(SUDO_DEBUG_INFO,
-       "using krb5 credential cache: %s", ccname_path);
-    debug_return_bool(true);
-}
-#endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
-
-static bool
-sudo_ldap_read_config(void)
-{
-    char *cp, *keyword, *value, *line = NULL;
-    struct ldap_config_str *conf_str;
-    size_t linesize = 0;
-    FILE *fp;
-    debug_decl(sudo_ldap_read_config, SUDOERS_DEBUG_LDAP)
-
-    /* defaults */
-    ldap_conf.version = 3;
-    ldap_conf.port = -1;
-    ldap_conf.tls_checkpeer = -1;
-    ldap_conf.timelimit = -1;
-    ldap_conf.timeout = -1;
-    ldap_conf.bind_timelimit = -1;
-    ldap_conf.use_sasl = -1;
-    ldap_conf.rootuse_sasl = -1;
-    ldap_conf.deref = -1;
-    ldap_conf.search_filter = strdup(DEFAULT_SEARCH_FILTER);
-    ldap_conf.netgroup_search_filter = strdup(DEFAULT_NETGROUP_SEARCH_FILTER);
-    STAILQ_INIT(&ldap_conf.uri);
-    STAILQ_INIT(&ldap_conf.base);
-    STAILQ_INIT(&ldap_conf.netgroup_base);
-
-    if (ldap_conf.search_filter == NULL || ldap_conf.netgroup_search_filter == NULL) {
-       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-       debug_return_bool(false);
-    }
-
-    if ((fp = fopen(path_ldap_conf, "r")) == NULL)
-       debug_return_bool(false);
-
-    while (sudo_parseln(&line, &linesize, NULL, fp, PARSELN_COMM_BOL|PARSELN_CONT_IGN) != -1) {
-       if (*line == '\0')
-           continue;           /* skip empty line */
-
-       /* split into keyword and value */
-       keyword = cp = line;
-       while (*cp && !isblank((unsigned char) *cp))
-           cp++;
-       if (*cp)
-           *cp++ = '\0';       /* terminate keyword */
-
-       /* skip whitespace before value */
-       while (isblank((unsigned char) *cp))
-           cp++;
-       value = cp;
-
-       /* Look up keyword in config tables */
-       if (!sudo_ldap_parse_keyword(keyword, value, ldap_conf_global))
-           sudo_ldap_parse_keyword(keyword, value, ldap_conf_conn);
-    }
-    free(line);
-    fclose(fp);
-
-    if (!ldap_conf.host) {
-       ldap_conf.host = strdup("localhost");
-       if (ldap_conf.host == NULL) {
-           sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-           debug_return_bool(false);
-       }
-    }
-
-    DPRINTF1("LDAP Config Summary");
-    DPRINTF1("===================");
-    if (!STAILQ_EMPTY(&ldap_conf.uri)) {
-       STAILQ_FOREACH(conf_str, &ldap_conf.uri, entries) {
-           DPRINTF1("uri              %s", conf_str->val);
-       }
-    } else {
-       DPRINTF1("host             %s",
-           ldap_conf.host ? ldap_conf.host : "(NONE)");
-       DPRINTF1("port             %d", ldap_conf.port);
-    }
-    DPRINTF1("ldap_version     %d", ldap_conf.version);
-
-    if (!STAILQ_EMPTY(&ldap_conf.base)) {
-       STAILQ_FOREACH(conf_str, &ldap_conf.base, entries) {
-           DPRINTF1("sudoers_base     %s", conf_str->val);
-       }
-    } else {
-       DPRINTF1("sudoers_base     %s", "(NONE: LDAP disabled)");
-    }
-    if (ldap_conf.search_filter) {
-       DPRINTF1("search_filter    %s", ldap_conf.search_filter);
-    }
-    if (!STAILQ_EMPTY(&ldap_conf.netgroup_base)) {
-       STAILQ_FOREACH(conf_str, &ldap_conf.netgroup_base, entries) {
-           DPRINTF1("netgroup_base    %s", conf_str->val);
-       }
-    } else {
-       DPRINTF1("netgroup_base %s", "(NONE: will use nsswitch)");
-    }
-    if (ldap_conf.netgroup_search_filter) {
-        DPRINTF1("netgroup_search_filter %s", ldap_conf.netgroup_search_filter);
-    }
-    DPRINTF1("binddn           %s",
-       ldap_conf.binddn ? ldap_conf.binddn : "(anonymous)");
-    DPRINTF1("bindpw           %s",
-       ldap_conf.bindpw ? ldap_conf.bindpw : "(anonymous)");
-    if (ldap_conf.bind_timelimit > 0) {
-       DPRINTF1("bind_timelimit   %d", ldap_conf.bind_timelimit);
-    }
-    if (ldap_conf.timelimit > 0) {
-       DPRINTF1("timelimit        %d", ldap_conf.timelimit);
-    }
-    if (ldap_conf.deref != -1) {
-       DPRINTF1("deref            %d", ldap_conf.deref);
-    }
-    DPRINTF1("ssl              %s", ldap_conf.ssl ? ldap_conf.ssl : "(no)");
-    if (ldap_conf.tls_checkpeer != -1) {
-       DPRINTF1("tls_checkpeer    %s",
-           ldap_conf.tls_checkpeer ? "(yes)" : "(no)");
-    }
-    if (ldap_conf.tls_cacertfile != NULL) {
-       DPRINTF1("tls_cacertfile   %s", ldap_conf.tls_cacertfile);
-    }
-    if (ldap_conf.tls_cacertdir != NULL) {
-       DPRINTF1("tls_cacertdir    %s", ldap_conf.tls_cacertdir);
-    }
-    if (ldap_conf.tls_random_file != NULL) {
-       DPRINTF1("tls_random_file  %s", ldap_conf.tls_random_file);
-    }
-    if (ldap_conf.tls_cipher_suite != NULL) {
-       DPRINTF1("tls_cipher_suite %s", ldap_conf.tls_cipher_suite);
-    }
-    if (ldap_conf.tls_certfile != NULL) {
-       DPRINTF1("tls_certfile     %s", ldap_conf.tls_certfile);
-    }
-    if (ldap_conf.tls_keyfile != NULL) {
-       DPRINTF1("tls_keyfile      %s", ldap_conf.tls_keyfile);
-    }
-#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
-    if (ldap_conf.use_sasl != -1) {
-       if (ldap_conf.sasl_mech == NULL) {
-           /* Default mechanism is GSSAPI. */
-           ldap_conf.sasl_mech = strdup("GSSAPI");
-           if (ldap_conf.sasl_mech == NULL) {
-               sudo_warnx(U_("%s: %s"), __func__,
-                   U_("unable to allocate memory"));
-               debug_return_bool(false);
-           }
-       }
-       DPRINTF1("use_sasl         %s", ldap_conf.use_sasl ? "yes" : "no");
-       DPRINTF1("sasl_mech        %s", ldap_conf.sasl_mech);
-       DPRINTF1("sasl_auth_id     %s",
-           ldap_conf.sasl_auth_id ? ldap_conf.sasl_auth_id : "(NONE)");
-       DPRINTF1("rootuse_sasl     %d",
-           ldap_conf.rootuse_sasl);
-       DPRINTF1("rootsasl_auth_id %s",
-           ldap_conf.rootsasl_auth_id ? ldap_conf.rootsasl_auth_id : "(NONE)");
-       DPRINTF1("sasl_secprops    %s",
-           ldap_conf.sasl_secprops ? ldap_conf.sasl_secprops : "(NONE)");
-       DPRINTF1("krb5_ccname      %s",
-           ldap_conf.krb5_ccname ? ldap_conf.krb5_ccname : "(NONE)");
-    }
-#endif
-    DPRINTF1("===================");
-
-    if (STAILQ_EMPTY(&ldap_conf.base))
-       debug_return_bool(false);       /* if no base is defined, ignore LDAP */
-
-    if (ldap_conf.bind_timelimit > 0)
-       ldap_conf.bind_timelimit *= 1000;       /* convert to ms */
-
-    /*
-     * Interpret SSL option
-     */
-    if (ldap_conf.ssl != NULL) {
-       if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
-           ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
-       else if (sudo_strtobool(ldap_conf.ssl) == true)
-           ldap_conf.ssl_mode = SUDO_LDAP_SSL;
-    }
-
-#if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
-    if (ldap_conf.tls_checkpeer != -1) {
-       ldapssl_set_strength(NULL,
-           ldap_conf.tls_checkpeer ? LDAPSSL_AUTH_CERT : LDAPSSL_AUTH_WEAK);
-    }
-#endif
-
-#ifndef HAVE_LDAP_INITIALIZE
-    /* Convert uri list to host list if no ldap_initialize(). */
-    if (!STAILQ_EMPTY(&ldap_conf.uri)) {
-       struct ldap_config_str *uri;
-
-       if (sudo_ldap_parse_uri(&ldap_conf.uri) != LDAP_SUCCESS)
-           debug_return_bool(false);
-       while ((uri = STAILQ_FIRST(&ldap_conf.uri)) != NULL) {
-           STAILQ_REMOVE_HEAD(&ldap_conf.uri, entries);
-           free(uri);
-       }
-       ldap_conf.port = LDAP_PORT;
-    }
-#endif
-
-    if (STAILQ_EMPTY(&ldap_conf.uri)) {
-       /* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */
-       if (ldap_conf.port < 0)
-           ldap_conf.port =
-               ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
-
-#ifdef HAVE_LDAP_CREATE
-       /*
-        * Cannot specify port directly to ldap_create(), each host must
-        * include :port to override the default.
-        */
-       if (ldap_conf.port != LDAP_PORT) {
-           if (!sudo_ldap_conf_add_ports())
-               debug_return_bool(false);
-       }
-#endif
-    }
-
-    /* If search filter is not parenthesized, make it so. */
-    if (ldap_conf.search_filter && ldap_conf.search_filter[0] != '(') {
-       size_t len = strlen(ldap_conf.search_filter);
-       cp = ldap_conf.search_filter;
-       ldap_conf.search_filter = malloc(len + 3);
-       if (ldap_conf.search_filter == NULL) {
-           sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-           debug_return_bool(false);
-       }
-       ldap_conf.search_filter[0] = '(';
-       memcpy(ldap_conf.search_filter + 1, cp, len);
-       ldap_conf.search_filter[len + 1] = ')';
-       ldap_conf.search_filter[len + 2] = '\0';
-       free(cp);
-    }
-
-
-    /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
-    if (ldap_conf.rootbinddn) {
-       sudo_ldap_read_secret(path_ldap_secret);
-    } else if (ldap_conf.bindpw) {
-       cp = sudo_ldap_decode_secret(ldap_conf.bindpw);
-       if (cp != NULL) {
-           free(ldap_conf.bindpw);
-           ldap_conf.bindpw = cp;
-       }
-    }
-
-    if (ldap_conf.tls_keypw) {
-       cp = sudo_ldap_decode_secret(ldap_conf.tls_keypw);
-       if (cp != NULL) {
-           free(ldap_conf.tls_keypw);
-           ldap_conf.tls_keypw = cp;
-       }
-    }
-
-#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
-    /*
-     * Make sure we can open the file specified by krb5_ccname.
-     */
-    if (ldap_conf.krb5_ccname != NULL) {
-       if (!sudo_check_krb5_ccname(ldap_conf.krb5_ccname))
-           ldap_conf.krb5_ccname = NULL;
-    }
-#endif
-
-    debug_return_bool(true);
-}
-
 /*
  * Extract the dn from an entry and return the first rdn from it.
  */
@@ -2646,137 +1828,6 @@ sudo_ldap_sasl_interact(LDAP *ld, unsigned int flags, void *_auth_id,
 }
 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
 
-/*
- * Set LDAP options from the specified options table
- * Returns LDAP_SUCCESS on success, else non-zero.
- */
-static int
-sudo_ldap_set_options_table(LDAP *ld, struct ldap_config_table *table)
-{
-    struct ldap_config_table *cur;
-    int ival, rc, errors = 0;
-    char *sval;
-    debug_decl(sudo_ldap_set_options_table, SUDOERS_DEBUG_LDAP)
-
-    for (cur = table; cur->conf_str != NULL; cur++) {
-       if (cur->opt_val == -1)
-           continue;
-
-       switch (cur->type) {
-       case CONF_BOOL:
-       case CONF_INT:
-           ival = *(int *)(cur->valp);
-           if (ival >= 0) {
-               DPRINTF1("ldap_set_option: %s -> %d", cur->conf_str, ival);
-               rc = ldap_set_option(ld, cur->opt_val, &ival);
-               if (rc != LDAP_OPT_SUCCESS) {
-                   sudo_warnx("ldap_set_option: %s -> %d: %s",
-                       cur->conf_str, ival, ldap_err2string(rc));
-                   errors++;
-               }
-           }
-           break;
-       case CONF_STR:
-           sval = *(char **)(cur->valp);
-           if (sval != NULL) {
-               DPRINTF1("ldap_set_option: %s -> %s", cur->conf_str, sval);
-               rc = ldap_set_option(ld, cur->opt_val, sval);
-               if (rc != LDAP_OPT_SUCCESS) {
-                   sudo_warnx("ldap_set_option: %s -> %s: %s",
-                       cur->conf_str, sval, ldap_err2string(rc));
-                   errors++;
-               }
-           }
-           break;
-       }
-    }
-    debug_return_int(errors ? -1 : LDAP_SUCCESS);
-}
-
-/*
- * Set LDAP options based on the global config table.
- * Returns LDAP_SUCCESS on success, else non-zero.
- */
-static int
-sudo_ldap_set_options_global(void)
-{
-    int ret;
-    debug_decl(sudo_ldap_set_options_global, SUDOERS_DEBUG_LDAP)
-
-    /* Set ber options */
-#ifdef LBER_OPT_DEBUG_LEVEL
-    if (ldap_conf.ldap_debug)
-       ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
-#endif
-
-    /* Parse global LDAP options table. */
-    ret = sudo_ldap_set_options_table(NULL, ldap_conf_global);
-    debug_return_int(ret);
-}
-
-/*
- * Set LDAP options based on the per-connection config table.
- * Returns LDAP_SUCCESS on success, else non-zero.
- */
-static int
-sudo_ldap_set_options_conn(LDAP *ld)
-{
-    int rc;
-    debug_decl(sudo_ldap_set_options_conn, SUDOERS_DEBUG_LDAP)
-
-    /* Parse per-connection LDAP options table. */
-    rc = sudo_ldap_set_options_table(ld, ldap_conf_conn);
-    if (rc == -1)
-       debug_return_int(-1);
-
-#ifdef LDAP_OPT_TIMEOUT
-    /* Convert timeout to a timeval */
-    if (ldap_conf.timeout > 0) {
-       struct timeval tv;
-       tv.tv_sec = ldap_conf.timeout;
-       tv.tv_usec = 0;
-       DPRINTF1("ldap_set_option(LDAP_OPT_TIMEOUT, %d)", ldap_conf.timeout);
-       rc = ldap_set_option(ld, LDAP_OPT_TIMEOUT, &tv);
-       if (rc != LDAP_OPT_SUCCESS) {
-           sudo_warnx("ldap_set_option(TIMEOUT, %d): %s",
-               ldap_conf.timeout, ldap_err2string(rc));
-       }
-    }
-#endif
-#ifdef LDAP_OPT_NETWORK_TIMEOUT
-    /* Convert bind_timelimit to a timeval */
-    if (ldap_conf.bind_timelimit > 0) {
-       struct timeval tv;
-       tv.tv_sec = ldap_conf.bind_timelimit / 1000;
-       tv.tv_usec = 0;
-       DPRINTF1("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %d)",
-           ldap_conf.bind_timelimit / 1000);
-       rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
-# if !defined(LDAP_OPT_CONNECT_TIMEOUT) || LDAP_VENDOR_VERSION != 510
-       /* Tivoli Directory Server 6.3 libs always return a (bogus) error. */
-       if (rc != LDAP_OPT_SUCCESS) {
-           sudo_warnx("ldap_set_option(NETWORK_TIMEOUT, %d): %s",
-               ldap_conf.bind_timelimit / 1000, ldap_err2string(rc));
-       }
-# endif
-    }
-#endif
-
-#if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
-    if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
-       int val = LDAP_OPT_X_TLS_HARD;
-       DPRINTF1("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD)");
-       rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
-       if (rc != LDAP_SUCCESS) {
-           sudo_warnx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
-               ldap_err2string(rc));
-           debug_return_int(-1);
-       }
-    }
-#endif
-    debug_return_int(LDAP_SUCCESS);
-}
-
 /*
  * Create a new sudo_ldap_result structure.
  */
diff --git a/plugins/sudoers/ldap_conf.c b/plugins/sudoers/ldap_conf.c
new file mode 100644 (file)
index 0000000..6fcb0c8
--- /dev/null
@@ -0,0 +1,902 @@
+/*
+ * Copyright (c) 2003-2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * This code is derived from software contributed by Aaron Spangler.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#include <unistd.h>
+#include <time.h>
+#include <ctype.h>
+#include <fcntl.h>
+#ifdef HAVE_LBER_H
+# include <lber.h>
+#endif
+#include <ldap.h>
+#if defined(HAVE_LDAP_SSL_H)
+# include <ldap_ssl.h>
+#elif defined(HAVE_MPS_LDAP_SSL_H)
+# include <mps/ldap_ssl.h>
+#endif
+
+#include "sudoers.h"
+#include "parse.h"
+#include "sudo_lbuf.h"
+#include "sudo_ldap.h"
+#include "sudo_ldap_conf.h"
+
+/* Older Netscape LDAP SDKs don't prototype ldapssl_set_strength() */
+#if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(HAVE_LDAP_SSL_H) && !defined(HAVE_MPS_LDAP_SSL_H)
+extern int ldapssl_set_strength(LDAP *ldap, int strength);
+#endif
+
+#if !defined(LDAP_OPT_NETWORK_TIMEOUT) && defined(LDAP_OPT_CONNECT_TIMEOUT)
+# define LDAP_OPT_NETWORK_TIMEOUT LDAP_OPT_CONNECT_TIMEOUT
+#endif
+
+#ifndef LDAP_OPT_SUCCESS
+# define LDAP_OPT_SUCCESS LDAP_SUCCESS
+#endif
+
+#ifndef LDAPS_PORT
+# define LDAPS_PORT 636
+#endif
+
+/* Default search filter. */
+#define DEFAULT_SEARCH_FILTER  "(objectClass=sudoRole)"
+
+/* Default netgroup search filter. */
+#define DEFAULT_NETGROUP_SEARCH_FILTER "(objectClass=nisNetgroup)"
+
+/* LDAP configuration structure */
+struct ldap_config ldap_conf;
+
+static struct ldap_config_table ldap_conf_global[] = {
+    { "sudoers_debug", CONF_INT, -1, &ldap_conf.debug },
+    { "host", CONF_STR, -1, &ldap_conf.host },
+    { "port", CONF_INT, -1, &ldap_conf.port },
+    { "ssl", CONF_STR, -1, &ldap_conf.ssl },
+    { "sslpath", CONF_STR, -1, &ldap_conf.tls_certfile },
+    { "uri", CONF_LIST_STR, -1, &ldap_conf.uri },
+#ifdef LDAP_OPT_DEBUG_LEVEL
+    { "debug", CONF_INT, LDAP_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug },
+#endif
+#ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
+    { "tls_checkpeer", CONF_BOOL, LDAP_OPT_X_TLS_REQUIRE_CERT,
+       &ldap_conf.tls_checkpeer },
+#else
+    { "tls_checkpeer", CONF_BOOL, -1, &ldap_conf.tls_checkpeer },
+#endif
+#ifdef LDAP_OPT_X_TLS_CACERTFILE
+    { "tls_cacertfile", CONF_STR, LDAP_OPT_X_TLS_CACERTFILE,
+       &ldap_conf.tls_cacertfile },
+    { "tls_cacert", CONF_STR, LDAP_OPT_X_TLS_CACERTFILE,
+       &ldap_conf.tls_cacertfile },
+#endif
+#ifdef LDAP_OPT_X_TLS_CACERTDIR
+    { "tls_cacertdir", CONF_STR, LDAP_OPT_X_TLS_CACERTDIR,
+       &ldap_conf.tls_cacertdir },
+#endif
+#ifdef LDAP_OPT_X_TLS_RANDOM_FILE
+    { "tls_randfile", CONF_STR, LDAP_OPT_X_TLS_RANDOM_FILE,
+       &ldap_conf.tls_random_file },
+#endif
+#ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
+    { "tls_ciphers", CONF_STR, LDAP_OPT_X_TLS_CIPHER_SUITE,
+       &ldap_conf.tls_cipher_suite },
+#elif defined(LDAP_OPT_SSL_CIPHER)
+    { "tls_ciphers", CONF_STR, LDAP_OPT_SSL_CIPHER,
+       &ldap_conf.tls_cipher_suite },
+#endif
+#ifdef LDAP_OPT_X_TLS_CERTFILE
+    { "tls_cert", CONF_STR, LDAP_OPT_X_TLS_CERTFILE,
+       &ldap_conf.tls_certfile },
+#else
+    { "tls_cert", CONF_STR, -1, &ldap_conf.tls_certfile },
+#endif
+#ifdef LDAP_OPT_X_TLS_KEYFILE
+    { "tls_key", CONF_STR, LDAP_OPT_X_TLS_KEYFILE,
+       &ldap_conf.tls_keyfile },
+#else
+    { "tls_key", CONF_STR, -1, &ldap_conf.tls_keyfile },
+#endif
+#ifdef HAVE_LDAP_SSL_CLIENT_INIT
+    { "tls_keypw", CONF_STR, -1, &ldap_conf.tls_keypw },
+#endif
+    { "binddn", CONF_STR, -1, &ldap_conf.binddn },
+    { "bindpw", CONF_STR, -1, &ldap_conf.bindpw },
+    { "rootbinddn", CONF_STR, -1, &ldap_conf.rootbinddn },
+    { "sudoers_base", CONF_LIST_STR, -1, &ldap_conf.base },
+    { "sudoers_timed", CONF_BOOL, -1, &ldap_conf.timed },
+    { "sudoers_search_filter", CONF_STR, -1, &ldap_conf.search_filter },
+    { "netgroup_base", CONF_LIST_STR, -1, &ldap_conf.netgroup_base },
+    { "netgroup_search_filter", CONF_STR, -1, &ldap_conf.netgroup_search_filter },
+#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
+    { "use_sasl", CONF_BOOL, -1, &ldap_conf.use_sasl },
+    { "sasl_mech", CONF_STR, -1, &ldap_conf.sasl_mech },
+    { "sasl_auth_id", CONF_STR, -1, &ldap_conf.sasl_auth_id },
+    { "rootuse_sasl", CONF_BOOL, -1, &ldap_conf.rootuse_sasl },
+    { "rootsasl_auth_id", CONF_STR, -1, &ldap_conf.rootsasl_auth_id },
+    { "krb5_ccname", CONF_STR, -1, &ldap_conf.krb5_ccname },
+#endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
+    { NULL }
+};
+
+static struct ldap_config_table ldap_conf_conn[] = {
+#ifdef LDAP_OPT_PROTOCOL_VERSION
+    { "ldap_version", CONF_INT, LDAP_OPT_PROTOCOL_VERSION,
+       &ldap_conf.version },
+#endif
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+    { "bind_timelimit", CONF_INT, -1 /* needs timeval, set manually */,
+       &ldap_conf.bind_timelimit },
+    { "network_timeout", CONF_INT, -1 /* needs timeval, set manually */,
+       &ldap_conf.bind_timelimit },
+#elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
+    { "bind_timelimit", CONF_INT, LDAP_X_OPT_CONNECT_TIMEOUT,
+       &ldap_conf.bind_timelimit },
+    { "network_timeout", CONF_INT, LDAP_X_OPT_CONNECT_TIMEOUT,
+       &ldap_conf.bind_timelimit },
+#endif
+    { "timelimit", CONF_INT, LDAP_OPT_TIMELIMIT, &ldap_conf.timelimit },
+#ifdef LDAP_OPT_TIMEOUT
+    { "timeout", CONF_INT, -1 /* needs timeval, set manually */,
+       &ldap_conf.timeout },
+#endif
+#ifdef LDAP_OPT_DEREF
+    { "deref", CONF_DEREF_VAL, LDAP_OPT_DEREF, &ldap_conf.deref },
+#endif
+#ifdef LDAP_OPT_X_SASL_SECPROPS
+    { "sasl_secprops", CONF_STR, LDAP_OPT_X_SASL_SECPROPS,
+       &ldap_conf.sasl_secprops },
+#endif
+    { NULL }
+};
+
+#ifdef HAVE_LDAP_CREATE
+/*
+ * Rebuild the hosts list and include a specific port for each host.
+ * ldap_create() does not take a default port parameter so we must
+ * append one if we want something other than LDAP_PORT.
+ */
+static bool
+sudo_ldap_conf_add_ports(void)
+{
+    char *host, *last, *port, defport[13];
+    char hostbuf[LINE_MAX * 2];
+    int len;
+    debug_decl(sudo_ldap_conf_add_ports, SUDOERS_DEBUG_LDAP)
+
+    hostbuf[0] = '\0';
+    len = snprintf(defport, sizeof(defport), ":%d", ldap_conf.port);
+    if (len <= 0 || (size_t)len >= sizeof(defport)) {
+       sudo_warnx(U_("sudo_ldap_conf_add_ports: port too large"));
+       debug_return_bool(false);
+    }
+
+    for ((host = strtok_r(ldap_conf.host, " \t", &last)); host; (host = strtok_r(NULL, " \t", &last))) {
+       if (hostbuf[0] != '\0')
+           CHECK_STRLCAT(hostbuf, " ", sizeof(hostbuf));
+       CHECK_STRLCAT(hostbuf, host, sizeof(hostbuf));
+
+       /* Append port if there is not one already. */
+       if ((port = strrchr(host, ':')) == NULL ||
+           !isdigit((unsigned char)port[1])) {
+           CHECK_STRLCAT(hostbuf, defport, sizeof(hostbuf));
+       }
+    }
+
+    free(ldap_conf.host);
+    if ((ldap_conf.host = strdup(hostbuf)) == NULL)
+       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+    debug_return_bool(ldap_conf.host != NULL);
+
+overflow:
+    sudo_warnx(U_("internal error, %s overflow"), __func__);
+    debug_return_bool(false);
+}
+#endif
+
+#ifndef HAVE_LDAP_INITIALIZE
+/*
+ * For each uri, convert to host:port pairs.  For ldaps:// enable SSL
+ * Accepts: uris of the form ldap:/// or ldap://hostname:portnum/
+ * where the trailing slash is optional.
+ * Returns LDAP_SUCCESS on success, else non-zero.
+ */
+static int
+sudo_ldap_parse_uri(const struct ldap_config_str_list *uri_list)
+{
+    const struct ldap_config_str *entry;
+    char *buf, hostbuf[LINE_MAX];
+    int nldap = 0, nldaps = 0;
+    int ret = -1;
+    debug_decl(sudo_ldap_parse_uri, SUDOERS_DEBUG_LDAP)
+
+    hostbuf[0] = '\0';
+    STAILQ_FOREACH(entry, uri_list, entries) {
+       char *cp, *host, *last, *port, *uri;
+
+       buf = strdup(entry->val);
+       if (buf == NULL) {
+           sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+           goto done;
+       }
+       for ((uri = strtok_r(buf, " \t", &last)); uri != NULL; (uri = strtok_r(NULL, " \t", &last))) {
+           if (strncasecmp(uri, "ldap://", 7) == 0) {
+               nldap++;
+               host = uri + 7;
+           } else if (strncasecmp(uri, "ldaps://", 8) == 0) {
+               nldaps++;
+               host = uri + 8;
+           } else {
+               sudo_warnx(U_("unsupported LDAP uri type: %s"), uri);
+               goto done;
+           }
+
+           /* trim optional trailing slash */
+           if ((cp = strrchr(host, '/')) != NULL && cp[1] == '\0') {
+               *cp = '\0';
+           }
+
+           if (hostbuf[0] != '\0')
+               CHECK_STRLCAT(hostbuf, " ", sizeof(hostbuf));
+
+           if (*host == '\0')
+               host = "localhost";             /* no host specified, use localhost */
+
+           CHECK_STRLCAT(hostbuf, host, sizeof(hostbuf));
+
+           /* If using SSL and no port specified, add port 636 */
+           if (nldaps) {
+               if ((port = strrchr(host, ':')) == NULL ||
+                   !isdigit((unsigned char)port[1]))
+                   CHECK_STRLCAT(hostbuf, ":636", sizeof(hostbuf));
+           }
+       }
+
+       if (nldaps != 0) {
+           if (nldap != 0) {
+               sudo_warnx(U_("unable to mix ldap and ldaps URIs"));
+               goto done;
+           }
+           if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS)
+               sudo_warnx(U_("starttls not supported when using ldaps"));
+           ldap_conf.ssl_mode = SUDO_LDAP_SSL;
+       }
+       free(buf);
+    }
+    buf = NULL;
+
+    /* Store parsed URI(s) in host for ldap_create() or ldap_init(). */
+    free(ldap_conf.host);
+    if ((ldap_conf.host = strdup(hostbuf)) == NULL) {
+       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+       goto done;
+    }
+
+    ret = LDAP_SUCCESS;
+
+done:
+    free(buf);
+    debug_return_int(ret);
+
+overflow:
+    sudo_warnx(U_("internal error, %s overflow"), __func__);
+    free(buf);
+    debug_return_int(-1);
+}
+#endif /* HAVE_LDAP_INITIALIZE */
+
+/*
+ * Decode a secret if it is base64 encoded, else return NULL.
+ */
+static char *
+sudo_ldap_decode_secret(const char *secret)
+{
+    unsigned char *result = NULL;
+    size_t len, reslen;
+    debug_decl(sudo_ldap_decode_secret, SUDOERS_DEBUG_LDAP)
+
+    if (strncasecmp(secret, "base64:", sizeof("base64:") - 1) == 0) {
+       /*
+        * Decode a base64 secret.  The decoded length is 3/4 the encoded
+        * length but padding may be missing so round up to a multiple of 4.
+        */
+       secret += sizeof("base64:") - 1;
+       reslen = ((strlen(secret) + 3) / 4 * 3);
+       result = malloc(reslen + 1);
+       if (result == NULL) {
+           sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+       } else {
+           len = base64_decode(secret, result, reslen);
+           if (len == (size_t)-1) {
+               free(result);
+               result = NULL;
+           } else {
+               result[len] = '\0';
+           }
+       }
+    }
+    debug_return_str((char *)result);
+}
+
+static void
+sudo_ldap_read_secret(const char *path)
+{
+    FILE *fp;
+    char *line = NULL;
+    size_t linesize = 0;
+    ssize_t len;
+    debug_decl(sudo_ldap_read_secret, SUDOERS_DEBUG_LDAP)
+
+    if ((fp = fopen(path_ldap_secret, "r")) != NULL) {
+       len = getline(&line, &linesize, fp);
+       if (len != -1) {
+           /* trim newline */
+           while (len > 0 && line[len - 1] == '\n')
+               line[--len] = '\0';
+           /* copy to bindpw and binddn */
+           free(ldap_conf.bindpw);
+           ldap_conf.bindpw = sudo_ldap_decode_secret(line);
+           if (ldap_conf.bindpw == NULL) {
+               /* not base64 encoded, use directly */
+               ldap_conf.bindpw = line;
+               line = NULL;
+           }
+           free(ldap_conf.binddn);
+           ldap_conf.binddn = ldap_conf.rootbinddn;
+           ldap_conf.rootbinddn = NULL;
+       }
+       fclose(fp);
+       free(line);
+    }
+    debug_return;
+}
+
+/*
+ * Look up keyword in config tables.
+ * Returns true if found, else false.
+ */
+static bool
+sudo_ldap_parse_keyword(const char *keyword, const char *value,
+    struct ldap_config_table *table)
+{
+    struct ldap_config_table *cur;
+    const char *errstr;
+    debug_decl(sudo_ldap_parse_keyword, SUDOERS_DEBUG_LDAP)
+
+    /* Look up keyword in config tables */
+    for (cur = table; cur->conf_str != NULL; cur++) {
+       if (strcasecmp(keyword, cur->conf_str) == 0) {
+           switch (cur->type) {
+           case CONF_DEREF_VAL:
+               if (strcasecmp(value, "searching") == 0)
+                   *(int *)(cur->valp) = LDAP_DEREF_SEARCHING;
+               else if (strcasecmp(value, "finding") == 0)
+                   *(int *)(cur->valp) = LDAP_DEREF_FINDING;
+               else if (strcasecmp(value, "always") == 0)
+                   *(int *)(cur->valp) = LDAP_DEREF_ALWAYS;
+               else
+                   *(int *)(cur->valp) = LDAP_DEREF_NEVER;
+               break;
+           case CONF_BOOL:
+               *(int *)(cur->valp) = sudo_strtobool(value) == true;
+               break;
+           case CONF_INT:
+               *(int *)(cur->valp) = strtonum(value, INT_MIN, INT_MAX, &errstr);
+               if (errstr != NULL) {
+                   sudo_warnx(U_("%s: %s: %s: %s"),
+                       path_ldap_conf, keyword, value, U_(errstr));
+               }
+               break;
+           case CONF_STR:
+               {
+                   char *cp = NULL;
+
+                   free(*(char **)(cur->valp));
+                   if (*value && (cp = strdup(value)) == NULL) {
+                       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+                       debug_return_bool(false);
+                   }
+                   *(char **)(cur->valp) = cp;
+                   break;
+               }
+           case CONF_LIST_STR:
+               {
+                   struct ldap_config_str_list *head;
+                   struct ldap_config_str *str;
+                   size_t len = strlen(value);
+
+                   if (len > 0) {
+                       head = (struct ldap_config_str_list *)cur->valp;
+                       if ((str = malloc(sizeof(*str) + len)) == NULL) {
+                           sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+                           debug_return_bool(false);
+                       }
+                       memcpy(str->val, value, len + 1);
+                       STAILQ_INSERT_TAIL(head, str, entries);
+                   }
+               }
+               break;
+           }
+           debug_return_bool(true);
+       }
+    }
+    debug_return_bool(false);
+}
+
+#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
+const char *
+sudo_krb5_ccname_path(const char *old_ccname)
+{
+    const char *ccname = old_ccname;
+    debug_decl(sudo_krb5_ccname_path, SUDOERS_DEBUG_LDAP)
+
+    /* Strip off leading FILE: or WRFILE: prefix. */
+    switch (ccname[0]) {
+       case 'F':
+       case 'f':
+           if (strncasecmp(ccname, "FILE:", 5) == 0)
+               ccname += 5;
+           break;
+       case 'W':
+       case 'w':
+           if (strncasecmp(ccname, "WRFILE:", 7) == 0)
+               ccname += 7;
+           break;
+    }
+    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+       "ccache %s -> %s", old_ccname, ccname);
+
+    /* Credential cache must be a fully-qualified path name. */
+    debug_return_const_str(*ccname == '/' ? ccname : NULL);
+}
+
+static bool
+sudo_check_krb5_ccname(const char *ccname)
+{
+    int fd = -1;
+    const char *ccname_path;
+    debug_decl(sudo_check_krb5_ccname, SUDOERS_DEBUG_LDAP)
+
+    /* Strip off prefix to get path name. */
+    ccname_path = sudo_krb5_ccname_path(ccname);
+    if (ccname_path == NULL) {
+       sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
+           "unsupported krb5 credential cache path: %s", ccname);
+       debug_return_bool(false);
+    }
+    /* Make sure credential cache is fully-qualified and exists. */
+    fd = open(ccname_path, O_RDONLY|O_NONBLOCK, 0);
+    if (fd == -1) {
+       sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
+           "unable to open krb5 credential cache: %s", ccname_path);
+       debug_return_bool(false);
+    }
+    close(fd);
+    sudo_debug_printf(SUDO_DEBUG_INFO,
+       "using krb5 credential cache: %s", ccname_path);
+    debug_return_bool(true);
+}
+#endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
+
+bool
+sudo_ldap_read_config(void)
+{
+    char *cp, *keyword, *value, *line = NULL;
+    struct ldap_config_str *conf_str;
+    size_t linesize = 0;
+    FILE *fp;
+    debug_decl(sudo_ldap_read_config, SUDOERS_DEBUG_LDAP)
+
+    /* defaults */
+    ldap_conf.version = 3;
+    ldap_conf.port = -1;
+    ldap_conf.tls_checkpeer = -1;
+    ldap_conf.timelimit = -1;
+    ldap_conf.timeout = -1;
+    ldap_conf.bind_timelimit = -1;
+    ldap_conf.use_sasl = -1;
+    ldap_conf.rootuse_sasl = -1;
+    ldap_conf.deref = -1;
+    ldap_conf.search_filter = strdup(DEFAULT_SEARCH_FILTER);
+    ldap_conf.netgroup_search_filter = strdup(DEFAULT_NETGROUP_SEARCH_FILTER);
+    STAILQ_INIT(&ldap_conf.uri);
+    STAILQ_INIT(&ldap_conf.base);
+    STAILQ_INIT(&ldap_conf.netgroup_base);
+
+    if (ldap_conf.search_filter == NULL || ldap_conf.netgroup_search_filter == NULL) {
+       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+       debug_return_bool(false);
+    }
+
+    if ((fp = fopen(path_ldap_conf, "r")) == NULL)
+       debug_return_bool(false);
+
+    while (sudo_parseln(&line, &linesize, NULL, fp, PARSELN_COMM_BOL|PARSELN_CONT_IGN) != -1) {
+       if (*line == '\0')
+           continue;           /* skip empty line */
+
+       /* split into keyword and value */
+       keyword = cp = line;
+       while (*cp && !isblank((unsigned char) *cp))
+           cp++;
+       if (*cp)
+           *cp++ = '\0';       /* terminate keyword */
+
+       /* skip whitespace before value */
+       while (isblank((unsigned char) *cp))
+           cp++;
+       value = cp;
+
+       /* Look up keyword in config tables */
+       if (!sudo_ldap_parse_keyword(keyword, value, ldap_conf_global))
+           sudo_ldap_parse_keyword(keyword, value, ldap_conf_conn);
+    }
+    free(line);
+    fclose(fp);
+
+    if (!ldap_conf.host) {
+       ldap_conf.host = strdup("localhost");
+       if (ldap_conf.host == NULL) {
+           sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+           debug_return_bool(false);
+       }
+    }
+
+    DPRINTF1("LDAP Config Summary");
+    DPRINTF1("===================");
+    if (!STAILQ_EMPTY(&ldap_conf.uri)) {
+       STAILQ_FOREACH(conf_str, &ldap_conf.uri, entries) {
+           DPRINTF1("uri              %s", conf_str->val);
+       }
+    } else {
+       DPRINTF1("host             %s",
+           ldap_conf.host ? ldap_conf.host : "(NONE)");
+       DPRINTF1("port             %d", ldap_conf.port);
+    }
+    DPRINTF1("ldap_version     %d", ldap_conf.version);
+
+    if (!STAILQ_EMPTY(&ldap_conf.base)) {
+       STAILQ_FOREACH(conf_str, &ldap_conf.base, entries) {
+           DPRINTF1("sudoers_base     %s", conf_str->val);
+       }
+    } else {
+       DPRINTF1("sudoers_base     %s", "(NONE: LDAP disabled)");
+    }
+    if (ldap_conf.search_filter) {
+       DPRINTF1("search_filter    %s", ldap_conf.search_filter);
+    }
+    if (!STAILQ_EMPTY(&ldap_conf.netgroup_base)) {
+       STAILQ_FOREACH(conf_str, &ldap_conf.netgroup_base, entries) {
+           DPRINTF1("netgroup_base    %s", conf_str->val);
+       }
+    } else {
+       DPRINTF1("netgroup_base %s", "(NONE: will use nsswitch)");
+    }
+    if (ldap_conf.netgroup_search_filter) {
+        DPRINTF1("netgroup_search_filter %s", ldap_conf.netgroup_search_filter);
+    }
+    DPRINTF1("binddn           %s",
+       ldap_conf.binddn ? ldap_conf.binddn : "(anonymous)");
+    DPRINTF1("bindpw           %s",
+       ldap_conf.bindpw ? ldap_conf.bindpw : "(anonymous)");
+    if (ldap_conf.bind_timelimit > 0) {
+       DPRINTF1("bind_timelimit   %d", ldap_conf.bind_timelimit);
+    }
+    if (ldap_conf.timelimit > 0) {
+       DPRINTF1("timelimit        %d", ldap_conf.timelimit);
+    }
+    if (ldap_conf.deref != -1) {
+       DPRINTF1("deref            %d", ldap_conf.deref);
+    }
+    DPRINTF1("ssl              %s", ldap_conf.ssl ? ldap_conf.ssl : "(no)");
+    if (ldap_conf.tls_checkpeer != -1) {
+       DPRINTF1("tls_checkpeer    %s",
+           ldap_conf.tls_checkpeer ? "(yes)" : "(no)");
+    }
+    if (ldap_conf.tls_cacertfile != NULL) {
+       DPRINTF1("tls_cacertfile   %s", ldap_conf.tls_cacertfile);
+    }
+    if (ldap_conf.tls_cacertdir != NULL) {
+       DPRINTF1("tls_cacertdir    %s", ldap_conf.tls_cacertdir);
+    }
+    if (ldap_conf.tls_random_file != NULL) {
+       DPRINTF1("tls_random_file  %s", ldap_conf.tls_random_file);
+    }
+    if (ldap_conf.tls_cipher_suite != NULL) {
+       DPRINTF1("tls_cipher_suite %s", ldap_conf.tls_cipher_suite);
+    }
+    if (ldap_conf.tls_certfile != NULL) {
+       DPRINTF1("tls_certfile     %s", ldap_conf.tls_certfile);
+    }
+    if (ldap_conf.tls_keyfile != NULL) {
+       DPRINTF1("tls_keyfile      %s", ldap_conf.tls_keyfile);
+    }
+#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
+    if (ldap_conf.use_sasl != -1) {
+       if (ldap_conf.sasl_mech == NULL) {
+           /* Default mechanism is GSSAPI. */
+           ldap_conf.sasl_mech = strdup("GSSAPI");
+           if (ldap_conf.sasl_mech == NULL) {
+               sudo_warnx(U_("%s: %s"), __func__,
+                   U_("unable to allocate memory"));
+               debug_return_bool(false);
+           }
+       }
+       DPRINTF1("use_sasl         %s", ldap_conf.use_sasl ? "yes" : "no");
+       DPRINTF1("sasl_mech        %s", ldap_conf.sasl_mech);
+       DPRINTF1("sasl_auth_id     %s",
+           ldap_conf.sasl_auth_id ? ldap_conf.sasl_auth_id : "(NONE)");
+       DPRINTF1("rootuse_sasl     %d",
+           ldap_conf.rootuse_sasl);
+       DPRINTF1("rootsasl_auth_id %s",
+           ldap_conf.rootsasl_auth_id ? ldap_conf.rootsasl_auth_id : "(NONE)");
+       DPRINTF1("sasl_secprops    %s",
+           ldap_conf.sasl_secprops ? ldap_conf.sasl_secprops : "(NONE)");
+       DPRINTF1("krb5_ccname      %s",
+           ldap_conf.krb5_ccname ? ldap_conf.krb5_ccname : "(NONE)");
+    }
+#endif
+    DPRINTF1("===================");
+
+    if (STAILQ_EMPTY(&ldap_conf.base))
+       debug_return_bool(false);       /* if no base is defined, ignore LDAP */
+
+    if (ldap_conf.bind_timelimit > 0)
+       ldap_conf.bind_timelimit *= 1000;       /* convert to ms */
+
+    /*
+     * Interpret SSL option
+     */
+    if (ldap_conf.ssl != NULL) {
+       if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
+           ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
+       else if (sudo_strtobool(ldap_conf.ssl) == true)
+           ldap_conf.ssl_mode = SUDO_LDAP_SSL;
+    }
+
+#if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
+    if (ldap_conf.tls_checkpeer != -1) {
+       ldapssl_set_strength(NULL,
+           ldap_conf.tls_checkpeer ? LDAPSSL_AUTH_CERT : LDAPSSL_AUTH_WEAK);
+    }
+#endif
+
+#ifndef HAVE_LDAP_INITIALIZE
+    /* Convert uri list to host list if no ldap_initialize(). */
+    if (!STAILQ_EMPTY(&ldap_conf.uri)) {
+       struct ldap_config_str *uri;
+
+       if (sudo_ldap_parse_uri(&ldap_conf.uri) != LDAP_SUCCESS)
+           debug_return_bool(false);
+       while ((uri = STAILQ_FIRST(&ldap_conf.uri)) != NULL) {
+           STAILQ_REMOVE_HEAD(&ldap_conf.uri, entries);
+           free(uri);
+       }
+       ldap_conf.port = LDAP_PORT;
+    }
+#endif
+
+    if (STAILQ_EMPTY(&ldap_conf.uri)) {
+       /* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */
+       if (ldap_conf.port < 0)
+           ldap_conf.port =
+               ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
+
+#ifdef HAVE_LDAP_CREATE
+       /*
+        * Cannot specify port directly to ldap_create(), each host must
+        * include :port to override the default.
+        */
+       if (ldap_conf.port != LDAP_PORT) {
+           if (!sudo_ldap_conf_add_ports())
+               debug_return_bool(false);
+       }
+#endif
+    }
+
+    /* If search filter is not parenthesized, make it so. */
+    if (ldap_conf.search_filter && ldap_conf.search_filter[0] != '(') {
+       size_t len = strlen(ldap_conf.search_filter);
+       cp = ldap_conf.search_filter;
+       ldap_conf.search_filter = malloc(len + 3);
+       if (ldap_conf.search_filter == NULL) {
+           sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+           debug_return_bool(false);
+       }
+       ldap_conf.search_filter[0] = '(';
+       memcpy(ldap_conf.search_filter + 1, cp, len);
+       ldap_conf.search_filter[len + 1] = ')';
+       ldap_conf.search_filter[len + 2] = '\0';
+       free(cp);
+    }
+
+
+    /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
+    if (ldap_conf.rootbinddn) {
+       sudo_ldap_read_secret(path_ldap_secret);
+    } else if (ldap_conf.bindpw) {
+       cp = sudo_ldap_decode_secret(ldap_conf.bindpw);
+       if (cp != NULL) {
+           free(ldap_conf.bindpw);
+           ldap_conf.bindpw = cp;
+       }
+    }
+
+    if (ldap_conf.tls_keypw) {
+       cp = sudo_ldap_decode_secret(ldap_conf.tls_keypw);
+       if (cp != NULL) {
+           free(ldap_conf.tls_keypw);
+           ldap_conf.tls_keypw = cp;
+       }
+    }
+
+#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
+    /*
+     * Make sure we can open the file specified by krb5_ccname.
+     */
+    if (ldap_conf.krb5_ccname != NULL) {
+       if (!sudo_check_krb5_ccname(ldap_conf.krb5_ccname))
+           ldap_conf.krb5_ccname = NULL;
+    }
+#endif
+
+    debug_return_bool(true);
+}
+
+/*
+ * Set LDAP options from the specified options table
+ * Returns LDAP_SUCCESS on success, else non-zero.
+ */
+static int
+sudo_ldap_set_options_table(LDAP *ld, struct ldap_config_table *table)
+{
+    struct ldap_config_table *cur;
+    int ival, rc, errors = 0;
+    char *sval;
+    debug_decl(sudo_ldap_set_options_table, SUDOERS_DEBUG_LDAP)
+
+    for (cur = table; cur->conf_str != NULL; cur++) {
+       if (cur->opt_val == -1)
+           continue;
+
+       switch (cur->type) {
+       case CONF_BOOL:
+       case CONF_INT:
+           ival = *(int *)(cur->valp);
+           if (ival >= 0) {
+               DPRINTF1("ldap_set_option: %s -> %d", cur->conf_str, ival);
+               rc = ldap_set_option(ld, cur->opt_val, &ival);
+               if (rc != LDAP_OPT_SUCCESS) {
+                   sudo_warnx("ldap_set_option: %s -> %d: %s",
+                       cur->conf_str, ival, ldap_err2string(rc));
+                   errors++;
+               }
+           }
+           break;
+       case CONF_STR:
+           sval = *(char **)(cur->valp);
+           if (sval != NULL) {
+               DPRINTF1("ldap_set_option: %s -> %s", cur->conf_str, sval);
+               rc = ldap_set_option(ld, cur->opt_val, sval);
+               if (rc != LDAP_OPT_SUCCESS) {
+                   sudo_warnx("ldap_set_option: %s -> %s: %s",
+                       cur->conf_str, sval, ldap_err2string(rc));
+                   errors++;
+               }
+           }
+           break;
+       }
+    }
+    debug_return_int(errors ? -1 : LDAP_SUCCESS);
+}
+
+/*
+ * Set LDAP options based on the global config table.
+ * Returns LDAP_SUCCESS on success, else non-zero.
+ */
+int
+sudo_ldap_set_options_global(void)
+{
+    int ret;
+    debug_decl(sudo_ldap_set_options_global, SUDOERS_DEBUG_LDAP)
+
+    /* Set ber options */
+#ifdef LBER_OPT_DEBUG_LEVEL
+    if (ldap_conf.ldap_debug)
+       ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
+#endif
+
+    /* Parse global LDAP options table. */
+    ret = sudo_ldap_set_options_table(NULL, ldap_conf_global);
+    debug_return_int(ret);
+}
+
+/*
+ * Set LDAP options based on the per-connection config table.
+ * Returns LDAP_SUCCESS on success, else non-zero.
+ */
+int
+sudo_ldap_set_options_conn(LDAP *ld)
+{
+    int rc;
+    debug_decl(sudo_ldap_set_options_conn, SUDOERS_DEBUG_LDAP)
+
+    /* Parse per-connection LDAP options table. */
+    rc = sudo_ldap_set_options_table(ld, ldap_conf_conn);
+    if (rc == -1)
+       debug_return_int(-1);
+
+#ifdef LDAP_OPT_TIMEOUT
+    /* Convert timeout to a timeval */
+    if (ldap_conf.timeout > 0) {
+       struct timeval tv;
+       tv.tv_sec = ldap_conf.timeout;
+       tv.tv_usec = 0;
+       DPRINTF1("ldap_set_option(LDAP_OPT_TIMEOUT, %d)", ldap_conf.timeout);
+       rc = ldap_set_option(ld, LDAP_OPT_TIMEOUT, &tv);
+       if (rc != LDAP_OPT_SUCCESS) {
+           sudo_warnx("ldap_set_option(TIMEOUT, %d): %s",
+               ldap_conf.timeout, ldap_err2string(rc));
+       }
+    }
+#endif
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+    /* Convert bind_timelimit to a timeval */
+    if (ldap_conf.bind_timelimit > 0) {
+       struct timeval tv;
+       tv.tv_sec = ldap_conf.bind_timelimit / 1000;
+       tv.tv_usec = 0;
+       DPRINTF1("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %d)",
+           ldap_conf.bind_timelimit / 1000);
+       rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
+# if !defined(LDAP_OPT_CONNECT_TIMEOUT) || LDAP_VENDOR_VERSION != 510
+       /* Tivoli Directory Server 6.3 libs always return a (bogus) error. */
+       if (rc != LDAP_OPT_SUCCESS) {
+           sudo_warnx("ldap_set_option(NETWORK_TIMEOUT, %d): %s",
+               ldap_conf.bind_timelimit / 1000, ldap_err2string(rc));
+       }
+# endif
+    }
+#endif
+
+#if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
+    if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
+       int val = LDAP_OPT_X_TLS_HARD;
+       DPRINTF1("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD)");
+       rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
+       if (rc != LDAP_SUCCESS) {
+           sudo_warnx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
+               ldap_err2string(rc));
+           debug_return_int(-1);
+       }
+    }
+#endif
+    debug_return_int(LDAP_SUCCESS);
+}
index 10286b8379eb1948d61a7c87366731386da65254..7880725906e508e8dbdaf193f119a8d4ac29407c 100644 (file)
 #ifndef SUDOERS_LDAP_H
 #define SUDOERS_LDAP_H
 
+/* Iterators used by sudo_ldap_role_to_priv() to handle bervar ** or char ** */
 typedef char * (*sudo_ldap_iter_t)(void **);
 
+/* ldap_common.c */
 bool sudo_ldap_is_negated(char **valp);
 int sudo_ldap_parse_option(char *optstr, char **varp, char **valp);
 struct privilege *sudo_ldap_role_to_priv(const char *cn, void *runasusers, void *runasgroups, void *cmnds, void *opts, const char *notbefore, const char *notafter, sudo_ldap_iter_t iter);
diff --git a/plugins/sudoers/sudo_ldap_conf.h b/plugins/sudoers/sudo_ldap_conf.h
new file mode 100644 (file)
index 0000000..49c7f1e
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef SUDOERS_LDAP_CONF_H
+#define SUDOERS_LDAP_CONF_H
+
+/* Macros for checking strlcpy/strlcat/sudo_ldap_value_cat return value. */
+#define CHECK_STRLCPY(d, s, l) do {                                           \
+       if (strlcpy((d), (s), (l)) >= (l))                                     \
+           goto overflow;                                                     \
+} while (0)
+#define CHECK_STRLCAT(d, s, l) do {                                           \
+       if (strlcat((d), (s), (l)) >= (l))                                     \
+           goto overflow;                                                     \
+} while (0)
+#define CHECK_LDAP_VCAT(d, s, l) do {                                         \
+       if (sudo_ldap_value_cat((d), (s), (l)) >= (l))                         \
+           goto overflow;                                                     \
+} while (0)
+
+#if defined(__GNUC__) && __GNUC__ == 2
+# define DPRINTF1(fmt...) do {                                         \
+    sudo_debug_printf(SUDO_DEBUG_DIAG, fmt);                           \
+    if (ldap_conf.debug >= 1)                                          \
+       sudo_warnx_nodebug(fmt);                                        \
+} while (0)
+# define DPRINTF2(fmt...) do {                                         \
+    sudo_debug_printf(SUDO_DEBUG_INFO, fmt);                           \
+    if (ldap_conf.debug >= 2)                                          \
+       sudo_warnx_nodebug(fmt);                                        \
+} while (0)
+#else
+# define DPRINTF1(...) do {                                            \
+    sudo_debug_printf(SUDO_DEBUG_DIAG, __VA_ARGS__);                   \
+    if (ldap_conf.debug >= 1)                                          \
+       sudo_warnx_nodebug(__VA_ARGS__);                                \
+} while (0)
+# define DPRINTF2(...) do {                                            \
+    sudo_debug_printf(SUDO_DEBUG_INFO, __VA_ARGS__);                   \
+    if (ldap_conf.debug >= 2)                                          \
+       sudo_warnx_nodebug(__VA_ARGS__);                                \
+} while (0)
+#endif
+
+#define CONF_BOOL      0
+#define CONF_INT       1
+#define CONF_STR       2
+#define CONF_LIST_STR  4
+#define CONF_DEREF_VAL 5
+
+#define SUDO_LDAP_CLEAR                0
+#define SUDO_LDAP_SSL          1
+#define SUDO_LDAP_STARTTLS     2
+
+struct ldap_config_table {
+    const char *conf_str;      /* config file string */
+    int type;                  /* CONF_BOOL, CONF_INT, CONF_STR */
+    int opt_val;               /* LDAP_OPT_* (or -1 for sudo internal) */
+    void *valp;                        /* pointer into ldap_conf */
+};
+
+struct ldap_config_str {
+    STAILQ_ENTRY(ldap_config_str) entries;
+    char val[1];
+};
+STAILQ_HEAD(ldap_config_str_list, ldap_config_str);
+
+/* LDAP configuration structure */
+struct ldap_config {
+    int port;
+    int version;
+    int debug;
+    int ldap_debug;
+    int tls_checkpeer;
+    int timelimit;
+    int timeout;
+    int bind_timelimit;
+    int use_sasl;
+    int rootuse_sasl;
+    int ssl_mode;
+    int timed;
+    int deref;
+    char *host;
+    struct ldap_config_str_list uri;
+    char *binddn;
+    char *bindpw;
+    char *rootbinddn;
+    struct ldap_config_str_list base;
+    struct ldap_config_str_list netgroup_base;
+    char *search_filter;
+    char *netgroup_search_filter;
+    char *ssl;
+    char *tls_cacertfile;
+    char *tls_cacertdir;
+    char *tls_random_file;
+    char *tls_cipher_suite;
+    char *tls_certfile;
+    char *tls_keyfile;
+    char *tls_keypw;
+    char *sasl_mech;
+    char *sasl_auth_id;
+    char *rootsasl_auth_id;
+    char *sasl_secprops;
+    char *krb5_ccname;
+};
+
+extern struct ldap_config ldap_conf;
+
+const char *sudo_krb5_ccname_path(const char *old_ccname);
+bool sudo_ldap_read_config(void);
+int sudo_ldap_set_options_global(void);
+int sudo_ldap_set_options_conn(LDAP *ld);
+
+#endif /* SUDOERS_LDAP_CONF_H */