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