On Wed, Sep 15, 2010 at 07:34:51AM -0700, Quanah Gibson-Mount wrote:
> I wrote a patch to support SASL mechanisms when connecting to LDAP many
> years ago, and maintained for for a time. However, I left the job where I
> required it back in 2007, and didn't do much with it since that point in
> time. I have submitted that work to Victor Duchovni of the Postfix
> project, and it is my understanding he is looking at having it included in
> a future release. Stanford University may still have the original work I
> did on Postfix for this, since it was a requirement for their Postfix
> deployment. You may wish to ping them.
I have the patch, it has not yet been fully reviewed/integrated. If
anyone wants to test it "as is", it is attached.
--
Viktor.
--- proto/LDAP_README.html 6 Feb 2010 07:34:26 -0000 1.1.1.1
+++ proto/LDAP_README.html 9 Sep 2010 17:35:14 -0000
@@ -128,6 +128,16 @@
or whatever you have, and you may need to use the appropriate linker
option (e.g. '-R') so the executables can find it at runtime. </p>
+<p> If you are using OpenLDAP, and the libraries were built with SASL
+support, you can add -DUSE_LDAP_SASL to the CCARGS to enable SASL support.
+For example: </p>
+
+<blockquote>
+<pre>
+ CCARGS="-I/usr/local/include -DHAS_LDAP -DUSE_LDAP_SASL"
+</pre>
+</blockquote>
+
<h2><a name="config">Configuring LDAP lookups</a></h2>
<p> In order to use LDAP lookups, define an LDAP source
@@ -140,9 +150,9 @@
</blockquote>
<p> The file /etc/postfix/ldap-aliases.cf can specify a great number
-of parameters, including parameters that enable LDAP SSL and
-STARTTLS. For a complete description, see the ldap_table(5) manual
-page. </p>
+of parameters, including parameters that enable LDAP SSL or STARTTLS,
+and LDAP SASL. For a complete description, see the ldap_table(5)
+manual page. </p>
<h2><a name="example_alias">Example: local(8) aliases</a></h2>
@@ -588,6 +598,9 @@
limit LDAP search results to leaf nodes only. Victor generalized
this into the Postfix 2.4 "leaf_result_attribute" feature. </li>
+<li>Quanah Gibson-Mount contributed support for advanced LDAP SASL
+mechanisms, beyond the password-based LDAP "simple" bind. </li>
+
</ul>
And of course Wietse.
--- proto/ldap_table.orig 2008-07-23 11:20:19.000000000 -0700
+++ proto/ldap_table 2008-07-23 11:23:13.000000000 -0700
@@ -470,6 +470,29 @@
# Specifies the LDAP protocol version to use.
# .IP "\fBdebuglevel (default: 0)\fR"
# What level to set for debugging in the OpenLDAP libraries.
+# LDAP SASL PARAMETERS
+# .ad
+# .fi
+# If you're using the OpenLDAP libraries compiled with SASL
+# support, Postfix can connect to LDAP servers using SASL
+# mechanisms.
+#
+# Using SASL mechanisms requires LDAP protocol version 3, which
+# is the default.
+#
+# The following parameters are relevant to using LDAP with
+# SASL
+# .IP "\fBsasl (default: no)\fR"
+# Whether or not to use SASL binds to the server. Can be yes or no.
+# .IP "\fBsasl_mechs (No default)\fR"
+# Space separated list of SASL mechanism(s) to try.
+# .IP "\fsasl_realm (No default)\fR"
+# SASL Realm to use, if applicable.
+# .IP "\fsasl_authz_id (No default)\fR"
+# The SASL authorization identity to assert, if applicable.
+# .IP "\fsasl_minssf (default: 0)\fR"
+# The minimum required sasl security factor required to
+# establish a connection.
# LDAP SSL AND STARTTLS PARAMETERS
# .ad
# .fi
--- src/global/dict_ldap.c.orig 2008-01-25 16:50:59.000000000 -0800
+++ src/global/dict_ldap.c 2008-07-24 15:53:30.000000000 -0700
@@ -103,6 +103,16 @@
/* .IP version
/* Specifies the LDAP protocol version to use. Default is version
/* \fI2\fR.
+/* .IP sasl
+/* Whether or not to use SASL binds with the server. Defaults to no.
+/* .IP sasl_mechs
+/* Specifies the LDAP SASL Mechanisms to use. Should be space seperated.
+/* .IP sasl_realm
+/* The realm to use for SASL binds.
+/* .IP sasl_authz_id
+/* The SASL Authorization Identity to assert.
+/* .IP sasl_minssf
+/* The minimum SASL SSF to allow. Default is 0.
/* .IP start_tls
/* Whether or not to issue STARTTLS upon connection to the server.
/* At this time, STARTTLS and LDAP SSL are only available if the
@@ -214,6 +224,12 @@
#include "cfg_parser.h"
#include "db_common.h"
+#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
+/* SASL Library header, for sasl_interact_t */
+/* Both SASL v1 and v2 should be fine */
+#include <sasl.h>
+#endif
+
/* Application-specific. */
#include "dict_ldap.h"
@@ -254,6 +270,13 @@
int debuglevel;
int version;
#ifdef LDAP_API_FEATURE_X_OPENLDAP
+#if defined(USE_LDAP_SASL)
+ int sasl;
+ char *sasl_mechs;
+ char *sasl_realm;
+ char *sasl_authz_id;
+ int sasl_minssf;
+#endif
int ldap_ssl;
int start_tls;
int tls_require_cert;
@@ -407,6 +430,54 @@
return rc;
}
+#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
+/* We need to set up a structure, and a function to handle the
+ * SASL callbacks
+ */
+
+typedef struct bictx {
+ char *authcid;
+ char *passwd;
+ char *realm;
+ char *authzid;
+} bictx;
+
+static int
+ldap_b2_interact(LDAP *ld, unsigned flags, void *def, void *inter)
+{
+
+ sasl_interact_t *in = inter;
+ const char *p;
+ bictx *ctx = def;
+
+ for (;in->id != SASL_CB_LIST_END;in++)
+ {
+ p = NULL;
+ switch(in->id)
+ {
+ case SASL_CB_GETREALM:
+ p = ctx->realm;
+ break;
+ case SASL_CB_AUTHNAME:
+ p = ctx->authcid;
+ break;
+ case SASL_CB_USER:
+ p = ctx->authzid;
+ break;
+ case SASL_CB_PASS:
+ p = ctx->passwd;
+ break;
+ }
+ if (p)
+ {
+ in->len = strlen(p);
+ in->result = p;
+ }
+ }
+ return LDAP_SUCCESS;
+}
+#endif
+
/* dict_ldap_result - Read and parse LDAP result */
static int dict_ldap_result(LDAP *ld, int msgid, int timeout, LDAPMessage
**res)
@@ -427,8 +498,47 @@
return LDAP_SUCCESS;
}
-/* dict_ldap_bind_st - Synchronous simple auth with timeout */
+#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
+/* Asynchronous SASL auth with timeout if SASL is enabled */
+static int dict_ldap_bind_sasl(DICT_LDAP *dict_ldap)
+{
+ int rc;
+ bictx ctx;
+ struct timeval mytimeval;
+ VSTRING *minssf;
+
+ if (minssf == 0)
+ minssf = vstring_alloc(128);
+ mytimeval.tv_sec = dict_ldap->timeout;
+ mytimeval.tv_usec = 0;
+
+ vstring_sprintf(minssf,"minssf=%d",dict_ldap->sasl_minssf);
+
+ if ((rc = ldap_set_option(dict_ldap->ld, LDAP_OPT_X_SASL_SECPROPS,
+ (char *) minssf)) != LDAP_OPT_SUCCESS)
+ return (rc);
+
+ if (dict_ldap->timeout > 0 ) {
+ if ((rc = ldap_set_option(dict_ldap->ld, LDAP_OPT_NETWORK_TIMEOUT,
&mytimeval)) != LDAP_OPT_SUCCESS)
+ return (rc);
+ }
+
+ ctx.authcid = dict_ldap->bind_dn;
+ ctx.passwd = dict_ldap->bind_pw;
+ ctx.realm = dict_ldap->sasl_realm;
+ ctx.authzid = dict_ldap->sasl_authz_id;
+
+ if ((rc = ldap_sasl_interactive_bind_s(dict_ldap->ld, NULL,
+ dict_ldap->sasl_mechs, NULL, NULL,
+ LDAP_SASL_QUIET, ldap_b2_interact, &ctx)) !=
LDAP_SUCCESS)
+ return (rc);
+
+ return (LDAP_SUCCESS);
+}
+#endif
+
+/* dict_ldap_bind_st - Synchronous simple auth with timeout */
static int dict_ldap_bind_st(DICT_LDAP *dict_ldap)
{
int rc;
@@ -740,7 +850,15 @@
msg_info("%s: Binding to server %s as dn %s",
myname, dict_ldap->server_host, dict_ldap->bind_dn);
+#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
+ if(dict_ldap->sasl) {
+ rc = dict_ldap_bind_sasl(dict_ldap);
+ } else {
+ rc = dict_ldap_bind_st(dict_ldap);
+ }
+#else
rc = dict_ldap_bind_st(dict_ldap);
+#endif
if (rc != LDAP_SUCCESS) {
msg_warn("%s: Unable to bind to server %s as %s: %d (%s)",
@@ -790,6 +908,13 @@
ADDINT(keybuf, dict_ldap->debuglevel);
ADDINT(keybuf, dict_ldap->version);
#ifdef LDAP_API_FEATURE_X_OPENLDAP
+#if defined(USE_LDAP_SASL)
+ ADDINT(keybuf, dict_ldap->sasl);
+ ADDSTR(keybuf, dict_ldap->sasl ? dict_ldap->sasl_mechs : "");
+ ADDSTR(keybuf, dict_ldap->sasl ? dict_ldap->sasl_realm : "");
+ ADDSTR(keybuf, dict_ldap->sasl ? dict_ldap->sasl_authz_id : "");
+ ADDINT(keybuf, dict_ldap->sasl_minssf);
+#endif
ADDINT(keybuf, dict_ldap->ldap_ssl);
ADDINT(keybuf, dict_ldap->start_tls);
ADDINT(keybuf, sslon ? dict_ldap->tls_require_cert : 0);
@@ -1310,6 +1435,11 @@
if (dict_ldap->ctx)
db_common_free_ctx(dict_ldap->ctx);
#ifdef LDAP_API_FEATURE_X_OPENLDAP
+#if defined(USE_LDAP_SASL)
+ myfree(dict_ldap->sasl_mechs);
+ myfree(dict_ldap->sasl_realm);
+ myfree(dict_ldap->sasl_authz_id);
+#endif
myfree(dict_ldap->tls_ca_cert_file);
myfree(dict_ldap->tls_ca_cert_dir);
myfree(dict_ldap->tls_cert);
@@ -1596,7 +1726,17 @@
"chase_referrals", 0);
#ifdef LDAP_API_FEATURE_X_OPENLDAP
+#if defined(USE_LDAP_SASL)
+ /*
+ * SASL options
+ */
+ dict_ldap->sasl = cfg_get_bool(dict_ldap->parser, "sasl", 0);
+ dict_ldap->sasl_mechs = cfg_get_str(dict_ldap->parser, "sasl_mechs", "",
0, 0);
+ dict_ldap->sasl_realm = cfg_get_str(dict_ldap->parser, "sasl_realm", "",
0, 0);
+ dict_ldap->sasl_authz_id = cfg_get_str(dict_ldap->parser, "sasl_authz_id",
"", 0, 0);
+ dict_ldap->sasl_minssf= cfg_get_int(dict_ldap->parser, "sasl_minssf", 0,
0, 4096);
+#endif
/*
* TLS options
*/