]> git.sur5r.net Git - openldap/blob - servers/slapd/sasl.c
Fix dnValidate
[openldap] / servers / slapd / sasl.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6
7 #include "portable.h"
8
9 #include <ac/stdlib.h>
10 #include <stdio.h>
11
12 #include <lber.h>
13 #include <ldap_log.h>
14
15 #include "slap.h"
16
17 #ifdef HAVE_CYRUS_SASL
18 #include <limits.h>
19 #include <sasl.h>
20
21 #include <ldap_pvt.h>
22
23 #ifdef SLAPD_SPASSWD
24 #include <lutil.h>
25 #endif
26
27
28 static sasl_security_properties_t sasl_secprops;
29
30 static int
31 slap_sasl_log(
32         void *context,
33         int priority,
34         const char *message) 
35 {
36         Connection *conn = context;
37         int level;
38         const char * label;
39
40         if ( message == NULL ) {
41                 return SASL_BADPARAM;
42         }
43
44         switch (priority) {
45         case SASL_LOG_ERR:
46                 level = LDAP_DEBUG_ANY;
47                 label = "Error";
48                 break;
49         case SASL_LOG_WARNING:
50                 level = LDAP_DEBUG_TRACE;
51                 label = "Warning";
52                 break;
53         case SASL_LOG_INFO:
54                 level = LDAP_DEBUG_TRACE;
55                 label = "Info";
56                 break;
57         default:
58                 return SASL_BADPARAM;
59         }
60
61 #ifdef NEW_LOGGING
62         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
63                 "SASL [conn=%ld] %s: %s\n",
64                 conn ? conn->c_connid : -1,
65                 label, message ));
66 #else
67         Debug( level, "SASL [conn=%ld] %s: %s\n",
68                 conn ? conn->c_connid: -1,
69                 label, message );
70 #endif
71
72
73         return SASL_OK;
74 }
75
76
77 /* Take any sort of identity string and return a DN with the "dn:" prefix. The
78    string returned in *dnptr is in its own allocated memory, and must be free'd 
79    by the calling process.
80    -Mark Adamson, Carnegie Mellon
81 */
82
83 int slap_sasl_getdn( Connection *conn, char *id, char **dnptr, int flags )
84 {
85         char *c=NULL, *c1, *dn=NULL;
86         int rc, len;
87         sasl_conn_t *ctx;
88
89
90 #ifdef NEW_LOGGING
91         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
92                 "slap_sasl_getdn: conn %d id=%s\n",
93                 conn ? conn->c_connid : -1,
94                 id ? (*id ? id : "<empty>") : "NULL" ));
95 #else
96         Debug( LDAP_DEBUG_ARGS, "slap_sasl_getdn: id=%s\n", 
97       id?(*id?id:"<empty>"):"NULL",0,0 );
98 #endif
99
100
101         /* Blatantly anonymous ID */
102         len = sizeof( "anonymous" ) - 1;
103         if( id && ( id[len] == '\0' || id[len] == '@' ) &&
104                 !strncasecmp( id, "anonymous", len) ) {
105                 *dnptr = NULL;
106                 return( LDAP_SUCCESS );
107         }
108         ctx = conn->c_sasl_context;
109         len = strlen( id );
110
111         /* An authcID needs to be converted to authzID form */
112         if( flags & FLAG_GETDN_AUTHCID ) {
113                 if( sasl_external_x509dn_convert && conn->c_sasl_bind_mech
114                         && ( strcasecmp( "EXTERNAL", conn->c_sasl_bind_mech ) == 0 ) 
115                         && len && id[0] == '/' /* && id[len-1]== '/' */)
116                 {
117                         /* check SASL external for X.509 style DN and */
118                         /* convert to dn:<dn> form */
119                         char *tmpdn = ldap_dcedn2dn( id );
120                         len = strlen( tmpdn );
121
122                         dn = ch_malloc( len+4 );
123                         dn[0] = 'd';
124                         dn[1] = 'n';
125                         dn[2] = ':';
126                         memmove( &dn[3], tmpdn, len+1 );
127                         len += 3;
128
129                 } else {
130                         /* convert to u:<username> form */
131                         dn = ch_malloc( len+3 );
132                         dn[0] = 'u';
133                         dn[1] = ':';
134                         memmove( &dn[2], id, len+1 );
135                         len += 2;
136                 }
137         } else {
138                 dn = ch_strdup( id );
139         }
140
141         /* An authzID must be properly prefixed */
142         if( flags & FLAG_GETDN_AUTHZID
143                 && strncasecmp( dn, "u:", 2 )
144                 && strncasecmp( dn, "dn:", 3 ) )
145         {
146                 ch_free( dn );
147                 *dnptr = NULL;
148                 return( LDAP_INAPPROPRIATE_AUTH );
149         }
150
151         /* Username strings */
152         if( !strncasecmp( dn, "u:", 2 ) ) {
153                 int len1  = strlen( ",cn=auth" );
154                 len += strlen( "dn:uid=" ) + len1;
155
156                 /* Figure out how much data we have for the dn */
157                 rc = sasl_getprop( ctx, SASL_REALM, (void **)&c );
158                 if( rc != SASL_OK && rc != SASL_NOTDONE ) {
159 #ifdef NEW_LOGGING
160                         LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
161                                 "slap_sasl_getdn: getprop(REALM) failed.\n" ));
162 #else
163                         Debug(LDAP_DEBUG_TRACE,
164                                 "getdn: getprop(REALM) failed!\n", 0,0,0);
165 #endif
166
167                         ch_free( dn );
168                         *dnptr = NULL;
169                         return( LDAP_OPERATIONS_ERROR );
170                 }
171
172                 if( c && *c ) {
173                         len += strlen( c ) + strlen(",cn=" );
174                 }
175
176                 if( conn->c_sasl_bind_mech ) {
177                         len += strlen( conn->c_sasl_bind_mech ) + strlen( ",cn=" );
178                 }
179
180                 /* Build the new dn */
181                 c1 = dn;
182                 dn = ch_malloc( len );
183                 len = sprintf( dn, "dn:uid=%s", c1+2 );
184                 ch_free( c1 );
185
186                 if( c ) {
187                         len += sprintf( dn+len, ",cn=%s", c );
188                 }
189                 if( conn->c_sasl_bind_mech ) {
190                         len += sprintf( dn+len, ",cn=%s", conn->c_sasl_bind_mech );
191                 }
192                 strcpy( dn+len, ",cn=auth" );
193                 len += len1;
194
195 #ifdef NEW_LOGGING
196                 LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
197                         "slap_sasl_getdn: u:id converted to %s.\n", dn ));
198 #else
199                 Debug( LDAP_DEBUG_TRACE, "getdn: u:id converted to %s\n", dn,0,0 );
200 #endif
201         }
202
203         /* DN strings that are a cn=auth identity to run through regexp */
204         if( !strncasecmp( dn, "dn:", 3) &&
205                 ( ( flags & FLAG_GETDN_FINAL ) == 0 ) )
206         {
207                 c1 = slap_sasl2dn( dn + 3 );
208                 if( c1 ) {
209                         ch_free( dn );
210                         dn = c1;
211                         /* Reaffix the dn: prefix if it was removed */
212                         if( strncasecmp( dn, "dn:", 3) ) {
213                                 c1 = dn;
214                                 dn = ch_malloc( strlen( c1 ) + 4 );
215                                 sprintf( dn, "dn:%s", c1 );
216                                 ch_free( c1 );
217                         }
218
219 #ifdef NEW_LOGGING
220                         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
221                                 "slap_sasl_getdn: dn:id converted to %s.\n", dn ));
222 #else
223                         Debug( LDAP_DEBUG_TRACE, "getdn: dn:id converted to %s\n",
224                                 dn, 0, 0 );
225 #endif
226                 }
227         }
228
229         if( ( flags & FLAG_GETDN_FINAL ) == 0 )  {
230                 dn_normalize( dn+3 );
231         }
232
233         *dnptr = dn;
234         return( LDAP_SUCCESS );
235 }
236
237
238
239 static int
240 slap_sasl_authorize(
241         void *context,
242         const char *authcid,
243         const char *authzid,
244         const char **user,
245         const char **errstr)
246 {
247         char *authcDN, *authzDN;
248         int rc;
249         Connection *conn = context;
250
251         *user = NULL;
252
253 #ifdef NEW_LOGGING
254         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
255                    "slap_sas_authorize: conn %d  authcid=\"%s\" authzid=\"%s\"\n",
256                    conn ? conn->c_connid : -1,
257                    authcid ? authcid : "<empty>",
258                    authzid ? authzid : "<empty>" ));
259 #else
260         Debug( LDAP_DEBUG_ARGS, "SASL Authorize [conn=%ld]: "
261                 "authcid=\"%s\" authzid=\"%s\"\n",
262                 (long) (conn ? conn->c_connid : -1),
263                 authcid ? authcid : "<empty>",
264                 authzid ? authzid : "<empty>" );
265 #endif
266
267
268         /* Convert the identities to DN's. If no authzid was given, client will
269            be bound as the DN matching their username */
270         rc = slap_sasl_getdn( conn, (char *)authcid, &authcDN, FLAG_GETDN_AUTHCID );
271         if( rc != LDAP_SUCCESS ) {
272                 *errstr = ldap_err2string( rc );
273                 return SASL_NOAUTHZ;
274         }
275         if( ( authzid == NULL ) || !strcmp( authcid,authzid ) ) {
276 #ifdef NEW_LOGGING
277                 LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
278                            "slap_sasl_authorize: conn %d  Using authcDN=%s\n",
279                            conn ? conn->c_connid : -1, authcDN ));
280 #else
281                 Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
282                  "Using authcDN=%s\n", (long) (conn ? conn->c_connid : -1), authcDN,0 );
283 #endif
284
285                 *user = authcDN;
286                 *errstr = NULL;
287                 return SASL_OK;
288         }
289         rc = slap_sasl_getdn( conn, (char *)authzid, &authzDN, FLAG_GETDN_AUTHZID );
290         if( rc != LDAP_SUCCESS ) {
291                 ch_free( authcDN );
292                 *errstr = ldap_err2string( rc );
293                 return SASL_NOAUTHZ;
294         }
295
296         rc = slap_sasl_authorized( authcDN, authzDN );
297         if( rc ) {
298 #ifdef NEW_LOGGING
299                 LDAP_LOG(( "sasl", LDAP_LEVEL_INFO,
300                            "slap_sasl_authorize: conn %ld  authorization disallowed (%d)\n",
301                            (long)(conn ? conn->c_connid : -1), rc ));
302 #else
303                 Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
304                         " authorization disallowed (%d)\n",
305                         (long) (conn ? conn->c_connid : -1), rc, 0 );
306 #endif
307
308                 *errstr = "not authorized";
309                 ch_free( authcDN );
310                 ch_free( authzDN );
311                 return SASL_NOAUTHZ;
312         }
313
314 #ifdef NEW_LOGGING
315         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
316                    "slap_sasl_authorize: conn %d authorization allowed\n",
317                    (long)(conn ? conn->c_connid : -1 ) ));
318 #else
319         Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
320                 " authorization allowed\n",
321                 (long) (conn ? conn->c_connid : -1), 0, 0 );
322 #endif
323
324
325         ch_free( authcDN );
326         *user = authzDN;
327         *errstr = NULL;
328         return SASL_OK;
329 }
330
331
332 static int
333 slap_sasl_err2ldap( int saslerr )
334 {
335         int rc;
336
337         switch (saslerr) {
338                 case SASL_CONTINUE:
339                         rc = LDAP_SASL_BIND_IN_PROGRESS;
340                         break;
341                 case SASL_FAIL:
342                         rc = LDAP_OTHER;
343                         break;
344                 case SASL_NOMEM:
345                         rc = LDAP_OTHER;
346                         break;
347                 case SASL_NOMECH:
348                         rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
349                         break;
350                 case SASL_BADAUTH:
351                         rc = LDAP_INVALID_CREDENTIALS;
352                         break;
353                 case SASL_NOAUTHZ:
354                         rc = LDAP_INSUFFICIENT_ACCESS;
355                         break;
356                 case SASL_TOOWEAK:
357                 case SASL_ENCRYPT:
358                         rc = LDAP_INAPPROPRIATE_AUTH;
359                         break;
360                 default:
361                         rc = LDAP_OTHER;
362                         break;
363         }
364
365         return rc;
366 }
367 #endif
368
369
370 int slap_sasl_init( void )
371 {
372 #ifdef HAVE_CYRUS_SASL
373         int rc;
374         static sasl_callback_t server_callbacks[] = {
375                 { SASL_CB_LOG, &slap_sasl_log, NULL },
376                 { SASL_CB_LIST_END, NULL, NULL }
377         };
378
379         sasl_set_alloc(
380                 ch_malloc,
381                 ch_calloc,
382                 ch_realloc,
383                 ch_free ); 
384
385         sasl_set_mutex(
386                 ldap_pvt_sasl_mutex_new,
387                 ldap_pvt_sasl_mutex_lock,
388                 ldap_pvt_sasl_mutex_unlock,
389                 ldap_pvt_sasl_mutex_dispose );
390
391         /* should provide callbacks for logging */
392         /* server name should be configurable */
393         rc = sasl_server_init( server_callbacks, "slapd" );
394
395         if( rc != SASL_OK ) {
396 #ifdef NEW_LOGGING
397                 LDAP_LOG(( "sasl", LDAP_LEVEL_INFO,
398                            "slap_sasl_init: init failed.\n" ));
399 #else
400                 Debug( LDAP_DEBUG_ANY, "sasl_server_init failed\n",
401                         0, 0, 0 );
402 #endif
403
404                 return -1;
405         }
406
407 #ifdef NEW_LOGGING
408         LDAP_LOG(( "sasl", LDAP_LEVEL_INFO,
409                    "slap_sasl_init: initialized!\n"));
410 #else
411         Debug( LDAP_DEBUG_TRACE, "slap_sasl_init: initialized!\n",
412                 0, 0, 0 );
413 #endif
414
415
416         /* default security properties */
417         memset( &sasl_secprops, '\0', sizeof(sasl_secprops) );
418     sasl_secprops.max_ssf = INT_MAX;
419     sasl_secprops.maxbufsize = 65536;
420     sasl_secprops.security_flags = SASL_SEC_NOPLAINTEXT|SASL_SEC_NOANONYMOUS;
421 #endif
422
423         return 0;
424 }
425
426 int slap_sasl_destroy( void )
427 {
428 #ifdef HAVE_CYRUS_SASL
429         sasl_done();
430 #endif
431         free( global_host );
432         global_host = NULL;
433
434         return 0;
435 }
436
437 int slap_sasl_open( Connection *conn )
438 {
439         int sc = LDAP_SUCCESS;
440
441 #ifdef HAVE_CYRUS_SASL
442         sasl_conn_t *ctx = NULL;
443         sasl_callback_t *session_callbacks;
444
445         assert( conn->c_sasl_context == NULL );
446         assert( conn->c_sasl_extra == NULL );
447
448         conn->c_sasl_layers = 0;
449
450         session_callbacks =
451                 ch_calloc( 3, sizeof(sasl_callback_t));
452         conn->c_sasl_extra = session_callbacks;
453
454         session_callbacks[0].id = SASL_CB_LOG;
455         session_callbacks[0].proc = &slap_sasl_log;
456         session_callbacks[0].context = conn;
457
458         session_callbacks[1].id = SASL_CB_PROXY_POLICY;
459         session_callbacks[1].proc = &slap_sasl_authorize;
460         session_callbacks[1].context = conn;
461
462         session_callbacks[2].id = SASL_CB_LIST_END;
463         session_callbacks[2].proc = NULL;
464         session_callbacks[2].context = NULL;
465
466         if( global_host == NULL ) {
467                 global_host = ldap_pvt_get_fqdn( NULL );
468         }
469
470         /* create new SASL context */
471         sc = sasl_server_new( "ldap", global_host, global_realm,
472                 session_callbacks, SASL_SECURITY_LAYER, &ctx );
473
474         if( sc != SASL_OK ) {
475 #ifdef NEW_LOGGING
476                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
477                            "slap_sasl_open: sasl_server_new failed: %d\n", sc ));
478 #else
479                 Debug( LDAP_DEBUG_ANY, "sasl_server_new failed: %d\n",
480                         sc, 0, 0 );
481 #endif
482
483                 return -1;
484         }
485
486         conn->c_sasl_context = ctx;
487
488         if( sc == SASL_OK ) {
489                 sc = sasl_setprop( ctx,
490                         SASL_SEC_PROPS, &sasl_secprops );
491
492                 if( sc != SASL_OK ) {
493 #ifdef NEW_LOGGING
494                         LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
495                                    "slap_sasl_open: sasl_setprop failed: %d \n", sc ));
496 #else
497                         Debug( LDAP_DEBUG_ANY, "sasl_setprop failed: %d\n",
498                                 sc, 0, 0 );
499 #endif
500
501                         slap_sasl_close( conn );
502                         return -1;
503                 }
504         }
505
506         sc = slap_sasl_err2ldap( sc );
507 #endif
508         return sc;
509 }
510
511 int slap_sasl_external(
512         Connection *conn,
513         slap_ssf_t ssf,
514         const char *auth_id )
515 {
516 #ifdef HAVE_CYRUS_SASL
517         int sc;
518         sasl_conn_t *ctx = conn->c_sasl_context;
519         sasl_external_properties_t extprops;
520
521         if ( ctx == NULL ) {
522                 return LDAP_UNAVAILABLE;
523         }
524
525         memset( &extprops, '\0', sizeof(extprops) );
526         extprops.ssf = ssf;
527         extprops.auth_id = (char *) auth_id;
528
529         sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
530                 (void *) &extprops );
531
532         if ( sc != SASL_OK ) {
533                 return LDAP_OTHER;
534         }
535 #endif
536
537         return LDAP_SUCCESS;
538 }
539
540 int slap_sasl_reset( Connection *conn )
541 {
542 #ifdef HAVE_CYRUS_SASL
543         sasl_conn_t *ctx = conn->c_sasl_context;
544
545         if( ctx != NULL ) {
546         }
547 #endif
548         /* must return "anonymous" */
549         return LDAP_SUCCESS;
550 }
551
552 char ** slap_sasl_mechs( Connection *conn )
553 {
554         char **mechs = NULL;
555
556 #ifdef HAVE_CYRUS_SASL
557         sasl_conn_t *ctx = conn->c_sasl_context;
558
559         if( ctx != NULL ) {
560                 int sc;
561                 char *mechstr;
562
563                 sc = sasl_listmech( ctx,
564                         NULL, NULL, ",", NULL,
565                         &mechstr, NULL, NULL );
566
567                 if( sc != SASL_OK ) {
568 #ifdef NEW_LOGGING
569                         LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
570                                    "slap_sasl_mechs: sasl_listmech failed: %d\n", sc ));
571 #else
572                         Debug( LDAP_DEBUG_ANY, "slap_sasl_listmech failed: %d\n",
573                                 sc, 0, 0 );
574 #endif
575
576                         return NULL;
577                 }
578
579                 mechs = str2charray( mechstr, "," );
580
581                 ch_free( mechstr );
582         }
583 #endif
584
585         return mechs;
586 }
587
588 int slap_sasl_close( Connection *conn )
589 {
590 #ifdef HAVE_CYRUS_SASL
591         sasl_conn_t *ctx = conn->c_sasl_context;
592
593         if( ctx != NULL ) {
594                 sasl_dispose( &ctx );
595         }
596
597         conn->c_sasl_context = NULL;
598
599         free( conn->c_sasl_extra );
600         conn->c_sasl_extra = NULL;
601 #endif
602
603         return LDAP_SUCCESS;
604 }
605
606 int slap_sasl_bind(
607     Connection          *conn,
608     Operation           *op,  
609     struct berval       *dn,  
610     struct berval       *ndn,
611     struct berval       *cred,
612         char                    **edn,
613         slap_ssf_t              *ssfp )
614 {
615         int rc = 1;
616
617 #ifdef HAVE_CYRUS_SASL
618         sasl_conn_t *ctx = conn->c_sasl_context;
619         struct berval response;
620         unsigned reslen = 0;
621         const char *errstr;
622         int sc;
623
624 #ifdef NEW_LOGGING
625         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
626                 "sasl_bind: conn %ld dn=\"%s\" mech=%s datalen=%ld\n",
627                 conn->c_connid,
628                 dn->bv_len ? dn->bv_val : "",
629                 conn->c_sasl_bind_in_progress ? "<continuing>" : conn->c_sasl_bind_mech,
630                 cred ? cred->bv_len : 0 ));
631 #else
632         Debug(LDAP_DEBUG_ARGS,
633                 "==> sasl_bind: dn=\"%s\" mech=%s datalen=%ld\n",
634                 dn->bv_len ? dn->bv_val : "",
635                 conn->c_sasl_bind_in_progress ? "<continuing>":conn->c_sasl_bind_mech,
636                 cred ? cred->bv_len : 0 );
637 #endif
638
639
640         if( ctx == NULL ) {
641                 send_ldap_result( conn, op, LDAP_UNAVAILABLE,
642                         NULL, "SASL unavailable on this session", NULL, NULL );
643                 return rc;
644         }
645
646         if ( !conn->c_sasl_bind_in_progress ) {
647                 sc = sasl_server_start( ctx,
648                         conn->c_sasl_bind_mech,
649                         cred->bv_len ? cred->bv_val : "",
650                         cred->bv_len,
651                         (char **)&response.bv_val, &reslen, &errstr );
652
653         } else {
654                 sc = sasl_server_step( ctx,
655                         cred->bv_val, cred->bv_len,
656                         (char **)&response.bv_val, &reslen, &errstr );
657         }
658
659         response.bv_len = reslen;
660
661         if ( sc == SASL_OK ) {
662                 char *username = NULL;
663
664                 sc = sasl_getprop( ctx,
665                         SASL_USERNAME, (void **)&username );
666
667                 if ( sc != SASL_OK ) {
668 #ifdef NEW_LOGGING
669                         LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
670                                 "slap_sasl_bind: getprop(USERNAME) failed: %d\n", sc ));
671 #else
672                         Debug(LDAP_DEBUG_TRACE,
673                                 "slap_sasl_bind: getprop(USERNAME) failed!\n",
674                                 0, 0, 0);
675 #endif
676
677
678                         send_ldap_result( conn, op, rc = slap_sasl_err2ldap( sc ),
679                                 NULL, "no SASL username", NULL, NULL );
680
681                 } else {
682                         rc = slap_sasl_getdn( conn, username, edn, FLAG_GETDN_FINAL );
683
684                         if( rc == LDAP_SUCCESS ) {
685                                 int i;
686                                 sasl_ssf_t *ssf = NULL;
687                                 (void) sasl_getprop( ctx, SASL_SSF, (void *)&ssf );
688                                 *ssfp = ssf ? *ssf : 0;
689
690                                 if( *ssfp ) {
691                                         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
692                                         conn->c_sasl_layers++;
693                                         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
694                                 }
695
696                                 /* Store the authorization DN as a subjectDN */
697                                 if ( *edn ) {
698                                         i = 2;
699                                         do {
700                                                 i++;
701                                                 (*edn)[i-3] = (*edn)[i];
702                                         } while( (*edn)[i] );
703                                 }
704
705                                 send_ldap_sasl( conn, op, rc,
706                                         NULL, NULL, NULL, NULL,
707                                         response.bv_len ? &response : NULL );
708
709                         } else {
710                                 send_ldap_result( conn, op, rc,
711                                         NULL, errstr, NULL, NULL );
712                         }
713                 }
714
715         } else if ( sc == SASL_CONTINUE ) {
716                 send_ldap_sasl( conn, op, rc = LDAP_SASL_BIND_IN_PROGRESS,
717                         NULL, NULL, NULL, NULL, &response );
718
719         } else {
720                 send_ldap_result( conn, op, rc = slap_sasl_err2ldap( sc ),
721                         NULL, errstr, NULL, NULL );
722         }
723
724         if( response.bv_len ) {
725                 ch_free( response.bv_val );
726         }
727
728 #ifdef NEW_LOGGING
729         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
730                 "slap_sasl_bind: rc=%d\n", rc ));
731 #else
732         Debug(LDAP_DEBUG_TRACE, "<== slap_sasl_bind: rc=%d\n", rc, 0, 0);
733 #endif
734
735
736 #else
737         send_ldap_result( conn, op, rc = LDAP_UNAVAILABLE,
738                 NULL, "SASL not supported", NULL, NULL );
739 #endif
740
741         return rc;
742 }
743
744 char* slap_sasl_secprops( const char *in )
745 {
746 #ifdef HAVE_CYRUS_SASL
747         int rc = ldap_pvt_sasl_secprops( in, &sasl_secprops );
748
749         return rc == LDAP_SUCCESS ? NULL : "Invalid security properties";
750 #else
751         return "SASL not supported";
752 #endif
753 }