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