]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
Fix dnValidate
[openldap] / servers / slapd / saslauthz.c
1 /*
2  * Copyright (c) 2000, Mark Adamson, Carnegie Mellon.  All rights reserved.
3  * This software is not subject to any license of Carnegie Mellon University.
4  *
5  * Redistribution and use in source and binary forms are permitted without 
6  * restriction or fee of any kind as long as this notice is preserved.
7  *
8  * The name "Carnegie Mellon" must not be used to endorse or promote
9  * products derived from this software without prior written permission.
10  *
11  */
12
13 #include "portable.h"
14
15 #include <ac/stdlib.h>
16 #include <stdio.h>
17
18 #include <ac/string.h>
19
20 #include "slap.h"
21
22 #ifdef HAVE_CYRUS_SASL
23 #include <limits.h>
24 #include <sasl.h>
25 #include <ldap_pvt.h>
26 #endif
27
28 /* URI format:  ldap://<host>/<base>[?[<attrs>][?[<scope>][?[<filter>]]]]   */
29
30 int slap_parseURI( char *uri, struct berval *searchbase, int *scope, Filter **filter )
31 {
32         char *start, *end;
33         struct berval bv;
34         int rc;
35
36
37         assert( uri != NULL );
38         searchbase->bv_val = NULL;
39         searchbase->bv_len = 0;
40         *scope = -1;
41         *filter = NULL;
42
43 #ifdef NEW_LOGGING
44         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
45                    "slap_parseURI: parsing %s\n", uri ));
46 #else
47         Debug( LDAP_DEBUG_TRACE, "slap_parseURI: parsing %s\n", uri, 0, 0 );
48 #endif
49
50
51         /* If it does not look like a URI, assume it is a DN */
52         if( !strncasecmp( uri, "dn:", 3 ) ) {
53                 uri += 3;
54                 uri += strspn( uri, " " );
55                 bv.bv_val = uri;
56                 /* FIXME: if dnNormalize actually uses input bv_len we
57                  * will have to make this right.
58                  */
59 is_dn:          bv.bv_len = 1;
60                 rc = dnNormalize2( NULL, &bv, searchbase );
61                 if (rc == LDAP_SUCCESS) {
62                         *scope = LDAP_SCOPE_BASE;
63                 }
64                 return( rc );
65         }
66         if( strncasecmp( uri, "ldap://", 7 ) ) {
67                 bv.bv_val = uri;
68                 goto is_dn;
69         }
70
71         end = strchr( uri + 7, '/' );
72         if ( end == NULL )
73                 return( LDAP_PROTOCOL_ERROR );
74
75         /* could check the hostname here */
76
77         /* Grab the searchbase */
78         start = end+1;
79         end = strchr( start, '?' );
80         bv.bv_val = start;
81         if( end == NULL ) {
82                 bv.bv_len = 1;
83                 return dnNormalize2( NULL, &bv, searchbase );
84         }
85         *end = '\0';
86         bv.bv_len = end - start;
87         rc = dnNormalize2( NULL, &bv, searchbase );
88         *end = '?';
89         if (rc != LDAP_SUCCESS)
90                 return( rc );
91
92         /* Skip the attrs */
93         start = end+1;
94         end = strchr( start, '?' );
95         if( end == NULL ) {
96                 return( LDAP_SUCCESS );
97         }
98
99         /* Grab the scope */
100         start = end+1;
101         if( !strncasecmp( start, "base?", 5 )) {
102                 *scope = LDAP_SCOPE_BASE;
103                 start += 5;
104         }
105         else if( !strncasecmp( start, "one?", 4 )) {
106                 *scope = LDAP_SCOPE_ONELEVEL;
107                 start += 4;
108         }
109         else if( !strncasecmp( start, "sub?", 3 )) {
110                 *scope = LDAP_SCOPE_SUBTREE;
111                 start += 4;
112         }
113         else {
114                 free( searchbase->bv_val );
115                 searchbase->bv_val = NULL;
116                 return( LDAP_PROTOCOL_ERROR );
117         }
118
119         /* Grab the filter */
120         *filter = str2filter( start );
121
122         return( LDAP_SUCCESS );
123 }
124
125
126
127
128
129 int slap_sasl_regexp_config( const char *match, const char *replace )
130 {
131 #ifdef HAVE_CYRUS_SASL
132         const char *c;
133         int rc, n;
134         SaslRegexp_t *reg;
135
136         SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
137           (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
138         reg = &( SaslRegexp[nSaslRegexp] );
139         reg->match = ch_strdup( match );
140         reg->replace = ch_strdup( replace );
141         dn_normalize( reg->match );
142         dn_normalize( reg->replace );
143
144         /* Precompile matching pattern */
145         rc = regcomp( &reg->workspace, reg->match, REG_EXTENDED|REG_ICASE );
146         if ( rc ) {
147 #ifdef NEW_LOGGING
148                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
149                            "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
150                            reg->match ));
151 #else
152                 Debug( LDAP_DEBUG_ANY,
153                 "SASL match pattern %s could not be compiled by regexp engine\n",
154                 reg->match, 0, 0 );
155 #endif
156
157                 return( LDAP_OPERATIONS_ERROR );
158         }
159
160         /* Precompile replace pattern. Find the $<n> placeholders */
161         reg->offset[0] = -2;
162         n = 1;
163         for ( c = reg->replace;  *c;  c++ ) {
164                 if ( *c == '\\' ) {
165                         c++;
166                         continue;
167                 }
168                 if ( *c == '$' ) {
169                         if ( n == SASLREGEX_REPLACE ) {
170 #ifdef NEW_LOGGING
171                                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
172                                            "slap_sasl_regexp_config: \"%s\" has too many $n placeholders (max %d)\n",
173                                            reg->replace, SASLREGEX_REPLACE ));
174 #else
175                                 Debug( LDAP_DEBUG_ANY,
176                                    "SASL replace pattern %s has too many $n placeholders (max %d)\n",
177                                    reg->replace, SASLREGEX_REPLACE, 0 );
178 #endif
179
180                                 return( LDAP_OPERATIONS_ERROR );
181                         }
182                         reg->offset[n] = c - reg->replace;
183                         n++;
184                 }
185         }
186
187         /* Final placeholder, after the last $n */
188         reg->offset[n] = c - reg->replace;
189         n++;
190         reg->offset[n] = -1;
191
192         nSaslRegexp++;
193 #endif
194         return( LDAP_SUCCESS );
195 }
196
197
198
199
200
201 #ifdef HAVE_CYRUS_SASL
202
203
204
205 /* Take the passed in SASL name and attempt to convert it into an
206    LDAP URI to find the matching LDAP entry, using the pattern matching
207    strings given in the saslregexp config file directive(s) */
208 static
209 char *slap_sasl_regexp( char *saslname )
210 {
211         char *uri=NULL;
212         int i, n, len, insert;
213         SaslRegexp_t *reg;
214
215
216 #ifdef NEW_LOGGING
217         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
218                    "slap_sasl_regexp: converting SASL name %s\n", saslname ));
219 #else
220         Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
221            saslname, 0, 0 );
222 #endif
223
224         if (( saslname == NULL ) || ( nSaslRegexp == 0 ))
225                 return( NULL );
226
227         /* Match the normalized SASL name to the saslregexp patterns */
228         for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
229                 if ( regexec( &reg->workspace, saslname, SASLREGEX_REPLACE,
230                   reg->strings, 0)  == 0 )
231                         break;
232         }
233
234         if( i >= nSaslRegexp )
235                 return( NULL );
236
237         /*
238          * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
239          * replace pattern of the form "x$1y$2z". The returned string needs
240          * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
241          */
242
243
244         /* Get the total length of the final URI */
245
246         n=1;
247         len = 0;
248         while( reg->offset[n] >= 0 ) {
249                 /* Len of next section from replacement string (x,y,z above) */
250                 len += reg->offset[n] - reg->offset[n-1] - 2;
251                 if( reg->offset[n+1] < 0)
252                         break;
253
254                 /* Len of string from saslname that matched next $i  (b,d above) */
255                 i = reg->replace[ reg->offset[n] + 1 ]  - '0';
256                 len += reg->strings[i].rm_eo - reg->strings[i].rm_so;
257                 n++;
258         }
259         uri = ch_malloc( len + 1 );
260
261         /* Fill in URI with replace string, replacing $i as we go */
262         n=1;
263         insert = 0;
264         while( reg->offset[n] >= 0) {
265                 /* Paste in next section from replacement string (x,y,z above) */
266                 len = reg->offset[n] - reg->offset[n-1] - 2;
267                 strncpy( uri+insert, reg->replace + reg->offset[n-1] + 2, len);
268                 insert += len;
269                 if( reg->offset[n+1] < 0)
270                         break;
271
272                 /* Paste in string from saslname that matched next $i  (b,d above) */
273                 i = reg->replace[ reg->offset[n] + 1 ]  - '0';
274                 len = reg->strings[i].rm_eo - reg->strings[i].rm_so;
275                 strncpy( uri+insert, saslname + reg->strings[i].rm_so, len );
276                 insert += len;
277
278                 n++;
279         }
280
281         uri[insert] = '\0';
282 #ifdef NEW_LOGGING
283         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
284                    "slap_sasl_regexp: converted SASL name to %s\n", uri ));
285 #else
286         Debug( LDAP_DEBUG_TRACE,
287            "slap_sasl_regexp: converted SASL name to %s\n", uri, 0, 0 );
288 #endif
289
290         return( uri );
291 }
292
293
294 /*
295  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
296  * return the LDAP DN to which it matches. The SASL regexp rules in the config
297  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
298  * search with scope=base), just return the URI (or its searchbase). Otherwise
299  * an internal search must be done, and if that search returns exactly one
300  * entry, return the DN of that one entry.
301  */
302
303 char *slap_sasl2dn( char *saslname )
304 {
305         char *uri=NULL;
306         struct berval searchbase = {0, NULL};
307         struct berval dn = {0, NULL};
308         struct berval ndn;
309         int rc, scope;
310         Backend *be;
311         Filter *filter=NULL;
312         Connection *conn=NULL;
313         LDAP *client=NULL;
314         LDAPMessage *res=NULL, *msg;
315
316 #ifdef NEW_LOGGING
317         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
318                 "slap_sasl2dn: converting SASL name %s to DN.\n", saslname ));
319 #else
320         Debug( LDAP_DEBUG_TRACE,
321                 "==>slap_sasl2dn: Converting SASL name %s to a DN\n", saslname, 0,0 );
322 #endif
323
324         /* Convert the SASL name into an LDAP URI */
325         uri = slap_sasl_regexp( saslname );
326         if( uri == NULL )
327                 goto FINISHED;
328
329         rc = slap_parseURI( uri, &searchbase, &scope, &filter );
330         if( rc ) {
331                 goto FINISHED;
332         }
333
334         /* Massive shortcut: search scope == base */
335         if( scope == LDAP_SCOPE_BASE ) {
336                 dn = searchbase;
337                 searchbase.bv_len = 0;
338                 searchbase.bv_val = NULL;
339                 goto FINISHED;
340         }
341
342         /* Must do an internal search */
343
344 #ifdef NEW_LOGGING
345         LDAP_LOG(( "sasl", LDAP_LEVEL_DETAIL1,
346                    "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
347                    searchbase.bv_val, scope ));
348 #else
349         Debug( LDAP_DEBUG_TRACE,
350            "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
351            searchbase.bv_val, scope, 0 );
352 #endif
353
354
355         be = select_backend( &searchbase, 0, 1 );
356         if(( be == NULL ) || ( be->be_search == NULL))
357                 goto FINISHED;
358         suffix_alias( be, &searchbase );
359
360         rc = connection_internal_open( &conn, &client, saslname );
361         if( rc != LDAP_SUCCESS )
362                 goto FINISHED;
363
364         (*be->be_search)( be, conn, STAILQ_FIRST(&conn->c_ops), /*base*/NULL, &searchbase,
365            scope, /*deref=*/1, /*sizelimit=*/1, /*time=*/0, filter, /*fstr=*/NULL,
366            /*attrs=*/NULL, /*attrsonly=*/0 );
367
368
369         /* Read the client side of the internal search */
370         rc = ldap_result( client, LDAP_RES_ANY, LDAP_MSG_ALL, NULL, &res );
371         if( rc == -1 )
372                 goto FINISHED;
373
374         /* Make sure exactly one entry was returned */
375         rc = ldap_count_entries( client, res );
376 #ifdef NEW_LOGGING
377         LDAP_LOG(( "sasl", LDAP_LEVEL_DETAIL1,
378                    "slap_sasl2dn: search DN returned %d entries\n", rc ));
379 #else
380         Debug( LDAP_DEBUG_TRACE,
381            "slap_sasl2dn: search DN returned %d entries\n", rc,0,0 );
382 #endif
383
384         if( rc != 1 )
385                 goto FINISHED;
386
387         msg = ldap_first_entry( client, res );
388         dn.bv_val = ldap_get_dn( client, msg );
389         dn.bv_len = dn.bv_val ? strlen( dn.bv_val ) : 0;
390         if( dn.bv_val ) {
391                 rc = dnNormalize2( NULL, &dn, &ndn );
392                 ldap_memfree( dn.bv_val );
393                 if( rc != LDAP_SUCCESS ) {
394                         dn.bv_val = NULL;
395                         dn.bv_len = 0;
396                         goto FINISHED;
397                 }
398                 dn = ndn;
399         }
400
401 FINISHED:
402         if( searchbase.bv_len ) ch_free( searchbase.bv_val );
403         if( filter ) filter_free( filter );
404         if( uri ) ch_free( uri );
405         if( conn ) connection_internal_close( conn );
406         if( res ) ldap_msgfree( res );
407         if( client  ) ldap_unbind( client );
408
409 #ifdef NEW_LOGGING
410         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
411                 "slap_sasl2dn: Converted SASL name to %s\n",
412                 dn.bv_len ? dn.bv_val : "<nothing>" ));
413 #else
414         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
415                 dn.bv_len ? dn.bv_val : "<nothing>", 0, 0 );
416 #endif
417
418         return( dn.bv_val );
419 }
420
421
422 /*
423  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
424  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
425  * the rule must be used as an internal search for entries. If that search
426  * returns the *assertDN entry, the match is successful.
427  *
428  * The assertDN should not have the dn: prefix
429  */
430
431 static
432 int slap_sasl_match( char *rule, char *assertDN, char *authc )
433 {
434         struct berval searchbase = {0, NULL};
435         int rc, scope;
436         Backend *be;
437         Filter *filter=NULL;
438         Connection *conn=NULL;
439         LDAP *client=NULL;
440         LDAPMessage *res=NULL, *msg;
441         regex_t reg;
442
443 #ifdef NEW_LOGGING
444         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
445                 "slap_sasl_match: comparing DN %s to rule %s\n", assertDN, rule ));
446 #else
447         Debug( LDAP_DEBUG_TRACE,
448            "===>slap_sasl_match: comparing DN %s to rule %s\n", assertDN, rule, 0 );
449 #endif
450
451         rc = slap_parseURI( rule, &searchbase, &scope, &filter );
452         if( rc != LDAP_SUCCESS )
453                 goto CONCLUDED;
454
455         /* Massive shortcut: search scope == base */
456         if( scope == LDAP_SCOPE_BASE ) {
457                 rc = regcomp(&reg, searchbase.bv_val,
458                         REG_EXTENDED|REG_ICASE|REG_NOSUB);
459                 if ( rc == 0 ) {
460                         rc = regexec(&reg, assertDN, 0, NULL, 0);
461                         regfree( &reg );
462                 }
463                 if ( rc == 0 )
464                         rc = LDAP_SUCCESS;
465                 else
466                         rc = LDAP_INAPPROPRIATE_AUTH;
467                 goto CONCLUDED;
468         }
469
470         /* Must run an internal search. */
471
472 #ifdef NEW_LOGGING
473         LDAP_LOG(( "sasl", LDAP_LEVEL_DETAIL1,
474                 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
475                 searchbase.bv_val, scope ));
476 #else
477         Debug( LDAP_DEBUG_TRACE,
478            "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
479            searchbase.bv_val, scope, 0 );
480 #endif
481
482         be = select_backend( &searchbase, 0, 1 );
483         if(( be == NULL ) || ( be->be_search == NULL)) {
484                 rc = LDAP_INAPPROPRIATE_AUTH;
485                 goto CONCLUDED;
486         }
487         suffix_alias( be, &searchbase );
488
489         /* Make an internal connection on which to run the search */
490         rc = connection_internal_open( &conn, &client, authc );
491         if( rc != LDAP_SUCCESS )
492                 goto CONCLUDED;
493
494         (*be->be_search)( be, conn, STAILQ_FIRST(&conn->c_ops), /*base=*/NULL, &searchbase,
495            scope, /*deref=*/1, /*sizelimit=*/0, /*time=*/0, filter, /*fstr=*/NULL,
496            /*attrs=*/NULL, /*attrsonly=*/0 );
497
498         /* On the client side of the internal search, read the results. Check
499            if the assertDN matches any of the DN's returned by the search */
500         rc = ldap_result( client, LDAP_RES_ANY, LDAP_MSG_ALL, NULL, &res );
501         if( rc == -1 )
502                 goto CONCLUDED;
503
504         for( msg=ldap_first_entry( client, res );
505               msg;
506               msg=ldap_next_entry( client, msg ) )
507         {
508                 struct berval dn;
509                 dn.bv_val = ldap_get_dn( client, msg );
510
511                 if( dn.bv_val ) {
512                         struct berval ndn;
513                         dn.bv_len = strlen( dn.bv_val );
514                         rc = dnNormalize2( NULL, &dn, &ndn );
515                         ldap_memfree( dn.bv_val );
516                         if( rc != LDAP_SUCCESS ) {
517                                 goto CONCLUDED;
518                         }
519                         rc = strcmp( ndn.bv_val, assertDN );
520                         free( ndn.bv_val );
521                         if( rc == 0 ) {
522                                 rc = LDAP_SUCCESS;
523                                 goto CONCLUDED;
524                         }
525                 }
526         }
527         rc = LDAP_INAPPROPRIATE_AUTH;
528
529 CONCLUDED:
530         if( searchbase.bv_len ) ch_free( searchbase.bv_val );
531         if( filter ) filter_free( filter );
532         if( conn ) connection_internal_close( conn );
533         if( res ) ldap_msgfree( res );
534         if( client  ) ldap_unbind( client );
535 #ifdef NEW_LOGGING
536         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
537                    "slap_sasl_match: comparison returned %d\n", rc ));
538 #else
539         Debug( LDAP_DEBUG_TRACE,
540            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
541 #endif
542
543         return( rc );
544 }
545
546
547
548
549
550 /*
551  * This function answers the question, "Can this ID authorize to that ID?",
552  * based on authorization rules. The rules are stored in the *searchDN, in the
553  * attribute named by *attr. If any of those rules map to the *assertDN, the
554  * authorization is approved.
555  *
556  * DN's passed in should have a dn: prefix
557  */
558
559 static int
560 slap_sasl_check_authz(char *searchDN, char *assertDN, char *attr, char *authc)
561 {
562         const char *errmsg;
563         int i, rc;
564         struct berval **vals=NULL;
565         AttributeDescription *ad=NULL;
566         struct berval bv;
567
568
569 #ifdef NEW_LOGGING
570         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
571                    "slap_sasl_check_authz: does %s match %s rule in %s?\n",
572                    assertDN, attr, searchDN ));
573 #else
574         Debug( LDAP_DEBUG_TRACE,
575            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
576            assertDN, attr, searchDN);
577 #endif
578
579         rc = slap_str2ad( attr, &ad, &errmsg );
580         if( rc != LDAP_SUCCESS )
581                 goto COMPLETE;
582
583         bv.bv_val = searchDN+3;
584         bv.bv_len = strlen(bv.bv_val);
585         rc = backend_attribute( NULL, NULL, NULL, NULL, &bv, ad, &vals );
586         if( rc != LDAP_SUCCESS )
587                 goto COMPLETE;
588
589         /* Check if the *assertDN matches any **vals */
590         for( i=0; vals[i] != NULL; i++ ) {
591                 rc = slap_sasl_match( vals[i]->bv_val, assertDN+3, authc );
592                 if ( rc == LDAP_SUCCESS )
593                         goto COMPLETE;
594         }
595         rc = LDAP_INAPPROPRIATE_AUTH;
596
597 COMPLETE:
598         if( vals ) ber_bvecfree( vals );
599
600 #ifdef NEW_LOGGING
601         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
602                    "slap_sasl_check_authz: %s check returning %s\n", attr, rc ));
603 #else
604         Debug( LDAP_DEBUG_TRACE,
605            "<==slap_sasl_check_authz: %s check returning %d\n", attr, rc, 0);
606 #endif
607
608         return( rc );
609 }
610
611
612
613 #endif  /* HAVE_CYRUS_SASL */
614
615
616
617
618
619 /* Check if a bind can SASL authorize to another identity.
620    Accepts authorization DN's with "dn:" prefix */
621
622 int slap_sasl_authorized( char *authcDN, char *authzDN )
623 {
624         int rc = LDAP_INAPPROPRIATE_AUTH;
625
626 #ifdef HAVE_CYRUS_SASL
627         /* User binding as anonymous */
628         if ( authzDN == NULL ) {
629                 rc = LDAP_SUCCESS;
630                 goto DONE;
631         }
632
633 #ifdef NEW_LOGGING
634         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
635                    "slap_sasl_authorized: can %s become %s?\n", authcDN, authzDN ));
636 #else
637         Debug( LDAP_DEBUG_TRACE,
638            "==>slap_sasl_authorized: can %s become %s?\n", authcDN, authzDN, 0 );
639 #endif
640
641
642         /* If person is authorizing to self, succeed */
643         if ( !strcmp( authcDN, authzDN ) ) {
644                 rc = LDAP_SUCCESS;
645                 goto DONE;
646         }
647
648         /* Check source rules */
649         rc = slap_sasl_check_authz( authcDN, authzDN, SASL_AUTHZ_SOURCE_ATTR,
650            authcDN );
651         if( rc == LDAP_SUCCESS ) {
652                 goto DONE;
653         }
654
655         /* Check destination rules */
656         rc = slap_sasl_check_authz( authzDN, authcDN, SASL_AUTHZ_DEST_ATTR,
657            authcDN );
658         if( rc == LDAP_SUCCESS ) {
659                 goto DONE;
660         }
661
662         rc = LDAP_INAPPROPRIATE_AUTH;
663
664 DONE:
665 #endif
666
667 #ifdef NEW_LOGGING
668         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
669                    "slap_sasl_authorized: return %d\n", rc ));
670 #else
671         Debug( LDAP_DEBUG_TRACE, "<== slap_sasl_authorized: return %d\n",rc,0,0 );
672 #endif
673
674         return( rc );
675 }