From: Todd C. Miller Date: Sun, 15 Jul 2007 13:23:20 +0000 (+0000) Subject: Add support for SASL auth when connecting to an LDAP server. X-Git-Tag: SUDO_1_7_0~488 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5fdb0649b08069e2ed9f4da81a2394eeedd1919a;p=sudo Add support for SASL auth when connecting to an LDAP server. Adapted from a diff by Tom McLaughlin. --- diff --git a/README.LDAP b/README.LDAP index 030c1f9fe..ba95f5436 100644 --- a/README.LDAP +++ b/README.LDAP @@ -254,6 +254,12 @@ when you imported the sudoers. Below is an example /etc/ldap.conf #tls_cert /etc/certs/client_cert.pem #tls_key /etc/certs/client_key.pem # + # If using SASL authentication for LDAP + # use_sasl yes + # sasl_authid + # rootuse_sasl yes + # rootsasl_authid + # Debugging your LDAP configuration ================================= diff --git a/config.h.in b/config.h.in index 9ed0f61c7..cd1b5ee73 100644 --- a/config.h.in +++ b/config.h.in @@ -212,6 +212,12 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_SYSTRACE_H +/* Define to 1 if you have the `ldap_sasl_interactive_bind_s' function. */ +#undef HAVE_LDAP_SASL_INTERACTIVE_BIND_S + +/* Define to 1 if you have the header file. */ +#undef HAVE_SASL_SASL_H + /* Define to 1 if you have the `lockf' function. */ #undef HAVE_LOCKF diff --git a/configure b/configure index d194e2a54..b571b1414 100755 --- a/configure +++ b/configure @@ -22022,7 +22022,8 @@ rm -f conftest.err conftest.$ac_objext \ -for ac_func in ldap_initialize ldap_start_tls_s + +for ac_func in ldap_initialize ldap_start_tls_s ldap_sasl_interactive_bind_s do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 @@ -22124,6 +22125,156 @@ fi done +for ac_header in sasl/sasl.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------- ## +## Report this to the sudo lists. ## +## ------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + SUDO_LIBS="${SUDO_LIBS}${LDAP_LIBS}" LIBS="$_LIBS" LDFLAGS="$_LDFLAGS" diff --git a/configure.in b/configure.in index 0fb925a83..5f21209c5 100644 --- a/configure.in +++ b/configure.in @@ -2217,7 +2217,8 @@ if test -n "$with_ldap"; then AC_MSG_RESULT([yes]) AC_DEFINE(HAVE_LBER_H)]) - AC_CHECK_FUNCS(ldap_initialize ldap_start_tls_s) + AC_CHECK_FUNCS(ldap_initialize ldap_start_tls_s ldap_sasl_interactive_bind_s) + AC_CHECK_HEADERS([sasl/sasl.h]) SUDO_LIBS="${SUDO_LIBS}${LDAP_LIBS}" LIBS="$_LIBS" diff --git a/ldap.c b/ldap.c index 3b3c3ee8c..500057c16 100644 --- a/ldap.c +++ b/ldap.c @@ -55,6 +55,13 @@ # include #endif #include +#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S +# ifdef HAVE_SASL_SASL_H +# include +# else +# include +# endif +#endif #include "sudo.h" #include "parse.h" @@ -90,6 +97,8 @@ struct ldap_config { int tls_checkpeer; int timelimit; int bind_timelimit; + int use_sasl; + int rootuse_sasl; char *host; char *uri; char *binddn; @@ -103,6 +112,8 @@ struct ldap_config { char *tls_cipher_suite; char *tls_certfile; char *tls_keyfile; + char *sasl_authid; + char *rootsasl_authid; } ldap_conf; /* @@ -476,6 +487,8 @@ sudo_ldap_read_config() ldap_conf.tls_checkpeer = -1; ldap_conf.timelimit = -1; ldap_conf.bind_timelimit = -1; + ldap_conf.use_sasl = -1; + ldap_conf.rootuse_sasl = -1; if ((f = fopen(_PATH_LDAP_CONF, "r")) == NULL) return(FALSE); @@ -557,6 +570,14 @@ sudo_ldap_read_config() MATCH_S("sudoers_base", ldap_conf.base) else MATCH_I("sudoers_debug", ldap_conf.debug) + else + MATCH_B("use_sasl", ldap_conf.use_sasl) + else + MATCH_S("sasl_authid", ldap_conf.sasl_authid) + else + MATCH_B("rootuse_sasl", ldap_conf.rootuse_sasl) + else + MATCH_S("rootsasl_authid", ldap_conf.rootsasl_authid) else { /* @@ -601,6 +622,14 @@ sudo_ldap_read_config() #ifdef HAVE_LDAP_START_TLS_S fprintf(stderr, "ssl %s\n", ldap_conf.ssl ? ldap_conf.ssl : "(no)"); +#endif +#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S + fprintf(stderr, "use_sasl %d\n", ldap_conf.use_sasl); + fprintf(stderr, "sasl_authid %s\n", ldap_conf.sasl_authid ? + ldap_conf.sasl_authid : "(NONE)"); + fprintf(stderr, "use_sasl %d\n", ldap_conf.use_sasl); + fprintf(stderr, "rootsasl_authid %s\n", ldap_conf.rootsasl_authid ? + ldap_conf.rootsasl_authid : "(NONE)"); #endif fprintf(stderr, "===================\n"); } @@ -831,6 +860,33 @@ sudo_ldap_display_cmnd(ldv, pw) } \ } while(0) +#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S +static int +sudo_ldap_sasl_interact(ld, flags, v_authid, v_interact) + LDAP *ld; + unsigned int flags; + void *v_authid; + void *v_interact; +{ + char *authid = (char *)v_authid; + sasl_interact_t *interact = (sasl_interact_t *)v_interact; + + for (;interact->id != SASL_CB_LIST_END; interact++) { + if (interact->id != SASL_CB_USER) + return (LDAP_PARAM_ERROR); + + if (authid != NULL) + interact->result = authid; + else if (interact->defresult != NULL) + interact->result = interact->defresult; + else + interact->result = ""; + interact->len = strlen(interact->result); + } + return (LDAP_SUCCESS); +} +#endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */ + /* * Open a connection to the LDAP server. */ @@ -938,13 +994,31 @@ sudo_ldap_open() } #endif /* HAVE_LDAP_START_TLS_S */ - /* Actually connect */ - if ((rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw))) { - fprintf(stderr, "ldap_simple_bind_s()=%d : %s\n", - rc, ldap_err2string(rc)); - return(NULL); +#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S + /* XXX - should use krb5_ccname from ldap.conf too! */ + if (ldap_conf.rootuse_sasl != FALSE && ldap_conf.use_sasl == TRUE) { + void *authid = ldap_conf.rootsasl_authid ? + ldap_conf.rootsasl_authid : ldap_conf.sasl_authid; + + rc = ldap_sasl_interactive_bind_s(ld, ldap_conf.binddn, "GSSAPI", + NULL, NULL, LDAP_SASL_QUIET, sudo_ldap_sasl_interact, authid); + if (rc != LDAP_SUCCESS) { + fprintf(stderr, "ldap_sasl_interactive_bind_s(): %d : %s\n", + rc, ldap_err2string(rc)); + return(NULL); + } + DPRINTF(("ldap_sasl_interactive_bind_s() ok"), 1); + } else +#endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */ + { + /* Actually connect */ + if ((rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw))) { + fprintf(stderr, "ldap_simple_bind_s()=%d : %s\n", + rc, ldap_err2string(rc)); + return(NULL); + } + DPRINTF(("ldap_bind() ok"), 1); } - DPRINTF(("ldap_bind() ok"), 1); return((VOID *) ld); }