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