sasl.c 9.81 KB
Newer Older
1
/* $OpenLDAP$ */
2
3
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
4
 * Copyright 1998-2007 The OpenLDAP Foundation.
5
6
7
8
9
10
11
12
13
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
14
15
16
17
18
19
20
21
 */

/*
 *	BindRequest ::= SEQUENCE {
 *		version		INTEGER,
 *		name		DistinguishedName,	 -- who
 *		authentication	CHOICE {
 *			simple		[0] OCTET STRING -- passwd
22
23
 *			krbv42ldap	[1] OCTET STRING -- OBSOLETE
 *			krbv42dsa	[2] OCTET STRING -- OBSOLETE
Kurt Zeilenga's avatar
Kurt Zeilenga committed
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 *			sasl		[3] SaslCredentials	-- LDAPv3
 *		}
 *	}
 *
 *	BindResponse ::= SEQUENCE {
 *		COMPONENTS OF LDAPResult,
 *		serverSaslCreds		OCTET STRING OPTIONAL -- LDAPv3
 *	}
 *
 */

#include "portable.h"

#include <stdio.h>

#include <ac/socket.h>
40
#include <ac/stdlib.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
41
42
#include <ac/string.h>
#include <ac/time.h>
43
#include <ac/errno.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
44
45
46
47

#include "ldap-int.h"

/*
Kurt Zeilenga's avatar
Kurt Zeilenga committed
48
49
50
51
 * ldap_sasl_bind - bind to the ldap server (and X.500).
 * The dn (usually NULL), mechanism, and credentials are provided.
 * The message id of the request initiated is provided upon successful
 * (LDAP_SUCCESS) return.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
52
53
 *
 * Example:
Kurt Zeilenga's avatar
Kurt Zeilenga committed
54
55
 *	ldap_sasl_bind( ld, NULL, "mechanism",
 *		cred, NULL, NULL, &msgid )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
56
57
58
59
60
61
62
63
64
65
66
67
68
69
 */

int
ldap_sasl_bind(
	LDAP			*ld,
	LDAP_CONST char	*dn,
	LDAP_CONST char	*mechanism,
	struct berval	*cred,
	LDAPControl		**sctrls,
	LDAPControl		**cctrls,
	int				*msgidp )
{
	BerElement	*ber;
	int rc;
70
	ber_int_t id;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
71
72
73
74
75
76
77

	Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n", 0, 0, 0 );

	assert( ld != NULL );
	assert( LDAP_VALID( ld ) );
	assert( msgidp != NULL );

78
79
80
81
	/* check client controls */
	rc = ldap_int_client_controls( ld, cctrls );
	if( rc != LDAP_SUCCESS ) return rc;

82
	if( mechanism == LDAP_SASL_SIMPLE ) {
83
		if( dn == NULL && cred != NULL && cred->bv_len ) {
84
85
86
87
88
			/* use default binddn */
			dn = ld->ld_defbinddn;
		}

	} else if( ld->ld_version < LDAP_VERSION3 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
89
90
91
92
		ld->ld_errno = LDAP_NOT_SUPPORTED;
		return ld->ld_errno;
	}

93
	if ( dn == NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
94
		dn = "";
95
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
96
97
98
99
100
101
102

	/* create a message to send */
	if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
		ld->ld_errno = LDAP_NO_MEMORY;
		return ld->ld_errno;
	}

103
	assert( LBER_VALID( ber ) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
104

105
	LDAP_NEXT_MSGID( ld, id );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
106
107
	if( mechanism == LDAP_SASL_SIMPLE ) {
		/* simple bind */
108
		rc = ber_printf( ber, "{it{istON}" /*}*/,
109
			id, LDAP_REQ_BIND,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
110
111
112
			ld->ld_version, dn, LDAP_AUTH_SIMPLE,
			cred );
		
Kurt Zeilenga's avatar
Kurt Zeilenga committed
113
	} else if ( cred == NULL || cred->bv_val == NULL ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
114
		/* SASL bind w/o credentials */
115
		rc = ber_printf( ber, "{it{ist{sN}N}" /*}*/,
116
			id, LDAP_REQ_BIND,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
117
118
119
120
			ld->ld_version, dn, LDAP_AUTH_SASL,
			mechanism );

	} else {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
121
		/* SASL bind w/ credentials */
122
		rc = ber_printf( ber, "{it{ist{sON}N}" /*}*/,
123
			id, LDAP_REQ_BIND,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
			ld->ld_version, dn, LDAP_AUTH_SASL,
			mechanism, cred );
	}

	if( rc == -1 ) {
		ld->ld_errno = LDAP_ENCODING_ERROR;
		ber_free( ber, 1 );
		return( -1 );
	}

	/* Put Server Controls */
	if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
		ber_free( ber, 1 );
		return ld->ld_errno;
	}

140
	if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
141
142
143
144
145
146
147
		ld->ld_errno = LDAP_ENCODING_ERROR;
		ber_free( ber, 1 );
		return ld->ld_errno;
	}


	/* send the message */
148
	*msgidp = ldap_send_initial_request( ld, LDAP_REQ_BIND, dn, ber, id );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

	if(*msgidp < 0)
		return ld->ld_errno;

	return LDAP_SUCCESS;
}


int
ldap_sasl_bind_s(
	LDAP			*ld,
	LDAP_CONST char	*dn,
	LDAP_CONST char	*mechanism,
	struct berval	*cred,
	LDAPControl		**sctrls,
	LDAPControl		**cctrls,
	struct berval	**servercredp )
{
	int	rc, msgid;
	LDAPMessage	*result;
	struct berval	*scredp = NULL;

	Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind_s\n", 0, 0, 0 );

	/* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
	if( servercredp != NULL ) {
		if (ld->ld_version < LDAP_VERSION3) {
			ld->ld_errno = LDAP_NOT_SUPPORTED;
			return ld->ld_errno;
		}
		*servercredp = NULL;
	}

	rc = ldap_sasl_bind( ld, dn, mechanism, cred, sctrls, cctrls, &msgid );

	if ( rc != LDAP_SUCCESS ) {
		return( rc );
	}

188
189
190
191
192
193
#ifdef LDAP_CONNECTIONLESS
	if (LDAP_IS_UDP(ld)) {
		return( rc );
	}
#endif

194
	if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
195
196
197
198
199
200
201
202
203
		return( ld->ld_errno );	/* ldap_result sets ld_errno */
	}

	/* parse the results */
	scredp = NULL;
	if( servercredp != NULL ) {
		rc = ldap_parse_sasl_bind_result( ld, result, &scredp, 0 );
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
204
	if ( rc != LDAP_SUCCESS ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
205
206
207
208
209
210
		ldap_msgfree( result );
		return( rc );
	}

	rc = ldap_result2error( ld, result, 1 );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
211
	if ( rc == LDAP_SUCCESS || rc == LDAP_SASL_BIND_IN_PROGRESS ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
212
213
		if( servercredp != NULL ) {
			*servercredp = scredp;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
214
			scredp = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
215
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
216
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
217

Kurt Zeilenga's avatar
Kurt Zeilenga committed
218
	if ( scredp != NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
219
220
221
222
223
224
225
226
		ber_bvfree(scredp);
	}

	return rc;
}


/*
227
228
229
230
231
232
233
234
235
236
237
238
* Parse BindResponse:
*
*   BindResponse ::= [APPLICATION 1] SEQUENCE {
*     COMPONENTS OF LDAPResult,
*     serverSaslCreds  [7] OCTET STRING OPTIONAL }
*
*   LDAPResult ::= SEQUENCE {
*     resultCode      ENUMERATED,
*     matchedDN       LDAPDN,
*     errorMessage    LDAPString,
*     referral        [3] Referral OPTIONAL }
*/
Kurt Zeilenga's avatar
Kurt Zeilenga committed
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258

int
ldap_parse_sasl_bind_result(
	LDAP			*ld,
	LDAPMessage		*res,
	struct berval	**servercredp,
	int				freeit )
{
	ber_int_t errcode;
	struct berval* scred;

	ber_tag_t tag;
	BerElement	*ber;

	Debug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 );

	assert( ld != NULL );
	assert( LDAP_VALID( ld ) );
	assert( res != NULL );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
259
	if( servercredp != NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
260
261
262
263
264
265
		if( ld->ld_version < LDAP_VERSION2 ) {
			return LDAP_NOT_SUPPORTED;
		}
		*servercredp = NULL;
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
266
	if( res->lm_msgtype != LDAP_RES_BIND ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
		ld->ld_errno = LDAP_PARAM_ERROR;
		return ld->ld_errno;
	}

	scred = NULL;

	if ( ld->ld_error ) {
		LDAP_FREE( ld->ld_error );
		ld->ld_error = NULL;
	}
	if ( ld->ld_matched ) {
		LDAP_FREE( ld->ld_matched );
		ld->ld_matched = NULL;
	}

	/* parse results */

	ber = ber_dup( res->lm_ber );

286
287
288
289
290
	if( ber == NULL ) {
		ld->ld_errno = LDAP_NO_MEMORY;
		return ld->ld_errno;
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
291
	if ( ld->ld_version < LDAP_VERSION2 ) {
292
293
		tag = ber_scanf( ber, "{iA}",
			&errcode, &ld->ld_error );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
294

295
296
297
298
299
300
		if( tag == LBER_ERROR ) {
			ber_free( ber, 0 );
			ld->ld_errno = LDAP_DECODING_ERROR;
			return ld->ld_errno;
		}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
301
302
303
	} else {
		ber_len_t len;

304
305
		tag = ber_scanf( ber, "{eAA" /*}*/,
			&errcode, &ld->ld_matched, &ld->ld_error );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
306

307
308
309
310
		if( tag == LBER_ERROR ) {
			ber_free( ber, 0 );
			ld->ld_errno = LDAP_DECODING_ERROR;
			return ld->ld_errno;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
311
312
		}

313
314
		tag = ber_peek_tag(ber, &len);

Kurt Zeilenga's avatar
Kurt Zeilenga committed
315
316
		if( tag == LDAP_TAG_REFERRAL ) {
			/* skip 'em */
317
318
319
320
			if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
				ber_free( ber, 0 );
				ld->ld_errno = LDAP_DECODING_ERROR;
				return ld->ld_errno;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
321
			}
322
323

			tag = ber_peek_tag(ber, &len);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
324
325
326
		}

		if( tag == LDAP_TAG_SASL_RES_CREDS ) {
327
328
329
330
331
			if( ber_scanf( ber, "O", &scred ) == LBER_ERROR ) {
				ber_free( ber, 0 );
				ld->ld_errno = LDAP_DECODING_ERROR;
				return ld->ld_errno;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
332
333
334
		}
	}

335
	ber_free( ber, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
336

337
	if ( servercredp != NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
338
339
340
341
342
343
		*servercredp = scred;

	} else if ( scred != NULL ) {
		ber_bvfree( scred );
	}

344
345
	ld->ld_errno = errcode;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
346
347
348
349
	if ( freeit ) {
		ldap_msgfree( res );
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
350
	return( LDAP_SUCCESS );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
351
}
352
353

int
Kurt Zeilenga's avatar
Kurt Zeilenga committed
354
ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist )
355
356
357
358
{
	/* we need to query the server for supported mechs anyway */
	LDAPMessage *res, *e;
	char *attrs[] = { "supportedSASLMechanisms", NULL };
359
	char **values, *mechlist;
360
361
	int rc;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
362
363
	Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_getmech\n", 0, 0, 0 );

364
	rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
365
		NULL, attrs, 0, &res );
366
367
368
369
370
371
372

	if ( rc != LDAP_SUCCESS ) {
		return ld->ld_errno;
	}
		
	e = ldap_first_entry( ld, res );
	if ( e == NULL ) {
373
		ldap_msgfree( res );
374
		if ( ld->ld_errno == LDAP_SUCCESS ) {
375
			ld->ld_errno = LDAP_NO_SUCH_OBJECT;
376
377
378
379
380
381
382
		}
		return ld->ld_errno;
	}

	values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
	if ( values == NULL ) {
		ldap_msgfree( res );
383
		ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
384
385
386
		return ld->ld_errno;
	}

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
393
394
		return ld->ld_errno;
	} 

395
	LDAP_VFREE( values );
396
397
398
399
400
401
402
403
	ldap_msgfree( res );

	*pmechlist = mechlist;

	return LDAP_SUCCESS;
}

/*
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
410
411
 * otherwise.
 */
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
420
	LDAP_SASL_INTERACT_PROC *interact,
	void *defaults )
421
422
{
	int rc;
423
	char *smechs = NULL;
424

Kurt Zeilenga's avatar
Kurt Zeilenga committed
425
426
427
#if defined( LDAP_R_COMPILE ) && defined( HAVE_CYRUS_SASL )
	ldap_pvt_thread_mutex_lock( &ldap_int_sasl_mutex );
#endif
428
429
430
431
432
433
#ifdef LDAP_CONNECTIONLESS
	if( LDAP_IS_UDP(ld) ) {
		/* Just force it to simple bind, silly to make the user
		 * ask all the time. No, we don't ever actually bind, but I'll
		 * let the final bind handler take care of saving the cdn.
		 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
434
435
436
		rc = ldap_simple_bind( ld, dn, NULL );
		rc = rc < 0 ? rc : 0;
		goto done;
437
438
	} else
#endif
439
440

#ifdef HAVE_CYRUS_SASL
441
	if( mechs == NULL || *mechs == '\0' ) {
442
443
		mechs = ld->ld_options.ldo_def_sasl_mech;
	}
444
#endif
445
446
		
	if( mechs == NULL || *mechs == '\0' ) {
447
		rc = ldap_pvt_sasl_getmechs( ld, &smechs );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
448
		if( rc != LDAP_SUCCESS ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
449
			goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
450
451
		}

452
		Debug( LDAP_DEBUG_TRACE,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
453
			"ldap_sasl_interactive_bind_s: server supports: %s\n",
454
			smechs, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
455

456
		mechs = smechs;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
457

458
459
	} else {
		Debug( LDAP_DEBUG_TRACE,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
460
			"ldap_sasl_interactive_bind_s: user selected: %s\n",
461
			mechs, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
462
463
	}

464
	rc = ldap_int_sasl_bind( ld, dn, mechs,
465
		serverControls, clientControls,
466
		flags, interact, defaults );
467

Kurt Zeilenga's avatar
Kurt Zeilenga committed
468
469
470
471
done:
#if defined( LDAP_R_COMPILE ) && defined( HAVE_CYRUS_SASL )
	ldap_pvt_thread_mutex_unlock( &ldap_int_sasl_mutex );
#endif
472
	if ( smechs ) LDAP_FREE( smechs );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
473

474
475
	return rc;
}