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