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