]> git.sur5r.net Git - openldap/blob - libraries/libldap/cyrus.c
aad8f8ed8843a34ff4e9cadbdc7430c0d8f660d9
[openldap] / libraries / libldap / cyrus.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2008 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15
16 #include "portable.h"
17
18 #include <stdio.h>
19
20 #include <ac/socket.h>
21 #include <ac/stdlib.h>
22 #include <ac/string.h>
23 #include <ac/time.h>
24 #include <ac/errno.h>
25 #include <ac/ctype.h>
26 #include <ac/unistd.h>
27
28 #ifdef HAVE_LIMITS_H
29 #include <limits.h>
30 #endif
31
32 #include "ldap-int.h"
33
34 #ifdef HAVE_CYRUS_SASL
35
36 #ifdef HAVE_LIMITS_H
37 #include <limits.h>
38 #endif
39
40 #ifndef INT_MAX
41 #define INT_MAX 2147483647      /* 32 bit signed max */
42 #endif
43
44 #ifdef LDAP_R_COMPILE
45 ldap_pvt_thread_mutex_t ldap_int_sasl_mutex;
46 #endif
47
48 #ifdef HAVE_SASL_SASL_H
49 #include <sasl/sasl.h>
50 #else
51 #include <sasl.h>
52 #endif
53
54 #if SASL_VERSION_MAJOR >= 2
55 #define SASL_CONST const
56 #else
57 #define SASL_CONST
58 #endif
59
60 /*
61 * Various Cyrus SASL related stuff.
62 */
63
64 static const sasl_callback_t client_callbacks[] = {
65 #ifdef SASL_CB_GETREALM
66         { SASL_CB_GETREALM, NULL, NULL },
67 #endif
68         { SASL_CB_USER, NULL, NULL },
69         { SASL_CB_AUTHNAME, NULL, NULL },
70         { SASL_CB_PASS, NULL, NULL },
71         { SASL_CB_ECHOPROMPT, NULL, NULL },
72         { SASL_CB_NOECHOPROMPT, NULL, NULL },
73         { SASL_CB_LIST_END, NULL, NULL }
74 };
75
76 int ldap_int_sasl_init( void )
77 {
78         /* XXX not threadsafe */
79         static int sasl_initialized = 0;
80
81 #ifdef HAVE_SASL_VERSION
82         /* stringify the version number, sasl.h doesn't do it for us */
83 #define VSTR0(maj, min, pat)    #maj "." #min "." #pat
84 #define VSTR(maj, min, pat)     VSTR0(maj, min, pat)
85 #define SASL_VERSION_STRING     VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \
86                                 SASL_VERSION_STEP)
87         { int rc;
88         sasl_version( NULL, &rc );
89         if ( ((rc >> 16) != ((SASL_VERSION_MAJOR << 8)|SASL_VERSION_MINOR)) ||
90                 (rc & 0xffff) < SASL_VERSION_STEP) {
91                 char version[sizeof("xxx.xxx.xxxxx")];
92                 sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff,
93                         rc & 0xffff );
94
95                 Debug( LDAP_DEBUG_ANY,
96                 "ldap_int_sasl_init: SASL library version mismatch:"
97                 " expected " SASL_VERSION_STRING ","
98                 " got %s\n", version, 0, 0 );
99                 return -1;
100         }
101         }
102 #endif
103         if ( sasl_initialized ) {
104                 return 0;
105         }
106
107 /* SASL 2 takes care of its own memory completely internally */
108 #if SASL_VERSION_MAJOR < 2 && !defined(CSRIMALLOC)
109         sasl_set_alloc(
110                 ber_memalloc,
111                 ber_memcalloc,
112                 ber_memrealloc,
113                 ber_memfree );
114 #endif /* CSRIMALLOC */
115
116 #ifdef LDAP_R_COMPILE
117         sasl_set_mutex(
118                 ldap_pvt_sasl_mutex_new,
119                 ldap_pvt_sasl_mutex_lock,
120                 ldap_pvt_sasl_mutex_unlock,    
121                 ldap_pvt_sasl_mutex_dispose );    
122 #endif
123
124         if ( sasl_client_init( NULL ) == SASL_OK ) {
125                 sasl_initialized = 1;
126                 return 0;
127         }
128
129 #if SASL_VERSION_MAJOR < 2
130         /* A no-op to make sure we link with Cyrus 1.5 */
131         sasl_client_auth( NULL, NULL, NULL, 0, NULL, NULL );
132 #endif
133         return -1;
134 }
135
136 static void
137 sb_sasl_cyrus_init(
138         struct sb_sasl_generic_data *p,
139         ber_len_t *min_send,
140         ber_len_t *max_send,
141         ber_len_t *max_recv)
142 {
143         sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
144         ber_len_t maxbuf;
145
146         sasl_getprop( sasl_context, SASL_MAXOUTBUF,
147                       (SASL_CONST void **)(char *) &maxbuf );
148
149         *min_send = SASL_MIN_BUFF_SIZE;
150         *max_send = maxbuf;
151         *max_recv = SASL_MAX_BUFF_SIZE;
152 }
153
154 static ber_int_t
155 sb_sasl_cyrus_encode(
156         struct sb_sasl_generic_data *p,
157         unsigned char *buf,
158         ber_len_t len,
159         Sockbuf_Buf *dst)
160 {
161         sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
162         ber_int_t ret;
163         unsigned tmpsize = dst->buf_size;
164
165         ret = sasl_encode( sasl_context, (char *)buf, len,
166                            (SASL_CONST char **)&dst->buf_base,
167                            &tmpsize );
168
169         dst->buf_size = tmpsize;
170         dst->buf_end = dst->buf_size;
171
172         if ( ret != SASL_OK ) {
173                 ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
174                                 "sb_sasl_cyrus_encode: failed to encode packet: %s\n",
175                                 sasl_errstring( ret, NULL, NULL ) );
176                 return -1;
177         }
178
179         return 0;
180 }
181
182 static ber_int_t
183 sb_sasl_cyrus_decode(
184         struct sb_sasl_generic_data *p,
185         const Sockbuf_Buf *src,
186         Sockbuf_Buf *dst)
187 {
188         sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
189         ber_int_t ret;
190         unsigned tmpsize = dst->buf_size;
191
192         ret = sasl_decode( sasl_context,
193                            src->buf_base, src->buf_end,
194                            (SASL_CONST char **)&dst->buf_base,
195                            (unsigned *)&tmpsize );
196
197
198         dst->buf_size = tmpsize;
199         dst->buf_end = dst->buf_size;
200
201         if ( ret != SASL_OK ) {
202                 ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
203                                 "sb_sasl_cyrus_decode: failed to decode packet: %s\n",
204                                 sasl_errstring( ret, NULL, NULL ) );
205                 return -1;
206         }
207
208         return 0;
209 }
210
211 static void
212 sb_sasl_cyrus_reset_buf(
213         struct sb_sasl_generic_data *p,
214         Sockbuf_Buf *buf)
215 {
216 #if SASL_VERSION_MAJOR >= 2
217         ber_pvt_sb_buf_init( buf );
218 #else
219         ber_pvt_sb_buf_destroy( buf );
220 #endif
221 }
222
223 static void
224 sb_sasl_cyrus_fini(
225         struct sb_sasl_generic_data *p)
226 {
227 #if SASL_VERSION_MAJOR >= 2
228         /*
229          * SASLv2 encode/decode buffers are managed by
230          * libsasl2. Ensure they are not freed by liblber.
231          */
232         p->buf_in.buf_base = NULL;
233         p->buf_out.buf_base = NULL;
234 #endif
235 }
236
237 static const struct sb_sasl_generic_ops sb_sasl_cyrus_ops = {
238         sb_sasl_cyrus_init,
239         sb_sasl_cyrus_encode,
240         sb_sasl_cyrus_decode,
241         sb_sasl_cyrus_reset_buf,
242         sb_sasl_cyrus_fini
243  };
244
245 int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg )
246 {
247         struct sb_sasl_generic_install install_arg;
248
249         install_arg.ops         = &sb_sasl_cyrus_ops;
250         install_arg.ops_private = ctx_arg;
251
252         return ldap_pvt_sasl_generic_install( sb, &install_arg );
253 }
254
255 void ldap_pvt_sasl_remove( Sockbuf *sb )
256 {
257         ldap_pvt_sasl_generic_remove( sb );
258 }
259
260 static int
261 sasl_err2ldap( int saslerr )
262 {
263         int rc;
264
265         /* map SASL errors to LDAP API errors returned by:
266          *      sasl_client_new()
267          *              SASL_OK, SASL_NOMECH, SASL_NOMEM
268          *      sasl_client_start()
269          *              SASL_OK, SASL_NOMECH, SASL_NOMEM, SASL_INTERACT
270          *      sasl_client_step()
271          *              SASL_OK, SASL_INTERACT, SASL_BADPROT, SASL_BADSERV
272          */
273
274         switch (saslerr) {
275                 case SASL_CONTINUE:
276                         rc = LDAP_MORE_RESULTS_TO_RETURN;
277                         break;
278                 case SASL_INTERACT:
279                         rc = LDAP_LOCAL_ERROR;
280                         break;
281                 case SASL_OK:
282                         rc = LDAP_SUCCESS;
283                         break;
284                 case SASL_NOMEM:
285                         rc = LDAP_NO_MEMORY;
286                         break;
287                 case SASL_NOMECH:
288                         rc = LDAP_AUTH_UNKNOWN;
289                         break;
290                 case SASL_BADPROT:
291                         rc = LDAP_DECODING_ERROR;
292                         break;
293                 case SASL_BADSERV:
294                         rc = LDAP_AUTH_UNKNOWN;
295                         break;
296
297                 /* other codes */
298                 case SASL_BADAUTH:
299                         rc = LDAP_AUTH_UNKNOWN;
300                         break;
301                 case SASL_NOAUTHZ:
302                         rc = LDAP_PARAM_ERROR;
303                         break;
304                 case SASL_FAIL:
305                         rc = LDAP_LOCAL_ERROR;
306                         break;
307                 case SASL_TOOWEAK:
308                 case SASL_ENCRYPT:
309                         rc = LDAP_AUTH_UNKNOWN;
310                         break;
311                 default:
312                         rc = LDAP_LOCAL_ERROR;
313                         break;
314         }
315
316         assert( rc == LDAP_SUCCESS || LDAP_API_ERROR( rc ) );
317         return rc;
318 }
319
320 int
321 ldap_int_sasl_open(
322         LDAP *ld, 
323         LDAPConn *lc,
324         const char * host )
325 {
326         int rc;
327         sasl_conn_t *ctx;
328
329         assert( lc->lconn_sasl_authctx == NULL );
330
331         if ( host == NULL ) {
332                 ld->ld_errno = LDAP_LOCAL_ERROR;
333                 return ld->ld_errno;
334         }
335
336         if ( ldap_int_sasl_init() ) {
337                 ld->ld_errno = LDAP_LOCAL_ERROR;
338                 return ld->ld_errno;
339         }
340
341 #if SASL_VERSION_MAJOR >= 2
342         rc = sasl_client_new( "ldap", host, NULL, NULL,
343                 client_callbacks, 0, &ctx );
344 #else
345         rc = sasl_client_new( "ldap", host, client_callbacks,
346                 SASL_SECURITY_LAYER, &ctx );
347 #endif
348
349         if ( rc != SASL_OK ) {
350                 ld->ld_errno = sasl_err2ldap( rc );
351                 return ld->ld_errno;
352         }
353
354         Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_open: host=%s\n",
355                 host, 0, 0 );
356
357         lc->lconn_sasl_authctx = ctx;
358
359         return LDAP_SUCCESS;
360 }
361
362 int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
363 {
364         sasl_conn_t *ctx = lc->lconn_sasl_authctx;
365
366         if( ctx != NULL ) {
367                 sasl_dispose( &ctx );
368                 if ( lc->lconn_sasl_sockctx &&
369                         lc->lconn_sasl_authctx != lc->lconn_sasl_sockctx ) {
370                         ctx = lc->lconn_sasl_sockctx;
371                         sasl_dispose( &ctx );
372                 }
373                 lc->lconn_sasl_sockctx = NULL;
374                 lc->lconn_sasl_authctx = NULL;
375         }
376
377         return LDAP_SUCCESS;
378 }
379
380 int
381 ldap_int_sasl_bind(
382         LDAP                    *ld,
383         const char              *dn,
384         const char              *mechs,
385         LDAPControl             **sctrls,
386         LDAPControl             **cctrls,
387         unsigned                flags,
388         LDAP_SASL_INTERACT_PROC *interact,
389         void * defaults )
390 {
391         char *data;
392         const char *mech = NULL;
393         const char *pmech = NULL;
394         int                     saslrc, rc;
395         sasl_ssf_t              *ssf = NULL;
396         sasl_conn_t     *ctx, *oldctx = NULL;
397         sasl_interact_t *prompts = NULL;
398         unsigned credlen;
399         struct berval ccred;
400         ber_socket_t            sd;
401         void    *ssl;
402
403         Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n",
404                 mechs ? mechs : "<null>", 0, 0 );
405
406         /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
407         if (ld->ld_version < LDAP_VERSION3) {
408                 ld->ld_errno = LDAP_NOT_SUPPORTED;
409                 return ld->ld_errno;
410         }
411
412         rc = 0;
413 #ifdef LDAP_R_COMPILE
414         ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
415 #endif
416         ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
417
418         if ( sd == AC_SOCKET_INVALID ) {
419                 /* not connected yet */
420
421                 rc = ldap_open_defconn( ld );
422
423                 if ( rc == 0 ) {
424                         ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
425                                 LBER_SB_OPT_GET_FD, &sd );
426
427                         if( sd == AC_SOCKET_INVALID ) {
428                                 ld->ld_errno = LDAP_LOCAL_ERROR;
429                                 rc = ld->ld_errno;
430                         }
431                 }
432         }   
433 #ifdef LDAP_R_COMPILE
434         ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
435 #endif
436         if( rc != 0 ) return ld->ld_errno;
437
438         oldctx = ld->ld_defconn->lconn_sasl_authctx;
439
440         /* If we already have an authentication context, clear it out */
441         if( oldctx ) {
442                 if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) {
443                         sasl_dispose( &oldctx );
444                 }
445                 ld->ld_defconn->lconn_sasl_authctx = NULL;
446         }
447
448         {
449                 char *saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb,
450                         "localhost" );
451                 rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost );
452                 LDAP_FREE( saslhost );
453         }
454
455         if ( rc != LDAP_SUCCESS ) return rc;
456
457         ctx = ld->ld_defconn->lconn_sasl_authctx;
458
459         /* Check for TLS */
460         ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb );
461         if ( ssl ) {
462                 struct berval authid = BER_BVNULL;
463                 ber_len_t fac;
464
465                 fac = ldap_pvt_tls_get_strength( ssl );
466                 /* failure is OK, we just can't use SASL EXTERNAL */
467                 (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 );
468
469                 (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac );
470                 LDAP_FREE( authid.bv_val );
471         }
472
473 #if !defined(_WIN32)
474         /* Check for local */
475         if ( ldap_pvt_url_scheme2proto(
476                 ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC )
477         {
478                 char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295,"
479                         "cn=peercred,cn=external,cn=auth")];
480                 sprintf( authid, "gidNumber=%u+uidNumber=%u,"
481                         "cn=peercred,cn=external,cn=auth",
482                         getegid(), geteuid() );
483                 (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid,
484                         LDAP_PVT_SASL_LOCAL_SSF );
485         }
486 #endif
487
488         /* (re)set security properties */
489         sasl_setprop( ctx, SASL_SEC_PROPS,
490                 &ld->ld_options.ldo_sasl_secprops );
491
492         ccred.bv_val = NULL;
493         ccred.bv_len = 0;
494
495         do {
496                 saslrc = sasl_client_start( ctx,
497                         mechs,
498 #if SASL_VERSION_MAJOR < 2
499                         NULL,
500 #endif
501                         &prompts,
502                         (SASL_CONST char **)&ccred.bv_val,
503                         &credlen,
504                         &mech );
505
506                 if( pmech == NULL && mech != NULL ) {
507                         pmech = mech;
508
509                         if( flags != LDAP_SASL_QUIET ) {
510                                 fprintf(stderr,
511                                         "SASL/%s authentication started\n",
512                                         pmech );
513                         }
514                 }
515
516                 if( saslrc == SASL_INTERACT ) {
517                         int res;
518                         if( !interact ) break;
519                         res = (interact)( ld, flags, defaults, prompts );
520
521                         if( res != LDAP_SUCCESS ) break;
522                 }
523         } while ( saslrc == SASL_INTERACT );
524
525         ccred.bv_len = credlen;
526
527         if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
528                 rc = ld->ld_errno = sasl_err2ldap( saslrc );
529 #if SASL_VERSION_MAJOR >= 2
530                 if ( ld->ld_error ) {
531                         LDAP_FREE( ld->ld_error );
532                 }
533                 ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) );
534 #endif
535                 goto done;
536         }
537
538         do {
539                 struct berval *scred;
540                 unsigned credlen;
541
542                 scred = NULL;
543
544                 rc = ldap_sasl_bind_s( ld, dn, mech, &ccred, sctrls, cctrls,
545                         &scred );
546
547                 if ( ccred.bv_val != NULL ) {
548 #if SASL_VERSION_MAJOR < 2
549                         LDAP_FREE( ccred.bv_val );
550 #endif
551                         ccred.bv_val = NULL;
552                 }
553
554                 if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
555                         if( scred ) {
556                                 /* and server provided us with data? */
557                                 Debug( LDAP_DEBUG_TRACE,
558                                         "ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n",
559                                         rc, saslrc, scred ? (long) scred->bv_len : -1L );
560                                 ber_bvfree( scred );
561                                 scred = NULL;
562                         }
563                         rc = ld->ld_errno;
564                         goto done;
565                 }
566
567                 if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) {
568                         /* we're done, no need to step */
569                         if( scred ) {
570                                 /* but we got additional data? */
571 #define KLUDGE_FOR_MSAD
572 #ifdef  KLUDGE_FOR_MSAD
573                                 /*
574                                  * MSAD provides empty additional data in violation of LDAP
575                                  * technical specifications.  As no existing SASL mechanism
576                                  * allows empty data with an outcome message, just ignore it
577                                  * for now.  Hopefully MS will fix their bug before someone
578                                  * defines a mechanism with possibly empty additional data.
579                                  */
580                                 if( scred->bv_len == 0 ) {
581                                         Debug( LDAP_DEBUG_ANY,
582                                                 "ldap_int_sasl_bind: ignoring "
583                                                         " bogus empty data provided with SASL outcome message.\n",
584                                                 rc, saslrc, scred->bv_len );
585                                         ber_bvfree( scred );
586                                 } else
587 #endif
588                                 {
589                                         Debug( LDAP_DEBUG_TRACE,
590                                                 "ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n",
591                                                 rc, saslrc, scred->bv_len );
592                                         rc = ld->ld_errno = LDAP_LOCAL_ERROR;
593                                         ber_bvfree( scred );
594                                         goto done;
595                                 }
596                         }
597                         break;
598                 }
599
600                 do {
601                         if( ! scred ) {
602                                 /* no data! */
603                                 Debug( LDAP_DEBUG_TRACE,
604                                         "ldap_int_sasl_bind: no data in step!\n",
605                                         0, 0, 0 );
606                         }
607
608                         saslrc = sasl_client_step( ctx,
609                                 (scred == NULL) ? NULL : scred->bv_val,
610                                 (scred == NULL) ? 0 : scred->bv_len,
611                                 &prompts,
612                                 (SASL_CONST char **)&ccred.bv_val,
613                                 &credlen );
614
615                         Debug( LDAP_DEBUG_TRACE, "sasl_client_step: %d\n",
616                                 saslrc, 0, 0 );
617
618                         if( saslrc == SASL_INTERACT ) {
619                                 int res;
620                                 if( !interact ) break;
621                                 res = (interact)( ld, flags, defaults, prompts );
622                                 if( res != LDAP_SUCCESS ) break;
623                         }
624                 } while ( saslrc == SASL_INTERACT );
625
626                 ccred.bv_len = credlen;
627                 ber_bvfree( scred );
628
629                 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
630                         ld->ld_errno = sasl_err2ldap( saslrc );
631 #if SASL_VERSION_MAJOR >= 2
632                         if ( ld->ld_error ) {
633                                 LDAP_FREE( ld->ld_error );
634                         }
635                         ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) );
636 #endif
637                         rc = ld->ld_errno;
638                         goto done;
639                 }
640         } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
641
642         if ( rc != LDAP_SUCCESS ) goto done;
643
644         if ( saslrc != SASL_OK ) {
645 #if SASL_VERSION_MAJOR >= 2
646                 if ( ld->ld_error ) {
647                         LDAP_FREE( ld->ld_error );
648                 }
649                 ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) );
650 #endif
651                 rc = ld->ld_errno = sasl_err2ldap( saslrc );
652                 goto done;
653         }
654
655         if( flags != LDAP_SASL_QUIET ) {
656                 saslrc = sasl_getprop( ctx, SASL_USERNAME,
657                         (SASL_CONST void **)(char *) &data );
658                 if( saslrc == SASL_OK && data && *data ) {
659                         fprintf( stderr, "SASL username: %s\n", data );
660                 }
661
662 #if SASL_VERSION_MAJOR < 2
663                 saslrc = sasl_getprop( ctx, SASL_REALM,
664                         (SASL_CONST void **) &data );
665                 if( saslrc == SASL_OK && data && *data ) {
666                         fprintf( stderr, "SASL realm: %s\n", data );
667                 }
668 #endif
669         }
670
671         saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf );
672         if( saslrc == SASL_OK ) {
673                 if( flags != LDAP_SASL_QUIET ) {
674                         fprintf( stderr, "SASL SSF: %lu\n",
675                                 (unsigned long) *ssf );
676                 }
677
678                 if( ssf && *ssf ) {
679                         if ( ld->ld_defconn->lconn_sasl_sockctx ) {
680                                 oldctx = ld->ld_defconn->lconn_sasl_sockctx;
681                                 sasl_dispose( &oldctx );
682                                 ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb );
683                         }
684                         ldap_pvt_sasl_install( ld->ld_defconn->lconn_sb, ctx );
685                         ld->ld_defconn->lconn_sasl_sockctx = ctx;
686
687                         if( flags != LDAP_SASL_QUIET ) {
688                                 fprintf( stderr, "SASL data security layer installed.\n" );
689                         }
690                 }
691         }
692         ld->ld_defconn->lconn_sasl_authctx = ctx;
693
694 done:
695         return rc;
696 }
697
698 int
699 ldap_int_sasl_external(
700         LDAP *ld,
701         LDAPConn *conn,
702         const char * authid,
703         ber_len_t ssf )
704 {
705         int sc;
706         sasl_conn_t *ctx;
707 #if SASL_VERSION_MAJOR < 2
708         sasl_external_properties_t extprops;
709 #else
710         sasl_ssf_t sasl_ssf = ssf;
711 #endif
712
713         ctx = conn->lconn_sasl_authctx;
714
715         if ( ctx == NULL ) {
716                 return LDAP_LOCAL_ERROR;
717         }
718    
719 #if SASL_VERSION_MAJOR >= 2
720         sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf );
721         if ( sc == SASL_OK )
722                 sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid );
723 #else
724         memset( &extprops, '\0', sizeof(extprops) );
725         extprops.ssf = ssf;
726         extprops.auth_id = (char *) authid;
727
728         sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
729                 (void *) &extprops );
730 #endif
731
732         if ( sc != SASL_OK ) {
733                 return LDAP_LOCAL_ERROR;
734         }
735
736         return LDAP_SUCCESS;
737 }
738
739
740 #define GOT_MINSSF      1
741 #define GOT_MAXSSF      2
742 #define GOT_MAXBUF      4
743
744 static struct {
745         struct berval key;
746         int sflag;
747         int ival;
748         int idef;
749 } sprops[] = {
750         { BER_BVC("none"), 0, 0, 0 },
751         { BER_BVC("nodict"), SASL_SEC_NODICTIONARY, 0, 0 },
752         { BER_BVC("noplain"), SASL_SEC_NOPLAINTEXT, 0, 0 },
753         { BER_BVC("noactive"), SASL_SEC_NOACTIVE, 0, 0 },
754         { BER_BVC("passcred"), SASL_SEC_PASS_CREDENTIALS, 0, 0 },
755         { BER_BVC("forwardsec"), SASL_SEC_FORWARD_SECRECY, 0, 0 },
756         { BER_BVC("noanonymous"), SASL_SEC_NOANONYMOUS, 0, 0 },
757         { BER_BVC("minssf="), 0, GOT_MINSSF, 0 },
758         { BER_BVC("maxssf="), 0, GOT_MAXSSF, INT_MAX },
759         { BER_BVC("maxbufsize="), 0, GOT_MAXBUF, 65536 },
760         { BER_BVNULL, 0, 0, 0 }
761 };
762
763 void ldap_pvt_sasl_secprops_unparse(
764         sasl_security_properties_t *secprops,
765         struct berval *out )
766 {
767         int i, l = 0;
768         int comma;
769         char *ptr;
770
771         if ( secprops == NULL || out == NULL ) {
772                 return;
773         }
774
775         comma = 0;
776         for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) {
777                 if ( sprops[i].ival ) {
778                         int v = 0;
779
780                         switch( sprops[i].ival ) {
781                         case GOT_MINSSF: v = secprops->min_ssf; break;
782                         case GOT_MAXSSF: v = secprops->max_ssf; break;
783                         case GOT_MAXBUF: v = secprops->maxbufsize; break;
784                         }
785                         /* It is the default, ignore it */
786                         if ( v == sprops[i].idef ) continue;
787
788                         l += sprops[i].key.bv_len + 24;
789                 } else if ( sprops[i].sflag ) {
790                         if ( sprops[i].sflag & secprops->security_flags ) {
791                                 l += sprops[i].key.bv_len;
792                         }
793                 } else if ( secprops->security_flags == 0 ) {
794                         l += sprops[i].key.bv_len;
795                 }
796                 if ( comma ) l++;
797                 comma = 1;
798         }
799         l++;
800
801         out->bv_val = LDAP_MALLOC( l );
802         if ( out->bv_val == NULL ) {
803                 out->bv_len = 0;
804                 return;
805         }
806
807         ptr = out->bv_val;
808         comma = 0;
809         for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) {
810                 if ( sprops[i].ival ) {
811                         int v = 0;
812
813                         switch( sprops[i].ival ) {
814                         case GOT_MINSSF: v = secprops->min_ssf; break;
815                         case GOT_MAXSSF: v = secprops->max_ssf; break;
816                         case GOT_MAXBUF: v = secprops->maxbufsize; break;
817                         }
818                         /* It is the default, ignore it */
819                         if ( v == sprops[i].idef ) continue;
820
821                         if ( comma ) *ptr++ = ',';
822                         ptr += sprintf(ptr, "%s%d", sprops[i].key.bv_val, v );
823                         comma = 1;
824                 } else if ( sprops[i].sflag ) {
825                         if ( sprops[i].sflag & secprops->security_flags ) {
826                                 if ( comma ) *ptr++ = ',';
827                                 ptr += sprintf(ptr, "%s", sprops[i].key.bv_val );
828                                 comma = 1;
829                         }
830                 } else if ( secprops->security_flags == 0 ) {
831                         if ( comma ) *ptr++ = ',';
832                         ptr += sprintf(ptr, "%s", sprops[i].key.bv_val );
833                         comma = 1;
834                 }
835         }
836         out->bv_len = ptr - out->bv_val;
837 }
838
839 int ldap_pvt_sasl_secprops(
840         const char *in,
841         sasl_security_properties_t *secprops )
842 {
843         unsigned i, j, l;
844         char **props;
845         unsigned sflags = 0;
846         int got_sflags = 0;
847         sasl_ssf_t max_ssf = 0;
848         int got_max_ssf = 0;
849         sasl_ssf_t min_ssf = 0;
850         int got_min_ssf = 0;
851         unsigned maxbufsize = 0;
852         int got_maxbufsize = 0;
853
854         if( secprops == NULL ) {
855                 return LDAP_PARAM_ERROR;
856         }
857         props = ldap_str2charray( in, "," );
858         if( props == NULL ) {
859                 return LDAP_PARAM_ERROR;
860         }
861
862         for( i=0; props[i]; i++ ) {
863                 l = strlen( props[i] );
864                 for ( j=0; !BER_BVISNULL( &sprops[j].key ); j++ ) {
865                         if ( l < sprops[j].key.bv_len ) continue;
866                         if ( strncasecmp( props[i], sprops[j].key.bv_val,
867                                 sprops[j].key.bv_len )) continue;
868                         if ( sprops[j].ival ) {
869                                 unsigned v;
870                                 char *next = NULL;
871                                 if ( !isdigit( (unsigned char)props[i][sprops[j].key.bv_len] ))
872                                         continue;
873                                 v = strtoul( &props[i][sprops[j].key.bv_len], &next, 10 );
874                                 if ( next == &props[i][sprops[j].key.bv_len] || next[0] != '\0' ) continue;
875                                 switch( sprops[j].ival ) {
876                                 case GOT_MINSSF:
877                                         min_ssf = v; got_min_ssf++; break;
878                                 case GOT_MAXSSF:
879                                         max_ssf = v; got_max_ssf++; break;
880                                 case GOT_MAXBUF:
881                                         maxbufsize = v; got_maxbufsize++; break;
882                                 }
883                         } else {
884                                 if ( props[i][sprops[j].key.bv_len] ) continue;
885                                 if ( sprops[j].sflag )
886                                         sflags |= sprops[j].sflag;
887                                 else
888                                         sflags = 0;
889                                 got_sflags++;
890                         }
891                         break;
892                 }
893                 if ( BER_BVISNULL( &sprops[j].key )) {
894                         ldap_charray_free( props );
895                         return LDAP_NOT_SUPPORTED;
896                 }
897         }
898
899         if(got_sflags) {
900                 secprops->security_flags = sflags;
901         }
902         if(got_min_ssf) {
903                 secprops->min_ssf = min_ssf;
904         }
905         if(got_max_ssf) {
906                 secprops->max_ssf = max_ssf;
907         }
908         if(got_maxbufsize) {
909                 secprops->maxbufsize = maxbufsize;
910         }
911
912         ldap_charray_free( props );
913         return LDAP_SUCCESS;
914 }
915
916 int
917 ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg )
918 {
919         int rc;
920
921         switch( option ) {
922         case LDAP_OPT_X_SASL_SECPROPS:
923                 rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops );
924                 if( rc == LDAP_SUCCESS ) return 0;
925         }
926
927         return -1;
928 }
929
930 int
931 ldap_int_sasl_get_option( LDAP *ld, int option, void *arg )
932 {
933         if ( option == LDAP_OPT_X_SASL_MECHLIST ) {
934                 if ( ldap_int_sasl_init() )
935                         return -1;
936                 *(char ***)arg = (char **)sasl_global_listmech();
937                 return 0;
938         }
939
940         if ( ld == NULL )
941                 return -1;
942
943         switch ( option ) {
944                 case LDAP_OPT_X_SASL_MECH: {
945                         *(char **)arg = ld->ld_options.ldo_def_sasl_mech
946                                 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_mech ) : NULL;
947                 } break;
948                 case LDAP_OPT_X_SASL_REALM: {
949                         *(char **)arg = ld->ld_options.ldo_def_sasl_realm
950                                 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_realm ) : NULL;
951                 } break;
952                 case LDAP_OPT_X_SASL_AUTHCID: {
953                         *(char **)arg = ld->ld_options.ldo_def_sasl_authcid
954                                 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authcid ) : NULL;
955                 } break;
956                 case LDAP_OPT_X_SASL_AUTHZID: {
957                         *(char **)arg = ld->ld_options.ldo_def_sasl_authzid
958                                 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authzid ) : NULL;
959                 } break;
960
961                 case LDAP_OPT_X_SASL_SSF: {
962                         int sc;
963                         sasl_ssf_t      *ssf;
964                         sasl_conn_t *ctx;
965
966                         if( ld->ld_defconn == NULL ) {
967                                 return -1;
968                         }
969
970                         ctx = ld->ld_defconn->lconn_sasl_sockctx;
971
972                         if ( ctx == NULL ) {
973                                 return -1;
974                         }
975
976                         sc = sasl_getprop( ctx, SASL_SSF,
977                                 (SASL_CONST void **)(char *) &ssf );
978
979                         if ( sc != SASL_OK ) {
980                                 return -1;
981                         }
982
983                         *(ber_len_t *)arg = *ssf;
984                 } break;
985
986                 case LDAP_OPT_X_SASL_SSF_EXTERNAL:
987                         /* this option is write only */
988                         return -1;
989
990                 case LDAP_OPT_X_SASL_SSF_MIN:
991                         *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.min_ssf;
992                         break;
993                 case LDAP_OPT_X_SASL_SSF_MAX:
994                         *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.max_ssf;
995                         break;
996                 case LDAP_OPT_X_SASL_MAXBUFSIZE:
997                         *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.maxbufsize;
998                         break;
999
1000                 case LDAP_OPT_X_SASL_SECPROPS:
1001                         /* this option is write only */
1002                         return -1;
1003
1004                 default:
1005                         return -1;
1006         }
1007         return 0;
1008 }
1009
1010 int
1011 ldap_int_sasl_set_option( LDAP *ld, int option, void *arg )
1012 {
1013         if ( ld == NULL || arg == NULL )
1014                 return -1;
1015
1016         switch ( option ) {
1017         case LDAP_OPT_X_SASL_SSF:
1018                 /* This option is read-only */
1019                 return -1;
1020
1021         case LDAP_OPT_X_SASL_SSF_EXTERNAL: {
1022                 int sc;
1023 #if SASL_VERSION_MAJOR < 2
1024                 sasl_external_properties_t extprops;
1025 #else
1026                 sasl_ssf_t sasl_ssf;
1027 #endif
1028                 sasl_conn_t *ctx;
1029
1030                 if( ld->ld_defconn == NULL ) {
1031                         return -1;
1032                 }
1033
1034                 ctx = ld->ld_defconn->lconn_sasl_authctx;
1035
1036                 if ( ctx == NULL ) {
1037                         return -1;
1038                 }
1039
1040 #if SASL_VERSION_MAJOR >= 2
1041                 sasl_ssf = * (ber_len_t *)arg;
1042                 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf);
1043 #else
1044                 memset(&extprops, 0L, sizeof(extprops));
1045
1046                 extprops.ssf = * (ber_len_t *) arg;
1047
1048                 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
1049                         (void *) &extprops );
1050 #endif
1051
1052                 if ( sc != SASL_OK ) {
1053                         return -1;
1054                 }
1055                 } break;
1056
1057         case LDAP_OPT_X_SASL_SSF_MIN:
1058                 ld->ld_options.ldo_sasl_secprops.min_ssf = *(ber_len_t *)arg;
1059                 break;
1060         case LDAP_OPT_X_SASL_SSF_MAX:
1061                 ld->ld_options.ldo_sasl_secprops.max_ssf = *(ber_len_t *)arg;
1062                 break;
1063         case LDAP_OPT_X_SASL_MAXBUFSIZE:
1064                 ld->ld_options.ldo_sasl_secprops.maxbufsize = *(ber_len_t *)arg;
1065                 break;
1066
1067         case LDAP_OPT_X_SASL_SECPROPS: {
1068                 int sc;
1069                 sc = ldap_pvt_sasl_secprops( (char *) arg,
1070                         &ld->ld_options.ldo_sasl_secprops );
1071
1072                 return sc == LDAP_SUCCESS ? 0 : -1;
1073                 }
1074
1075         default:
1076                 return -1;
1077         }
1078         return 0;
1079 }
1080
1081 #ifdef LDAP_R_COMPILE
1082 #define LDAP_DEBUG_R_SASL
1083 void *ldap_pvt_sasl_mutex_new(void)
1084 {
1085         ldap_pvt_thread_mutex_t *mutex;
1086
1087         mutex = (ldap_pvt_thread_mutex_t *) LDAP_CALLOC( 1,
1088                 sizeof(ldap_pvt_thread_mutex_t) );
1089
1090         if ( ldap_pvt_thread_mutex_init( mutex ) == 0 ) {
1091                 return mutex;
1092         }
1093 #ifndef LDAP_DEBUG_R_SASL
1094         assert( 0 );
1095 #endif /* !LDAP_DEBUG_R_SASL */
1096         return NULL;
1097 }
1098
1099 int ldap_pvt_sasl_mutex_lock(void *mutex)
1100 {
1101 #ifdef LDAP_DEBUG_R_SASL
1102         if ( mutex == NULL ) {
1103                 return SASL_OK;
1104         }
1105 #else /* !LDAP_DEBUG_R_SASL */
1106         assert( mutex != NULL );
1107 #endif /* !LDAP_DEBUG_R_SASL */
1108         return ldap_pvt_thread_mutex_lock( (ldap_pvt_thread_mutex_t *)mutex )
1109                 ? SASL_FAIL : SASL_OK;
1110 }
1111
1112 int ldap_pvt_sasl_mutex_unlock(void *mutex)
1113 {
1114 #ifdef LDAP_DEBUG_R_SASL
1115         if ( mutex == NULL ) {
1116                 return SASL_OK;
1117         }
1118 #else /* !LDAP_DEBUG_R_SASL */
1119         assert( mutex != NULL );
1120 #endif /* !LDAP_DEBUG_R_SASL */
1121         return ldap_pvt_thread_mutex_unlock( (ldap_pvt_thread_mutex_t *)mutex )
1122                 ? SASL_FAIL : SASL_OK;
1123 }
1124
1125 void ldap_pvt_sasl_mutex_dispose(void *mutex)
1126 {
1127 #ifdef LDAP_DEBUG_R_SASL
1128         if ( mutex == NULL ) {
1129                 return;
1130         }
1131 #else /* !LDAP_DEBUG_R_SASL */
1132         assert( mutex != NULL );
1133 #endif /* !LDAP_DEBUG_R_SASL */
1134         (void) ldap_pvt_thread_mutex_destroy( (ldap_pvt_thread_mutex_t *)mutex );
1135         LDAP_FREE( mutex );
1136 }
1137 #endif
1138
1139 #else
1140 int ldap_int_sasl_init( void )
1141 { return LDAP_SUCCESS; }
1142
1143 int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
1144 { return LDAP_SUCCESS; }
1145
1146 int
1147 ldap_int_sasl_bind(
1148         LDAP                    *ld,
1149         const char              *dn,
1150         const char              *mechs,
1151         LDAPControl             **sctrls,
1152         LDAPControl             **cctrls,
1153         unsigned                flags,
1154         LDAP_SASL_INTERACT_PROC *interact,
1155         void * defaults )
1156 { return LDAP_NOT_SUPPORTED; }
1157
1158 int
1159 ldap_int_sasl_external(
1160         LDAP *ld,
1161         LDAPConn *conn,
1162         const char * authid,
1163         ber_len_t ssf )
1164 { return LDAP_SUCCESS; }
1165
1166 #endif /* HAVE_CYRUS_SASL */