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