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