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