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