saslauthd patch for the bind-like method with SASL

IKEDA Yasuyuki <[email protected]>
Newsgroups gmane.comp.security.cyrus.sasl
Message-ID <[email protected]>
Hello all.

I wrote an attached patch to work with the following case.
How about merge this patch to the cyrus-sasl source tree?

When you use the SASL authentication with LDAP, you specify
the attribute used as a user ID in the LDAP server configuration
(for example, sasl-regexp in OpenLDAP).

In my case, the user ID a user enters is not the attribute
specified as SASL authentication user ID: a user enters
his or her mail user name, but the LDAP server accepts
only emploee IDs for the SASL authentication (and could not
configure the LDAP server to use mail user names as SASL
authentication ID for some reasons) .

To work with the above case, my patched saslauthd authenticates
a user as following:

1. First, binds the LDAP server with ldap_id or ldap_bind_dn
2. Searches for the user object to the authenticate,
   with the ID the user entered.
3. Retrieves the attribute in the user object, which can use
   for the SASL authentication.
4. Binds with the retrieved SASL user ID and the password
   the user entered.

This works like the bind method do, but can be used with SASL.
I'm happy if I can use this feature in the upstream cyrus-sasl.

-- 
IKEDA Yasuyuki <[email protected]>
cyrus-sasl-2.1.25-saslbind.patch (application/octet-stream, 6 KB)
diff -uNr cyrus-sasl-2.1.25.orig/saslauthd/lak.c cyrus-sasl-2.1.25/saslauthd/lak.c
--- cyrus-sasl-2.1.25.orig/saslauthd/lak.c	2009-12-04 04:07:03.000000000 +0900
+++ cyrus-sasl-2.1.25/saslauthd/lak.c	2012-11-04 00:58:23.843750000 +0900
@@ -91,6 +91,7 @@
 static int lak_auth_custom(LAK *, const char *, const char *, const char *, const char *);
 static int lak_auth_bind(LAK *, const char *, const char *, const char *, const char *);
 static int lak_auth_fastbind(LAK *, const char *, const char *, const char *, const char *);
+static int lak_auth_saslbind(LAK *, const char *, const char *, const char *, const char *);
 static int lak_group_member(LAK *, const char *, const char *, const char *, const char *);
 static char *lak_result_get(const LAK_RESULT *, const char *);
 static int lak_result_add(const char *, const char *, LAK_RESULT **);
@@ -110,6 +111,7 @@
 	{ LAK_AUTH_METHOD_BIND, lak_auth_bind },
 	{ LAK_AUTH_METHOD_CUSTOM, lak_auth_custom },
 	{ LAK_AUTH_METHOD_FASTBIND, lak_auth_fastbind },
+	{ LAK_AUTH_METHOD_SASLBIND, lak_auth_saslbind },
 	{ -1, NULL }
 };
 
@@ -235,6 +237,8 @@
 				conf->auth_method = LAK_AUTH_METHOD_CUSTOM;
 			} else if (!strcasecmp(p, "fastbind")) {
 				conf->auth_method = LAK_AUTH_METHOD_FASTBIND;
+			} else if (!strcasecmp(p, "saslbind")) {
+				conf->auth_method = LAK_AUTH_METHOD_SASLBIND;
 			}
 		} else if (!strcasecmp(key, "ldap_timeout")) {
 			conf->timeout.tv_sec = lak_config_int(p);
@@ -278,6 +282,9 @@
                  !strcasecmp(key, "ldap_sasl_authz_id"))
 			strlcpy(conf->authz_id, p, LAK_BUF_LEN);
 
+		else if (!strcasecmp(key, "ldap_sasl_username_attr"))
+			strlcpy(conf->sasl_username_attr, p, LAK_BUF_LEN);
+
 		else if (!strcasecmp(key, "ldap_realm") ||
                  !strcasecmp(key, "ldap_sasl_realm"))
 			strlcpy(conf->realm, p, LAK_BUF_LEN);
@@ -323,6 +330,10 @@
         conf->auth_method == LAK_AUTH_METHOD_BIND)
         conf->auth_method = LAK_AUTH_METHOD_FASTBIND;
 
+    if (!conf->use_sasl &&
+        conf->auth_method == LAK_AUTH_METHOD_SASLBIND)
+        conf->auth_method = LAK_AUTH_METHOD_BIND;
+
     if ( ISSET(conf->group_filter) &&
          ISSET(conf->search_base) &&
          EMPTY(conf->group_search_base) )
@@ -389,6 +400,7 @@
 	conf->restart = 1;
 	conf->start_tls = 0;
 	conf->use_sasl = 0;
+	strlcpy(conf->sasl_username_attr, "cn", LAK_BUF_LEN);
 
 	strlcpy(conf->path, configfile, LAK_PATH_LEN);
 
@@ -1509,6 +1521,50 @@
 
 	return rc;
 }
+
+static int lak_auth_saslbind(
+	LAK *lak,
+	const char *user,
+	const char *service,
+	const char *realm,
+	const char *password) 
+{
+	LAK_USER *lu = NULL;
+	LAK_RESULT *lres;
+	int rc;
+	const char *attrs[] = { lak->conf->sasl_username_attr, NULL};
+
+	rc = lak_retrieve(lak, user, service, realm, attrs, &lres);
+	if (rc != LAK_OK)
+		return rc;
+
+	syslog(LOG_INFO|LOG_AUTH, "Mapped user: %s -> %s", user, lres->value);
+	rc = lak_user(	
+		NULL,
+		lres->value,
+		NULL,
+		lak->conf->mech,
+		lak->conf->realm,
+		password,
+		&lu);
+	if (rc != LAK_OK)
+		goto done;
+
+	rc = lak_bind(lak, lu);
+
+	if ( rc == LAK_OK &&
+	    (ISSET(lak->conf->group_dn) ||
+         ISSET(lak->conf->group_filter)) )
+            rc = lak_group_member(lak, user, service, realm, NULL);
+
+done:;
+	if (lu)
+		lak_user_free(lu);
+	if (lres != NULL)
+		lak_result_free(lres);
+
+	return rc;
+}
 
 int lak_authenticate(
 	LAK *lak,
diff -uNr cyrus-sasl-2.1.25.orig/saslauthd/lak.h cyrus-sasl-2.1.25/saslauthd/lak.h
--- cyrus-sasl-2.1.25.orig/saslauthd/lak.h	2004-02-25 06:56:31.000000000 +0900
+++ cyrus-sasl-2.1.25/saslauthd/lak.h	2012-10-31 21:43:37.078125000 +0900
@@ -58,6 +58,7 @@
 #define LAK_AUTH_METHOD_BIND 0
 #define LAK_AUTH_METHOD_CUSTOM 1
 #define LAK_AUTH_METHOD_FASTBIND 2
+#define LAK_AUTH_METHOD_SASLBIND 3
 
 #define LAK_GROUP_MATCH_METHOD_ATTR 0
 #define LAK_GROUP_MATCH_METHOD_FILTER 1
@@ -84,6 +85,7 @@
     char   search_base[LAK_DN_LEN];
     char   filter[LAK_DN_LEN];
     char   password_attr[LAK_BUF_LEN];
+    char   sasl_username_attr[LAK_BUF_LEN];
     char   group_dn[LAK_DN_LEN];
     char   group_attr[LAK_BUF_LEN];
     char   group_filter[LAK_DN_LEN];
diff -uNr cyrus-sasl-2.1.25.orig/saslauthd/LDAP_SASLAUTHD cyrus-sasl-2.1.25/saslauthd/LDAP_SASLAUTHD
--- cyrus-sasl-2.1.25.orig/saslauthd/LDAP_SASLAUTHD	2004-02-25 06:56:31.000000000 +0900
+++ cyrus-sasl-2.1.25/saslauthd/LDAP_SASLAUTHD	2012-11-04 22:27:01.535024200 +0900
@@ -82,7 +82,7 @@
 within the first set of <>.  There may be a second set of <> which provide
 available values.
 
-ldap_auth_method: <bind|fastbind> <bind|custom|fastbind>
+ldap_auth_method: <bind|fastbind> <bind|custom|fastbind|saslbind>
 	Specify an authentication method.
 
     The bind method uses the LDAP bind facility to verify the password.  The
@@ -101,6 +101,18 @@
 	  1. Expanding the ldap_filter expression gives the user's fully-qualified DN
 	  2. There is no cost to staying bound as a named user
 
+    The saslbind method works as following:
+	  1. binds using SASL with ldap_id (or ldap_bind_dn) and ldap_password.
+	  2. searches for the user using ldap_search_base and ldap_filter
+	     as the bind method do, and retrieves the username from the attribute
+	     specified with ldap_sasl_username_attr.
+	  3. binds using SASL with the retrieved username and the passed password.
+    Generally, this method has a performance disadvantage. It is prefered to
+    configure your ldap server to map SASL user ID to DN as you want (for example,
+    by adding a new realm and sasl-regexp).
+    The saslbind method is not available when ldap_use_sasl is turned off. In that
+    case the bind method is used.
+
 ldap_bind_dn: <none>
 	Specify DN (distinguished name) to bind to the LDAP directory.  Do not
 	specify this parameter for the anonymous bind.
@@ -236,6 +248,10 @@
 	Use SASL bind rather than simple bind when connecting to the ldap
 	server.
 
+ldap_sasl_username_attr: <cn>
+	Specify what attribute to use for SASL authentication.
+	For example, use sAMAccountName with Active Directory.
+
 ldap_version: <3> <2|3>
 	Specify the LDAP protocol version.  If ldap_start_tls and/or
 	ldap_use_sasl are enabled, ldap_version will be automatiacally set to
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.