]> git.sur5r.net Git - openldap/blob - libraries/libldap/sasl.c
Pull in a variety of bug fixes from HEAD including
[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 <stdlib.h>
31 #include <stdio.h>
32
33 #include <ac/socket.h>
34 #include <ac/string.h>
35 #include <ac/time.h>
36 #include <ac/errno.h>
37
38 #include "ldap-int.h"
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         /* check client controls */
71         rc = ldap_int_client_controls( ld, cctrls );
72         if( rc != LDAP_SUCCESS ) return rc;
73
74         if( msgidp == NULL ) {
75                 ld->ld_errno = LDAP_PARAM_ERROR;
76                 return ld->ld_errno;
77         }
78
79         if( mechanism == LDAP_SASL_SIMPLE ) {
80                 if( dn == NULL && cred != NULL ) {
81                         /* use default binddn */
82                         dn = ld->ld_defbinddn;
83                 }
84
85         } else if( ld->ld_version < LDAP_VERSION3 ) {
86                 ld->ld_errno = LDAP_NOT_SUPPORTED;
87                 return ld->ld_errno;
88         }
89
90         if ( dn == NULL ) {
91                 dn = "";
92         }
93
94         /* create a message to send */
95         if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
96                 ld->ld_errno = LDAP_NO_MEMORY;
97                 return ld->ld_errno;
98         }
99
100         assert( BER_VALID( ber ) );
101
102         if( mechanism == LDAP_SASL_SIMPLE ) {
103                 /* simple bind */
104                 rc = ber_printf( ber, "{it{istON}" /*}*/,
105                         ++ld->ld_msgid, LDAP_REQ_BIND,
106                         ld->ld_version, dn, LDAP_AUTH_SIMPLE,
107                         cred );
108                 
109         } else if ( cred == NULL || !cred->bv_len ) {
110                 /* SASL bind w/o creditials */
111                 rc = ber_printf( ber, "{it{ist{sN}N}" /*}*/,
112                         ++ld->ld_msgid, LDAP_REQ_BIND,
113                         ld->ld_version, dn, LDAP_AUTH_SASL,
114                         mechanism );
115
116         } else {
117                 /* SASL bind w/ creditials */
118                 rc = ber_printf( ber, "{it{ist{sON}N}" /*}*/,
119                         ++ld->ld_msgid, LDAP_REQ_BIND,
120                         ld->ld_version, dn, LDAP_AUTH_SASL,
121                         mechanism, cred );
122         }
123
124         if( rc == -1 ) {
125                 ld->ld_errno = LDAP_ENCODING_ERROR;
126                 ber_free( ber, 1 );
127                 return( -1 );
128         }
129
130         /* Put Server Controls */
131         if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
132                 ber_free( ber, 1 );
133                 return ld->ld_errno;
134         }
135
136         if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
137                 ld->ld_errno = LDAP_ENCODING_ERROR;
138                 ber_free( ber, 1 );
139                 return ld->ld_errno;
140         }
141
142 #ifndef LDAP_NOCACHE
143         if ( ld->ld_cache != NULL ) {
144                 ldap_flush_cache( ld );
145         }
146 #endif /* !LDAP_NOCACHE */
147
148         /* send the message */
149         *msgidp = ldap_send_initial_request( ld, LDAP_REQ_BIND, dn, ber );
150
151         if(*msgidp < 0)
152                 return ld->ld_errno;
153
154         return LDAP_SUCCESS;
155 }
156
157
158 int
159 ldap_sasl_bind_s(
160         LDAP                    *ld,
161         LDAP_CONST char *dn,
162         LDAP_CONST char *mechanism,
163         struct berval   *cred,
164         LDAPControl             **sctrls,
165         LDAPControl             **cctrls,
166         struct berval   **servercredp )
167 {
168         int     rc, msgid;
169         LDAPMessage     *result;
170         struct berval   *scredp = NULL;
171
172         Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind_s\n", 0, 0, 0 );
173
174         /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
175         if( servercredp != NULL ) {
176                 if (ld->ld_version < LDAP_VERSION3) {
177                         ld->ld_errno = LDAP_NOT_SUPPORTED;
178                         return ld->ld_errno;
179                 }
180                 *servercredp = NULL;
181         }
182
183         rc = ldap_sasl_bind( ld, dn, mechanism, cred, sctrls, cctrls, &msgid );
184
185         if ( rc != LDAP_SUCCESS ) {
186                 return( rc );
187         }
188
189         if ( ldap_result( ld, msgid, 1, NULL, &result ) == -1 ) {
190                 return( ld->ld_errno ); /* ldap_result sets ld_errno */
191         }
192
193         /* parse the results */
194         scredp = NULL;
195         if( servercredp != NULL ) {
196                 rc = ldap_parse_sasl_bind_result( ld, result, &scredp, 0 );
197         }
198
199         if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
200                 ldap_msgfree( result );
201                 return( rc );
202         }
203
204         rc = ldap_result2error( ld, result, 1 );
205
206         if ( rc == LDAP_SUCCESS || rc == LDAP_SASL_BIND_IN_PROGRESS ) {
207                 if( servercredp != NULL ) {
208                         *servercredp = scredp;
209                         scredp = NULL;
210                 }
211         }
212
213         if ( scredp != NULL ) {
214                 ber_bvfree(scredp);
215         }
216
217         return rc;
218 }
219
220
221 /*
222 * Parse BindResponse:
223 *
224 *   BindResponse ::= [APPLICATION 1] SEQUENCE {
225 *     COMPONENTS OF LDAPResult,
226 *     serverSaslCreds  [7] OCTET STRING OPTIONAL }
227 *
228 *   LDAPResult ::= SEQUENCE {
229 *     resultCode      ENUMERATED,
230 *     matchedDN       LDAPDN,
231 *     errorMessage    LDAPString,
232 *     referral        [3] Referral OPTIONAL }
233 */
234
235 int
236 ldap_parse_sasl_bind_result(
237         LDAP                    *ld,
238         LDAPMessage             *res,
239         struct berval   **servercredp,
240         int                             freeit )
241 {
242         ber_int_t errcode;
243         struct berval* scred;
244
245         ber_tag_t tag;
246         BerElement      *ber;
247
248         Debug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 );
249
250         assert( ld != NULL );
251         assert( LDAP_VALID( ld ) );
252         assert( res != NULL );
253
254         if ( ld == NULL || res == NULL ) {
255                 return LDAP_PARAM_ERROR;
256         }
257
258         if( servercredp != NULL ) {
259                 if( ld->ld_version < LDAP_VERSION2 ) {
260                         return LDAP_NOT_SUPPORTED;
261                 }
262                 *servercredp = NULL;
263         }
264
265         if( res->lm_msgtype != LDAP_RES_BIND ) {
266                 ld->ld_errno = LDAP_PARAM_ERROR;
267                 return ld->ld_errno;
268         }
269
270         scred = NULL;
271
272         if ( ld->ld_error ) {
273                 LDAP_FREE( ld->ld_error );
274                 ld->ld_error = NULL;
275         }
276         if ( ld->ld_matched ) {
277                 LDAP_FREE( ld->ld_matched );
278                 ld->ld_matched = NULL;
279         }
280
281         /* parse results */
282
283         ber = ber_dup( res->lm_ber );
284
285         if( ber == NULL ) {
286                 ld->ld_errno = LDAP_NO_MEMORY;
287                 return ld->ld_errno;
288         }
289
290         if ( ld->ld_version < LDAP_VERSION2 ) {
291                 tag = ber_scanf( ber, "{ia}",
292                         &errcode, &ld->ld_error );
293
294                 if( tag == LBER_ERROR ) {
295                         ber_free( ber, 0 );
296                         ld->ld_errno = LDAP_DECODING_ERROR;
297                         return ld->ld_errno;
298                 }
299
300         } else {
301                 ber_len_t len;
302
303                 tag = ber_scanf( ber, "{iaa" /*}*/,
304                         &errcode, &ld->ld_matched, &ld->ld_error );
305
306                 if( tag == LBER_ERROR ) {
307                         ber_free( ber, 0 );
308                         ld->ld_errno = LDAP_DECODING_ERROR;
309                         return ld->ld_errno;
310                 }
311
312                 tag = ber_peek_tag(ber, &len);
313
314                 if( tag == LDAP_TAG_REFERRAL ) {
315                         /* skip 'em */
316                         if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
317                                 ber_free( ber, 0 );
318                                 ld->ld_errno = LDAP_DECODING_ERROR;
319                                 return ld->ld_errno;
320                         }
321
322                         tag = ber_peek_tag(ber, &len);
323                 }
324
325                 if( tag == LDAP_TAG_SASL_RES_CREDS ) {
326                         if( ber_scanf( ber, "O", &scred ) == LBER_ERROR ) {
327                                 ber_free( ber, 0 );
328                                 ld->ld_errno = LDAP_DECODING_ERROR;
329                                 return ld->ld_errno;
330                         }
331                 }
332         }
333
334         ber_free( ber, 0 );
335
336         if ( servercredp != NULL ) {
337                 *servercredp = scred;
338
339         } else if ( scred != NULL ) {
340                 ber_bvfree( scred );
341         }
342
343         ld->ld_errno = errcode;
344
345         if ( freeit ) {
346                 ldap_msgfree( res );
347         }
348
349         return( ld->ld_errno );
350 }
351
352 int
353 ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist )
354 {
355         /* we need to query the server for supported mechs anyway */
356         LDAPMessage *res, *e;
357         char *attrs[] = { "supportedSASLMechanisms", NULL };
358         char **values, *mechlist;
359         int rc;
360
361         Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_getmech\n", 0, 0, 0 );
362
363         rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE,
364                 NULL, attrs, 0, &res );
365
366         if ( rc != LDAP_SUCCESS ) {
367                 return ld->ld_errno;
368         }
369                 
370         e = ldap_first_entry( ld, res );
371         if ( e == NULL ) {
372                 ldap_msgfree( res );
373                 if ( ld->ld_errno == LDAP_SUCCESS ) {
374                         ld->ld_errno = LDAP_NO_SUCH_OBJECT;
375                 }
376                 return ld->ld_errno;
377         }
378
379         values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
380         if ( values == NULL ) {
381                 ldap_msgfree( res );
382                 ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
383                 return ld->ld_errno;
384         }
385
386         mechlist = ldap_charray2str( values, " " );
387         if ( mechlist == NULL ) {
388                 LDAP_VFREE( values );
389                 ldap_msgfree( res );
390                 ld->ld_errno = LDAP_NO_MEMORY;
391                 return ld->ld_errno;
392         } 
393
394         LDAP_VFREE( values );
395         ldap_msgfree( res );
396
397         *pmechlist = mechlist;
398
399         return LDAP_SUCCESS;
400 }
401
402 /*
403  * ldap_sasl_interactive_bind_s - interactive SASL authentication
404  *
405  * This routine uses interactive callbacks.
406  *
407  * LDAP_SUCCESS is returned upon success, the ldap error code
408  * otherwise.
409  */
410 int
411 ldap_sasl_interactive_bind_s(
412         LDAP *ld,
413         LDAP_CONST char *dn, /* usually NULL */
414         LDAP_CONST char *mechs,
415         LDAPControl **serverControls,
416         LDAPControl **clientControls,
417         unsigned flags,
418         LDAP_SASL_INTERACT_PROC *interact,
419         void *defaults )
420 {
421         int rc;
422
423 #if defined( LDAP_R_COMPILE ) && defined( HAVE_CYRUS_SASL )
424         ldap_pvt_thread_mutex_lock( &ldap_int_sasl_mutex );
425 #endif
426
427         if( mechs == NULL || *mechs == '\0' ) {
428                 char *smechs;
429
430                 rc = ldap_pvt_sasl_getmechs( ld, &smechs );
431
432                 if( rc != LDAP_SUCCESS ) {
433                         goto done;
434                 }
435
436                 Debug( LDAP_DEBUG_TRACE,
437                         "ldap_interactive_sasl_bind_s: server supports: %s\n",
438                         smechs, 0, 0 );
439
440                 mechs = smechs;
441
442         } else {
443                 Debug( LDAP_DEBUG_TRACE,
444                         "ldap_interactive_sasl_bind_s: user selected: %s\n",
445                         mechs, 0, 0 );
446         }
447
448         rc = ldap_int_sasl_bind( ld, dn, mechs,
449                 serverControls, clientControls,
450                 flags, interact, defaults );
451
452 done:
453 #if defined( LDAP_R_COMPILE ) && defined( HAVE_CYRUS_SASL )
454         ldap_pvt_thread_mutex_unlock( &ldap_int_sasl_mutex );
455 #endif
456
457         return rc;
458 }