Sponsoring a canon_user plugin for LDAP lookup
Howard Chu
hyc at highlandsun.com
Fri Jan 12 20:07:09 EST 2007
Torsten Schlabach wrote:
> > And now just to doublecheck - you actually want just the simple name
> > returned, not the full DN. Correct?
>
> Sure. As this is what Cyrus IMAPd expects.
>
> Without the canon_user, you'd type
>
> AUTH id000012
>
> You wouldn't type
>
> AUTH uid=id000012,ou=user,ou=...
>
> It's getting 1:00 a.m. now where I am. So I hope you got all the info
> and I can go to bed now. I'd really be thankful for you help with
> that. Any probably many others as well.
>
The attached patch should do what you want. Add "ldapdb_canon_attr: uid"
to your configuration to use it.
--
-- Howard Chu
Chief Architect, Symas Corp. http://www.symas.com
Director, Highland Sun http://highlandsun.com/hyc
OpenLDAP Core Team http://www.openldap.org/project/
-------------- next part --------------
Index: doc/options.html
===================================================================
RCS file: /cvs/src/sasl/doc/options.html,v
retrieving revision 1.30
diff -u -r1.30 options.html
--- doc/options.html 16 Feb 2005 20:52:05 -0000 1.30
+++ doc/options.html 13 Jan 2007 01:05:49 -0000
@@ -83,6 +83,14 @@
<TD>none</TD>
</TR>
<TR>
+<TD>ldapdb_canon_attr</TD><TD>LDAPDB plugin</TD>
+<TD>Use the value of the specified attribute as the user's
+canonical name. The attribute will be looked up in the user's LDAP
+entry. This setting must be configured in order to use LDAPDB as
+a canonuser plugin.</TD>
+<TD>none</TD>
+</TR>
+<TR>
<TD>log_level</TD><TD>SASL Library</TD>
<TD><b>Numeric</b> Logging Level (see <TT>SASL_LOG_*</TT> in <tt>sasl.h</tt>
for values and descriptions</TD>
@@ -273,7 +281,7 @@
</pre>
is a valid value for <tt>sql_select</tt>.
-<h2>Notes on LDAPDB auxprop options</h2>
+<h2>Notes on LDAPDB plugin options</h2>
<p>
</p>
@@ -286,7 +294,8 @@
makes the configuration of remote services much simpler.</p>
<p>This plugin is not for use with slapd itself. When OpenLDAP is
-built with SASL support, slapd uses its own internal auxprop module.
+built with SASL support, slapd uses its own internal auxprop and
+canonuser module.
By default, without configuring anything else, slapd will fail to load
the ldapdb module when it's present. This is as it should be. If you
don't like the "auxpropfunc: error -7" message that is sent to syslog
@@ -303,6 +312,7 @@
ldapdb_id: root
ldapdb_pw: secret
ldapdb_mech: DIGEST-MD5
+ldapdb_canon_attr: uid
</pre>
<p>The LDAP server must be configured to map the SASL authcId "root" into a DN
Index: plugins/ldapdb.c
===================================================================
RCS file: /cvs/src/sasl/plugins/ldapdb.c,v
retrieving revision 1.2
diff -u -r1.2 ldapdb.c
--- plugins/ldapdb.c 19 Feb 2005 02:26:31 -0000 1.2
+++ plugins/ldapdb.c 13 Jan 2007 01:05:49 -0000
@@ -1,6 +1,6 @@
/* $OpenLDAP: pkg/ldap/contrib/ldapsasl/ldapdb.c,v 1.1.2.7 2003/11/29 22:10:03 hyc Exp $ */
-/* SASL LDAP auxprop implementation
- * Copyright (C) 2002,2003 Howard Chu, All rights reserved. <hyc at symas.com>
+/* SASL LDAP auxprop+canonuser implementation
+ * Copyright (C) 2002-2007 Howard Chu, All rights reserved. <hyc at symas.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
@@ -14,6 +14,7 @@
#include <config.h>
#include <stdio.h>
+#include <ctype.h>
#include "sasl.h"
#include "saslutil.h"
@@ -26,13 +27,17 @@
static char ldapdb[] = "ldapdb";
typedef struct ldapctx {
+ int inited; /* Have we already read the config? */
const char *uri; /* URI of LDAP server */
struct berval id; /* SASL authcid to bind as */
struct berval pw; /* password for bind */
struct berval mech; /* SASL mech */
int use_tls; /* Issue StartTLS request? */
+ struct berval canon; /* Use attr in user entry for canonical name */
} ldapctx;
+static ldapctx ldapdb_ctx;
+
static int ldapdb_interact(LDAP *ld, unsigned flags __attribute__((unused)),
void *def, void *inter)
{
@@ -79,7 +84,7 @@
char *authzid;
if((i=ldap_initialize(&cp->ld, ctx->uri))) {
- return i;
+ return i;
}
authzid = sparams->utils->malloc(ulen + sizeof("u:"));
@@ -203,7 +208,7 @@
done:
if(attrs) sparams->utils->free(attrs);
- if(cp.ld) ldap_unbind(cp.ld);
+ if(cp.ld) ldap_unbind_ext(cp.ld, NULL, NULL);
}
static int ldapdb_auxprop_store(void *glob_context,
@@ -254,58 +259,133 @@
if (i == LDAP_NO_MEMORY) i = SASL_NOMEM;
else i = SASL_FAIL;
}
- if (cp.ld) ldap_unbind(cp.ld);
+ if(cp.ld) ldap_unbind_ext(cp.ld, NULL, NULL);
return i;
}
-static void ldapdb_auxprop_free(void *glob_ctx, const sasl_utils_t *utils)
+static int
+ldapdb_canon_server(void *glob_context,
+ sasl_server_params_t *sparams,
+ const char *user,
+ unsigned ulen,
+ unsigned flags,
+ char *out,
+ unsigned out_max,
+ unsigned *out_ulen)
{
- utils->free(glob_ctx);
-}
+ ldapctx *ctx = glob_context;
+ connparm cp;
+ struct berval **bvals;
+ LDAPMessage *msg, *res;
+ char *rdn, *attrs[2];
+ unsigned len;
+ int ret;
+
+ if(!ctx || !sparams || !user) return SASL_BADPARAM;
-static sasl_auxprop_plug_t ldapdb_auxprop_plugin = {
- 0, /* Features */
- 0, /* spare */
- NULL, /* glob_context */
- ldapdb_auxprop_free, /* auxprop_free */
- ldapdb_auxprop_lookup, /* auxprop_lookup */
- ldapdb, /* name */
- ldapdb_auxprop_store /* auxprop store */
-};
+ /* If no canon attribute was configured, we can't do anything */
+ if(!ctx->canon.bv_val) return SASL_BADPARAM;
-int ldapdb_auxprop_plug_init(const sasl_utils_t *utils,
- int max_version,
- int *out_version,
- sasl_auxprop_plug_t **plug,
- const char *plugname __attribute__((unused)))
+ /* Trim whitespace */
+ while(isspace(*(unsigned char *)user)) {
+ user++;
+ ulen--;
+ }
+ while(isspace((unsigned char)user[ulen-1])) {
+ ulen--;
+ }
+
+ if (!ulen) {
+ sparams->utils->seterror(sparams->utils->conn, 0,
+ "All-whitespace username.");
+ return SASL_FAIL;
+ }
+
+ ret = ldapdb_connect(ctx, sparams, user, ulen, &cp);
+ if ( ret ) goto done;
+
+ /* See if the RDN uses the canon attr. If so, just use the RDN
+ * value, we don't need to do a search.
+ */
+ rdn = cp.dn->bv_val+3;
+ if (!strncasecmp(ctx->canon.bv_val, rdn, ctx->canon.bv_len) &&
+ rdn[ctx->canon.bv_len] == '=') {
+ char *comma;
+ rdn += ctx->canon.bv_len + 2;
+ comma = strchr(rdn, ',');
+ if ( comma )
+ len = comma - rdn;
+ else
+ len = cp.dn->bv_len - (rdn - cp.dn->bv_val);
+ if ( len > out_max )
+ len = out_max;
+ memcpy(out, rdn, len);
+ *out_ulen = len;
+ ret = SASL_OK;
+ ber_bvfree(cp.dn);
+ goto done;
+ }
+
+ /* Have to read the user's entry */
+ attrs[0] = ctx->canon.bv_val;
+ attrs[1] = NULL;
+ ret = ldap_search_ext_s(cp.ld, cp.dn->bv_val+3, LDAP_SCOPE_BASE,
+ "(objectclass=*)", attrs, 0, cp.ctrl, NULL, NULL, 1, &res);
+ ber_bvfree(cp.dn);
+
+ if (ret != LDAP_SUCCESS) goto done;
+
+ for(msg=ldap_first_message(cp.ld, res); msg; msg=ldap_next_message(cp.ld, msg))
+ {
+ if (ldap_msgtype(msg) != LDAP_RES_SEARCH_ENTRY) continue;
+ bvals = ldap_get_values_len(cp.ld, msg, attrs[0]);
+ if (!bvals) continue;
+ len = bvals[0]->bv_len;
+ if ( len > out_max )
+ len = out_max;
+ memcpy(out, bvals[0]->bv_val, len);
+ *out_ulen = len;
+ ber_bvecfree(bvals);
+ }
+ ldap_msgfree(res);
+
+ done:
+ if(cp.ld) ldap_unbind_ext(cp.ld, NULL, NULL);
+ if (ret) {
+ sparams->utils->seterror(sparams->utils->conn, 0,
+ ldap_err2string(ret));
+ if (ret == LDAP_NO_MEMORY) ret = SASL_NOMEM;
+ else ret = SASL_FAIL;
+ }
+ return ret;
+}
+
+static int
+ldapdb_config(const sasl_utils_t *utils)
{
- ldapctx tmp, *p;
+ ldapctx *p = &ldapdb_ctx;
const char *s;
unsigned len;
- if(!out_version || !plug) return SASL_BADPARAM;
-
- if(max_version < SASL_AUXPROP_PLUG_VERSION) return SASL_BADVERS;
-
- memset(&tmp, 0, sizeof(tmp));
+ if(p->inited) return SASL_OK;
- utils->getopt(utils->getopt_context, ldapdb, "ldapdb_uri", &tmp.uri, NULL);
- if(!tmp.uri) return SASL_BADPARAM;
+ utils->getopt(utils->getopt_context, ldapdb, "ldapdb_uri", &p->uri, NULL);
+ if(!p->uri) return SASL_BADPARAM;
utils->getopt(utils->getopt_context, ldapdb, "ldapdb_id",
- (const char **)&tmp.id.bv_val, &len);
- tmp.id.bv_len = len;
+ (const char **)&p->id.bv_val, &len);
+ p->id.bv_len = len;
utils->getopt(utils->getopt_context, ldapdb, "ldapdb_pw",
- (const char **)&tmp.pw.bv_val, &len);
- tmp.pw.bv_len = len;
+ (const char **)&p->pw.bv_val, &len);
+ p->pw.bv_len = len;
utils->getopt(utils->getopt_context, ldapdb, "ldapdb_mech",
- (const char **)&tmp.mech.bv_val, &len);
- tmp.mech.bv_len = len;
+ (const char **)&p->mech.bv_val, &len);
+ p->mech.bv_len = len;
utils->getopt(utils->getopt_context, ldapdb, "ldapdb_starttls", &s, NULL);
if (s)
{
- if (!strcasecmp(s, "demand")) tmp.use_tls = 2;
- else if (!strcasecmp(s, "try")) tmp.use_tls = 1;
+ if (!strcasecmp(s, "demand")) p->use_tls = 2;
+ else if (!strcasecmp(s, "try")) p->use_tls = 1;
}
utils->getopt(utils->getopt_context, ldapdb, "ldapdb_rc", &s, &len);
if (s)
@@ -320,15 +400,75 @@
return SASL_NOMEM;
}
}
+ utils->getopt(utils->getopt_context, ldapdb, "ldapdb_canon_attr",
+ (const char **)&p->canon.bv_val, &len);
+ p->canon.bv_len = len;
+ p->inited = 1;
- p = utils->malloc(sizeof(ldapctx));
- if (!p) return SASL_NOMEM;
- *p = tmp;
- ldapdb_auxprop_plugin.glob_context = p;
+ return SASL_OK;
+}
+
+static sasl_auxprop_plug_t ldapdb_auxprop_plugin = {
+ 0, /* Features */
+ 0, /* spare */
+ &ldapdb_ctx, /* glob_context */
+ NULL, /* auxprop_free */
+ ldapdb_auxprop_lookup, /* auxprop_lookup */
+ ldapdb, /* name */
+ ldapdb_auxprop_store /* auxprop store */
+};
+
+int ldapdb_auxprop_plug_init(const sasl_utils_t *utils,
+ int max_version,
+ int *out_version,
+ sasl_auxprop_plug_t **plug,
+ const char *plugname __attribute__((unused)))
+{
+ int rc;
+
+ if(!out_version || !plug) return SASL_BADPARAM;
+
+ if(max_version < SASL_AUXPROP_PLUG_VERSION) return SASL_BADVERS;
+
+ rc = ldapdb_config(utils);
*out_version = SASL_AUXPROP_PLUG_VERSION;
*plug = &ldapdb_auxprop_plugin;
- return SASL_OK;
+ return rc;
+}
+
+static sasl_canonuser_plug_t ldapdb_canonuser_plugin = {
+ 0, /* features */
+ 0, /* spare */
+ &ldapdb_ctx, /* glob_context */
+ ldapdb, /* name */
+ NULL, /* canon_user_free */
+ ldapdb_canon_server, /* canon_user_server */
+ NULL, /* canon_user_client */
+ NULL,
+ NULL,
+ NULL
+};
+
+int ldapdb_canonuser_plug_init(const sasl_utils_t *utils,
+ int max_version,
+ int *out_version,
+ sasl_canonuser_plug_t **plug,
+ const char *plugname __attribute__((unused)))
+{
+ int rc;
+
+ if(!out_version || !plug) return SASL_BADPARAM;
+
+ if(max_version < SASL_CANONUSER_PLUG_VERSION) return SASL_BADVERS;
+
+ rc = ldapdb_config(utils);
+
+ *out_version = SASL_CANONUSER_PLUG_VERSION;
+
+ *plug = &ldapdb_canonuser_plugin;
+
+ return rc;
}
Index: plugins/makeinit.sh
===================================================================
RCS file: /cvs/src/sasl/plugins/makeinit.sh,v
retrieving revision 1.10
diff -u -r1.10 makeinit.sh
--- plugins/makeinit.sh 16 Feb 2005 20:52:12 -0000 1.10
+++ plugins/makeinit.sh 13 Jan 2007 01:05:49 -0000
@@ -45,7 +45,7 @@
" > ${mech}_init.c
done
-for mech in sasldb sql ldapdb; do
+for mech in sasldb sql; do
echo "
#include <config.h>
@@ -87,3 +87,46 @@
" > ${mech}_init.c
done
+for mech in ldapdb; do
+
+echo "
+#include <config.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#ifndef macintosh
+#include <sys/stat.h>
+#endif
+#include <fcntl.h>
+#include <assert.h>
+
+#include <sasl.h>
+#include <saslplug.h>
+#include <saslutil.h>
+
+#include \"plugin_common.h\"
+
+#ifdef WIN32
+BOOL APIENTRY DllMain( HANDLE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+#endif
+
+SASL_AUXPROP_PLUG_INIT( $mech )
+SASL_CANONUSER_PLUG_INIT( $mech )
+" > ${mech}_init.c
+done
+
More information about the Cyrus-sasl
mailing list