#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <openssl/hmac.h>
#define WIMAX2ATTR(x) ((24757 << 16) | (x))
typedef struct rlm_wimax_t {
int delete_mppe_keys;
} rlm_wimax_t;
static const CONF_PARSER module_config[] = {
{ "delete_mppe_keys", PW_TYPE_BOOLEAN,
offsetof(rlm_wimax_t,delete_mppe_keys), NULL, "no" },
{ NULL, -1, 0, NULL, NULL }
};
static int wimax_detach(void *instance)
{
free(instance);
return 0;
}
static int wimax_instantiate(CONF_SECTION *conf, void **instance)
{
rlm_wimax_t *inst;
inst = rad_malloc(sizeof(*inst));
if (!inst) {
return -1;
}
memset(inst, 0, sizeof(*inst));
if (cf_section_parse(conf, inst, module_config) < 0) {
wimax_detach(inst);
return -1;
}
*instance = inst;
return 0;
}
static int wimax_authorize(void *instance, REQUEST *request)
{
VALUE_PAIR *vp;
instance = instance;
request = request;
vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID);
if (vp && (vp->length == 6)) {
int i;
uint8_t buffer[6];
memcpy(buffer, vp->vp_octets, 6);
for (i = 0; i < 6; i++) {
fr_bin2hex(&buffer[i], &vp->vp_strvalue[i * 3], 1);
vp->vp_strvalue[(i * 3) + 2] = '-';
}
vp->vp_strvalue[(5*3)+2] = '\0';
vp->length = (5*3)+2;
DEBUG2("rlm_wimax: Fixing WiMAX binary Calling-Station-Id to %s",
vp->vp_strvalue);
}
return RLM_MODULE_OK;
}
static int wimax_preacct(void *instance, REQUEST *request)
{
return wimax_authorize(instance, request);
}
static int wimax_accounting(void *instance, REQUEST *request)
{
instance = instance;
request = request;
return RLM_MODULE_OK;
}
static int wimax_postauth(void *instance, REQUEST *request)
{
rlm_wimax_t *inst = instance;
VALUE_PAIR *msk, *emsk, *vp;
VALUE_PAIR *mn_nai, *ip, *fa_rk;
HMAC_CTX hmac;
unsigned int rk1_len, rk2_len, rk_len;
int rk_lifetime = 3600;
uint32_t mip_spi;
uint8_t usage_data[24];
uint8_t mip_rk_1[EVP_MAX_MD_SIZE], mip_rk_2[EVP_MAX_MD_SIZE];
uint8_t mip_rk[2 * EVP_MAX_MD_SIZE];
msk = pairfind(request->reply->vps, 1129);
emsk = pairfind(request->reply->vps, 1130);
if (!msk || !emsk) {
RDEBUG("No EAP-MSK or EAP-EMSK. Cannot create WiMAX keys.");
return RLM_MODULE_NOOP;
}
if (inst->delete_mppe_keys) {
pairdelete(&request->reply->vps, ((311 << 16) | 16));
pairdelete(&request->reply->vps, ((311 << 16) | 17));
vp = radius_pairmake(request, &request->reply->vps, "WiMAX-MSK", "0x00", T_OP_EQ);
if (vp) {
memcpy(vp->vp_octets, msk->vp_octets, msk->length);
vp->length = msk->length;
}
}
memcpy(usage_data, "miprk@wimaxforum.org", 21);
usage_data[21] = 0x02;
usage_data[22] = 0x00;
usage_data[23] = 0x01;
HMAC_CTX_init(&hmac);
HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL);
HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data));
HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL);
HMAC_Update(&hmac, (const uint8_t *) &mip_rk_1, rk1_len);
HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data));
HMAC_Final(&hmac, &mip_rk_2[0], &rk2_len);
vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT);
if (vp) rk_lifetime = vp->vp_integer;
memcpy(mip_rk, mip_rk_1, rk1_len);
memcpy(mip_rk + rk1_len, mip_rk_2, rk2_len);
rk_len = rk1_len + rk2_len;
HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha256(), NULL);
HMAC_Update(&hmac, (const uint8_t *) "SPI CMIP PMIP", 12);
HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
mip_spi = ((mip_rk_1[0] << 24) | (mip_rk_1[1] << 16) |
(mip_rk_1[2] << 8) | mip_rk_1[3]);
if (mip_spi < 256) mip_spi += 256;
if (debug_flag) {
int len = rk_len;
char buffer[512];
if (len > 128) len = 128;
fr_bin2hex(mip_rk, buffer, len);
radlog_request(L_DBG, 0, request, "MIP-RK = 0x%s", buffer);
radlog_request(L_DBG, 0, request, "MIP-SPI = %08x",
ntohl(mip_spi));
}
mn_nai = pairfind(request->packet->vps, 1900);
if (!mn_nai) mn_nai = pairfind(request->reply->vps, 1900);
if (!mn_nai) {
RDEBUG("WARNING: WiMAX-MN-NAI was not found in the request or in the reply.");
RDEBUG("WARNING: We cannot calculate MN-HA keys.");
}
vp = NULL;
if (mn_nai) vp = pairfind(request->reply->vps, WIMAX2ATTR(23));
if (!vp) {
RDEBUG("WARNING: WiMAX-IP-Technology not found in reply.");
RDEBUG("WARNING: Not calculating MN-HA keys");
}
if (vp) switch (vp->vp_integer) {
case 2:
ip = pairfind(request->reply->vps, WIMAX2ATTR(6));
if (!ip) {
RDEBUG("WARNING: WiMAX-hHA-IP-MIP4 not found. Cannot calculate MN-HA-PMIP4 key");
break;
}
HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
HMAC_Update(&hmac, (const uint8_t *) "PMIP4 MN HA", 11);
HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
vp = pairfind(request->reply->vps, WIMAX2ATTR(10));
if (!vp) {
vp = radius_paircreate(request, &request->reply->vps,
WIMAX2ATTR(10), PW_TYPE_OCTETS);
}
if (!vp) {
RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-Key");
break;
}
memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
vp->length = rk1_len;
vp = pairfind(request->reply->vps, WIMAX2ATTR(11));
if (!vp) {
vp = radius_paircreate(request, &request->reply->vps,
WIMAX2ATTR(11), PW_TYPE_INTEGER);
}
if (!vp) {
RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-SPI");
break;
}
vp->vp_integer = mip_spi + 1;
break;
case 3:
ip = pairfind(request->reply->vps, WIMAX2ATTR(6));
if (!ip) {
RDEBUG("WARNING: WiMAX-hHA-IP-MIP4 not found. Cannot calculate MN-HA-CMIP4 key");
break;
}
HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
HMAC_Update(&hmac, (const uint8_t *) "CMIP4 MN HA", 11);
HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
vp = pairfind(request->reply->vps, WIMAX2ATTR(10));
if (!vp) {
vp = radius_paircreate(request, &request->reply->vps,
WIMAX2ATTR(10), PW_TYPE_OCTETS);
}
if (!vp) {
RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-Key");
break;
}
memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
vp->length = rk1_len;
vp = pairfind(request->reply->vps, WIMAX2ATTR(11));
if (!vp) {
vp = radius_paircreate(request, &request->reply->vps,
WIMAX2ATTR(11), PW_TYPE_INTEGER);
}
if (!vp) {
RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-SPI");
break;
}
vp->vp_integer = mip_spi;
break;
case 4:
ip = pairfind(request->reply->vps, WIMAX2ATTR(7));
if (!ip) {
RDEBUG("WARNING: WiMAX-hHA-IP-MIP6 not found. Cannot calculate MN-HA-CMIP6 key");
break;
}
HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
HMAC_Update(&hmac, (const uint8_t *) "CMIP6 MN HA", 11);
HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipv6addr, 16);
HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
vp = pairfind(request->reply->vps, WIMAX2ATTR(12));
if (!vp) {
vp = radius_paircreate(request, &request->reply->vps,
WIMAX2ATTR(12), PW_TYPE_OCTETS);
}
if (!vp) {
RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP6-Key");
break;
}
memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
vp->length = rk1_len;
vp = pairfind(request->reply->vps, WIMAX2ATTR(13));
if (!vp) {
vp = radius_paircreate(request, &request->reply->vps,
WIMAX2ATTR(13), PW_TYPE_INTEGER);
}
if (!vp) {
RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP6-SPI");
break;
}
vp->vp_integer = mip_spi + 2;
break;
default:
break;
}
fa_rk = pairfind(request->reply->vps, WIMAX2ATTR(14));
if (fa_rk && (fa_rk->length <= 1)) {
HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
HMAC_Update(&hmac, (const uint8_t *) "FA-RK", 5);
HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
memcpy(fa_rk->vp_octets, &mip_rk_1[0], rk1_len);
fa_rk->length = rk1_len;
}
if (fa_rk) {
vp = pairfind(request->reply->vps, WIMAX2ATTR(61));
if (!vp) {
vp = radius_paircreate(request, &request->reply->vps,
WIMAX2ATTR(61), PW_TYPE_INTEGER);
}
if (!vp) {
RDEBUG("WARNING: Failed creating WiMAX-FA-RK-SPI");
} else {
vp->vp_integer = mip_spi;
}
}
vp = pairfind(request->packet->vps, WIMAX2ATTR(20));
if (vp) {
RDEBUG("Client requested MN-HA key: Should use SPI to look up key from storage.");
if (!mn_nai) {
RDEBUG("WARNING: MN-NAI was not found!");
}
if (!pairfind(request->packet->vps, WIMAX2ATTR(18))) {
RDEBUG("WARNING: HA-IP was not found!");
}
vp = pairfind(request->packet->vps, WIMAX2ATTR(58));
if (vp && (vp->vp_integer == 1)) {
RDEBUG("Client requested HA-RK: Should use IP to look it up from storage.");
}
}
HMAC_CTX_cleanup(&hmac);
return RLM_MODULE_UPDATED;
}
module_t rlm_wimax = {
RLM_MODULE_INIT,
"wimax",
RLM_TYPE_THREAD_SAFE,
wimax_instantiate,
wimax_detach,
{
NULL,
wimax_authorize,
wimax_preacct,
wimax_accounting,
NULL,
NULL,
NULL,
wimax_postauth
},
};