slap_sasl_match( Operation *opx, struct berval *rule,
	struct berval *assertDN, struct berval *authc )
{
	int rc; 
	regex_t reg;
	smatch_info sm;
	slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
	Operation op = {0};
	SlapReply rs = {REP_RESULT};
	struct berval base = BER_BVNULL;

	sm.dn = assertDN;
	sm.match = 0;
	cb.sc_private = &sm;

	Debug( LDAP_DEBUG_TRACE,
	   "===>slap_sasl_match: comparing DN %s to rule %s\n",
		assertDN->bv_len ? assertDN->bv_val : "(null)", rule->bv_val, 0 );

	/* NOTE: don't normalize rule if authz syntax is enabled */
	rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
		&op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 );

	if( rc != LDAP_SUCCESS ) goto CONCLUDED;

	switch ( op.ors_scope ) {
	case LDAP_X_SCOPE_EXACT:
exact_match:
		if ( dn_match( &op.o_req_ndn, assertDN ) ) {
			rc = LDAP_SUCCESS;
		} else {
			rc = LDAP_INAPPROPRIATE_AUTH;
		}
		goto CONCLUDED;

	case LDAP_X_SCOPE_CHILDREN:
	case LDAP_X_SCOPE_SUBTREE:
	case LDAP_X_SCOPE_ONELEVEL:
	{
		int	d = assertDN->bv_len - op.o_req_ndn.bv_len;

		rc = LDAP_INAPPROPRIATE_AUTH;

		if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
			goto exact_match;

		} else if ( d > 0 ) {
			struct berval bv;

			/* leave room for at least one char of attributeType,
			 * one for '=' and one for ',' */
			if ( d < (int) STRLENOF( "x=,") ) {
				goto CONCLUDED;
			}

			bv.bv_len = op.o_req_ndn.bv_len;
			bv.bv_val = assertDN->bv_val + d;

			if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
				switch ( op.ors_scope ) {
				case LDAP_X_SCOPE_SUBTREE:
				case LDAP_X_SCOPE_CHILDREN:
					rc = LDAP_SUCCESS;
					break;

				case LDAP_X_SCOPE_ONELEVEL:
				{
					struct berval	pdn;

					dnParent( assertDN, &pdn );
					/* the common portion of the DN
					 * already matches, so only check
					 * if parent DN of assertedDN 
					 * is all the pattern */
					if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
						rc = LDAP_SUCCESS;
					}
					break;
				}
				default:
					/* at present, impossible */
					assert( 0 );
				}
			}
		}
		goto CONCLUDED;
	}

	case LDAP_X_SCOPE_REGEX:
		rc = regcomp(&reg, op.o_req_ndn.bv_val,
			REG_EXTENDED|REG_ICASE|REG_NOSUB);
		if ( rc == 0 ) {
			rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
			regfree( &reg );
		}
		if ( rc == 0 ) {
			rc = LDAP_SUCCESS;
		} else {
			rc = LDAP_INAPPROPRIATE_AUTH;
		}
		goto CONCLUDED;

	case LDAP_X_SCOPE_GROUP: {
		char	*tmp;

		/* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
		 * we need to append the <assertDN> so that the <group_dn> is searched
		 * with scope "base", and the filter ensures that <assertDN> is
		 * member of the group */
		tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
			assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
		if ( tmp == NULL ) {
			rc = LDAP_NO_MEMORY;
			goto CONCLUDED;
		}
		op.ors_filterstr.bv_val = tmp;
		
		tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
		tmp = lutil_strcopy( tmp, /*"(("*/ "))" );

		/* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
		op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
		if ( op.ors_filter == NULL ) {
			rc = LDAP_PROTOCOL_ERROR;
			goto CONCLUDED;
		}
		op.ors_scope = LDAP_SCOPE_BASE;

		/* hijack match DN: use that of the group instead of the assertDN;
		 * assertDN is now in the filter */
		sm.dn = &op.o_req_ndn;

		/* do the search */
		break;
		}

	case LDAP_X_SCOPE_USERS:
		if ( !BER_BVISEMPTY( assertDN ) ) {
			rc = LDAP_SUCCESS;
		} else {
			rc = LDAP_INAPPROPRIATE_AUTH;
		}
		goto CONCLUDED;

	default:
		break;
	}

	/* Must run an internal search. */
	if ( op.ors_filter == NULL ) {
		rc = LDAP_FILTER_ERROR;
		goto CONCLUDED;
	}

	Debug( LDAP_DEBUG_TRACE,
	   "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
	   op.o_req_ndn.bv_val, op.ors_scope, 0 );

	op.o_bd = select_backend( &op.o_req_ndn, 1 );
	if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
		rc = LDAP_INAPPROPRIATE_AUTH;
		goto CONCLUDED;
	}

	op.o_hdr = opx->o_hdr;
	op.o_tag = LDAP_REQ_SEARCH;
	op.o_ndn = *authc;
	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;
	/* use req_ndn as req_dn instead of non-pretty base of uri */
	if( !BER_BVISNULL( &base ) ) {
		ch_free( base.bv_val );
		/* just in case... */
		BER_BVZERO( &base );
	}
	ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
	op.ors_deref = LDAP_DEREF_NEVER;
	op.ors_slimit = 1;
	op.ors_tlimit = SLAP_NO_LIMIT;
	op.ors_attrs = slap_anlist_no_attrs;
	op.ors_attrsonly = 1;

	op.o_bd->be_search( &op, &rs );

	if (sm.match) {
		rc = LDAP_SUCCESS;
	} else {
		rc = LDAP_INAPPROPRIATE_AUTH;
	}

CONCLUDED:
	if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
	if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
	if( op.ors_filter ) filter_free_x( opx, op.ors_filter, 1 );
	if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );

	Debug( LDAP_DEBUG_TRACE,
	   "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);

	return( rc );
}