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