Sponsoring a canon_user plugin for LDAP lookup

Torsten Schlabach tschlabach at gmx.net
Mon Feb 19 16:20:13 EST 2007


Hi Howard,

after adding

sasl_canon_user_plugin: ldapdb

to my imapd.conf I get one step further.

But there seems to be a problem, most likely in this code fragment here:

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

What's happening:

The canonicalized user name is not properly truncated. So for example, 
what's entered is: "someone" which is supposed to come back with 
"cntst". What you get back is "ntstone".

I really wondered what nstone might be, until I figured that

- the length of the input is kept even if the canonicalized name is shorter
- the first character of the canonicalized name is omitted.

So from cntst you get ntst which gets copied over someone which makes 
ntstone.

Could you have a 2nd look at you code maybe? I am just a good enough C 
programmer to sort this out myself.

Regards,
Torsten

Howard Chu schrieb:
> 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.
> 
> 
> ------------------------------------------------------------------------
> 
> 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