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
      */

Reply via email to