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