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