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
iscsit_snd_radius_request(ksocket_t socket,iscsi_ipaddr_t rsvr_ip_addr,uint32_t rsvr_port,radius_packet_data_t * req_data)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
iscsit_rcv_radius_response(ksocket_t socket,uint8_t * shared_secret,uint32_t shared_secret_len,uint8_t * req_authenticator,radius_packet_data_t * resp_data)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
encode_chap_password(int identifier,int chap_passwd_len,uint8_t * chap_passwd,uint8_t * result)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
iscsit_net_recvmsg(ksocket_t socket,struct msghdr * msg,int timeout)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