ca4745e
diff -u -r freeradius-1.1.7/src/modules/rlm_ldap/configure.in work/src/modules/rlm_ldap/configure.in
05ae0f7
--- freeradius-1.1.7/src/modules/rlm_ldap/configure.in	2007-06-26 03:34:58.000000000 -0400
05ae0f7
+++ work/src/modules/rlm_ldap/configure.in	2007-11-03 14:49:46.000000000 -0400
05ae0f7
@@ -70,6 +70,75 @@
05ae0f7
 		;;
05ae0f7
 	    esac ])
05ae0f7
 
05ae0f7
+	dnl extra argument: --with-rlm-ldap-krb5-include-dir
05ae0f7
+	rlm_ldap_krb5_inc_dir=
05ae0f7
+	AC_ARG_WITH(rlm-ldap-krb5-include-dir,
05ae0f7
+	[  --with-rlm-ldap-krb5-include-dir=DIR   Directory for krb5 include files []],
05ae0f7
+	[ case "$withval" in
05ae0f7
+	    no)
05ae0f7
+		AC_MSG_ERROR(Need rlm-ldap-krb5-include-dir)
05ae0f7
+		;;
05ae0f7
+	    yes)
05ae0f7
+		;;
05ae0f7
+	    *)
05ae0f7
+		rlm_ldap_krb5_include_dir="$withval"
05ae0f7
+		;;
05ae0f7
+	  esac ]
05ae0f7
+	)
05ae0f7
+
05ae0f7
+	dnl extra argument: --with-rlm-ldap-sasl2-include-dir
05ae0f7
+	rlm_ldap_sasl2_inc_dir=
05ae0f7
+	AC_ARG_WITH(rlm-ldap-sasl2-include-dir,
05ae0f7
+	[  --with-rlm-ldap-sasl2-include-dir=DIR   Directory for sasl2 include files []],
05ae0f7
+	[ case "$withval" in
05ae0f7
+	    no)
05ae0f7
+		AC_MSG_ERROR(Need rlm-ldap-sasl2-include-dir)
05ae0f7
+		;;
05ae0f7
+	    yes)
05ae0f7
+		;;
05ae0f7
+	    *)
05ae0f7
+		rlm_ldap_sasl2_include_dir="$withval"
05ae0f7
+		;;
05ae0f7
+	  esac ]
05ae0f7
+	)
05ae0f7
+
05ae0f7
+	dnl extra argument: --with-rlm-ldap-sasl2
05ae0f7
+	rlm_ldap_with_sasl2=yes
05ae0f7
+	AC_ARG_WITH(rlm-ldap-sasl2,
05ae0f7
+	[  --with-rlm-ldap-sasl2            use sasl2, if available. (default=yes) ],
05ae0f7
+	[ case "$withval" in
05ae0f7
+	    no)
05ae0f7
+		rlm_ldap_with_sasl2=no
05ae0f7
+		;;
05ae0f7
+	    *)
05ae0f7
+		;;
05ae0f7
+	    esac ])
05ae0f7
+
05ae0f7
+	if test "x$rlm_ldap_with_sasl2" = "xyes"; then
05ae0f7
+	    AC_DEFINE(USE_SASL2, [], [Defined if building with SASL2 support])
05ae0f7
+	fi
05ae0f7
+
05ae0f7
+	dnl extra argument: --with-rlm-ldap-krb5
05ae0f7
+	rlm_ldap_with_krb5=yes
05ae0f7
+	AC_ARG_WITH(rlm-ldap-krb5,
05ae0f7
+	[  --with-rlm-ldap-krb5             use krb5, if available. (default=yes) ],
05ae0f7
+	[ case "$withval" in
05ae0f7
+	    no)
05ae0f7
+		rlm_ldap_with_krb5=no
05ae0f7
+		;;
05ae0f7
+	    *)
05ae0f7
+		;;
05ae0f7
+	    esac ])
05ae0f7
+
05ae0f7
+	if test "x$rlm_ldap_with_krb5" = "xyes"; then
05ae0f7
+	    if test "x$rlm_ldap_with_sasl2" != "xyes"; then
05ae0f7
+	        fail="$fail sasl2 if using krb5"
05ae0f7
+		# force sasl on if using krb5
05ae0f7
+		#rlm_ldap_with_sasl2="yes"
05ae0f7
+	    fi
05ae0f7
+	    AC_DEFINE(USE_KRB5, [], [Defined if building with krb5 support])
05ae0f7
+	fi
05ae0f7
+
05ae0f7
 	dnl ############################################################
05ae0f7
 	dnl # Check for libraries
05ae0f7
 	dnl ############################################################
05ae0f7
@@ -102,6 +171,20 @@
05ae0f7
 	    fi
05ae0f7
 	fi
05ae0f7
 
05ae0f7
+	if test "x$rlm_ldap_with_sasl2" = "xyes"; then
05ae0f7
+	    AC_SMART_CHECK_LIB(sasl2, sasl_client_start)
05ae0f7
+	    if test "x$ac_cv_lib_sasl2_sasl_client_start" != "xyes"; then
05ae0f7
+		fail="$fail libsasl2"
05ae0f7
+	    fi
05ae0f7
+	fi
05ae0f7
+
05ae0f7
+	if test "x$rlm_ldap_with_krb5" = "xyes"; then
05ae0f7
+	    AC_SMART_CHECK_LIB(krb5, krb5_init_context)
05ae0f7
+	    if test "x$ac_cv_lib_krb5_krb5_init_context" != xyes; then
05ae0f7
+		    fail="$fail krb5"
05ae0f7
+	    fi
05ae0f7
+	fi
05ae0f7
+
05ae0f7
 	dnl ############################################################
05ae0f7
 	dnl # Check for header files
05ae0f7
 	dnl ############################################################
05ae0f7
@@ -112,6 +195,23 @@
05ae0f7
 	  fail="$fail ldap.h"
05ae0f7
 	fi
05ae0f7
 
05ae0f7
+	if test "x$rlm_ldap_with_sasl2" = "xyes"; then
05ae0f7
+	    smart_try_dir=$rlm_ldap_sasl2_include_dir
05ae0f7
+	    AC_SMART_CHECK_INCLUDE(sasl.h)
05ae0f7
+	    if test "$ac_cv_header_sasl_h" != "yes"; then
05ae0f7
+	        fail="$fail sasl.h"
05ae0f7
+	    fi
05ae0f7
+	fi
05ae0f7
+
05ae0f7
+	if test "x$rlm_ldap_with_krb5" = "xyes"; then
05ae0f7
+	    smart_try_dir=$rlm_ldap_krb5_include_dir
05ae0f7
+	    AC_SMART_CHECK_INCLUDE(krb5.h)
05ae0f7
+	    if test "$ac_cv_header_krb5_h" != "yes"; then
05ae0f7
+	        fail="$fail krb5.h"
05ae0f7
+	    fi
05ae0f7
+	fi
05ae0f7
+
05ae0f7
+
05ae0f7
 	dnl ############################################################
05ae0f7
 	dnl # Check for library functions
05ae0f7
 	dnl ############################################################
05ae0f7
@@ -168,4 +268,5 @@
05ae0f7
 AC_SUBST(ldap_ldflags)
05ae0f7
 AC_SUBST(ldap_cflags)
05ae0f7
 AC_SUBST(targetname)
05ae0f7
+AC_CONFIG_HEADER(config.h)
05ae0f7
 AC_OUTPUT(Makefile)
ca4745e
diff -u -r freeradius-1.1.7/src/modules/rlm_ldap/rlm_ldap.c work/src/modules/rlm_ldap/rlm_ldap.c
05ae0f7
--- freeradius-1.1.7/src/modules/rlm_ldap/rlm_ldap.c	2007-11-01 13:16:18.000000000 -0400
ca4745e
+++ work/src/modules/rlm_ldap/rlm_ldap.c	2007-11-09 16:52:36.000000000 -0500
05ae0f7
@@ -1,3 +1,4 @@
05ae0f7
+// -*- mode: c; indent-tabs-mode: t; c-basic-offset: 8; -*-
05ae0f7
 /*
05ae0f7
  * rlm_ldap.c	LDAP authorization and authentication module.
05ae0f7
  *
05ae0f7
@@ -159,7 +160,9 @@
05ae0f7
  */
ca4745e
 static const char rcsid[] = "$Id: freeradius-1.1.7-ipa.patch,v 1.2 2007/11/16 13:34:48 jdennis Exp $";
05ae0f7
 
05ae0f7
+#define _GNU_SOURCE
05ae0f7
 #include "autoconf.h"
05ae0f7
+#include "config.h"
05ae0f7
 
05ae0f7
 #include	<sys/types.h>
05ae0f7
 #include	<sys/socket.h>
05ae0f7
@@ -187,6 +190,14 @@
05ae0f7
 #include	"modules.h"
05ae0f7
 #include	"rad_assert.h"
05ae0f7
 
05ae0f7
+#ifdef USE_KRB5
05ae0f7
+#include <krb5.h>
05ae0f7
+#endif
05ae0f7
+
05ae0f7
+#ifdef USE_SASL2
05ae0f7
+#include <sasl.h>
05ae0f7
+#endif
05ae0f7
+
05ae0f7
 #ifndef HAVE_PTHREAD_H
05ae0f7
 /*
05ae0f7
  *      This is a lot simpler than putting ifdef's around
05ae0f7
@@ -274,6 +285,19 @@
05ae0f7
 #endif
05ae0f7
 } LDAP_CONN;
05ae0f7
 
05ae0f7
+#ifdef USE_KRB5
05ae0f7
+typedef struct krb_session {
05ae0f7
+    krb5_context context;
05ae0f7
+    char *realm_name;
05ae0f7
+    krb5_principal principal;
05ae0f7
+    krb5_keytab keytab;
05ae0f7
+    char *ccname;
05ae0f7
+    char *ccache_file;
05ae0f7
+    krb5_ccache ccache;
05ae0f7
+    krb5_creds creds;
05ae0f7
+} krb_session;
05ae0f7
+#endif
05ae0f7
+
05ae0f7
 typedef struct {
05ae0f7
 	char           *server;
05ae0f7
 	int             port;
ca4745e
@@ -322,6 +346,17 @@
05ae0f7
 	int			edir_account_policy_check;
05ae0f7
 #endif
05ae0f7
 	int		set_auth_type;
05ae0f7
+#ifdef USE_SASL2
05ae0f7
+	int		use_sasl;
05ae0f7
+	char		*sasl_mech;
05ae0f7
+#endif
05ae0f7
+#ifdef USE_KRB5
05ae0f7
+	char		*krb_keytab;
05ae0f7
+	char		*krb_principal;
05ae0f7
+	krb_session	krb;
05ae0f7
+#endif
ca4745e
+	char           *clients_basedn;
ca4745e
+	char           *clients_filter;
05ae0f7
 }               ldap_instance;
05ae0f7
 
05ae0f7
 /* The default setting for TLS Certificate Verification */
ca4745e
@@ -370,6 +405,16 @@
05ae0f7
 #endif
05ae0f7
 
05ae0f7
 	{"set_auth_type", PW_TYPE_BOOLEAN, offsetof(ldap_instance,set_auth_type), NULL, "yes"},
05ae0f7
+#ifdef USE_SASL2
05ae0f7
+	{"use_sasl", PW_TYPE_BOOLEAN, offsetof(ldap_instance,use_sasl), NULL, "no"},
05ae0f7
+	{"sasl_mech", PW_TYPE_STRING_PTR, offsetof(ldap_instance,sasl_mech), NULL, "GSSAPI"},
05ae0f7
+#endif
05ae0f7
+#ifdef USE_KRB5
05ae0f7
+	{"krb_keytab", PW_TYPE_STRING_PTR, offsetof(ldap_instance,krb_keytab), NULL, "${confdir}/krb5.keytab"},
05ae0f7
+	{"krb_principal", PW_TYPE_STRING_PTR, offsetof(ldap_instance,krb_principal), NULL, NULL},
05ae0f7
+#endif
ca4745e
+	{"clients_basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance,clients_basedn), NULL, NULL},
ca4745e
+	{"clients_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance,clients_filter), NULL, "(objectclass=radiusClientProfile)"},
05ae0f7
 	{NULL, -1, 0, NULL, NULL}
05ae0f7
 };
05ae0f7
 
ca4745e
@@ -380,11 +425,220 @@
05ae0f7
 #ifdef FIELDCPY
05ae0f7
 static void     fieldcpy(char *, char **);
05ae0f7
 #endif
05ae0f7
+
05ae0f7
+#ifdef USE_KRB5
05ae0f7
+static krb5_timestamp ticket_lifetime = 300;
05ae0f7
+
05ae0f7
+static void krb_session_zero(krb_session *krb)
05ae0f7
+{
05ae0f7
+	DEBUG("rlm_ldap: krb_session_zero (%p)", krb);
05ae0f7
+	krb->context = NULL;
05ae0f7
+	krb->realm_name = NULL;
05ae0f7
+	memset ((char*)&krb->principal, 0, sizeof(krb->principal));
05ae0f7
+	krb->keytab = NULL;
05ae0f7
+	krb->ccname = NULL;
05ae0f7
+	krb->ccache_file = NULL;
05ae0f7
+	krb->ccache = NULL;
05ae0f7
+	memset ((char*)&krb->creds, 0, sizeof(krb->creds));
05ae0f7
+}
05ae0f7
+
05ae0f7
+static void krb_session_free(krb_session *krb)
05ae0f7
+{
05ae0f7
+	DEBUG("rlm_ldap: krb_session_free (%p)", krb);
05ae0f7
+	if (krb->ccache_file) {
05ae0f7
+		unlink(krb->ccache_file);
05ae0f7
+		free(krb->ccache_file);
05ae0f7
+	}
05ae0f7
+
05ae0f7
+	if (krb->ccname) {
05ae0f7
+		free(krb->ccname);
05ae0f7
+		if (unsetenv("KRB5CCNAME") < 0) {
05ae0f7
+			radlog(L_ERR, "rlm_ldap: Unable remove the environment variable KRB5CCNAME (%d)%s",
05ae0f7
+			       errno, strerror(errno));
05ae0f7
+		}
05ae0f7
+	}
05ae0f7
+
05ae0f7
+	if (krb->context) {
05ae0f7
+		krb5_free_cred_contents(krb->context, &krb->creds);
05ae0f7
+		if (krb->ccache) krb5_cc_close(krb->context, krb->ccache);
05ae0f7
+		if (krb->keytab) krb5_kt_close(krb->context, krb->keytab);
05ae0f7
+		if (krb->principal) krb5_free_principal(krb->context, krb->principal);
05ae0f7
+		if (krb->realm_name) krb5_free_default_realm(krb->context, krb->realm_name);
05ae0f7
+		krb5_free_context(krb->context);
05ae0f7
+	}
05ae0f7
+	krb_session_zero(krb);
05ae0f7
+}
05ae0f7
+
05ae0f7
+static int krb_session_expired(krb_session *krb)
05ae0f7
+{
05ae0f7
+	int retval;
05ae0f7
+	krb5_timestamp currenttime;
05ae0f7
+	krb5_timestamp epsilon = 60;
05ae0f7
+
05ae0f7
+	if (!krb->context) return TRUE;
05ae0f7
+
05ae0f7
+	if ((retval = krb5_timeofday (krb->context, &currenttime))){ 
05ae0f7
+		radlog(L_ERR, "rlm_ldap: could not get time of day (%s)", krb5_get_error_message(krb->context, retval));
05ae0f7
+		return TRUE;		
05ae0f7
+	}	
05ae0f7
+
05ae0f7
+	if (krb->creds.times.endtime < currenttime+epsilon) {
05ae0f7
+		return TRUE;
05ae0f7
+	} else {
05ae0f7
+		return FALSE;
05ae0f7
+	}
05ae0f7
+
05ae0f7
+}
05ae0f7
+
05ae0f7
+static char *get_tmp_file()
05ae0f7
+{
05ae0f7
+	char *tmp_template = "/tmp/XXXXXX";
05ae0f7
+	char *tmp_file = NULL;
05ae0f7
+	int fd = -1;
05ae0f7
+        
05ae0f7
+	if (!(tmp_file = strdup(tmp_template))) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Out of memory!");
05ae0f7
+		return NULL;
05ae0f7
+	}
05ae0f7
+
05ae0f7
+	if ((fd = mkstemp(tmp_file)) < 0) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Failed to create tmp file with errno: (%d)%s", errno, strerror(errno));
05ae0f7
+		free(tmp_file);
05ae0f7
+		return NULL;
05ae0f7
+	}
05ae0f7
+	/* close mimmediately, we don't need to keep the file open */
05ae0f7
+	close(fd);
05ae0f7
+	return tmp_file;
05ae0f7
+}
05ae0f7
+
05ae0f7
+static int krb_session_get_ticket(krb_session *krb)
05ae0f7
+{
05ae0f7
+	int krberr;
05ae0f7
+	int ticket_expired;
05ae0f7
+	krb5_get_init_creds_opt options;
05ae0f7
+
05ae0f7
+	ticket_expired = krb_session_expired(krb);
05ae0f7
+	DEBUG("rlm_ldap: krb_session_get_ticket, %s",
05ae0f7
+	      ticket_expired ? "expired, getting new ticket":"ticket still valid, returning");
05ae0f7
+	if (!ticket_expired) return 0;
05ae0f7
+
05ae0f7
+	memset(&options, 0, sizeof(options));
05ae0f7
+	krb5_get_init_creds_opt_set_address_list(&options, NULL);
05ae0f7
+	krb5_get_init_creds_opt_set_forwardable(&options, 0);
05ae0f7
+	krb5_get_init_creds_opt_set_proxiable(&options, 0);
05ae0f7
+	/* set a very short lifetime, we don't keep the ticket around */
05ae0f7
+	krb5_get_init_creds_opt_set_tkt_life(&options, ticket_lifetime);
05ae0f7
+
05ae0f7
+	if ((krberr = krb5_get_init_creds_keytab(krb->context, &krb->creds, krb->principal,
05ae0f7
+						 krb->keytab, 0, NULL, &options))) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Failed to init credentials: %s", krb5_get_error_message(krb->context, krberr));
05ae0f7
+		return -1;
05ae0f7
+	}
05ae0f7
+
05ae0f7
+	if ((krberr = krb5_cc_initialize(krb->context, krb->ccache, krb->principal))) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Failed to init ccache: %s", krb5_get_error_message(krb->context, krberr));
05ae0f7
+		return -1;
05ae0f7
+	}
05ae0f7
+    
05ae0f7
+	if ((krberr = krb5_cc_store_cred(krb->context, krb->ccache, &krb->creds))) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Failed to store creds: %s", krb5_get_error_message(krb->context, krberr));
05ae0f7
+		return -1;
05ae0f7
+        }
05ae0f7
+
05ae0f7
+	return 0;
05ae0f7
+}
05ae0f7
+
05ae0f7
+static int krb_session_init(void *instance, krb_session *krb)
05ae0f7
+{
05ae0f7
+	ldap_instance  *inst = instance;
05ae0f7
+	int krberr;
05ae0f7
+
05ae0f7
+	DEBUG("rlm_ldap: krb_session_init (%p) principal=%s keytab=%s" , krb, inst->krb_principal, inst->krb_keytab);
05ae0f7
+	if ((krberr = krb5_init_context(&krb->context))) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Failed to init kerberos context");
05ae0f7
+		return -1;
05ae0f7
+	}
05ae0f7
+
05ae0f7
+	if ((krberr = krb5_get_default_realm(krb->context, &krb->realm_name))) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Failed to get default realm name: %s", krb5_get_error_message(krb->context, krberr));
05ae0f7
+		return -1;
05ae0f7
+	}
05ae0f7
+
05ae0f7
+	if ((krberr = krb5_parse_name(krb->context, inst->krb_principal, &krb->principal))) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Unable to parse principal %s: %s", inst->krb_principal, krb5_get_error_message(krb->context, krberr));
05ae0f7
+		return -1;
05ae0f7
+	}
05ae0f7
+
05ae0f7
+	if ((krberr = krb5_kt_resolve(krb->context, inst->krb_keytab, &krb->keytab))) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Failed to read keytab file: %s", krb5_get_error_message(krb->context, krberr));
05ae0f7
+		return -1;
05ae0f7
+	}
05ae0f7
+
05ae0f7
+	if (!(krb->ccache_file = get_tmp_file())) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: could not create krb credential cached file");
05ae0f7
+		return -1;
05ae0f7
+	}
05ae0f7
+
05ae0f7
+	if (asprintf(&krb->ccname, "FILE:%s", krb->ccache_file) < 0) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Out of memory!");
05ae0f7
+		return -1;
05ae0f7
+	}
05ae0f7
+
05ae0f7
+	if (setenv("KRB5CCNAME", krb->ccname, 1) < 0) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Unable to set environment variable KRB5CCNAME (%d)%s", errno, strerror(errno));
05ae0f7
+		return -1;
05ae0f7
+	}
05ae0f7
+
05ae0f7
+	if ((krberr = krb5_cc_resolve(krb->context, krb->ccname, &krb->ccache))) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Failed to set cache name: %s", krb5_get_error_message(krb->context, krberr));
05ae0f7
+		return -1;
05ae0f7
+	}
05ae0f7
+
05ae0f7
+	return 0;
05ae0f7
+}
05ae0f7
+
05ae0f7
+#endif
05ae0f7
+
05ae0f7
+#ifdef USE_SASL2
05ae0f7
+static int ldap_sasl_interact(LDAP *ld, unsigned flags, void *priv_data, void *in)
05ae0f7
+{
05ae0f7
+    sasl_interact_t *interact = NULL;
05ae0f7
+    int ret = LDAP_OTHER;
05ae0f7
+
05ae0f7
+    DEBUG("rlm_ldap: ldap_sasl_interact");
05ae0f7
+
05ae0f7
+    if (!ld) return LDAP_PARAM_ERROR;
05ae0f7
+
05ae0f7
+    for (interact = in; interact->id != SASL_CB_LIST_END; interact++) {
05ae0f7
+        switch(interact->id) {
05ae0f7
+        case SASL_CB_USER:
05ae0f7
+            interact->result = "";
05ae0f7
+            interact->len = strlen(interact->result);
05ae0f7
+            ret = LDAP_SUCCESS;
05ae0f7
+            break;
05ae0f7
+        case SASL_CB_GETREALM:
05ae0f7
+            interact->result = "";
05ae0f7
+            interact->len = strlen(interact->result);
05ae0f7
+            ret = LDAP_SUCCESS;
05ae0f7
+            break;
05ae0f7
+        default:
05ae0f7
+            radlog(L_ERR, "rlm_ldap: Unhandled SASL int. option %ld", interact->id);
05ae0f7
+            interact->result = NULL;
05ae0f7
+            interact->len = 0;
05ae0f7
+            ret = LDAP_OTHER;
05ae0f7
+        }
05ae0f7
+    }
05ae0f7
+    return ret;
05ae0f7
+}
05ae0f7
+
05ae0f7
+#endif
05ae0f7
+
05ae0f7
 static VALUE_PAIR *ldap_pairget(LDAP *, LDAPMessage *, TLDAP_RADIUS *,VALUE_PAIR **,char);
05ae0f7
 static int ldap_groupcmp(void *, REQUEST *, VALUE_PAIR *, VALUE_PAIR *, VALUE_PAIR *, VALUE_PAIR **);
05ae0f7
 static int ldap_xlat(void *, REQUEST *, char *, char *, size_t, RADIUS_ESCAPE_STRING);
ca4745e
 static LDAP    *ldap_connect(void *instance, const char *, const char *, int, int *, char **);
ca4745e
 static int     read_mappings(ldap_instance* inst);
ca4745e
+static int generate_ldap_clients(ldap_instance* inst);
ca4745e
 
ca4745e
 static inline int ldap_get_conn(LDAP_CONN *conns,LDAP_CONN **ret,void *instance)
ca4745e
 {
ca4745e
@@ -662,6 +916,15 @@
05ae0f7
 
05ae0f7
 	DEBUG("conns: %p",inst->conns);
05ae0f7
 
05ae0f7
+#ifdef USE_KRB5
05ae0f7
+	if (krb_session_init(inst, &inst->krb)) {
05ae0f7
+		radlog(L_ERR, "rlm_ldap: Failed to init kerberos session");
05ae0f7
+	}
05ae0f7
+#endif
ca4745e
+	if (generate_ldap_clients(inst)) {
ca4745e
+		radlog(L_ERR, "rlm_ldap: Failed to read client list from ldap");
ca4745e
+	}
ca4745e
+
05ae0f7
 	*instance = inst;
05ae0f7
 
05ae0f7
 
ca4745e
@@ -770,7 +1033,7 @@
ca4745e
 
ca4745e
 static int
ca4745e
 perform_search(void *instance, LDAP_CONN *conn, char *search_basedn, int scope, char *filter,
ca4745e
-		char **attrs, LDAPMessage ** result)
ca4745e
+	       char **attrs, int single, LDAPMessage ** result)
ca4745e
 {
ca4745e
 	int             res = RLM_MODULE_OK;
ca4745e
 	int		ldap_errno = 0;
ca4745e
@@ -854,11 +1117,13 @@
ca4745e
 		return (RLM_MODULE_FAIL);
ca4745e
 	}
ca4745e
 
ca4745e
-	if ((ldap_count_entries(conn->ld, *result)) != 1) {
ca4745e
+	
ca4745e
+	if (single && (ldap_count_entries(conn->ld, *result)) != 1) {
ca4745e
 		DEBUG("rlm_ldap: object not found or got ambiguous search result");
ca4745e
 		res = RLM_MODULE_NOTFOUND;
ca4745e
 		ldap_msgfree(*result);
ca4745e
 	}
ca4745e
+
ca4745e
 	return res;
ca4745e
 }
ca4745e
 
ca4745e
@@ -914,6 +1179,156 @@
ca4745e
 }
ca4745e
 
ca4745e
 /*
ca4745e
+ * generate_ldap_clients(). read clients from ldap, append to client list
ca4745e
+ */
ca4745e
+
ca4745e
+static int generate_ldap_clients(ldap_instance* inst)
ca4745e
+{
ca4745e
+	LDAP_CONN	*conn;
ca4745e
+	int		conn_id = -1;
ca4745e
+	int		res;
ca4745e
+	LDAPMessage    *result, *entry;
ca4745e
+	int		client_idx;
ca4745e
+
ca4745e
+	if (!inst->clients_basedn) {
ca4745e
+		DEBUG("rlm_ldap: generate_ldap_clients, client basedn not set, skipping...");
ca4745e
+		return 0;
ca4745e
+	}
ca4745e
+	DEBUG("rlm_ldap: generate_ldap_clients, client_basedn=%s", inst->clients_basedn);
ca4745e
+
ca4745e
+
ca4745e
+	if ((conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
ca4745e
+		radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
ca4745e
+		return 1;
ca4745e
+	}
ca4745e
+	if ((res = perform_search(inst, conn, inst->clients_basedn, LDAP_SCOPE_SUBTREE,
ca4745e
+				  inst->clients_filter, NULL, FALSE, &result)) != RLM_MODULE_OK){
ca4745e
+		DEBUG("rlm_ldap::generate_ldap_clients: search failed");
ca4745e
+		ldap_release_conn(conn_id,inst->conns);
ca4745e
+		return 1;
ca4745e
+	}
ca4745e
+
ca4745e
+	for (entry = ldap_first_entry(conn->ld, result), client_idx = 0;
ca4745e
+	     entry != NULL;
ca4745e
+	     entry = ldap_next_entry(conn->ld, entry), client_idx++) {
ca4745e
+		char *val, **vals;
ca4745e
+		RADCLIENT client, *p_client; 
ca4745e
+		char *netmask;
ca4745e
+		char ip_buf[64];
ca4745e
+
ca4745e
+		memset(&client, 0, sizeof(client));
ca4745e
+
ca4745e
+		if ((vals = ldap_get_values(conn->ld, entry, "radiusClientNASIpAddress")) != NULL) {
ca4745e
+			val = vals[0];
ca4745e
+			if (strlen(val) >= sizeof(client.longname)) {
ca4745e
+				radlog(L_ERR, "rlm_ldap, retrieving clients: address (%s) length %d exceeds %d maximum",
ca4745e
+				       val, strlen(val), sizeof(client.longname)-1);
ca4745e
+				ldap_value_free(vals);
ca4745e
+				continue;
ca4745e
+			} else {
ca4745e
+				strcpy(client.longname, val);
ca4745e
+				ldap_value_free(vals);
ca4745e
+			}
ca4745e
+		}
ca4745e
+
ca4745e
+
ca4745e
+		if ((vals = ldap_get_values(conn->ld, entry, "radiusClientSecret")) != NULL) {
ca4745e
+			val = vals[0];
ca4745e
+			if (strlen(val) >= sizeof(client.secret)) {
ca4745e
+				radlog(L_ERR, "rlm_ldap, retrieving clients: secret length %d exceeds %d maximum",
ca4745e
+				       strlen(val), sizeof(client.secret)-1);
ca4745e
+				ldap_value_free(vals);
ca4745e
+				continue;
ca4745e
+			} else {
ca4745e
+				strcpy(client.secret, val);
ca4745e
+				ldap_value_free(vals);
ca4745e
+			}
ca4745e
+		}
ca4745e
+
ca4745e
+
ca4745e
+		if ((vals = ldap_get_values(conn->ld, entry, "radiusClientNASType")) != NULL) {
ca4745e
+			val = vals[0];
ca4745e
+			if (strlen(val) >= sizeof(client.nastype)) {
ca4745e
+				radlog(L_ERR, "rlm_ldap, retrieving clients:NAS Type (%s) length %d exceeds %d maximum",
ca4745e
+				       val, strlen(val), sizeof(client.nastype)-1);
ca4745e
+				ldap_value_free(vals);
ca4745e
+				continue;
ca4745e
+			} else {
ca4745e
+				strcpy(client.nastype, val);
ca4745e
+				ldap_value_free(vals);
ca4745e
+			}
ca4745e
+		}
ca4745e
+
ca4745e
+
ca4745e
+		if ((vals = ldap_get_values(conn->ld, entry, "radiusClientShortName")) != NULL) {
ca4745e
+			val = vals[0];
ca4745e
+			if (strlen(val) >= sizeof(client.shortname)) {
ca4745e
+				radlog(L_ERR, "rlm_ldap, retrieving clients: address (%s) length %d exceeds %d maximum",
ca4745e
+				       val, strlen(val), sizeof(client.shortname)-1);
ca4745e
+				ldap_value_free(vals);
ca4745e
+				continue;
ca4745e
+			} else {
ca4745e
+				strcpy(client.shortname, val);
ca4745e
+				ldap_value_free(vals);
ca4745e
+			}
ca4745e
+		}
ca4745e
+
ca4745e
+
ca4745e
+		/* Look for a mask in the hostname */
ca4745e
+		netmask = strchr(client.longname, '/');
ca4745e
+		client.netmask = ~0;
ca4745e
+		if (netmask) {
ca4745e
+			int mask_length;
ca4745e
+
ca4745e
+			*netmask = '\0';
ca4745e
+			netmask++;
ca4745e
+
ca4745e
+			mask_length = atoi(netmask);
ca4745e
+			if ((mask_length < 0) || (mask_length > 32)) {
ca4745e
+				radlog(L_ERR, "rlm_ldap: Invalid value '%s' for IP network mask.for %s", netmask, client.longname);
ca4745e
+				continue;
ca4745e
+			}
ca4745e
+
ca4745e
+			if (mask_length == 0) {
ca4745e
+				client.netmask = 0;
ca4745e
+			} else {
ca4745e
+				client.netmask = ~0 << (32 - mask_length);
ca4745e
+			}
ca4745e
+		}
ca4745e
+
ca4745e
+		client.ipaddr = ip_getaddr(client.longname);
ca4745e
+		if (client.ipaddr == INADDR_NONE) {
ca4745e
+			radlog(L_ERR, "rlm_ldap: Failed to look up hostname %s", client.longname);
ca4745e
+			continue;
ca4745e
+		}
ca4745e
+		client.netmask = htonl(client.netmask);
ca4745e
+		client.ipaddr &= client.netmask; /* addr & mask are in network order */
ca4745e
+		if (netmask) *netmask = '/';
ca4745e
+
ca4745e
+
ca4745e
+		DEBUG("rlm_ldap: client[%2d] client=%s ip=%s mask=0x%x shortname=%s nastype=%s",
ca4745e
+		      client_idx, client.longname, ip_ntoa(ip_buf, client.ipaddr), client.netmask, client.shortname, client.nastype);
ca4745e
+
ca4745e
+
ca4745e
+		if ((p_client = rad_malloc(sizeof(RADCLIENT))) == NULL) {
ca4745e
+			radlog(L_ERR, "rlm_ldap: Out of memory!");
ca4745e
+			ldap_msgfree(result);
ca4745e
+			ldap_release_conn(conn_id,inst->conns);
ca4745e
+			return 1;
ca4745e
+		}
ca4745e
+		memcpy(p_client, &client, sizeof(client));
ca4745e
+		p_client->next = mainconfig.clients;
ca4745e
+		mainconfig.clients = p_client;
ca4745e
+
ca4745e
+	}
ca4745e
+
ca4745e
+	ldap_msgfree(result);
ca4745e
+	ldap_release_conn(conn_id,inst->conns);
ca4745e
+	// client_walk(); /* uncomment for debugging */
ca4745e
+	return 0;
ca4745e
+}
ca4745e
+
ca4745e
+/*
ca4745e
  * ldap_groupcmp(). Implement the Ldap-Group == "group" filter
ca4745e
  */
ca4745e
 
ca4745e
@@ -967,7 +1382,7 @@
ca4745e
 			return 1;
ca4745e
 		}
ca4745e
                 if ((res = perform_search(inst, conn, basedn, LDAP_SCOPE_SUBTREE,
ca4745e
-					filter, attrs, &result)) != RLM_MODULE_OK){
ca4745e
+					filter, attrs, TRUE, &result)) != RLM_MODULE_OK){
ca4745e
                         DEBUG("rlm_ldap::ldap_groupcmp: search failed");
ca4745e
 			ldap_release_conn(conn_id,inst->conns);
ca4745e
                         return 1;
ca4745e
@@ -1012,7 +1427,7 @@
ca4745e
 	}
ca4745e
 
ca4745e
 	if ((res = perform_search(inst, conn, basedn, LDAP_SCOPE_SUBTREE,
ca4745e
-				filter, attrs, &result)) == RLM_MODULE_OK){
ca4745e
+				filter, attrs, TRUE, &result)) == RLM_MODULE_OK){
ca4745e
 		DEBUG("rlm_ldap::ldap_groupcmp: User found in group %s",
ca4745e
 				(char *)check->strvalue);
ca4745e
 		ldap_msgfree(result);
ca4745e
@@ -1042,7 +1457,7 @@
ca4745e
 		return 1;
ca4745e
 	}
ca4745e
 	if ((res = perform_search(inst, conn, (char *)vp_user_dn->strvalue, LDAP_SCOPE_BASE,
ca4745e
-					filter, group_attrs,&result)) != RLM_MODULE_OK){
ca4745e
+				filter, group_attrs, TRUE, &result)) != RLM_MODULE_OK){
ca4745e
 		DEBUG("rlm_ldap::ldap_groupcmp: Search returned error");
ca4745e
 		ldap_release_conn(conn_id, inst->conns);
ca4745e
 		return 1;
ca4745e
@@ -1066,7 +1481,7 @@
ca4745e
 					(char *)check->strvalue);
ca4745e
 				if ((res = perform_search(inst, conn, vals[i],
ca4745e
 						LDAP_SCOPE_BASE, filter,
ca4745e
-						attrs, &gr_result)) != RLM_MODULE_OK){
ca4745e
+						attrs, TRUE, &gr_result)) != RLM_MODULE_OK){
ca4745e
 					if (res != RLM_MODULE_NOTFOUND){
ca4745e
 						DEBUG("rlm_ldap::ldap_groupcmp: \
ca4745e
 							Search returned error");
ca4745e
@@ -1161,7 +1576,7 @@
ca4745e
 		ldap_free_urldesc(ldap_url);
ca4745e
 		return 0;
ca4745e
 	}
ca4745e
-	if ((res = perform_search(inst, conn, ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter, ldap_url->lud_attrs, &result)) != RLM_MODULE_OK){
ca4745e
+	if ((res = perform_search(inst, conn, ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter, ldap_url->lud_attrs, TRUE, &result)) != RLM_MODULE_OK){
ca4745e
 		if (res == RLM_MODULE_NOTFOUND){
ca4745e
 			DEBUG("rlm_ldap: Search returned not found");
ca4745e
 			ldap_free_urldesc(ldap_url);
ca4745e
@@ -1275,7 +1690,7 @@
ca4745e
 		radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
ca4745e
 		return RLM_MODULE_FAIL;
ca4745e
 	}
ca4745e
-	if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, inst->atts, &result)) != RLM_MODULE_OK) {
ca4745e
+	if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, inst->atts, TRUE, &result)) != RLM_MODULE_OK) {
ca4745e
 		DEBUG("rlm_ldap: search failed");
ca4745e
 		if (res == RLM_MODULE_NOTFOUND){
ca4745e
 			snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: User not found");
ca4745e
@@ -1360,7 +1775,7 @@
ca4745e
 		if (profile && strlen(profile)){
ca4745e
 			if ((res = perform_search(instance, conn,
ca4745e
 				profile, LDAP_SCOPE_BASE,
ca4745e
-				filter, inst->atts, &def_result)) == RLM_MODULE_OK){
ca4745e
+				filter, inst->atts, TRUE, &def_result)) == RLM_MODULE_OK){
ca4745e
 				if ((def_msg = ldap_first_entry(conn->ld,def_result))){
ca4745e
 					if ((check_tmp = ldap_pairget(conn->ld,def_msg,inst->check_item_map,check_pairs,1))) {
ca4745e
 						if (inst->do_xlat){
ca4745e
@@ -1399,7 +1814,7 @@
ca4745e
 			while(vals[i] != NULL && strlen(vals[i])){
ca4745e
 				if ((res = perform_search(instance, conn,
ca4745e
 					vals[i], LDAP_SCOPE_BASE,
ca4745e
-					filter, inst->atts, &def_attr_result)) == RLM_MODULE_OK){
ca4745e
+					filter, inst->atts, TRUE, &def_attr_result)) == RLM_MODULE_OK){
ca4745e
 					if ((def_attr_msg = ldap_first_entry(conn->ld,def_attr_result))){
ca4745e
 						if ((check_tmp = ldap_pairget(conn->ld,def_attr_msg,inst->check_item_map,check_pairs,1))) {
ca4745e
 							if (inst->do_xlat){
ca4745e
@@ -1778,7 +2193,7 @@
ca4745e
 			radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
ca4745e
 			return RLM_MODULE_FAIL;
ca4745e
 		}
ca4745e
-		if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, &result)) != RLM_MODULE_OK) {
ca4745e
+		if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, TRUE, &result)) != RLM_MODULE_OK) {
ca4745e
 			if (res == RLM_MODULE_NOTFOUND){
ca4745e
 				snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: User not found");
ca4745e
 				module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
ca4745e
@@ -2167,6 +2582,12 @@
05ae0f7
 	ldap_version = LDAP_VERSION3;
05ae0f7
 	if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldap_version) != LDAP_OPT_SUCCESS) {
05ae0f7
 		radlog(L_ERR, "rlm_ldap: Could not set LDAP version to V3");
05ae0f7
+#ifdef USE_SASL2
05ae0f7
+		if (inst->use_sasl) {
05ae0f7
+			radlog(L_ERR, "rlm_ldap: sasl was enabled, but sasl requires LDAP V3, disabling sasl");
05ae0f7
+			inst->use_sasl = FALSE;
05ae0f7
+		}
05ae0f7
+#endif
05ae0f7
 	}
05ae0f7
 #ifdef HAVE_LDAP_START_TLS
05ae0f7
         if(inst->tls_mode) {
ca4745e
@@ -2208,7 +2629,7 @@
ca4745e
 
ca4745e
 #ifdef HAVE_LDAP_INT_TLS_CONFIG
ca4745e
 
ca4745e
-	if ( ldap_set_option( NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
ca4745e
+	if ( ldap_int_tls_config( NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
ca4745e
 							  (inst->tls_require_cert) )
ca4745e
 		 != LDAP_OPT_SUCCESS) {
ca4745e
 		radlog(L_ERR, "rlm_ldap: could not set "
ca4745e
@@ -2273,6 +2694,25 @@
05ae0f7
 	else{
05ae0f7
 		DEBUG("rlm_ldap: bind as %s/%s to %s:%d", dn, password, inst->server, inst->port);
05ae0f7
 	}
05ae0f7
+#ifdef USE_SASL2
05ae0f7
+	if (inst->use_sasl) {
05ae0f7
+		DEBUG("rlm_ldap: performing sasl bind");
05ae0f7
+#ifdef USE_KRB5
05ae0f7
+		if (krb_session_get_ticket(&inst->krb)) {
05ae0f7
+			radlog(L_ERR, "rlm_ldap: could not get kerberos ticket");
05ae0f7
+		}
05ae0f7
+#endif
05ae0f7
+		ldap_errno = ldap_sasl_interactive_bind_s(ld,			/* ldap connection */
05ae0f7
+							  NULL,			/* dn */
05ae0f7
+							  "GSSAPI",		/* mechanism */
05ae0f7
+							  NULL,			/* server controls */
05ae0f7
+							  NULL,			/* client controls */
05ae0f7
+							  LDAP_SASL_QUIET,	/* flags */
05ae0f7
+							  ldap_sasl_interact,	/* callback function */
05ae0f7
+							  NULL);		/* defaults */
05ae0f7
+	} else {
05ae0f7
+#endif
05ae0f7
+	DEBUG("rlm_ldap: performing simple bind");
05ae0f7
 	msgid = ldap_bind(ld, dn, password,LDAP_AUTH_SIMPLE);
05ae0f7
 	if (msgid == -1) {
05ae0f7
 		ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
ca4745e
@@ -2314,6 +2754,9 @@
05ae0f7
 		return (NULL);
05ae0f7
 	}
05ae0f7
 	ldap_errno = ldap_result2error(ld, res, 1);
05ae0f7
+#ifdef USE_SASL2
05ae0f7
+	}
05ae0f7
+#endif
05ae0f7
 	switch (ldap_errno) {
05ae0f7
 	case LDAP_SUCCESS:
05ae0f7
 		DEBUG("rlm_ldap: Bind was successful");
ca4745e
@@ -2406,6 +2849,18 @@
05ae0f7
 		free(inst->conns);
05ae0f7
 	}
05ae0f7
 
05ae0f7
+#ifdef USE_SASL2
05ae0f7
+	if (inst->sasl_mech)
05ae0f7
+		free((char *)inst->sasl_mech);
05ae0f7
+#endif
05ae0f7
+#ifdef USE_KRB5
05ae0f7
+	if (inst->krb_keytab)
05ae0f7
+		free((char *)inst->krb_keytab);
05ae0f7
+	if (inst->krb_principal)
05ae0f7
+		free((char *)inst->krb_principal);
05ae0f7
+	krb_session_free(&inst->krb);
05ae0f7
+#endif
05ae0f7
+
05ae0f7
 #ifdef NOVELL
05ae0f7
 	if (inst->apc_conns){ 
05ae0f7
 		int i;