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