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