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