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