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 "iscsi.h"
27 #include <sys/scsi/adapters/iscsi_if.h>
28 #include "radius_packet.h"
29 #include "radius_protocol.h"
30 #include <sys/int_types.h>
31 #include <sys/socket.h>
32 #include <sys/types.h>
33 #include <sys/sunddi.h>
34 
35 static void encode_chap_password(int identifier, int chap_passwd_len,
36     uint8_t *chap_passwd, uint8_t *result);
37 
38 /*
39  * See radius_packet.h.
40  */
41 int
42 snd_radius_request(void *socket, iscsi_ipaddr_t rsvr_ip_addr,
43     uint32_t rsvr_port, radius_packet_data_t *req_data)
44 {
45 	int		i;		/* Loop counter. */
46 	int		data_len;
47 	int		len;
48 	ushort_t	total_length;	/* Has to be 2 octets in size */
49 	uint8_t		*ptr;		/* Pointer to RADIUS packet data */
50 	uint8_t		*length_ptr;	/* Points to the Length field of the */
51 					/* packet. */
52 	uint8_t		*data;		/* RADIUS data to be sent */
53 	radius_attr_t	*req_attr;	/* Request attributes */
54 	radius_packet_t	*packet;	/* Outbound RADIUS packet */
55 	union {
56 		struct sockaddr_in s_in4;
57 		struct sockaddr_in6 s_in6;
58 	} sa_rsvr;			/* Socket address of the server */
59 	struct nmsghdr	msg;
60 	struct iovec	iov[1];
61 
62 	/*
63 	 * Create a RADIUS packet with minimal length for now.
64 	 */
65 	total_length = MIN_RAD_PACKET_LEN;
66 	data = kmem_zalloc(MAX_RAD_PACKET_LEN, KM_SLEEP);
67 	packet = (radius_packet_t *)data;
68 	packet->code = req_data->code;
69 	packet->identifier = req_data->identifier;
70 	bcopy(req_data->authenticator, packet->authenticator,
71 	    RAD_AUTHENTICATOR_LEN);
72 	ptr = packet->data;
73 
74 	/* Loop over all attributes of the request. */
75 	for (i = 0; i < req_data->num_of_attrs; i++) {
76 		if (total_length > MAX_RAD_PACKET_LEN) {
77 			/* The packet has exceed its maximum size. */
78 			kmem_free(data, MAX_RAD_PACKET_LEN);
79 			return (-1);
80 		}
81 
82 		req_attr = &req_data->attrs[i];
83 		*ptr++ = (req_attr->attr_type_code & 0xFF);
84 		length_ptr = ptr;
85 		/* Length is 2 octets - RFC 2865 section 3 */
86 		*ptr++ = 2;
87 		total_length += 2;
88 
89 		/* If the attribute is CHAP-Password, encode it. */
90 		if (req_attr->attr_type_code == RAD_CHAP_PASSWORD) {
91 			/*
92 			 * Identifier plus CHAP response. RFC 2865
93 			 * section 5.3.
94 			 */
95 			uint8_t encoded_chap_passwd[RAD_CHAP_PASSWD_STR_LEN +
96 			    RAD_IDENTIFIER_LEN + 1];
97 			encode_chap_password
98 			    (req_data->identifier,
99 			    req_attr->attr_value_len,
100 			    req_attr->attr_value,
101 			    encoded_chap_passwd);
102 
103 			req_attr->attr_value_len = RAD_CHAP_PASSWD_STR_LEN +
104 			    RAD_IDENTIFIER_LEN;
105 
106 			bcopy(encoded_chap_passwd,
107 			    req_attr->attr_value,
108 			    req_attr->attr_value_len);
109 		}
110 
111 		len = req_attr->attr_value_len;
112 		*length_ptr += len;
113 
114 		bcopy(req_attr->attr_value, ptr, req_attr->attr_value_len);
115 		ptr += req_attr->attr_value_len;
116 
117 		total_length += len;
118 	} /* Done looping over all attributes */
119 
120 	data_len = total_length;
121 	total_length = htons(total_length);
122 	bcopy(&total_length, packet->length, sizeof (ushort_t));
123 
124 	/*
125 	 * Send the packet to the RADIUS server.
126 	 */
127 	bzero((char *)&sa_rsvr, sizeof (sa_rsvr));
128 	if (rsvr_ip_addr.i_insize == sizeof (in_addr_t)) {
129 		int recv_len;
130 
131 		/* IPv4 */
132 		sa_rsvr.s_in4.sin_family = AF_INET;
133 		sa_rsvr.s_in4.sin_addr.s_addr =
134 		    rsvr_ip_addr.i_addr.in4.s_addr;
135 		sa_rsvr.s_in4.sin_port = htons((ushort_t)rsvr_port);
136 
137 		iov[0].iov_base = (char *)data;
138 		iov[0].iov_len = data_len;
139 
140 		bzero(&msg, sizeof (msg));
141 		msg.msg_name	    = (struct sockaddr *)&sa_rsvr.s_in4;
142 		msg.msg_namelen	    = sizeof (struct sockaddr_in);
143 		msg.msg_iov	    = iov;
144 		msg.msg_iovlen	    = 1;
145 
146 		recv_len = iscsi_net->sendmsg(socket, &msg);
147 		kmem_free(data, MAX_RAD_PACKET_LEN);
148 		return (recv_len == data_len ? 0 : -1);
149 	} else if (rsvr_ip_addr.i_insize == sizeof (in6_addr_t)) {
150 		/* No IPv6 support for now. */
151 		return (-1);
152 	} else {
153 		/* Invalid IP address for RADIUS server. */
154 		kmem_free(data, MAX_RAD_PACKET_LEN);
155 		return (-1);
156 	}
157 }
158 
159 /*
160  * See radius_packet.h.
161  */
162 int
163 rcv_radius_response(void *socket, uint8_t *shared_secret,
164     uint32_t shared_secret_len, uint8_t *req_authenticator,
165     radius_packet_data_t *resp_data)
166 {
167 	int			rcv_len = 0;
168 	radius_packet_t		*packet;
169 	MD5_CTX			context;
170 	uint8_t			*tmp_data;
171 	uint8_t			md5_digest[16]; /* MD5 Digest Length 16 */
172 	uint16_t		declared_len = 0;
173 	ushort_t		len;
174 	struct nmsghdr		msg;
175 	struct iovec		iov[1];
176 	struct sonode		*so = NULL;
177 	int			ret = 0;
178 
179 	tmp_data = kmem_zalloc(MAX_RAD_PACKET_LEN, KM_SLEEP);
180 
181 	iov[0].iov_base	    = (char *)tmp_data;
182 	iov[0].iov_len	    = MAX_RAD_PACKET_LEN;
183 
184 	bzero(&msg, sizeof (msg));
185 	msg.msg_name	    = NULL;
186 	msg.msg_namelen	    = 0;
187 	msg.msg_control	    = NULL;
188 	msg.msg_controllen  = 0;
189 	msg.msg_flags	    = MSG_WAITALL;
190 	msg.msg_iov	    = iov;
191 	msg.msg_iovlen	    = 1;
192 
193 	/*
194 	 * Convert the socket end point to a device stream. This can
195 	 * make our stream function kstrgetmsg() get the udp data
196 	 * during polling.
197 	 */
198 	so = (struct sonode *)socket;
199 	(void) VOP_IOCTL(SOTOV(so), I_POP, 0, FKIOCTL, CRED(), &ret, NULL);
200 	if (ret != 0) {
201 		cmn_err(CE_NOTE, "iscsi convertion failed to change socket end"
202 		    "point to device stream");
203 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
204 		return (RAD_RSP_RCVD_NO_DATA);
205 	}
206 	rcv_len = iscsi_net->recvmsg(socket, &msg, RAD_RCV_TIMEOUT);
207 	if (rcv_len == 0) {
208 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
209 		return (RAD_RSP_RCVD_NO_DATA);
210 	}
211 
212 	DTRACE_PROBE1(rcv_rad_resp_summary, int, rcv_len);
213 
214 	packet = (radius_packet_t *)tmp_data;
215 	bcopy(packet->length, &len, sizeof (ushort_t));
216 	declared_len = ntohs(len);
217 
218 	DTRACE_PROBE1(rcv_rad_resp_data, uint16_t, declared_len);
219 
220 	/*
221 	 * Check if the received packet length is within allowable range.
222 	 * RFC 2865 section 3.
223 	 */
224 	if (rcv_len < MIN_RAD_PACKET_LEN) {
225 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
226 		return (RAD_RSP_RCVD_PROTOCOL_ERR);
227 	} else if (rcv_len > MAX_RAD_PACKET_LEN) {
228 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
229 		return (RAD_RSP_RCVD_PROTOCOL_ERR);
230 	}
231 
232 	/*
233 	 * Check if the declared packet length is within allowable range.
234 	 * RFC 2865 section 3.
235 	 */
236 	if (declared_len < MIN_RAD_PACKET_LEN) {
237 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
238 		return (RAD_RSP_RCVD_PROTOCOL_ERR);
239 	} else if (declared_len > MAX_RAD_PACKET_LEN) {
240 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
241 		return (RAD_RSP_RCVD_PROTOCOL_ERR);
242 	}
243 
244 	/*
245 	 * Discard packet with received length shorter than declared
246 	 * length. RFC 2865 section 3.
247 	 */
248 	if (rcv_len < declared_len) {
249 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
250 		return (RAD_RSP_RCVD_PROTOCOL_ERR);
251 	}
252 
253 	/*
254 	 * Authenticate the incoming packet, using the following algorithm
255 	 * (RFC 2865 section 3):
256 	 *
257 	 * 	MD5(Code+ID+Length+RequestAuth+Attributes+Secret)
258 	 *
259 	 * Code = RADIUS packet code
260 	 * ID = RADIUS packet identifier
261 	 * Length = Declared length of the packet
262 	 * RequestAuth = The request authenticator
263 	 * Attributes = The response attributes
264 	 * Secret = The shared secret
265 	 */
266 	MD5Init(&context);
267 	bzero(&md5_digest, 16);
268 	MD5Update(&context, &packet->code, 1);
269 	MD5Update(&context, &packet->identifier, 1);
270 	MD5Update(&context, packet->length, 2);
271 	MD5Update(&context, req_authenticator, RAD_AUTHENTICATOR_LEN);
272 	/* Include response attributes only if there is a payload */
273 	if (declared_len > RAD_PACKET_HDR_LEN) {
274 		/* Response Attributes */
275 		MD5Update(&context, packet->data,
276 		    declared_len - RAD_PACKET_HDR_LEN);
277 	}
278 	MD5Update(&context, shared_secret, shared_secret_len);
279 	MD5Final(md5_digest, &context);
280 
281 	if (bcmp(md5_digest, packet->authenticator, RAD_AUTHENTICATOR_LEN)
282 	    != 0) {
283 		kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
284 		return (RAD_RSP_RCVD_AUTH_FAILED);
285 	}
286 
287 	/*
288 	 * If the received length is greater than the declared length,
289 	 * trust the declared length and shorten the packet (i.e., to
290 	 * treat the octets outside the range of the Length field as
291 	 * padding - RFC 2865 section 3).
292 	 */
293 	if (rcv_len > declared_len) {
294 		/* Clear the padding data. */
295 		bzero(tmp_data + declared_len, rcv_len - declared_len);
296 		rcv_len = declared_len;
297 	}
298 
299 	/*
300 	 * Annotate the RADIUS packet data with the data we received from
301 	 * the server.
302 	 */
303 	resp_data->code = packet->code;
304 	resp_data->identifier = packet->identifier;
305 
306 	kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
307 	return (RAD_RSP_RCVD_SUCCESS);
308 }
309 
310 /*
311  * encode_chap_password -
312  *
313  * Encode a CHAP-Password attribute. This function basically prepends
314  * the identifier in front of chap_passwd and copy the results to
315  * *result.
316  */
317 static void
318 encode_chap_password(int identifier, int chap_passwd_len,
319     uint8_t *chap_passwd, uint8_t *result)
320 {
321 	int i;
322 	uint8_t *p;
323 	uint8_t tmp_result[RAD_CHAP_PASSWD_STR_LEN +
324 	    RAD_IDENTIFIER_LEN + 1];
325 
326 	p = tmp_result;
327 	*p = identifier; /* Identifier is 1 octet */
328 	p++;
329 	for (i = 0; i < chap_passwd_len; i++) {
330 		*p = chap_passwd[i];
331 		p++;
332 	}
333 
334 	bcopy(tmp_result, result,
335 	    RAD_CHAP_PASSWD_STR_LEN + RAD_IDENTIFIER_LEN);
336 }
337