]> git.sur5r.net Git - openldap/blob - libraries/libldap/sasl.c
00656001d9cbec9f2bb8331e2984d07f3bb89eb0
[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
36 #include "ldap-int.h"
37
38
39 /*
40  * ldap_sasl_bind - bind to the ldap server (and X.500).
41  * The dn (usually NULL), mechanism, and credentials are provided.
42  * The message id of the request initiated is provided upon successful
43  * (LDAP_SUCCESS) return.
44  *
45  * Example:
46  *      ldap_sasl_bind( ld, NULL, "mechanism",
47  *              cred, NULL, NULL, &msgid )
48  */
49
50 int
51 ldap_sasl_bind(
52         LDAP                    *ld,
53         LDAP_CONST char *dn,
54         LDAP_CONST char *mechanism,
55         struct berval   *cred,
56         LDAPControl             **sctrls,
57         LDAPControl             **cctrls,
58         int                             *msgidp )
59 {
60         BerElement      *ber;
61         int rc;
62
63         Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n", 0, 0, 0 );
64
65         assert( ld != NULL );
66         assert( LDAP_VALID( ld ) );
67         assert( msgidp != NULL );
68
69         if( msgidp == NULL ) {
70                 ld->ld_errno = LDAP_PARAM_ERROR;
71                 return ld->ld_errno;
72         }
73
74         if( mechanism == LDAP_SASL_SIMPLE ) {
75                 if( dn == NULL && cred != NULL ) {
76                         /* use default binddn */
77                         dn = ld->ld_defbinddn;
78                 }
79
80         } else if( ld->ld_version < LDAP_VERSION3 ) {
81                 ld->ld_errno = LDAP_NOT_SUPPORTED;
82                 return ld->ld_errno;
83         }
84
85         if ( dn == NULL ) {
86                 dn = "";
87         }
88
89         /* create a message to send */
90         if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
91                 ld->ld_errno = LDAP_NO_MEMORY;
92                 return ld->ld_errno;
93         }
94
95         assert( BER_VALID( ber ) );
96
97         if( mechanism == LDAP_SASL_SIMPLE ) {
98                 /* simple bind */
99                 rc = ber_printf( ber, "{it{istO}" /*}*/,
100                         ++ld->ld_msgid, LDAP_REQ_BIND,
101                         ld->ld_version, dn, LDAP_AUTH_SIMPLE,
102                         cred );
103                 
104         } else if ( cred == NULL ) {
105                 /* SASL bind w/o creditials */
106                 rc = ber_printf( ber, "{it{ist{s}}" /*}*/,
107                         ++ld->ld_msgid, LDAP_REQ_BIND,
108                         ld->ld_version, dn, LDAP_AUTH_SASL,
109                         mechanism );
110
111         } else {
112                 /* SASL bind w/ creditials */
113                 rc = ber_printf( ber, "{it{ist{sO}}" /*}*/,
114                         ++ld->ld_msgid, LDAP_REQ_BIND,
115                         ld->ld_version, dn, LDAP_AUTH_SASL,
116                         mechanism, cred );
117         }
118
119         if( rc == -1 ) {
120                 ld->ld_errno = LDAP_ENCODING_ERROR;
121                 ber_free( ber, 1 );
122                 return( -1 );
123         }
124
125         /* Put Server Controls */
126         if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
127                 ber_free( ber, 1 );
128                 return ld->ld_errno;
129         }
130
131         if ( ber_printf( ber, /*{*/ "}" ) == -1 ) {
132                 ld->ld_errno = LDAP_ENCODING_ERROR;
133                 ber_free( ber, 1 );
134                 return ld->ld_errno;
135         }
136
137 #ifndef LDAP_NOCACHE
138         if ( ld->ld_cache != NULL ) {
139                 ldap_flush_cache( ld );
140         }
141 #endif /* !LDAP_NOCACHE */
142
143         /* send the message */
144         *msgidp = ldap_send_initial_request( ld, LDAP_REQ_BIND, dn, ber );
145
146         if(*msgidp < 0)
147                 return ld->ld_errno;
148
149         return LDAP_SUCCESS;
150 }
151
152
153 int
154 ldap_sasl_bind_s(
155         LDAP                    *ld,
156         LDAP_CONST char *dn,
157         LDAP_CONST char *mechanism,
158         struct berval   *cred,
159         LDAPControl             **sctrls,
160         LDAPControl             **cctrls,
161         struct berval   **servercredp )
162 {
163         int     rc, msgid;
164         LDAPMessage     *result;
165         struct berval   *scredp = NULL;
166
167         Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind_s\n", 0, 0, 0 );
168
169         /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
170         if( servercredp != NULL ) {
171                 if (ld->ld_version < LDAP_VERSION3) {
172                         ld->ld_errno = LDAP_NOT_SUPPORTED;
173                         return ld->ld_errno;
174                 }
175                 *servercredp = NULL;
176         }
177
178         rc = ldap_sasl_bind( ld, dn, mechanism, cred, sctrls, cctrls, &msgid );
179
180         if ( rc != LDAP_SUCCESS ) {
181                 return( rc );
182         }
183
184         if ( ldap_result( ld, msgid, 1, NULL, &result ) == -1 ) {
185                 return( ld->ld_errno ); /* ldap_result sets ld_errno */
186         }
187
188         /* parse the results */
189         scredp = NULL;
190         if( servercredp != NULL ) {
191                 rc = ldap_parse_sasl_bind_result( ld, result, &scredp, 0 );
192         }
193
194         if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
195                 ldap_msgfree( result );
196                 return( rc );
197         }
198
199         rc = ldap_result2error( ld, result, 1 );
200
201         if ( rc == LDAP_SUCCESS || rc == LDAP_SASL_BIND_IN_PROGRESS ) {
202                 if( servercredp != NULL ) {
203                         *servercredp = scredp;
204                         scredp = NULL;
205                 }
206         }
207
208         if ( scredp != NULL ) {
209                 ber_bvfree(scredp);
210         }
211
212         return rc;
213 }
214
215
216 /*
217 * Parse BindResponse:
218 *
219 *   BindResponse ::= [APPLICATION 1] SEQUENCE {
220 *     COMPONENTS OF LDAPResult,
221 *     serverSaslCreds  [7] OCTET STRING OPTIONAL }
222 *
223 *   LDAPResult ::= SEQUENCE {
224 *     resultCode      ENUMERATED,
225 *     matchedDN       LDAPDN,
226 *     errorMessage    LDAPString,
227 *     referral        [3] Referral OPTIONAL }
228 */
229
230 int
231 ldap_parse_sasl_bind_result(
232         LDAP                    *ld,
233         LDAPMessage             *res,
234         struct berval   **servercredp,
235         int                             freeit )
236 {
237         ber_int_t errcode;
238         struct berval* scred;
239
240         ber_tag_t tag;
241         BerElement      *ber;
242
243         Debug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 );
244
245         assert( ld != NULL );
246         assert( LDAP_VALID( ld ) );
247         assert( res != NULL );
248
249         if ( ld == NULL || res == NULL ) {
250                 return LDAP_PARAM_ERROR;
251         }
252
253         if( servercredp != NULL ) {
254                 if( ld->ld_version < LDAP_VERSION2 ) {
255                         return LDAP_NOT_SUPPORTED;
256                 }
257                 *servercredp = NULL;
258         }
259
260         if( res->lm_msgtype != LDAP_RES_BIND ) {
261                 ld->ld_errno = LDAP_PARAM_ERROR;
262                 return ld->ld_errno;
263         }
264
265         scred = NULL;
266
267         if ( ld->ld_error ) {
268                 LDAP_FREE( ld->ld_error );
269                 ld->ld_error = NULL;
270         }
271         if ( ld->ld_matched ) {
272                 LDAP_FREE( ld->ld_matched );
273                 ld->ld_matched = NULL;
274         }
275
276         /* parse results */
277
278         ber = ber_dup( res->lm_ber );
279
280         if( ber == NULL ) {
281                 ld->ld_errno = LDAP_NO_MEMORY;
282                 return ld->ld_errno;
283         }
284
285         if ( ld->ld_version < LDAP_VERSION2 ) {
286                 tag = ber_scanf( ber, "{ia}",
287                         &errcode, &ld->ld_error );
288
289                 if( tag == LBER_ERROR ) {
290                         ber_free( ber, 0 );
291                         ld->ld_errno = LDAP_DECODING_ERROR;
292                         return ld->ld_errno;
293                 }
294
295         } else {
296                 ber_len_t len;
297
298                 tag = ber_scanf( ber, "{iaa" /*}*/,
299                         &errcode, &ld->ld_matched, &ld->ld_error );
300
301                 if( tag == LBER_ERROR ) {
302                         ber_free( ber, 0 );
303                         ld->ld_errno = LDAP_DECODING_ERROR;
304                         return ld->ld_errno;
305                 }
306
307                 tag = ber_peek_tag(ber, &len);
308
309                 if( tag == LDAP_TAG_REFERRAL ) {
310                         /* skip 'em */
311                         if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
312                                 ber_free( ber, 0 );
313                                 ld->ld_errno = LDAP_DECODING_ERROR;
314                                 return ld->ld_errno;
315                         }
316
317                         tag = ber_peek_tag(ber, &len);
318                 }
319
320                 if( tag == LDAP_TAG_SASL_RES_CREDS ) {
321                         if( ber_scanf( ber, "O", &scred ) == LBER_ERROR ) {
322                                 ber_free( ber, 0 );
323                                 ld->ld_errno = LDAP_DECODING_ERROR;
324                                 return ld->ld_errno;
325                         }
326                 }
327         }
328
329         ber_free( ber, 0 );
330
331         if ( servercredp != NULL ) {
332                 *servercredp = scred;
333
334         } else if ( scred != NULL ) {
335                 ber_bvfree( scred );
336         }
337
338         ld->ld_errno = errcode;
339
340         if ( freeit ) {
341                 ldap_msgfree( res );
342         }
343
344         return( ld->ld_errno );
345 }
346
347 #ifdef HAVE_CYRUS_SASL
348 /*
349 * Various Cyrus SASL related stuff.
350 */
351
352 static int sasl_setup( Sockbuf *sb, void *arg );
353 static int sasl_remove( Sockbuf *sb );
354 static ber_slen_t sasl_read( Sockbuf *sb, void *buf, ber_len_t len );
355 static ber_slen_t sasl_write( Sockbuf *sb, void *buf, ber_len_t len );
356 static int sasl_close( Sockbuf *sb );
357
358 static Sockbuf_IO sasl_io=
359 {
360 sasl_setup,
361 sasl_remove,
362 sasl_read,
363 sasl_write,
364 sasl_close
365 }; 
366
367 #define HAS_SASL( sb ) ((sb)->sb_io==&sasl_io)
368
369 static char *
370 array2str( char **a )
371 {
372         char *s, **v, *p;
373         int len = 0;
374
375         for ( v = a; *v != NULL; v++ ) {
376                 len += strlen( *v ) + 1; /* for a space */
377         }
378
379         if ( len == 0 ) {
380                 return NULL;
381         }
382
383         s = LDAP_MALLOC ( len ); /* last space holds \0 */
384
385         if ( s == NULL ) {
386                 return NULL;    
387         }
388
389         p = s;
390         for ( v = a; *v != NULL; v++ ) {
391                 int len;
392
393                 if ( v != a ) {
394                         strncpy( p, " ", 1 );
395                         ++p;
396                 }
397                 len = strlen( *v );
398                 strncpy( p, *v, len );
399                 p += len;
400         }
401
402         *p = '\0';
403
404         return s;
405 }
406
407 int ldap_pvt_sasl_init( void )
408 {
409         /* XXX not threadsafe */
410         static int sasl_initialized = 0;
411
412         if ( sasl_initialized ) {
413                 return 0;
414         }
415 #ifndef CSRIMALLOC
416         sasl_set_alloc( ber_memalloc, ber_memcalloc, ber_memrealloc, ber_memfree );
417 #endif /* CSRIMALLOC */
418
419         if ( sasl_client_init( NULL ) == SASL_OK ) {
420                 sasl_initialized = 1;
421                 return 0;
422         }
423
424         return -1;
425 }
426
427 int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg )
428 {
429         /* don't install the stuff unless security has been negotiated */
430
431         if ( !HAS_SASL( sb ) ) {
432                 ber_pvt_sb_clear_io( sb );
433                 ber_pvt_sb_set_io( sb, &sasl_io, ctx_arg );
434         }
435
436         return 0;
437 }
438
439 static int sasl_setup( Sockbuf *sb, void *arg )
440 {
441         sb->sb_iodata = arg;
442         return 0;
443 }
444
445 static int sasl_remove( Sockbuf *sb )
446 {
447         return 0;
448 }
449
450 static ber_slen_t sasl_read( Sockbuf *sb, void *buf, ber_len_t buflen )
451 {
452         char *recv_tok;
453         unsigned recv_tok_len;
454         sasl_conn_t *conn = (sasl_conn_t *)sb->sb_iodata;
455
456         if ((ber_pvt_sb_io_tcp.sbi_read)( sb, buf, buflen ) != buflen ) {
457                 return -1;
458         }
459
460         if ( sasl_decode( conn, buf, buflen, &recv_tok, &recv_tok_len ) != SASL_OK ) {
461                 return -1;
462         }
463
464         if ( recv_tok_len > buflen ) {
465                 LDAP_FREE( recv_tok );
466                 return -1;
467         }
468
469         memcpy( buf, recv_tok, recv_tok_len );  
470
471         LDAP_FREE( recv_tok );
472
473         return recv_tok_len;
474 }
475
476 static ber_slen_t sasl_write( Sockbuf *sb, void *buf, ber_len_t len )
477 {
478         char *wrapped_tok;
479         unsigned wrapped_tok_len;
480         sasl_conn_t *conn = (sasl_conn_t *)sb->sb_iodata;
481
482         if ( sasl_encode( conn, (const char *)buf, len,
483                 &wrapped_tok, &wrapped_tok_len ) != SASL_OK ) {
484                 return -1;
485         }
486
487         if ((ber_pvt_sb_io_tcp.sbi_write)( sb, wrapped_tok, wrapped_tok_len ) != wrapped_tok_len ) {
488                 LDAP_FREE( wrapped_tok );
489                 return -1;
490         }
491
492         LDAP_FREE( wrapped_tok );
493
494         return len;
495 }
496
497 static int sasl_close( Sockbuf *sb )
498 {
499         return (ber_pvt_sb_io_tcp.sbi_close)( sb );
500 }
501
502 static int
503 sasl_err2ldap( int saslerr )
504 {
505         int rc;
506
507         switch (saslerr) {
508                 case SASL_CONTINUE:
509                         rc = LDAP_MORE_RESULTS_TO_RETURN;
510                         break;
511                 case SASL_OK:
512                         rc = LDAP_SUCCESS;
513                         break;
514                 case SASL_FAIL:
515                         rc = LDAP_LOCAL_ERROR;
516                         break;
517                 case SASL_NOMEM:
518                         rc = LDAP_NO_MEMORY;
519                         break;
520                 case SASL_NOMECH:
521                         rc = LDAP_AUTH_UNKNOWN;
522                         break;
523                 case SASL_BADAUTH:
524                         rc = LDAP_AUTH_UNKNOWN;
525                         break;
526                 case SASL_NOAUTHZ:
527                         rc = LDAP_PARAM_ERROR;
528                         break;
529                 case SASL_TOOWEAK:
530                 case SASL_ENCRYPT:
531                         rc = LDAP_AUTH_UNKNOWN;
532                         break;
533                 default:
534                         rc = LDAP_LOCAL_ERROR;
535                         break;
536         }
537
538         assert( rc == LDAP_SUCCESS || LDAP_API_ERROR( rc ) );
539         return rc;
540 }
541
542 int
543 ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist )
544 {
545         /* we need to query the server for supported mechs anyway */
546         LDAPMessage *res, *e;
547         char *attrs[] = { "supportedSASLMechanisms", NULL };
548         char **values, *mechlist;
549         int rc;
550
551         Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_getmech\n", 0, 0, 0 );
552
553         rc = ldap_search_s( ld, NULL, LDAP_SCOPE_BASE,
554                 NULL, attrs, 0, &res );
555
556         if ( rc != LDAP_SUCCESS ) {
557                 return ld->ld_errno;
558         }
559                 
560         e = ldap_first_entry( ld, res );
561         if ( e == NULL ) {
562                 if ( ld->ld_errno == LDAP_SUCCESS ) {
563                         ld->ld_errno = LDAP_UNAVAILABLE;
564                 }
565                 return ld->ld_errno;
566         }
567
568         values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
569         if ( values == NULL ) {
570                 ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
571                 ldap_msgfree( res );
572                 return ld->ld_errno;
573         }
574
575         mechlist = array2str( values );
576         if ( mechlist == NULL ) {
577                 ld->ld_errno = LDAP_NO_MEMORY;
578                 ldap_value_free( values );
579                 ldap_msgfree( res );
580                 return ld->ld_errno;
581         } 
582
583         ldap_value_free( values );
584         ldap_msgfree( res );
585
586         *pmechlist = mechlist;
587
588         return LDAP_SUCCESS;
589 }
590
591 int
592 ldap_pvt_sasl_bind(
593         LDAP                    *ld,
594         LDAP_CONST char         *dn,
595         LDAP_CONST char         *mechs,
596         LDAP_CONST sasl_callback_t      *callbacks,
597         LDAPControl             **sctrls,
598         LDAPControl             **cctrls )
599 {
600         const char *mech;
601         int     saslrc, rc, ssf = 0;
602         unsigned credlen;
603         struct berval ccred, *scred;
604         char *host;
605         sasl_interact_t *client_interact = NULL;
606
607         Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_bind\n", 0, 0, 0 );
608
609         /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
610         if (ld->ld_version < LDAP_VERSION3) {
611                 ld->ld_errno = LDAP_NOT_SUPPORTED;
612                 return ld->ld_errno;
613         }
614
615         if( ! ber_pvt_sb_in_use( &ld->ld_sb ) ) {
616                 /* not connected yet */
617                 int rc = ldap_open_defconn( ld );
618   
619                 if( rc < 0 ) return ld->ld_errno;
620         }   
621
622         /* XXX this doesn't work with PF_LOCAL hosts */
623         host = ldap_host_connected_to( &ld->ld_sb );
624
625         if ( host == NULL ) {
626                 ld->ld_errno = LDAP_UNAVAILABLE;
627                 return ld->ld_errno;
628         }
629
630         if ( ld->ld_sasl_context != NULL ) {
631                 sasl_dispose( &ld->ld_sasl_context );
632         }
633
634         saslrc = sasl_client_new( "ldap", host, callbacks, 0, &ld->ld_sasl_context );
635
636         LDAP_FREE( host );
637
638         if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
639                 ld->ld_errno = sasl_err2ldap( rc );
640                 sasl_dispose( &ld->ld_sasl_context );
641                 return ld->ld_errno;
642         }
643
644         ccred.bv_val = NULL;
645         ccred.bv_len = 0;
646
647         saslrc = sasl_client_start( ld->ld_sasl_context,
648                 mechs,
649                 NULL,
650                 &client_interact,
651                 &ccred.bv_val,
652                 &credlen,
653                 &mech );
654
655         ccred.bv_len = credlen;
656
657         if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
658                 ld->ld_errno = sasl_err2ldap( saslrc );
659                 sasl_dispose( &ld->ld_sasl_context );
660                 return ld->ld_errno;
661         }
662
663         scred = NULL;
664
665         do {
666                 unsigned credlen;
667                 sasl_interact_t *client_interact = NULL;
668
669                 rc = ldap_sasl_bind_s( ld, dn, mech, &ccred, sctrls, cctrls, &scred );
670                 if ( rc == LDAP_SUCCESS ) {
671                         break;
672                 } else if ( rc != LDAP_SASL_BIND_IN_PROGRESS ) {
673                         if ( ccred.bv_val != NULL ) {
674                                 LDAP_FREE( ccred.bv_val );
675                         }
676                         sasl_dispose( &ld->ld_sasl_context );
677                         return ld->ld_errno;
678                 }
679
680                 if ( ccred.bv_val != NULL ) {
681                         LDAP_FREE( ccred.bv_val );
682                         ccred.bv_val = NULL;
683                 }
684
685                 saslrc = sasl_client_step( ld->ld_sasl_context,
686                         (scred == NULL) ? NULL : scred->bv_val,
687                         (scred == NULL) ? 0 : scred->bv_len,
688                         &client_interact,
689                         &ccred.bv_val,
690                         &credlen );
691
692                 ccred.bv_len = credlen;
693                 ber_bvfree( scred );
694
695                 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
696                         ld->ld_errno = sasl_err2ldap( saslrc );
697                         sasl_dispose( &ld->ld_sasl_context );
698                         return ld->ld_errno;
699                 }
700         } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
701
702         assert ( rc == LDAP_SUCCESS );
703
704         if ( sasl_getprop( ld->ld_sasl_context, SASL_SSF, (void **)&ssf )
705                 == SASL_OK && ssf ) {
706                 ldap_pvt_sasl_install( &ld->ld_sb, ld->ld_sasl_context );
707         }
708
709         return rc;
710 }
711
712 /* based on sample/sample-client.c */
713 static int
714 ldap_pvt_sasl_getsecret(sasl_conn_t *conn,
715         void *context, int id, sasl_secret_t **psecret)
716 {
717         struct berval *passphrase = (struct berval *)context;
718         size_t len;           
719
720         if ( conn == NULL || psecret == NULL || id != SASL_CB_PASS ) {
721                 return SASL_BADPARAM;
722         }
723
724         len = (passphrase != NULL) ? (size_t)passphrase->bv_len: 0;
725
726         *psecret = (sasl_secret_t *) LDAP_MALLOC( sizeof( sasl_secret_t ) + len );
727         if ( *psecret == NULL ) {
728                 return SASL_NOMEM;
729         }
730
731         (*psecret)->len = passphrase->bv_len;
732
733         if ( passphrase != NULL ) {
734                 memcpy((*psecret)->data, passphrase->bv_val, len);
735         }
736
737         return SASL_OK;
738 }
739
740 static int
741 ldap_pvt_sasl_getsimple(void *context, int id, const char **result, int *len)
742 {
743         const char *value = (const char *)context;
744
745         if ( result == NULL ) {
746                 return SASL_BADPARAM;
747         }
748
749         switch ( id ) {
750                 case SASL_CB_USER:
751                 case SASL_CB_AUTHNAME:
752                         *result = value;
753                         if ( len )
754                                 *len = value ? strlen( value ) : 0;
755                         break;
756                 case SASL_CB_LANGUAGE:
757                         *result = NULL;
758                         if ( len )
759                                 *len = 0;
760                         break;
761                 default:
762                         return SASL_BADPARAM;
763         }
764
765         return SASL_OK;
766 }
767
768 int
769 ldap_pvt_sasl_get_option( LDAP *ld, int option, void *arg )
770 {
771         sasl_ssf_t      *ssf;
772         
773         if ( ld == NULL )
774                 return -1;
775
776         switch ( option ) {
777                 case LDAP_OPT_X_SASL_MINSSF:
778                         *(int *)arg = ld->ld_options.ldo_sasl_minssf;
779                         break;
780                 case LDAP_OPT_X_SASL_MAXSSF:
781                         *(int *)arg = ld->ld_options.ldo_sasl_maxssf;
782                         break;
783                 case LDAP_OPT_X_SASL_ACTSSF:
784                         if ( ld->ld_sasl_context == NULL ) {
785                                 *(int *)arg = -1;
786                                 break;
787                         }
788                         if ( sasl_getprop( ld->ld_sasl_context, SASL_SSF,
789                                 (void **) &ssf ) != SASL_OK )
790                         {
791                                 return -1;
792                         }
793                         *(int *)arg = *ssf;
794                         break;
795                 default:
796                         return -1;
797         }
798         return 0;
799 }
800
801 int
802 ldap_pvt_sasl_set_option( LDAP *ld, int option, void *arg )
803 {
804         if ( ld == NULL )
805                 return -1;
806
807         switch ( option ) {
808                 case LDAP_OPT_X_SASL_MINSSF:
809                         ld->ld_options.ldo_sasl_minssf = *(int *)arg;
810                         break;
811                 case LDAP_OPT_X_SASL_MAXSSF:
812                         ld->ld_options.ldo_sasl_maxssf = *(int *)arg;
813                         break;
814                 case LDAP_OPT_X_SASL_ACTSSF:
815                         /* This option is read-only */
816                 default:
817                         return -1;
818         }
819         return 0;
820 }
821
822 /*
823  * ldap_negotiated_sasl_bind_s - bind to the ldap server (and X.500)
824  * using SASL authentication.
825  *
826  * This routine attempts to authenticate the user referred by the
827  * authentication id using the provided password.  An optional
828  * authorization identity may be provided.  An DN is generally not
829  * provided [see AuthMethod].
830  *
831  * If the mechanism negotiated does not require a password, the
832  * passwd field is ignored.  [A callback mechanism should really
833  * be used].
834  * 
835  * LDAP_SUCCESS is returned upon success, the ldap error code
836  * otherwise.
837  *
838  * Examples:
839  *      ldap_negotiated_sasl_bind_s( ld, NULL,
840  *          NULL, NULL, NULL,
841  *              NULL, NULL, NULL, NULL );
842  *
843  *      ldap_negotiated_sasl_bind_s( ld, NULL,
844  *          "user@OPENLDAP.ORG", NULL, NULL,
845  *              "GSSAPI", NULL, NULL, NULL );
846  *
847  *      ldap_negotiated_sasl_bind_s( ld, NULL,
848  *          "manager", "dn:cn=user,dc=openldap,dc=org", NULL,
849  *              "DIGEST-MD5", NULL, NULL, NULL );
850  *
851  *      ldap_negotiated_sasl_bind_s( ld, NULL,
852  *          "root@OPENLDAP.ORG", "u:user@OPENLDAP.ORG", NULL,
853  *              "GSSAPI", NULL, NULL, NULL );
854  *
855  *      ldap_negotiated_sasl_bind_s( ld, NULL,
856  *          "manager", "dn:cn=user,dc=openldap,dc=org", NULL,
857  *              "DIGEST-MD5", NULL, NULL, NULL );
858  */
859 int
860 ldap_negotiated_sasl_bind_s(
861         LDAP *ld,
862         LDAP_CONST char *dn, /* usually NULL */
863         LDAP_CONST char *authenticationId,
864         LDAP_CONST char *authorizationId, /* commonly NULL */
865         LDAP_CONST char *saslMechanism,
866         struct berval *passPhrase,
867         LDAPControl **serverControls,
868         LDAPControl **clientControls)
869 {
870         int n;
871         sasl_callback_t callbacks[4];
872         int rc;
873
874         Debug( LDAP_DEBUG_TRACE, "ldap_negotiated_sasl_bind_s\n", 0, 0, 0 );
875
876         if( saslMechanism == NULL || *saslMechanism == '\0' ) {
877                 char *mechs;
878                 rc = ldap_pvt_sasl_getmechs( ld, &mechs );
879
880                 if( rc != LDAP_SUCCESS ) {
881                         return rc;
882                 }
883
884                 saslMechanism = mechs;
885         }
886
887         /* SASL Authentication Identity */
888         callbacks[n=0].id = SASL_CB_AUTHNAME;
889         callbacks[n].proc = ldap_pvt_sasl_getsimple;
890         callbacks[n].context = (void *)authenticationId;
891
892         /* SASL Authorization Identity (userid) */
893         if( authorizationId != NULL ) {
894                 callbacks[++n].id = SASL_CB_USER;
895                 callbacks[n].proc = ldap_pvt_sasl_getsimple;
896                 callbacks[n].context = (void *)authorizationId;
897         }
898
899         callbacks[++n].id = SASL_CB_PASS;
900         callbacks[n].proc = ldap_pvt_sasl_getsecret;
901         callbacks[n].context = (void *)passPhrase;
902
903         callbacks[++n].id = SASL_CB_LIST_END;
904         callbacks[n].proc = NULL;
905         callbacks[n].context = NULL;
906
907         assert( n * sizeof(sasl_callback_t) < sizeof(callbacks) );
908
909         rc = ldap_pvt_sasl_bind(ld, dn, saslMechanism, callbacks,
910                 serverControls, clientControls);
911
912         return rc;
913 }
914 #endif /* HAVE_CYRUS_SASL */