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