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 (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
iscsiAuthClientChapAuthCancel(IscsiAuthClient * client)205 iscsiAuthClientChapAuthCancel(IscsiAuthClient * client)
206 {
207 }
208
209
210 int
iscsiAuthClientTextToNumber(const char * text,unsigned long * pNumber)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
iscsiAuthClientNumberToText(unsigned long number,char * text,unsigned int length)236 iscsiAuthClientNumberToText(unsigned long number, char *text,
237 unsigned int length)
238 {
239 (void) sprintf(text, "%lu", number);
240 }
241
242
243 void
iscsiAuthRandomSetData(uchar_t * data,unsigned int length)244 iscsiAuthRandomSetData(uchar_t *data, unsigned int length)
245 {
246 (void) random_get_pseudo_bytes(data, length);
247 }
248
249
250 void
iscsiAuthMd5Init(IscsiAuthMd5Context * context)251 iscsiAuthMd5Init(IscsiAuthMd5Context * context)
252 {
253 MD5Init(context);
254 }
255
256
257 void
iscsiAuthMd5Update(IscsiAuthMd5Context * context,uchar_t * data,unsigned int length)258 iscsiAuthMd5Update(IscsiAuthMd5Context *context, uchar_t *data,
259 unsigned int length)
260 {
261 MD5Update(context, data, length);
262 }
263
264
265 void
iscsiAuthMd5Final(uchar_t * hash,IscsiAuthMd5Context * context)266 iscsiAuthMd5Final(uchar_t *hash, IscsiAuthMd5Context *context)
267 {
268 MD5Final(hash, context);
269 }
270
271
272 int
iscsiAuthClientData(uchar_t * outData,unsigned int * outLength,uchar_t * inData,unsigned int inLength)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