1/*
2 * Copyright 2002-2003 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6#pragma ident	"%Z%%M%	%I%	%E% SMI"
7
8/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
9 *
10 * The contents of this file are subject to the Netscape Public License
11 * Version 1.0 (the "NPL"); you may not use this file except in
12 * compliance with the NPL.  You may obtain a copy of the NPL at
13 * http://www.mozilla.org/NPL/
14 *
15 * Software distributed under the NPL is distributed on an "AS IS" basis,
16 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
17 * for the specific language governing rights and limitations under the
18 * NPL.
19 *
20 * The Initial Developer of the Original Code is Netscape
21 * Communications Corporation. Portions created by Netscape are
22 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
23 * Rights Reserved.
24 *
25 * Contributor(s):
26 */
27#include "ldap-int.h"
28
29/*
30 * ldap_sasl_bind - authenticate to the ldap server.  The dn, mechanism,
31 * and credentials of the entry to which to bind are supplied. An LDAP
32 * error code is returned and if LDAP_SUCCESS is returned *msgidp is set
33 * to the id of the request initiated.
34 *
35 * Example:
36 *	struct berval	creds;
37 *	LDAPControl	**ctrls;
38 *	int		err, msgid;
39 *	... fill in creds with credentials ...
40 *	... fill in ctrls with server controls ...
41 *	err = ldap_sasl_bind( ld, "cn=manager, o=university of michigan, c=us",
42 *	    "mechanismname", &creds, ctrls, NULL, &msgid );
43 */
44int
45LDAP_CALL
46ldap_sasl_bind(
47    LDAP		*ld,
48    const char		*dn,
49    const char		*mechanism,
50    const struct berval	*cred,
51    LDAPControl		**serverctrls,
52    LDAPControl		**clientctrls,
53    int			*msgidp
54)
55{
56	BerElement	*ber;
57	int		rc, simple, msgid, ldapversion;
58
59	/*
60	 * The ldapv3 bind request looks like this:
61	 *	BindRequest ::= SEQUENCE {
62	 *		version		INTEGER,
63	 *		name		DistinguishedName,	 -- who
64	 *		authentication	CHOICE {
65	 *			simple		[0] OCTET STRING, -- passwd
66	 *			sasl		[3] SaslCredentials -- v3 only
67	 *		}
68	 *	}
69	 *	SaslCredentials ::= SEQUENCE {
70	 *		mechanism	LDAPString,
71	 * 		credentials	OCTET STRING
72	 *	}
73	 * all wrapped up in an LDAPMessage sequence.
74	 */
75
76	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n", 0, 0, 0 );
77
78	if ( msgidp == NULL ) {
79		LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
80                return( LDAP_PARAM_ERROR );
81	}
82
83	simple = ( mechanism == LDAP_SASL_SIMPLE );
84	ldapversion = NSLDAPI_LDAP_VERSION( ld );
85
86	/* only ldapv3 or higher can do sasl binds */
87	if ( !simple && ldapversion < LDAP_VERSION3 ) {
88		LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
89		return( LDAP_NOT_SUPPORTED );
90	}
91
92	LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
93	msgid = ++ld->ld_msgid;
94	LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );
95
96	if ( dn == NULL )
97		dn = "";
98
99	if ( ld->ld_cache_on && ld->ld_cache_bind != NULL ) {
100		LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK );
101		if ( (rc = (ld->ld_cache_bind)( ld, msgid, LDAP_REQ_BIND, dn,
102		    cred, LDAP_AUTH_SASL )) != 0 ) {
103			*msgidp = rc;
104			LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
105			return( LDAP_SUCCESS );
106		}
107		LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
108	}
109
110	/* create a message to send */
111	if (( rc = nsldapi_alloc_ber_with_options( ld, &ber ))
112	    != LDAP_SUCCESS ) {
113		return( rc );
114	}
115
116	/* fill it in */
117	if ( simple ) {		/* simple bind; works in LDAPv2 or v3 */
118		struct berval	tmpcred;
119
120		if ( cred == NULL ) {
121			tmpcred.bv_val = "";
122			tmpcred.bv_len = 0;
123			cred = &tmpcred;
124		}
125		rc = ber_printf( ber, "{it{isto}", msgid, LDAP_REQ_BIND,
126		    ldapversion, dn, LDAP_AUTH_SIMPLE, cred->bv_val,
127		    (int)cred->bv_len /* XXX lossy cast */ );
128
129	} else {		/* SASL bind; requires LDAPv3 or better */
130		if ( cred == NULL ) {
131			rc = ber_printf( ber, "{it{ist{s}}", msgid,
132			    LDAP_REQ_BIND, ldapversion, dn, LDAP_AUTH_SASL,
133			    mechanism );
134		} else {
135			rc = ber_printf( ber, "{it{ist{so}}", msgid,
136			    LDAP_REQ_BIND, ldapversion, dn, LDAP_AUTH_SASL,
137			    mechanism, cred->bv_val,
138			    (int)cred->bv_len /* XXX lossy cast */ );
139		}
140	}
141
142	if ( rc == -1 ) {
143		LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL );
144		ber_free( ber, 1 );
145		return( LDAP_ENCODING_ERROR );
146	}
147
148	if ( (rc = nsldapi_put_controls( ld, serverctrls, 1, ber ))
149	    != LDAP_SUCCESS ) {
150		ber_free( ber, 1 );
151		return( rc );
152	}
153
154	/* send the message */
155	rc = nsldapi_send_initial_request( ld, msgid, LDAP_REQ_BIND,
156		(char *)dn, ber );
157	*msgidp = rc;
158	return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS );
159}
160
161/*
162 * ldap_sasl_bind_s - bind to the ldap server using sasl authentication
163 * The dn, mechanism, and credentials of the entry to which to bind are
164 * supplied.  LDAP_SUCCESS is returned upon success, the ldap error code
165 * otherwise.
166 *
167 * Example:
168 *	struct berval	creds;
169 *	... fill in creds with credentials ...
170 *	ldap_sasl_bind_s( ld, "cn=manager, o=university of michigan, c=us",
171 *	    "mechanismname", &creds )
172 */
173int
174LDAP_CALL
175ldap_sasl_bind_s(
176    LDAP		*ld,
177    const char		*dn,
178    const char		*mechanism,
179    const struct berval	*cred,
180    LDAPControl		**serverctrls,
181    LDAPControl		**clientctrls,
182    struct berval	**servercredp
183)
184{
185	int		err, msgid;
186	LDAPMessage	*result;
187
188	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_sasl_bind_s\n", 0, 0, 0 );
189
190	if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) {
191		LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
192		return( LDAP_NOT_SUPPORTED );
193	}
194
195	if ( ( err = ldap_sasl_bind( ld, dn, mechanism, cred, serverctrls,
196	    clientctrls, &msgid )) != LDAP_SUCCESS )
197		return( err );
198
199	if ( ldap_result( ld, msgid, 1, (struct timeval *) 0, &result ) == -1 )
200		return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
201
202	err = ldap_parse_sasl_bind_result( ld, result, servercredp, 0 );
203	if (err != LDAP_SUCCESS  && err != LDAP_SASL_BIND_IN_PROGRESS) {
204		ldap_msgfree( result );
205		return( err );
206	}
207
208	return( ldap_result2error( ld, result, 1 ) );
209}
210
211
212/* returns an LDAP error code that indicates if parse succeeded or not */
213int
214LDAP_CALL
215ldap_parse_sasl_bind_result(
216    LDAP		*ld,
217    LDAPMessage		*res,
218    struct berval	**servercredp,
219    int			freeit
220)
221{
222	BerElement	ber;
223	int		rc, err;
224	ber_int_t	along;
225	ber_len_t	len;
226	char		*m, *e;
227
228	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 );
229
230	/*
231	 * the ldapv3 SASL bind response looks like this:
232	 *
233	 *	BindResponse ::= [APPLICATION 1] SEQUENCE {
234	 *		COMPONENTS OF LDAPResult,
235	 *		serverSaslCreds [7] OCTET STRING OPTIONAL
236	 *	}
237	 *
238	 * all wrapped up in an LDAPMessage sequence.
239	 */
240
241	if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) ||
242	    !NSLDAPI_VALID_LDAPMESSAGE_BINDRESULT_POINTER( res )) {
243		return( LDAP_PARAM_ERROR );
244	}
245
246	/* only ldapv3 or higher can do sasl binds */
247	if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) {
248		LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
249		return( LDAP_NOT_SUPPORTED );
250	}
251
252	if ( servercredp != NULL ) {
253		*servercredp = NULL;
254	}
255
256	ber = *(res->lm_ber);	/* struct copy */
257
258	/* skip past message id, matched dn, error message ... */
259	rc = ber_scanf( &ber, "{iaa}", &along, &m, &e );
260
261	if ( rc != LBER_ERROR &&
262	    ber_peek_tag( &ber, &len ) == LDAP_TAG_SASL_RES_CREDS ) {
263		rc = ber_get_stringal( &ber, servercredp );
264	}
265
266	if ( freeit ) {
267		ldap_msgfree( res );
268	}
269
270	if ( rc == LBER_ERROR ) {
271		err = LDAP_DECODING_ERROR;
272	} else {
273		err = (int) along;
274	}
275
276	LDAP_SET_LDERRNO( ld, err, m, e );
277	/* this is a little kludge for the 3.0 Barracuda/hammerhead relese */
278	/* the docs state that the return is either LDAP_DECODING_ERROR */
279	/* or LDAP_SUCCESS.  Here we match the docs...  it's cleaner in 3.1 */
280
281	if ( LDAP_DECODING_ERROR == err ) {
282		return (LDAP_DECODING_ERROR);
283	} else {
284		return( LDAP_SUCCESS );
285	}
286}
287