/* radius.cc * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * Copyright (C) 2004,2007 Benjamin C. R. LaHaise * Copyright (C) 2009 Lhnet.ca Inc. * $Id: radius.cc,v 1.6 2004/10/20 05:09:54 bcrl Exp $ * Released under the GNU Public License. See LICENSE file for details. */ #include #include #include #include #include #include #include #include #include #include "config.h" #include "radius.h" #include "md5.h" #include "babd.h" #include "ctrlfd.h" #include "iface.h" #if 0 #include "fr_md5.h" #include "fr_md5.c" #include "hmac.c" #endif extern void pretty_dump(int len, const unsigned char *buf); #define RETRANS_TIME (5*100) /* 5s */ #define RETRANS_MAX 10 u32 radius_local_ip = 0; static u32 radius_primary_dns = 0; static u32 radius_secondary_dns = 0; #define DEFAULT_RADIUS_DYN_IP_POOL "radius" static char radius_dyn_ip_pool[256] = DEFAULT_RADIUS_DYN_IP_POOL; static char radius_secret[256] = "testing123"; static int radius_secret_len = 10; static RadiusClient *cur_auth_radius_client; static RadiusClient *first_radius_client; static RadiusClient *last_radius_client; static RadiusClient *first_radius_acct_client; static RadiusClient *last_radius_acct_client; struct user_map *user_map; struct user_map *user_map_lookup(const char *old) { for (struct user_map *map = user_map; map; map = map->next) { if (!strcmp(map->old_user, old)) return map; } return NULL; } static void do_radius_map_user(ctrlfd_t *cfd, char *str) { int do_del = 0; if (!strncmp(str, "--del ", 6)) { str += 6; do_del = 1; } char *old_user = str, *new_user; if (isspace(*str)) goto usage; while (*str && !isspace(*str)) str++; if (!*str) { usage: cfd->printf("usage: bradius map-user [--del] \n"); cfd->done(-2); return; } *str++ = 0; new_user = str; while (*str) { if (isspace(*str)) goto usage; str++; } struct user_map **mapp; for (mapp = &user_map; *mapp; mapp = &(*mapp)->next) { if (!strcmp(old_user, (*mapp)->old_user) && !strcmp(new_user, (*mapp)->new_user)) break; } if ((!*mapp && do_del) || (*mapp && !do_del)) { cfd->printf("user mapping '%s' => '%s' %s found\n", old_user, new_user, do_del ? "not" : "already"); cfd->done(-2); return; } if (do_del) { struct user_map *map = *mapp; *mapp = (*mapp)->next; free(map->old_user); free(map->new_user); delete map; cfd->done(0); return; } struct user_map *map = new struct user_map; map->next = user_map; map->old_user = strdup(old_user); map->new_user = strdup(new_user); user_map = map; cfd->done(0); return; } RadiusClient::RadiusClient(struct sockaddr_in sin, unsigned long keepalive, struct sockaddr_in *loc_sin, ctrlfd_t *cfd) { m_init_failed = 1; memset(&m_reqs, 0, sizeof(m_reqs)); m_secret = NULL; m_secret_len = 0; m_next_ident = 0; m_up = 1; /* assume radius client is up initially */ m_next = NULL; m_sin = sin; if (keepalive && keepalive < 11) keepalive = 11; /* min for jitter */ m_keepalive_interval = keepalive; memset(&m_local_sin, 0, sizeof(m_local_sin)); if (loc_sin) { m_local_sin = *loc_sin; m_have_local_sin = 1; } else m_have_local_sin = 0; m_sockfd = socket(sin.sin_family, SOCK_DGRAM, IPPROTO_UDP); if (m_sockfd < 0) { cfd->perror("RadiusClient::RadiusClient(): socket"); cfd->done(-2); return; } if (m_have_local_sin && bind(m_sockfd, (struct sockaddr *)&m_local_sin, sizeof(m_local_sin))) { cfd->perror("RadiusClient::RadiusClient(): bind"); cfd->done(-2); return; } if (connect(m_sockfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { cfd->perror("RadiusClient::RadiusClient(): connect"); cfd->done(-2); return; } this->SelectSetEvents(m_sockfd, SEL_READ); if (m_keepalive_interval) Start(m_keepalive_interval); fprintf(stderr, "radius started\n"); m_init_failed = 0; } void RadiusClient::TimerExpired(void) { if (m_keepalive_interval) { unsigned jitter = rand(); jitter %= 20; RadiusStatusReq *req = new RadiusStatusReq(NULL); req->Go_status(this); Start(m_keepalive_interval + jitter - 10); } } RadiusClient::~RadiusClient() { for (int i = 0; i < RADIUSCLIENT_N_REQS; i++) { RadiusReq *req = m_reqs[i]; if (!req) continue; delete req; } if (this == cur_auth_radius_client) cur_auth_radius_client = m_next; if (m_sockfd >= 0) { this->SelectRemoveEvent(m_sockfd, SEL_READ); close(m_sockfd); } } /* * the write callback is only installed when we have a packet to transmit, but * it was delayed previously. Hopefully select wakes us up before the request * times out. */ void RadiusClient::SelectEvent(int fd, SelectEventType event) { if (event & SEL_READ) { unsigned char buf[RAD_MAX_PKT_SIZE]; int l; l = read(fd, buf, RAD_MAX_PKT_SIZE); if (l > 0 && l < RAD_MAX_PKT_SIZE) { unsigned ident = buf[1]; RadiusReq *req = m_reqs[ident]; if (req) { CuscArray *pkt = new CuscArray(buf, (uint)l); req->RadiusRsp(pkt); delete pkt; } } else if (l < 0) /* DEBUG */ perror("read(radius)"); } if (event & SEL_WRITE) { /* okay, no more requests, I hope */ SelectRemoveEvent(fd, SEL_WRITE); } } void RadiusClient::QueueReq(RadiusReq *req) { for (unsigned i=0; iSetIdent(m_next_ident); m_next_ident = (m_next_ident + 1) % RADIUSCLIENT_N_REQS; Retrans(req); } void RadiusClient::Retrans(RadiusReq *req) { if (!req->HaveIdent()) return; int err = write(m_sockfd, req->m_pkt.m_start, req->m_pkt.GetLength()); if (err < 0 && errno != ECONNREFUSED) perror("RadiusClient::QueueReq(): write"); } void RadiusReq::TimerExpired(void) { /* If we've already hit our callback, the request must have expired. */ if (m_cfg_done) { if (m_client) m_client->RemoveReq(this, m_ident); m_client = NULL; return; } if (m_retrans_count++ < RETRANS_MAX) { m_client->Retrans(this); Start(RETRANS_TIME); return; } /* Damn. Well, callback so the protocol can report that auth failed. */ RadiusCallback(NULL); } void RadiusReq::RadiusCallback(OptionSet_t *options) { if (options && options->cancel_obj == this) options->cancel_obj = NULL; if (!m_cfg_done) { m_cfg_done = 1; if (!options) options = m_cfg.m_options; if (m_cfg.m_options && m_cfg.m_options->cancel_obj == this) m_cfg.m_options->cancel_obj = NULL; if (options) { if (m_cfg.reserved1) m_cfg.reserved1(m_cfg.reserved2, options); } } delete this; } #include "ascend-filters.h" int RadiusReq::decodeAscendDataFilter(u8 *data, int len, OptionSet_t *options) { ascend_filter_t *filter = (ascend_filter_t *)data; if (len != sizeof(ascend_filter_t)) return 0; fprintf(stderr, "type(%d) forward(%d) dir(%d) fill(%d)\n", filter->type, filter->forward, filter->direction, filter->fill); if (filter->type != RAD_FILTER_IP) { fprintf(stderr, "decodeAscendDataFilter: invalid filter type(%d)\n", filter->type); return 0; } fprintf(stderr, "ascend_ip_filter: src(%s)/%d dst(%s)/%d\n" "ascend_ip_filter: proto(%d) established(%d)\n" "ascend_ip_filter: srcport(%d) srcportcomp(%d) dstport(%d) dstportcomp(%d)\n", iptostr(ntohl(filter->u.ip.srcip)), filter->u.ip.srcmask, iptostr(ntohl(filter->u.ip.dstip)), filter->u.ip.dstmask, filter->u.ip.proto, filter->u.ip.established, ntohs(filter->u.ip.srcport), filter->u.ip.srcPortComp, ntohs(filter->u.ip.dstport), filter->u.ip.dstPortComp ); /* we only support no compare/equal/not equal */ RadFilterComparison comp; comp = (RadFilterComparison)filter->u.ip.srcPortComp; if ((comp != RAD_NO_COMPARE) && (comp != RAD_COMPARE_EQUAL) && (comp != RAD_COMPARE_NOT_EQUAL)) return 0; /* we only support no compare/equal/not equal */ comp = (RadFilterComparison)filter->u.ip.dstPortComp; if ((comp != RAD_NO_COMPARE) && (comp != RAD_COMPARE_EQUAL) && (comp != RAD_COMPARE_NOT_EQUAL)) return 0; fprintf(stderr, "looks good!\n"); return 0; } int RadiusReq::decodeAscendRadiusRsp(u8 *data, int length, OptionSet_t *options) { u8 type, len; u32 ip; if (length < 2) return 0; type = data[0]; len = data[1]; if (len < 2 || len > length) return 0; data += 2; length -= 2; switch (type) { case 132: /* Ascend-Client-Gateway */ if (len < 4) return 0; ip = ntohl(*(u32 *)data); if (ip) options->loc_ip = ip; break; case 135: /* Ascend-Client-Primary-DNS */ if (len < 4) return 0; ip = ntohl(*(u32 *)data); if (ip) options->dns_primary = ip; break; case 136: /* Ascend-Client-Secondary-DNS */ if (len < 4) return 0; ip = ntohl(*(u32 *)data); if (ip) options->dns_second = ip; break; case 242: /* Ascend-Data-Filter */ decodeAscendDataFilter(data, len-2, options); break; default: break; } return 0; } int RadiusReq::decodeBayNetworksRadiusRsp(u8 *data, int length, OptionSet_t *options) { u8 type, len; u32 num; if (length < 2) return 0; type = data[0]; len = data[1]; if (len < 2 || len > length) return 0; data += 2; length -= 2; switch (type) { case 62: /* Annex-MRRU - integer */ if (len < 4) return 0; num = ntohl(*(u32 *)data); if (num) { if (!options->pol) options->pol = new policy_t; options->pol->Set(P_MRRU, num); } break; default: break; } return 0; } int RadiusReq::decodeERXRadiusRsp(u8 *data, int length, OptionSet_t *options) { u8 type, len; u32 ip; if (length < 2) return 0; type = data[0]; len = data[1]; if (len < 2 || len > length) return 0; data += 2; length -= 2; switch (type) { case 2: /* ERX-Address-Pool-Name */ if (len < 3) return 0; len -= 2; if (options->rem_ip_pool) free(options->rem_ip_pool); options->rem_ip_pool = strndup((char *)data, len); break; case 26: /* ERX-Qos-Profile-Name: string */ if (len < 3) return 0; len -= 2; if (options->qos_profile_name) free(options->qos_profile_name); options->qos_profile_name = strndup((char *)data, len); break; case 64: { /* ERX-Tunnel-Group */ if (len < 3) return 0; len -= 2; if (len > MAX_VALUE_LEN-1) len = MAX_VALUE_LEN-1; memcpy(options->tunnel_group, data, len); options->tunnel_group[len] = 0; options->tunnel_group_len = len; fprintf(stderr, "tunnel-group '%s' len=%d\n", options->tunnel_group, options->tunnel_group_len); break; } case 4: /* ERX-Primary-DNS */ if (len < 4) return 0; ip = ntohl(*(u32 *)data); if (ip) options->dns_primary = ip; break; case 5: /* ERX-Secondary-DNS */ if (len < 4) return 0; ip = ntohl(*(u32 *)data); if (ip) options->dns_second = ip; break; case 129: /* ERX-IPv6-NdRa-Prefix */ if (len < 3) return 0; len -= 2; if (len != 18) { fprintf(stderr, "ERX-IPv6-NdRa-Prefix: len(%d) != 18\n", len); return 0; } /* data is u16 prefix_len, u8 prefix[16] */ options->ipv6_ndra_len = ntohs(*(u16*)(data)); memcpy(options->ipv6_ndra, &data[2], 16); data += 2; fprintf(stderr, "ERX-IPv6-NdRa-Prefix: " "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%u\n", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], ntohs(*(u16*)(data-2))); if (!options->pol) options->pol = new policy_t; options->pol->Set(P_WANT_IPV6, 1); break; default: break; } return 0; } int RadiusReq::decodeLucentRadiusRsp(u8 *data, int length, OptionSet_t *options) { u16 type, len; u32 num; if (length < 2) return 0; /* Lucent options have 16 bits for the type */ type = ntohs(*(u16 *)data); len = data[2]; if (len < 3 || len > length) return 0; data += 3; length -= 3; switch (type) { case 310: /* Lucent-Reverse-Path-Check - integer */ if (len < 4) return 0; num = ntohl(*(u32 *)data); if (!options->pol) options->pol = new policy_t; options->pol->Set(P_REVERSE_PATH, num); break; case 321: /* Lucent-LCP-Keepalive-Period - integer */ if (len < 4) return 0; num = ntohl(*(u32 *)data); if (num) { if (!options->pol) options->pol = new policy_t; options->pol->Set(P_ECHO_INTERVAL, num); } break; case 322: /* Lucent-LCP-Keepalive-Missed-Limit - integer */ if (len < 4) return 0; num = ntohl(*(u32 *)data); if (num) { if (!options->pol) options->pol = new policy_t; options->pol->Set(P_ECHO, num); } break; default: break; } return 0; } int RadiusReq::decodeVendorRadiusRsp(u8 *data, int length, OptionSet_t *options) { u32 vendor_id; if (length < 4) return 0; vendor_id = ntohl(*(u32 *)data); data += 4; length -= 4; switch (vendor_id) { case 529: /* Ascend */ return decodeAscendRadiusRsp(data, length, options); case 1584: /* Bay Networks */ return decodeBayNetworksRadiusRsp(data, length, options); case 4846: /* Lucent */ return decodeLucentRadiusRsp(data, length, options); case 4874: /* ERX */ return decodeERXRadiusRsp(data, length, options); default: fprintf(stderr, "unknown VendorRadiusRsp:\n"); pretty_dump(length + 4, data - 4); return 0; /* FIXME: do we need to do anything better */ } return 0; } /* str is in the format "/ " */ void option_add_route(OptionSet_t *options, char *str) { RouteMsg_t *route; const char *tmp; if (options->nr_routes >= MAX_OPTIONSET_ROUTES) return; route = &options->routes[options->nr_routes++]; route->dst_addr = htonl(strtoip(str)); tmp = strchr(str, '/'); if (tmp && *tmp == '/') { int bits = atoi(tmp+1); route->mask = 0xffffffffU; route->mask = route->mask >> (32 - bits); route->mask = route->mask << (32 - bits); } else route->mask = 0xffffff00U; route->mask = htonl(route->mask); tmp = strchr(str, ' '); if (tmp) route->gw_addr = htonl(strtoip(tmp)); else route->gw_addr = options->rem_ip; route->flags = (route->mask == 0xffffffffU) ? ROUTE_HOST : ROUTE_NET; if (tmp) tmp = strchr(tmp, ' '); if (tmp) route->metric = atoi(tmp); else route->metric = 0; route->dev[0] = 0; } void RadiusReq::RadiusRsp(CuscArray *rsppkt) { u8 code; u8 ident; u16 len; MD5_CTX md5; int trailing = rsppkt->GetLength(); trailing -= 4 + 16; if (trailing < 0) { fprintf(stderr, "bad radius response\n"); return; } MD5Init(&md5); MD5Update(&md5, rsppkt->m_start, 4); /* code+id+length */ MD5Update(&md5, m_authenticator, 16); if (trailing) MD5Update(&md5, rsppkt->m_start+4+16, trailing);/* code+id+length */ MD5Update(&md5, (u8*)radius_secret, radius_secret_len); MD5Final(&md5); code = rsppkt->Pull8(); ident = rsppkt->Pull8(); len = rsppkt->Pull16(); if (debug) fprintf(stderr, "RadiusRspr[%d, %d, %d]\n", code, ident, len); /* pull off authenticator */ u8 authenticator[16]; rsppkt->Pull(authenticator, 16); if (memcmp(authenticator, md5.digest, 16)) { fprintf(stderr, "radius: bad authenticator\n"); return; } RadiusParseRsp(rsppkt, code); } void RadiusReq::decodeFramedIpv6Route(OptionSet_t *options, char *data, int len) { if (options->nr_ipv6_routes >= MAX_OPTIONSET_ROUTES) { fprintf(stderr, "Too many IPv6 routes in radius response.\n"); return; } RouteIpv6_t *rt = &options->ipv6_routes[options->nr_ipv6_routes]; char *str = index(data, '/'); if (str) *str++ = 0; if (1 != inet_pton(AF_INET6, data, (void *)rt->dst)) { perror("RadiusReq::decodeFramedIpv6Route: inet_pton"); return; } if (!str) rt->dst_len = 128; else rt->dst_len = atoi(str); options->nr_ipv6_routes ++; } void RadiusReq::RadiusParseRsp(CuscArray *rsppkt, u8 code) { OptionSet_t tmp, *options = &tmp; if (m_cfg.m_options) options->Set(m_cfg.m_options); options->loc_ip = radius_local_ip; options->dns_primary = radius_primary_dns; options->dns_second = radius_secondary_dns; /* okay, process the attribute-value pairs */ while (rsppkt->GetLength() >= 2) { u8 type = rsppkt->Pull8(); u8 length = rsppkt->Pull8(); if (length < 2) goto out_badpkt; length -= 2; if (rsppkt->GetLength() < length || length > 64) goto out_badpkt; u8 data[256]; rsppkt->Pull(data, length); switch (type) { case RT_USER_PASSWORD: break; case RT_CHAP_PASSWORD: break; case RT_SERVICE_TYPE: if (length != 4) goto out_badpkt; switch (ntohl(*(u32 *)data)) { case 2: /* Framed==PPP */ break; default: goto denied; } break; case RT_FRAMED_IP_ADDRESS: if (length != 4) goto out_badpkt; options->rem_ip = ntohl(*(u32 *)data); break; case RT_FRAMED_IP_NETMASK: if (length != 4) goto out_badpkt; options->netmask = ntohl(*(u32 *)data); options->netroute = 1; break; case RT_FRAMED_ROUTE: if (length < 3) goto out_badpkt; data[length] = 0; option_add_route(options, (char *)data); break; case RT_FRAMED_COMPRESSION: if (length != 4) goto out_badpkt; switch (ntohl(*(u32 *)data)) { case 0: options->vjc = 0; break; case 1: options->vjc = 0; break; default: break; } break; case RT_IDLE_TIMEOUT: if (length != 4) goto out_badpkt; options->droptime = ntohl(*(u32 *)data); break; case RT_PORT_LIMIT: if (length != 4) goto out_badpkt; options->max_links = ntohl(*(u32 *)data); break; case RT_VENDOR_SPECIFIC: if (decodeVendorRadiusRsp(data, length, options) < 0) break; /* FIXME? Do we need to ignore this? */ break; case RT_FRAMED_IPV6_ROUTE: data[length] = 0; decodeFramedIpv6Route(options, (char *)data, length); break; default: break; } } if (code == RC_ACCESS_ACCEPT) { options->is_valid = 1; if (!options->rem_ip && options->rem_ip_pool) { options->rem_ip = 0xffffffffU; } else if (!options->rem_ip) { options->rem_ip = 0xffffffffU; if (options->rem_ip_pool) free(options->rem_ip_pool); options->rem_ip_pool = strdup(radius_dyn_ip_pool); } } else options->is_valid = 0; Stop(); RadiusCallback(options); return; denied: out_badpkt: return; } static int random_fd = -1; void get_random(u8 *buf, int len) { if (random_fd < 0) { random_fd = open("/dev/urandom", O_RDONLY); if (random_fd < 0) { perror("open(/dev/urandom, O_RDONLY)"); return; } } do { int c = read(random_fd, buf, len); if (c <= 0) { perror("get_random: read"); break; } buf += c; len -= c; } while (len > 0); } RadiusReq::RadiusReq(void) { m_ident = 0; m_cfg_done = 0; m_have_ident = 0; m_retrans_count = 0; m_have_message_authenticator_tail = 0; m_calc_simple_authenticator = 0; memset(&m_cfg, 0, sizeof m_cfg); /* build the Radius packet */ get_random(m_authenticator, sizeof(m_authenticator)); MD5Init(&m_md5); MD5Update(&m_md5, (u8*)radius_secret, radius_secret_len); MD5Update(&m_md5, m_authenticator, 16); /* code+id+length */ MD5Final(&m_md5); } RadiusReq::~RadiusReq(void) { Stop(); if (m_cfg.m_options) { m_cfg.m_options->cancel_obj = NULL; m_cfg.m_options = NULL; } if (m_client) m_client->RemoveReq(this, m_ident); } void RadiusReq::CancelGetConfig(void) { if (m_cfg.m_options->cancel_obj != this) *(char *)0 = 0; m_cfg.m_options->cancel_obj = NULL; m_cfg.m_options = NULL; delete this; } static RadiusClient *choose_next_radius_client(void) { RadiusClient *client = cur_auth_radius_client; if (!client) client = first_radius_client; cur_auth_radius_client = client->m_next; if (!cur_auth_radius_client) cur_auth_radius_client = first_radius_client; return client; } static RadiusClient *choose_radius_client(void) { RadiusClient *first = choose_next_radius_client(); if (!first) return NULL; RadiusClient *client = first; while (!client->is_Up()) { client = choose_next_radius_client(); if (client == first) break; } return client; } void RadiusReq::Go_cfg(void (*cbf)(void *, OptionSet_t *), void *obj, OptionSet_t *options) { if (options->cancel_obj) options->cancel_obj->CancelGetConfig(); options->cancel_obj = this; m_cfg.reserved1 = cbf; m_cfg.reserved2 = obj; m_cfg.m_options = options; m_pkt.Put(m_authenticator, 16); /* User Name attribute */ m_pkt.Put8(RT_USER_NAME); int len = strlen(m_cfg.m_options->user); m_pkt.Put8(len+2); m_pkt.Put((u8*)m_cfg.m_options->user, len); /* */ if (m_cfg.m_options->proto_id == PAUTH_CHAP) { int len = strlen(m_cfg.m_options->passwd); m_pkt.Put8(RT_CHAP_PASSWORD); m_pkt.Put8(len+2); m_pkt.Put((u8*)m_cfg.m_options->passwd, len); } else { /* PAP */ int i, len = strlen(m_cfg.m_options->passwd); m_pkt.Put8(RT_USER_PASSWORD); m_pkt.Put8(((len+15)& ~15)+2); fprintf(stderr, "radius: user[%s] pap[%s]\n", m_cfg.m_options->user, m_cfg.m_options->passwd); for (i=0; ipasswd[i] ^ m_md5.digest[i%16]); while (i & 0xf) m_pkt.Put8(m_md5.digest[i++%16]); } /* we only do framed ppp */ m_pkt.Put8(RT_SERVICE_TYPE); m_pkt.Put8(6); /* Length */ m_pkt.Put32(2); /* Framed */ m_pkt.Put8(RT_FRAMED_PROTOCOL); m_pkt.Put8(6); /* Length */ m_pkt.Put32(1); /* PPP */ /* we push these onto the front last as we need to know the length */ m_pkt.Push16(m_pkt.GetLength()+4); m_pkt.Push8(0); /* placeholder for identifier */ m_pkt.Push8(RC_ACCESS_REQUEST); /* code */ Start(RETRANS_TIME); m_client = choose_radius_client(); m_client->QueueReq(this); } void RadiusReq::Go(AcctMessage_t *acct) { m_pkt.Put("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16); /* User Name attribute */ m_pkt.Put8(RT_USER_NAME); int len = strlen(acct->user); m_pkt.Put8(len+2); m_pkt.Put((u8*)acct->user, len); /* */ m_pkt.Put8(RT_ACCT_STATUS_TYPE); m_pkt.Put8(6); /* Length */ m_pkt.Put32(acct->type); char str[256]; len = sprintf(str, "%s.%s", acct->port, acct->ifname); m_pkt.Put8(RT_ACCT_SESSION_ID); m_pkt.Put8(2+len); m_pkt.Put((u8*)str, len); /* byte counters*/ m_pkt.Put8(RT_ACCT_INPUT_GIGAWORDS); m_pkt.Put8(6); /* Length */ m_pkt.Put32((u32)(acct->in_octets >> 32)); m_pkt.Put8(RT_ACCT_INPUT_OCTETS); m_pkt.Put8(6); /* Length */ m_pkt.Put32((u32)acct->in_octets); m_pkt.Put8(RT_ACCT_OUTPUT_GIGAWORDS); m_pkt.Put8(6); /* Length */ m_pkt.Put32((u32)(acct->out_octets >> 32)); m_pkt.Put8(RT_ACCT_OUTPUT_OCTETS); m_pkt.Put8(6); /* Length */ m_pkt.Put32((u32)acct->out_octets); if (acct->rem_ip_address) { m_pkt.Put8(RT_FRAMED_IP_ADDRESS); m_pkt.Put8(6); m_pkt.Put32(acct->rem_ip_address); } if (acct->loc_ip_address) { m_pkt.Put8(RT_NAS_IP_ADDRESS); m_pkt.Put8(6); m_pkt.Put32(acct->loc_ip_address); } if (acct->phone[0]) { int len = strlen(acct->phone); m_pkt.Put8(RT_CALLING_STATION_ID); m_pkt.Put8(2 + len); m_pkt.Put(acct->phone, len); } if (acct->connected_time) { m_pkt.Put8(RT_ACCT_SESSION_TIME); m_pkt.Put8(6); m_pkt.Put32(acct->connected_time); } /* we push these onto the front last as we need to know the length */ m_pkt.Push16(m_pkt.GetLength()+4); m_pkt.Push8(0); /* placeholder for identifier */ m_pkt.Push8(RC_ACCOUNTING_REQUEST); /* code */ Start(RETRANS_TIME); m_calc_simple_authenticator = 1; m_client = first_radius_acct_client; m_client->QueueReq(this); } void RadiusReq::SetIdent(u8 ident) { if (m_pkt.GetLength() >= 2) { m_pkt.m_start[1] = ident; m_have_ident = 1; } m_ident = ident; if (m_calc_simple_authenticator) { MD5Init(&m_md5); MD5Update(&m_md5, m_pkt.m_start, m_pkt.GetLength()); MD5Update(&m_md5, (u8*)radius_secret, radius_secret_len); MD5Final(&m_md5); memcpy(m_pkt.m_start + 4, m_md5.digest, 16); memcpy(m_authenticator, m_md5.digest, 16); } if (m_have_message_authenticator_tail) { hmac_md5(m_pkt.m_start, m_pkt.GetLength(), (const u8 *)radius_secret, radius_secret_len, &m_md5); m_pkt.Chop(16); m_pkt.Put(m_md5.digest, 16); } } void radius_acct(AcctMessage_t *acct) { RadiusReq *req = new RadiusReq(); req->Go(acct); } RadiusStatusReq::RadiusStatusReq(ctrlfd_t *cfd) { m_cfd = cfd; if (cfd) cfd->unlinkme(&m_cfd); } /* call by the timeout code */ void RadiusStatusReq::RadiusCallback(OptionSet_t *options) { if (m_cfd) { m_cfd->printf("status request timed out\n"); m_cfd->printf("marking server as down (previous state %s)\n", m_client->is_Up() ? "up" : "down"); m_cfd->done(-2); } m_client->mark_Down(); delete this; } void RadiusStatusReq::RadiusParseRsp(CuscArray *pkt, u8 code) { int ret; if (!m_cfd) goto done; if (code == RC_ACCESS_ACCEPT) { m_cfd->printf("okay!\n"); ret = 0; } else { m_cfd->printf("status not accepted\n"); ret = -2; } m_cfd->done(ret); m_cfd = NULL; done: m_client->mark_Up(); delete this; } void RadiusStatusReq::Go_status(RadiusClient *client) { m_pkt.Put(m_authenticator, 16); /* Message-Authenticator (80) 0x00 */ m_pkt.Put8(80); m_pkt.Put8(18); m_pkt.Put("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16); m_have_message_authenticator_tail = 1; /* we push these onto the front last as we need to know the length */ m_pkt.Push16(m_pkt.GetLength()+4); m_pkt.Push8(0); /* placeholder for identifier */ m_pkt.Push8(RC_STATUS_SERVER); /* code */ Start(RETRANS_TIME); m_client = client; m_client->QueueReq(this); } static void do_status(ctrlfd_t *cfd, const char *str, int acct) { struct sockaddr_in sin; if (bstrtoip_sin(str, &sin)) { cfd->printf("invalid ip address '%s'\n", str); cfd->done(-2); return; } if (!sin.sin_port) sin.sin_port = acct ? htons(1813) : htons(1812); RadiusClient **first = acct ? &first_radius_acct_client : &first_radius_client; if (!*first) { cfd->printf("no radius clients found!\n"); cfd->done(-2); return; } RadiusClient *cur = *first; while (cur) { if (cur->is_sin(&sin)) { cfd->printf("found client; sending status request\n"); RadiusStatusReq *req = new RadiusStatusReq(cfd); req->Go_status(cur); return; } cur = cur->m_next; } cfd->printf("radius client not found!\n"); cfd->done(-2); } /*=========================================*/ void remove_radius(ctrlfd_t *cfd, const char *str, int acct) { struct sockaddr_in sin, local_sin, *loc_sin = NULL; if (!strncmp(str, "--bind=", 7)) { str += 7; if (bstrtoip_sin(str, &local_sin)) { cfd->printf("invalid local ip address '%s'\n", str); cfd->done(-2); return; } loc_sin = &local_sin; while (*str && !isspace(*str)) str++; while (*str && isspace(*str)) str++; } if (bstrtoip_sin(str, &sin)) { cfd->printf("invalid ip address '%s'\n", str); cfd->done(-2); return; } if (!sin.sin_port) sin.sin_port = acct ? htons(1813) : htons(1812); RadiusClient **first = acct ? &first_radius_acct_client : &first_radius_client; RadiusClient **last = acct ? &last_radius_acct_client : &last_radius_client; if (!*first) { cfd->printf("no radius clients found!\n"); cfd->done(-2); return; } RadiusClient *cur = *first; RadiusClient **prevp = first; RadiusClient *prev = NULL; while (cur) { if (!cur->is_local_sin(loc_sin)) goto next; if (cur->is_sin(&sin)) { cfd->printf("found client\n"); *prevp = cur->m_next; if (cur == *last) *last = prev; delete cur; cfd->printf("%sradius client removed\n", *first ? "" : "last "); cfd->done(0); return; } next: prev = cur; prevp = &cur->m_next; cur = cur->m_next; } cfd->printf("radius client not found!\n"); cfd->done(-2); } void setup_radius(ctrlfd_t *cfd, const char *str, int acct) { unsigned long keepalive_interval = 0; struct sockaddr_in sin, local_sin, *loc_sin = NULL; if (!strncmp(str, "--bind=", 7)) { str += 7; if (bstrtoip_sin(str, &local_sin)) { cfd->printf("invalid local ip address '%s'\n", str); cfd->done(-2); return; } loc_sin = &local_sin; while (*str && !isspace(*str)) str++; while (*str && isspace(*str)) str++; } if (bstrtoip_sin(str, &sin)) { cfd->printf("invalid ip address '%s'\n", str); cfd->done(-2); return; } while (*str && !isspace(*str)) str++; while (*str && isspace(*str)) str++; if (*str && !strncmp(str, "keepalive ", 10)) { char *end = NULL; str += 10; keepalive_interval = strtoul(str, &end, 10); if (keepalive_interval == ULONG_MAX) { cfd->perror("invalid keepalive interval"); cfd->done(-2); return; } if (end && *end) { cfd->printf("excess string '%s' after keepalive\n", end); cfd->done(-2); return; } } else if(*str) { cfd->printf("unknown option '%s'\n", str); cfd->done(-2); return; } if (!sin.sin_port) sin.sin_port = acct ? htons(1813) : htons(1812); RadiusClient *client = new RadiusClient(sin, keepalive_interval, loc_sin, cfd); if (client->m_init_failed) { delete client; return; } RadiusClient **first = acct ? &first_radius_acct_client : &first_radius_client; RadiusClient **last = acct ? &last_radius_acct_client : &last_radius_client; if (!*first) *first = client; if (*last) (*last)->m_next = client; *last = client; if (acct) am_radius_acct_client = 1; else am_radius_client = 1; cfd->done(0); } static void do_show_status(ctrlfd_t *cfd, RadiusClient *client) { int nr_up = 0, nr_down = 0; cfd->printf("%-16s %s\n", "radius server", "state"); while (client) { const char *state; if (client->is_Up()) { state = "up"; nr_up++; } else { state = "down"; nr_down++; } cfd->printf("%-16s %s\n", client->get_ip(), state); client = client->m_next; } cfd->printf("Found %d up %d down\n", nr_up, nr_down); cfd->done(0); } void do_dns(ctrlfd_t *cfd, const char *str, u32 *dns_ip, const char *which) { u32 ip; ip = strtoip(str); if (ip == 0xffffffff) { cfd->printf("invalid ip for dns: '%s'\n", str); cfd->done(-2); return; } *dns_ip = ip; cfd->printf("set %s dns to %s\n", which, iptostr(*dns_ip)); cfd->done(0); } void do_bradius(ctrlfd_t *cfd, char *str) { while (*str && isspace(*str)) str++; if (!strncmp(str, "secret ", 7)) { str += 7; strncpy(radius_secret, str, sizeof radius_secret); radius_secret[-1 + sizeof radius_secret] = 0; radius_secret_len = strlen(radius_secret); cfd->printf("set radius secret len = %d\n", radius_secret_len); cfd->done(0); } else if (!strncmp(str, "add_auth ", 9) || !strncmp(str, "add-auth ", 9)) { str += 9; setup_radius(cfd, str, 0); } else if (!strncmp(str, "add_acct ", 9) || !strncmp(str, "add-acct ", 9)) { str += 9; setup_radius(cfd, str, 1); } else if (!strncmp(str, "del_auth ", 9) || !strncmp(str, "del-auth ", 9)) { str += 9; remove_radius(cfd, str, 0); } else if (!strncmp(str, "set-acct-update-interval ", 25)) { unsigned long interval; char *ptr = NULL; interval = strtoul(str + 25, &ptr, 10); if ((interval >= (ULONG_MAX / 100UL)) || (ptr && *ptr != 0)) { cfd->printf("invalid interval '%s'\n", str + 25); cfd->done(-2); } else { interim_update_interval = interval * 100UL; cfd->printf("interval set to %lu seconds\n", interval); cfd->done(0); } } else if (!strncmp(str, "del_acct ", 9) || !strncmp(str, "del-acct ", 9)) { str += 9; remove_radius(cfd, str, 1); } else if (!strncmp(str, "map-user ", 9)) { str += 9; do_radius_map_user(cfd, str); } else if (!strncmp(str, "status ", 7)) { str += 7; do_status(cfd, str, 0); } else if (!strncmp(str, "set-dynamic-ip-pool ", 20)) { str += 20; strncpy(radius_dyn_ip_pool, str, sizeof radius_dyn_ip_pool); cfd->done(0); } else if (!strcmp(str, "show-auth-status")) { do_show_status(cfd, first_radius_client); } else if (!strncmp(str, "primary-dns ", 12)) { do_dns(cfd, str + 12, &radius_primary_dns, "primary"); } else if (!strncmp(str, "secondary-dns ", 14)) { do_dns(cfd, str + 14, &radius_secondary_dns, "secondary"); } else { cfd->printf("Usage:\n"); cfd->printf(" add-auth --bind= [:] [keepalive ]\n"); cfd->printf(" del-auth --bind= [:]\n"); cfd->printf(" add-acct [:]\n"); cfd->printf(" del-acct [:]\n"); cfd->printf(" set-acct-update-interval \n"); cfd->printf(" map-user [--del] \n"); cfd->printf(" status [:]\n"); cfd->printf(" secret \n"); cfd->printf(" set-dynamic-ip-pool \n"); cfd->printf(" show-auth-status\n"); cfd->printf(" primary-dns \n"); cfd->printf(" secondary-dns \n"); cfd->done(-2); } } void bradius_show_running_config(ctrlfd_t *cfd, int verbose) { RadiusClient *client; for (client = first_radius_client; client; client = client->m_next) { char local_bind[64]; char keepalive[32]; char port[16]; if (client->m_keepalive_interval || verbose) { sprintf(keepalive, " keepalive %lu", client->m_keepalive_interval); } else keepalive[0] = 0; if (verbose || client->m_sin.sin_port != htons(1812)) sprintf(port, ":%d", ntohs(client->m_sin.sin_port)); else port[0] = 0; if (client->m_have_local_sin) { int l; l = sprintf(local_bind, "--bind=%s", iptostr(ntohl(client->m_local_sin.sin_addr.s_addr))); if (client->m_local_sin.sin_port) l += sprintf(local_bind + l, ":%u", ntohs(client->m_local_sin.sin_port)); local_bind[l++] = ' '; local_bind[l++] = 0; } else local_bind[0] = 0; cfd->printf("bradius add_auth %s%s%s%s\n", local_bind, iptostr(ntohl(client->m_sin.sin_addr.s_addr)), port, keepalive); } for (client = first_radius_acct_client; client; client = client->m_next) { char keepalive[32]; char port[16]; if (client->m_keepalive_interval || verbose) { sprintf(keepalive, " keepalive %lu", client->m_keepalive_interval); } else keepalive[0] = 0; if (verbose || client->m_sin.sin_port != htons(1813)) sprintf(port, ":%d", ntohs(client->m_sin.sin_port)); else port[0] = 0; cfd->printf("bradius add_acct %s%s%s\n", iptostr(ntohl(client->m_sin.sin_addr.s_addr)), port, keepalive); } if (am_radius_client || am_radius_acct_client || verbose) cfd->printf("bradius secret %s\n", radius_secret); if (interim_update_interval != (6 * 3600 * 100UL) || verbose) cfd->printf("bradius set-acct-update-interval %lu\n", interim_update_interval / 100); if (verbose || strcmp(radius_dyn_ip_pool, DEFAULT_RADIUS_DYN_IP_POOL)) cfd->printf("set-dynamic-ip-pool %s\n", radius_dyn_ip_pool); if (radius_primary_dns) cfd->printf("primary-dns %s\n", iptostr(radius_primary_dns)); if (radius_secondary_dns) cfd->printf("secondary-dns %s\n", iptostr(radius_secondary_dns)); for (struct user_map *map = user_map; map; map = map->next) cfd->printf("bradius map-user %s %s\n", map->old_user, map->new_user); }