xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/iscsi/iscsiAuthClientGlue.c (revision 47842382d52f28aa3173aa6b511781c322ccb6a2)
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(isp->sess_auth.username_in,
115 		    isp->sess_auth.username, challengeData, responseData,
116 		    id, RADIUS_AUTHENTICATION, (void *)&radius_cfg);
117 
118 		switch (chap_valid_status) {
119 			case CHAP_VALIDATION_PASSED:
120 				authStatus = iscsiAuthStatusPass;
121 				break;
122 			case CHAP_VALIDATION_INVALID_RESPONSE:
123 				authStatus = iscsiAuthStatusFail;
124 				break;
125 			case CHAP_VALIDATION_DUP_SECRET:
126 				authStatus = iscsiAuthStatusFail;
127 				break;
128 			case CHAP_VALIDATION_RADIUS_ACCESS_ERROR:
129 				authStatus = iscsiAuthStatusFail;
130 				break;
131 			case CHAP_VALIDATION_BAD_RADIUS_SECRET:
132 				authStatus = iscsiAuthStatusFail;
133 				break;
134 			default:
135 				authStatus = iscsiAuthStatusFail;
136 				break;
137 		}
138 		return (authStatus);
139 	} else {
140 		/* Use target secret (if defined) to authenticate target */
141 		if ((isp->sess_auth.password_length_in < 1) ||
142 		    (isp->sess_auth.password_in == NULL) ||
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