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