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