1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2000 by Cisco Systems, Inc.  All rights reserved.
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 *
26 * iSCSI Pseudo HBA Driver
27 */
28
29#include <sys/random.h>
30
31#include "chap.h"
32#include "iscsi.h"
33#include <sys/iscsi_protocol.h>
34#include "iscsiAuthClient.h"
35#include "persistent.h"
36
37/*
38 * Authenticate a target's CHAP response.
39 *
40 * username - Incoming username from the the target.
41 * responseData - Incoming response data from the target.
42 */
43int
44iscsiAuthClientChapAuthRequest(IscsiAuthClient *client,
45    char *username, unsigned int id, uchar_t *challengeData,
46    unsigned int challengeLength, uchar_t *responseData,
47    unsigned int responseLength)
48{
49	iscsi_sess_t		*isp = (iscsi_sess_t *)client->userHandle;
50	IscsiAuthMd5Context	context;
51	uchar_t			verifyData[16];
52	iscsi_radius_props_t p_radius_cfg;
53
54	if (isp == NULL) {
55		return (iscsiAuthStatusFail);
56	}
57
58	/*
59	 * the expected credentials are in the session
60	 */
61	if (isp->sess_auth.username_in == NULL) {
62		cmn_err(CE_WARN, "iscsi session(%u) failed authentication, "
63		    "no incoming username configured to authenticate target",
64		    isp->sess_oid);
65		return (iscsiAuthStatusFail);
66	}
67	if (strcmp(username, isp->sess_auth.username_in) != 0) {
68		cmn_err(CE_WARN, "iscsi session(%u) failed authentication, "
69		    "received incorrect username from target",
70		    isp->sess_oid);
71		return (iscsiAuthStatusFail);
72	}
73
74	/* Check if RADIUS access is enabled */
75	if (persistent_radius_get(&p_radius_cfg) == ISCSI_NVFILE_SUCCESS &&
76	    p_radius_cfg.r_radius_access == B_TRUE) {
77		chap_validation_status_type chap_valid_status;
78		int authStatus;
79		RADIUS_CONFIG radius_cfg;
80
81		if (p_radius_cfg.r_radius_config_valid == B_FALSE) {
82			/*
83			 * Radius enabled but configuration invalid -
84			 * invalid condition
85			 */
86			return (iscsiAuthStatusFail);
87		}
88
89		/* Use RADIUS server to authentication target */
90		if (p_radius_cfg.r_insize == sizeof (in_addr_t)) {
91			/* IPv4 */
92			radius_cfg.rad_svr_addr.i_addr.in4.s_addr =
93			    p_radius_cfg.r_addr.u_in4.s_addr;
94			radius_cfg.rad_svr_addr.i_insize
95			    = sizeof (in_addr_t);
96		} else if (p_radius_cfg.r_insize == sizeof (in6_addr_t)) {
97			/* IPv6 */
98			bcopy(p_radius_cfg.r_addr.u_in6.s6_addr,
99			    radius_cfg.rad_svr_addr.i_addr.in6.s6_addr,
100			    16);
101			radius_cfg.rad_svr_addr.i_insize = sizeof (in6_addr_t);
102		} else {
103			return (iscsiAuthStatusFail);
104		}
105
106		radius_cfg.rad_svr_port = p_radius_cfg.r_port;
107		bcopy(p_radius_cfg.r_shared_secret,
108		    radius_cfg.rad_svr_shared_secret,
109		    MAX_RAD_SHARED_SECRET_LEN);
110		radius_cfg.rad_svr_shared_secret_len =
111		    p_radius_cfg.r_shared_secret_len;
112
113		/* Entry point to the CHAP authentication module. */
114		chap_valid_status = chap_validate_tgt(
115		    isp->sess_auth.username_in,
116		    isp->sess_auth.username,
117		    challengeData,
118		    challengeLength,
119		    responseData,
120		    responseLength,
121		    id,
122		    RADIUS_AUTHENTICATION,
123		    (void *)&radius_cfg);
124
125		switch (chap_valid_status) {
126			case CHAP_VALIDATION_PASSED:
127				authStatus = iscsiAuthStatusPass;
128				break;
129			case CHAP_VALIDATION_INVALID_RESPONSE:
130				authStatus = iscsiAuthStatusFail;
131				break;
132			case CHAP_VALIDATION_DUP_SECRET:
133				authStatus = iscsiAuthStatusFail;
134				break;
135			case CHAP_VALIDATION_RADIUS_ACCESS_ERROR:
136				authStatus = iscsiAuthStatusFail;
137				break;
138			case CHAP_VALIDATION_BAD_RADIUS_SECRET:
139				authStatus = iscsiAuthStatusFail;
140				break;
141			default:
142				authStatus = iscsiAuthStatusFail;
143				break;
144		}
145		return (authStatus);
146	} else {
147		/* Use target secret (if defined) to authenticate target */
148		if ((isp->sess_auth.password_length_in < 1) ||
149		    (isp->sess_auth.password_in == NULL) ||
150		    (isp->sess_auth.password_in[0] == '\0')) {
151			/* No target secret defined - invalid condition */
152			return (iscsiAuthStatusFail);
153		}
154
155		/*
156		 * challenge length is I->T, and shouldn't need to
157		 * be checked
158		 */
159		if (responseLength != sizeof (verifyData)) {
160			cmn_err(CE_WARN, "iscsi session(%u) failed "
161			    "authentication, received incorrect CHAP response "
162			    "from target", isp->sess_oid);
163			return (iscsiAuthStatusFail);
164		}
165
166		iscsiAuthMd5Init(&context);
167
168		/*
169		 * id byte
170		 */
171		verifyData[0] = id;
172		iscsiAuthMd5Update(&context, verifyData, 1);
173
174		/*
175		 * shared secret
176		 */
177		iscsiAuthMd5Update(&context,
178		    (uchar_t *)isp->sess_auth.password_in,
179		    isp->sess_auth.password_length_in);
180
181		/*
182		 * challenge value
183		 */
184		iscsiAuthMd5Update(&context,
185		    (uchar_t *)challengeData,
186		    challengeLength);
187
188		iscsiAuthMd5Final(verifyData, &context);
189
190		if (bcmp(responseData, verifyData,
191		    sizeof (verifyData)) == 0) {
192			return (iscsiAuthStatusPass);
193		}
194
195		cmn_err(CE_WARN, "iscsi session(%u) failed authentication, "
196		    "received incorrect CHAP response from target",
197		    isp->sess_oid);
198	}
199
200	return (iscsiAuthStatusFail);
201}
202
203/* ARGSUSED */
204void
205iscsiAuthClientChapAuthCancel(IscsiAuthClient * client)
206{
207}
208
209
210int
211iscsiAuthClientTextToNumber(const char *text, unsigned long *pNumber)
212{
213	char *pEnd;
214	unsigned long number;
215
216	if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) {
217		if (ddi_strtoul(text + 2, &pEnd, 16, &number) != 0) {
218			return (1); /* Error */
219		}
220	} else {
221		if (ddi_strtoul(text, &pEnd, 10, &number) != 0) {
222			return (1); /* Error */
223		}
224	}
225
226	if (*text != '\0' && *pEnd == '\0') {
227		*pNumber = number;
228		return (0);	/* No error */
229	} else {
230		return (1);	/* Error */
231	}
232}
233
234/* ARGSUSED */
235void
236iscsiAuthClientNumberToText(unsigned long number, char *text,
237    unsigned int length)
238{
239	(void) sprintf(text, "%lu", number);
240}
241
242
243void
244iscsiAuthRandomSetData(uchar_t *data, unsigned int length)
245{
246	(void) random_get_pseudo_bytes(data, length);
247}
248
249
250void
251iscsiAuthMd5Init(IscsiAuthMd5Context * context)
252{
253	MD5Init(context);
254}
255
256
257void
258iscsiAuthMd5Update(IscsiAuthMd5Context *context, uchar_t *data,
259    unsigned int length)
260{
261	MD5Update(context, data, length);
262}
263
264
265void
266iscsiAuthMd5Final(uchar_t *hash, IscsiAuthMd5Context *context)
267{
268	MD5Final(hash, context);
269}
270
271
272int
273iscsiAuthClientData(uchar_t *outData, unsigned int *outLength,
274    uchar_t *inData, unsigned int inLength)
275{
276	if (*outLength < inLength) {
277		return (1);	/* error */
278	}
279	bcopy(inData, outData, inLength);
280	*outLength = inLength;
281	return (0);		/* no error */
282}
283