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