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