Mercurial > illumos > onarm
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 } |