xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/iscsi/iscsiAuthClientGlue.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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  */
43 int
44 iscsiAuthClientChapAuthRequest(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 */
204 void
205 iscsiAuthClientChapAuthCancel(IscsiAuthClient * client)
206 {
207 }
208 
209 
210 int
211 iscsiAuthClientTextToNumber(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 */
235 void
236 iscsiAuthClientNumberToText(unsigned long number, char *text,
237     unsigned int length)
238 {
239 	(void) sprintf(text, "%lu", number);
240 }
241 
242 
243 void
244 iscsiAuthRandomSetData(uchar_t *data, unsigned int length)
245 {
246 	(void) random_get_pseudo_bytes(data, length);
247 }
248 
249 
250 void
251 iscsiAuthMd5Init(IscsiAuthMd5Context * context)
252 {
253 	MD5Init(context);
254 }
255 
256 
257 void
258 iscsiAuthMd5Update(IscsiAuthMd5Context *context, uchar_t *data,
259     unsigned int length)
260 {
261 	MD5Update(context, data, length);
262 }
263 
264 
265 void
266 iscsiAuthMd5Final(uchar_t *hash, IscsiAuthMd5Context *context)
267 {
268 	MD5Final(hash, context);
269 }
270 
271 
272 int
273 iscsiAuthClientData(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