]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
Implement slapi_entry_has_children()
[openldap] / servers / slapd / saslauthz.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2004 The OpenLDAP Foundation.
5  * Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/stdlib.h>
22 #include <ac/string.h>
23 #include <ac/ctype.h>
24
25 #include "slap.h"
26
27 #include <limits.h>
28
29 #include <ldap_pvt.h>
30 #include "lutil.h"
31
32 #define SASLREGEX_REPLACE 10
33
34 #define LDAP_X_SCOPE_EXACT      ((ber_int_t) 0x0010)
35 #define LDAP_X_SCOPE_REGEX      ((ber_int_t) 0x0020)
36 #define LDAP_X_SCOPE_CHILDREN   ((ber_int_t) 0x0030)
37 #define LDAP_X_SCOPE_SUBTREE    ((ber_int_t) 0x0040)
38 #define LDAP_X_SCOPE_ONELEVEL   ((ber_int_t) 0x0050)
39 #define LDAP_X_SCOPE_GROUP      ((ber_int_t) 0x0060)
40
41 /*
42  * IDs in DNauthzid form can now have a type specifier, that
43  * influences how they are used in related operations.
44  *
45  * syntax: dn[.{exact|regex}]:<val>
46  *
47  * dn.exact:    the value must pass normalization and is used 
48  *              in exact DN match.
49  * dn.regex:    the value is treated as a regular expression 
50  *              in matching DN values in authz{To|From}
51  *              attributes.
52  * dn:          for backwards compatibility reasons, the value 
53  *              is treated as a regular expression, and thus 
54  *              it is not normalized nor validated; it is used
55  *              in exact or regex comparisons based on the 
56  *              context.
57  *
58  * IDs in DNauthzid form can now have a type specifier, that
59  * influences how they are used in related operations.
60  *
61  * syntax: u[.mech[/realm]]:<val>
62  * 
63  * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name
64  * and realm is mechanism specific realm (separate to those
65  * which are representable as part of the principal).
66  */
67
68 typedef struct sasl_regexp {
69   char *sr_match;                                               /* regexp match pattern */
70   char *sr_replace;                                     /* regexp replace pattern */
71   regex_t sr_workspace;                                 /* workspace for regexp engine */
72   int sr_offset[SASLREGEX_REPLACE+2];   /* offsets of $1,$2... in *replace */
73 } SaslRegexp_t;
74
75 static int nSaslRegexp = 0;
76 static SaslRegexp_t *SaslRegexp = NULL;
77
78 #ifdef SLAP_AUTH_REWRITE
79 #include "rewrite.h"
80 struct rewrite_info     *sasl_rwinfo = NULL;
81 #define AUTHID_CONTEXT  "authid"
82 #endif /* SLAP_AUTH_REWRITE */
83
84 /* What SASL proxy authorization policies are allowed? */
85 #define SASL_AUTHZ_NONE 0x00
86 #define SASL_AUTHZ_FROM 0x01
87 #define SASL_AUTHZ_TO   0x02
88 #define SASL_AUTHZ_AND  0x10
89
90 static int authz_policy = SASL_AUTHZ_NONE;
91
92 static
93 int slap_sasl_match( Operation *opx, struct berval *rule,
94         struct berval *assertDN, struct berval *authc );
95
96 int slap_sasl_setpolicy( const char *arg )
97 {
98         int rc = LDAP_SUCCESS;
99
100         if ( strcasecmp( arg, "none" ) == 0 ) {
101                 authz_policy = SASL_AUTHZ_NONE;
102         } else if ( strcasecmp( arg, "from" ) == 0 ) {
103                 authz_policy = SASL_AUTHZ_FROM;
104         } else if ( strcasecmp( arg, "to" ) == 0 ) {
105                 authz_policy = SASL_AUTHZ_TO;
106         } else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) {
107                 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
108         } else if ( strcasecmp( arg, "all" ) == 0 ) {
109                 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
110         } else {
111                 rc = LDAP_OTHER;
112         }
113         return rc;
114 }
115
116 int slap_parse_user( struct berval *id, struct berval *user,
117                 struct berval *realm, struct berval *mech )
118 {
119         char    u;
120         
121         assert( id );
122         assert( !BER_BVISNULL( id ) );
123         assert( user );
124         assert( realm );
125         assert( mech );
126
127         u = id->bv_val[ 0 ];
128         
129         if ( u != 'u' && u != 'U' ) {
130                 /* called with something other than u: */
131                 return LDAP_PROTOCOL_ERROR;
132         }
133
134         /* uauthzid form:
135          *              u[.mech[/realm]]:user
136          */
137         
138         user->bv_val = strchr( id->bv_val, ':' );
139         if ( BER_BVISNULL( user ) ) {
140                 return LDAP_PROTOCOL_ERROR;
141         }
142         user->bv_val[ 0 ] = '\0';
143         user->bv_val++;
144         user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
145
146         mech->bv_val = strchr( id->bv_val, '.' );
147         if ( !BER_BVISNULL( mech ) ) {
148                 mech->bv_val[ 0 ] = '\0';
149                 mech->bv_val++;
150
151                 realm->bv_val = strchr( mech->bv_val, '/' );
152
153                 if ( !BER_BVISNULL( realm ) ) {
154                         realm->bv_val[ 0 ] = '\0';
155                         realm->bv_val++;
156                         mech->bv_len = realm->bv_val - mech->bv_val - 1;
157                         realm->bv_len = user->bv_val - realm->bv_val - 1;
158                 } else {
159                         mech->bv_len = user->bv_val - mech->bv_val - 1;
160                 }
161
162         } else {
163                 BER_BVZERO( realm );
164         }
165
166         if ( id->bv_val[ 1 ] != '\0' ) {
167                 return LDAP_PROTOCOL_ERROR;
168         }
169
170         if ( !BER_BVISNULL( mech ) ) {
171                 assert( mech->bv_val == id->bv_val + 2 );
172
173                 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
174                 mech->bv_val -= 2;
175         }
176
177         if ( !BER_BVISNULL( realm ) ) {
178                 assert( realm->bv_val >= id->bv_val + 2 );
179
180                 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
181                 realm->bv_val -= 2;
182         }
183
184         /* leave "u:" before user */
185         user->bv_val -= 2;
186         user->bv_len += 2;
187         user->bv_val[ 0 ] = u;
188         user->bv_val[ 1 ] = ':';
189
190         return LDAP_SUCCESS;
191 }
192
193 static int slap_parseURI( Operation *op, struct berval *uri,
194         struct berval *base, struct berval *nbase,
195         int *scope, Filter **filter, struct berval *fstr )
196 {
197         struct berval bv;
198         int rc;
199         LDAPURLDesc *ludp;
200
201         assert( uri != NULL && !BER_BVISNULL( uri ) );
202         BER_BVZERO( base );
203         BER_BVZERO( nbase );
204         BER_BVZERO( fstr );
205         *scope = -1;
206         *filter = NULL;
207
208 #ifdef NEW_LOGGING
209         LDAP_LOG( TRANSPORT, ENTRY, 
210                 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
211 #else
212         Debug( LDAP_DEBUG_TRACE,
213                 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
214 #endif
215
216         rc = LDAP_PROTOCOL_ERROR;
217         /*
218          * dn[.<dnstyle>]:<dnpattern>
219          * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
220          *
221          * <dnstyle> defaults to "exact"
222          * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
223          */
224         if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
225                 bv.bv_val = uri->bv_val + STRLENOF( "dn" );
226
227                 if ( bv.bv_val[ 0 ] == '.' ) {
228                         bv.bv_val++;
229
230                         if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
231                                 bv.bv_val += STRLENOF( "exact:" );
232                                 *scope = LDAP_X_SCOPE_EXACT;
233
234                         } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
235                                 bv.bv_val += STRLENOF( "regex:" );
236                                 *scope = LDAP_X_SCOPE_REGEX;
237
238                         } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
239                                 bv.bv_val += STRLENOF( "children:" );
240                                 *scope = LDAP_X_SCOPE_CHILDREN;
241
242                         } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
243                                 bv.bv_val += STRLENOF( "subtree:" );
244                                 *scope = LDAP_X_SCOPE_SUBTREE;
245
246                         } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
247                                 bv.bv_val += STRLENOF( "onelevel:" );
248                                 *scope = LDAP_X_SCOPE_ONELEVEL;
249
250                         } else {
251                                 return LDAP_PROTOCOL_ERROR;
252                         }
253
254                 } else {
255                         if ( bv.bv_val[ 0 ] != ':' ) {
256                                 return LDAP_PROTOCOL_ERROR;
257                         }
258                         *scope = LDAP_X_SCOPE_EXACT;
259                         bv.bv_val++;
260                 }
261
262                 bv.bv_val += strspn( bv.bv_val, " " );
263                 /* jump here in case no type specification was present
264                  * and uri was not an URI... HEADS-UP: assuming EXACT */
265 is_dn:          bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
266
267                 switch ( *scope ) {
268                 case LDAP_X_SCOPE_EXACT:
269                 case LDAP_X_SCOPE_CHILDREN:
270                 case LDAP_X_SCOPE_SUBTREE:
271                 case LDAP_X_SCOPE_ONELEVEL:
272                         rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
273                         if( rc != LDAP_SUCCESS ) {
274                                 *scope = -1;
275                         }
276                         break;
277
278                 case LDAP_X_SCOPE_REGEX:
279                         ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
280                         rc = LDAP_SUCCESS;
281                         break;
282
283                 default:
284                         *scope = -1;
285                         break;
286                 }
287
288                 return rc;
289
290         /*
291          * u:<uid>
292          */
293         } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
294                         && ( uri->bv_val[ 1 ] == ':' 
295                                 || uri->bv_val[ 1 ] == '/' 
296                                 || uri->bv_val[ 1 ] == '.' ) )
297         {
298                 Connection      c = *op->o_conn;
299                 char            buf[ SLAP_LDAPDN_MAXLEN ];
300                 struct berval   id,
301                                 user = BER_BVNULL,
302                                 realm = BER_BVNULL,
303                                 mech = BER_BVNULL;
304
305                 if ( sizeof( buf ) <= uri->bv_len ) {
306                         return LDAP_INVALID_SYNTAX;
307                 }
308
309                 id.bv_len = uri->bv_len;
310                 id.bv_val = buf;
311                 strncpy( buf, uri->bv_val, sizeof( buf ) );
312
313                 rc = slap_parse_user( &id, &user, &realm, &mech );
314                 if ( rc != LDAP_SUCCESS ) {
315                         return rc;
316                 }
317
318                 if ( !BER_BVISNULL( &mech ) ) {
319                         c.c_sasl_bind_mech = mech;
320                 } else {
321                         BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
322                 }
323                 
324                 rc = slap_sasl_getdn( &c, op, &user,
325                                 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
326
327                 if ( rc == LDAP_SUCCESS ) {
328                         *scope = LDAP_X_SCOPE_EXACT;
329                 }
330
331                 return rc;
332
333         /*
334          * group[/<groupoc>[/<groupat>]]:<groupdn>
335          *
336          * groupoc defaults to "groupOfNames"
337          * groupat defaults to "member"
338          * 
339          * <groupdn> must pass DN normalization
340          */
341         } else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
342         {
343                 struct berval   group_dn = BER_BVNULL,
344                                 group_oc = BER_BVNULL,
345                                 member_at = BER_BVNULL;
346                 char            *tmp;
347
348                 bv.bv_val = uri->bv_val + STRLENOF( "group" );
349                 group_dn.bv_val = strchr( bv.bv_val, ':' );
350                 if ( group_dn.bv_val == NULL ) {
351                         /* last chance: assume it's a(n exact) DN ... */
352                         bv.bv_val = uri->bv_val;
353                         *scope = LDAP_X_SCOPE_EXACT;
354                         goto is_dn;
355                 }
356                 
357                 if ( bv.bv_val[ 0 ] == '/' ) {
358                         group_oc.bv_val = &bv.bv_val[ 1 ];
359
360                         member_at.bv_val = strchr( group_oc.bv_val, '/' );
361                         if ( member_at.bv_val ) {
362                                 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
363                                 member_at.bv_val++;
364                                 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
365
366                         } else {
367                                 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
368                                 BER_BVSTR( &member_at, "member" );
369                         }
370
371                 } else {
372                         BER_BVSTR( &group_oc, "groupOfNames" );
373                 }
374                 group_dn.bv_val++;
375                 group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
376
377                 rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
378                 if ( rc != LDAP_SUCCESS ) {
379                         *scope = -1;
380                         return rc;
381                 }
382                 *scope = LDAP_X_SCOPE_GROUP;
383
384                 /* FIXME: caller needs to add value of member attribute
385                  * and close brackets twice */
386                 fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
387                         + group_oc.bv_len + member_at.bv_len;
388                 fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
389
390                 tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
391                                 STRLENOF( "(&(objectClass=" /* )) */ ) );
392                 tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
393                 tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
394                                 STRLENOF( /* ( */ ")(" /* ) */ ) );
395                 tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
396                 tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
397
398                 return rc;
399         }
400
401         /*
402          * ldap:///<base>??<scope>?<filter>
403          * <scope> ::= {base|one|subtree}
404          *
405          * <scope> defaults to "base"
406          * <base> must pass DN normalization
407          * <filter> must pass str2filter()
408          */
409         rc = ldap_url_parse( uri->bv_val, &ludp );
410         if ( rc == LDAP_URL_ERR_BADSCHEME ) {
411                 /*
412                  * last chance: assume it's a(n exact) DN ...
413                  *
414                  * NOTE: must pass DN normalization
415                  */
416                 bv.bv_val = uri->bv_val;
417                 *scope = LDAP_X_SCOPE_EXACT;
418                 goto is_dn;
419         }
420
421         if ( rc != LDAP_URL_SUCCESS ) {
422                 return LDAP_PROTOCOL_ERROR;
423         }
424
425         if (( ludp->lud_host && *ludp->lud_host )
426                 || ludp->lud_attrs || ludp->lud_exts )
427         {
428                 /* host part must be empty */
429                 /* attrs and extensions parts must be empty */
430                 rc = LDAP_PROTOCOL_ERROR;
431                 goto done;
432         }
433
434         /* Grab the scope */
435         *scope = ludp->lud_scope;
436
437         /* Grab the filter */
438         if ( ludp->lud_filter ) {
439                 *filter = str2filter_x( op, ludp->lud_filter );
440                 if ( *filter == NULL ) {
441                         rc = LDAP_PROTOCOL_ERROR;
442                         goto done;
443                 }
444                 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
445         }
446
447         /* Grab the searchbase */
448         ber_str2bv( ludp->lud_dn, 0, 0, base );
449         rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
450
451 done:
452         if( rc != LDAP_SUCCESS ) {
453                 if( *filter ) filter_free_x( op, *filter );
454                 BER_BVZERO( base );
455                 BER_BVZERO( fstr );
456         } else {
457                 /* Don't free these, return them to caller */
458                 ludp->lud_filter = NULL;
459                 ludp->lud_dn = NULL;
460         }
461
462         ldap_free_urldesc( ludp );
463         return( rc );
464 }
465
466 static int slap_sasl_rx_off(char *rep, int *off)
467 {
468         const char *c;
469         int n;
470
471         /* Precompile replace pattern. Find the $<n> placeholders */
472         off[0] = -2;
473         n = 1;
474         for ( c = rep;   *c;  c++ ) {
475                 if ( *c == '\\' && c[1] ) {
476                         c++;
477                         continue;
478                 }
479                 if ( *c == '$' ) {
480                         if ( n == SASLREGEX_REPLACE ) {
481 #ifdef NEW_LOGGING
482                                 LDAP_LOG( TRANSPORT, ERR, 
483                                         "slap_sasl_rx_off: \"%s\" has too many $n "
484                                         "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0  );
485 #else
486                                 Debug( LDAP_DEBUG_ANY,
487                                         "SASL replace pattern %s has too many $n "
488                                                 "placeholders (max %d)\n",
489                                         rep, SASLREGEX_REPLACE, 0 );
490 #endif
491
492                                 return( LDAP_OTHER );
493                         }
494                         off[n] = c - rep;
495                         n++;
496                 }
497         }
498
499         /* Final placeholder, after the last $n */
500         off[n] = c - rep;
501         n++;
502         off[n] = -1;
503         return( LDAP_SUCCESS );
504 }
505
506 #ifdef SLAP_AUTH_REWRITE
507 int slap_sasl_rewrite_config( 
508                 const char      *fname,
509                 int             lineno,
510                 int             argc,
511                 char            **argv
512 )
513 {
514         int     rc;
515         char    *savearg0;
516
517         /* init at first call */
518         if ( sasl_rwinfo == NULL ) {
519                 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
520         }
521
522         /* strip "authid-" prefix for parsing */
523         savearg0 = argv[0];
524         argv[0] += STRLENOF( "authid-" );
525         rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
526         argv[0] = savearg0;
527
528         return rc;
529 }
530
531 int slap_sasl_rewrite_destroy( void )
532 {
533         if ( sasl_rwinfo ) {
534                 rewrite_info_delete( &sasl_rwinfo );
535                 sasl_rwinfo = NULL;
536         }
537
538         return 0;
539 }
540
541 int slap_sasl_regexp_rewrite_config(
542                 const char      *fname,
543                 int             lineno,
544                 const char      *match,
545                 const char      *replace,
546                 const char      *context )
547 {
548         int     rc;
549         char    *newreplace, *p;
550         char    *argvRule[] = { "rewriteRule", NULL, NULL, "@", NULL };
551
552         /* init at first call */
553         if ( sasl_rwinfo == NULL ) {
554                 char *argvEngine[] = { "rewriteEngine", "on", NULL };
555                 char *argvContext[] = { "rewriteContext", NULL, NULL };
556
557                 /* initialize rewrite engine */
558                 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
559
560                 /* switch on rewrite engine */
561                 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
562                 if (rc != LDAP_SUCCESS) {
563                         return rc;
564                 }
565
566                 /* create generic authid context */
567                 argvContext[1] = AUTHID_CONTEXT;
568                 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
569                 if (rc != LDAP_SUCCESS) {
570                         return rc;
571                 }
572         }
573
574         newreplace = ch_strdup( replace );
575         
576         for (p = strchr( newreplace, '$' ); p; p = strchr( p + 1, '$' ) ) {
577                 if ( isdigit( p[1] ) ) {
578                         p[0] = '%';
579                 } else {
580                         p++;
581                 }
582         }
583
584         argvRule[1] = (char *)match;
585         argvRule[2] = newreplace;
586         rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
587         ch_free( newreplace );
588
589         return rc;
590 }
591 #endif /* SLAP_AUTH_REWRITE */
592
593 int slap_sasl_regexp_config( const char *match, const char *replace )
594 {
595 #ifdef SLAP_AUTH_REWRITE
596         return slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
597                         match, replace, AUTHID_CONTEXT );
598 #else /* ! SLAP_AUTH_REWRITE */
599         int rc;
600         SaslRegexp_t *reg;
601
602         SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
603           (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
604
605         reg = &SaslRegexp[nSaslRegexp];
606
607         reg->sr_match = ch_strdup( match );
608         reg->sr_replace = ch_strdup( replace );
609
610         /* Precompile matching pattern */
611         rc = regcomp( &reg->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
612         if ( rc ) {
613 #ifdef NEW_LOGGING
614                 LDAP_LOG( TRANSPORT, ERR, 
615                         "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
616                         reg->sr_match, 0, 0 );
617 #else
618                 Debug( LDAP_DEBUG_ANY,
619                 "SASL match pattern %s could not be compiled by regexp engine\n",
620                 reg->sr_match, 0, 0 );
621 #endif
622
623                 return( LDAP_OTHER );
624         }
625
626         rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
627         if ( rc != LDAP_SUCCESS ) return rc;
628
629         nSaslRegexp++;
630         return( LDAP_SUCCESS );
631 #endif /* ! SLAP_AUTH_REWRITE */
632 }
633
634 /* Perform replacement on regexp matches */
635 static void slap_sasl_rx_exp(
636         const char *rep,
637         const int *off,
638         regmatch_t *str,
639         const char *saslname,
640         struct berval *out,
641         void *ctx )
642 {
643         int i, n, len, insert;
644
645         /* Get the total length of the final URI */
646
647         n=1;
648         len = 0;
649         while( off[n] >= 0 ) {
650                 /* Len of next section from replacement string (x,y,z above) */
651                 len += off[n] - off[n-1] - 2;
652                 if( off[n+1] < 0)
653                         break;
654
655                 /* Len of string from saslname that matched next $i  (b,d above) */
656                 i = rep[ off[n] + 1 ]   - '0';
657                 len += str[i].rm_eo - str[i].rm_so;
658                 n++;
659         }
660         out->bv_val = slap_sl_malloc( len + 1, ctx );
661         out->bv_len = len;
662
663         /* Fill in URI with replace string, replacing $i as we go */
664         n=1;
665         insert = 0;
666         while( off[n] >= 0) {
667                 /* Paste in next section from replacement string (x,y,z above) */
668                 len = off[n] - off[n-1] - 2;
669                 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
670                 insert += len;
671                 if( off[n+1] < 0)
672                         break;
673
674                 /* Paste in string from saslname that matched next $i  (b,d above) */
675                 i = rep[ off[n] + 1 ]   - '0';
676                 len = str[i].rm_eo - str[i].rm_so;
677                 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
678                 insert += len;
679
680                 n++;
681         }
682
683         out->bv_val[insert] = '\0';
684 }
685
686 /* Take the passed in SASL name and attempt to convert it into an
687    LDAP URI to find the matching LDAP entry, using the pattern matching
688    strings given in the saslregexp config file directive(s) */
689
690 static int slap_authz_regexp( struct berval *in, struct berval *out,
691                 int flags, void *ctx )
692 {
693 #ifdef SLAP_AUTH_REWRITE
694         const char      *context = AUTHID_CONTEXT;
695
696         if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
697                 return 0;
698         }
699
700         /* FIXME: if aware of authc/authz mapping, 
701          * we could use different contexts ... */
702         switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL, 
703                                 &out->bv_val ) )
704         {
705         case REWRITE_REGEXEC_OK:
706                 if ( !BER_BVISNULL( out ) ) {
707                         char *val = out->bv_val;
708                         ber_str2bv_x( val, 0, 1, out, ctx );
709                         free( val );
710                 } else {
711                         ber_dupbv_x( out, in, ctx );
712                 }
713 #ifdef NEW_LOGGING
714                 LDAP_LOG( BACK_LDAP, DETAIL1, 
715                         "[rw] %s: \"%s\" -> \"%s\"\n",
716                         context, in->bv_val, out->bv_val );             
717 #else /* !NEW_LOGGING */
718                 Debug( LDAP_DEBUG_ARGS,
719                         "[rw] %s: \"%s\" -> \"%s\"\n",
720                         context, in->bv_val, out->bv_val );             
721 #endif /* !NEW_LOGGING */
722                 return 1;
723                 
724         case REWRITE_REGEXEC_UNWILLING:
725         case REWRITE_REGEXEC_ERR:
726         default:
727                 return 0;
728         }
729
730 #else /* ! SLAP_AUTH_REWRITE */
731         char *saslname = in->bv_val;
732         SaslRegexp_t *reg;
733         regmatch_t sr_strings[SASLREGEX_REPLACE];       /* strings matching $1,$2 ... */
734         int i;
735
736         memset( out, 0, sizeof( *out ) );
737
738 #ifdef NEW_LOGGING
739         LDAP_LOG( TRANSPORT, ENTRY, 
740                 "slap_authz_regexp: converting SASL name %s\n", saslname, 0, 0 );
741 #else
742         Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
743            saslname, 0, 0 );
744 #endif
745
746         if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
747                 return( 0 );
748         }
749
750         /* Match the normalized SASL name to the saslregexp patterns */
751         for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
752                 if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
753                   sr_strings, 0)  == 0 )
754                         break;
755         }
756
757         if( i >= nSaslRegexp ) return( 0 );
758
759         /*
760          * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
761          * replace pattern of the form "x$1y$2z". The returned string needs
762          * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
763          */
764         slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
765                 sr_strings, saslname, out, ctx );
766
767 #ifdef NEW_LOGGING
768         LDAP_LOG( TRANSPORT, ENTRY, 
769                 "slap_authz_regexp: converted SASL name to %s\n",
770                 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
771 #else
772         Debug( LDAP_DEBUG_TRACE,
773                 "slap_authz_regexp: converted SASL name to %s\n",
774                 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
775 #endif
776
777         return( 1 );
778 #endif /* ! SLAP_AUTH_REWRITE */
779 }
780
781 /* This callback actually does some work...*/
782 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
783 {
784         struct berval *ndn = o->o_callback->sc_private;
785
786         if (rs->sr_type != REP_SEARCH) return 0;
787
788         /* We only want to be called once */
789         if ( !BER_BVISNULL( ndn ) ) {
790                 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
791                 BER_BVZERO( ndn );
792
793 #ifdef NEW_LOGGING
794                 LDAP_LOG( TRANSPORT, DETAIL1,
795                         "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
796 #else
797                 Debug( LDAP_DEBUG_TRACE,
798                         "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
799 #endif
800                 return -1;
801         }
802
803         ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
804         return 0;
805 }
806
807
808 typedef struct smatch_info {
809         struct berval *dn;
810         int match;
811 } smatch_info;
812
813 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
814 {
815         smatch_info *sm = o->o_callback->sc_private;
816
817         if ( rs->sr_type != REP_SEARCH ) {
818                 if ( rs->sr_err != LDAP_SUCCESS ) {
819                         sm->match = -1;
820                 }
821                 return 0;
822         }
823
824         if ( sm->match == 1 ) {
825                 sm->match = -1;
826                 return 0;
827         }
828
829         if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
830                 sm->match = 1;
831
832         } else {
833                 sm->match = -1;
834         }
835
836         return 0;
837 }
838
839 int
840 slap_sasl_matches( Operation *op, BerVarray rules,
841                 struct berval *assertDN, struct berval *authc )
842 {
843         int     rc = LDAP_INAPPROPRIATE_AUTH;
844
845         if ( rules != NULL ) {
846                 int     i;
847
848                 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
849                         rc = slap_sasl_match( op, &rules[i], assertDN, authc );
850                         if ( rc == LDAP_SUCCESS ) break;
851                 }
852         }
853         
854         return rc;
855 }
856
857 /*
858  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
859  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
860  * the rule must be used as an internal search for entries. If that search
861  * returns the *assertDN entry, the match is successful.
862  *
863  * The assertDN should not have the dn: prefix
864  */
865
866 static
867 int slap_sasl_match( Operation *opx, struct berval *rule,
868         struct berval *assertDN, struct berval *authc )
869 {
870         int rc; 
871         regex_t reg;
872         smatch_info sm;
873         slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
874         Operation op = {0};
875         SlapReply rs = {REP_RESULT};
876
877         sm.dn = assertDN;
878         sm.match = 0;
879         cb.sc_private = &sm;
880
881 #ifdef NEW_LOGGING
882         LDAP_LOG( TRANSPORT, ENTRY, 
883                 "slap_sasl_match: comparing DN %s to rule %s\n", 
884                 assertDN->bv_val, rule->bv_val,0 );
885 #else
886         Debug( LDAP_DEBUG_TRACE,
887            "===>slap_sasl_match: comparing DN %s to rule %s\n",
888                 assertDN->bv_val, rule->bv_val, 0 );
889 #endif
890
891         rc = slap_parseURI( opx, rule, &op.o_req_dn,
892                 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
893                 &op.ors_filterstr );
894         if( rc != LDAP_SUCCESS ) goto CONCLUDED;
895
896         /* Massive shortcut: search scope == base */
897         switch ( op.oq_search.rs_scope ) {
898         case LDAP_SCOPE_BASE:
899         case LDAP_X_SCOPE_EXACT:
900 exact_match:
901                 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
902                         rc = LDAP_SUCCESS;
903                 } else {
904                         rc = LDAP_INAPPROPRIATE_AUTH;
905                 }
906                 goto CONCLUDED;
907
908         case LDAP_X_SCOPE_CHILDREN:
909         case LDAP_X_SCOPE_SUBTREE:
910         case LDAP_X_SCOPE_ONELEVEL:
911         {
912                 int     d = assertDN->bv_len - op.o_req_ndn.bv_len;
913
914                 rc = LDAP_INAPPROPRIATE_AUTH;
915
916                 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
917                         goto exact_match;
918
919                 } else if ( d > 0 ) {
920                         struct berval bv;
921
922                         /* leave room for at least one char of attributeType,
923                          * one for '=' and one for ',' */
924                         if ( d < STRLENOF( "x=,") ) {
925                                 goto CONCLUDED;
926                         }
927
928                         bv.bv_len = op.o_req_ndn.bv_len;
929                         bv.bv_val = assertDN->bv_val + d;
930
931                         if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
932                                 switch ( op.oq_search.rs_scope ) {
933                                 case LDAP_X_SCOPE_SUBTREE:
934                                 case LDAP_X_SCOPE_CHILDREN:
935                                         rc = LDAP_SUCCESS;
936                                         break;
937
938                                 case LDAP_X_SCOPE_ONELEVEL:
939                                 {
940                                         struct berval   pdn;
941
942                                         dnParent( assertDN, &pdn );
943                                         /* the common portion of the DN
944                                          * already matches, so only check
945                                          * if parent DN of assertedDN 
946                                          * is all the pattern */
947                                         if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
948                                                 rc = LDAP_SUCCESS;
949                                         }
950                                         break;
951                                 }
952                                 default:
953                                         /* at present, impossible */
954                                         assert( 0 );
955                                 }
956                         }
957                 }
958                 goto CONCLUDED;
959         }
960
961         case LDAP_X_SCOPE_REGEX:
962                 rc = regcomp(&reg, op.o_req_ndn.bv_val,
963                         REG_EXTENDED|REG_ICASE|REG_NOSUB);
964                 if ( rc == 0 ) {
965                         rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
966                         regfree( &reg );
967                 }
968                 if ( rc == 0 ) {
969                         rc = LDAP_SUCCESS;
970                 } else {
971                         rc = LDAP_INAPPROPRIATE_AUTH;
972                 }
973                 goto CONCLUDED;
974
975         case LDAP_X_SCOPE_GROUP: {
976                 char    *tmp;
977
978                 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
979                  * we need to append the <assertDN> so that the <group_dn> is searched
980                  * with scope "base", and the filter ensures that <assertDN> is
981                  * member of the group */
982                 tmp = ch_realloc( op.ors_filterstr.bv_val,
983                                 op.ors_filterstr.bv_len + assertDN->bv_len + STRLENOF( /* (( */ "))" ) + 1 );
984                 if ( tmp == NULL ) {
985                         rc = LDAP_NO_MEMORY;
986                         goto CONCLUDED;
987                 }
988                 op.ors_filterstr.bv_val = tmp;
989                 
990                 tmp = lutil_strcopy( &tmp[ op.ors_filterstr.bv_len ], assertDN->bv_val );
991                 tmp = lutil_strcopy( tmp, /* (( */ "))" );
992
993                 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
994                 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
995                 if ( op.ors_filter == NULL ) {
996                         rc = LDAP_PROTOCOL_ERROR;
997                         goto CONCLUDED;
998                 }
999                 op.ors_scope = LDAP_SCOPE_BASE;
1000
1001                 /* hijack match DN: use that of the group instead of the assertDN;
1002                  * assertDN is now in the filter */
1003                 sm.dn = &op.o_req_ndn;
1004
1005                 /* do the search */
1006                 break;
1007                 }
1008
1009         default:
1010                 break;
1011         }
1012
1013         /* Must run an internal search. */
1014         if ( op.oq_search.rs_filter == NULL ) {
1015                 rc = LDAP_FILTER_ERROR;
1016                 goto CONCLUDED;
1017         }
1018
1019 #ifdef NEW_LOGGING
1020         LDAP_LOG( TRANSPORT, DETAIL1, 
1021                 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1022                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
1023 #else
1024         Debug( LDAP_DEBUG_TRACE,
1025            "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1026            op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
1027 #endif
1028
1029         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1030         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1031                 rc = LDAP_INAPPROPRIATE_AUTH;
1032                 goto CONCLUDED;
1033         }
1034
1035         op.o_tag = LDAP_REQ_SEARCH;
1036         op.o_protocol = LDAP_VERSION3;
1037         op.o_ndn = *authc;
1038         op.o_callback = &cb;
1039         op.o_time = slap_get_time();
1040         op.o_do_not_cache = 1;
1041         op.o_is_auth_check = 1;
1042         op.o_threadctx = opx->o_threadctx;
1043         op.o_tmpmemctx = opx->o_tmpmemctx;
1044         op.o_tmpmfuncs = opx->o_tmpmfuncs;
1045 #ifdef LDAP_SLAPI
1046         op.o_pb = opx->o_pb;
1047 #endif
1048         op.o_conn = opx->o_conn;
1049         op.o_connid = opx->o_connid;
1050         /* use req_ndn as req_dn instead of non-pretty base of uri */
1051         if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
1052         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1053         op.oq_search.rs_slimit = 1;
1054         op.oq_search.rs_tlimit = -1;
1055         op.o_sync_slog_size = -1;
1056
1057         op.o_bd->be_search( &op, &rs );
1058
1059         if (sm.match == 1) {
1060                 rc = LDAP_SUCCESS;
1061         } else {
1062                 rc = LDAP_INAPPROPRIATE_AUTH;
1063         }
1064
1065 CONCLUDED:
1066         if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1067         if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1068         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
1069         if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1070
1071 #ifdef NEW_LOGGING
1072         LDAP_LOG( TRANSPORT, ENTRY, 
1073                 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
1074 #else
1075         Debug( LDAP_DEBUG_TRACE,
1076            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1077 #endif
1078
1079         return( rc );
1080 }
1081
1082
1083 /*
1084  * This function answers the question, "Can this ID authorize to that ID?",
1085  * based on authorization rules. The rules are stored in the *searchDN, in the
1086  * attribute named by *attr. If any of those rules map to the *assertDN, the
1087  * authorization is approved.
1088  *
1089  * The DNs should not have the dn: prefix
1090  */
1091 static int
1092 slap_sasl_check_authz( Operation *op,
1093         struct berval *searchDN,
1094         struct berval *assertDN,
1095         AttributeDescription *ad,
1096         struct berval *authc )
1097 {
1098         int i, rc;
1099         BerVarray vals = NULL;
1100
1101 #ifdef NEW_LOGGING
1102         LDAP_LOG( TRANSPORT, ENTRY, 
1103                 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
1104             assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1105 #else
1106         Debug( LDAP_DEBUG_TRACE,
1107            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1108            assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1109 #endif
1110
1111         rc = backend_attribute( op, NULL, searchDN, ad, &vals );
1112         if( rc != LDAP_SUCCESS ) goto COMPLETE;
1113
1114         /* Check if the *assertDN matches any *vals */
1115         rc = slap_sasl_matches( op, vals, assertDN, authc );
1116
1117 COMPLETE:
1118         if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1119
1120 #ifdef NEW_LOGGING
1121         LDAP_LOG( TRANSPORT, RESULTS, 
1122                 "slap_sasl_check_authz: %s check returning %s\n", 
1123                 ad->ad_cname.bv_val, rc, 0 );
1124 #else
1125         Debug( LDAP_DEBUG_TRACE,
1126            "<==slap_sasl_check_authz: %s check returning %d\n",
1127                 ad->ad_cname.bv_val, rc, 0);
1128 #endif
1129
1130         return( rc );
1131 }
1132
1133 /*
1134  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1135  * return the LDAP DN to which it matches. The SASL regexp rules in the config
1136  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1137  * search with scope=base), just return the URI (or its searchbase). Otherwise
1138  * an internal search must be done, and if that search returns exactly one
1139  * entry, return the DN of that one entry.
1140  */
1141 void slap_sasl2dn( Operation *opx,
1142         struct berval *saslname, struct berval *sasldn, int flags )
1143 {
1144         int rc;
1145         slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1146         Operation op = {0};
1147         SlapReply rs = {REP_RESULT};
1148         struct berval regout = BER_BVNULL;
1149
1150 #ifdef NEW_LOGGING
1151         LDAP_LOG( TRANSPORT, ENTRY, 
1152                 "slap_sasl2dn: converting SASL name %s to DN.\n",
1153                 saslname->bv_val, 0, 0 );
1154 #else
1155         Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1156                 "converting SASL name %s to a DN\n",
1157                 saslname->bv_val, 0,0 );
1158 #endif
1159
1160         sasldn->bv_val = NULL;
1161         sasldn->bv_len = 0;
1162         cb.sc_private = sasldn;
1163
1164         /* Convert the SASL name into a minimal URI */
1165         if( !slap_authz_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
1166                 goto FINISHED;
1167         }
1168
1169         rc = slap_parseURI( opx, &regout, &op.o_req_dn,
1170                 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
1171                 &op.ors_filterstr );
1172         if ( !BER_BVISNULL( &regout ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1173         if ( rc != LDAP_SUCCESS ) {
1174                 goto FINISHED;
1175         }
1176
1177         /* Must do an internal search */
1178         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1179
1180         /* Massive shortcut: search scope == base */
1181         switch ( op.oq_search.rs_scope ) {
1182         case LDAP_SCOPE_BASE:
1183         case LDAP_X_SCOPE_EXACT:
1184                 *sasldn = op.o_req_ndn;
1185                 BER_BVZERO( &op.o_req_ndn );
1186                 /* intentionally continue to next case */
1187
1188         case LDAP_X_SCOPE_REGEX:
1189         case LDAP_X_SCOPE_SUBTREE:
1190         case LDAP_X_SCOPE_CHILDREN:
1191         case LDAP_X_SCOPE_ONELEVEL:
1192         case LDAP_X_SCOPE_GROUP:
1193                 /* correctly parsed, but illegal */
1194                 goto FINISHED;
1195
1196         case LDAP_SCOPE_ONELEVEL:
1197         case LDAP_SCOPE_SUBTREE:
1198 #ifdef LDAP_SCOPE_SUBORDINATE
1199         case LDAP_SCOPE_SUBORDINATE:
1200 #endif
1201                 /* do a search */
1202                 break;
1203
1204         default:
1205                 /* catch unhandled cases (there shouldn't be) */
1206                 assert( 0 );
1207         }
1208
1209 #ifdef NEW_LOGGING
1210         LDAP_LOG( TRANSPORT, DETAIL1, 
1211                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1212                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
1213 #else
1214         Debug( LDAP_DEBUG_TRACE,
1215                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1216                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
1217 #endif
1218
1219         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1220                 goto FINISHED;
1221         }
1222
1223         op.o_conn = opx->o_conn;
1224         op.o_connid = opx->o_connid;
1225         op.o_tag = LDAP_REQ_SEARCH;
1226         op.o_protocol = LDAP_VERSION3;
1227         op.o_ndn = opx->o_conn->c_ndn;
1228         op.o_callback = &cb;
1229         op.o_time = slap_get_time();
1230         op.o_do_not_cache = 1;
1231         op.o_is_auth_check = 1;
1232         op.o_threadctx = opx->o_threadctx;
1233         op.o_tmpmemctx = opx->o_tmpmemctx;
1234         op.o_tmpmfuncs = opx->o_tmpmfuncs;
1235 #ifdef LDAP_SLAPI
1236         op.o_pb = opx->o_pb;
1237 #endif
1238         op.oq_search.rs_deref = LDAP_DEREF_NEVER;
1239         op.oq_search.rs_slimit = 1;
1240         op.oq_search.rs_tlimit = -1;
1241         op.oq_search.rs_attrsonly = 1;
1242         /* use req_ndn as req_dn instead of non-pretty base of uri */
1243         if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
1244         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1245
1246         op.o_bd->be_search( &op, &rs );
1247         
1248 FINISHED:
1249         if( !BER_BVISEMPTY( sasldn ) ) {
1250                 opx->o_conn->c_authz_backend = op.o_bd;
1251         }
1252         if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1253         if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1254         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
1255         if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1256
1257 #ifdef NEW_LOGGING
1258         LDAP_LOG( TRANSPORT, ENTRY, 
1259                 "slap_sasl2dn: Converted SASL name to %s\n",
1260                 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
1261 #else
1262         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
1263                 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
1264 #endif
1265
1266         return;
1267 }
1268
1269
1270 /* Check if a bind can SASL authorize to another identity.
1271  * The DNs should not have the dn: prefix
1272  */
1273
1274 int slap_sasl_authorized( Operation *op,
1275         struct berval *authcDN, struct berval *authzDN )
1276 {
1277         int rc = LDAP_INAPPROPRIATE_AUTH;
1278
1279         /* User binding as anonymous */
1280         if ( authzDN == NULL ) {
1281                 rc = LDAP_SUCCESS;
1282                 goto DONE;
1283         }
1284
1285 #ifdef NEW_LOGGING
1286         LDAP_LOG( TRANSPORT, ENTRY, 
1287                 "slap_sasl_authorized: can %s become %s?\n", 
1288                 authcDN->bv_val, authzDN->bv_val, 0 );
1289 #else
1290         Debug( LDAP_DEBUG_TRACE,
1291            "==>slap_sasl_authorized: can %s become %s?\n",
1292                 authcDN->bv_val, authzDN->bv_val, 0 );
1293 #endif
1294
1295         /* If person is authorizing to self, succeed */
1296         if ( dn_match( authcDN, authzDN ) ) {
1297                 rc = LDAP_SUCCESS;
1298                 goto DONE;
1299         }
1300
1301         /* Allow the manager to authorize as any DN. */
1302         if( op->o_conn->c_authz_backend &&
1303                 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
1304         {
1305                 rc = LDAP_SUCCESS;
1306                 goto DONE;
1307         }
1308
1309         /* Check source rules */
1310         if( authz_policy & SASL_AUTHZ_TO ) {
1311                 rc = slap_sasl_check_authz( op, authcDN, authzDN,
1312                         slap_schema.si_ad_saslAuthzTo, authcDN );
1313                 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
1314                         goto DONE;
1315                 }
1316         }
1317
1318         /* Check destination rules */
1319         if( authz_policy & SASL_AUTHZ_FROM ) {
1320                 rc = slap_sasl_check_authz( op, authzDN, authcDN,
1321                         slap_schema.si_ad_saslAuthzFrom, authcDN );
1322                 if( rc == LDAP_SUCCESS ) {
1323                         goto DONE;
1324                 }
1325         }
1326
1327         rc = LDAP_INAPPROPRIATE_AUTH;
1328
1329 DONE:
1330
1331 #ifdef NEW_LOGGING
1332         LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1333 #else
1334         Debug( LDAP_DEBUG_TRACE,
1335                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
1336 #endif
1337
1338         return( rc );
1339 }