0
|
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 /*
|
|
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
|
24 * Use is subject to license terms.
|
|
25 */
|
|
26
|
|
27 #pragma ident "@(#)radius.c 1.1 06/06/20 SMI"
|
|
28
|
|
29 #include <sys/random.h>
|
|
30 #include <sys/types.h>
|
|
31 #include <sys/stat.h>
|
|
32 #include <fcntl.h>
|
|
33 #include <string.h>
|
|
34 #include <strings.h>
|
|
35 #include <unistd.h>
|
|
36 #include <stdlib.h>
|
|
37
|
|
38 #include <netinet/in.h>
|
|
39 #include <sys/socket.h>
|
|
40
|
|
41 #include <md5.h>
|
|
42 #include "target.h"
|
|
43 #include "radius.h"
|
|
44
|
|
45 /* Forward declaration */
|
|
46
|
|
47 /*
|
|
48 * Encode a CHAP-Password attribute. This function basically prepends
|
|
49 * the identifier in front of chap_passwd and copy the results to
|
|
50 * *result.
|
|
51 */
|
|
52 static
|
|
53 void
|
|
54 encode_chap_password(int identifier,
|
|
55 int chap_passwd_len,
|
|
56 uint8_t *chap_passwd,
|
|
57 uint8_t *result);
|
|
58
|
|
59 int
|
|
60 snd_radius_request(int sd,
|
|
61 iscsi_ipaddr_t rsvr_ip_addr,
|
|
62 uint32_t rsvr_port,
|
|
63 radius_packet_data_t *req_data);
|
|
64
|
|
65 int
|
|
66 rcv_radius_response(int sd,
|
|
67 uint8_t *shared_secret,
|
|
68 uint32_t shared_secret_len,
|
|
69 uint8_t *req_authenticator,
|
|
70 radius_packet_data_t *resp_data);
|
|
71
|
|
72 /*
|
|
73 * Annotate the radius_attr_t objects with authentication data.
|
|
74 */
|
|
75 static
|
|
76 void
|
|
77 set_radius_attrs(radius_packet_data_t *req,
|
|
78 char *target_chap_name,
|
|
79 unsigned char *target_response,
|
|
80 uint32_t responseLength,
|
|
81 uint8_t *challenge,
|
|
82 uint32_t challengeLength);
|
|
83
|
|
84 /*
|
|
85 * See radius_auth.h.
|
|
86 */
|
|
87 /* ARGSUSED */
|
|
88 chap_validation_status_type
|
|
89 radius_chap_validate(char *target_chap_name,
|
|
90 char *initiator_chap_name,
|
|
91 uint8_t *challenge,
|
|
92 uint32_t challengeLength,
|
|
93 uint8_t *target_response,
|
|
94 uint32_t responseLength,
|
|
95 uint8_t identifier,
|
|
96 iscsi_ipaddr_t rad_svr_ip_addr,
|
|
97 uint32_t rad_svr_port,
|
|
98 uint8_t *rad_svr_shared_secret,
|
|
99 uint32_t rad_svr_shared_secret_len)
|
|
100 {
|
|
101 chap_validation_status_type validation_status;
|
|
102 int rcv_status;
|
|
103 int sd;
|
|
104 int rc;
|
|
105 struct sockaddr_in sockaddr;
|
|
106 radius_packet_data_t req;
|
|
107 radius_packet_data_t resp;
|
|
108 MD5_CTX context;
|
|
109 uint8_t md5_digest[16]; /* MD5 digest length 16 */
|
|
110 uint8_t random_number[16];
|
|
111 int fd;
|
|
112
|
|
113 if (rad_svr_shared_secret_len == 0) {
|
|
114 /* The secret must not be empty (section 3, RFC 2865) */
|
|
115 return (CHAP_VALIDATION_BAD_RADIUS_SECRET);
|
|
116 }
|
|
117
|
|
118 bzero(&req, sizeof (radius_packet_data_t));
|
|
119
|
|
120 req.identifier = identifier;
|
|
121 req.code = RAD_ACCESS_REQ;
|
|
122 set_radius_attrs(&req,
|
|
123 target_chap_name,
|
|
124 target_response,
|
|
125 responseLength,
|
|
126 challenge,
|
|
127 challengeLength);
|
|
128
|
|
129 /* Prepare the request authenticator */
|
|
130 MD5Init(&context);
|
|
131 bzero(&md5_digest, 16);
|
|
132 /* First, the shared secret */
|
|
133 MD5Update(&context, rad_svr_shared_secret, rad_svr_shared_secret_len);
|
|
134 /* Then a unique number - use a random number */
|
|
135 fd = open("/dev/random", O_RDONLY);
|
|
136 if (fd == -1)
|
|
137 return (CHAP_VALIDATION_INTERNAL_ERROR);
|
|
138 (void) read(fd, &random_number, sizeof (random_number));
|
|
139 (void) close(fd);
|
|
140 MD5Update(&context, random_number, sizeof (random_number));
|
|
141 MD5Final(md5_digest, &context);
|
|
142 bcopy(md5_digest, &req.authenticator, RAD_AUTHENTICATOR_LEN);
|
|
143
|
|
144 /* Create UDP socket */
|
|
145 sd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
146 if (sd < 0) {
|
|
147 return (CHAP_VALIDATION_RADIUS_ACCESS_ERROR);
|
|
148 }
|
|
149 sockaddr.sin_family = AF_INET;
|
|
150 sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
151 sockaddr.sin_port = htons(0);
|
|
152 rc = bind(sd, (struct sockaddr *)&sockaddr, sizeof (sockaddr));
|
|
153 if (rc < 0) {
|
|
154 return (CHAP_VALIDATION_RADIUS_ACCESS_ERROR);
|
|
155 }
|
|
156
|
|
157 /* Send the authentication access request to the RADIUS server */
|
|
158 if (snd_radius_request(sd,
|
|
159 rad_svr_ip_addr,
|
|
160 rad_svr_port,
|
|
161 &req) == -1) {
|
|
162 (void) close(sd);
|
|
163 return (CHAP_VALIDATION_RADIUS_ACCESS_ERROR);
|
|
164 }
|
|
165
|
|
166 bzero(&resp, sizeof (radius_packet_data_t));
|
|
167 /* Analyze the response coming through from the same socket. */
|
|
168 rcv_status = rcv_radius_response(sd,
|
|
169 rad_svr_shared_secret,
|
|
170 rad_svr_shared_secret_len,
|
|
171 req.authenticator, &resp);
|
|
172 if (rcv_status == RAD_RSP_RCVD_SUCCESS) {
|
|
173 if (resp.code == RAD_ACCESS_ACPT) {
|
|
174 validation_status = CHAP_VALIDATION_PASSED;
|
|
175 } else if (resp.code == RAD_ACCESS_REJ) {
|
|
176 validation_status = CHAP_VALIDATION_INVALID_RESPONSE;
|
|
177 } else {
|
|
178 validation_status =
|
|
179 CHAP_VALIDATION_UNKNOWN_RADIUS_CODE;
|
|
180 }
|
|
181 } else if (rcv_status == RAD_RSP_RCVD_AUTH_FAILED) {
|
|
182 validation_status = CHAP_VALIDATION_BAD_RADIUS_SECRET;
|
|
183 } else {
|
|
184 validation_status = CHAP_VALIDATION_RADIUS_ACCESS_ERROR;
|
|
185 }
|
|
186
|
|
187 (void) close(sd);
|
|
188 return (validation_status);
|
|
189 }
|
|
190
|
|
191 /* See forward declaration. */
|
|
192 static void
|
|
193 set_radius_attrs(radius_packet_data_t *req,
|
|
194 char *target_chap_name,
|
|
195 unsigned char *target_response,
|
|
196 uint32_t responseLength,
|
|
197 uint8_t *challenge,
|
|
198 uint32_t challengeLength)
|
|
199 {
|
|
200 req->attrs[0].attr_type_code = RAD_USER_NAME;
|
|
201 (void) strncpy((char *)req->attrs[0].attr_value,
|
|
202 (const char *)target_chap_name,
|
|
203 strlen(target_chap_name));
|
|
204 req->attrs[0].attr_value_len = strlen(target_chap_name);
|
|
205
|
|
206 req->attrs[1].attr_type_code = RAD_CHAP_PASSWORD;
|
|
207 bcopy(target_response,
|
|
208 (char *)req->attrs[1].attr_value,
|
|
209 min(responseLength, sizeof (req->attrs[1].attr_value)));
|
|
210 /* A target response is an MD5 hash thus its length has to be 16. */
|
|
211 req->attrs[1].attr_value_len = responseLength;
|
|
212
|
|
213 req->attrs[2].attr_type_code = RAD_CHAP_CHALLENGE;
|
|
214 bcopy(challenge,
|
|
215 (char *)req->attrs[2].attr_value,
|
|
216 min(challengeLength, sizeof (req->attrs[2].attr_value)));
|
|
217 req->attrs[2].attr_value_len = challengeLength;
|
|
218
|
|
219 /* 3 attributes associated with each RADIUS packet. */
|
|
220 req->num_of_attrs = 3;
|
|
221 }
|
|
222
|
|
223 /*
|
|
224 * See radius_packet.h.
|
|
225 */
|
|
226 int
|
|
227 snd_radius_request(int sd,
|
|
228 iscsi_ipaddr_t rsvr_ip_addr,
|
|
229 uint32_t rsvr_port,
|
|
230 radius_packet_data_t *req_data)
|
|
231 {
|
|
232 int i; /* Loop counter. */
|
|
233 int data_len;
|
|
234 int len;
|
|
235 ushort_t total_length; /* Has to be 2 octets in size */
|
|
236 uint8_t *ptr; /* Pointer to RADIUS packet data */
|
|
237 uint8_t *length_ptr; /* Points to the Length field of the */
|
|
238 /* packet. */
|
|
239 uint8_t *data; /* RADIUS data to be sent */
|
|
240 radius_attr_t *req_attr; /* Request attributes */
|
|
241 radius_packet_t *packet; /* Outbound RADIUS packet */
|
|
242 union {
|
|
243 struct sockaddr_in s_in4;
|
|
244 struct sockaddr_in6 s_in6;
|
|
245 } sa_rsvr; /* Socket address of the server */
|
|
246
|
|
247 /*
|
|
248 * Create a RADIUS packet with minimal length for now.
|
|
249 */
|
|
250 total_length = MIN_RAD_PACKET_LEN;
|
|
251 data = (uint8_t *)malloc(MAX_RAD_PACKET_LEN);
|
|
252 packet = (radius_packet_t *)data;
|
|
253 packet->code = req_data->code;
|
|
254 packet->identifier = req_data->identifier;
|
|
255 bcopy(req_data->authenticator, packet->authenticator,
|
|
256 RAD_AUTHENTICATOR_LEN);
|
|
257 ptr = packet->data;
|
|
258
|
|
259 /* Loop over all attributes of the request. */
|
|
260 for (i = 0; i < req_data->num_of_attrs; i++) {
|
|
261 if (total_length > MAX_RAD_PACKET_LEN) {
|
|
262 /* The packet has exceed its maximum size. */
|
|
263 free(data);
|
|
264 return (-1);
|
|
265 }
|
|
266
|
|
267 req_attr = &req_data->attrs[i];
|
|
268 *ptr++ = (req_attr->attr_type_code & 0xFF);
|
|
269 length_ptr = ptr;
|
|
270 /* Length is 2 octets - RFC 2865 section 3 */
|
|
271 *ptr++ = 2;
|
|
272 total_length += 2;
|
|
273
|
|
274 /* If the attribute is CHAP-Password, encode it. */
|
|
275 if (req_attr->attr_type_code == RAD_CHAP_PASSWORD) {
|
|
276 /*
|
|
277 * Identifier plus CHAP response. RFC 2865
|
|
278 * section 5.3.
|
|
279 */
|
|
280 uint8_t encoded_chap_passwd[RAD_CHAP_PASSWD_STR_LEN +
|
|
281 RAD_IDENTIFIER_LEN +
|
|
282 1];
|
|
283 encode_chap_password
|
|
284 (req_data->identifier,
|
|
285 req_attr->attr_value_len,
|
|
286 req_attr->attr_value,
|
|
287 encoded_chap_passwd);
|
|
288
|
|
289 req_attr->attr_value_len = RAD_CHAP_PASSWD_STR_LEN +
|
|
290 RAD_IDENTIFIER_LEN;
|
|
291
|
|
292 bcopy(encoded_chap_passwd,
|
|
293 req_attr->attr_value,
|
|
294 req_attr->attr_value_len);
|
|
295 }
|
|
296
|
|
297 len = req_attr->attr_value_len;
|
|
298 *length_ptr += len;
|
|
299
|
|
300 bcopy(req_attr->attr_value, ptr, req_attr->attr_value_len);
|
|
301 ptr += req_attr->attr_value_len;
|
|
302
|
|
303 total_length += len;
|
|
304 } /* Done looping over all attributes */
|
|
305
|
|
306 data_len = total_length;
|
|
307 total_length = htons(total_length);
|
|
308 bcopy(&total_length, packet->length, sizeof (ushort_t));
|
|
309
|
|
310 /*
|
|
311 * Send the packet to the RADIUS server.
|
|
312 */
|
|
313 bzero((char *)&sa_rsvr, sizeof (sa_rsvr));
|
|
314 if (rsvr_ip_addr.i_insize == sizeof (in_addr_t)) {
|
|
315 int ret;
|
|
316
|
|
317 /* IPv4 */
|
|
318 sa_rsvr.s_in4.sin_family = AF_INET;
|
|
319 sa_rsvr.s_in4.sin_addr.s_addr =
|
|
320 rsvr_ip_addr.i_addr.in4.s_addr;
|
|
321 /*
|
|
322 * sin_port is of type u_short (or ushort_t - POSIX compliant).
|
|
323 */
|
|
324 sa_rsvr.s_in4.sin_port = htons((ushort_t)rsvr_port);
|
|
325
|
|
326 ret = sendto(sd, data, data_len, 0,
|
|
327 (struct sockaddr *)&sa_rsvr.s_in4,
|
|
328 sizeof (struct sockaddr_in));
|
|
329 free(data);
|
|
330 return (ret);
|
|
331 } else if (rsvr_ip_addr.i_insize == sizeof (in6_addr_t)) {
|
|
332 /* IPv6 */
|
|
333 sa_rsvr.s_in6.sin6_family = AF_INET6;
|
|
334 bcopy(sa_rsvr.s_in6.sin6_addr.s6_addr,
|
|
335 rsvr_ip_addr.i_addr.in6.s6_addr, 16);
|
|
336 /*
|
|
337 * sin6_port is of type in_port_t (i.e., uint16_t).
|
|
338 */
|
|
339 sa_rsvr.s_in6.sin6_port = htons((in_port_t)rsvr_port);
|
|
340
|
|
341 free(data);
|
|
342 /* No IPv6 support for now. */
|
|
343 return (-1);
|
|
344 } else {
|
|
345 /* Invalid IP address for RADIUS server. */
|
|
346 free(data);
|
|
347 return (-1);
|
|
348 }
|
|
349 }
|
|
350
|
|
351 /*
|
|
352 * See radius_packet.h.
|
|
353 */
|
|
354 int
|
|
355 rcv_radius_response(int sd,
|
|
356 uint8_t *shared_secret,
|
|
357 uint32_t shared_secret_len,
|
|
358 uint8_t *req_authenticator,
|
|
359 radius_packet_data_t *resp_data)
|
|
360 {
|
|
361 int poll_cnt = 0;
|
|
362 int rcv_len = 0;
|
|
363 radius_packet_t *packet;
|
|
364 MD5_CTX context;
|
|
365 uint8_t *tmp_data;
|
|
366 uint8_t md5_digest[16]; /* MD5 Digest Length 16 */
|
|
367 uint16_t declared_len = 0;
|
|
368 ushort_t len;
|
|
369
|
|
370 fd_set fdset;
|
|
371 struct timeval timeout;
|
|
372
|
|
373 tmp_data = (uint8_t *)malloc(MAX_RAD_PACKET_LEN);
|
|
374
|
|
375 /*
|
|
376 * Poll and receive RADIUS packet.
|
|
377 */
|
|
378 poll_cnt = 0;
|
|
379 do {
|
|
380 timeout.tv_sec = RAD_RCV_TIMEOUT;
|
|
381 timeout.tv_usec = 0;
|
|
382
|
|
383 FD_ZERO(&fdset);
|
|
384 FD_SET(sd, &fdset);
|
|
385
|
|
386 if (select(sd+1, &fdset, NULL, NULL, &timeout) < 0) {
|
|
387 free(tmp_data);
|
|
388 return (RAD_RSP_RCVD_PROTOCOL_ERR);
|
|
389 }
|
|
390
|
|
391 if (FD_ISSET(sd, &fdset)) {
|
|
392 rcv_len = recv(sd, tmp_data, MAX_RAD_PACKET_LEN, 0);
|
|
393 break;
|
|
394 } else {
|
|
395 poll_cnt++;
|
|
396 }
|
|
397
|
|
398 } while (poll_cnt < RAD_RETRY_MAX);
|
|
399
|
|
400 if (poll_cnt >= RAD_RETRY_MAX) {
|
|
401 free(tmp_data);
|
|
402 return (RAD_RSP_RCVD_TIMEOUT);
|
|
403 }
|
|
404
|
|
405 if (rcv_len < 0) {
|
|
406 /* Socket error. */
|
|
407 free(tmp_data);
|
|
408 return (RAD_RSP_RCVD_PROTOCOL_ERR);
|
|
409 }
|
|
410
|
|
411 packet = (radius_packet_t *)tmp_data;
|
|
412 bcopy(packet->length, &len, sizeof (ushort_t));
|
|
413 declared_len = ntohs(len);
|
|
414
|
|
415 /*
|
|
416 * Check if the received packet length is within allowable range.
|
|
417 * RFC 2865 section 3.
|
|
418 */
|
|
419 if (rcv_len < MIN_RAD_PACKET_LEN) {
|
|
420 free(tmp_data);
|
|
421 return (RAD_RSP_RCVD_PROTOCOL_ERR);
|
|
422 } else if (rcv_len > MAX_RAD_PACKET_LEN) {
|
|
423 free(tmp_data);
|
|
424 return (RAD_RSP_RCVD_PROTOCOL_ERR);
|
|
425 }
|
|
426
|
|
427 /*
|
|
428 * Check if the declared packet length is within allowable range.
|
|
429 * RFC 2865 section 3.
|
|
430 */
|
|
431 if (declared_len < MIN_RAD_PACKET_LEN) {
|
|
432 free(tmp_data);
|
|
433 return (RAD_RSP_RCVD_PROTOCOL_ERR);
|
|
434 } else if (declared_len > MAX_RAD_PACKET_LEN) {
|
|
435 free(tmp_data);
|
|
436 return (RAD_RSP_RCVD_PROTOCOL_ERR);
|
|
437 }
|
|
438
|
|
439 /*
|
|
440 * Discard packet with received length shorter than declared
|
|
441 * length. RFC 2865 section 3.
|
|
442 */
|
|
443 if (rcv_len < declared_len) {
|
|
444 free(tmp_data);
|
|
445 return (RAD_RSP_RCVD_PROTOCOL_ERR);
|
|
446 }
|
|
447
|
|
448 /*
|
|
449 * Authenticate the incoming packet, using the following algorithm
|
|
450 * (RFC 2865 section 3):
|
|
451 *
|
|
452 * MD5(Code+ID+Length+RequestAuth+Attributes+Secret)
|
|
453 *
|
|
454 * Code = RADIUS packet code
|
|
455 * ID = RADIUS packet identifier
|
|
456 * Length = Declared length of the packet
|
|
457 * RequestAuth = The request authenticator
|
|
458 * Attributes = The response attributes
|
|
459 * Secret = The shared secret
|
|
460 */
|
|
461 MD5Init(&context);
|
|
462 bzero(&md5_digest, 16);
|
|
463 MD5Update(&context, &packet->code, 1);
|
|
464 MD5Update(&context, &packet->identifier, 1);
|
|
465 MD5Update(&context, packet->length, 2);
|
|
466 MD5Update(&context, req_authenticator, RAD_AUTHENTICATOR_LEN);
|
|
467 /* Include response attributes only if there is a payload */
|
|
468 if (declared_len > RAD_PACKET_HDR_LEN) {
|
|
469 /* Response Attributes */
|
|
470 MD5Update(&context, packet->data,
|
|
471 declared_len - RAD_PACKET_HDR_LEN);
|
|
472 }
|
|
473 MD5Update(&context, shared_secret, shared_secret_len);
|
|
474 MD5Final(md5_digest, &context);
|
|
475
|
|
476 if (bcmp(md5_digest, packet->authenticator, RAD_AUTHENTICATOR_LEN)
|
|
477 != 0) {
|
|
478 free(tmp_data);
|
|
479 return (RAD_RSP_RCVD_AUTH_FAILED);
|
|
480 }
|
|
481
|
|
482 /*
|
|
483 * If the received length is greater than the declared length,
|
|
484 * trust the declared length and shorten the packet (i.e., to
|
|
485 * treat the octets outside the range of the Length field as
|
|
486 * padding - RFC 2865 section 3).
|
|
487 */
|
|
488 if (rcv_len > declared_len) {
|
|
489 /* Clear the padding data. */
|
|
490 bzero(tmp_data + declared_len, rcv_len - declared_len);
|
|
491 rcv_len = declared_len;
|
|
492 }
|
|
493
|
|
494 /*
|
|
495 * Annotate the RADIUS packet data with the data we received from
|
|
496 * the server.
|
|
497 */
|
|
498 resp_data->code = packet->code;
|
|
499 resp_data->identifier = packet->identifier;
|
|
500
|
|
501 free(tmp_data);
|
|
502 return (RAD_RSP_RCVD_SUCCESS);
|
|
503 }
|
|
504
|
|
505 static
|
|
506 void
|
|
507 encode_chap_password(int identifier,
|
|
508 int chap_passwd_len,
|
|
509 uint8_t *chap_passwd,
|
|
510 uint8_t *result)
|
|
511 {
|
|
512 result[0] = (uint8_t)identifier;
|
|
513 bcopy(chap_passwd, &result[1], chap_passwd_len);
|
|
514 }
|