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