]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
add group authz
[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
984         op.o_bd->be_search( &op, &rs );
985
986         if (sm.match) {
987                 rc = LDAP_SUCCESS;
988         } else {
989                 rc = LDAP_INAPPROPRIATE_AUTH;
990         }
991
992 CONCLUDED:
993         if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
994         if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
995         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
996         if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
997
998 #ifdef NEW_LOGGING
999         LDAP_LOG( TRANSPORT, ENTRY, 
1000                 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
1001 #else
1002         Debug( LDAP_DEBUG_TRACE,
1003            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1004 #endif
1005
1006         return( rc );
1007 }
1008
1009
1010 /*
1011  * This function answers the question, "Can this ID authorize to that ID?",
1012  * based on authorization rules. The rules are stored in the *searchDN, in the
1013  * attribute named by *attr. If any of those rules map to the *assertDN, the
1014  * authorization is approved.
1015  *
1016  * The DNs should not have the dn: prefix
1017  */
1018 static int
1019 slap_sasl_check_authz( Operation *op,
1020         struct berval *searchDN,
1021         struct berval *assertDN,
1022         AttributeDescription *ad,
1023         struct berval *authc )
1024 {
1025         int i, rc;
1026         BerVarray vals=NULL;
1027
1028 #ifdef NEW_LOGGING
1029         LDAP_LOG( TRANSPORT, ENTRY, 
1030                 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
1031             assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1032 #else
1033         Debug( LDAP_DEBUG_TRACE,
1034            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1035            assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1036 #endif
1037
1038         rc = backend_attribute( op, NULL,
1039                 searchDN, ad, &vals );
1040         if( rc != LDAP_SUCCESS ) goto COMPLETE;
1041
1042         /* Check if the *assertDN matches any **vals */
1043         if( vals != NULL ) {
1044                 for( i=0; !BER_BVISNULL( &vals[i] ); i++ ) {
1045                         rc = slap_sasl_match( op, &vals[i], assertDN, authc );
1046                         if ( rc == LDAP_SUCCESS ) goto COMPLETE;
1047                 }
1048         }
1049         rc = LDAP_INAPPROPRIATE_AUTH;
1050
1051 COMPLETE:
1052         if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1053
1054 #ifdef NEW_LOGGING
1055         LDAP_LOG( TRANSPORT, RESULTS, 
1056                 "slap_sasl_check_authz: %s check returning %s\n", 
1057                 ad->ad_cname.bv_val, rc, 0 );
1058 #else
1059         Debug( LDAP_DEBUG_TRACE,
1060            "<==slap_sasl_check_authz: %s check returning %d\n",
1061                 ad->ad_cname.bv_val, rc, 0);
1062 #endif
1063
1064         return( rc );
1065 }
1066
1067 /*
1068  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1069  * return the LDAP DN to which it matches. The SASL regexp rules in the config
1070  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1071  * search with scope=base), just return the URI (or its searchbase). Otherwise
1072  * an internal search must be done, and if that search returns exactly one
1073  * entry, return the DN of that one entry.
1074  */
1075 void slap_sasl2dn( Operation *opx,
1076         struct berval *saslname, struct berval *sasldn, int flags )
1077 {
1078         int rc;
1079         slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1080         Operation op = {0};
1081         SlapReply rs = {REP_RESULT};
1082         struct berval regout = BER_BVNULL;
1083
1084 #ifdef NEW_LOGGING
1085         LDAP_LOG( TRANSPORT, ENTRY, 
1086                 "slap_sasl2dn: converting SASL name %s to DN.\n",
1087                 saslname->bv_val, 0, 0 );
1088 #else
1089         Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1090                 "converting SASL name %s to a DN\n",
1091                 saslname->bv_val, 0,0 );
1092 #endif
1093
1094         sasldn->bv_val = NULL;
1095         sasldn->bv_len = 0;
1096         cb.sc_private = sasldn;
1097
1098         /* Convert the SASL name into a minimal URI */
1099         if( !slap_sasl_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
1100                 goto FINISHED;
1101         }
1102
1103         rc = slap_parseURI( opx, &regout, &op.o_req_dn,
1104                 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
1105                 &op.ors_filterstr );
1106         if ( !BER_BVISNULL( &regout ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1107         if ( rc != LDAP_SUCCESS ) {
1108                 goto FINISHED;
1109         }
1110
1111         /* Must do an internal search */
1112         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1113
1114         /* Massive shortcut: search scope == base */
1115         switch ( op.oq_search.rs_scope ) {
1116         case LDAP_SCOPE_BASE:
1117         case LDAP_X_SCOPE_EXACT:
1118                 *sasldn = op.o_req_ndn;
1119                 BER_BVZERO( &op.o_req_ndn );
1120                 /* intentionally continue to next case */
1121
1122         case LDAP_X_SCOPE_REGEX:
1123         case LDAP_X_SCOPE_SUBTREE:
1124         case LDAP_X_SCOPE_CHILDREN:
1125         case LDAP_X_SCOPE_ONELEVEL:
1126         case LDAP_X_SCOPE_GROUP:
1127                 /* correctly parsed, but illegal */
1128                 goto FINISHED;
1129
1130         case LDAP_SCOPE_ONELEVEL:
1131         case LDAP_SCOPE_SUBTREE:
1132 #ifdef LDAP_SCOPE_SUBORDINATE
1133         case LDAP_SCOPE_SUBORDINATE:
1134 #endif
1135                 /* do a search */
1136                 break;
1137
1138         default:
1139                 /* catch unhandled cases (there shouldn't be) */
1140                 assert( 0 );
1141         }
1142
1143 #ifdef NEW_LOGGING
1144         LDAP_LOG( TRANSPORT, DETAIL1, 
1145                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1146                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
1147 #else
1148         Debug( LDAP_DEBUG_TRACE,
1149                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1150                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
1151 #endif
1152
1153         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1154                 goto FINISHED;
1155         }
1156
1157         op.o_conn = opx->o_conn;
1158         op.o_connid = opx->o_connid;
1159         op.o_tag = LDAP_REQ_SEARCH;
1160         op.o_protocol = LDAP_VERSION3;
1161         op.o_ndn = opx->o_conn->c_ndn;
1162         op.o_callback = &cb;
1163         op.o_time = slap_get_time();
1164         op.o_do_not_cache = 1;
1165         op.o_is_auth_check = 1;
1166         op.o_threadctx = opx->o_threadctx;
1167         op.o_tmpmemctx = opx->o_tmpmemctx;
1168         op.o_tmpmfuncs = opx->o_tmpmfuncs;
1169 #ifdef LDAP_SLAPI
1170         op.o_pb = opx->o_pb;
1171 #endif
1172         op.oq_search.rs_deref = LDAP_DEREF_NEVER;
1173         op.oq_search.rs_slimit = 1;
1174         op.oq_search.rs_tlimit = -1;
1175         op.oq_search.rs_attrsonly = 1;
1176         /* use req_ndn as req_dn instead of non-pretty base of uri */
1177         if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
1178         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1179
1180         op.o_bd->be_search( &op, &rs );
1181         
1182 FINISHED:
1183         if( !BER_BVISEMPTY( sasldn ) ) {
1184                 opx->o_conn->c_authz_backend = op.o_bd;
1185         }
1186         if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1187         if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1188         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
1189         if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1190
1191 #ifdef NEW_LOGGING
1192         LDAP_LOG( TRANSPORT, ENTRY, 
1193                 "slap_sasl2dn: Converted SASL name to %s\n",
1194                 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
1195 #else
1196         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
1197                 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
1198 #endif
1199
1200         return;
1201 }
1202
1203
1204 /* Check if a bind can SASL authorize to another identity.
1205  * The DNs should not have the dn: prefix
1206  */
1207
1208 int slap_sasl_authorized( Operation *op,
1209         struct berval *authcDN, struct berval *authzDN )
1210 {
1211         int rc = LDAP_INAPPROPRIATE_AUTH;
1212
1213         /* User binding as anonymous */
1214         if ( authzDN == NULL ) {
1215                 rc = LDAP_SUCCESS;
1216                 goto DONE;
1217         }
1218
1219 #ifdef NEW_LOGGING
1220         LDAP_LOG( TRANSPORT, ENTRY, 
1221                 "slap_sasl_authorized: can %s become %s?\n", 
1222                 authcDN->bv_val, authzDN->bv_val, 0 );
1223 #else
1224         Debug( LDAP_DEBUG_TRACE,
1225            "==>slap_sasl_authorized: can %s become %s?\n",
1226                 authcDN->bv_val, authzDN->bv_val, 0 );
1227 #endif
1228
1229         /* If person is authorizing to self, succeed */
1230         if ( dn_match( authcDN, authzDN ) ) {
1231                 rc = LDAP_SUCCESS;
1232                 goto DONE;
1233         }
1234
1235         /* Allow the manager to authorize as any DN. */
1236         if( op->o_conn->c_authz_backend &&
1237                 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
1238         {
1239                 rc = LDAP_SUCCESS;
1240                 goto DONE;
1241         }
1242
1243         /* Check source rules */
1244         if( authz_policy & SASL_AUTHZ_TO ) {
1245                 rc = slap_sasl_check_authz( op, authcDN, authzDN,
1246                         slap_schema.si_ad_saslAuthzTo, authcDN );
1247                 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
1248                         goto DONE;
1249                 }
1250         }
1251
1252         /* Check destination rules */
1253         if( authz_policy & SASL_AUTHZ_FROM ) {
1254                 rc = slap_sasl_check_authz( op, authzDN, authcDN,
1255                         slap_schema.si_ad_saslAuthzFrom, authcDN );
1256                 if( rc == LDAP_SUCCESS ) {
1257                         goto DONE;
1258                 }
1259         }
1260
1261         rc = LDAP_INAPPROPRIATE_AUTH;
1262
1263 DONE:
1264
1265 #ifdef NEW_LOGGING
1266         LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1267 #else
1268         Debug( LDAP_DEBUG_TRACE,
1269                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
1270 #endif
1271
1272         return( rc );
1273 }