Mercurial > hg > freeDiameter
comparison extensions/app_radgw/rgw_clients.c @ 516:1c2f5ee38039
Allow RADIUS Proxies with the app_radgw extension
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Fri, 27 Aug 2010 10:59:51 +0900 |
parents | d4fc98a3b79c |
children | 3f43713be92d |
comparison
equal
deleted
inserted
replaced
515:b9167b4de7dc | 516:1c2f5ee38039 |
---|---|
37 | 37 |
38 /* Probably some changes are needed to support RADIUS Proxies */ | 38 /* Probably some changes are needed to support RADIUS Proxies */ |
39 | 39 |
40 #include "rgw.h" | 40 #include "rgw.h" |
41 | 41 |
42 #define REVERSE_DNS_SIZE_MAX 512 /* length of our buffer for reverse DNS */ | |
43 | |
42 /* Ordered lists of clients. The order relationship is a memcmp on the address zone. | 44 /* Ordered lists of clients. The order relationship is a memcmp on the address zone. |
43 For same addresses, the port is compared. | 45 For same addresses, the port is compared. |
44 The same address cannot be added twice, once with a 0-port and once with another port value. | 46 The same address cannot be added twice, once with a 0-port and once with another port value. |
45 */ | 47 */ |
46 static struct fd_list cli_ip = FD_LIST_INITIALIZER(cli_ip); | 48 static struct fd_list cli_ip = FD_LIST_INITIALIZER(cli_ip); |
64 struct sockaddr_in6 *sin6; | 66 struct sockaddr_in6 *sin6; |
65 }; | 67 }; |
66 | 68 |
67 /* The FQDN, realm, and optional aliases */ | 69 /* The FQDN, realm, and optional aliases */ |
68 int is_local; /* true if the RADIUS client runs on the same host -- we use Diameter Identity in that case */ | 70 int is_local; /* true if the RADIUS client runs on the same host -- we use Diameter Identity in that case */ |
71 enum rgw_cli_type type; /* is it a proxy ? */ | |
69 char *fqdn; | 72 char *fqdn; |
70 size_t fqdn_len; | 73 size_t fqdn_len; |
71 char *realm; | 74 char *realm; |
72 char **aliases; | 75 char **aliases; |
73 size_t aliases_nb; | 76 size_t aliases_nb; |
88 }; | 91 }; |
89 | 92 |
90 | 93 |
91 | 94 |
92 /* create a new rgw_client. the arguments are moved into the structure (to limit malloc & free calls). */ | 95 /* create a new rgw_client. the arguments are moved into the structure (to limit malloc & free calls). */ |
93 static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen ) | 96 static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type ) |
94 { | 97 { |
95 struct rgw_client *tmp = NULL; | 98 struct rgw_client *tmp = NULL; |
96 char buf[255]; | 99 char buf[255]; |
97 int ret; | 100 int ret; |
98 int loc = 0; | 101 int loc = 0; |
114 | 117 |
115 /* Create the new object */ | 118 /* Create the new object */ |
116 CHECK_MALLOC( tmp = malloc(sizeof (struct rgw_client)) ); | 119 CHECK_MALLOC( tmp = malloc(sizeof (struct rgw_client)) ); |
117 memset(tmp, 0, sizeof(struct rgw_client)); | 120 memset(tmp, 0, sizeof(struct rgw_client)); |
118 fd_list_init(&tmp->chain, NULL); | 121 fd_list_init(&tmp->chain, NULL); |
122 | |
123 tmp->type = type; | |
119 | 124 |
120 if (loc) { | 125 if (loc) { |
121 tmp->is_local = 1; | 126 tmp->is_local = 1; |
122 } else { | 127 } else { |
123 /* Copy the fqdn */ | 128 /* Copy the fqdn */ |
228 *key = cli->key.data; | 233 *key = cli->key.data; |
229 *key_len = cli->key.len; | 234 *key_len = cli->key.len; |
230 return 0; | 235 return 0; |
231 } | 236 } |
232 | 237 |
238 int rgw_clients_gettype(struct rgw_client * cli, enum rgw_cli_type *type) | |
239 { | |
240 CHECK_PARAMS( cli && type ); | |
241 *type = cli->type; | |
242 return 0; | |
243 } | |
244 | |
245 | |
233 int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref) | 246 int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref) |
234 { | 247 { |
235 int ret = 0; | 248 int ret = 0; |
236 | 249 |
237 TRACE_ENTRY("%p %p", ip_port, ref); | 250 TRACE_ENTRY("%p %p", ip_port, ref); |
301 return 0; | 314 return 0; |
302 } | 315 } |
303 | 316 |
304 /* Check that the NAS-IP-Adress or NAS-Identifier is coherent with the IP the packet was received from */ | 317 /* Check that the NAS-IP-Adress or NAS-Identifier is coherent with the IP the packet was received from */ |
305 /* Also update the client list of aliases if needed */ | 318 /* Also update the client list of aliases if needed */ |
306 /* NOTE: This function will require changes to allow RADIUS Proxy on the path... */ | 319 /* NOTE: This function does nothing if the client is a RADIUS Proxy... */ |
307 int rgw_clients_check_origin(struct rgw_radius_msg_meta *msg, struct rgw_client *cli) | 320 /* Check if the message has a valid authenticator, and update the meta-data accordingly */ |
321 int rgw_clients_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth) | |
322 { | |
323 unsigned char * key; | |
324 size_t keylen; | |
325 int count; | |
326 | |
327 TRACE_ENTRY("%p %p %p", msg, cli, req_auth); | |
328 | |
329 CHECK_PARAMS(msg && cli); | |
330 | |
331 CHECK_FCT(rgw_clients_getkey(cli, &key, &keylen)); | |
332 | |
333 count = radius_msg_count_attr(&msg->radius, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 0); | |
334 if (count > 1) { | |
335 TRACE_DEBUG(INFO, "Too many Message-Authenticator attributes (%d), discarding message.", count); | |
336 return EINVAL; | |
337 } | |
338 if (count == 0) { | |
339 TRACE_DEBUG(FULL, "Message does not contain a Message-Authenticator attributes."); | |
340 msg->valid_mac = 0; | |
341 } else { | |
342 if (radius_msg_verify_msg_auth( &msg->radius, key, keylen, req_auth )) { | |
343 TRACE_DEBUG(INFO, "Invalid Message-Authenticator received, discarding message."); | |
344 return EINVAL; | |
345 } | |
346 msg->valid_mac = 1; | |
347 } | |
348 | |
349 return 0; | |
350 } | |
351 | |
352 static struct dict_object * cache_orig_host = NULL; | |
353 static struct dict_object * cache_orig_realm = NULL; | |
354 static struct dict_object * cache_route_record = NULL; | |
355 | |
356 int rgw_clients_init(void) | |
357 { | |
358 TRACE_ENTRY(); | |
359 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &cache_orig_host, ENOENT) ); | |
360 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_realm, ENOENT) ); | |
361 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &cache_route_record, ENOENT) ); | |
362 return 0; | |
363 } | |
364 | |
365 | |
366 /* The following function checks if a RADIUS message contains a valid NAS identifier, and initializes an empty Diameter | |
367 message with the appropriate routing information */ | |
368 int rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client * cli, struct msg ** diam) | |
308 { | 369 { |
309 int idx; | 370 int idx; |
371 int valid_nas_info = 0; | |
310 struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL; | 372 struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL; |
311 | 373 char * oh_str = NULL; |
312 TRACE_ENTRY("%p %p", msg, cli); | 374 char * or_str = NULL; |
313 CHECK_PARAMS(msg && cli && !msg->valid_nas_info ); | 375 char * rr_str = NULL; |
314 | 376 char buf[REVERSE_DNS_SIZE_MAX]; /* to store DNS lookups results */ |
377 | |
378 struct avp *avp = NULL; | |
379 union avp_value avp_val; | |
380 | |
381 TRACE_ENTRY("%p %p %p", msg, cli, diam); | |
382 CHECK_PARAMS(msg && cli && diam && (*diam == NULL)); | |
383 | |
315 /* Find the relevant attributes, if any */ | 384 /* Find the relevant attributes, if any */ |
316 for (idx = 0; idx < msg->radius.attr_used; idx++) { | 385 for (idx = 0; idx < msg->radius.attr_used; idx++) { |
317 struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[idx]); | 386 struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[idx]); |
318 size_t attr_len = attr->length - sizeof(struct radius_attr_hdr); | 387 size_t attr_len = attr->length - sizeof(struct radius_attr_hdr); |
319 | 388 |
333 } | 402 } |
334 } | 403 } |
335 | 404 |
336 if (!nas_ip && !nas_ip6 && !nas_id) { | 405 if (!nas_ip && !nas_ip6 && !nas_id) { |
337 TRACE_DEBUG(FULL, "The message does not contain any NAS identification attribute."); | 406 TRACE_DEBUG(FULL, "The message does not contain any NAS identification attribute."); |
338 goto end; | 407 |
408 /* Get information on this peer */ | |
409 CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); | |
410 | |
411 goto diameter; | |
339 } | 412 } |
340 | 413 |
341 /* Check if the message was received from the IP in NAS-IP-Address attribute */ | 414 /* Check if the message was received from the IP in NAS-IP-Address attribute */ |
342 if (nas_ip && (cli->sa->sa_family == AF_INET) && !memcmp(nas_ip+1, &cli->sin->sin_addr, sizeof(struct in_addr))) { | 415 if (nas_ip && (cli->sa->sa_family == AF_INET) && !memcmp(nas_ip+1, &cli->sin->sin_addr, sizeof(struct in_addr))) { |
343 TRACE_DEBUG(FULL, "NAS-IP-Address contains the same address as the message was received from."); | 416 TRACE_DEBUG(FULL, "NAS-IP-Address contains the same address as the message was received from."); |
344 msg->valid_nas_info |= 1; | 417 valid_nas_info |= 1; |
345 } | 418 } |
346 if (nas_ip6 && (cli->sa->sa_family == AF_INET6) && !memcmp(nas_ip6+1, &cli->sin6->sin6_addr, sizeof(struct in6_addr))) { | 419 if (nas_ip6 && (cli->sa->sa_family == AF_INET6) && !memcmp(nas_ip6+1, &cli->sin6->sin6_addr, sizeof(struct in6_addr))) { |
347 TRACE_DEBUG(FULL, "NAS-IPv6-Address contains the same address as the message was received from."); | 420 TRACE_DEBUG(FULL, "NAS-IPv6-Address contains the same address as the message was received from."); |
348 msg->valid_nas_info |= 1; | 421 valid_nas_info |= 2; |
349 } | 422 } |
350 | 423 |
351 /* If these conditions are not met, the message is probably forged (well, this might be false...) */ | 424 |
352 if ((! msg->valid_nas_info) && (nas_ip || nas_ip6)) { | 425 /* |
353 /* | 426 In RADIUS it would be possible for a rogue NAS to forge the NAS-IP- |
354 In RADIUS it would be possible for a rogue NAS to forge the NAS-IP- | 427 Address attribute value. Diameter/RADIUS translation agents MUST |
355 Address attribute value. Diameter/RADIUS translation agents MUST | 428 check a received NAS-IP-Address or NAS-IPv6-Address attribute against |
356 check a received NAS-IP-Address or NAS-IPv6-Address attribute against | 429 the source address of the RADIUS packet. If they do not match and |
357 the source address of the RADIUS packet. If they do not match and | 430 the Diameter/RADIUS translation agent does not know whether the |
358 the Diameter/RADIUS translation agent does not know whether the | 431 packet was sent by a RADIUS proxy or NAS (e.g., no Proxy-State |
359 packet was sent by a RADIUS proxy or NAS (e.g., no Proxy-State | 432 attribute), then by default it is assumed that the source address |
360 attribute), then by default it is assumed that the source address | 433 corresponds to a RADIUS proxy, and that the NAS Address is behind |
361 corresponds to a RADIUS proxy, and that the NAS Address is behind | 434 that proxy, potentially with some additional RADIUS proxies in |
362 that proxy, potentially with some additional RADIUS proxies in | 435 between. The Diameter/RADIUS translation agent MUST insert entries |
363 between. The Diameter/RADIUS translation agent MUST insert entries | 436 in the Route-Record AVP corresponding to the apparent route. This |
364 in the Route-Record AVP corresponding to the apparent route. This | 437 implies doing a reverse lookup on the source address and NAS-IP- |
365 implies doing a reverse lookup on the source address and NAS-IP- | 438 Address or NAS-IPv6-Address attributes to determine the corresponding |
366 Address or NAS-IPv6-Address attributes to determine the corresponding | 439 FQDNs. |
367 FQDNs. | 440 |
368 | 441 If the source address and the NAS-IP-Address or NAS-IPv6-Address do |
369 If the source address and the NAS-IP-Address or NAS-IPv6-Address do | 442 not match, and the Diameter/RADIUS translation agent knows that it is |
370 not match, and the Diameter/RADIUS translation agent knows that it is | 443 talking directly to the NAS (e.g., there are no RADIUS proxies |
371 talking directly to the NAS (e.g., there are no RADIUS proxies | 444 between it and the NAS), then the error should be logged, and the |
372 between it and the NAS), then the error should be logged, and the | 445 packet MUST be discarded. |
373 packet MUST be discarded. | 446 |
374 | 447 Diameter agents and servers MUST check whether the NAS-IP-Address AVP |
375 Diameter agents and servers MUST check whether the NAS-IP-Address AVP | 448 corresponds to an entry in the Route-Record AVP. This is done by |
376 corresponds to an entry in the Route-Record AVP. This is done by | 449 doing a reverse lookup (PTR RR) for the NAS-IP-Address to retrieve |
377 doing a reverse lookup (PTR RR) for the NAS-IP-Address to retrieve | 450 the corresponding FQDN, and by checking for a match with the Route- |
378 the corresponding FQDN, and by checking for a match with the Route- | 451 Record AVP. If no match is found, then an error is logged, but no |
379 Record AVP. If no match is found, then an error is logged, but no | 452 other action is taken. |
380 other action is taken. | 453 */ |
381 */ | 454 if (nas_ip || nas_ip6) { |
382 TRACE_DEBUG(INFO, "Message received with a NAS-IP-Address or NAS-IPv6-Address different from the sender's. Discarding..."); | 455 if (!valid_nas_info) { |
383 return ENOTSUP; | 456 if (cli->type == RGW_CLI_NAS) { |
384 } | 457 TRACE_DEBUG(INFO, "Message received with a NAS-IP-Address or NAS-IPv6-Address different \nfrom the sender's. Please configure as Proxy if this is expected.\n Message discarded."); |
385 | 458 return EINVAL; |
386 /* Now check the nas_id, but only for non-local hosts */ | 459 } else { |
387 if (nas_id && (! cli->is_local) ) { | 460 /* the peer is configured as a proxy, so accept the message */ |
388 char * str; | 461 sSS ss; |
462 | |
463 /* In that case, the cli will be stored as Route-Record and the NAS-IP-Address as origin */ | |
464 if (!cli->is_local) { | |
465 rr_str = cli->fqdn; | |
466 } | |
467 | |
468 /* We must DNS-reverse the NAS-IP*-Address */ | |
469 memset(&ss, 0 , sizeof(sSS)); | |
470 if (nas_ip) { | |
471 sSA4 * sin = (sSA4 *)&ss; | |
472 sin->sin_family = AF_INET; | |
473 memcpy(&sin->sin_addr, nas_ip + 1, sizeof(struct in_addr)); | |
474 } else { | |
475 sSA6 * sin6 = (sSA6 *)&ss; | |
476 sin6->sin6_family = AF_INET6; | |
477 memcpy(&sin6->sin6_addr, nas_ip6 + 1, sizeof(struct in6_addr)); | |
478 } | |
479 CHECK_SYS_DO( getnameinfo( (sSA *)&ss, sSAlen(&ss), &buf[0], sizeof(buf), NULL, 0, NI_NAMEREQD), | |
480 { | |
481 TRACE_DEBUG(INFO, "The NAS-IP*-Address cannot be DNS reversed in order to create the Origin-Host AVP; rejecting the message (translation is impossible)."); | |
482 return EINVAL; | |
483 } ); | |
484 | |
485 oh_str = &buf[0]; | |
486 or_str = strchr(oh_str, '.'); | |
487 if (or_str) { | |
488 or_str ++; /* move after the first dot */ | |
489 if (*or_str == '\0') | |
490 or_str = NULL; /* Discard this realm, we will use the local realm later */ | |
491 } | |
492 } | |
493 } else { | |
494 /* The attribute matches the source address, just use this in origin-host */ | |
495 CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); | |
496 } | |
497 | |
498 goto diameter; /* we ignore the nas_id in that case */ | |
499 } | |
500 | |
501 /* We don't have a NAS-IP*-Address attribute if we are here */ | |
502 if (cli->is_local) { | |
503 /* Simple: we use our own configuration */ | |
504 CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); | |
505 goto diameter; | |
506 } | |
507 | |
508 /* At this point, we only have nas_id, and the client is not local */ | |
509 ASSERT(nas_id); | |
510 | |
511 { | |
389 int found, ret; | 512 int found, ret; |
390 struct addrinfo hint, *res, *ptr; | 513 struct addrinfo hint, *res, *ptr; |
391 | 514 |
392 /* | 515 /* |
393 In RADIUS it would be possible for a rogue NAS to forge the NAS- | 516 In RADIUS it would be possible for a rogue NAS to forge the NAS- |
407 */ | 530 */ |
408 | 531 |
409 /* first, check if the nas_id is the fqdn of the peer or a known alias */ | 532 /* first, check if the nas_id is the fqdn of the peer or a known alias */ |
410 if ((cli->fqdn_len == (nas_id->length - sizeof(struct radius_attr_hdr))) | 533 if ((cli->fqdn_len == (nas_id->length - sizeof(struct radius_attr_hdr))) |
411 && (!strncasecmp((char *)(nas_id + 1), cli->fqdn, nas_id->length - sizeof(struct radius_attr_hdr)))) { | 534 && (!strncasecmp((char *)(nas_id + 1), cli->fqdn, nas_id->length - sizeof(struct radius_attr_hdr)))) { |
412 TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the NAS"); | 535 TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the client"); |
413 found = 1; | 536 found = 1; |
414 } else { | 537 } else { |
415 for (idx = 0; idx < cli->aliases_nb; idx++) { | 538 for (idx = 0; idx < cli->aliases_nb; idx++) { |
416 if (((nas_id->length - sizeof(struct radius_attr_hdr)) == strlen(cli->aliases[idx])) | 539 if (((nas_id->length - sizeof(struct radius_attr_hdr)) == strlen(cli->aliases[idx])) |
417 && (!strncasecmp((char *)(nas_id + 1), cli->aliases[idx], nas_id->length - sizeof(struct radius_attr_hdr)))) { | 540 && (!strncasecmp((char *)(nas_id + 1), cli->aliases[idx], nas_id->length - sizeof(struct radius_attr_hdr)))) { |
421 } | 544 } |
422 } | 545 } |
423 } | 546 } |
424 | 547 |
425 if (found) { | 548 if (found) { |
426 msg->valid_nas_info |= 2; | 549 /* The NAS-Identifier matches the source IP */ |
427 goto end; | 550 CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); |
428 } | 551 |
429 | 552 goto diameter; |
430 /* copy the identifier, we try to DNS resolve it */ | 553 } |
431 CHECK_MALLOC( str = malloc(nas_id->length - sizeof(struct radius_attr_hdr) + 1) ); | 554 |
432 memcpy(str, nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr)); | 555 /* Attempt DNS resolution of the identifier */ |
433 str[nas_id->length - sizeof(struct radius_attr_hdr)] = '\0'; | 556 ASSERT( nas_id->length - sizeof(struct radius_attr_hdr) < sizeof(buf) ); |
557 memcpy(buf, nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr)); | |
558 buf[nas_id->length - sizeof(struct radius_attr_hdr)] = '\0'; | |
434 | 559 |
435 /* Now check if this alias is valid for this peer */ | 560 /* Now check if this alias is valid for this peer */ |
436 memset(&hint, 0, sizeof(hint)); | 561 memset(&hint, 0, sizeof(hint)); |
437 hint.ai_family = cli->sa->sa_family; | |
438 hint.ai_flags = AI_CANONNAME; | 562 hint.ai_flags = AI_CANONNAME; |
439 ret = getaddrinfo(str, NULL, &hint, &res); | 563 ret = getaddrinfo(buf, NULL, &hint, &res); |
440 if (ret == 0) { | 564 if (ret == 0) { |
441 /* The name was resolved correctly, it must match the IP of the client: */ | 565 strncpy(buf, res->ai_canonname, sizeof(buf)); |
566 /* The name was resolved correctly, does it match the IP of the client? */ | |
442 for (ptr = res; ptr != NULL; ptr = ptr->ai_next) { | 567 for (ptr = res; ptr != NULL; ptr = ptr->ai_next) { |
443 if (cli->sa->sa_family != ptr->ai_family) | 568 if (cli->sa->sa_family != ptr->ai_family) |
444 continue; | 569 continue; |
445 if (memcmp(cli->sa, ptr->ai_addr, sSAlen(cli->sa))) | 570 if (memcmp(cli->sa, ptr->ai_addr, sSAlen(cli->sa))) |
446 continue; | 571 continue; |
447 | 572 |
448 /* It matches: the alias is valid */ | |
449 found = 1; | 573 found = 1; |
450 break; | 574 break; |
451 } | 575 } |
452 freeaddrinfo(res); | 576 freeaddrinfo(res); |
453 | 577 |
454 if (!found) { | 578 if (!found) { |
455 TRACE_DEBUG(INFO, "The NAS-Identifier value '%s' resolves to a different IP from the NAS's, discarding the message.", str); | 579 if (cli->type == RGW_CLI_NAS) { |
456 free(str); | 580 TRACE_DEBUG(INFO, "The NAS-Identifier value '%.*s' resolves to a different IP than the client's, discarding the message. \nConfigure this client as a Proxy if this message should be valid.", |
457 return EINVAL; | 581 nas_id->length - sizeof(struct radius_attr_hdr), nas_id + 1); |
582 return EINVAL; | |
583 } else { | |
584 /* This identifier matches a different IP, assume it is a proxied message */ | |
585 if (!cli->is_local) { | |
586 rr_str = cli->fqdn; | |
587 } | |
588 oh_str = &buf[0]; /* The canonname resolved */ | |
589 or_str = strchr(oh_str, '.'); | |
590 if (or_str) { | |
591 or_str ++; /* move after the first dot */ | |
592 if (*or_str == '\0') | |
593 or_str = NULL; /* Discard this realm, we will use the local realm later */ | |
594 } | |
595 } | |
596 } else { | |
597 /* It is a valid alias, save it */ | |
598 CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(char *)) ); | |
599 CHECK_MALLOC( cli->aliases[cli->aliases_nb + 1] = malloc( 1 + nas_id->length - sizeof(struct radius_attr_hdr) )); | |
600 memcpy( cli->aliases[cli->aliases_nb + 1], nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr)); | |
601 *(cli->aliases[cli->aliases_nb + 1] + nas_id->length - sizeof(struct radius_attr_hdr)) = '\0'; | |
602 cli->aliases_nb ++; | |
603 TRACE_DEBUG(FULL, "Saved valid alias for client: '%s' -> '%s'", cli->aliases[cli->aliases_nb + 1], cli->fqdn); | |
604 CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); | |
458 } | 605 } |
459 } else { | 606 } else { |
460 /* Error resolving the name */ | 607 /* Error resolving the name */ |
461 TRACE_DEBUG(INFO, "Error while resolving NAS-Identifier value '%s': %s. Ignoring...", str, gai_strerror(ret)); | 608 TRACE_DEBUG(INFO, "NAS-Identifier '%s' cannot be resolved: %s. Ignoring...", buf, gai_strerror(ret)); |
462 } | 609 /* Assume this is a valid identifier for the client */ |
463 | 610 CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); |
464 /* It is a valid alias, save it */ | 611 } |
465 CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(char *)) ); | 612 } |
466 cli->aliases[cli->aliases_nb + 1] = str; | 613 |
467 cli->aliases_nb ++; | 614 /* Now, let's create the empty Diameter message with Origin-Host, -Realm, and Route-Record if needed. */ |
468 TRACE_DEBUG(FULL, "Saved valid alias for client: '%s' -> '%s'", str, cli->fqdn); | 615 diameter: |
469 msg->valid_nas_info |= 2; | 616 ASSERT(oh_str); /* If it is not defined here, there is a bug... */ |
470 } | 617 if (!or_str) |
471 end: | 618 or_str = fd_g_config->cnf_diamrlm; /* Use local realm in that case */ |
619 | |
620 /* Create an empty Diameter message so that extensions can store their AVPs */ | |
621 CHECK_FCT( fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam ) ); | |
622 | |
623 /* Add the Origin-Host as next AVP */ | |
624 CHECK_FCT( fd_msg_avp_new ( cache_orig_host, 0, &avp ) ); | |
625 memset(&avp_val, 0, sizeof(avp_val)); | |
626 avp_val.os.data = (unsigned char *)oh_str; | |
627 avp_val.os.len = strlen(oh_str); | |
628 CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); | |
629 CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); | |
630 | |
631 /* Add the Origin-Realm as next AVP */ | |
632 CHECK_FCT( fd_msg_avp_new ( cache_orig_realm, 0, &avp ) ); | |
633 memset(&avp_val, 0, sizeof(avp_val)); | |
634 avp_val.os.data = (unsigned char *)or_str; | |
635 avp_val.os.len = strlen(or_str); | |
636 CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); | |
637 CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); | |
638 | |
639 if (rr_str) { | |
640 CHECK_FCT( fd_msg_avp_new ( cache_route_record, 0, &avp ) ); | |
641 memset(&avp_val, 0, sizeof(avp_val)); | |
642 avp_val.os.data = (unsigned char *)rr_str; | |
643 avp_val.os.len = strlen(rr_str); | |
644 CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); | |
645 CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); | |
646 } | |
647 | |
648 /* Done! */ | |
472 return 0; | 649 return 0; |
473 } | 650 } |
474 | 651 |
475 int rgw_clients_get_origin(struct rgw_client *cli, char **fqdn, char **realm) | 652 int rgw_clients_get_origin(struct rgw_client *cli, char **fqdn, char **realm) |
476 { | 653 { |
505 client_unlink(*ref); | 682 client_unlink(*ref); |
506 *ref = NULL; | 683 *ref = NULL; |
507 CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), ); | 684 CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), ); |
508 } | 685 } |
509 | 686 |
510 int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen ) | 687 int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type ) |
511 { | 688 { |
512 struct rgw_client * prev = NULL, *new = NULL; | 689 struct rgw_client * prev = NULL, *new = NULL; |
513 int ret; | 690 int ret; |
514 | 691 |
515 TRACE_ENTRY("%p %p %lu", ip_port, key, keylen); | 692 TRACE_ENTRY("%p %p %lu", ip_port, key, keylen); |
516 | 693 |
517 CHECK_PARAMS( ip_port && key && *key && keylen ); | 694 CHECK_PARAMS( ip_port && key && *key && keylen ); |
518 CHECK_PARAMS( (ip_port->sa_family == AF_INET) || (ip_port->sa_family == AF_INET6) ); | 695 CHECK_PARAMS( (ip_port->sa_family == AF_INET) || (ip_port->sa_family == AF_INET6) ); |
696 CHECK_PARAMS( (type == RGW_CLI_NAS) || (type == RGW_CLI_PXY) ); | |
519 | 697 |
520 /* Dump the entry in debug mode */ | 698 /* Dump the entry in debug mode */ |
521 if (TRACE_BOOL(FULL + 1 )) { | 699 if (TRACE_BOOL(FULL + 1 )) { |
522 TRACE_DEBUG(FULL, "Adding client:"); | 700 TRACE_DEBUG(FULL, "Adding %s:", (type == RGW_CLI_NAS) ? "NAS" : "PROXY" ); |
523 TRACE_DEBUG_sSA(FULL, "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" ); | 701 TRACE_DEBUG_sSA(FULL, "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" ); |
524 TRACE_DEBUG_BUFFER(FULL, "\tKey: [", *key, keylen, "]" ); | 702 TRACE_DEBUG_BUFFER(FULL, "\tKey: [", *key, keylen, "]" ); |
525 } | 703 } |
526 | 704 |
527 /* Lock the lists */ | 705 /* Lock the lists */ |
529 | 707 |
530 /* Check if the same entry does not already exist */ | 708 /* Check if the same entry does not already exist */ |
531 ret = client_search(&prev, ip_port ); | 709 ret = client_search(&prev, ip_port ); |
532 if (ret == ENOENT) { | 710 if (ret == ENOENT) { |
533 /* No duplicate found, Ok to add */ | 711 /* No duplicate found, Ok to add */ |
534 CHECK_FCT_DO( ret = client_create( &new, &ip_port, key, keylen ), goto end ); | 712 CHECK_FCT_DO( ret = client_create( &new, &ip_port, key, keylen, type ), goto end ); |
535 fd_list_insert_after(&prev->chain, &new->chain); | 713 fd_list_insert_after(&prev->chain, &new->chain); |
536 new->refcount++; | 714 new->refcount++; |
537 ret = 0; | 715 ret = 0; |
538 goto end; | 716 goto end; |
539 } | 717 } |
540 | 718 |
541 if (ret == EEXIST) { | 719 if (ret == EEXIST) { |
542 /* Check if the key is the same, then skip or return an error */ | 720 /* Check if the key is the same, then skip or return an error */ |
543 if ((keylen == prev->key.len ) && ( ! memcmp(*key, prev->key.data, keylen) )) { | 721 if ((keylen == prev->key.len ) && ( ! memcmp(*key, prev->key.data, keylen) ) && (type == prev->type)) { |
544 TRACE_DEBUG(INFO, "Skipping duplicate client description"); | 722 TRACE_DEBUG(INFO, "Skipping duplicate client description"); |
545 ret = 0; | 723 ret = 0; |
546 goto end; | 724 goto end; |
547 } | 725 } |
548 | 726 |
549 fd_log_debug("ERROR: Conflicting RADIUS clients descriptions!\n"); | 727 fd_log_debug("ERROR: Conflicting RADIUS clients descriptions!\n"); |
550 TRACE_DEBUG(NONE, "Previous entry:"); | 728 TRACE_DEBUG(NONE, "Previous entry: %s", (prev->type == RGW_CLI_NAS) ? "NAS" : "PROXY"); |
551 TRACE_DEBUG_sSA(NONE, "\tIP : ", prev->sa, NI_NUMERICHOST | NI_NUMERICSERV, "" ); | 729 TRACE_DEBUG_sSA(NONE, "\tIP : ", prev->sa, NI_NUMERICHOST | NI_NUMERICSERV, "" ); |
552 TRACE_DEBUG_BUFFER(NONE, "\tKey: [", prev->key.data, prev->key.len, "]" ); | 730 TRACE_DEBUG_BUFFER(NONE, "\tKey: [", prev->key.data, prev->key.len, "]" ); |
553 TRACE_DEBUG(NONE, "Conflicting entry:"); | 731 TRACE_DEBUG(NONE, "Conflicting entry: %s", (type == RGW_CLI_NAS) ? "NAS" : "PROXY"); |
554 TRACE_DEBUG_sSA(NONE, "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" ); | 732 TRACE_DEBUG_sSA(NONE, "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" ); |
555 TRACE_DEBUG_BUFFER(NONE, "\tKey: [", *key, keylen, "]" ); | 733 TRACE_DEBUG_BUFFER(NONE, "\tKey: [", *key, keylen, "]" ); |
556 } | 734 } |
557 end: | 735 end: |
558 /* release the lists */ | 736 /* release the lists */ |