comparison usr/src/cmd/iscsi/iscsitgtd/radius.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
comparison
equal deleted inserted replaced
-1:000000000000 0:c9caec207d52
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 }