Skip to content
Snippets Groups Projects
sasl.c 46.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
     *
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
     * Copyright 1998-2011 The OpenLDAP Foundation.
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted only as authorized by the OpenLDAP
     * Public License.
     *
     * A copy of this license is available in the file LICENSE in the
     * top-level directory of the distribution or, alternatively, at
     * <http://www.OpenLDAP.org/license.html>.
    
    #include "portable.h"
    
    #include <stdio.h>
    
    #ifdef HAVE_LIMITS_H
    #include <limits.h>
    #endif
    
    
    #include <ac/stdlib.h>
    #include <ac/string.h>
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    #include "slap.h"
    
    
    #ifdef ENABLE_REWRITE
    #include <rewrite.h>
    #endif
    
    
    #ifdef HAVE_CYRUS_SASL
    # ifdef HAVE_SASL_SASL_H
    #  include <sasl/sasl.h>
    #  include <sasl/saslplug.h>
    
    # else
    
    #  include <sasl.h>
    
    #  include <saslplug.h>
    # endif
    
    
    # define	SASL_CONST const
    
    #define SASL_VERSION_FULL	((SASL_VERSION_MAJOR << 16) |\
    	(SASL_VERSION_MINOR << 8) | SASL_VERSION_STEP)
    
    
    static sasl_security_properties_t sasl_secprops;
    
    #elif defined( SLAP_BUILTIN_SASL )
    
    /*
     * built-in SASL implementation
     *	only supports EXTERNAL
     */
    typedef struct sasl_ctx {
    	slap_ssf_t sc_external_ssf;
    	struct berval sc_external_id;
    } SASL_CTX;
    
    #endif
    
    #include <lutil.h>
    
    static struct berval ext_bv = BER_BVC( "EXTERNAL" );
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    char *slap_sasl_auxprops;
    
    
    #ifdef HAVE_CYRUS_SASL
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    /* Just use our internal auxprop by default */
    static int
    slap_sasl_getopt(
    	void *context,
    	const char *plugin_name,
    	const char *option,
    	const char **result,
    	unsigned *len)
    {
    	if ( strcmp( option, "auxprop_plugin" )) {
    		return SASL_FAIL;
    	}
    	if ( slap_sasl_auxprops )
    		*result = slap_sasl_auxprops;
    	else
    		*result = "slapd";
    	return SASL_OK;
    }
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    slap_sasl_log(
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	void *context,
    	int priority,
    	const char *message) 
    {
    	Connection *conn = context;
    	int level;
    	const char * label;
    
    	if ( message == NULL ) {
    		return SASL_BADPARAM;
    	}
    
    	switch (priority) {
    
    	case SASL_LOG_NONE:
    		level = LDAP_DEBUG_NONE;
    		label = "None";
    		break;
    	case SASL_LOG_ERR:
    		level = LDAP_DEBUG_ANY;
    		label = "Error";
    		break;
    	case SASL_LOG_FAIL:
    		level = LDAP_DEBUG_ANY;
    		label = "Failure";
    		break;
    	case SASL_LOG_WARN:
    		level = LDAP_DEBUG_TRACE;
    		label = "Warning";
    		break;
    	case SASL_LOG_NOTE:
    		level = LDAP_DEBUG_TRACE;
    		label = "Notice";
    		break;
    	case SASL_LOG_DEBUG:
    		level = LDAP_DEBUG_TRACE;
    		label = "Debug";
    		break;
    	case SASL_LOG_TRACE:
    		level = LDAP_DEBUG_TRACE;
    		label = "Trace";
    		break;
    	case SASL_LOG_PASS:
    		level = LDAP_DEBUG_TRACE;
    		label = "Password Trace";
    		break;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	default:
    		return SASL_BADPARAM;
    	}
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	Debug( level, "SASL [conn=%ld] %s: %s\n",
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		conn ? (long) conn->c_connid: -1L,
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		label, message );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    	return SASL_OK;
    }
    
    
    static const char *slap_propnames[] = {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	"*slapConn", "*slapAuthcDNlen", "*slapAuthcDN",
    	"*slapAuthzDNlen", "*slapAuthzDN", NULL };
    
    Hallvard Furuseth's avatar
    Hallvard Furuseth committed
    static Filter generic_filter = { LDAP_FILTER_PRESENT, { 0 }, NULL };
    
    static struct berval generic_filterstr = BER_BVC("(objectclass=*)");
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    #define	SLAP_SASL_PROP_AUTHCLEN	1
    #define	SLAP_SASL_PROP_AUTHC	2
    #define	SLAP_SASL_PROP_AUTHZLEN	3
    #define	SLAP_SASL_PROP_AUTHZ	4
    #define	SLAP_SASL_PROP_COUNT	5	/* Number of properties we used */
    
    
    typedef struct lookup_info {
    	int flags;
    	const struct propval *list;
    	sasl_server_params_t *sparams;
    } lookup_info;
    
    
    static slap_response sasl_ap_lookup;
    
    static struct berval sc_cleartext = BER_BVC("{CLEARTEXT}");
    
    
    sasl_ap_lookup( Operation *op, SlapReply *rs )
    
    {
    	BerVarray bv;
    	AttributeDescription *ad;
    	Attribute *a;
    	const char *text;
    	int rc, i;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	lookup_info *sl = (lookup_info *)op->o_callback->sc_private;
    
    	if (rs->sr_type != REP_SEARCH) return 0;
    
    
    Howard Chu's avatar
    Howard Chu committed
    	for( i = 0; sl->list[i].name; i++ ) {
    
    		const char *name = sl->list[i].name;
    
    		if ( name[0] == '*' ) {
    			if ( sl->flags & SASL_AUXPROP_AUTHZID ) continue;
    
    Howard Chu's avatar
    Howard Chu committed
    			/* Skip our private properties */
    			if ( !strcmp( name, slap_propnames[0] )) {
    
    Howard Chu's avatar
    Howard Chu committed
    				continue;
    			}
    
    			name++;
    		} else if ( !(sl->flags & SASL_AUXPROP_AUTHZID ) )
    			continue;
    
    		if ( sl->list[i].values ) {
    			if ( !(sl->flags & SASL_AUXPROP_OVERRIDE) ) continue;
    		}
    		ad = NULL;
    		rc = slap_str2ad( name, &ad, &text );
    		if ( rc != LDAP_SUCCESS ) {
    			Debug( LDAP_DEBUG_TRACE,
    
    				"slap_ap_lookup: str2ad(%s): %s\n", name, text, 0 );
    
    
    		/* If it's the rootdn and a rootpw was present, we already set
    		 * it so don't override it here.
    		 */
    		if ( ad == slap_schema.si_ad_userPassword && sl->list[i].values && 
    			be_isroot_dn( op->o_bd, &op->o_req_ndn ))
    			continue;
    
    
    		a = attr_find( rs->sr_entry->e_attrs, ad );
    
    		if ( ! access_allowed( op, rs->sr_entry, ad, NULL, ACL_AUTH, NULL ) ) {
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		}
    		if ( sl->list[i].values && ( sl->flags & SASL_AUXPROP_OVERRIDE ) ) {
    			sl->sparams->utils->prop_erase( sl->sparams->propctx,
    			sl->list[i].name );
    		}
    
    		for ( bv = a->a_vals; bv->bv_val; bv++ ) {
    
    			/* ITS#3846 don't give hashed passwords to SASL */
    			if ( ad == slap_schema.si_ad_userPassword &&
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    				bv->bv_val[0] == '{' /*}*/ )
    			{
    				if ( lutil_passwd_scheme( bv->bv_val ) ) {
    
    					/* If it's not a recognized scheme, just assume it's
    					 * a cleartext password that happened to include brackets.
    					 *
    					 * If it's a recognized scheme, skip this value, unless the
    					 * scheme is {CLEARTEXT}. In that case, skip over the
    					 * scheme name and use the remainder. If there is nothing
    					 * past the scheme name, skip this value.
    					 */
    #ifdef SLAPD_CLEARTEXT
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    					if ( !strncasecmp( bv->bv_val, sc_cleartext.bv_val,
    						sc_cleartext.bv_len )) {
    
    						struct berval cbv;
    						cbv.bv_len = bv->bv_len - sc_cleartext.bv_len;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    						if ( cbv.bv_len > 0 ) {
    
    							cbv.bv_val = bv->bv_val + sc_cleartext.bv_len;
    							sl->sparams->utils->prop_set( sl->sparams->propctx,
    								sl->list[i].name, cbv.bv_val, cbv.bv_len );
    						}
    					}
    #endif
    					continue;
    				}
    			}
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			sl->sparams->utils->prop_set( sl->sparams->propctx,
    				sl->list[i].name, bv->bv_val, bv->bv_len );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    #if SASL_VERSION_FULL >= 0x020118
    static int
    #else
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    #endif
    
    slap_auxprop_lookup(
    	void *glob_context,
    	sasl_server_params_t *sparams,
    	unsigned flags,
    	const char *user,
    	unsigned ulen)
    {
    
    	OperationBuffer opbuf = {{ NULL }};
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	Operation *op = (Operation *)&opbuf;
    
    	int i, doit = 0;
    
    	Connection *conn = NULL;
    	lookup_info sl;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	int rc = LDAP_SUCCESS;
    
    
    	sl.list = sparams->utils->prop_get( sparams->propctx );
    	sl.sparams = sparams;
    	sl.flags = flags;
    
    	/* Find our DN and conn first */
    
    Howard Chu's avatar
    Howard Chu committed
    	for( i = 0; sl.list[i].name; i++ ) {
    
    		if ( sl.list[i].name[0] == '*' ) {
    
    			if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_CONN] ) ) {
    
    				if ( sl.list[i].values && sl.list[i].values[0] )
    					AC_MEMCPY( &conn, sl.list[i].values[0], sizeof( conn ) );
    
    Howard Chu's avatar
    Howard Chu committed
    				continue;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			if ( flags & SASL_AUXPROP_AUTHZID ) {
    				if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHZLEN] )) {
    					if ( sl.list[i].values && sl.list[i].values[0] )
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    						AC_MEMCPY( &op->o_req_ndn.bv_len, sl.list[i].values[0],
    							sizeof( op->o_req_ndn.bv_len ) );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				} else if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHZ] )) {
    					if ( sl.list[i].values )
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    						op->o_req_ndn.bv_val = (char *)sl.list[i].values[0];
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    					break;
    				}
    			}
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHCLEN] )) {
    
    				if ( sl.list[i].values && sl.list[i].values[0] )
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    					AC_MEMCPY( &op->o_req_ndn.bv_len, sl.list[i].values[0],
    						sizeof( op->o_req_ndn.bv_len ) );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			} else if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHC] ) ) {
    				if ( sl.list[i].values ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    					op->o_req_ndn.bv_val = (char *)sl.list[i].values[0];
    
    					if ( !(flags & SASL_AUXPROP_AUTHZID) )
    						break;
    				}
    
    	/* Now see what else needs to be fetched */
    
    Howard Chu's avatar
    Howard Chu committed
    	for( i = 0; sl.list[i].name; i++ ) {
    
    		const char *name = sl.list[i].name;
    
    
    		if ( name[0] == '*' ) {
    			if ( flags & SASL_AUXPROP_AUTHZID ) continue;
    
    Howard Chu's avatar
    Howard Chu committed
    			/* Skip our private properties */
    			if ( !strcmp( name, slap_propnames[0] )) {
    
    Howard Chu's avatar
    Howard Chu committed
    				continue;
    			}
    
    			name++;
    		} else if ( !(flags & SASL_AUXPROP_AUTHZID ) )
    			continue;
    
    
    			if ( !(flags & SASL_AUXPROP_OVERRIDE) ) continue;
    		}
    
    Howard Chu's avatar
    Howard Chu committed
    		break;
    
    		slap_callback cb = { NULL, sasl_ap_lookup, NULL, NULL };
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		op->o_bd = select_backend( &op->o_req_ndn, 1 );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		if ( op->o_bd ) {
    
    			/* For rootdn, see if we can use the rootpw */
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) &&
    				!BER_BVISEMPTY( &op->o_bd->be_rootpw )) {
    
    				struct berval cbv = BER_BVNULL;
    
    				/* If there's a recognized scheme, see if it's CLEARTEXT */
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				if ( lutil_passwd_scheme( op->o_bd->be_rootpw.bv_val )) {
    					if ( !strncasecmp( op->o_bd->be_rootpw.bv_val,
    
    						sc_cleartext.bv_val, sc_cleartext.bv_len )) {
    
    						/* If it's CLEARTEXT, skip past scheme spec */
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    						cbv.bv_len = op->o_bd->be_rootpw.bv_len -
    
    							sc_cleartext.bv_len;
    						if ( cbv.bv_len ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    							cbv.bv_val = op->o_bd->be_rootpw.bv_val +
    
    								sc_cleartext.bv_len;
    						}
    					}
    				/* No scheme, use the whole value */
    				} else {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    					cbv = op->o_bd->be_rootpw;
    
    				}
    				if ( !BER_BVISEMPTY( &cbv )) {
    					for( i = 0; sl.list[i].name; i++ ) {
    						const char *name = sl.list[i].name;
    
    						if ( name[0] == '*' ) {
    							if ( flags & SASL_AUXPROP_AUTHZID ) continue;
    								name++;
    						} else if ( !(flags & SASL_AUXPROP_AUTHZID ) )
    							continue;
    
    						if ( !strcasecmp(name,"userPassword") ) {
    							sl.sparams->utils->prop_set( sl.sparams->propctx,
    								sl.list[i].name, cbv.bv_val, cbv.bv_len );
    							break;
    						}
    					}
    				}
    			}
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			if ( op->o_bd->be_search ) {
    
    				SlapReply rs = {REP_RESULT};
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				op->o_hdr = conn->c_sasl_bindop->o_hdr;
    				op->o_controls = opbuf.ob_controls;
    				op->o_tag = LDAP_REQ_SEARCH;
    				op->o_dn = conn->c_ndn;
    				op->o_ndn = conn->c_ndn;
    				op->o_callback = &cb;
    				slap_op_time( &op->o_time, &op->o_tincr );
    				op->o_do_not_cache = 1;
    				op->o_is_auth_check = 1;
    				op->o_req_dn = op->o_req_ndn;
    				op->ors_scope = LDAP_SCOPE_BASE;
    				op->ors_deref = LDAP_DEREF_NEVER;
    				op->ors_tlimit = SLAP_NO_LIMIT;
    				op->ors_slimit = 1;
    				op->ors_filter = &generic_filter;
    				op->ors_filterstr = generic_filterstr;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				op->o_authz = conn->c_authz;
    
    				/* FIXME: we want all attributes, right? */
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				op->ors_attrs = NULL;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				rc = op->o_bd->be_search( op, &rs );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    #if SASL_VERSION_FULL >= 0x020118
    	return rc != LDAP_SUCCESS ? SASL_FAIL : SASL_OK;
    #endif
    
    #if SASL_VERSION_FULL >= 0x020110
    
    static int
    slap_auxprop_store(
    	void *glob_context,
    	sasl_server_params_t *sparams,
    	struct propctx *prctx,
    	const char *user,
    	unsigned ulen)
    {
    	Operation op = {0};
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	Opheader oph;
    
    	SlapReply rs = {REP_RESULT};
    
    	int rc, i;
    	unsigned j;
    
    	Connection *conn = NULL;
    	const struct propval *pr;
    	Modifications *modlist = NULL, **modtail = &modlist, *mod;
    
    	slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
    
    	char textbuf[SLAP_TEXT_BUFLEN];
    	const char *text;
    	size_t textlen = sizeof(textbuf);
    
    	/* just checking if we are enabled */
    	if (!prctx) return SASL_OK;
    
    	if (!sparams || !user) return SASL_BADPARAM;
    
    	pr = sparams->utils->prop_get( sparams->propctx );
    
    	/* Find our DN and conn first */
    	for( i = 0; pr[i].name; i++ ) {
    		if ( pr[i].name[0] == '*' ) {
    
    			if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_CONN] ) ) {
    
    				if ( pr[i].values && pr[i].values[0] )
    					AC_MEMCPY( &conn, pr[i].values[0], sizeof( conn ) );
    				continue;
    			}
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_AUTHCLEN] )) {
    				if ( pr[i].values && pr[i].values[0] )
    					AC_MEMCPY( &op.o_req_ndn.bv_len, pr[i].values[0],
    						sizeof( op.o_req_ndn.bv_len ) );
    			} else if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_AUTHC] ) ) {
    				if ( pr[i].values )
    					op.o_req_ndn.bv_val = (char *)pr[i].values[0];
    
    			}
    		}
    	}
    	if (!conn || !op.o_req_ndn.bv_val) return SASL_BADPARAM;
    
    
    	op.o_bd = select_backend( &op.o_req_ndn, 1 );
    
    
    	if ( !op.o_bd || !op.o_bd->be_modify ) return SASL_FAIL;
    		
    	pr = sparams->utils->prop_get( prctx );
    	if (!pr) return SASL_BADPARAM;
    
    	for (i=0; pr[i].name; i++);
    	if (!i) return SASL_BADPARAM;
    
    	for (i=0; pr[i].name; i++) {
    		mod = (Modifications *)ch_malloc( sizeof(Modifications) );
    		mod->sml_op = LDAP_MOD_REPLACE;
    
    		ber_str2bv( pr[i].name, 0, 0, &mod->sml_type );
    
    		mod->sml_numvals = pr[i].nvalues;
    
    		mod->sml_values = (struct berval *)ch_malloc( (pr[i].nvalues + 1) *
    			sizeof(struct berval));
    		for (j=0; j<pr[i].nvalues; j++) {
    			ber_str2bv( pr[i].values[j], 0, 1, &mod->sml_values[j]);
    		}
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    		BER_BVZERO( &mod->sml_values[j] );
    
    		mod->sml_nvalues = NULL;
    		mod->sml_desc = NULL;
    		*modtail = mod;
    		modtail = &mod->sml_next;
    	}
    	*modtail = NULL;
    
    
    	rc = slap_mods_check( &op, modlist, &text, textbuf, textlen, NULL );
    
    
    	if ( rc == LDAP_SUCCESS ) {
    
    		rc = slap_mods_no_user_mod_check( &op, modlist,
    			&text, textbuf, textlen );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			if ( conn->c_sasl_bindop ) {
    				op.o_hdr = conn->c_sasl_bindop->o_hdr;
    			} else {
    				op.o_hdr = &oph;
    				memset( &oph, 0, sizeof(oph) );
    				operation_fake_init( conn, &op, ldap_pvt_thread_pool_context(), 0 );
    			}
    
    			op.o_tag = LDAP_REQ_MODIFY;
    			op.o_ndn = op.o_req_ndn;
    			op.o_callback = &cb;
    			slap_op_time( &op.o_time, &op.o_tincr );
    			op.o_do_not_cache = 1;
    			op.o_is_auth_check = 1;
    			op.o_req_dn = op.o_req_ndn;
    			op.orm_modlist = modlist;
    
    			rc = op.o_bd->be_modify( &op, &rs );
    
    	slap_mods_free( modlist, 1 );
    
    	return rc != LDAP_SUCCESS ? SASL_FAIL : SASL_OK;
    
    #endif /* SASL_VERSION_FULL >= 2.1.16 */
    
    static sasl_auxprop_plug_t slap_auxprop_plugin = {
    	0,	/* Features */
    	0,	/* spare */
    	NULL,	/* glob_context */
    	NULL,	/* auxprop_free */
    	slap_auxprop_lookup,
    	"slapd",	/* name */
    
    #if SASL_VERSION_FULL >= 0x020110
    
    	slap_auxprop_store	/* the declaration of this member changed
    				 * in cyrus SASL from 2.1.15 to 2.1.16 */
    
    #else
    	NULL
    #endif
    
    };
    
    static int
    slap_auxprop_init(
    	const sasl_utils_t *utils,
    	int max_version,
    	int *out_version,
    	sasl_auxprop_plug_t **plug,
    	const char *plugname)
    {
    
    	if ( !out_version || !plug ) return SASL_BADPARAM;
    
    
    	if ( max_version < SASL_AUXPROP_PLUG_VERSION ) return SASL_BADVERS;
    
    	*out_version = SASL_AUXPROP_PLUG_VERSION;
    	*plug = &slap_auxprop_plugin;
    	return SASL_OK;
    }
    
    /* Convert a SASL authcid or authzid into a DN. Store the DN in an
     * auxiliary property, so that we can refer to it in sasl_authorize
     * without interfering with anything else. Also, the SASL username
     * buffer is constrained to 256 characters, and our DNs could be
    
     * much longer (SLAP_LDAPDN_MAXLEN, currently set to 8192)
    
    static int
    slap_sasl_canonicalize(
    	sasl_conn_t *sconn,
    	void *context,
    	const char *in,
    	unsigned inlen,
    	unsigned flags,
    	const char *user_realm,
    	char *out,
    	unsigned out_max,
    	unsigned *out_len)
    {
    	Connection *conn = (Connection *)context;
    
    	struct propctx *props = sasl_auxprop_getctx( sconn );
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	struct propval auxvals[ SLAP_SASL_PROP_COUNT ] = { { 0 } };
    
    	struct berval dn;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	struct berval	bvin;
    
    	Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: %s=\"%s\"\n",
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		conn ? (long) conn->c_connid : -1L,
    
    		(flags & SASL_CU_AUTHID) ? "authcid" : "authzid",
    		in ? in : "<empty>");
    
    	/* If name is too big, just truncate. We don't care, we're
    	 * using DNs, not the usernames.
    	 */
    
    	/* This is a Simple Bind using SPASSWD. That means the in-directory
    	 * userPassword of the Binding user already points at SASL, so it
    	 * cannot be used to actually satisfy a password comparison. Just
    	 * ignore it, some other mech will process it.
    	 */
    	if ( !conn->c_sasl_bindop ||
    		conn->c_sasl_bindop->orb_method != LDAP_AUTH_SASL ) goto done;
    
    
    	/* See if we need to add request, can only do it once */
    	prop_getnames( props, slap_propnames, auxvals );
    	if ( !auxvals[0].name )
    		prop_request( props, slap_propnames );
    
    	if ( flags & SASL_CU_AUTHID )
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		which = SLAP_SASL_PROP_AUTHCLEN;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		which = SLAP_SASL_PROP_AUTHZLEN;
    
    	/* Need to store the Connection for auxprop_lookup */
    
    	if ( !auxvals[SLAP_SASL_PROP_CONN].values ) {
    		names[0] = slap_propnames[SLAP_SASL_PROP_CONN];
    
    		names[1] = NULL;
    		prop_set( props, names[0], (char *)&conn, sizeof( conn ) );
    	}
    		
    
    	/* Already been here? */
    	if ( auxvals[which].values )
    		goto done;
    
    	/* Normally we require an authzID to have a u: or dn: prefix.
    	 * However, SASL frequently gives us an authzID that is just
    	 * an exact copy of the authcID, without a prefix. We need to
    	 * detect and allow this condition. If SASL calls canonicalize
    	 * with SASL_CU_AUTHID|SASL_CU_AUTHZID this is a no-brainer.
    	 * But if it's broken into two calls, we need to remember the
    	 * authcID so that we can compare the authzID later. We store
    	 * the authcID temporarily in conn->c_sasl_dn. We necessarily
    	 * finish Canonicalizing before Authorizing, so there is no
    	 * conflict with slap_sasl_authorize's use of this temp var.
    
    	 *
    	 * The SASL EXTERNAL mech is backwards from all the other mechs,
    	 * it does authzID before the authcID. If we see that authzID
    	 * has already been done, don't do anything special with authcID.
    
    	if ( flags == SASL_CU_AUTHID && !auxvals[SLAP_SASL_PROP_AUTHZ].values ) {
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		conn->c_sasl_dn.bv_val = (char *) in;
    
    		conn->c_sasl_dn.bv_len = 0;
    
    	} else if ( flags == SASL_CU_AUTHZID && conn->c_sasl_dn.bv_val ) {
    		rc = strcmp( in, conn->c_sasl_dn.bv_val );
    		conn->c_sasl_dn.bv_val = NULL;
    		/* They were equal, no work needed */
    		if ( !rc ) goto done;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	bvin.bv_val = (char *)in;
    	bvin.bv_len = inlen;
    	rc = slap_sasl_getdn( conn, NULL, &bvin, (char *)user_realm, &dn,
    
    		(flags & SASL_CU_AUTHID) ? SLAP_GETDN_AUTHCID : SLAP_GETDN_AUTHZID );
    
    	if ( rc != LDAP_SUCCESS ) {
    		sasl_seterror( sconn, 0, ldap_err2string( rc ) );
    		return SASL_NOAUTHZ;
    
    	names[0] = slap_propnames[which];
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	prop_set( props, names[0], (char *)&dn.bv_len, sizeof( dn.bv_len ) );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	which++;
    	names[0] = slap_propnames[which];
    	prop_set( props, names[0], dn.bv_val, dn.bv_len );
    
    	Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: %s=\"%s\"\n",
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		conn ? (long) conn->c_connid : -1L, names[0]+1,
    
    		dn.bv_val ? dn.bv_val : "<EMPTY>" );
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	/* Not needed any more, SASL has copied it */
    	if ( conn && conn->c_sasl_bindop )
    		conn->c_sasl_bindop->o_tmpfree( dn.bv_val, conn->c_sasl_bindop->o_tmpmemctx );
    
    
    done:
    	AC_MEMCPY( out, in, inlen );
    
    
    	return SASL_OK;
    }
    
    static int
    slap_sasl_authorize(
    	sasl_conn_t *sconn,
    	void *context,
    
    	unsigned rlen,
    
    	unsigned alen,
    	const char *def_realm,
    	unsigned urlen,
    
    {
    	Connection *conn = (Connection *)context;
    
    	/* actually:
    	 *	(SLAP_SASL_PROP_COUNT - 1)	because we skip "conn",
    	 *	+ 1				for NULL termination?
    	 */
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	struct propval auxvals[ SLAP_SASL_PROP_COUNT ] = { { 0 } };
    
    	struct berval authcDN, authzDN = BER_BVNULL;
    
    	/* Simple Binds don't support proxy authorization, ignore it */
    	if ( !conn->c_sasl_bindop ||
    		conn->c_sasl_bindop->orb_method != LDAP_AUTH_SASL ) return SASL_OK;
    
    
    	Debug( LDAP_DEBUG_ARGS, "SASL proxy authorize [conn=%ld]: "
    
    		"authcid=\"%s\" authzid=\"%s\"\n",
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		conn ? (long) conn->c_connid : -1L, auth_identity, requested_user );
    
    	if ( conn->c_sasl_dn.bv_val ) {
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    		BER_BVZERO( &conn->c_sasl_dn );
    
    	prop_getnames( props, slap_propnames+1, auxvals );
    
    	/* Should not happen */
    	if ( !auxvals[0].values ) {
    		sasl_seterror( sconn, 0, "invalid authcid" );
    		return SASL_NOAUTHZ;
    	}
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	AC_MEMCPY( &authcDN.bv_len, auxvals[0].values[0], sizeof(authcDN.bv_len) );
    	authcDN.bv_val = auxvals[1].values ? (char *)auxvals[1].values[0] : NULL;
    
    	/* Nothing to do if no authzID was given */
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	if ( !auxvals[2].name || !auxvals[2].values ) {
    
    Howard Chu's avatar
    Howard Chu committed
    		goto ok;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	AC_MEMCPY( &authzDN.bv_len, auxvals[2].values[0], sizeof(authzDN.bv_len) );
    	authzDN.bv_val = auxvals[3].values ? (char *)auxvals[3].values[0] : NULL;
    
    	rc = slap_sasl_authorized( conn->c_sasl_bindop, &authcDN, &authzDN );
    
    	if ( rc != LDAP_SUCCESS ) {
    
    		Debug( LDAP_DEBUG_TRACE, "SASL Proxy Authorize [conn=%ld]: "
    			"proxy authorization disallowed (%d)\n",
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			conn ? (long) conn->c_connid : -1L, rc, 0 );
    
    
    		sasl_seterror( sconn, 0, "not authorized" );
    		return SASL_NOAUTHZ;
    	}
    
    
    	/* FIXME: we need yet another dup because slap_sasl_getdn()
    	 * is using the bind operation slab */
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	ber_dupbv( &conn->c_sasl_authz_dn, &authzDN );
    
    Howard Chu's avatar
    Howard Chu committed
    ok:
    
    Howard Chu's avatar
    Howard Chu committed
    	if (conn->c_sasl_bindop) {
    		Statslog( LDAP_DEBUG_STATS,
    
    			"%s BIND authcid=\"%s\" authzid=\"%s\"\n",
    			conn->c_sasl_bindop->o_log_prefix, 
    			auth_identity, requested_user, 0, 0 );
    
    Howard Chu's avatar
    Howard Chu committed
    	}
    
    	Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
    
    		" proxy authorization allowed authzDN=\"%s\"\n",
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		conn ? (long) conn->c_connid : -1L, 
    
    		authzDN.bv_val ? authzDN.bv_val : "", 0 );
    
    	return SASL_OK;
    } 
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    static int
    slap_sasl_err2ldap( int saslerr )
    {
    	int rc;
    
    
    	/* map SASL errors to LDAP resultCode returned by:
    	 *	sasl_server_new()
    	 *		SASL_OK, SASL_NOMEM
    	 *	sasl_server_step()
    	 *		SASL_OK, SASL_CONTINUE, SASL_TRANS, SASL_BADPARAM, SASL_BADPROT,
    	 *      ...
    	 *	sasl_server_start()
    	 *      + SASL_NOMECH
    	 *	sasl_setprop()
    	 *		SASL_OK, SASL_BADPARAM
    	 */
    
    
    		case SASL_OK:
    			rc = LDAP_SUCCESS;
    			break;
    
    		case SASL_CONTINUE:
    			rc = LDAP_SASL_BIND_IN_PROGRESS;
    			break;
    		case SASL_FAIL:
    		case SASL_NOMEM:
    			rc = LDAP_OTHER;
    			break;
    		case SASL_NOMECH:
    			rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
    			break;
    		case SASL_BADAUTH:
    
    		case SASL_NOUSER:
    		case SASL_TRANS:
    		case SASL_EXPIRED:
    
    			rc = LDAP_INVALID_CREDENTIALS;
    			break;
    		case SASL_NOAUTHZ:
    			rc = LDAP_INSUFFICIENT_ACCESS;
    			break;
    		case SASL_TOOWEAK:
    		case SASL_ENCRYPT:
    			rc = LDAP_INAPPROPRIATE_AUTH;
    			break;
    
    		case SASL_UNAVAIL:
    		case SASL_TRYAGAIN:
    			rc = LDAP_UNAVAILABLE;
    			break;
    		case SASL_DISABLED:
    			rc = LDAP_UNWILLING_TO_PERFORM;
    			break;
    
    
    #ifdef SLAPD_SPASSWD
    
    static struct berval sasl_pwscheme = BER_BVC("{SASL}");
    
    static int chk_sasl(
    	const struct berval *sc,
    	const struct berval * passwd,
    	const struct berval * cred,
    	const char **text )
    {
    	unsigned int i;
    	int rtn;
    	void *ctx, *sconn = NULL;
    
    	for( i=0; i<cred->bv_len; i++) {
    		if(cred->bv_val[i] == '\0') {
    			return LUTIL_PASSWD_ERR;	/* NUL character in password */
    		}
    	}
    
    	if( cred->bv_val[i] != '\0' ) {
    		return LUTIL_PASSWD_ERR;	/* cred must behave like a string */
    	}
    
    	for( i=0; i<passwd->bv_len; i++) {
    		if(passwd->bv_val[i] == '\0') {
    			return LUTIL_PASSWD_ERR;	/* NUL character in password */
    		}
    	}
    
    	if( passwd->bv_val[i] != '\0' ) {
    		return LUTIL_PASSWD_ERR;	/* passwd must behave like a string */
    	}
    
    	rtn = LUTIL_PASSWD_ERR;
    
    	ctx = ldap_pvt_thread_pool_context();
    
    	ldap_pvt_thread_pool_getkey( ctx, (void *)slap_sasl_bind, &sconn, NULL );
    
    
    	if( sconn != NULL ) {
    		int sc;
    		sc = sasl_checkpass( sconn,
    			passwd->bv_val, passwd->bv_len,
    			cred->bv_val, cred->bv_len );
    		rtn = ( sc != SASL_OK ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
    	}
    
    	return rtn;
    }
    #endif /* SLAPD_SPASSWD */
    
    #endif /* HAVE_CYRUS_SASL */
    
    #ifdef ENABLE_REWRITE
    
    typedef struct slapd_map_data {
    	struct berval base;
    	struct berval filter;
    	AttributeName attrs[2];
    	int scope;
    } slapd_map_data;
    
    static void *
    slapd_rw_config( const char *fname, int lineno, int argc, char **argv )
    {
    	slapd_map_data *ret = NULL;
    	LDAPURLDesc *lud = NULL;
    	char *uri;
    	AttributeDescription *ad = NULL;
    	int rc, flen = 0;
    	struct berval dn, ndn;
    
    	if ( argc != 1 ) {
    		Debug( LDAP_DEBUG_ANY,
    			"[%s:%d] slapd map needs URI\n",
    			fname, lineno, 0 );
            return NULL;
    	}
    
    	uri = argv[0];
    	if ( strncasecmp( uri, "uri=", STRLENOF( "uri=" ) ) == 0 ) {
    		uri += STRLENOF( "uri=" );
    	}
    
    	if ( ldap_url_parse( uri, &lud ) != LDAP_URL_SUCCESS ) {
    		Debug( LDAP_DEBUG_ANY,
    			"[%s:%d] illegal URI '%s'\n",
    			fname, lineno, uri );
            return NULL;
    	}
    
    	if ( strcasecmp( lud->lud_scheme, "ldap" )) {
    		Debug( LDAP_DEBUG_ANY,
    			"[%s:%d] illegal URI scheme '%s'\n",
    			fname, lineno, lud->lud_scheme );
    		goto done;
    	}
    
    	if (( lud->lud_host && lud->lud_host[0] ) || lud->lud_exts
    		|| !lud->lud_dn ) {
    		Debug( LDAP_DEBUG_ANY,
    			"[%s:%d] illegal URI '%s'\n",
    			fname, lineno, uri );
    		goto done;
    	}
    
    	if ( lud->lud_attrs ) {
    		if ( lud->lud_attrs[1] ) {
    			Debug( LDAP_DEBUG_ANY,
    				"[%s:%d] only one attribute allowed in URI\n",
    				fname, lineno, 0 );
    			goto done;
    		}
    		if ( strcasecmp( lud->lud_attrs[0], "dn" ) &&
    			strcasecmp( lud->lud_attrs[0], "entryDN" )) {
    			const char *text;
    			rc = slap_str2ad( lud->lud_attrs[0], &ad, &text );
    			if ( rc )
    				goto done;
    		}
    	}
    	ber_str2bv( lud->lud_dn, 0, 0, &dn );
    	if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ))
    		goto done;
    
    	if ( lud->lud_filter ) {
    		flen = strlen( lud->lud_filter ) + 1;
    	}
    	ret = ch_malloc( sizeof( slapd_map_data ) + flen );
    	ret->base = ndn;
    	if ( flen ) {
    		ret->filter.bv_val = (char *)(ret+1);
    		ret->filter.bv_len = flen - 1;
    		strcpy( ret->filter.bv_val, lud->lud_filter );
    	} else {
    		BER_BVZERO( &ret->filter );
    	}
    	ret->scope = lud->lud_scope;
    	if ( ad ) {
    		ret->attrs[0].an_name = ad->ad_cname;
    	} else {
    		BER_BVZERO( &ret->attrs[0].an_name );
    	}
    	ret->attrs[0].an_desc = ad;
    	BER_BVZERO( &ret->attrs[1].an_name );
    done:
    	ldap_free_urldesc( lud );
    	return ret;
    }
    
    struct slapd_rw_info {
    	slapd_map_data *si_data;
    	struct berval si_val;
    };
    
    static int
    slapd_rw_cb( Operation *op, SlapReply *rs )
    {
    	if ( rs->sr_type == REP_SEARCH ) {
    		struct slapd_rw_info *si = op->o_callback->sc_private;
    
    		if ( si->si_data->attrs[0].an_desc ) {
    			Attribute *a;
    
    			a = attr_find( rs->sr_entry->e_attrs,
    				si->si_data->attrs[0].an_desc );
    			if ( a ) {
    				ber_dupbv( &si->si_val, a->a_vals );
    			}
    		} else {
    			ber_dupbv( &si->si_val, &rs->sr_entry->e_name );
    		}