xref: /titanic_52/usr/src/uts/common/io/comstar/port/iscsit/iscsit_radiuspacket.c (revision 0f1702c5201310f0529cd5abb77652e5e9b241b6)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/ddi.h>
27 #include <sys/sunddi.h>
28 #include <sys/iscsit/iscsi_if.h>
29 #include <sys/md5.h>
30 
31 #include <sys/idm/idm.h>
32 #include <sys/idm/idm_so.h>
33 #include <sys/iscsit/radius_packet.h>
34 #include <sys/iscsit/radius_protocol.h>
35 #include <sys/ksocket.h>
36 
37 static void encode_chap_password(int identifier, int chap_passwd_len,
38     uint8_t *chap_passwd, uint8_t *result);
39 
40 static size_t iscsit_net_recvmsg(ksocket_t socket, struct msghdr *msg,
41     int timeout);
42 
43 /*
44  * See radius_packet.h.
45  */
46 int
47 iscsit_snd_radius_request(ksocket_t socket, iscsi_ipaddr_t rsvr_ip_addr,
48     uint32_t rsvr_port, radius_packet_data_t *req_data)
49 {
50 	int		i;		/* Loop counter. */
51 	int		data_len;
52 	int		len;
53 	ushort_t	total_length;	/* Has to be 2 octets in size */
54 	uint8_t		*ptr;		/* Pointer to RADIUS packet data */
55 	uint8_t		*length_ptr;	/* Points to the Length field of the */
56 					/* packet. */
57 	uint8_t		*data;		/* RADIUS data to be sent */
58 	radius_attr_t	*req_attr;	/* Request attributes */
59 	radius_packet_t	*packet;	/* Outbound RADIUS packet */
60 	union {
61 		struct sockaddr_in s_in4;
62 		struct sockaddr_in6 s_in6;
63 	} sa_rsvr;			/* Socket address of the server */
64 	int err;
65 
66 	/*
67 	 * Create a RADIUS packet with minimal length for now.
68 	 */
69 	total_length = MIN_RAD_PACKET_LEN;
70 	data = kmem_zalloc(MAX_RAD_PACKET_LEN, KM_SLEEP);
71 	packet = (radius_packet_t *)data;
72 	packet->code = req_data->code;
73 	packet->identifier = req_data->identifier;
74 	bcopy(req_data->authenticator, packet->authenticator,
75 	    RAD_AUTHENTICATOR_LEN);
76 	ptr = packet->data;
77 
78 	/* Loop over all attributes of the request. */
79 	for (i = 0; i < req_data->num_of_attrs; i++) {
80 		if (total_length > MAX_RAD_PACKET_LEN) {
81 			/* The packet has exceed its maximum size. */
82 			kmem_free(data, MAX_RAD_PACKET_LEN);
83 			return (-1);
84 		}
85 
86 		req_attr = &req_data->attrs[i];
87 		*ptr++ = (req_attr->attr_type_code & 0xFF);
88 		length_ptr = ptr;
89 		/* Length is 2 octets - RFC 2865 section 3 */
90 		*ptr++ = 2;
91 		total_length += 2;
92 
93 		/* If the attribute is CHAP-Password, encode it. */
94 		if (req_attr->attr_type_code == RAD_CHAP_PASSWORD) {
95 			/*
96 			 * Identifier plus CHAP response. RFC 2865
97 			 * section 5.3.
98 			 */
99 			uint8_t encoded_chap_passwd[
100 			    RAD_CHAP_PASSWD_STR_LEN + RAD_IDENTIFIER_LEN + 1];
101 			encode_chap_password(
102 			    req_data->identifier,
103 			    req_attr->attr_value_len,
104 			    req_attr->attr_value,
105 			    encoded_chap_passwd);
106 
107 			req_attr->attr_value_len =
108 			    RAD_CHAP_PASSWD_STR_LEN + RAD_IDENTIFIER_LEN;
109 
110 			bcopy(encoded_chap_passwd,
111 			    req_attr->attr_value,
112 			    req_attr->attr_value_len);
113 		}
114 
115 		len = req_attr->attr_value_len;
116 		*length_ptr += len;
117 
118 		bcopy(req_attr->attr_value, ptr, req_attr->attr_value_len);
119 		ptr += req_attr->attr_value_len;
120 
121 		total_length += len;
122 	} /* Done looping over all attributes */
123 
124 	data_len = total_length;
125 	total_length = htons(total_length);
126 	bcopy(&total_length, packet->length, sizeof (ushort_t));
127 
128 	/*
129 	 * Send the packet to the RADIUS server.
130 	 */
131 	bzero((char *)&sa_rsvr, sizeof (sa_rsvr));
132 	if (rsvr_ip_addr.i_insize == sizeof (in_addr_t)) {
133 
134 		/* IPv4 */
135 		sa_rsvr.s_in4.sin_family = AF_INET;
136 		sa_rsvr.s_in4.sin_addr.s_addr =
137 		    rsvr_ip_addr.i_addr.in4.s_addr;
138 		sa_rsvr.s_in4.sin_port = htons((ushort_t)rsvr_port);
139 
140 		err = idm_sosendto(socket, data, data_len,
141 		    (struct sockaddr *)&sa_rsvr.s_in4,
142 		    sizeof (struct sockaddr_in));
143 		kmem_free(data, MAX_RAD_PACKET_LEN);
144 		return (err);
145 	} else if (rsvr_ip_addr.i_insize == sizeof (in6_addr_t)) {
146 		/* IPv6 */
147 		sa_rsvr.s_in6.sin6_family = AF_INET6;
148 		bcopy(rsvr_ip_addr.i_addr.in6.s6_addr,
149 		    sa_rsvr.s_in6.sin6_addr.s6_addr, sizeof (struct in6_addr));
150 		sa_rsvr.s_in6.sin6_port = htons((ushort_t)rsvr_port);
151 
152 		err = idm_sosendto(socket, data, data_len,
153 		    (struct sockaddr *)&sa_rsvr.s_in6,
154 		    sizeof (struct sockaddr_in6));
155 		kmem_free(data, MAX_RAD_PACKET_LEN);
156 		return (err);
157 	} else {
158 		/* Invalid IP address for RADIUS server. */
159 		kmem_free(data, MAX_RAD_PACKET_LEN);
160 		return (-1);
161 	}
162 }
163 
164 /*
165  * See radius_packet.h.
166  */
167 int
168 iscsit_rcv_radius_response(ksocket_t socket, uint8_t *shared_secret,
169     uint32_t shared_secret_len, uint8_t *req_authenticator,
170     radius_packet_data_t *resp_data)
171 {
172 	radius_packet_t		*packet;
173 	MD5_CTX			context;
174 	uint8_t			*tmp_data;
175 	uint8_t			md5_digest[16]; /* MD5 Digest Length 16 */
176 	uint16_t		declared_len = 0;
177 	size_t			received_len = 0;
178 
179 	struct iovec		iov[1];
180 	struct nmsghdr		msg;
181 
182 	tmp_data = kmem_zalloc(MAX_RAD_PACKET_LEN, KM_SLEEP);
183 	iov[0].iov_base = (char *)tmp_data;
184 	iov[0].iov_len	= MAX_RAD_PACKET_LEN;
185 
186 	bzero(&msg, sizeof (msg));
187 	msg.msg_name		= NULL;
188 	msg.msg_namelen		= 0;
189 	msg.msg_control		= NULL;
190 	msg.msg_controllen	= 0;
191 	msg.msg_flags		= MSG_WAITALL;
192 	msg.msg_iov		= iov;
193 	msg.msg_iovlen		= 1;
194 
195 	received_len = iscsit_net_recvmsg(socket, &msg, RAD_RCV_TIMEOUT);
196 
197 	if (received_len <= (size_t)0) {
198 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
199 		return (RAD_RSP_RCVD_NO_DATA);
200 	}
201 
202 	/*
203 	 * Check if the received packet length is within allowable range.
204 	 * RFC 2865 section 3.
205 	 */
206 	if (received_len < MIN_RAD_PACKET_LEN) {
207 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
208 		return (RAD_RSP_RCVD_PROTOCOL_ERR);
209 	} else if (received_len > MAX_RAD_PACKET_LEN) {
210 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
211 		return (RAD_RSP_RCVD_PROTOCOL_ERR);
212 	}
213 
214 	packet = (radius_packet_t *)tmp_data;
215 	bcopy(packet->length, &declared_len, sizeof (ushort_t));
216 	declared_len = ntohs(declared_len);
217 
218 	/*
219 	 * Discard packet with received length shorter than declared
220 	 * length. RFC 2865 section 3.
221 	 */
222 	if (received_len < declared_len) {
223 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
224 		return (RAD_RSP_RCVD_PROTOCOL_ERR);
225 	}
226 
227 	/*
228 	 * Check if the declared packet length is within allowable range.
229 	 * RFC 2865 section 3.
230 	 */
231 	if (declared_len < MIN_RAD_PACKET_LEN) {
232 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
233 		return (RAD_RSP_RCVD_PROTOCOL_ERR);
234 	} else if (declared_len > MAX_RAD_PACKET_LEN) {
235 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
236 		return (RAD_RSP_RCVD_PROTOCOL_ERR);
237 	}
238 
239 	/*
240 	 * Authenticate the incoming packet, using the following algorithm
241 	 * (RFC 2865 section 3):
242 	 *
243 	 * 	MD5(Code+ID+Length+RequestAuth+Attributes+Secret)
244 	 *
245 	 * Code = RADIUS packet code
246 	 * ID = RADIUS packet identifier
247 	 * Length = Declared length of the packet
248 	 * RequestAuth = The request authenticator
249 	 * Attributes = The response attributes
250 	 * Secret = The shared secret
251 	 */
252 	MD5Init(&context);
253 	bzero(&md5_digest, 16);
254 	MD5Update(&context, &packet->code, 1);
255 	MD5Update(&context, &packet->identifier, 1);
256 	MD5Update(&context, packet->length, 2);
257 	MD5Update(&context, req_authenticator, RAD_AUTHENTICATOR_LEN);
258 
259 	/*
260 	 * Include response attributes only if there is a payload
261 	 * If the received length is greater than the declared length,
262 	 * trust the declared length and shorten the packet (i.e., to
263 	 * treat the octets outside the range of the Length field as
264 	 * padding - RFC 2865 section 3).
265 	 */
266 	if (declared_len > RAD_PACKET_HDR_LEN) {
267 		/* Response Attributes */
268 		MD5Update(&context, packet->data,
269 		    declared_len - RAD_PACKET_HDR_LEN);
270 	}
271 	MD5Update(&context, shared_secret, shared_secret_len);
272 	MD5Final(md5_digest, &context);
273 
274 	if (bcmp(md5_digest, packet->authenticator, RAD_AUTHENTICATOR_LEN)
275 	    != 0) {
276 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
277 		return (RAD_RSP_RCVD_AUTH_FAILED);
278 	}
279 
280 	/*
281 	 * Annotate the RADIUS packet data with the data we received from
282 	 * the server.
283 	 */
284 	resp_data->code = packet->code;
285 	resp_data->identifier = packet->identifier;
286 
287 	kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
288 	return (RAD_RSP_RCVD_SUCCESS);
289 }
290 
291 /*
292  * encode_chap_password -
293  *
294  * Encode a CHAP-Password attribute. This function basically prepends
295  * the identifier in front of chap_passwd and copy the results to
296  * *result.
297  */
298 static void
299 encode_chap_password(int identifier, int chap_passwd_len,
300     uint8_t *chap_passwd, uint8_t *result)
301 {
302 	result[0] = (uint8_t)identifier;
303 	bcopy(chap_passwd, &result[1], chap_passwd_len);
304 }
305 /*
306  * iscsi_net_recvmsg - receive message on socket
307  */
308 /* ARGSUSED */
309 static size_t
310 iscsit_net_recvmsg(ksocket_t socket, struct msghdr *msg, int timeout)
311 {
312 	int		prflag	= msg->msg_flags;
313 	size_t		recv	= 0;
314 	struct sockaddr_in6 	l_addr, f_addr;
315 	socklen_t	l_addrlen;
316 	socklen_t	f_addrlen;
317 
318 	bzero(&l_addr, sizeof (struct sockaddr_in6));
319 	bzero(&f_addr, sizeof (struct sockaddr_in6));
320 	l_addrlen = sizeof (struct sockaddr_in6);
321 	f_addrlen = sizeof (struct sockaddr_in6);
322 	/* If timeout requested on receive */
323 	if (timeout > 0) {
324 		boolean_t   loopback = B_FALSE;
325 		(void) ksocket_getsockname(socket, (struct sockaddr *)(&l_addr),
326 		    &l_addrlen, CRED());
327 		(void) ksocket_getpeername(socket, (struct sockaddr *)(&f_addr),
328 		    &f_addrlen, CRED());
329 
330 		/* And this isn't a loopback connection */
331 		if (((struct sockaddr *)(&l_addr))->sa_family == AF_INET) {
332 			struct sockaddr_in *lin = (struct sockaddr_in *)
333 			    ((void *)(&l_addr));
334 			struct sockaddr_in *fin = (struct sockaddr_in *)
335 			    ((void *)(&f_addr));
336 
337 			if ((lin->sin_family == fin->sin_family) &&
338 			    (bcmp(&lin->sin_addr, &fin->sin_addr,
339 			    sizeof (struct in_addr)) == 0)) {
340 				loopback = B_TRUE;
341 			}
342 		} else {
343 			struct sockaddr_in6 *lin6 = (struct sockaddr_in6 *)
344 			    ((void *)(&l_addr));
345 			struct sockaddr_in6 *fin6 = (struct sockaddr_in6 *)
346 			    ((void *)(&f_addr));
347 
348 			if ((lin6->sin6_family == fin6->sin6_family) &&
349 			    (bcmp(&lin6->sin6_addr, &fin6->sin6_addr,
350 			    sizeof (struct in6_addr)) == 0)) {
351 				loopback = B_TRUE;
352 			}
353 		}
354 		if (loopback == B_FALSE) {
355 			struct timeval tl;
356 			tl.tv_sec = timeout;
357 			tl.tv_usec = 0;
358 			/* Set recv timeout */
359 			if (ksocket_setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO,
360 			    &tl, sizeof (struct timeval), CRED()))
361 				return (0);
362 		}
363 	}
364 
365 	/*
366 	 * Receive the requested data.  Block until all
367 	 * data is received or timeout.
368 	 *
369 	 * resid occurs only when the connection is
370 	 * disconnected.  In that case it will return
371 	 * the amount of data that was not received.
372 	 * In general this is the total amount we
373 	 * requested.
374 	 */
375 	(void) ksocket_recvmsg(socket, msg, prflag, &recv, CRED());
376 	return (recv);
377 }
378