1/*
2 *
3 * Copyright 2000 Sun Microsystems, Inc.  All rights reserved.
4 * Use is subject to license terms.
5 *
6 *
7 * Comments:
8 *
9 */
10
11#pragma ident	"%Z%%M%	%I%	%E% SMI"
12
13#include <stdio.h>
14#include <string.h>
15
16#include "lber.h"
17#include "ldap.h"
18#include "ldap-private.h"
19#include "ldap-int.h"
20#include "sec.h"
21#include <strings.h>
22
23BerElement * ldap_build_sasl_bind_req( LDAP *ld, char *dn, char *mechanism, struct berval *creds, LDAPControl ** serverctrls)
24{
25	BerElement *ber = NULL;
26	int err;
27
28	/* Create a Bind Request for SASL authentication.
29	 * It look like this :
30	 * BindRequest := [APPLICATION 0] SEQUENCE {
31	 *		version		INTEGER,
32	 *		name		LDAPDN,
33	 *		authentication	CHOICE {
34	 *			sasl		[3] SEQUENCE {
35	 *				mechanism	LDAPString,
36	 *				credential	OCTET STRING OPTIONAL
37	 * 			}
38	 *		}
39	 *	}
40	 * all wrapped up in an LDAPMessage sequence.
41	 */
42
43	if (dn == NULL || *dn == '\0'){
44		ld->ld_errno = LDAP_PARAM_ERROR;
45		return (NULLBER);
46	}
47
48
49	if ( (ber = alloc_ber_with_options( ld )) == NULLBER ) {
50		return (NULLBER);
51	}
52	if ( ber_printf( ber, "{it{ist{s", ++ld->ld_msgid, LDAP_REQ_BIND, ld->ld_version, dn, LDAP_AUTH_SASL, mechanism) == -1){
53		ld->ld_errno = LDAP_ENCODING_ERROR;
54		ber_free(ber, 1);
55		return (NULLBER);
56	}
57	if (creds != NULL && creds->bv_val != NULL) {
58		if (ber_printf(ber, "o", creds->bv_val, creds->bv_len) == -1){
59			ld->ld_errno = LDAP_ENCODING_ERROR;
60			ber_free(ber, 1);
61			return (NULLBER);
62		}
63	}
64	if (ber_printf(ber, "}}") == -1){
65		ld->ld_errno = LDAP_ENCODING_ERROR;
66		ber_free( ber, 1 );
67		return( NULLBER );
68	}
69	/* LDAPv3 */
70	/* Code controls if any */
71	if (serverctrls && serverctrls[0]) {
72		if (ldap_controls_code(ber, serverctrls) != LDAP_SUCCESS){
73			ld->ld_errno = LDAP_ENCODING_ERROR;
74			ber_free( ber, 1 );
75			return( NULLBER );
76		}
77	} else if (ld->ld_srvctrls && ld->ld_srvctrls[0]) {
78		/* Otherwise, is there any global server ctrls ? */
79		if (ldap_controls_code(ber, ld->ld_srvctrls) != LDAP_SUCCESS){
80			ld->ld_errno = LDAP_ENCODING_ERROR;
81			ber_free( ber, 1 );
82			return( NULLBER );
83		}
84	}
85
86	if ( ber_printf( ber, "}" ) == -1 ) {
87		ld->ld_errno = LDAP_ENCODING_ERROR;
88		ber_free( ber, 1 );
89		return( NULLBER );
90	}
91
92	return (ber);
93}
94
95/*
96 * ldap_sasl_bind - bind to the ldap server (and X.500).
97 * dn, mechanism, cred, serverctrls, and clientctrls are supplied.
98 * the message id of the request is returned in msgid
99 * Returns LDAP_SUCCESS or an error code.
100 */
101
102int ldap_sasl_bind(
103	LDAP *ld,
104	char *dn,
105	char *mechanism,
106	struct berval *cred,
107	LDAPControl **serverctrls,
108	LDAPControl **clientctrls,
109	int *msgidp)
110{
111	int theErr = LDAP_SUCCESS;
112	int rv;
113	BerElement *ber = NULL;
114
115	Debug ( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 1288, "ldap_sasl_bind\n"), 0,0,0);
116
117#ifdef _REENTRANT
118	LOCK_LDAP(ld);
119#endif
120	if (strcasecmp(mechanism, LDAP_SASL_SIMPLE) == 0){
121		/* Simple bind */
122		if ( (ber = ldap_build_simple_bind_req(ld, dn, cred->bv_val, serverctrls)) == NULLBER){
123			ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &theErr);
124#ifdef _REENTRANT
125			UNLOCK_LDAP(ld);
126#endif
127			return (theErr);
128		}
129	}
130
131	if (strcasecmp(mechanism, LDAP_SASL_CRAM_MD5) == 0){
132		if (( ber = ldap_build_sasl_bind_req(ld, dn, LDAP_SASL_CRAM_MD5, cred, serverctrls)) == NULLBER) {
133			ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &theErr);
134#ifdef _REENTRANT
135			UNLOCK_LDAP(ld);
136#endif
137			return (theErr);
138		}
139	}
140
141	if (strcasecmp(mechanism, LDAP_SASL_EXTERNAL) == 0){
142		if (( ber = ldap_build_sasl_bind_req(ld, dn, LDAP_SASL_EXTERNAL, cred, serverctrls)) == NULLBER) {
143			ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &theErr);
144#ifdef _REENTRANT
145			UNLOCK_LDAP(ld);
146#endif
147			return (theErr);
148		}
149	}
150
151	if (strcasecmp(mechanism, LDAP_SASL_X511_PROTECTED) == 0){
152#ifdef _REENTRANT
153		UNLOCK_LDAP(ld);
154#endif
155		return (LDAP_NOT_SUPPORTED);
156/*
157 *		if (( ber = ldap_build_sasl_bind_req(ld, dn, LDAP_SASL_X511_PROTECTED, cred, serverctrls)) == NULLBER) {
158 *			ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &theErr);
159 *#ifdef _REENTRANT
160 *			UNLOCK_LDAP(ld);
161 *#endif
162 *			return (theErr);
163 *		}
164 */
165	}
166	if (strcasecmp(mechanism, LDAP_SASL_X511_STRONG) == 0){
167#ifdef _REENTRANT
168		UNLOCK_LDAP(ld);
169#endif
170		return (LDAP_NOT_SUPPORTED);
171/*
172 *		if (( ber = ldap_build_sasl_bind_req(ld, dn, LDAP_SASL_X511_PROTECTED, cred, serverctrls)) == NULLBER) {
173 *			ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &theErr);
174 *#ifdef _REENTRANT
175 *			UNLOCK_LDAP(ld);
176 *#endif
177 *			return (theErr);
178 *		}
179 */
180	}
181	if (strcasecmp(mechanism, LDAP_SASL_KERBEROS_V4) == 0){
182#ifdef _REENTRANT
183		UNLOCK_LDAP(ld);
184#endif
185		return (LDAP_NOT_SUPPORTED);
186/*
187 *		if (( ber = ldap_build_sasl_bind_req(ld, dn, LDAP_SASL_X511_PROTECTED, cred, serverctrls)) == NULLBER) {
188 *			ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &theErr);
189 *#ifdef _REENTRANT
190 *			UNLOCK_LDAP(ld);
191 *#endif
192 *			return (theErr);
193 *		}
194 */
195	}
196	if (strcasecmp(mechanism, LDAP_SASL_GSSAPI) == 0){
197#ifdef _REENTRANT
198		UNLOCK_LDAP(ld);
199#endif
200		return (LDAP_NOT_SUPPORTED);
201/*
202 *		if (( ber = ldap_build_sasl_bind_req(ld, dn, LDAP_SASL_X511_PROTECTED, cred, serverctrls)) == NULLBER) {
203 *			ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &theErr);
204 *#ifdef _REENTRANT
205 *			UNLOCK_LDAP(ld);
206 *#endif
207 *			return (theErr);
208 *		}
209 */
210	}
211	if (strcasecmp(mechanism, LDAP_SASL_SKEY) == 0){
212#ifdef _REENTRANT
213		UNLOCK_LDAP(ld);
214#endif
215		return (LDAP_NOT_SUPPORTED);
216/*
217 *		if (( ber = ldap_build_sasl_bind_req(ld, dn, LDAP_SASL_X511_PROTECTED, cred, serverctrls)) == NULLBER) {
218 *			ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &theErr);
219 *#ifdef _REENTRANT
220 *			UNLOCK_LDAP(ld);
221 *#endif
222 *			return (theErr);
223 *		}
224 */
225	}
226	if (ber == NULL){
227#ifdef _REENTRANT
228		UNLOCK_LDAP(ld);
229#endif
230		return (LDAP_PARAM_ERROR);
231	}
232
233#ifndef NO_CACHE
234	if ( ld->ld_cache != NULL ) {
235		ldap_flush_cache( ld );
236	}
237#endif /* !NO_CACHE */
238
239	/* send the message */
240	rv = send_initial_request( ld, LDAP_REQ_BIND, dn, ber );
241	if (rv == -1){
242		rv = ld->ld_errno;
243		if (rv == LDAP_SUCCESS){
244			rv = LDAP_OTHER;
245		}
246#ifdef _REENTRANT
247		UNLOCK_LDAP(ld);
248#endif
249		return (rv);
250	}
251	*msgidp = rv;
252#ifdef _REENTRANT
253	UNLOCK_LDAP(ld);
254#endif
255	return ( LDAP_SUCCESS );
256}
257
258/*
259 * ldap_sasl_bind_s - bind to the ldap server (and X.500).
260 * dn, mechanism, cred, serverctrls, and clientctrls are supplied.
261 * the message id of the request is returned in msgid
262 * Returns LDAP_SUCCESS or an error code.
263 */
264
265int ldap_sasl_bind_s(
266	LDAP *ld,
267	char *dn,
268	char *mechanism,
269	struct berval *cred,
270	LDAPControl **serverctrls,
271	LDAPControl **clientctrls,
272	struct berval **servercredp)
273{
274	int msgid;
275	int retcode;
276	LDAPMessage *res;
277
278	Debug ( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 1288, "ldap_sasl_bind\n"), 0,0,0);
279
280	if ((retcode = ldap_sasl_bind(ld, dn, mechanism, cred, serverctrls, clientctrls, &msgid)) != LDAP_SUCCESS)
281		return (retcode);
282	if (ldap_result(ld, msgid, 1, (struct timeval *)NULL, &res ) == -1)
283		return (ld->ld_errno );
284
285	return (ldap_parse_sasl_bind_result(ld, res, servercredp, 1));
286}
287
288int ldap_sasl_cram_md5_bind_s(
289	LDAP *ld,
290	char *dn,
291	struct berval *cred,
292	LDAPControl **serverctrls,
293	LDAPControl **clientctrls )
294{
295	int res;
296	struct berval *challenge = NULL;
297	struct berval resp;
298	unsigned char digest[16];
299	char *theHDigest;
300
301	if (dn == NULL){
302		return (LDAP_PARAM_ERROR);
303	}
304
305	bzero(digest, sizeof (digest));
306
307	if ((res = ldap_sasl_bind_s(ld, dn, LDAP_SASL_CRAM_MD5, NULL, serverctrls, clientctrls, &challenge))
308		!= LDAP_SASL_BIND_INPROGRESS){
309		return (res);
310	}
311	if (challenge == NULL){
312		return (LDAP_PARAM_ERROR);
313	}
314
315	Debug (LDAP_DEBUG_TRACE, "SASL challenge: %s\n", challenge->bv_val, 0, 0);
316
317	hmac_md5((unsigned char *)challenge->bv_val, challenge->bv_len,
318					 (unsigned char *)cred->bv_val, cred->bv_len,  digest);
319	ber_bvfree(challenge);
320	challenge = NULL;
321
322	theHDigest = hexa_print(digest, 16);
323	if (theHDigest == NULL){
324		return (LDAP_NO_MEMORY);
325	}
326
327	resp.bv_len = (strlen(dn) + 32 + 1);
328	if ((resp.bv_val = (char *)malloc(resp.bv_len+1)) == NULL) {
329		return(LDAP_NO_MEMORY);
330	}
331
332	sprintf(resp.bv_val, "%s %s", dn, theHDigest);
333	free(theHDigest);
334
335	Debug (LDAP_DEBUG_TRACE, "SASL response: %s\n", resp.bv_val, 0, 0);
336	res = ldap_sasl_bind_s(ld, dn, LDAP_SASL_CRAM_MD5, &resp, serverctrls, clientctrls, &challenge);
337
338	free(resp.bv_val);
339	return (res);
340}
341