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