]> git.sur5r.net Git - openldap/blob - libraries/libldap/sasl.c
Update PF_INET6 and PF_UNIX detection, both default to auto
[openldap] / libraries / libldap / sasl.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6
7 /*
8  *      BindRequest ::= SEQUENCE {
9  *              version         INTEGER,
10  *              name            DistinguishedName,       -- who
11  *              authentication  CHOICE {
12  *                      simple          [0] OCTET STRING -- passwd
13 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND
14  *                      krbv42ldap      [1] OCTET STRING
15  *                      krbv42dsa       [2] OCTET STRING
16 #endif
17  *                      sasl            [3] SaslCredentials     -- LDAPv3
18  *              }
19  *      }
20  *
21  *      BindResponse ::= SEQUENCE {
22  *              COMPONENTS OF LDAPResult,
23  *              serverSaslCreds         OCTET STRING OPTIONAL -- LDAPv3
24  *      }
25  *
26  */
27
28 #include "portable.h"
29
30 #include <stdio.h>
31
32 #include <ac/socket.h>
33 #include <ac/string.h>
34 #include <ac/time.h>
35 #include <ac/errno.h>
36
37 #include "ldap-int.h"
38
39
40 /*
41  * ldap_sasl_bind - bind to the ldap server (and X.500).
42  * The dn (usually NULL), mechanism, and credentials are provided.
43  * The message id of the request initiated is provided upon successful
44  * (LDAP_SUCCESS) return.
45  *
46  * Example:
47  *      ldap_sasl_bind( ld, NULL, "mechanism",
48  *              cred, NULL, NULL, &msgid )
49  */
50
51 int
52 ldap_sasl_bind(
53         LDAP                    *ld,
54         LDAP_CONST char *dn,
55         LDAP_CONST char *mechanism,
56         struct berval   *cred,
57         LDAPControl             **sctrls,
58         LDAPControl             **cctrls,
59         int                             *msgidp )
60 {
61         BerElement      *ber;
62         int rc;
63
64         Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n", 0, 0, 0 );
65
66         assert( ld != NULL );
67         assert( LDAP_VALID( ld ) );
68         assert( msgidp != NULL );
69
70         if( msgidp == NULL ) {
71                 ld->ld_errno = LDAP_PARAM_ERROR;
72                 return ld->ld_errno;
73         }
74
75         if( mechanism == LDAP_SASL_SIMPLE ) {
76                 if( dn == NULL && cred != NULL ) {
77                         /* use default binddn */
78                         dn = ld->ld_defbinddn;
79                 }
80
81         } else if( ld->ld_version < LDAP_VERSION3 ) {
82                 ld->ld_errno = LDAP_NOT_SUPPORTED;
83                 return ld->ld_errno;
84         }
85
86         if ( dn == NULL ) {
87                 dn = "";
88         }
89
90         /* create a message to send */
91         if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
92                 ld->ld_errno = LDAP_NO_MEMORY;
93                 return ld->ld_errno;
94         }
95
96         assert( BER_VALID( ber ) );
97
98         if( mechanism == LDAP_SASL_SIMPLE ) {
99                 /* simple bind */
100                 rc = ber_printf( ber, "{it{istO}" /*}*/,
101                         ++ld->ld_msgid, LDAP_REQ_BIND,
102                         ld->ld_version, dn, LDAP_AUTH_SIMPLE,
103                         cred );
104                 
105         } else if ( cred == NULL ) {
106                 /* SASL bind w/o creditials */
107                 rc = ber_printf( ber, "{it{ist{s}}" /*}*/,
108                         ++ld->ld_msgid, LDAP_REQ_BIND,
109                         ld->ld_version, dn, LDAP_AUTH_SASL,
110                         mechanism );
111
112         } else {
113                 /* SASL bind w/ creditials */
114                 rc = ber_printf( ber, "{it{ist{sO}}" /*}*/,
115                         ++ld->ld_msgid, LDAP_REQ_BIND,
116                         ld->ld_version, dn, LDAP_AUTH_SASL,
117                         mechanism, cred );
118         }
119
120         if( rc == -1 ) {
121                 ld->ld_errno = LDAP_ENCODING_ERROR;
122                 ber_free( ber, 1 );
123                 return( -1 );
124         }
125
126         /* Put Server Controls */
127         if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
128                 ber_free( ber, 1 );
129                 return ld->ld_errno;
130         }
131
132         if ( ber_printf( ber, /*{*/ "}" ) == -1 ) {
133                 ld->ld_errno = LDAP_ENCODING_ERROR;
134                 ber_free( ber, 1 );
135                 return ld->ld_errno;
136         }
137
138 #ifndef LDAP_NOCACHE
139         if ( ld->ld_cache != NULL ) {
140                 ldap_flush_cache( ld );
141         }
142 #endif /* !LDAP_NOCACHE */
143
144         /* send the message */
145         *msgidp = ldap_send_initial_request( ld, LDAP_REQ_BIND, dn, ber );
146
147         if(*msgidp < 0)
148                 return ld->ld_errno;
149
150         return LDAP_SUCCESS;
151 }
152
153
154 int
155 ldap_sasl_bind_s(
156         LDAP                    *ld,
157         LDAP_CONST char *dn,
158         LDAP_CONST char *mechanism,
159         struct berval   *cred,
160         LDAPControl             **sctrls,
161         LDAPControl             **cctrls,
162         struct berval   **servercredp )
163 {
164         int     rc, msgid;
165         LDAPMessage     *result;
166         struct berval   *scredp = NULL;
167
168         Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind_s\n", 0, 0, 0 );
169
170         /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
171         if( servercredp != NULL ) {
172                 if (ld->ld_version < LDAP_VERSION3) {
173                         ld->ld_errno = LDAP_NOT_SUPPORTED;
174                         return ld->ld_errno;
175                 }
176                 *servercredp = NULL;
177         }
178
179         rc = ldap_sasl_bind( ld, dn, mechanism, cred, sctrls, cctrls, &msgid );
180
181         if ( rc != LDAP_SUCCESS ) {
182                 return( rc );
183         }
184
185         if ( ldap_result( ld, msgid, 1, NULL, &result ) == -1 ) {
186                 return( ld->ld_errno ); /* ldap_result sets ld_errno */
187         }
188
189         /* parse the results */
190         scredp = NULL;
191         if( servercredp != NULL ) {
192                 rc = ldap_parse_sasl_bind_result( ld, result, &scredp, 0 );
193         }
194
195         if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
196                 ldap_msgfree( result );
197                 return( rc );
198         }
199
200         rc = ldap_result2error( ld, result, 1 );
201
202         if ( rc == LDAP_SUCCESS || rc == LDAP_SASL_BIND_IN_PROGRESS ) {
203                 if( servercredp != NULL ) {
204                         *servercredp = scredp;
205                         scredp = NULL;
206                 }
207         }
208
209         if ( scredp != NULL ) {
210                 ber_bvfree(scredp);
211         }
212
213         return rc;
214 }
215
216
217 /*
218 * Parse BindResponse:
219 *
220 *   BindResponse ::= [APPLICATION 1] SEQUENCE {
221 *     COMPONENTS OF LDAPResult,
222 *     serverSaslCreds  [7] OCTET STRING OPTIONAL }
223 *
224 *   LDAPResult ::= SEQUENCE {
225 *     resultCode      ENUMERATED,
226 *     matchedDN       LDAPDN,
227 *     errorMessage    LDAPString,
228 *     referral        [3] Referral OPTIONAL }
229 */
230
231 int
232 ldap_parse_sasl_bind_result(
233         LDAP                    *ld,
234         LDAPMessage             *res,
235         struct berval   **servercredp,
236         int                             freeit )
237 {
238         ber_int_t errcode;
239         struct berval* scred;
240
241         ber_tag_t tag;
242         BerElement      *ber;
243
244         Debug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 );
245
246         assert( ld != NULL );
247         assert( LDAP_VALID( ld ) );
248         assert( res != NULL );
249
250         if ( ld == NULL || res == NULL ) {
251                 return LDAP_PARAM_ERROR;
252         }
253
254         if( servercredp != NULL ) {
255                 if( ld->ld_version < LDAP_VERSION2 ) {
256                         return LDAP_NOT_SUPPORTED;
257                 }
258                 *servercredp = NULL;
259         }
260
261         if( res->lm_msgtype != LDAP_RES_BIND ) {
262                 ld->ld_errno = LDAP_PARAM_ERROR;
263                 return ld->ld_errno;
264         }
265
266         scred = NULL;
267
268         if ( ld->ld_error ) {
269                 LDAP_FREE( ld->ld_error );
270                 ld->ld_error = NULL;
271         }
272         if ( ld->ld_matched ) {
273                 LDAP_FREE( ld->ld_matched );
274                 ld->ld_matched = NULL;
275         }
276
277         /* parse results */
278
279         ber = ber_dup( res->lm_ber );
280
281         if( ber == NULL ) {
282                 ld->ld_errno = LDAP_NO_MEMORY;
283                 return ld->ld_errno;
284         }
285
286         if ( ld->ld_version < LDAP_VERSION2 ) {
287                 tag = ber_scanf( ber, "{ia}",
288                         &errcode, &ld->ld_error );
289
290                 if( tag == LBER_ERROR ) {
291                         ber_free( ber, 0 );
292                         ld->ld_errno = LDAP_DECODING_ERROR;
293                         return ld->ld_errno;
294                 }
295
296         } else {
297                 ber_len_t len;
298
299                 tag = ber_scanf( ber, "{iaa" /*}*/,
300                         &errcode, &ld->ld_matched, &ld->ld_error );
301
302                 if( tag == LBER_ERROR ) {
303                         ber_free( ber, 0 );
304                         ld->ld_errno = LDAP_DECODING_ERROR;
305                         return ld->ld_errno;
306                 }
307
308                 tag = ber_peek_tag(ber, &len);
309
310                 if( tag == LDAP_TAG_REFERRAL ) {
311                         /* skip 'em */
312                         if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
313                                 ber_free( ber, 0 );
314                                 ld->ld_errno = LDAP_DECODING_ERROR;
315                                 return ld->ld_errno;
316                         }
317
318                         tag = ber_peek_tag(ber, &len);
319                 }
320
321                 if( tag == LDAP_TAG_SASL_RES_CREDS ) {
322                         if( ber_scanf( ber, "O", &scred ) == LBER_ERROR ) {
323                                 ber_free( ber, 0 );
324                                 ld->ld_errno = LDAP_DECODING_ERROR;
325                                 return ld->ld_errno;
326                         }
327                 }
328         }
329
330         ber_free( ber, 0 );
331
332         if ( servercredp != NULL ) {
333                 *servercredp = scred;
334
335         } else if ( scred != NULL ) {
336                 ber_bvfree( scred );
337         }
338
339         ld->ld_errno = errcode;
340
341         if ( freeit ) {
342                 ldap_msgfree( res );
343         }
344
345         return( ld->ld_errno );
346 }
347
348 #ifdef HAVE_CYRUS_SASL
349 /*
350 * Various Cyrus SASL related stuff.
351 */
352
353 #define MAX_BUFF_SIZE   65536
354 #define MIN_BUFF_SIZE   4096
355
356 static char *
357 array2str( char **a )
358 {
359         char *s, **v, *p;
360         int len = 0;
361
362         for ( v = a; *v != NULL; v++ ) {
363                 len += strlen( *v ) + 1; /* for a space */
364         }
365
366         if ( len == 0 ) {
367                 return NULL;
368         }
369
370         s = LDAP_MALLOC ( len ); /* last space holds \0 */
371
372         if ( s == NULL ) {
373                 return NULL;    
374         }
375
376         p = s;
377         for ( v = a; *v != NULL; v++ ) {
378                 int len;
379
380                 if ( v != a ) {
381                         strncpy( p, " ", 1 );
382                         ++p;
383                 }
384                 len = strlen( *v );
385                 strncpy( p, *v, len );
386                 p += len;
387         }
388
389         *p = '\0';
390
391         return s;
392 }
393
394 int ldap_pvt_sasl_init( void )
395 {
396         /* XXX not threadsafe */
397         static int sasl_initialized = 0;
398
399         if ( sasl_initialized ) {
400                 return 0;
401         }
402 #ifndef CSRIMALLOC
403         sasl_set_alloc( ber_memalloc, ber_memcalloc, ber_memrealloc, ber_memfree );
404 #endif /* CSRIMALLOC */
405
406         if ( sasl_client_init( NULL ) == SASL_OK ) {
407                 sasl_initialized = 1;
408                 return 0;
409         }
410
411         return -1;
412 }
413
414 /*
415  * SASL encryption support for LBER Sockbufs
416  */
417
418 struct sb_sasl_data {
419         sasl_conn_t             *sasl_context;
420         Sockbuf_Buf             sec_buf_in;
421         Sockbuf_Buf             buf_in;
422         Sockbuf_Buf             buf_out;
423 };
424
425 static int
426 sb_sasl_setup( Sockbuf_IO_Desc *sbiod, void *arg )
427 {
428         struct sb_sasl_data     *p;
429
430         assert( sbiod != NULL );
431
432         p = LBER_MALLOC( sizeof( *p ) );
433         if ( p == NULL )
434                 return -1;
435         p->sasl_context = (sasl_conn_t *)arg;
436         ber_pvt_sb_buf_init( &p->sec_buf_in );
437         ber_pvt_sb_buf_init( &p->buf_in );
438         ber_pvt_sb_buf_init( &p->buf_out );
439         if ( ber_pvt_sb_grow_buffer( &p->sec_buf_in, MIN_BUFF_SIZE ) < 0 ) {
440                 errno = ENOMEM;
441                 return -1;
442         }
443
444         sbiod->sbiod_pvt = p;
445
446         return 0;
447 }
448
449 static int
450 sb_sasl_remove( Sockbuf_IO_Desc *sbiod )
451 {
452         struct sb_sasl_data     *p;
453
454         assert( sbiod != NULL );
455         
456         p = (struct sb_sasl_data *)sbiod->sbiod_pvt;
457         ber_pvt_sb_buf_destroy( &p->sec_buf_in );
458         ber_pvt_sb_buf_destroy( &p->buf_in );
459         ber_pvt_sb_buf_destroy( &p->buf_out );
460         LBER_FREE( p );
461         sbiod->sbiod_pvt = NULL;
462         return 0;
463 }
464
465 static ber_len_t
466 sb_sasl_pkt_length( const char *buf, int debuglevel )
467 {
468         ber_len_t               size;
469         long                    tmp;
470
471         assert( buf != NULL );
472
473         tmp = *((long *)buf);
474         size = ntohl( tmp );
475    
476         if ( size > MAX_BUFF_SIZE ) {
477                 /* somebody is trying to mess me up. */
478                 ber_log_printf( LDAP_DEBUG_ANY, debuglevel,
479                         "sb_sasl_pkt_length: received illegal packet length "
480                         "of %lu bytes\n", (unsigned long)size );      
481                 size = 16; /* this should lead to an error. */
482 }
483
484         return size + 4; /* include the size !!! */
485 }
486
487 /* Drop a processed packet from the input buffer */
488 static void
489 sb_sasl_drop_packet ( Sockbuf_Buf *sec_buf_in, int debuglevel )
490 {
491         ber_slen_t                      len;
492
493         len = sec_buf_in->buf_ptr - sec_buf_in->buf_end;
494         if ( len > 0 )
495                 memmove( sec_buf_in->buf_base, sec_buf_in->buf_base +
496                         sec_buf_in->buf_end, len );
497    
498         if ( len >= 4 ) {
499                 sec_buf_in->buf_end = sb_sasl_pkt_length( sec_buf_in->buf_base,
500                         debuglevel);
501         }
502         else {
503                 sec_buf_in->buf_end = 0;
504         }
505         sec_buf_in->buf_ptr = len;
506 }
507
508 static ber_slen_t
509 sb_sasl_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
510 {
511         struct sb_sasl_data     *p;
512         ber_slen_t              ret, bufptr;
513    
514         assert( sbiod != NULL );
515         assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
516
517         p = (struct sb_sasl_data *)sbiod->sbiod_pvt;
518
519         /* Are there anything left in the buffer? */
520         ret = ber_pvt_sb_copy_out( &p->buf_in, buf, len );
521         bufptr = ret;
522         len -= ret;
523
524         if ( len == 0 )
525                 return bufptr;
526
527         ber_pvt_sb_buf_destroy( &p->buf_in );
528
529         /* Read the length of the packet */
530         while ( p->sec_buf_in.buf_ptr < 4 ) {
531                 ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base,
532                         4 - p->sec_buf_in.buf_ptr );
533 #ifdef EINTR
534                 if ( ( ret < 0 ) && ( errno == EINTR ) )
535                         continue;
536 #endif
537                 if ( ret <= 0 )
538                         return ret;
539
540                 p->sec_buf_in.buf_ptr += ret;
541         }
542
543         /* The new packet always starts at p->sec_buf_in.buf_base */
544         ret = sb_sasl_pkt_length( p->sec_buf_in.buf_base,
545                 sbiod->sbiod_sb->sb_debug );
546
547         /* Grow the packet buffer if neccessary */
548         if ( ( p->sec_buf_in.buf_size < ret ) && 
549                         ber_pvt_sb_grow_buffer( &p->sec_buf_in, ret ) < 0 ) {
550                 errno = ENOMEM;
551                 return -1;
552         }
553         p->sec_buf_in.buf_end = ret;
554
555         /* Did we read the whole encrypted packet? */
556         while ( p->sec_buf_in.buf_ptr < p->sec_buf_in.buf_end ) {
557                 /* No, we have got only a part of it */
558                 ret = p->sec_buf_in.buf_end - p->sec_buf_in.buf_ptr;
559
560                 ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base +
561                         p->sec_buf_in.buf_ptr, ret );
562 #ifdef EINTR
563                 if ( ( ret < 0 ) && ( errno == EINTR ) )
564                         continue;
565 #endif
566                 if ( ret <= 0 )
567                         return ret;
568
569                 p->sec_buf_in.buf_ptr += ret;
570         }
571
572         /* Decode the packet */
573         ret = sasl_decode( p->sasl_context, p->sec_buf_in.buf_base,
574                 p->sec_buf_in.buf_end, &p->buf_in.buf_base,
575                 (unsigned *)&p->buf_in.buf_end );
576         if ( ret != SASL_OK ) {
577                 ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug,
578                         "sb_sasl_read: failed to decode packet: %s\n",
579                         sasl_errstring( ret, NULL, NULL ) );
580                 sb_sasl_drop_packet( &p->sec_buf_in,
581                         sbiod->sbiod_sb->sb_debug );
582                 errno = EIO;
583                 return -1;
584         }
585         
586         /* Drop the packet from the input buffer */
587         sb_sasl_drop_packet( &p->sec_buf_in, sbiod->sbiod_sb->sb_debug );
588
589         p->buf_in.buf_size = p->buf_in.buf_end;
590
591         bufptr += ber_pvt_sb_copy_out( &p->buf_in, (char*) buf + bufptr, len );
592
593         return bufptr;
594 }
595
596 static ber_slen_t
597 sb_sasl_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
598 {
599         struct sb_sasl_data     *p;
600         int                     ret;
601
602         assert( sbiod != NULL );
603         assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
604
605         p = (struct sb_sasl_data *)sbiod->sbiod_pvt;
606
607         /* Are there anything left in the buffer? */
608         if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) {
609                 ret = ber_pvt_sb_do_write( sbiod, &p->buf_out );
610                 if ( ret <= 0 )
611                         return ret;
612         }
613
614         /* now encode the next packet. */
615         ber_pvt_sb_buf_destroy( &p->buf_out );
616         ret = sasl_encode( p->sasl_context, buf, len, &p->buf_out.buf_base,
617                 (unsigned *)&p->buf_out.buf_size );
618         if ( ret != SASL_OK ) {
619                 ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug,
620                         "sb_sasl_write: failed to encode packet: %s\n",
621                         sasl_errstring( ret, NULL, NULL ) );
622                 return -1;
623         }
624         p->buf_out.buf_end = p->buf_out.buf_size;
625
626         ret = ber_pvt_sb_do_write( sbiod, &p->buf_out );
627         if ( ret <= 0 )
628                 return ret;
629         return len;
630 }
631
632 static int
633 sb_sasl_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
634 {
635         struct sb_sasl_data     *p;
636
637         p = (struct sb_sasl_data *)sbiod->sbiod_pvt;
638
639         if ( opt == LBER_SB_OPT_DATA_READY ) {
640                 if ( p->buf_in.buf_ptr != p->buf_in.buf_end )
641                         return 1;
642         }
643         
644         return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
645 }
646
647 Sockbuf_IO ldap_pvt_sockbuf_io_sasl =
648 {
649         sb_sasl_setup,          /* sbi_setup */
650         sb_sasl_remove,         /* sbi_remove */
651         sb_sasl_ctrl,           /* sbi_ctrl */
652         sb_sasl_read,           /* sbi_read */
653         sb_sasl_write,          /* sbi_write */
654         NULL                    /* sbi_close */
655 };
656
657 int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg )
658 {
659         /* don't install the stuff unless security has been negotiated */
660
661         if ( !ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO,
662                         &ldap_pvt_sockbuf_io_sasl ) )
663                 ber_sockbuf_add_io( sb, &ldap_pvt_sockbuf_io_sasl,
664                         LBER_SBIOD_LEVEL_APPLICATION, ctx_arg );
665
666         return LDAP_SUCCESS;
667 }
668
669 static int
670 sasl_err2ldap( int saslerr )
671 {
672         int rc;
673
674         switch (saslerr) {
675                 case SASL_CONTINUE:
676                         rc = LDAP_MORE_RESULTS_TO_RETURN;
677                         break;
678                 case SASL_OK:
679                         rc = LDAP_SUCCESS;
680                         break;
681                 case SASL_FAIL:
682                         rc = LDAP_LOCAL_ERROR;
683                         break;
684                 case SASL_NOMEM:
685                         rc = LDAP_NO_MEMORY;
686                         break;
687                 case SASL_NOMECH:
688                         rc = LDAP_AUTH_UNKNOWN;
689                         break;
690                 case SASL_BADAUTH:
691                         rc = LDAP_AUTH_UNKNOWN;
692                         break;
693                 case SASL_NOAUTHZ:
694                         rc = LDAP_PARAM_ERROR;
695                         break;
696                 case SASL_TOOWEAK:
697                 case SASL_ENCRYPT:
698                         rc = LDAP_AUTH_UNKNOWN;
699                         break;
700                 default:
701                         rc = LDAP_LOCAL_ERROR;
702                         break;
703         }
704
705         assert( rc == LDAP_SUCCESS || LDAP_API_ERROR( rc ) );
706         return rc;
707 }
708
709 int
710 ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist )
711 {
712         /* we need to query the server for supported mechs anyway */
713         LDAPMessage *res, *e;
714         char *attrs[] = { "supportedSASLMechanisms", NULL };
715         char **values, *mechlist;
716         int rc;
717
718         Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_getmech\n", 0, 0, 0 );
719
720         rc = ldap_search_s( ld, NULL, LDAP_SCOPE_BASE,
721                 NULL, attrs, 0, &res );
722
723         if ( rc != LDAP_SUCCESS ) {
724                 return ld->ld_errno;
725         }
726                 
727         e = ldap_first_entry( ld, res );
728         if ( e == NULL ) {
729                 if ( ld->ld_errno == LDAP_SUCCESS ) {
730                         ld->ld_errno = LDAP_UNAVAILABLE;
731                 }
732                 return ld->ld_errno;
733         }
734
735         values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
736         if ( values == NULL ) {
737                 ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
738                 ldap_msgfree( res );
739                 return ld->ld_errno;
740         }
741
742         mechlist = array2str( values );
743         if ( mechlist == NULL ) {
744                 ld->ld_errno = LDAP_NO_MEMORY;
745                 LDAP_VFREE( values );
746                 ldap_msgfree( res );
747                 return ld->ld_errno;
748         } 
749
750         LDAP_VFREE( values );
751         ldap_msgfree( res );
752
753         *pmechlist = mechlist;
754
755         return LDAP_SUCCESS;
756 }
757
758 int
759 ldap_pvt_sasl_bind(
760         LDAP                    *ld,
761         LDAP_CONST char         *dn,
762         LDAP_CONST char         *mechs,
763         LDAP_CONST sasl_callback_t      *callbacks,
764         LDAPControl             **sctrls,
765         LDAPControl             **cctrls )
766 {
767         const char *mech;
768         int                     saslrc, rc;
769         sasl_ssf_t              *ssf = NULL;
770         unsigned credlen;
771         struct berval ccred, *scred;
772         char *host;
773         sasl_interact_t *client_interact = NULL;
774         struct sockaddr_in      sin;
775         socklen_t               len;
776         sasl_security_properties_t      secprops;
777         ber_socket_t            sd;
778
779         Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_bind\n", 0, 0, 0 );
780
781         /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
782         if (ld->ld_version < LDAP_VERSION3) {
783                 ld->ld_errno = LDAP_NOT_SUPPORTED;
784                 return ld->ld_errno;
785         }
786
787         ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
788
789         if ( sd == AC_SOCKET_INVALID ) {
790                 /* not connected yet */
791                 int rc = ldap_open_defconn( ld );
792   
793                 if( rc < 0 ) return ld->ld_errno;
794                 ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
795         }   
796
797         /* XXX this doesn't work with PF_LOCAL hosts */
798         host = ldap_host_connected_to( ld->ld_sb );
799
800         if ( host == NULL ) {
801                 ld->ld_errno = LDAP_UNAVAILABLE;
802                 return ld->ld_errno;
803         }
804
805         if ( ld->ld_sasl_context != NULL ) {
806                 sasl_dispose( &ld->ld_sasl_context );
807         }
808
809         saslrc = sasl_client_new( "ldap", host, callbacks, SASL_SECURITY_LAYER,
810                 &ld->ld_sasl_context );
811
812         LDAP_FREE( host );
813
814         if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
815                 ld->ld_errno = sasl_err2ldap( saslrc );
816                 sasl_dispose( &ld->ld_sasl_context );
817                 return ld->ld_errno;
818         }
819
820         len = sizeof( sin );
821         if ( getpeername( sd, (struct sockaddr *)&sin, &len ) == -1 ) {
822                 Debug( LDAP_DEBUG_ANY, "SASL: can't query remote IP.\n",
823                         0, 0, 0 );
824                 ld->ld_errno = LDAP_OPERATIONS_ERROR;
825                 return ld->ld_errno;
826         }
827         sasl_setprop( ld->ld_sasl_context, SASL_IP_REMOTE, &sin );
828
829         len = sizeof( sin );
830         if ( getsockname( sd, (struct sockaddr *)&sin, &len ) == -1 ) {
831                 Debug( LDAP_DEBUG_ANY, "SASL: can't query local IP.\n",
832                         0, 0, 0 );
833                 ld->ld_errno = LDAP_OPERATIONS_ERROR;
834                 return ld->ld_errno;
835         }
836         sasl_setprop( ld->ld_sasl_context, SASL_IP_LOCAL, &sin );
837
838         memset( &secprops, 0, sizeof( secprops ) );
839         secprops.min_ssf = ld->ld_options.ldo_sasl_minssf;
840         secprops.max_ssf = ld->ld_options.ldo_sasl_maxssf;
841         secprops.security_flags = SASL_SECURITY_LAYER;
842         secprops.maxbufsize = 65536;
843         sasl_setprop( ld->ld_sasl_context, SASL_SEC_PROPS, &secprops );
844
845         ccred.bv_val = NULL;
846         ccred.bv_len = 0;
847
848         saslrc = sasl_client_start( ld->ld_sasl_context,
849                 mechs,
850                 NULL,
851                 &client_interact,
852                 &ccred.bv_val,
853                 &credlen,
854                 &mech );
855
856         ccred.bv_len = credlen;
857
858         if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
859                 ld->ld_errno = sasl_err2ldap( saslrc );
860                 sasl_dispose( &ld->ld_sasl_context );
861                 return ld->ld_errno;
862         }
863
864         scred = NULL;
865
866         do {
867                 unsigned credlen;
868                 sasl_interact_t *client_interact = NULL;
869
870                 rc = ldap_sasl_bind_s( ld, dn, mech, &ccred, sctrls, cctrls, &scred );
871                 if ( rc == LDAP_SUCCESS ) {
872                         break;
873                 } else if ( rc != LDAP_SASL_BIND_IN_PROGRESS ) {
874                         if ( ccred.bv_val != NULL ) {
875                                 LDAP_FREE( ccred.bv_val );
876                         }
877                         sasl_dispose( &ld->ld_sasl_context );
878                         return ld->ld_errno;
879                 }
880
881                 if ( ccred.bv_val != NULL ) {
882                         LDAP_FREE( ccred.bv_val );
883                         ccred.bv_val = NULL;
884                 }
885
886                 saslrc = sasl_client_step( ld->ld_sasl_context,
887                         (scred == NULL) ? NULL : scred->bv_val,
888                         (scred == NULL) ? 0 : scred->bv_len,
889                         &client_interact,
890                         &ccred.bv_val,
891                         &credlen );
892
893                 ccred.bv_len = credlen;
894                 ber_bvfree( scred );
895
896                 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
897                         ld->ld_errno = sasl_err2ldap( saslrc );
898                         sasl_dispose( &ld->ld_sasl_context );
899                         return ld->ld_errno;
900                 }
901         } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
902
903         assert ( rc == LDAP_SUCCESS );
904
905         if ( sasl_getprop( ld->ld_sasl_context, SASL_SSF, (void **)&ssf )
906                 == SASL_OK && ssf && *ssf ) {
907                 ldap_pvt_sasl_install( ld->ld_sb, ld->ld_sasl_context );
908         }
909
910         return rc;
911 }
912
913 /* based on sample/sample-client.c */
914 static int
915 ldap_pvt_sasl_getsecret(sasl_conn_t *conn,
916         void *context, int id, sasl_secret_t **psecret)
917 {
918         struct berval *passphrase = (struct berval *)context;
919         size_t len;           
920
921         if ( conn == NULL || psecret == NULL || id != SASL_CB_PASS ) {
922                 return SASL_BADPARAM;
923         }
924
925         len = (passphrase != NULL) ? (size_t)passphrase->bv_len: 0;
926
927         *psecret = (sasl_secret_t *) LDAP_MALLOC( sizeof( sasl_secret_t ) + len );
928         if ( *psecret == NULL ) {
929                 return SASL_NOMEM;
930         }
931
932         (*psecret)->len = passphrase->bv_len;
933
934         if ( passphrase != NULL ) {
935                 memcpy((*psecret)->data, passphrase->bv_val, len);
936         }
937
938         return SASL_OK;
939 }
940
941 static int
942 ldap_pvt_sasl_getsimple(void *context, int id, const char **result, int *len)
943 {
944         const char *value = (const char *)context;
945
946         if ( result == NULL ) {
947                 return SASL_BADPARAM;
948         }
949
950         switch ( id ) {
951                 case SASL_CB_USER:
952                 case SASL_CB_AUTHNAME:
953                         *result = value;
954                         if ( len )
955                                 *len = value ? strlen( value ) : 0;
956                         break;
957                 case SASL_CB_LANGUAGE:
958                         *result = NULL;
959                         if ( len )
960                                 *len = 0;
961                         break;
962                 default:
963                         return SASL_BADPARAM;
964         }
965
966         return SASL_OK;
967 }
968
969 int
970 ldap_pvt_sasl_get_option( LDAP *ld, int option, void *arg )
971 {
972         sasl_ssf_t      *ssf;
973         
974         if ( ld == NULL )
975                 return -1;
976
977         switch ( option ) {
978                 case LDAP_OPT_X_SASL_MINSSF:
979                         *(int *)arg = ld->ld_options.ldo_sasl_minssf;
980                         break;
981                 case LDAP_OPT_X_SASL_MAXSSF:
982                         *(int *)arg = ld->ld_options.ldo_sasl_maxssf;
983                         break;
984                 case LDAP_OPT_X_SASL_ACTSSF:
985                         if ( ld->ld_sasl_context == NULL ) {
986                                 *(int *)arg = -1;
987                                 break;
988                         }
989                         if ( sasl_getprop( ld->ld_sasl_context, SASL_SSF,
990                                 (void **) &ssf ) != SASL_OK )
991                         {
992                                 return -1;
993                         }
994                         *(int *)arg = *ssf;
995                         break;
996                 default:
997                         return -1;
998         }
999         return 0;
1000 }
1001
1002 int
1003 ldap_pvt_sasl_set_option( LDAP *ld, int option, void *arg )
1004 {
1005         if ( ld == NULL )
1006                 return -1;
1007
1008         switch ( option ) {
1009                 case LDAP_OPT_X_SASL_MINSSF:
1010                         ld->ld_options.ldo_sasl_minssf = *(int *)arg;
1011                         break;
1012                 case LDAP_OPT_X_SASL_MAXSSF:
1013                         ld->ld_options.ldo_sasl_maxssf = *(int *)arg;
1014                         break;
1015                 case LDAP_OPT_X_SASL_ACTSSF:
1016                         /* This option is read-only */
1017                 default:
1018                         return -1;
1019         }
1020         return 0;
1021 }
1022
1023 /*
1024  * ldap_negotiated_sasl_bind_s - bind to the ldap server (and X.500)
1025  * using SASL authentication.
1026  *
1027  * This routine attempts to authenticate the user referred by the
1028  * authentication id using the provided password.  An optional
1029  * authorization identity may be provided.  An DN is generally not
1030  * provided [see AuthMethod].
1031  *
1032  * If the mechanism negotiated does not require a password, the
1033  * passwd field is ignored.  [A callback mechanism should really
1034  * be used].
1035  * 
1036  * LDAP_SUCCESS is returned upon success, the ldap error code
1037  * otherwise.
1038  *
1039  * Examples:
1040  *      ldap_negotiated_sasl_bind_s( ld, NULL,
1041  *          NULL, NULL, NULL,
1042  *              NULL, NULL, NULL, NULL );
1043  *
1044  *      ldap_negotiated_sasl_bind_s( ld, NULL,
1045  *          "user@OPENLDAP.ORG", NULL, NULL,
1046  *              "GSSAPI", NULL, NULL, NULL );
1047  *
1048  *      ldap_negotiated_sasl_bind_s( ld, NULL,
1049  *          "manager", "dn:cn=user,dc=openldap,dc=org", NULL,
1050  *              "DIGEST-MD5", NULL, NULL, NULL );
1051  *
1052  *      ldap_negotiated_sasl_bind_s( ld, NULL,
1053  *          "root@OPENLDAP.ORG", "u:user@OPENLDAP.ORG", NULL,
1054  *              "GSSAPI", NULL, NULL, NULL );
1055  *
1056  *      ldap_negotiated_sasl_bind_s( ld, NULL,
1057  *          "manager", "dn:cn=user,dc=openldap,dc=org", NULL,
1058  *              "DIGEST-MD5", NULL, NULL, NULL );
1059  */
1060 int
1061 ldap_negotiated_sasl_bind_s(
1062         LDAP *ld,
1063         LDAP_CONST char *dn, /* usually NULL */
1064         LDAP_CONST char *authenticationId,
1065         LDAP_CONST char *authorizationId, /* commonly NULL */
1066         LDAP_CONST char *saslMechanism,
1067         struct berval *passPhrase,
1068         LDAPControl **serverControls,
1069         LDAPControl **clientControls)
1070 {
1071         int n;
1072         sasl_callback_t callbacks[4];
1073         int rc;
1074
1075         Debug( LDAP_DEBUG_TRACE, "ldap_negotiated_sasl_bind_s\n", 0, 0, 0 );
1076
1077         if( saslMechanism == NULL || *saslMechanism == '\0' ) {
1078                 char *mechs;
1079                 rc = ldap_pvt_sasl_getmechs( ld, &mechs );
1080
1081                 if( rc != LDAP_SUCCESS ) {
1082                         return rc;
1083                 }
1084
1085                 saslMechanism = mechs;
1086         }
1087
1088         /* SASL Authentication Identity */
1089         callbacks[n=0].id = SASL_CB_AUTHNAME;
1090         callbacks[n].proc = ldap_pvt_sasl_getsimple;
1091         callbacks[n].context = (void *)authenticationId;
1092
1093         /* SASL Authorization Identity (userid) */
1094         if( authorizationId != NULL ) {
1095                 callbacks[++n].id = SASL_CB_USER;
1096                 callbacks[n].proc = ldap_pvt_sasl_getsimple;
1097                 callbacks[n].context = (void *)authorizationId;
1098         }
1099
1100         callbacks[++n].id = SASL_CB_PASS;
1101         callbacks[n].proc = ldap_pvt_sasl_getsecret;
1102         callbacks[n].context = (void *)passPhrase;
1103
1104         callbacks[++n].id = SASL_CB_LIST_END;
1105         callbacks[n].proc = NULL;
1106         callbacks[n].context = NULL;
1107
1108         assert( n * sizeof(sasl_callback_t) < sizeof(callbacks) );
1109
1110         rc = ldap_pvt_sasl_bind(ld, dn, saslMechanism, callbacks,
1111                 serverControls, clientControls);
1112
1113         return rc;
1114 }
1115 #endif /* HAVE_CYRUS_SASL */