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