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