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 2009 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
snd_radius_request(void * socket,iscsi_ipaddr_t rsvr_ip_addr,uint32_t rsvr_port,radius_packet_data_t * req_data)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
rcv_radius_response(void * socket,uint8_t * shared_secret,uint32_t shared_secret_len,uint8_t * req_authenticator,radius_packet_data_t * resp_data)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
177 tmp_data = kmem_zalloc(MAX_RAD_PACKET_LEN, KM_SLEEP);
178
179 iov[0].iov_base = (char *)tmp_data;
180 iov[0].iov_len = MAX_RAD_PACKET_LEN;
181
182 bzero(&msg, sizeof (msg));
183 msg.msg_name = NULL;
184 msg.msg_namelen = 0;
185 msg.msg_control = NULL;
186 msg.msg_controllen = 0;
187 msg.msg_flags = MSG_WAITALL;
188 msg.msg_iov = iov;
189 msg.msg_iovlen = 1;
190
191 rcv_len = iscsi_net->recvmsg(socket, &msg, RAD_RCV_TIMEOUT);
192 if (rcv_len == 0) {
193 kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
194 return (RAD_RSP_RCVD_NO_DATA);
195 }
196
197 DTRACE_PROBE1(rcv_rad_resp_summary, int, rcv_len);
198
199 packet = (radius_packet_t *)tmp_data;
200 bcopy(packet->length, &len, sizeof (ushort_t));
201 declared_len = ntohs(len);
202
203 DTRACE_PROBE1(rcv_rad_resp_data, uint16_t, declared_len);
204
205 /*
206 * Check if the received packet length is within allowable range.
207 * RFC 2865 section 3.
208 */
209 if (rcv_len < MIN_RAD_PACKET_LEN) {
210 kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
211 return (RAD_RSP_RCVD_PROTOCOL_ERR);
212 } else if (rcv_len > MAX_RAD_PACKET_LEN) {
213 kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
214 return (RAD_RSP_RCVD_PROTOCOL_ERR);
215 }
216
217 /*
218 * Check if the declared packet length is within allowable range.
219 * RFC 2865 section 3.
220 */
221 if (declared_len < MIN_RAD_PACKET_LEN) {
222 kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
223 return (RAD_RSP_RCVD_PROTOCOL_ERR);
224 } else if (declared_len > MAX_RAD_PACKET_LEN) {
225 kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
226 return (RAD_RSP_RCVD_PROTOCOL_ERR);
227 }
228
229 /*
230 * Discard packet with received length shorter than declared
231 * length. RFC 2865 section 3.
232 */
233 if (rcv_len < declared_len) {
234 kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
235 return (RAD_RSP_RCVD_PROTOCOL_ERR);
236 }
237
238 /*
239 * Authenticate the incoming packet, using the following algorithm
240 * (RFC 2865 section 3):
241 *
242 * MD5(Code+ID+Length+RequestAuth+Attributes+Secret)
243 *
244 * Code = RADIUS packet code
245 * ID = RADIUS packet identifier
246 * Length = Declared length of the packet
247 * RequestAuth = The request authenticator
248 * Attributes = The response attributes
249 * Secret = The shared secret
250 */
251 MD5Init(&context);
252 bzero(&md5_digest, 16);
253 MD5Update(&context, &packet->code, 1);
254 MD5Update(&context, &packet->identifier, 1);
255 MD5Update(&context, packet->length, 2);
256 MD5Update(&context, req_authenticator, RAD_AUTHENTICATOR_LEN);
257 /* Include response attributes only if there is a payload */
258 if (declared_len > RAD_PACKET_HDR_LEN) {
259 /* Response Attributes */
260 MD5Update(&context, packet->data,
261 declared_len - RAD_PACKET_HDR_LEN);
262 }
263 MD5Update(&context, shared_secret, shared_secret_len);
264 MD5Final(md5_digest, &context);
265
266 if (bcmp(md5_digest, packet->authenticator, RAD_AUTHENTICATOR_LEN)
267 != 0) {
268 kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
269 return (RAD_RSP_RCVD_AUTH_FAILED);
270 }
271
272 /*
273 * If the received length is greater than the declared length,
274 * trust the declared length and shorten the packet (i.e., to
275 * treat the octets outside the range of the Length field as
276 * padding - RFC 2865 section 3).
277 */
278 if (rcv_len > declared_len) {
279 /* Clear the padding data. */
280 bzero(tmp_data + declared_len, rcv_len - declared_len);
281 rcv_len = declared_len;
282 }
283
284 /*
285 * Annotate the RADIUS packet data with the data we received from
286 * the server.
287 */
288 resp_data->code = packet->code;
289 resp_data->identifier = packet->identifier;
290
291 kmem_free(tmp_data, MAX_RAD_PACKET_LEN);
292 return (RAD_RSP_RCVD_SUCCESS);
293 }
294
295 /*
296 * encode_chap_password -
297 *
298 * Encode a CHAP-Password attribute. This function basically prepends
299 * the identifier in front of chap_passwd and copy the results to
300 * *result.
301 */
302 static void
encode_chap_password(int identifier,int chap_passwd_len,uint8_t * chap_passwd,uint8_t * result)303 encode_chap_password(int identifier, int chap_passwd_len,
304 uint8_t *chap_passwd, uint8_t *result)
305 {
306 int i;
307 uint8_t *p;
308 uint8_t tmp_result[RAD_CHAP_PASSWD_STR_LEN +
309 RAD_IDENTIFIER_LEN + 1];
310
311 p = tmp_result;
312 *p = identifier; /* Identifier is 1 octet */
313 p++;
314 for (i = 0; i < chap_passwd_len; i++) {
315 *p = chap_passwd[i];
316 p++;
317 }
318
319 bcopy(tmp_result, result,
320 RAD_CHAP_PASSWD_STR_LEN + RAD_IDENTIFIER_LEN);
321 }
322