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