xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/iscsi/iscsiAuthClientGlue.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
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 (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
198 iscsiAuthClientChapAuthCancel(IscsiAuthClient * client)
199 {
200 }
201 
202 
203 int
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
229 iscsiAuthClientNumberToText(unsigned long number, char *text,
230     unsigned int length)
231 {
232 	(void) sprintf(text, "%lu", number);
233 }
234 
235 
236 void
237 iscsiAuthRandomSetData(uchar_t *data, unsigned int length)
238 {
239 	(void) random_get_pseudo_bytes(data, length);
240 }
241 
242 
243 void
244 iscsiAuthMd5Init(IscsiAuthMd5Context * context)
245 {
246 	MD5Init(context);
247 }
248 
249 
250 void
251 iscsiAuthMd5Update(IscsiAuthMd5Context *context, uchar_t *data,
252     unsigned int length)
253 {
254 	MD5Update(context, data, length);
255 }
256 
257 
258 void
259 iscsiAuthMd5Final(uchar_t *hash, IscsiAuthMd5Context *context)
260 {
261 	MD5Final(hash, context);
262 }
263 
264 
265 int
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