]> git.sur5r.net Git - openldap/blob - libraries/libldap/sasl.c
ITS#2484, set sasl_maxbuf to SASL_MAX_BUFF_SIZE if it was negotiated
[openldap] / libraries / libldap / sasl.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*
7  * Portions Copyright (C) The Internet Society (1997)
8  * ASN.1 fragments are from RFC 2251; see RFC for full legal notices.
9  */
10
11 /*
12  *      BindRequest ::= SEQUENCE {
13  *              version         INTEGER,
14  *              name            DistinguishedName,       -- who
15  *              authentication  CHOICE {
16  *                      simple          [0] OCTET STRING -- passwd
17 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND
18  *                      krbv42ldap      [1] OCTET STRING
19  *                      krbv42dsa       [2] OCTET STRING
20 #endif
21  *                      sasl            [3] SaslCredentials     -- LDAPv3
22  *              }
23  *      }
24  *
25  *      BindResponse ::= SEQUENCE {
26  *              COMPONENTS OF LDAPResult,
27  *              serverSaslCreds         OCTET STRING OPTIONAL -- LDAPv3
28  *      }
29  *
30  */
31
32 #include "portable.h"
33
34 #include <stdio.h>
35
36 #include <ac/socket.h>
37 #include <ac/stdlib.h>
38 #include <ac/string.h>
39 #include <ac/time.h>
40 #include <ac/errno.h>
41
42 #include "ldap-int.h"
43
44 /*
45  * ldap_sasl_bind - bind to the ldap server (and X.500).
46  * The dn (usually NULL), mechanism, and credentials are provided.
47  * The message id of the request initiated is provided upon successful
48  * (LDAP_SUCCESS) return.
49  *
50  * Example:
51  *      ldap_sasl_bind( ld, NULL, "mechanism",
52  *              cred, NULL, NULL, &msgid )
53  */
54
55 int
56 ldap_sasl_bind(
57         LDAP                    *ld,
58         LDAP_CONST char *dn,
59         LDAP_CONST char *mechanism,
60         struct berval   *cred,
61         LDAPControl             **sctrls,
62         LDAPControl             **cctrls,
63         int                             *msgidp )
64 {
65         BerElement      *ber;
66         int rc;
67         ber_int_t id;
68
69 #ifdef NEW_LOGGING
70         LDAP_LOG ( TRANSPORT, ENTRY, "ldap_sasl_bind\n", 0, 0, 0 );
71 #else
72         Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n", 0, 0, 0 );
73 #endif
74
75         assert( ld != NULL );
76         assert( LDAP_VALID( ld ) );
77         assert( msgidp != NULL );
78
79         /* check client controls */
80         rc = ldap_int_client_controls( ld, cctrls );
81         if( rc != LDAP_SUCCESS ) return rc;
82
83         if( mechanism == LDAP_SASL_SIMPLE ) {
84                 if( dn == NULL && cred != NULL && cred->bv_len ) {
85                         /* use default binddn */
86                         dn = ld->ld_defbinddn;
87                 }
88
89         } else if( ld->ld_version < LDAP_VERSION3 ) {
90                 ld->ld_errno = LDAP_NOT_SUPPORTED;
91                 return ld->ld_errno;
92         }
93
94         if ( dn == NULL ) {
95                 dn = "";
96         }
97
98         /* create a message to send */
99         if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
100                 ld->ld_errno = LDAP_NO_MEMORY;
101                 return ld->ld_errno;
102         }
103
104         assert( LBER_VALID( ber ) );
105
106         LDAP_NEXT_MSGID( ld, id );
107         if( mechanism == LDAP_SASL_SIMPLE ) {
108                 /* simple bind */
109                 rc = ber_printf( ber, "{it{istON}" /*}*/,
110                         id, LDAP_REQ_BIND,
111                         ld->ld_version, dn, LDAP_AUTH_SIMPLE,
112                         cred );
113                 
114         } else if ( cred == NULL || cred->bv_val == NULL ) {
115                 /* SASL bind w/o creditials */
116                 rc = ber_printf( ber, "{it{ist{sN}N}" /*}*/,
117                         id, LDAP_REQ_BIND,
118                         ld->ld_version, dn, LDAP_AUTH_SASL,
119                         mechanism );
120
121         } else {
122                 /* SASL bind w/ creditials */
123                 rc = ber_printf( ber, "{it{ist{sON}N}" /*}*/,
124                         id, LDAP_REQ_BIND,
125                         ld->ld_version, dn, LDAP_AUTH_SASL,
126                         mechanism, cred );
127         }
128
129         if( rc == -1 ) {
130                 ld->ld_errno = LDAP_ENCODING_ERROR;
131                 ber_free( ber, 1 );
132                 return( -1 );
133         }
134
135         /* Put Server Controls */
136         if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
137                 ber_free( ber, 1 );
138                 return ld->ld_errno;
139         }
140
141         if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
142                 ld->ld_errno = LDAP_ENCODING_ERROR;
143                 ber_free( ber, 1 );
144                 return ld->ld_errno;
145         }
146
147
148         /* send the message */
149         *msgidp = ldap_send_initial_request( ld, LDAP_REQ_BIND, dn, ber, id );
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 #ifdef NEW_LOGGING
173         LDAP_LOG ( TRANSPORT, ENTRY, "ldap_sasl_bind_s\n", 0, 0, 0 );
174 #else
175         Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind_s\n", 0, 0, 0 );
176 #endif
177
178         /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
179         if( servercredp != NULL ) {
180                 if (ld->ld_version < LDAP_VERSION3) {
181                         ld->ld_errno = LDAP_NOT_SUPPORTED;
182                         return ld->ld_errno;
183                 }
184                 *servercredp = NULL;
185         }
186
187         rc = ldap_sasl_bind( ld, dn, mechanism, cred, sctrls, cctrls, &msgid );
188
189         if ( rc != LDAP_SUCCESS ) {
190                 return( rc );
191         }
192
193 #ifdef LDAP_CONNECTIONLESS
194         if (LDAP_IS_UDP(ld)) {
195                 return( rc );
196         }
197 #endif
198
199         if ( ldap_result( ld, msgid, 1, NULL, &result ) == -1 ) {
200                 return( ld->ld_errno ); /* ldap_result sets ld_errno */
201         }
202
203         /* parse the results */
204         scredp = NULL;
205         if( servercredp != NULL ) {
206                 rc = ldap_parse_sasl_bind_result( ld, result, &scredp, 0 );
207         }
208
209         if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
210                 ldap_msgfree( result );
211                 return( rc );
212         }
213
214         rc = ldap_result2error( ld, result, 1 );
215
216         if ( rc == LDAP_SUCCESS || rc == LDAP_SASL_BIND_IN_PROGRESS ) {
217                 if( servercredp != NULL ) {
218                         *servercredp = scredp;
219                         scredp = NULL;
220                 }
221         }
222
223         if ( scredp != NULL ) {
224                 ber_bvfree(scredp);
225         }
226
227         return rc;
228 }
229
230
231 /*
232 * Parse BindResponse:
233 *
234 *   BindResponse ::= [APPLICATION 1] SEQUENCE {
235 *     COMPONENTS OF LDAPResult,
236 *     serverSaslCreds  [7] OCTET STRING OPTIONAL }
237 *
238 *   LDAPResult ::= SEQUENCE {
239 *     resultCode      ENUMERATED,
240 *     matchedDN       LDAPDN,
241 *     errorMessage    LDAPString,
242 *     referral        [3] Referral OPTIONAL }
243 */
244
245 int
246 ldap_parse_sasl_bind_result(
247         LDAP                    *ld,
248         LDAPMessage             *res,
249         struct berval   **servercredp,
250         int                             freeit )
251 {
252         ber_int_t errcode;
253         struct berval* scred;
254
255         ber_tag_t tag;
256         BerElement      *ber;
257
258 #ifdef NEW_LOGGING
259         LDAP_LOG ( TRANSPORT, ENTRY, "ldap_parse_sasl_bind_result\n", 0, 0, 0 );
260 #else
261         Debug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 );
262 #endif
263
264         assert( ld != NULL );
265         assert( LDAP_VALID( ld ) );
266         assert( res != NULL );
267
268         if( servercredp != NULL ) {
269                 if( ld->ld_version < LDAP_VERSION2 ) {
270                         return LDAP_NOT_SUPPORTED;
271                 }
272                 *servercredp = NULL;
273         }
274
275         if( res->lm_msgtype != LDAP_RES_BIND ) {
276                 ld->ld_errno = LDAP_PARAM_ERROR;
277                 return ld->ld_errno;
278         }
279
280         scred = NULL;
281
282         if ( ld->ld_error ) {
283                 LDAP_FREE( ld->ld_error );
284                 ld->ld_error = NULL;
285         }
286         if ( ld->ld_matched ) {
287                 LDAP_FREE( ld->ld_matched );
288                 ld->ld_matched = NULL;
289         }
290
291         /* parse results */
292
293         ber = ber_dup( res->lm_ber );
294
295         if( ber == NULL ) {
296                 ld->ld_errno = LDAP_NO_MEMORY;
297                 return ld->ld_errno;
298         }
299
300         if ( ld->ld_version < LDAP_VERSION2 ) {
301                 tag = ber_scanf( ber, "{ia}",
302                         &errcode, &ld->ld_error );
303
304                 if( tag == LBER_ERROR ) {
305                         ber_free( ber, 0 );
306                         ld->ld_errno = LDAP_DECODING_ERROR;
307                         return ld->ld_errno;
308                 }
309
310         } else {
311                 ber_len_t len;
312
313                 tag = ber_scanf( ber, "{iaa" /*}*/,
314                         &errcode, &ld->ld_matched, &ld->ld_error );
315
316                 if( tag == 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                 if( tag == LDAP_TAG_REFERRAL ) {
325                         /* skip 'em */
326                         if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
327                                 ber_free( ber, 0 );
328                                 ld->ld_errno = LDAP_DECODING_ERROR;
329                                 return ld->ld_errno;
330                         }
331
332                         tag = ber_peek_tag(ber, &len);
333                 }
334
335                 if( tag == LDAP_TAG_SASL_RES_CREDS ) {
336                         if( ber_scanf( ber, "O", &scred ) == LBER_ERROR ) {
337                                 ber_free( ber, 0 );
338                                 ld->ld_errno = LDAP_DECODING_ERROR;
339                                 return ld->ld_errno;
340                         }
341                 }
342         }
343
344         ber_free( ber, 0 );
345
346         if ( servercredp != NULL ) {
347                 *servercredp = scred;
348
349         } else if ( scred != NULL ) {
350                 ber_bvfree( scred );
351         }
352
353         ld->ld_errno = errcode;
354
355         if ( freeit ) {
356                 ldap_msgfree( res );
357         }
358
359         return( ld->ld_errno );
360 }
361
362 int
363 ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist )
364 {
365         /* we need to query the server for supported mechs anyway */
366         LDAPMessage *res, *e;
367         char *attrs[] = { "supportedSASLMechanisms", NULL };
368         char **values, *mechlist;
369         int rc;
370
371 #ifdef NEW_LOGGING
372         LDAP_LOG ( TRANSPORT, ENTRY, "ldap_pvt_sasl_getmech\n", 0, 0, 0 );
373 #else
374         Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_getmech\n", 0, 0, 0 );
375 #endif
376
377         rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE,
378                 NULL, attrs, 0, &res );
379
380         if ( rc != LDAP_SUCCESS ) {
381                 return ld->ld_errno;
382         }
383                 
384         e = ldap_first_entry( ld, res );
385         if ( e == NULL ) {
386                 ldap_msgfree( res );
387                 if ( ld->ld_errno == LDAP_SUCCESS ) {
388                         ld->ld_errno = LDAP_NO_SUCH_OBJECT;
389                 }
390                 return ld->ld_errno;
391         }
392
393         values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
394         if ( values == NULL ) {
395                 ldap_msgfree( res );
396                 ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
397                 return ld->ld_errno;
398         }
399
400         mechlist = ldap_charray2str( values, " " );
401         if ( mechlist == NULL ) {
402                 LDAP_VFREE( values );
403                 ldap_msgfree( res );
404                 ld->ld_errno = LDAP_NO_MEMORY;
405                 return ld->ld_errno;
406         } 
407
408         LDAP_VFREE( values );
409         ldap_msgfree( res );
410
411         *pmechlist = mechlist;
412
413         return LDAP_SUCCESS;
414 }
415
416 /*
417  * ldap_sasl_interactive_bind_s - interactive SASL authentication
418  *
419  * This routine uses interactive callbacks.
420  *
421  * LDAP_SUCCESS is returned upon success, the ldap error code
422  * otherwise.
423  */
424 int
425 ldap_sasl_interactive_bind_s(
426         LDAP *ld,
427         LDAP_CONST char *dn, /* usually NULL */
428         LDAP_CONST char *mechs,
429         LDAPControl **serverControls,
430         LDAPControl **clientControls,
431         unsigned flags,
432         LDAP_SASL_INTERACT_PROC *interact,
433         void *defaults )
434 {
435         int rc;
436
437 #if defined( LDAP_R_COMPILE ) && defined( HAVE_CYRUS_SASL )
438         ldap_pvt_thread_mutex_lock( &ldap_int_sasl_mutex );
439 #endif
440 #ifdef LDAP_CONNECTIONLESS
441         if( LDAP_IS_UDP(ld) ) {
442                 /* Just force it to simple bind, silly to make the user
443                  * ask all the time. No, we don't ever actually bind, but I'll
444                  * let the final bind handler take care of saving the cdn.
445                  */
446                 rc = ldap_simple_bind( ld, dn, NULL );
447                 rc = rc < 0 ? rc : 0;
448                 goto done;
449         } else
450 #endif
451         if( mechs == NULL || *mechs == '\0' ) {
452                 char *smechs;
453
454                 rc = ldap_pvt_sasl_getmechs( ld, &smechs );
455                 if( rc != LDAP_SUCCESS ) {
456                         goto done;
457                 }
458
459 #ifdef NEW_LOGGING
460                 LDAP_LOG ( TRANSPORT, DETAIL1, 
461                         "ldap_sasl_interactive_bind_s: server supports: %s\n", 
462                         smechs, 0, 0 );
463 #else
464                 Debug( LDAP_DEBUG_TRACE,
465                         "ldap_sasl_interactive_bind_s: server supports: %s\n",
466                         smechs, 0, 0 );
467 #endif
468
469                 mechs = smechs;
470
471         } else {
472 #ifdef NEW_LOGGING
473                 LDAP_LOG ( TRANSPORT, DETAIL1, 
474                         "ldap_sasl_interactive_bind_s: user selected: %s\n",
475                         mechs, 0, 0 );
476 #else
477                 Debug( LDAP_DEBUG_TRACE,
478                         "ldap_sasl_interactive_bind_s: user selected: %s\n",
479                         mechs, 0, 0 );
480 #endif
481         }
482
483         rc = ldap_int_sasl_bind( ld, dn, mechs,
484                 serverControls, clientControls,
485                 flags, interact, defaults );
486
487 done:
488 #if defined( LDAP_R_COMPILE ) && defined( HAVE_CYRUS_SASL )
489         ldap_pvt_thread_mutex_unlock( &ldap_int_sasl_mutex );
490 #endif
491
492         return rc;
493 }