]> git.sur5r.net Git - openldap/blob - libraries/libldap/cyrus.c
9bd5938dc400d23c4fc716dbb44eeda648b40bcf
[openldap] / libraries / libldap / cyrus.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2010 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         LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
414         ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
415
416         if ( sd == AC_SOCKET_INVALID ) {
417                 /* not connected yet */
418
419                 rc = ldap_open_defconn( ld );
420
421                 if ( rc == 0 ) {
422                         ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
423                                 LBER_SB_OPT_GET_FD, &sd );
424
425                         if( sd == AC_SOCKET_INVALID ) {
426                                 ld->ld_errno = LDAP_LOCAL_ERROR;
427                                 rc = ld->ld_errno;
428                         }
429                 }
430         }   
431         LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
432         if( rc != 0 ) return ld->ld_errno;
433
434         oldctx = ld->ld_defconn->lconn_sasl_authctx;
435
436         /* If we already have an authentication context, clear it out */
437         if( oldctx ) {
438                 if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) {
439                         sasl_dispose( &oldctx );
440                 }
441                 ld->ld_defconn->lconn_sasl_authctx = NULL;
442         }
443
444         {
445                 char *saslhost;
446                 int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options,
447                         LDAP_BOOL_SASL_NOCANON );
448
449                 /* If we don't need to canonicalize just use the host
450                  * from the LDAP URI.
451                  */
452                 if ( nocanon )
453                         saslhost = ld->ld_defconn->lconn_server->lud_host;
454                 else 
455                         saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb,
456                         "localhost" );
457                 rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost );
458                 if ( !nocanon )
459                         LDAP_FREE( saslhost );
460         }
461
462         if ( rc != LDAP_SUCCESS ) return rc;
463
464         ctx = ld->ld_defconn->lconn_sasl_authctx;
465
466 #ifdef HAVE_TLS
467         /* Check for TLS */
468         ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb );
469         if ( ssl ) {
470                 struct berval authid = BER_BVNULL;
471                 ber_len_t fac;
472
473                 fac = ldap_pvt_tls_get_strength( ssl );
474                 /* failure is OK, we just can't use SASL EXTERNAL */
475                 (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 );
476
477                 (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac );
478                 LDAP_FREE( authid.bv_val );
479         }
480 #endif
481
482 #if !defined(_WIN32)
483         /* Check for local */
484         if ( ldap_pvt_url_scheme2proto(
485                 ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC )
486         {
487                 char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295,"
488                         "cn=peercred,cn=external,cn=auth")];
489                 sprintf( authid, "gidNumber=%u+uidNumber=%u,"
490                         "cn=peercred,cn=external,cn=auth",
491                         getegid(), geteuid() );
492                 (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid,
493                         LDAP_PVT_SASL_LOCAL_SSF );
494         }
495 #endif
496
497         /* (re)set security properties */
498         sasl_setprop( ctx, SASL_SEC_PROPS,
499                 &ld->ld_options.ldo_sasl_secprops );
500
501         ccred.bv_val = NULL;
502         ccred.bv_len = 0;
503
504         do {
505                 saslrc = sasl_client_start( ctx,
506                         mechs,
507 #if SASL_VERSION_MAJOR < 2
508                         NULL,
509 #endif
510                         &prompts,
511                         (SASL_CONST char **)&ccred.bv_val,
512                         &credlen,
513                         &mech );
514
515                 if( pmech == NULL && mech != NULL ) {
516                         pmech = mech;
517
518                         if( flags != LDAP_SASL_QUIET ) {
519                                 fprintf(stderr,
520                                         "SASL/%s authentication started\n",
521                                         pmech );
522                         }
523                 }
524
525                 if( saslrc == SASL_INTERACT ) {
526                         int res;
527                         if( !interact ) break;
528                         res = (interact)( ld, flags, defaults, prompts );
529
530                         if( res != LDAP_SUCCESS ) break;
531                 }
532         } while ( saslrc == SASL_INTERACT );
533
534         ccred.bv_len = credlen;
535
536         if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
537                 rc = ld->ld_errno = sasl_err2ldap( saslrc );
538 #if SASL_VERSION_MAJOR >= 2
539                 if ( ld->ld_error ) {
540                         LDAP_FREE( ld->ld_error );
541                 }
542                 ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) );
543 #endif
544                 goto done;
545         }
546
547         do {
548                 struct berval *scred;
549                 unsigned credlen;
550
551                 scred = NULL;
552
553                 rc = ldap_sasl_bind_s( ld, dn, mech, &ccred, sctrls, cctrls,
554                         &scred );
555
556                 if ( ccred.bv_val != NULL ) {
557 #if SASL_VERSION_MAJOR < 2
558                         LDAP_FREE( ccred.bv_val );
559 #endif
560                         ccred.bv_val = NULL;
561                 }
562
563                 if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
564                         if( scred ) {
565                                 /* and server provided us with data? */
566                                 Debug( LDAP_DEBUG_TRACE,
567                                         "ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n",
568                                         rc, saslrc, scred ? (long) scred->bv_len : -1L );
569                                 ber_bvfree( scred );
570                                 scred = NULL;
571                         }
572                         rc = ld->ld_errno;
573                         goto done;
574                 }
575
576                 if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) {
577                         /* we're done, no need to step */
578                         if( scred ) {
579                                 /* but we got additional data? */
580 #define KLUDGE_FOR_MSAD
581 #ifdef  KLUDGE_FOR_MSAD
582                                 /*
583                                  * MSAD provides empty additional data in violation of LDAP
584                                  * technical specifications.  As no existing SASL mechanism
585                                  * allows empty data with an outcome message, just ignore it
586                                  * for now.  Hopefully MS will fix their bug before someone
587                                  * defines a mechanism with possibly empty additional data.
588                                  */
589                                 if( scred->bv_len == 0 ) {
590                                         Debug( LDAP_DEBUG_ANY,
591                                                 "ldap_int_sasl_bind: ignoring "
592                                                         " bogus empty data provided with SASL outcome message.\n",
593                                                 rc, saslrc, scred->bv_len );
594                                         ber_bvfree( scred );
595                                 } else
596 #endif
597                                 {
598                                         Debug( LDAP_DEBUG_TRACE,
599                                                 "ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n",
600                                                 rc, saslrc, scred->bv_len );
601                                         rc = ld->ld_errno = LDAP_LOCAL_ERROR;
602                                         ber_bvfree( scred );
603                                         goto done;
604                                 }
605                         }
606                         break;
607                 }
608
609                 do {
610                         if( ! scred ) {
611                                 /* no data! */
612                                 Debug( LDAP_DEBUG_TRACE,
613                                         "ldap_int_sasl_bind: no data in step!\n",
614                                         0, 0, 0 );
615                         }
616
617                         saslrc = sasl_client_step( ctx,
618                                 (scred == NULL) ? NULL : scred->bv_val,
619                                 (scred == NULL) ? 0 : scred->bv_len,
620                                 &prompts,
621                                 (SASL_CONST char **)&ccred.bv_val,
622                                 &credlen );
623
624                         Debug( LDAP_DEBUG_TRACE, "sasl_client_step: %d\n",
625                                 saslrc, 0, 0 );
626
627                         if( saslrc == SASL_INTERACT ) {
628                                 int res;
629                                 if( !interact ) break;
630                                 res = (interact)( ld, flags, defaults, prompts );
631                                 if( res != LDAP_SUCCESS ) break;
632                         }
633                 } while ( saslrc == SASL_INTERACT );
634
635                 ccred.bv_len = credlen;
636                 ber_bvfree( scred );
637
638                 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
639                         ld->ld_errno = sasl_err2ldap( saslrc );
640 #if SASL_VERSION_MAJOR >= 2
641                         if ( ld->ld_error ) {
642                                 LDAP_FREE( ld->ld_error );
643                         }
644                         ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) );
645 #endif
646                         rc = ld->ld_errno;
647                         goto done;
648                 }
649         } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
650
651         if ( rc != LDAP_SUCCESS ) goto done;
652
653         if ( saslrc != SASL_OK ) {
654 #if SASL_VERSION_MAJOR >= 2
655                 if ( ld->ld_error ) {
656                         LDAP_FREE( ld->ld_error );
657                 }
658                 ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) );
659 #endif
660                 rc = ld->ld_errno = sasl_err2ldap( saslrc );
661                 goto done;
662         }
663
664         if( flags != LDAP_SASL_QUIET ) {
665                 saslrc = sasl_getprop( ctx, SASL_USERNAME,
666                         (SASL_CONST void **)(char *) &data );
667                 if( saslrc == SASL_OK && data && *data ) {
668                         fprintf( stderr, "SASL username: %s\n", data );
669                 }
670
671 #if SASL_VERSION_MAJOR < 2
672                 saslrc = sasl_getprop( ctx, SASL_REALM,
673                         (SASL_CONST void **) &data );
674                 if( saslrc == SASL_OK && data && *data ) {
675                         fprintf( stderr, "SASL realm: %s\n", data );
676                 }
677 #endif
678         }
679
680         saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf );
681         if( saslrc == SASL_OK ) {
682                 if( flags != LDAP_SASL_QUIET ) {
683                         fprintf( stderr, "SASL SSF: %lu\n",
684                                 (unsigned long) *ssf );
685                 }
686
687                 if( ssf && *ssf ) {
688                         if ( ld->ld_defconn->lconn_sasl_sockctx ) {
689                                 oldctx = ld->ld_defconn->lconn_sasl_sockctx;
690                                 sasl_dispose( &oldctx );
691                                 ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb );
692                         }
693                         ldap_pvt_sasl_install( ld->ld_defconn->lconn_sb, ctx );
694                         ld->ld_defconn->lconn_sasl_sockctx = ctx;
695
696                         if( flags != LDAP_SASL_QUIET ) {
697                                 fprintf( stderr, "SASL data security layer installed.\n" );
698                         }
699                 }
700         }
701         ld->ld_defconn->lconn_sasl_authctx = ctx;
702
703 done:
704         return rc;
705 }
706
707 int
708 ldap_int_sasl_external(
709         LDAP *ld,
710         LDAPConn *conn,
711         const char * authid,
712         ber_len_t ssf )
713 {
714         int sc;
715         sasl_conn_t *ctx;
716 #if SASL_VERSION_MAJOR < 2
717         sasl_external_properties_t extprops;
718 #else
719         sasl_ssf_t sasl_ssf = ssf;
720 #endif
721
722         ctx = conn->lconn_sasl_authctx;
723
724         if ( ctx == NULL ) {
725                 return LDAP_LOCAL_ERROR;
726         }
727    
728 #if SASL_VERSION_MAJOR >= 2
729         sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf );
730         if ( sc == SASL_OK )
731                 sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid );
732 #else
733         memset( &extprops, '\0', sizeof(extprops) );
734         extprops.ssf = ssf;
735         extprops.auth_id = (char *) authid;
736
737         sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
738                 (void *) &extprops );
739 #endif
740
741         if ( sc != SASL_OK ) {
742                 return LDAP_LOCAL_ERROR;
743         }
744
745         return LDAP_SUCCESS;
746 }
747
748
749 #define GOT_MINSSF      1
750 #define GOT_MAXSSF      2
751 #define GOT_MAXBUF      4
752
753 static struct {
754         struct berval key;
755         int sflag;
756         int ival;
757         int idef;
758 } sprops[] = {
759         { BER_BVC("none"), 0, 0, 0 },
760         { BER_BVC("nodict"), SASL_SEC_NODICTIONARY, 0, 0 },
761         { BER_BVC("noplain"), SASL_SEC_NOPLAINTEXT, 0, 0 },
762         { BER_BVC("noactive"), SASL_SEC_NOACTIVE, 0, 0 },
763         { BER_BVC("passcred"), SASL_SEC_PASS_CREDENTIALS, 0, 0 },
764         { BER_BVC("forwardsec"), SASL_SEC_FORWARD_SECRECY, 0, 0 },
765         { BER_BVC("noanonymous"), SASL_SEC_NOANONYMOUS, 0, 0 },
766         { BER_BVC("minssf="), 0, GOT_MINSSF, 0 },
767         { BER_BVC("maxssf="), 0, GOT_MAXSSF, INT_MAX },
768         { BER_BVC("maxbufsize="), 0, GOT_MAXBUF, 65536 },
769         { BER_BVNULL, 0, 0, 0 }
770 };
771
772 void ldap_pvt_sasl_secprops_unparse(
773         sasl_security_properties_t *secprops,
774         struct berval *out )
775 {
776         int i, l = 0;
777         int comma;
778         char *ptr;
779
780         if ( secprops == NULL || out == NULL ) {
781                 return;
782         }
783
784         comma = 0;
785         for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) {
786                 if ( sprops[i].ival ) {
787                         int v = 0;
788
789                         switch( sprops[i].ival ) {
790                         case GOT_MINSSF: v = secprops->min_ssf; break;
791                         case GOT_MAXSSF: v = secprops->max_ssf; break;
792                         case GOT_MAXBUF: v = secprops->maxbufsize; break;
793                         }
794                         /* It is the default, ignore it */
795                         if ( v == sprops[i].idef ) continue;
796
797                         l += sprops[i].key.bv_len + 24;
798                 } else if ( sprops[i].sflag ) {
799                         if ( sprops[i].sflag & secprops->security_flags ) {
800                                 l += sprops[i].key.bv_len;
801                         }
802                 } else if ( secprops->security_flags == 0 ) {
803                         l += sprops[i].key.bv_len;
804                 }
805                 if ( comma ) l++;
806                 comma = 1;
807         }
808         l++;
809
810         out->bv_val = LDAP_MALLOC( l );
811         if ( out->bv_val == NULL ) {
812                 out->bv_len = 0;
813                 return;
814         }
815
816         ptr = out->bv_val;
817         comma = 0;
818         for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) {
819                 if ( sprops[i].ival ) {
820                         int v = 0;
821
822                         switch( sprops[i].ival ) {
823                         case GOT_MINSSF: v = secprops->min_ssf; break;
824                         case GOT_MAXSSF: v = secprops->max_ssf; break;
825                         case GOT_MAXBUF: v = secprops->maxbufsize; break;
826                         }
827                         /* It is the default, ignore it */
828                         if ( v == sprops[i].idef ) continue;
829
830                         if ( comma ) *ptr++ = ',';
831                         ptr += sprintf(ptr, "%s%d", sprops[i].key.bv_val, v );
832                         comma = 1;
833                 } else if ( sprops[i].sflag ) {
834                         if ( sprops[i].sflag & secprops->security_flags ) {
835                                 if ( comma ) *ptr++ = ',';
836                                 ptr += sprintf(ptr, "%s", sprops[i].key.bv_val );
837                                 comma = 1;
838                         }
839                 } else if ( secprops->security_flags == 0 ) {
840                         if ( comma ) *ptr++ = ',';
841                         ptr += sprintf(ptr, "%s", sprops[i].key.bv_val );
842                         comma = 1;
843                 }
844         }
845         out->bv_len = ptr - out->bv_val;
846 }
847
848 int ldap_pvt_sasl_secprops(
849         const char *in,
850         sasl_security_properties_t *secprops )
851 {
852         unsigned i, j, l;
853         char **props;
854         unsigned sflags = 0;
855         int got_sflags = 0;
856         sasl_ssf_t max_ssf = 0;
857         int got_max_ssf = 0;
858         sasl_ssf_t min_ssf = 0;
859         int got_min_ssf = 0;
860         unsigned maxbufsize = 0;
861         int got_maxbufsize = 0;
862
863         if( secprops == NULL ) {
864                 return LDAP_PARAM_ERROR;
865         }
866         props = ldap_str2charray( in, "," );
867         if( props == NULL ) {
868                 return LDAP_PARAM_ERROR;
869         }
870
871         for( i=0; props[i]; i++ ) {
872                 l = strlen( props[i] );
873                 for ( j=0; !BER_BVISNULL( &sprops[j].key ); j++ ) {
874                         if ( l < sprops[j].key.bv_len ) continue;
875                         if ( strncasecmp( props[i], sprops[j].key.bv_val,
876                                 sprops[j].key.bv_len )) continue;
877                         if ( sprops[j].ival ) {
878                                 unsigned v;
879                                 char *next = NULL;
880                                 if ( !isdigit( (unsigned char)props[i][sprops[j].key.bv_len] ))
881                                         continue;
882                                 v = strtoul( &props[i][sprops[j].key.bv_len], &next, 10 );
883                                 if ( next == &props[i][sprops[j].key.bv_len] || next[0] != '\0' ) continue;
884                                 switch( sprops[j].ival ) {
885                                 case GOT_MINSSF:
886                                         min_ssf = v; got_min_ssf++; break;
887                                 case GOT_MAXSSF:
888                                         max_ssf = v; got_max_ssf++; break;
889                                 case GOT_MAXBUF:
890                                         maxbufsize = v; got_maxbufsize++; break;
891                                 }
892                         } else {
893                                 if ( props[i][sprops[j].key.bv_len] ) continue;
894                                 if ( sprops[j].sflag )
895                                         sflags |= sprops[j].sflag;
896                                 else
897                                         sflags = 0;
898                                 got_sflags++;
899                         }
900                         break;
901                 }
902                 if ( BER_BVISNULL( &sprops[j].key )) {
903                         ldap_charray_free( props );
904                         return LDAP_NOT_SUPPORTED;
905                 }
906         }
907
908         if(got_sflags) {
909                 secprops->security_flags = sflags;
910         }
911         if(got_min_ssf) {
912                 secprops->min_ssf = min_ssf;
913         }
914         if(got_max_ssf) {
915                 secprops->max_ssf = max_ssf;
916         }
917         if(got_maxbufsize) {
918                 secprops->maxbufsize = maxbufsize;
919         }
920
921         ldap_charray_free( props );
922         return LDAP_SUCCESS;
923 }
924
925 int
926 ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg )
927 {
928         int rc;
929
930         switch( option ) {
931         case LDAP_OPT_X_SASL_SECPROPS:
932                 rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops );
933                 if( rc == LDAP_SUCCESS ) return 0;
934         }
935
936         return -1;
937 }
938
939 int
940 ldap_int_sasl_get_option( LDAP *ld, int option, void *arg )
941 {
942         if ( option == LDAP_OPT_X_SASL_MECHLIST ) {
943                 if ( ldap_int_sasl_init() )
944                         return -1;
945                 *(char ***)arg = (char **)sasl_global_listmech();
946                 return 0;
947         }
948
949         if ( ld == NULL )
950                 return -1;
951
952         switch ( option ) {
953                 case LDAP_OPT_X_SASL_MECH: {
954                         *(char **)arg = ld->ld_options.ldo_def_sasl_mech
955                                 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_mech ) : NULL;
956                 } break;
957                 case LDAP_OPT_X_SASL_REALM: {
958                         *(char **)arg = ld->ld_options.ldo_def_sasl_realm
959                                 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_realm ) : NULL;
960                 } break;
961                 case LDAP_OPT_X_SASL_AUTHCID: {
962                         *(char **)arg = ld->ld_options.ldo_def_sasl_authcid
963                                 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authcid ) : NULL;
964                 } break;
965                 case LDAP_OPT_X_SASL_AUTHZID: {
966                         *(char **)arg = ld->ld_options.ldo_def_sasl_authzid
967                                 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authzid ) : NULL;
968                 } break;
969
970                 case LDAP_OPT_X_SASL_SSF: {
971                         int sc;
972                         sasl_ssf_t      *ssf;
973                         sasl_conn_t *ctx;
974
975                         if( ld->ld_defconn == NULL ) {
976                                 return -1;
977                         }
978
979                         ctx = ld->ld_defconn->lconn_sasl_sockctx;
980
981                         if ( ctx == NULL ) {
982                                 return -1;
983                         }
984
985                         sc = sasl_getprop( ctx, SASL_SSF,
986                                 (SASL_CONST void **)(char *) &ssf );
987
988                         if ( sc != SASL_OK ) {
989                                 return -1;
990                         }
991
992                         *(ber_len_t *)arg = *ssf;
993                 } break;
994
995                 case LDAP_OPT_X_SASL_SSF_EXTERNAL:
996                         /* this option is write only */
997                         return -1;
998
999                 case LDAP_OPT_X_SASL_SSF_MIN:
1000                         *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.min_ssf;
1001                         break;
1002                 case LDAP_OPT_X_SASL_SSF_MAX:
1003                         *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.max_ssf;
1004                         break;
1005                 case LDAP_OPT_X_SASL_MAXBUFSIZE:
1006                         *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.maxbufsize;
1007                         break;
1008                 case LDAP_OPT_X_SASL_NOCANON:
1009                         *(int *)arg = (int) LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
1010                         break;
1011
1012                 case LDAP_OPT_X_SASL_USERNAME: {
1013                         int sc;
1014                         char *username;
1015                         sasl_conn_t *ctx;
1016
1017                         if( ld->ld_defconn == NULL ) {
1018                                 return -1;
1019                         }
1020
1021                         ctx = ld->ld_defconn->lconn_sasl_authctx;
1022
1023                         if ( ctx == NULL ) {
1024                                 return -1;
1025                         }
1026
1027                         sc = sasl_getprop( ctx, SASL_USERNAME,
1028                                 (SASL_CONST void **)(char **) &username );
1029
1030                         if ( sc != SASL_OK ) {
1031                                 return -1;
1032                         }
1033
1034                         *(char **)arg = username ? LDAP_STRDUP( username ) : NULL;
1035                 } break;
1036
1037                 case LDAP_OPT_X_SASL_SECPROPS:
1038                         /* this option is write only */
1039                         return -1;
1040
1041 #ifdef SASL_GSS_CREDS
1042                 case LDAP_OPT_X_SASL_GSS_CREDS: {
1043                         sasl_conn_t *ctx;
1044                         int sc;
1045
1046                         if ( ld->ld_defconn == NULL )
1047                                 return -1;
1048
1049                         ctx = ld->ld_defconn->lconn_sasl_authctx;
1050                         if ( ctx == NULL )
1051                                 return -1;
1052
1053                         sc = sasl_getprop( ctx, SASL_GSS_CREDS, arg );
1054                         if ( sc != SASL_OK )
1055                                 return -1;
1056                         }
1057                         break;
1058 #endif
1059
1060                 default:
1061                         return -1;
1062         }
1063         return 0;
1064 }
1065
1066 int
1067 ldap_int_sasl_set_option( LDAP *ld, int option, void *arg )
1068 {
1069         if ( ld == NULL )
1070                 return -1;
1071
1072         if ( arg == NULL && option != LDAP_OPT_X_SASL_NOCANON )
1073                 return -1;
1074
1075         switch ( option ) {
1076         case LDAP_OPT_X_SASL_SSF:
1077         case LDAP_OPT_X_SASL_USERNAME:
1078                 /* This option is read-only */
1079                 return -1;
1080
1081         case LDAP_OPT_X_SASL_SSF_EXTERNAL: {
1082                 int sc;
1083 #if SASL_VERSION_MAJOR < 2
1084                 sasl_external_properties_t extprops;
1085 #else
1086                 sasl_ssf_t sasl_ssf;
1087 #endif
1088                 sasl_conn_t *ctx;
1089
1090                 if( ld->ld_defconn == NULL ) {
1091                         return -1;
1092                 }
1093
1094                 ctx = ld->ld_defconn->lconn_sasl_authctx;
1095
1096                 if ( ctx == NULL ) {
1097                         return -1;
1098                 }
1099
1100 #if SASL_VERSION_MAJOR >= 2
1101                 sasl_ssf = * (ber_len_t *)arg;
1102                 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf);
1103 #else
1104                 memset(&extprops, 0L, sizeof(extprops));
1105
1106                 extprops.ssf = * (ber_len_t *) arg;
1107
1108                 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
1109                         (void *) &extprops );
1110 #endif
1111
1112                 if ( sc != SASL_OK ) {
1113                         return -1;
1114                 }
1115                 } break;
1116
1117         case LDAP_OPT_X_SASL_SSF_MIN:
1118                 ld->ld_options.ldo_sasl_secprops.min_ssf = *(ber_len_t *)arg;
1119                 break;
1120         case LDAP_OPT_X_SASL_SSF_MAX:
1121                 ld->ld_options.ldo_sasl_secprops.max_ssf = *(ber_len_t *)arg;
1122                 break;
1123         case LDAP_OPT_X_SASL_MAXBUFSIZE:
1124                 ld->ld_options.ldo_sasl_secprops.maxbufsize = *(ber_len_t *)arg;
1125                 break;
1126         case LDAP_OPT_X_SASL_NOCANON:
1127                 if ( arg == LDAP_OPT_OFF ) {
1128                         LDAP_BOOL_CLR(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
1129                 } else {
1130                         LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
1131                 }
1132                 break;
1133
1134         case LDAP_OPT_X_SASL_SECPROPS: {
1135                 int sc;
1136                 sc = ldap_pvt_sasl_secprops( (char *) arg,
1137                         &ld->ld_options.ldo_sasl_secprops );
1138
1139                 return sc == LDAP_SUCCESS ? 0 : -1;
1140                 }
1141
1142 #ifdef SASL_GSS_CREDS
1143         case LDAP_OPT_X_SASL_GSS_CREDS: {
1144                 sasl_conn_t *ctx;
1145                 int sc;
1146
1147                 if ( ld->ld_defconn == NULL )
1148                         return -1;
1149
1150                 ctx = ld->ld_defconn->lconn_sasl_authctx;
1151                 if ( ctx == NULL )
1152                         return -1;
1153
1154                 sc = sasl_setprop( ctx, SASL_GSS_CREDS, arg );
1155                 if ( sc != SASL_OK )
1156                         return -1;
1157                 }
1158                 break;
1159 #endif
1160
1161         default:
1162                 return -1;
1163         }
1164         return 0;
1165 }
1166
1167 #ifdef LDAP_R_COMPILE
1168 #define LDAP_DEBUG_R_SASL
1169 void *ldap_pvt_sasl_mutex_new(void)
1170 {
1171         ldap_pvt_thread_mutex_t *mutex;
1172
1173         mutex = (ldap_pvt_thread_mutex_t *) LDAP_CALLOC( 1,
1174                 sizeof(ldap_pvt_thread_mutex_t) );
1175
1176         if ( ldap_pvt_thread_mutex_init( mutex ) == 0 ) {
1177                 return mutex;
1178         }
1179 #ifndef LDAP_DEBUG_R_SASL
1180         assert( 0 );
1181 #endif /* !LDAP_DEBUG_R_SASL */
1182         return NULL;
1183 }
1184
1185 int ldap_pvt_sasl_mutex_lock(void *mutex)
1186 {
1187 #ifdef LDAP_DEBUG_R_SASL
1188         if ( mutex == NULL ) {
1189                 return SASL_OK;
1190         }
1191 #else /* !LDAP_DEBUG_R_SASL */
1192         assert( mutex != NULL );
1193 #endif /* !LDAP_DEBUG_R_SASL */
1194         return ldap_pvt_thread_mutex_lock( (ldap_pvt_thread_mutex_t *)mutex )
1195                 ? SASL_FAIL : SASL_OK;
1196 }
1197
1198 int ldap_pvt_sasl_mutex_unlock(void *mutex)
1199 {
1200 #ifdef LDAP_DEBUG_R_SASL
1201         if ( mutex == NULL ) {
1202                 return SASL_OK;
1203         }
1204 #else /* !LDAP_DEBUG_R_SASL */
1205         assert( mutex != NULL );
1206 #endif /* !LDAP_DEBUG_R_SASL */
1207         return ldap_pvt_thread_mutex_unlock( (ldap_pvt_thread_mutex_t *)mutex )
1208                 ? SASL_FAIL : SASL_OK;
1209 }
1210
1211 void ldap_pvt_sasl_mutex_dispose(void *mutex)
1212 {
1213 #ifdef LDAP_DEBUG_R_SASL
1214         if ( mutex == NULL ) {
1215                 return;
1216         }
1217 #else /* !LDAP_DEBUG_R_SASL */
1218         assert( mutex != NULL );
1219 #endif /* !LDAP_DEBUG_R_SASL */
1220         (void) ldap_pvt_thread_mutex_destroy( (ldap_pvt_thread_mutex_t *)mutex );
1221         LDAP_FREE( mutex );
1222 }
1223 #endif
1224
1225 #else
1226 int ldap_int_sasl_init( void )
1227 { return LDAP_SUCCESS; }
1228
1229 int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
1230 { return LDAP_SUCCESS; }
1231
1232 int
1233 ldap_int_sasl_bind(
1234         LDAP                    *ld,
1235         const char              *dn,
1236         const char              *mechs,
1237         LDAPControl             **sctrls,
1238         LDAPControl             **cctrls,
1239         unsigned                flags,
1240         LDAP_SASL_INTERACT_PROC *interact,
1241         void * defaults )
1242 { return LDAP_NOT_SUPPORTED; }
1243
1244 int
1245 ldap_int_sasl_external(
1246         LDAP *ld,
1247         LDAPConn *conn,
1248         const char * authid,
1249         ber_len_t ssf )
1250 { return LDAP_SUCCESS; }
1251
1252 #endif /* HAVE_CYRUS_SASL */