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
iscsiAuthClientChapAuthRequest(IscsiAuthClient * client,char * username,unsigned int id,uchar_t * challengeData,unsigned int challengeLength,uchar_t * responseData,unsigned int responseLength)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 (strcmp(username, isp->sess_auth.username_in) != 0) {
62 cmn_err(CE_WARN, "iscsi session(%u) failed authentication, "
63 "received incorrect username from target",
64 isp->sess_oid);
65 return (iscsiAuthStatusFail);
66 }
67
68 /* Check if RADIUS access is enabled */
69 if (persistent_radius_get(&p_radius_cfg) == ISCSI_NVFILE_SUCCESS &&
70 p_radius_cfg.r_radius_access == B_TRUE) {
71 chap_validation_status_type chap_valid_status;
72 int authStatus;
73 RADIUS_CONFIG radius_cfg;
74
75 if (p_radius_cfg.r_radius_config_valid == B_FALSE) {
76 /*
77 * Radius enabled but configuration invalid -
78 * invalid condition
79 */
80 return (iscsiAuthStatusFail);
81 }
82
83 /* Use RADIUS server to authentication target */
84 if (p_radius_cfg.r_insize == sizeof (in_addr_t)) {
85 /* IPv4 */
86 radius_cfg.rad_svr_addr.i_addr.in4.s_addr =
87 p_radius_cfg.r_addr.u_in4.s_addr;
88 radius_cfg.rad_svr_addr.i_insize
89 = sizeof (in_addr_t);
90 } else if (p_radius_cfg.r_insize == sizeof (in6_addr_t)) {
91 /* IPv6 */
92 bcopy(p_radius_cfg.r_addr.u_in6.s6_addr,
93 radius_cfg.rad_svr_addr.i_addr.in6.s6_addr,
94 16);
95 radius_cfg.rad_svr_addr.i_insize = sizeof (in6_addr_t);
96 } else {
97 return (iscsiAuthStatusFail);
98 }
99
100 radius_cfg.rad_svr_port = p_radius_cfg.r_port;
101 bcopy(p_radius_cfg.r_shared_secret,
102 radius_cfg.rad_svr_shared_secret,
103 MAX_RAD_SHARED_SECRET_LEN);
104 radius_cfg.rad_svr_shared_secret_len =
105 p_radius_cfg.r_shared_secret_len;
106
107 /* Entry point to the CHAP authentication module. */
108 chap_valid_status = chap_validate_tgt(
109 isp->sess_auth.username_in,
110 isp->sess_auth.username,
111 challengeData,
112 challengeLength,
113 responseData,
114 responseLength,
115 id,
116 RADIUS_AUTHENTICATION,
117 (void *)&radius_cfg);
118
119 switch (chap_valid_status) {
120 case CHAP_VALIDATION_PASSED:
121 authStatus = iscsiAuthStatusPass;
122 break;
123 case CHAP_VALIDATION_INVALID_RESPONSE:
124 authStatus = iscsiAuthStatusFail;
125 break;
126 case CHAP_VALIDATION_DUP_SECRET:
127 authStatus = iscsiAuthStatusFail;
128 break;
129 case CHAP_VALIDATION_RADIUS_ACCESS_ERROR:
130 authStatus = iscsiAuthStatusFail;
131 break;
132 case CHAP_VALIDATION_BAD_RADIUS_SECRET:
133 authStatus = iscsiAuthStatusFail;
134 break;
135 default:
136 authStatus = iscsiAuthStatusFail;
137 break;
138 }
139 return (authStatus);
140 } else {
141 /* Use target secret (if defined) to authenticate target */
142 if ((isp->sess_auth.password_length_in < 1) ||
143 (isp->sess_auth.password_in[0] == '\0')) {
144 /* No target secret defined - invalid condition */
145 return (iscsiAuthStatusFail);
146 }
147
148 /*
149 * challenge length is I->T, and shouldn't need to
150 * be checked
151 */
152 if (responseLength != sizeof (verifyData)) {
153 cmn_err(CE_WARN, "iscsi session(%u) failed "
154 "authentication, received incorrect CHAP response "
155 "from target", isp->sess_oid);
156 return (iscsiAuthStatusFail);
157 }
158
159 iscsiAuthMd5Init(&context);
160
161 /*
162 * id byte
163 */
164 verifyData[0] = id;
165 iscsiAuthMd5Update(&context, verifyData, 1);
166
167 /*
168 * shared secret
169 */
170 iscsiAuthMd5Update(&context,
171 (uchar_t *)isp->sess_auth.password_in,
172 isp->sess_auth.password_length_in);
173
174 /*
175 * challenge value
176 */
177 iscsiAuthMd5Update(&context,
178 (uchar_t *)challengeData,
179 challengeLength);
180
181 iscsiAuthMd5Final(verifyData, &context);
182
183 if (bcmp(responseData, verifyData,
184 sizeof (verifyData)) == 0) {
185 return (iscsiAuthStatusPass);
186 }
187
188 cmn_err(CE_WARN, "iscsi session(%u) failed authentication, "
189 "received incorrect CHAP response from target",
190 isp->sess_oid);
191 }
192
193 return (iscsiAuthStatusFail);
194 }
195
196 /* ARGSUSED */
197 void
iscsiAuthClientChapAuthCancel(IscsiAuthClient * client)198 iscsiAuthClientChapAuthCancel(IscsiAuthClient * client)
199 {
200 }
201
202
203 int
iscsiAuthClientTextToNumber(const char * text,unsigned long * pNumber)204 iscsiAuthClientTextToNumber(const char *text, unsigned long *pNumber)
205 {
206 char *pEnd;
207 unsigned long number;
208
209 if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) {
210 if (ddi_strtoul(text + 2, &pEnd, 16, &number) != 0) {
211 return (1); /* Error */
212 }
213 } else {
214 if (ddi_strtoul(text, &pEnd, 10, &number) != 0) {
215 return (1); /* Error */
216 }
217 }
218
219 if (*text != '\0' && *pEnd == '\0') {
220 *pNumber = number;
221 return (0); /* No error */
222 } else {
223 return (1); /* Error */
224 }
225 }
226
227 /* ARGSUSED */
228 void
iscsiAuthClientNumberToText(unsigned long number,char * text,unsigned int length)229 iscsiAuthClientNumberToText(unsigned long number, char *text,
230 unsigned int length)
231 {
232 (void) sprintf(text, "%lu", number);
233 }
234
235
236 void
iscsiAuthRandomSetData(uchar_t * data,unsigned int length)237 iscsiAuthRandomSetData(uchar_t *data, unsigned int length)
238 {
239 (void) random_get_pseudo_bytes(data, length);
240 }
241
242
243 void
iscsiAuthMd5Init(IscsiAuthMd5Context * context)244 iscsiAuthMd5Init(IscsiAuthMd5Context * context)
245 {
246 MD5Init(context);
247 }
248
249
250 void
iscsiAuthMd5Update(IscsiAuthMd5Context * context,uchar_t * data,unsigned int length)251 iscsiAuthMd5Update(IscsiAuthMd5Context *context, uchar_t *data,
252 unsigned int length)
253 {
254 MD5Update(context, data, length);
255 }
256
257
258 void
iscsiAuthMd5Final(uchar_t * hash,IscsiAuthMd5Context * context)259 iscsiAuthMd5Final(uchar_t *hash, IscsiAuthMd5Context *context)
260 {
261 MD5Final(hash, context);
262 }
263
264
265 int
iscsiAuthClientData(uchar_t * outData,unsigned int * outLength,uchar_t * inData,unsigned int inLength)266 iscsiAuthClientData(uchar_t *outData, unsigned int *outLength,
267 uchar_t *inData, unsigned int inLength)
268 {
269 if (*outLength < inLength) {
270 return (1); /* error */
271 }
272 bcopy(inData, outData, inLength);
273 *outLength = inLength;
274 return (0); /* no error */
275 }
276