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