# HG changeset patch # User Sebastien Decugis # Date 1282874391 -32400 # Node ID 1c2f5ee380399429a154944799b272b64191e998 # Parent b9167b4de7dcb509b8a8bafd40611b8fdf0a3863 Allow RADIUS Proxies with the app_radgw extension diff -r b9167b4de7dc -r 1c2f5ee38039 doc/app_radgw.conf.sample --- a/doc/app_radgw.conf.sample Thu Aug 26 14:10:03 2010 +0900 +++ b/doc/app_radgw.conf.sample Fri Aug 27 10:59:51 2010 +0900 @@ -48,15 +48,24 @@ # RADIUS Clients # ################## -# Each RADIUS client must be declared in the form: cli = IP / shared-secret ; +# Each RADIUS client must be declared in the form: +# nas = IP / shared-secret ; # IP can be ipv4 or ipv6 # port can be additionaly restricted with brackets: IP[port] (ex: 192.168.0.1[1812]) # shared-secret can be a quoted string, or a list of hexadecimal values. # examples: -# cli = 192.168.100.1 / "secret key" ; # the shared secret buffer is 0x736563726574206b6579 (length 10 bytes) -# cli = fe00::1 / 73 65 63 72 65 74 20 6b 65 79; # same shared secret as previously +# nas = 192.168.100.1 / "secret key" ; # the shared secret buffer is 0x736563726574206b6579 (length 10 bytes) +# nas = fe00::1 / 73 65 63 72 65 74 20 6b 65 79; # same shared secret as previously # When a packet is received from an IP not declared here, it is discarded. +# If the RADIUS client is a Proxy that forwards messages from different peers, it must be +# declared instead as follow: +# pxy = IP / shared-secret ; +# Note that it is not recommended to use this gateway implementation with a proxy currently, +# since the management of duplicate messages might be insufficient. + +# The old notation cli = ... is equivalent to nas = ... and kept for backward compatibility. + #################### # Authentication # diff -r b9167b4de7dc -r 1c2f5ee38039 extensions/app_radgw/radius.c --- a/extensions/app_radgw/radius.c Thu Aug 26 14:10:03 2010 +0900 +++ b/extensions/app_radgw/radius.c Fri Aug 27 10:59:51 2010 +0900 @@ -3,7 +3,8 @@ * The content from this file comes directly from the hostap project. * It is redistributed under the terms of the BSD license, as allowed * by the original copyright reproduced bellow. - * In addition to this notice, only the #include directives have been modified. + * In addition to this notice, the following changes have been done: + * - created the radius_msg_dump_attr_val function */ #include "rgw_common.h" @@ -217,7 +218,7 @@ } -static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) +void radius_msg_dump_attr_val(struct radius_attr_hdr *hdr) { struct radius_attr_type *attr; int i, len; @@ -225,9 +226,6 @@ attr = radius_get_attr_type(hdr->type); - printf(" Attribute %d (%s) length=%d\n", - hdr->type, attr ? attr->name : "?Unknown?", hdr->length); - if (attr == NULL) return; @@ -284,6 +282,18 @@ } } +static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) +{ + struct radius_attr_type *attr; + + attr = radius_get_attr_type(hdr->type); + + printf(" Attribute %d (%s) length=%d\n", + hdr->type, attr ? attr->name : "?Unknown?", hdr->length); + + radius_msg_dump_attr_val(hdr); +} + void radius_msg_dump(struct radius_msg *msg) { diff -r b9167b4de7dc -r 1c2f5ee38039 extensions/app_radgw/radius.h --- a/extensions/app_radgw/radius.h Thu Aug 26 14:10:03 2010 +0900 +++ b/extensions/app_radgw/radius.h Fri Aug 27 10:59:51 2010 +0900 @@ -3,8 +3,11 @@ * The content from this file comes directly from the hostap project. * It is redistributed under the terms of the BSD license, as allowed * by the original copyright reproduced bellow. - * The file has not been modified, except for this notice. + * The file has not been modified, except for this notice and + * declaration of: + * void radius_msg_dump_attr_val(struct radius_attr_hdr *hdr); */ + /*********************************************************************************/ /* @@ -215,6 +218,7 @@ int radius_msg_initialize(struct radius_msg *msg, size_t init_len); void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier); void radius_msg_free(struct radius_msg *msg); +void radius_msg_dump_attr_val(struct radius_attr_hdr *hdr); void radius_msg_dump(struct radius_msg *msg); int radius_msg_finish(struct radius_msg *msg, const u8 *secret, size_t secret_len); diff -r b9167b4de7dc -r 1c2f5ee38039 extensions/app_radgw/rgw.h --- a/extensions/app_radgw/rgw.h Thu Aug 26 14:10:03 2010 +0900 +++ b/extensions/app_radgw/rgw.h Fri Aug 27 10:59:51 2010 +0900 @@ -57,18 +57,12 @@ /* The message has a valid Message-Authenticator attribute */ unsigned valid_mac :1; - - /* The message has a valid NAS-IP(v6)-Address (1) and/or NAS-Identifier (2) attribute */ - unsigned valid_nas_info :2; }; }; void rgw_msg_free(struct rgw_radius_msg_meta ** msg); int rgw_msg_parse(unsigned char * buf, size_t len, struct rgw_radius_msg_meta ** msg); void rgw_msg_dump(struct rgw_radius_msg_meta * msg); -int rgw_msg_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth); -int rgw_msg_create_base(struct rgw_client * cli, struct msg ** diam); -int rgw_msg_init(void); /* Local RADIUS server(s) configuration */ struct rgw_serv { @@ -96,14 +90,18 @@ /* Clients management */ -int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen ); +enum rgw_cli_type { RGW_CLI_NAS, RGW_CLI_PXY }; +int rgw_clients_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth); +int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type ); int rgw_clients_getkey(struct rgw_client * cli, unsigned char **key, size_t *key_len); +int rgw_clients_gettype(struct rgw_client * cli, enum rgw_cli_type *type); int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref); int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli); -int rgw_clients_check_origin(struct rgw_radius_msg_meta *msg, struct rgw_client *cli); +int rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client * cli, struct msg ** diam); int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli); void rgw_clients_dispose(struct rgw_client ** ref); void rgw_clients_dump(void); +int rgw_clients_init(void); void rgw_clients_fini(void); int rgw_client_session_add(struct rgw_client * cli, struct session *sess, char * dest_realm, char * dest_host, application_id_t appid); int rgw_client_session_stop(struct rgw_client * cli, struct session * sess, int32_t reason); diff -r b9167b4de7dc -r 1c2f5ee38039 extensions/app_radgw/rgw_clients.c --- a/extensions/app_radgw/rgw_clients.c Thu Aug 26 14:10:03 2010 +0900 +++ b/extensions/app_radgw/rgw_clients.c Fri Aug 27 10:59:51 2010 +0900 @@ -39,6 +39,8 @@ #include "rgw.h" +#define REVERSE_DNS_SIZE_MAX 512 /* length of our buffer for reverse DNS */ + /* Ordered lists of clients. The order relationship is a memcmp on the address zone. For same addresses, the port is compared. The same address cannot be added twice, once with a 0-port and once with another port value. @@ -66,6 +68,7 @@ /* The FQDN, realm, and optional aliases */ int is_local; /* true if the RADIUS client runs on the same host -- we use Diameter Identity in that case */ + enum rgw_cli_type type; /* is it a proxy ? */ char *fqdn; size_t fqdn_len; char *realm; @@ -90,7 +93,7 @@ /* create a new rgw_client. the arguments are moved into the structure (to limit malloc & free calls). */ -static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen ) +static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type ) { struct rgw_client *tmp = NULL; char buf[255]; @@ -117,6 +120,8 @@ memset(tmp, 0, sizeof(struct rgw_client)); fd_list_init(&tmp->chain, NULL); + tmp->type = type; + if (loc) { tmp->is_local = 1; } else { @@ -230,6 +235,14 @@ return 0; } +int rgw_clients_gettype(struct rgw_client * cli, enum rgw_cli_type *type) +{ + CHECK_PARAMS( cli && type ); + *type = cli->type; + return 0; +} + + int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref) { int ret = 0; @@ -303,15 +316,71 @@ /* Check that the NAS-IP-Adress or NAS-Identifier is coherent with the IP the packet was received from */ /* Also update the client list of aliases if needed */ -/* NOTE: This function will require changes to allow RADIUS Proxy on the path... */ -int rgw_clients_check_origin(struct rgw_radius_msg_meta *msg, struct rgw_client *cli) +/* NOTE: This function does nothing if the client is a RADIUS Proxy... */ +/* Check if the message has a valid authenticator, and update the meta-data accordingly */ +int rgw_clients_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth) +{ + unsigned char * key; + size_t keylen; + int count; + + TRACE_ENTRY("%p %p %p", msg, cli, req_auth); + + CHECK_PARAMS(msg && cli); + + CHECK_FCT(rgw_clients_getkey(cli, &key, &keylen)); + + count = radius_msg_count_attr(&msg->radius, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 0); + if (count > 1) { + TRACE_DEBUG(INFO, "Too many Message-Authenticator attributes (%d), discarding message.", count); + return EINVAL; + } + if (count == 0) { + TRACE_DEBUG(FULL, "Message does not contain a Message-Authenticator attributes."); + msg->valid_mac = 0; + } else { + if (radius_msg_verify_msg_auth( &msg->radius, key, keylen, req_auth )) { + TRACE_DEBUG(INFO, "Invalid Message-Authenticator received, discarding message."); + return EINVAL; + } + msg->valid_mac = 1; + } + + return 0; +} + +static struct dict_object * cache_orig_host = NULL; +static struct dict_object * cache_orig_realm = NULL; +static struct dict_object * cache_route_record = NULL; + +int rgw_clients_init(void) +{ + TRACE_ENTRY(); + CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &cache_orig_host, ENOENT) ); + CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_realm, ENOENT) ); + CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &cache_route_record, ENOENT) ); + return 0; +} + + +/* The following function checks if a RADIUS message contains a valid NAS identifier, and initializes an empty Diameter + message with the appropriate routing information */ +int rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client * cli, struct msg ** diam) { int idx; + int valid_nas_info = 0; struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL; + char * oh_str = NULL; + char * or_str = NULL; + char * rr_str = NULL; + char buf[REVERSE_DNS_SIZE_MAX]; /* to store DNS lookups results */ - TRACE_ENTRY("%p %p", msg, cli); - CHECK_PARAMS(msg && cli && !msg->valid_nas_info ); - + struct avp *avp = NULL; + union avp_value avp_val; + + TRACE_ENTRY("%p %p %p", msg, cli, diam); + CHECK_PARAMS(msg && cli && diam && (*diam == NULL)); + /* Find the relevant attributes, if any */ for (idx = 0; idx < msg->radius.attr_used; idx++) { struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[idx]); @@ -335,57 +404,111 @@ if (!nas_ip && !nas_ip6 && !nas_id) { TRACE_DEBUG(FULL, "The message does not contain any NAS identification attribute."); - goto end; + + /* Get information on this peer */ + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); + + goto diameter; } /* Check if the message was received from the IP in NAS-IP-Address attribute */ if (nas_ip && (cli->sa->sa_family == AF_INET) && !memcmp(nas_ip+1, &cli->sin->sin_addr, sizeof(struct in_addr))) { TRACE_DEBUG(FULL, "NAS-IP-Address contains the same address as the message was received from."); - msg->valid_nas_info |= 1; + valid_nas_info |= 1; } if (nas_ip6 && (cli->sa->sa_family == AF_INET6) && !memcmp(nas_ip6+1, &cli->sin6->sin6_addr, sizeof(struct in6_addr))) { TRACE_DEBUG(FULL, "NAS-IPv6-Address contains the same address as the message was received from."); - msg->valid_nas_info |= 1; + valid_nas_info |= 2; } - /* If these conditions are not met, the message is probably forged (well, this might be false...) */ - if ((! msg->valid_nas_info) && (nas_ip || nas_ip6)) { - /* - In RADIUS it would be possible for a rogue NAS to forge the NAS-IP- - Address attribute value. Diameter/RADIUS translation agents MUST - check a received NAS-IP-Address or NAS-IPv6-Address attribute against - the source address of the RADIUS packet. If they do not match and - the Diameter/RADIUS translation agent does not know whether the - packet was sent by a RADIUS proxy or NAS (e.g., no Proxy-State - attribute), then by default it is assumed that the source address - corresponds to a RADIUS proxy, and that the NAS Address is behind - that proxy, potentially with some additional RADIUS proxies in - between. The Diameter/RADIUS translation agent MUST insert entries - in the Route-Record AVP corresponding to the apparent route. This - implies doing a reverse lookup on the source address and NAS-IP- - Address or NAS-IPv6-Address attributes to determine the corresponding - FQDNs. + + /* + In RADIUS it would be possible for a rogue NAS to forge the NAS-IP- + Address attribute value. Diameter/RADIUS translation agents MUST + check a received NAS-IP-Address or NAS-IPv6-Address attribute against + the source address of the RADIUS packet. If they do not match and + the Diameter/RADIUS translation agent does not know whether the + packet was sent by a RADIUS proxy or NAS (e.g., no Proxy-State + attribute), then by default it is assumed that the source address + corresponds to a RADIUS proxy, and that the NAS Address is behind + that proxy, potentially with some additional RADIUS proxies in + between. The Diameter/RADIUS translation agent MUST insert entries + in the Route-Record AVP corresponding to the apparent route. This + implies doing a reverse lookup on the source address and NAS-IP- + Address or NAS-IPv6-Address attributes to determine the corresponding + FQDNs. + + If the source address and the NAS-IP-Address or NAS-IPv6-Address do + not match, and the Diameter/RADIUS translation agent knows that it is + talking directly to the NAS (e.g., there are no RADIUS proxies + between it and the NAS), then the error should be logged, and the + packet MUST be discarded. - If the source address and the NAS-IP-Address or NAS-IPv6-Address do - not match, and the Diameter/RADIUS translation agent knows that it is - talking directly to the NAS (e.g., there are no RADIUS proxies - between it and the NAS), then the error should be logged, and the - packet MUST be discarded. - - Diameter agents and servers MUST check whether the NAS-IP-Address AVP - corresponds to an entry in the Route-Record AVP. This is done by - doing a reverse lookup (PTR RR) for the NAS-IP-Address to retrieve - the corresponding FQDN, and by checking for a match with the Route- - Record AVP. If no match is found, then an error is logged, but no - other action is taken. - */ - TRACE_DEBUG(INFO, "Message received with a NAS-IP-Address or NAS-IPv6-Address different from the sender's. Discarding..."); - return ENOTSUP; + Diameter agents and servers MUST check whether the NAS-IP-Address AVP + corresponds to an entry in the Route-Record AVP. This is done by + doing a reverse lookup (PTR RR) for the NAS-IP-Address to retrieve + the corresponding FQDN, and by checking for a match with the Route- + Record AVP. If no match is found, then an error is logged, but no + other action is taken. + */ + if (nas_ip || nas_ip6) { + if (!valid_nas_info) { + if (cli->type == RGW_CLI_NAS) { + 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."); + return EINVAL; + } else { + /* the peer is configured as a proxy, so accept the message */ + sSS ss; + + /* In that case, the cli will be stored as Route-Record and the NAS-IP-Address as origin */ + if (!cli->is_local) { + rr_str = cli->fqdn; + } + + /* We must DNS-reverse the NAS-IP*-Address */ + memset(&ss, 0 , sizeof(sSS)); + if (nas_ip) { + sSA4 * sin = (sSA4 *)&ss; + sin->sin_family = AF_INET; + memcpy(&sin->sin_addr, nas_ip + 1, sizeof(struct in_addr)); + } else { + sSA6 * sin6 = (sSA6 *)&ss; + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr, nas_ip6 + 1, sizeof(struct in6_addr)); + } + CHECK_SYS_DO( getnameinfo( (sSA *)&ss, sSAlen(&ss), &buf[0], sizeof(buf), NULL, 0, NI_NAMEREQD), + { + 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)."); + return EINVAL; + } ); + + oh_str = &buf[0]; + or_str = strchr(oh_str, '.'); + if (or_str) { + or_str ++; /* move after the first dot */ + if (*or_str == '\0') + or_str = NULL; /* Discard this realm, we will use the local realm later */ + } + } + } else { + /* The attribute matches the source address, just use this in origin-host */ + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); + } + + goto diameter; /* we ignore the nas_id in that case */ } - /* Now check the nas_id, but only for non-local hosts */ - if (nas_id && (! cli->is_local) ) { - char * str; + /* We don't have a NAS-IP*-Address attribute if we are here */ + if (cli->is_local) { + /* Simple: we use our own configuration */ + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); + goto diameter; + } + + /* At this point, we only have nas_id, and the client is not local */ + ASSERT(nas_id); + + { int found, ret; struct addrinfo hint, *res, *ptr; @@ -409,7 +532,7 @@ /* first, check if the nas_id is the fqdn of the peer or a known alias */ if ((cli->fqdn_len == (nas_id->length - sizeof(struct radius_attr_hdr))) && (!strncasecmp((char *)(nas_id + 1), cli->fqdn, nas_id->length - sizeof(struct radius_attr_hdr)))) { - TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the NAS"); + TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the client"); found = 1; } else { for (idx = 0; idx < cli->aliases_nb; idx++) { @@ -423,52 +546,106 @@ } if (found) { - msg->valid_nas_info |= 2; - goto end; + /* The NAS-Identifier matches the source IP */ + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); + + goto diameter; } - /* copy the identifier, we try to DNS resolve it */ - CHECK_MALLOC( str = malloc(nas_id->length - sizeof(struct radius_attr_hdr) + 1) ); - memcpy(str, nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr)); - str[nas_id->length - sizeof(struct radius_attr_hdr)] = '\0'; + /* Attempt DNS resolution of the identifier */ + ASSERT( nas_id->length - sizeof(struct radius_attr_hdr) < sizeof(buf) ); + memcpy(buf, nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr)); + buf[nas_id->length - sizeof(struct radius_attr_hdr)] = '\0'; /* Now check if this alias is valid for this peer */ memset(&hint, 0, sizeof(hint)); - hint.ai_family = cli->sa->sa_family; hint.ai_flags = AI_CANONNAME; - ret = getaddrinfo(str, NULL, &hint, &res); + ret = getaddrinfo(buf, NULL, &hint, &res); if (ret == 0) { - /* The name was resolved correctly, it must match the IP of the client: */ + strncpy(buf, res->ai_canonname, sizeof(buf)); + /* The name was resolved correctly, does it match the IP of the client? */ for (ptr = res; ptr != NULL; ptr = ptr->ai_next) { if (cli->sa->sa_family != ptr->ai_family) continue; if (memcmp(cli->sa, ptr->ai_addr, sSAlen(cli->sa))) continue; - /* It matches: the alias is valid */ found = 1; break; } freeaddrinfo(res); if (!found) { - TRACE_DEBUG(INFO, "The NAS-Identifier value '%s' resolves to a different IP from the NAS's, discarding the message.", str); - free(str); - return EINVAL; + if (cli->type == RGW_CLI_NAS) { + 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.", + nas_id->length - sizeof(struct radius_attr_hdr), nas_id + 1); + return EINVAL; + } else { + /* This identifier matches a different IP, assume it is a proxied message */ + if (!cli->is_local) { + rr_str = cli->fqdn; + } + oh_str = &buf[0]; /* The canonname resolved */ + or_str = strchr(oh_str, '.'); + if (or_str) { + or_str ++; /* move after the first dot */ + if (*or_str == '\0') + or_str = NULL; /* Discard this realm, we will use the local realm later */ + } + } + } else { + /* It is a valid alias, save it */ + CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(char *)) ); + CHECK_MALLOC( cli->aliases[cli->aliases_nb + 1] = malloc( 1 + nas_id->length - sizeof(struct radius_attr_hdr) )); + memcpy( cli->aliases[cli->aliases_nb + 1], nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr)); + *(cli->aliases[cli->aliases_nb + 1] + nas_id->length - sizeof(struct radius_attr_hdr)) = '\0'; + cli->aliases_nb ++; + TRACE_DEBUG(FULL, "Saved valid alias for client: '%s' -> '%s'", cli->aliases[cli->aliases_nb + 1], cli->fqdn); + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); } } else { /* Error resolving the name */ - TRACE_DEBUG(INFO, "Error while resolving NAS-Identifier value '%s': %s. Ignoring...", str, gai_strerror(ret)); + TRACE_DEBUG(INFO, "NAS-Identifier '%s' cannot be resolved: %s. Ignoring...", buf, gai_strerror(ret)); + /* Assume this is a valid identifier for the client */ + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); } - - /* It is a valid alias, save it */ - CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(char *)) ); - cli->aliases[cli->aliases_nb + 1] = str; - cli->aliases_nb ++; - TRACE_DEBUG(FULL, "Saved valid alias for client: '%s' -> '%s'", str, cli->fqdn); - msg->valid_nas_info |= 2; } -end: + + /* Now, let's create the empty Diameter message with Origin-Host, -Realm, and Route-Record if needed. */ +diameter: + ASSERT(oh_str); /* If it is not defined here, there is a bug... */ + if (!or_str) + or_str = fd_g_config->cnf_diamrlm; /* Use local realm in that case */ + + /* Create an empty Diameter message so that extensions can store their AVPs */ + CHECK_FCT( fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam ) ); + + /* Add the Origin-Host as next AVP */ + CHECK_FCT( fd_msg_avp_new ( cache_orig_host, 0, &avp ) ); + memset(&avp_val, 0, sizeof(avp_val)); + avp_val.os.data = (unsigned char *)oh_str; + avp_val.os.len = strlen(oh_str); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); + CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); + + /* Add the Origin-Realm as next AVP */ + CHECK_FCT( fd_msg_avp_new ( cache_orig_realm, 0, &avp ) ); + memset(&avp_val, 0, sizeof(avp_val)); + avp_val.os.data = (unsigned char *)or_str; + avp_val.os.len = strlen(or_str); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); + CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); + + if (rr_str) { + CHECK_FCT( fd_msg_avp_new ( cache_route_record, 0, &avp ) ); + memset(&avp_val, 0, sizeof(avp_val)); + avp_val.os.data = (unsigned char *)rr_str; + avp_val.os.len = strlen(rr_str); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); + CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); + } + + /* Done! */ return 0; } @@ -507,7 +684,7 @@ CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), ); } -int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen ) +int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type ) { struct rgw_client * prev = NULL, *new = NULL; int ret; @@ -516,10 +693,11 @@ CHECK_PARAMS( ip_port && key && *key && keylen ); CHECK_PARAMS( (ip_port->sa_family == AF_INET) || (ip_port->sa_family == AF_INET6) ); + CHECK_PARAMS( (type == RGW_CLI_NAS) || (type == RGW_CLI_PXY) ); /* Dump the entry in debug mode */ if (TRACE_BOOL(FULL + 1 )) { - TRACE_DEBUG(FULL, "Adding client:"); + TRACE_DEBUG(FULL, "Adding %s:", (type == RGW_CLI_NAS) ? "NAS" : "PROXY" ); TRACE_DEBUG_sSA(FULL, "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" ); TRACE_DEBUG_BUFFER(FULL, "\tKey: [", *key, keylen, "]" ); } @@ -531,7 +709,7 @@ ret = client_search(&prev, ip_port ); if (ret == ENOENT) { /* No duplicate found, Ok to add */ - CHECK_FCT_DO( ret = client_create( &new, &ip_port, key, keylen ), goto end ); + CHECK_FCT_DO( ret = client_create( &new, &ip_port, key, keylen, type ), goto end ); fd_list_insert_after(&prev->chain, &new->chain); new->refcount++; ret = 0; @@ -540,17 +718,17 @@ if (ret == EEXIST) { /* Check if the key is the same, then skip or return an error */ - if ((keylen == prev->key.len ) && ( ! memcmp(*key, prev->key.data, keylen) )) { + if ((keylen == prev->key.len ) && ( ! memcmp(*key, prev->key.data, keylen) ) && (type == prev->type)) { TRACE_DEBUG(INFO, "Skipping duplicate client description"); ret = 0; goto end; } fd_log_debug("ERROR: Conflicting RADIUS clients descriptions!\n"); - TRACE_DEBUG(NONE, "Previous entry:"); + TRACE_DEBUG(NONE, "Previous entry: %s", (prev->type == RGW_CLI_NAS) ? "NAS" : "PROXY"); TRACE_DEBUG_sSA(NONE, "\tIP : ", prev->sa, NI_NUMERICHOST | NI_NUMERICSERV, "" ); TRACE_DEBUG_BUFFER(NONE, "\tKey: [", prev->key.data, prev->key.len, "]" ); - TRACE_DEBUG(NONE, "Conflicting entry:"); + TRACE_DEBUG(NONE, "Conflicting entry: %s", (type == RGW_CLI_NAS) ? "NAS" : "PROXY"); TRACE_DEBUG_sSA(NONE, "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" ); TRACE_DEBUG_BUFFER(NONE, "\tKey: [", *key, keylen, "]" ); } diff -r b9167b4de7dc -r 1c2f5ee38039 extensions/app_radgw/rgw_conf.l --- a/extensions/app_radgw/rgw_conf.l Thu Aug 26 14:10:03 2010 +0900 +++ b/extensions/app_radgw/rgw_conf.l Fri Aug 27 10:59:51 2010 +0900 @@ -121,7 +121,8 @@ /* Client section */ -(?i:"cli") { BEGIN(IN_CLI1); return CLI_PREFIX; } +(?i:"nas"|"cli") { BEGIN(IN_CLI1); yylval->integer=RGW_CLI_NAS; return NAS_OR_PXY; } +(?i:"pxy") { BEGIN(IN_CLI1); yylval->integer=RGW_CLI_PXY; return NAS_OR_PXY; } /* Match an IP (4 or 6) and optional port */ ({IP4}|{IP6}){BR_PORT}? { diff -r b9167b4de7dc -r 1c2f5ee38039 extensions/app_radgw/rgw_conf.y --- a/extensions/app_radgw/rgw_conf.y Thu Aug 26 14:10:03 2010 +0900 +++ b/extensions/app_radgw/rgw_conf.y Fri Aug 27 10:59:51 2010 +0900 @@ -145,13 +145,14 @@ %type FINDFILEEXT +%token NAS_OR_PXY + /* simple tokens */ %token DISABLED %token AUTH %token ACCT %token PLG_PREFIX -%token CLI_PREFIX %token AUTH_ENABLE %token AUTH_PORT @@ -263,10 +264,10 @@ clientdef: { buf_reinit(); } - CLI_PREFIX '=' IP '/' clisecret_key ';' + NAS_OR_PXY '=' IP '/' clisecret_key ';' { /* Add this client */ - if ( rgw_clients_add( $4, &buf, buf_sz ) ) { + if ( rgw_clients_add( $4, &buf, buf_sz, $2 ) ) { yyerror (&yylloc, conffile, "Error parsing / adding client !"); YYERROR; } diff -r b9167b4de7dc -r 1c2f5ee38039 extensions/app_radgw/rgw_main.c --- a/extensions/app_radgw/rgw_main.c Thu Aug 26 14:10:03 2010 +0900 +++ b/extensions/app_radgw/rgw_main.c Fri Aug 27 10:59:51 2010 +0900 @@ -41,7 +41,7 @@ /* Extension entry point called by freeDiameter */ static int rgw_main(char * conffile) { - CHECK_FCT( rgw_msg_init() ); + CHECK_FCT( rgw_clients_init() ); CHECK_FCT( rgw_servers_init() ); diff -r b9167b4de7dc -r 1c2f5ee38039 extensions/app_radgw/rgw_msg.c --- a/extensions/app_radgw/rgw_msg.c Thu Aug 26 14:10:03 2010 +0900 +++ b/extensions/app_radgw/rgw_msg.c Fri Aug 27 10:59:51 2010 +0900 @@ -78,38 +78,6 @@ return 0; } -/* Check if the message has a valid authenticator, and update the meta-data accordingly */ -int rgw_msg_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth) -{ - unsigned char * key; - size_t keylen; - int count; - - TRACE_ENTRY("%p %p %p", msg, cli, req_auth); - - CHECK_PARAMS(msg && cli); - - CHECK_FCT(rgw_clients_getkey(cli, &key, &keylen)); - - count = radius_msg_count_attr(&msg->radius, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 0); - if (count > 1) { - TRACE_DEBUG(INFO, "Too many Message-Authenticator attributes (%d), discarding message.", count); - return EINVAL; - } - if (count == 0) { - TRACE_DEBUG(FULL, "Message does not contain a Message-Authenticator attributes."); - msg->valid_mac = 0; - } else { - if (radius_msg_verify_msg_auth( &msg->radius, key, keylen, req_auth )) { - TRACE_DEBUG(INFO, "Invalid Message-Authenticator received, discarding message."); - return EINVAL; - } - msg->valid_mac = 1; - } - - return 0; -} - /* Dump a message (inspired from radius_msg_dump) -- can be used safely with a struct radius_msg as parameter (we don't dump the metadata) */ void rgw_msg_dump(struct rgw_radius_msg_meta * msg) { @@ -131,56 +99,8 @@ for (i = 0; i < msg->radius.attr_used; i++) { struct radius_attr_hdr *attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[i]); fd_log_debug(" - len:%3hhu, type:0x%02hhx (%s)\n", attr->length, attr->type, rgw_msg_attrtype_str(attr->type)); - /* If we need to dump the value, it's better to call directly radius_msg_dump instead... */ + radius_msg_dump_attr_val(attr); } fd_log_debug("-----------------------------\n"); } -static struct dict_object * cache_orig_host = NULL; -static struct dict_object * cache_orig_realm = NULL; - -int rgw_msg_init(void) -{ - TRACE_ENTRY(); - CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &cache_orig_host, ENOENT) ); - CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_realm, ENOENT) ); - return 0; -} - -/* Create a new Diameter msg with origin-host & realm */ -int rgw_msg_create_base(struct rgw_client * cli, struct msg ** diam) -{ - char * fqdn; - char * realm; - - struct avp *avp = NULL; - union avp_value avp_val; - - TRACE_ENTRY("%p %p", cli, diam); - CHECK_PARAMS( cli && diam && (*diam == NULL) ); - - /* Get information on this peer */ - CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) ); - - /* Create an empty Diameter message so that extensions can store their AVPs */ - CHECK_FCT( fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam ) ); - - /* Add the Origin-Host as next AVP */ - CHECK_FCT( fd_msg_avp_new ( cache_orig_host, 0, &avp ) ); - memset(&avp_val, 0, sizeof(avp_val)); - avp_val.os.data = (unsigned char *)fqdn; - avp_val.os.len = strlen(fqdn); - CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); - CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); - - /* Add the Origin-Realm as next AVP */ - CHECK_FCT( fd_msg_avp_new ( cache_orig_realm, 0, &avp ) ); - memset(&avp_val, 0, sizeof(avp_val)); - avp_val.os.data = (unsigned char *)realm; - avp_val.os.len = strlen(realm); - CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); - CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); - - /* Done! */ - return 0; -} diff -r b9167b4de7dc -r 1c2f5ee38039 extensions/app_radgw/rgw_worker.c --- a/extensions/app_radgw/rgw_worker.c Thu Aug 26 14:10:03 2010 +0900 +++ b/extensions/app_radgw/rgw_worker.c Fri Aug 27 10:59:51 2010 +0900 @@ -96,7 +96,7 @@ /* process the data */ /* Check authenticator, if any */ - CHECK_FCT_DO( rgw_msg_auth_check(msg, cli, NULL), + CHECK_FCT_DO( rgw_clients_auth_check(msg, cli, NULL), { /* An error occurred, discard message */ rgw_msg_free(&msg); @@ -117,22 +117,16 @@ continue; /* the message was a duplicate */ } - /* Check that IP is coherent with the identity in the message */ - CHECK_FCT_DO( rgw_clients_check_origin(msg, cli), + diam_msg = NULL; + /* Note: after this point, the radius message buffer may not be consistent with the array of attributes anymore. */ + + /* Check that IP is coherent with the identity in the message, and create an empty message with only Origin information */ + CHECK_FCT_DO( rgw_clients_create_origin(msg, cli, &diam_msg), { /* An error occurred, discard message */ - rgw_msg_free(&msg); - rgw_clients_dispose(&cli); - continue; - } ); - - /* Note: after this point, the radius message buffer may not be consistent with the array of attributes anymore. */ - diam_msg = NULL; - - /* Create an empty message with only Origin information (no session, no destination -- added by the plugins) */ - CHECK_FCT_DO( rgw_msg_create_base(cli, &diam_msg), - { - /* An error occurred, discard message */ + if (diam_msg) { + CHECK_FCT_DO( fd_msg_free(diam_msg), ); + } rgw_msg_free(&msg); rgw_clients_dispose(&cli); continue;