# The algorithm the SIM card uses (Milenage for UMTS, COMP128 for others)
ATTRIBUTE SIM-Algo-Version 1202 integer
+ATTRIBUTE SIM-Method-Hint 1203 integer
+
+VALUE SIM-Method-Hint Unknown 0
+VALUE SIM-Method-Hint SIM 1
+VALUE SIM-Method-Hint AKA 2
+
+ATTRIBUTE SIM-Identity-Type 1204 integer
+
+VALUE SIM-Identity-Type Unknown 0
+VALUE SIM-Identity-Type Permanent 1
+VALUE SIM-Identity-Type Pseudonym 2
+VALUE SIM-Identity-Type 3GPP-Pseudonym 3
+VALUE SIM-Identity-Type Fastauth 4
#
# Range: 1210-1219
comp128.c \
crypto.c \
fips186prf.c \
+ id.c \
sim_proto.c \
- vector.c
+ vector.c \
+ xlat.c
SRC_INCDIRS := . ${top_srcdir}/src/modules/rlm_eap/lib/base ${top_srcdir}/src/modules/rlm_eap/
#include <freeradius-devel/tls_log.h>
#include <openssl/evp.h>
#include "sim_proto.h"
+#include "id.h"
#define us(x) (uint8_t) x
* @return
* - How long the identity portion of the NAI is.
*/
-size_t fr_sim_id_len(char const *nai, size_t nai_len)
+size_t fr_sim_id_user_len(char const *nai, size_t nai_len)
{
char const *p;
p = (char *)memchr((uint8_t const *)nai, '@', nai_len);
if (!p) return nai_len;
- return nai - p;
+ return p - nai;
}
/** Find where in the NAI string the domain starts
* @param[in] domain to parse.
* @param[in] domain_len Length of the domain component.
* @return
- * - 0 on success.
- * - -1 on failure.
+ * - number of bytes parsed.
+ * - <= 0 on error - The negative offset of where parsing failed.
*/
-int fr_sim_3gpp_root_nai_domain_mcc_mnc(uint16_t *mnc, uint16_t *mcc,
- char const *domain, size_t domain_len)
+ssize_t fr_sim_3gpp_root_nai_domain_mcc_mnc(uint16_t *mnc, uint16_t *mcc,
+ char const *domain, size_t domain_len)
{
- uint8_t const *p = domain, *end = p + domain_len;
+ char const *p = domain, *end = p + domain_len;
+ char *q;
unsigned long num;
if (((p + 8) < end) || (memcmp(p, "wlan.mnc", 8) != 0)) return -1;
p += 8;
- if (((p + 3) < end) return -1;
- num = strtoul(
+ if (((p + 3) < end)) {
+ fr_strerror_printf("Missing MNC component");
+ return (domain - p);
+ }
+ num = strtoul(p, &q, 10);
+ if (*q != '.') {
+ fr_strerror_printf("Invalid MCN component");
+ return (domain - q);
+ }
+ *mnc = (uint16_t)num;
+ p = q + 1;
+
+ if (((p + 3) < end) || (memcmp(p, "mcc", 3) != 0)) {
+ fr_strerror_printf("Missing MCC component");
+ return (domain - p);
+ }
+ num = strtoul(p, &q, 10);
+ if (*q != '.') {
+ fr_strerror_printf("Invalid MCC component");
+ return (domain - q);
+ }
+ *mcc = (uint16_t)num;
+
+ p = q + 1;
+ if (((p + 15) < end) || (memcmp(p, "3gppnetwork.org", 15) != 0)) {
+ fr_strerror_printf("Missing 3gppnetwork.org suffix");
+ return (domain - p);
+ }
+ p += 15;
+
+ if (p != end) {
+ fr_strerror_printf("Trailing garbage");
+ return (domain - p);
+ }
+
+ return p - domain;
}
/** Determine what type of ID was provided in the initial identity response
*
* @param[out] hint Whether this is a hint to do EAP-SIM or EAP-AKA[']:
- * - SIM_HINT_AKA this ID was generated during an EAP-AKA exchange
- * or the supplicant hints it wants to perform EAP-AKA.
- * - SIM_HINT_SIM this IS was generated during an EAP-SIM exchange
- * or the supplicant hints it wants to perform EAP-SIM.
- * - SIM_HINT_UNKNOWN we don't know what type of authentication generated
- * this ID or which one to start.
+ * - SIM_METHOD_HINT_AKA this ID was generated during an EAP-AKA exchange
+ * or the supplicant hints it wants to perform EAP-AKA.
+ * - SIM_METHOD_HINT_SIM this IS was generated during an EAP-SIM exchange
+ * or the supplicant hints it wants to perform EAP-SIM.
+ * - SIM_METHOD_HINT_UNKNOWN we don't know what type of authentication generated
+ * this ID or which one to start.
* @param[out] type What type of identity this is:
- * - SIM_ID_PERMANENT if the ID is an IMSI.
- * - SIM_ID_3GPP_PSEUDONYM if the ID is a 3GPP pseudonym (not validated).
- * - SIM_ID_PSEUDONYM if the ID is a freeform pseudonym.
- * - SIM_ID_FASTAUTH if the ID is a fastauth identity.
- * - SIM_ID_INVALID if we can't determine what sort of ID this is.
- * @param[in] id provided.
- * @param[in] id_len the length of the ID.
+ * - SIM_ID_TYPE_PERMANENT if the ID is an IMSI.
+ * - SIM_ID_TYPE_3GPP_PSEUDONYM if the ID is a 3GPP pseudonym (not validated).
+ * - SIM_ID_TYPE_PSEUDONYM if the ID is a freeform pseudonym.
+ * - SIM_ID_TYPE_FASTAUTH if the ID is a fastauth identity.
+ * - SIM_ID_TYPE_UNKNOWN if we can't determine what sort of ID this is.
+ * @param[in] id the NAI string provided.
+ * @param[in] id_len the length of the user portion of the NAI string.
+ * See #fr_sim_id_user_len.
* @return Length of the ID written to out.
*/
-int fr_sim_id_type(fr_sim_identity_type_t *type, fr_sim_method_hint_t *hint,
- uint8_t const *id, size_t id_len)
+int fr_sim_id_type(fr_sim_id_type_t *type, fr_sim_method_hint_t *hint,
+ char const *id, size_t id_len)
{
size_t i;
if (id_len < 1) {
- *hint = SIM_HINT_UNKNOWN;
- *type = SIM_ID_INVALID;
+ *hint = SIM_METHOD_HINT_UNKNOWN;
+ *type = SIM_ID_TYPE_UNKNOWN;
return -1;
}
if (i == id_len) {
switch (id[0]) {
- case '0':
- *hint = SIM_HINT_AKA;
- *type = SIM_ID_PERMANENT; /* All digits */
+ case SIM_ID_TAG_PERMANENT_AKA:
+ *hint = SIM_METHOD_HINT_AKA;
+ *type = SIM_ID_TYPE_PERMANENT; /* All digits */
return 0;
- case '1':
- *hint = SIM_HINT_SIM;
- *type = SIM_ID_PERMANENT; /* All digits */
+ case SIM_ID_TAG_PERMANENT_SIM:
+ *hint = SIM_METHOD_HINT_SIM;
+ *type = SIM_ID_TYPE_PERMANENT; /* All digits */
return 0;
default:
if (i == id_len) {
switch (id[0]) {
- case '6':
- *hint = SIM_HINT_AKA;
- *type = SIM_ID_3GPP_PSEUDONYM;
+ case SIM_ID_TAG_3GPP_PSEUDONYM_AKA:
+ *hint = SIM_METHOD_HINT_AKA;
+ *type = SIM_ID_TYPE_3GPP_PSEUDONYM;
return 0;
- case '7':
- *hint = SIM_HINT_SIM;
- *type = SIM_ID_3GPP_PSEUDONYM;
+ case SIM_ID_TAG_3GPP_PSEUDONYM_SIM:
+ *hint = SIM_METHOD_HINT_SIM;
+ *type = SIM_ID_TYPE_3GPP_PSEUDONYM;
return 0;
default:
* User assigned pseudonym
*/
switch (id[0]) {
- case '2':
- *hint = SIM_HINT_AKA;
- *type = SIM_ID_PSEUDONYM;
+ case SIM_ID_TAG_PSEUDONYM_AKA:
+ *hint = SIM_METHOD_HINT_AKA;
+ *type = SIM_ID_TYPE_PSEUDONYM;
return 0;
- case '3':
- *hint = SIM_HINT_SIM;
- *type = SIM_ID_PSEUDONYM;
+ case SIM_ID_TAG_PSEUDONYM_SIM:
+ *hint = SIM_METHOD_HINT_SIM;
+ *type = SIM_ID_TYPE_PSEUDONYM;
return 0;
/*
* Fast reauth identity
*/
- case '4':
- *hint = SIM_HINT_AKA;
- *type = SIM_ID_FASTAUTH;
+ case SIM_ID_TAG_FASTAUTH_AKA:
+ *hint = SIM_METHOD_HINT_AKA;
+ *type = SIM_ID_TYPE_FASTAUTH;
return 0;
- case '5':
- *hint = SIM_HINT_SIM;
- *type = SIM_ID_FASTAUTH;
+ case SIM_ID_TAG_FASTAUTH_SIM:
+ *hint = SIM_METHOD_HINT_SIM;
+ *type = SIM_ID_TYPE_FASTAUTH;
return 0;
default:
- *hint = SIM_HINT_UNKNOWN;
- *type = SIM_ID_INVALID;
+ *hint = SIM_METHOD_HINT_UNKNOWN;
+ *type = SIM_ID_TYPE_UNKNOWN;
return -1;
}
}
/** Create a 3gpp pseudonym from a permanent ID
*
* @param[out] out Where to write the resulting pseudonym, must be a buffer of
- * exactly SIM_3GPP_PSEUDONYM_LEN bytes.
- * @param[in] id Permanent ID to derive pseudonym from. Note: If the IMSI is less than
+ * exactly SIM_3GPP_PSEUDONYM_LEN + 1 bytes.
+ * @param[in] imsi Permanent ID to derive pseudonym from. Note: If the IMSI is less than
* 15 digits it will be rpadded with zeros.
- * @param[in] id_len Length of that ID. Must be between 1-15.
- * @param[in] tag Tag value to prepend to the pseudonym. This field is 6 bits wide
+ * @param[in] imsi_len Length of the IMSI. Must be between 1-15.
+ * @param[in] tag Tag value to prepend to the pseudonym. This field is 6 bits wimsie
* (0-63).
* @param[in] key_ind Key indicator (or key index), the key number used to produce
* the encr ID. There may be up to 16 keys in use at any one
- * time. This field is 4 bits wide (0-15).
- * @param[in] kpseu as described by the 'Security aspects of non-3GPP accesses' document.
+ * time. This field is 4 bits wimsie (0-15).
+ * @param[in] key as described by the 'Security aspects of non-3GPP accesses' document.
* Must be 128 bits (8 bytes).
* @return
* - 0 on success.
- * - -1 if any of the parameters were invalid.
+ * - -1 if any of the parameters were invalimsi.
*/
-int fr_sim_id_3gpp_pseudonym_encrypt(uint8_t out[SIM_3GPP_PSEUDONYM_LEN],
- uint8_t const *id, size_t id_len,
- uint8_t tag, uint8_t key_ind, uint8_t const *kpseu[16])
+int fr_sim_id_3gpp_pseudonym_encrypt(char out[SIM_3GPP_PSEUDONYM_LEN + 1],
+ char const *imsi, size_t imsi_len,
+ uint8_t tag, uint8_t key_ind, uint8_t const key[8])
{
uint8_t padded[16]; /* Random (8 bytes) + Compressed (8 bytes) */
uint8_t encr[16]; /* aes_ecb(padded) */
char *out_p = out;
- uint8_t const *p = id, *end = p + id_len;
+ char const *p = imsi, *end = p + imsi_len;
+ uint8_t *u_p, *u_end;
uint32_t rand[2];
uint8_t *compressed = padded + sizeof(rand); /* Part of padded which contains the compressed IMSI */
EVP_CIPHER_CTX *cctx;
- if (unlikely(key_ind > 15)) { /* 4 bits wide */
+ if (unlikely(key_ind > 15)) { /* 4 bits wimsie */
fr_strerror_printf("Invalid key indicator value, expected value between 0-15, got %u", key_ind);
return -1;
}
- if (unlikely(tag > 63)) { /* 6 bits wide */
+ if (unlikely(tag > 63)) { /* 6 bits wimsie */
fr_strerror_printf("Invalid tag value, expected value between 0-63, got %u", tag);
return -1;
}
- if (unlikely(id_len != 15)) {
- fr_strerror_printf("Invalid ID len, expected length of 15, got %zu", id_len);
+ if (unlikely(imsi_len != 15)) {
+ fr_strerror_printf("Invalid ID len, expected length of 15, got %zu", imsi_len);
return -1;
}
- if (unlikely(!kpseu[key_ind])) {
+ if (unlikely(!key)) {
fr_strerror_printf("Provided key was NULL");
return -1;
}
- memset(padded, 0, sizeof(padded)); /* So we don't output garbage if id_len < 15 */
+ memset(padded, 0, sizeof(padded)); /* So we don't output garbage if imsi_len < 15 */
/*
* ID is an odd length (15).
*/
while (p < end) {
if (unlikely(!isdigit((char)p[0]) || !isdigit((char)p[1]))) {
- fr_strerror_printf("IMSI contains invalid character");
+ fr_strerror_printf("IMSI contains invalimsi character");
return -1;
}
return -1;
}
- if (unlikely(EVP_EncryptInit_ex(cctx, EVP_aes_128_ecb(), NULL, kpseu[key_ind], NULL) != 1)) {
+ if (unlikely(EVP_EncryptInit_ex(cctx, EVP_aes_128_ecb(), NULL, key, NULL) != 1)) {
tls_strerror_printf(true, "Failed initialising AES-128-ECB context");
error:
EVP_CIPHER_CTX_free(cctx);
/*
* Now encode the entire output as base64.
*/
- p = encr;
- end = p + encr_len;
+ u_p = encr;
+ u_end = u_p + encr_len;
/*
* Consume tag (6 bits) + key_ind (4 bits) + encr[0] (8 bits) = 18 bits (or 3 bytes of b64)
*/
*out_p++ = fr_base64_str[tag & 0x3f]; /* 6 bits tag */
- *out_p++ = fr_base64_str[((key_ind & 0x0f) << 2) | ((p[0] & 0xc0) >> 6)]; /* 4 bits key_ind + 2 high bits encr[0] */
- *out_p++ = fr_base64_str[p[0] & 0x3f]; /* 6 low bits of encr[0] */
- p++;
+ *out_p++ = fr_base64_str[((key_ind & 0x0f) << 2) | ((u_p[0] & 0xc0) >> 6)]; /* 4 bits key_ind + 2 high bits encr[0] */
+ *out_p++ = fr_base64_str[u_p[0] & 0x3f]; /* 6 low bits of encr[0] */
+ u_p++;
/*
* Consume 3 bytes of input for 4 bytes of b64 (5 iterations)
*/
- while (p < end) {
- *out_p++ = fr_base64_str[(p[0] & 0xfc) >> 2]; /* 6 high bits of p[0] */
- *out_p++ = fr_base64_str[((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4)]; /* 2 low bits of p[0] + 4 high bits of p[1] */
- *out_p++ = fr_base64_str[((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6)]; /* 4 low bits of p[1] + 2 high bits of p[2] */
- *out_p++ = fr_base64_str[p[2] & 0x3f]; /* 6 low bits of p[2] */
- p += 3;
+ while (u_p < u_end) {
+ *out_p++ = fr_base64_str[(u_p[0] & 0xfc) >> 2]; /* 6 high bits of p[0] */
+ *out_p++ = fr_base64_str[((u_p[0] & 0x03) << 4) | ((u_p[1] & 0xf0) >> 4)];/* 2 low bits of p[0] + 4 high bits of p[1] */
+ *out_p++ = fr_base64_str[((u_p[1] & 0x0f) << 2) | ((u_p[2] & 0xc0) >> 6)];/* 4 low bits of p[1] + 2 high bits of p[2] */
+ *out_p++ = fr_base64_str[u_p[2] & 0x3f]; /* 6 low bits of p[2] */
+ u_p += 3;
}
if ((out_p - out) != SIM_3GPP_PSEUDONYM_LEN) {
- fr_strerror_printf("Base64 output length invalid, expected %i bytes, got %zu bytes",
+ fr_strerror_printf("Base64 output length invalimsi, expected %i bytes, got %zu bytes",
SIM_3GPP_PSEUDONYM_LEN, out_p - out);
return -1;
}
+ out[SIM_3GPP_PSEUDONYM_LEN] = '\0';
+
return 0;
}
+/** Return the tag from a 3gpp pseudonym
+ *
+ * @param[in] encr_id The 3gpp pseudonym.
+ *
+ * @return the tag associated with the pseudonym.
+ */
+uint8_t fr_sim_id_3gpp_pseudonym_tag(char const encr_id[SIM_3GPP_PSEUDONYM_LEN])
+{
+ return fr_base64_sextet[us(encr_id[0])];
+}
+
+/** Return the key index from a 3gpp pseudonym
+ *
+ * @param[in] encr_id The 3gpp pseudonym.
+ *
+ * @return the key index associated with the pseudonym.
+ */
+uint8_t fr_sim_id_3gpp_pseudonym_key_index(char const encr_id[SIM_3GPP_PSEUDONYM_LEN])
+{
+ return ((fr_base64_sextet[us(encr_id[1])] & 0x3c) >> 2);
+}
+
/** Decrypt the 3GPP pseudonym
*
* @param[out] out Where to write the decypted, uncompressed IMSI.
- * @param[out] tag_out Tag retrieved from pseudonym.
- * @param[out] key_ind_out Key indicator retrieved from pseudonym.
- * @param[in] encr_id to decypt.
- * @param[in] kpseu array of 8 byte keys.
- * @param[in] kpseu_count the number of keys.
+ * @param[in] encr_id to decypt. Will read exactly 23 bytes from the buffer.
+ * @param[in] key to use to decrypt the encrypted, compressed IMSI.
* @return
* - 0 on success.
* - -1 if any of the parameters were invalid.
*/
-int fr_sim_id_3gpp_pseudonym_decypt(uint8_t out[SIM_IMSI_MAX_LEN], uint8_t *tag_out, uint8_t *key_ind_out,
- char const encr_id[SIM_3GPP_PSEUDONYM_LEN],
- uint8_t const *kpseu[16], size_t kpseu_count)
+int fr_sim_id_3gpp_pseudonym_decrypt(char out[SIM_IMSI_MAX_LEN + 1],
+ char const encr_id[SIM_3GPP_PSEUDONYM_LEN], uint8_t const key[8])
{
EVP_CIPHER_CTX *cctx;
- uint8_t tag, key_ind;
- uint8_t *out_p = out;
+ char *out_p = out;
uint8_t dec[16];
uint8_t *dec_p = dec;
size_t len = 0;
int i;
- if (unlikely(kpseu_count < 1) || unlikely(kpseu_count > 16)) {
- tls_strerror_printf(true, "Invalid number of keys provided, need between 1-16 keys");
- return -1;
- }
-
for (i = 0; i < SIM_3GPP_PSEUDONYM_LEN; i++) {
if (!fr_is_base64(encr_id[i])) {
fr_strerror_printf("Encrypted IMSI contains non-base64 char");
}
}
- /*
- * Decode tag (6 bit) + key_ind (4 bit) + encrypted[0]
- */
- tag = fr_base64_sextet[us(p[0])];
- key_ind = ((fr_base64_sextet[us(p[1])] & 0x3c) >> 2);
*dec_p++ = (((fr_base64_sextet[us(p[1])] & 0x03) << 6) | fr_base64_sextet[us(p[2])]);
p += 3;
p += 4; /* 32bit input -> 24bit output */
}
- if (key_ind >= kpseu_count) {
- fr_strerror_printf("key_ind specified key[%u], but we only have %zu keys", key_ind, kpseu_count);
- return -1;
- }
-
cctx = EVP_CIPHER_CTX_new();
if (!cctx) {
tls_strerror_printf(true, "Failed allocating EVP context");
return -1;
}
- if (unlikely(EVP_DecryptInit_ex(cctx, EVP_aes_128_ecb(), NULL, kpseu[key_ind], NULL) != 1)) {
+ if (unlikely(EVP_DecryptInit_ex(cctx, EVP_aes_128_ecb(), NULL, key, NULL) != 1)) {
tls_strerror_printf(true, "Failed initialising AES-128-ECB context");
error:
EVP_CIPHER_CTX_free(cctx);
* we ignore.
*/
*out_p++ = (compressed[0] & 0x0f) + '0';
- for (i = 1; i < SIM_IMSI_MAX_LEN; i++) {
+ for (i = 1; i < 8; i++) {
*out_p++ = ((compressed[i] & 0xf0) >> 4) + '0';
*out_p++ = (compressed[i] & 0x0f) + '0';
}
- if (tag_out) *tag_out = tag;
- if (key_ind_out) *key_ind_out = key_ind;
EVP_CIPHER_CTX_free(cctx);
+ out[SIM_IMSI_MAX_LEN] = '\0';
+
return 0;
}
#ifdef TESTING_SIM_ID
/*
- * cc id.c -g3 -Wall -DHAVE_DLFCN_H -DTESTING_SIM_ID -DWITH_TLS -I../../../../ -I../../../ -I ../base/ -I /usr/local/opt/openssl/include/ -include ../include/build.h -L /usr/local/opt/openssl/lib/ -l ssl -l crypto -l talloc -L ../../../../../build/lib/.libs/ -lfreeradius-server -lfreeradius-util -o test_sim_id && ./test_sim_id
+ * cc id.c -g3 -Wall -DHAVE_DLFCN_H -DTESTING_SIM_ID -DWITH_TLS -I../../../../ -I../../../ -I ../base/ -I /usr/local/opt/openssl/include/ -include ../include/build.h -L /usr/local/opt/openssl/lib/ -l ssl -l crypto -l talloc -L ../../../../../build/lib/local/.libs/ -lfreeradius-server -lfreeradius-tls -lfreeradius-util -o test_sim_id && ./test_sim_id
*/
#include <stddef.h>
#include <stdbool.h>
{
char const id[] = "001234554321001";
char const key[] = "1234567812345678";
- char const *keys[] = { key };
uint8_t tag;
uint8_t key_ind;
char const *log;
- char encrypted_id[SIM_3GPP_PSEUDONYM_LEN];
- uint8_t decrypted_id[sizeof(id)];
+ char encrypted_id[SIM_3GPP_PSEUDONYM_LEN + 1];
+ char decrypted_id[sizeof(id)];
fr_log_fp = stdout;
- TEST_CHECK(fr_sim_id_3gpp_pseudonym_encrypt(encrypted_id, (uint8_t const *)id, sizeof(id) - 1,
- 11, 0, (uint8_t const **)keys) == 0);
+ TEST_CHECK(fr_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1, 6, 0, (uint8_t const *)key) == 0);
while ((log = fr_strerror_pop())) printf("%s\n", log);
- TEST_CHECK(fr_sim_id_3gpp_pseudonym_decypt(decrypted_id, &tag, &key_ind,
- encrypted_id, (uint8_t const **)keys, 1) == 0);
+ tag = fr_sim_id_3gpp_pseudonym_tag(encrypted_id);
+ TEST_CHECK(tag == 6);
+ key_ind = fr_sim_id_3gpp_pseudonym_key_index(encrypted_id);
+ TEST_CHECK(key_ind == 0);
+
+ TEST_CHECK(fr_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)key) == 0);
while ((log = fr_strerror_pop())) printf("%s\n", log);
- TEST_CHECK(tag == 11);
- TEST_CHECK(key_ind == 0);
TEST_CHECK(memcmp(id, decrypted_id, 15) == 0);
}
void test_encrypt_decypt_key1(void)
{
char const id[] = "001234554321001";
- char const key0[] = "1234567812345678";
- char const key1[] = "2222222288888888";
- char const *keys[] = { key0, key1 };
+ char const key[] = "1234567812345678";
uint8_t tag;
uint8_t key_ind;
char const *log;
- char encrypted_id[SIM_3GPP_PSEUDONYM_LEN];
- uint8_t decrypted_id[sizeof(id)];
+ char encrypted_id[SIM_3GPP_PSEUDONYM_LEN + 1];
+ char decrypted_id[sizeof(id)];
fr_log_fp = stdout;
- TEST_CHECK(fr_sim_id_3gpp_pseudonym_encrypt(encrypted_id, (uint8_t const *)id, sizeof(id) - 1,
- 11, 1, (uint8_t const **)keys) == 0);
- while ((log = fr_strerror_pop())) printf("%s\n", log);
-
- TEST_CHECK(fr_sim_id_3gpp_pseudonym_decypt(decrypted_id, &tag, &key_ind,
- encrypted_id, (uint8_t const **)keys, 2) == 0);
+ TEST_CHECK(fr_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1, 11, 1, (uint8_t const *)key) == 0);
while ((log = fr_strerror_pop())) printf("%s\n", log);
+ tag = fr_sim_id_3gpp_pseudonym_tag(encrypted_id);
TEST_CHECK(tag == 11);
+ key_ind = fr_sim_id_3gpp_pseudonym_key_index(encrypted_id);
TEST_CHECK(key_ind == 1);
+
+ TEST_CHECK(fr_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)key) == 0);
+ while ((log = fr_strerror_pop())) printf("%s\n", log);
+
TEST_CHECK(memcmp(id, decrypted_id, 15) == 0);
}
uint8_t key_ind;
char const *log;
- char encrypted_id[SIM_3GPP_PSEUDONYM_LEN];
- uint8_t decrypted_id[sizeof(id)];
+ char encrypted_id[SIM_3GPP_PSEUDONYM_LEN + 1];
+ char decrypted_id[sizeof(id)];
fr_log_fp = stdout;
- TEST_CHECK(fr_sim_id_3gpp_pseudonym_encrypt(encrypted_id, (uint8_t const *)id, sizeof(id) - 1,
- 9, 15, (uint8_t const **)keys) == 0);
- while ((log = fr_strerror_pop())) printf("%s\n", log);
-
- TEST_CHECK(fr_sim_id_3gpp_pseudonym_decypt(decrypted_id, &tag, &key_ind,
- encrypted_id, (uint8_t const **)keys, 16) == 0);
+ TEST_CHECK(fr_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1,
+ 9, 15, (uint8_t const *)keys[15]) == 0);
while ((log = fr_strerror_pop())) printf("%s\n", log);
+ tag = fr_sim_id_3gpp_pseudonym_tag(encrypted_id);
TEST_CHECK(tag == 9);
+ key_ind = fr_sim_id_3gpp_pseudonym_key_index(encrypted_id);
TEST_CHECK(key_ind == 15);
+
+ TEST_CHECK(fr_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)keys[key_ind]) == 0);
+ while ((log = fr_strerror_pop())) printf("%s\n", log);
+
TEST_CHECK(memcmp(id, decrypted_id, 15) == 0);
}
--- /dev/null
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file rlm_eap/lib/sim/id.h
+ * @brief EAP-SIM/EAP-AKA identity detection, creation, and decyption.
+ *
+ * @copyright 2017 The FreeRADIUS server project
+ */
+#ifndef _EAP_SIM_ID_TYPE_H
+#define _EAP_SIM_ID_TYPE_H
+
+#define SIM_3GPP_PSEUDONYM_LEN 23 //!< Length of a base64 encoded 3gpp pseudonym.
+#define SIM_IMSI_MAX_LEN 15 //!< Length of an IMSI number in ASCII.
+
+/** SIM/AKA method hints
+ *
+ * Derived from processing the provided identity.
+ */
+typedef enum {
+ SIM_METHOD_HINT_UNKNOWN = 0, //!< We don't know what method the identity hints at.
+ SIM_METHOD_HINT_SIM = 1, //!< The identity hints the supplicant wants to use
+ ///< EAP-SIM.
+ SIM_METHOD_HINT_AKA = 2 //!< The identity hints the supplicant wants to use
+ ///< EAP-AKA.
+} fr_sim_method_hint_t;
+
+/** SIM/AKA identity type hints
+ *
+ * Derived from the processing the provided identity.
+ */
+typedef enum {
+ SIM_ID_TYPE_UNKNOWN = 0, //!< We don't know what type of identity this is.
+ SIM_ID_TYPE_PERMANENT = 1, //!< This is a permanent identity (the IMSI of the SIM).
+ SIM_ID_TYPE_PSEUDONYM = 2, //!< This is a custom pseudonym.
+ SIM_ID_TYPE_3GPP_PSEUDONYM = 3, //!< This is a reversibly encrypted 3gpp pseudonym.
+ SIM_ID_TYPE_FASTAUTH = 4 //!< This is a fastauth (session-resumption) id.
+} fr_sim_id_type_t;
+
+typedef enum {
+ SIM_ID_TAG_PERMANENT_AKA = '0',
+ SIM_ID_TAG_PERMANENT_SIM = '1',
+ SIM_ID_TAG_PSEUDONYM_AKA = '2',
+ SIM_ID_TAG_PSEUDONYM_SIM = '3',
+ SIM_ID_TAG_3GPP_PSEUDONYM_AKA = '6',
+ SIM_ID_TAG_3GPP_PSEUDONYM_SIM = '7',
+ SIM_ID_TAG_FASTAUTH_AKA = '4',
+ SIM_ID_TAG_FASTAUTH_SIM = '5'
+} fr_sim_id_tag_t;
+
+size_t fr_sim_id_user_len(char const *nai, size_t nai_len);
+
+char const *fr_sim_domain(char const *nai, size_t nai_len);
+
+ssize_t fr_sim_3gpp_root_nai_domain_mcc_mnc(uint16_t *mnc, uint16_t *mcc,
+ char const *domain, size_t domain_len);
+
+int fr_sim_id_type(fr_sim_id_type_t *type, fr_sim_method_hint_t *hint,
+ char const *id, size_t id_len);
+
+int fr_sim_id_3gpp_pseudonym_encrypt(char out[SIM_3GPP_PSEUDONYM_LEN + 1],
+ char const *imsi, size_t imsi_len,
+ uint8_t tag, uint8_t key_ind, uint8_t const key[8]);
+
+uint8_t fr_sim_id_3gpp_pseudonym_tag(char const encr_id[SIM_3GPP_PSEUDONYM_LEN]);
+
+uint8_t fr_sim_id_3gpp_pseudonym_key_index(char const encr_id[SIM_3GPP_PSEUDONYM_LEN]);
+
+int fr_sim_id_3gpp_pseudonym_decrypt(char out[SIM_IMSI_MAX_LEN],
+ char const encr_id[SIM_3GPP_PSEUDONYM_LEN], uint8_t const key[8]);
+#endif /* _EAP_SIM_ID_TYPE_H */
#include <assert.h>
#include "dict.h"
+#include "id.h"
#include "eap_types.h"
#include "eap_sim_common.h"
#include "eap_aka_common.h"
void fr_sim_fips186_2prf(uint8_t out[160], uint8_t mk[20])
CC_BOUNDED(__size__, 2, 160, 160)
CC_BOUNDED(__size__, 1, 20, 20);
+
+/*
+ * xlat.c
+ */
+void sim_xlat_register(void);
+void sim_xlat_unregister(void);
#endif /* _SIM_PROTO_H */
--- /dev/null
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file rlm_eap/lib/sim/id_xlat.c
+ * @brief EAP-SIM/EAP-AKA identity detection, creation, and decyption.
+ *
+ * @copyright 2017 The FreeRADIUS server project
+ */
+
+#include <freeradius-devel/radiusd.h>
+#include "sim_proto.h"
+
+static int sim_xlat_refs = 0;
+
+
+/** Returns the SIM method EAP-SIM or EAP-AKA hinted at by the user identifier
+ *
+ * %{sim_id_method:&id_attr}
+ */
+static ssize_t sim_xlat_id_method(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
+ UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+ REQUEST *request, char const *fmt)
+{
+ vp_tmpl_t *vpt;
+ TALLOC_CTX *our_ctx = talloc_init("sim_xlat");
+ ssize_t slen, len, id_len;
+ char const *p = fmt, *id, *method;
+ fr_sim_id_type_t type_hint;
+ fr_sim_method_hint_t method_hint;
+ fr_dict_attr_t const *da;
+
+ /*
+ * Trim whitespace
+ */
+ while (isspace(*p) && p++);
+
+ slen = tmpl_afrom_attr_substr(our_ctx, &vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
+ if (slen <= 0) {
+ RPEDEBUG("Invalid attribute reference");
+ error:
+ talloc_free(our_ctx);
+ return -1;
+ }
+
+ if (tmpl_aexpand(our_ctx, &id, request, vpt, NULL, NULL) < 0) {
+ RPEDEBUG2("Failing expanding ID attribute");
+ goto error;
+ }
+
+ id_len = talloc_array_length(id) - 1;
+ len = fr_sim_id_user_len(id, id_len);
+ if (len == id_len ) {
+ RPEDEBUG2("SIM ID \"%pS\" is not an NAI", id);
+ goto error;
+ }
+
+ if (fr_sim_id_type(&type_hint, &method_hint, id, len) < 0) {
+ RPEDEBUG2("SIM ID \"%pS\" has unrecognised format", id);
+ goto error;
+ }
+
+ da = fr_dict_attr_by_num(NULL, 0, FR_SIM_METHOD_HINT);
+ if (!da) {
+ REDEBUG("Missing Sim-Method-Hint attribute");
+ goto error;
+ }
+
+ method = fr_dict_enum_alias_by_value(NULL, da, fr_box_uint32(method_hint));
+ if (!method) {
+ REDEBUG("Missing Sim-Method-Hint value");
+ goto error;
+ }
+ *out = talloc_typed_strdup(ctx, method);
+ talloc_free(our_ctx);
+
+ return talloc_array_length(*out) - 1;
+}
+
+/** Returns the type of identity used
+ *
+ * %{sim_id_type:&id_attr}
+ */
+static ssize_t sim_xlat_id_type(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
+ UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+ REQUEST *request, char const *fmt)
+{
+ vp_tmpl_t *vpt;
+ TALLOC_CTX *our_ctx = talloc_init("sim_xlat");
+ ssize_t slen, user_len, id_len;
+ char const *p = fmt, *id, *method;
+ fr_sim_id_type_t type_hint;
+ fr_sim_method_hint_t method_hint;
+ fr_dict_attr_t const *da;
+
+ /*
+ * Trim whitespace
+ */
+ while (isspace(*p) && p++);
+
+ slen = tmpl_afrom_attr_substr(our_ctx, &vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
+ if (slen <= 0) {
+ RPEDEBUG("Invalid attribute reference");
+ error:
+ talloc_free(our_ctx);
+ return -1;
+ }
+
+ if (tmpl_aexpand(our_ctx, &id, request, vpt, NULL, NULL) < 0) {
+ RPEDEBUG2("Failing expanding ID attribute");
+ goto error;
+ }
+
+ id_len = talloc_array_length(id) - 1;
+ user_len = fr_sim_id_user_len(id, id_len);
+ if (user_len == id_len ) {
+ RPEDEBUG2("SIM ID \"%pS\" is not an NAI", id);
+ goto error;
+ }
+
+ if (fr_sim_id_type(&type_hint, &method_hint, id, user_len) < 0) {
+ RPEDEBUG2("SIM ID \"%pS\" has unrecognised format", id);
+ goto error;
+ }
+
+ da = fr_dict_attr_by_num(NULL, 0, FR_SIM_IDENTITY_TYPE);
+ if (!da) {
+ REDEBUG("Missing Sim-Method-Hint attribute");
+ goto error;
+ }
+
+ method = fr_dict_enum_alias_by_value(NULL, da, fr_box_uint32(type_hint));
+ if (!method) {
+ REDEBUG("Missing Sim-Method-Hint value");
+ goto error;
+ }
+ *out = talloc_typed_strdup(ctx, method);
+ talloc_free(our_ctx);
+
+ return talloc_array_length(*out) - 1;
+}
+
+/** Returns the key index from a 3gpp pseudonym
+ *
+ * %{sim_id_3gpp_pseudonym_key_index:&id_attr}
+ *
+ */
+static ssize_t sim_xlat_3gpp_pseudonym_key_index(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
+ UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+ REQUEST *request, char const *fmt)
+{
+ vp_tmpl_t *vpt;
+ TALLOC_CTX *our_ctx = talloc_init("sim_xlat");
+ ssize_t slen, user_len, id_len;
+ char const *p = fmt, *id;
+
+ /*
+ * Trim whitespace
+ */
+ while (isspace(*p) && p++);
+
+ slen = tmpl_afrom_attr_substr(our_ctx, &vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
+ if (slen <= 0) {
+ RPEDEBUG("Invalid attribute reference");
+ error:
+ talloc_free(our_ctx);
+ return -1;
+ }
+
+ if (tmpl_aexpand(our_ctx, &id, request, vpt, NULL, NULL) < 0) {
+ RPEDEBUG2("Failing expanding ID attribute");
+ goto error;
+ }
+
+ id_len = talloc_array_length(id) - 1;
+ user_len = fr_sim_id_user_len(id, id_len);
+ if (user_len != SIM_3GPP_PSEUDONYM_LEN) {
+ REDEBUG2("3gpp pseudonym incorrect length, expected %i bytes, got %zu bytes",
+ SIM_3GPP_PSEUDONYM_LEN, user_len);
+ goto error;
+ }
+
+ MEM(*out = talloc_typed_asprintf(ctx, "%i", fr_sim_id_3gpp_pseudonym_tag(id)));
+ talloc_free(our_ctx);
+
+ return talloc_array_length(*out) - 1;
+}
+
+/** Decrypts a 3gpp pseudonym
+ *
+ * %{sim_id_3gpp_pseudonym_decrypt_nai:&id_attr &key_attr}
+ *
+ */
+static ssize_t sim_xlat_3gpp_pseudonym_decrypt_nai(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
+ UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+ REQUEST *request, char const *fmt)
+{
+ vp_tmpl_t *id_vpt, *key_vpt;
+ TALLOC_CTX *our_ctx = talloc_init("sim_xlat");
+ ssize_t slen, user_len, id_len, key_len;
+ uint8_t tag;
+ char out_tag;
+ uint8_t *key;
+ char decrypted[SIM_IMSI_MAX_LEN + 1];
+ char const *p = fmt, *id;
+
+ /*
+ * Trim whitespace
+ */
+ while (isspace(*p) && p++);
+
+ slen = tmpl_afrom_attr_substr(our_ctx, &id_vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
+ if (slen <= 0) {
+ RPEDEBUG("Invalid ID attribute reference");
+ error:
+ talloc_free(our_ctx);
+ return -1;
+ }
+
+ p += slen;
+ if (*p != ' ') {
+ REDEBUG2("Missing key argument");
+ goto error;
+ }
+ p++;
+
+ slen = tmpl_afrom_attr_substr(our_ctx, &key_vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
+ if (slen <= 0) {
+ RPEDEBUG("Invalid key attribute reference");
+ goto error;
+ }
+
+ if (tmpl_aexpand(our_ctx, &id, request, id_vpt, NULL, NULL) < 0) {
+ RPEDEBUG2("Failing expanding ID attribute");
+ goto error;
+ }
+
+
+ if (tmpl_aexpand(our_ctx, &key, request, key_vpt, NULL, NULL) < 0) {
+ RPEDEBUG2("Failing expanding Key attribute");
+ goto error;
+ }
+
+ id_len = talloc_array_length(id);
+ user_len = fr_sim_id_user_len(id, id_len);
+ if (user_len != SIM_3GPP_PSEUDONYM_LEN) {
+ REDEBUG2("3gpp pseudonym incorrect length, expected %i bytes, got %zu bytes",
+ SIM_3GPP_PSEUDONYM_LEN, user_len);
+ return -1;
+ }
+
+ key_len = talloc_array_length(key);
+ if (key_len != 8) {
+ REDEBUG2("Decryption key incorrect length, expected %i bytes, got %zu bytes", 8, key_len);
+ return -1;
+ }
+
+ tag = fr_sim_id_3gpp_pseudonym_tag(id);
+ switch (tag) {
+ case 59: /* 7 in the base64 alphabet (SIM) */
+ out_tag = SIM_ID_TAG_PERMANENT_SIM;
+ break;
+
+ case 58: /* 6 in the base64 alphabet (AKA) */
+ out_tag = SIM_ID_TAG_PERMANENT_AKA;
+ break;
+
+ default:
+ REDEBUG2("Unexpected tag value (%u) in SIM ID \"%pS\"", tag, id);
+ return -1;
+ }
+
+ RDEBUG2("Decrypting \"%.*s\"", (int)user_len, id);
+ if (fr_sim_id_3gpp_pseudonym_decrypt(decrypted, id, key) < 0) {
+ RPEDEBUG2("SIM ID \"%pS\" is not a 3gpp pseudonym", id);
+ return -1;
+ }
+
+ /*
+ * Recombine unencrypted IMSI with @domain
+ */
+ MEM(*out = talloc_typed_asprintf(ctx, "%c%s%s", out_tag, decrypted, id + user_len));
+ talloc_free(our_ctx);
+
+ return talloc_array_length(*out) - 1;
+}
+
+/** Decrypts a 3gpp pseudonym
+ *
+ * %{sim_id_3gpp_pseudonym_encrypt:&id_attr &key_attr <key_index>}
+ *
+ */
+static ssize_t sim_xlat_3gpp_pseudonym_encrypt_nai(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
+ UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+ REQUEST *request, char const *fmt)
+{
+ vp_tmpl_t *id_vpt, *key_vpt;
+ TALLOC_CTX *our_ctx = talloc_init("sim_xlat");
+ ssize_t slen, user_len, id_len, key_len;
+ uint8_t *key, tag;
+ unsigned long key_index;
+ char encrypted[SIM_3GPP_PSEUDONYM_LEN + 1];
+ char const *p = fmt, *id;
+ fr_sim_id_type_t type_hint;
+ fr_sim_method_hint_t method_hint;
+
+ /*
+ * Trim whitespace
+ */
+ while (isspace(*p) && p++);
+
+ slen = tmpl_afrom_attr_substr(our_ctx, &id_vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
+ if (slen <= 0) {
+ RPEDEBUG("Invalid ID attribute reference");
+ error:
+ talloc_free(our_ctx);
+ return -1;
+ }
+
+ p += slen;
+ if (*p != ' ') {
+ REDEBUG2("Missing key argument");
+ goto error;
+ }
+ p++;
+
+ slen = tmpl_afrom_attr_substr(our_ctx, &key_vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
+ if (slen <= 0) {
+ RPEDEBUG("Invalid key attribute reference");
+ goto error;
+ }
+ p += slen;
+
+ if (*p != ' ') {
+ REDEBUG2("Missing key index");
+ goto error;
+ }
+ p++;
+
+ /*
+ * Get the key index
+ */
+ key_index = strtoul(p, NULL, 10);
+ if (key_index > 15) {
+ REDEBUG2("Key index must be between 0-15");
+ goto error;
+ }
+
+ /*
+ * Get the ID
+ */
+ if (tmpl_aexpand(our_ctx, &id, request, id_vpt, NULL, NULL) < 0) {
+ RPEDEBUG2("Failing expanding ID attribute");
+ goto error;
+ }
+
+ id_len = talloc_array_length(id) - 1;
+ user_len = fr_sim_id_user_len(id, id_len);
+ if (user_len > (SIM_IMSI_MAX_LEN + 1)) { /* +1 for tag */
+ REDEBUG2("3gpp pseudonym incorrect length, expected less than %i bytes, got %zu bytes",
+ SIM_IMSI_MAX_LEN + 1, user_len);
+ return -1;
+ }
+
+ /*
+ * Get the key
+ */
+ if (tmpl_aexpand(our_ctx, &key, request, key_vpt, NULL, NULL) < 0) {
+ RPEDEBUG2("Failing expanding Key attribute");
+ goto error;
+ }
+
+ key_len = talloc_array_length(key);
+ if (key_len != 8) {
+ REDEBUG2("Encryption key incorrect length, expected %i bytes, got %zu bytes", 8, key_len);
+ return -1;
+ }
+
+ /*
+ * Determine what type/method hints are in
+ * the current ID.
+ */
+ if (fr_sim_id_type(&type_hint, &method_hint, id, user_len) < 0) {
+ RPEDEBUG2("SIM ID \"%pS\" has unrecognised format", id);
+ goto error;
+ }
+
+ if (type_hint != SIM_ID_TYPE_PERMANENT) {
+ REDEBUG2("SIM ID \"%pS\" is not a permanent identity (IMSI)", id);
+ goto error;
+ }
+
+ switch (method_hint) {
+ case SIM_METHOD_HINT_SIM:
+ tag = 59; /* 7 in the base64 alphabet */
+ break;
+
+ case SIM_METHOD_HINT_AKA:
+ tag = 58; /* 6 in the base64 alphabet */
+ break;
+
+ case SIM_METHOD_HINT_UNKNOWN:
+ REDEBUG2("SIM ID \"%pS\" does not contain a method hint", id);
+ goto error;
+ }
+
+ /*
+ * Encrypt the IMSI
+ *
+ * Strip existing tag from the permanent id
+ */
+ if (fr_sim_id_3gpp_pseudonym_encrypt(encrypted, id + 1, user_len - 1, tag, (uint8_t)key_index, key) < 0) {
+ RPEDEBUG2("Failed encrypting SIM ID \"%pS\"", id);
+ return -1;
+ }
+
+ /*
+ * Recombine encrypted IMSI with @domain
+ */
+ MEM(*out = talloc_typed_asprintf(ctx, "%s%s", encrypted, id + user_len));
+ talloc_free(our_ctx);
+
+ return talloc_array_length(*out) - 1;
+}
+
+void sim_xlat_register(void)
+{
+ if (sim_xlat_refs) {
+ sim_xlat_refs++;
+ return;
+ }
+
+ xlat_register(NULL, "sim_id_method", sim_xlat_id_method, NULL, NULL, 0, 0, true);
+ xlat_register(NULL, "sim_id_type", sim_xlat_id_type, NULL, NULL, 0, 0, true);
+ xlat_register(NULL, "3gpp_pseudonym_key_index",
+ sim_xlat_3gpp_pseudonym_key_index, NULL, NULL, 0, 0, true);
+ xlat_register(NULL, "3gpp_pseudonym_decrypt_nai",
+ sim_xlat_3gpp_pseudonym_decrypt_nai, NULL, NULL, 0, 0, true);
+ xlat_register(NULL, "3gpp_pseudonym_encrypt_nai",
+ sim_xlat_3gpp_pseudonym_encrypt_nai, NULL, NULL, 0, 0, true);
+ sim_xlat_refs = 1;
+}
+
+void sim_xlat_unregister(void)
+{
+ if (sim_xlat_refs > 1) {
+ sim_xlat_refs--;
+ return;
+ }
+
+ xlat_unregister("sim_id_method");
+ xlat_unregister("sim_id_type");
+ xlat_unregister("3gpp_pseudonym_key_index");
+ xlat_unregister("3gpp_pseudonym_decrypt_nai");
+ xlat_unregister("3gpp_pseudonym_encrypt_nai");
+ sim_xlat_refs = 0;
+}
return -1;
}
if (fr_sim_global_init() < 0) return -1;
+ sim_xlat_register();
+
return 0;
}
+static void mod_unload(void)
+{
+ sim_xlat_unregister();
+}
+
/*
* The module name should be the only globally exported symbol.
* That is, everything else should be 'static'.
.name = "eap_aka",
.magic = RLM_MODULE_INIT,
.load = mod_load,
+ .unload = mod_unload,
.session_init = mod_session_init, /* Initialise a new EAP session */
.process = mod_process, /* Process next round of EAP method */
};
return -1;
}
if (fr_sim_global_init() < 0) return -1;
+ sim_xlat_register();
+
return 0;
}
+static void mod_unload(void)
+{
+ sim_xlat_unregister();
+}
+
/*
* The module name should be the only globally exported symbol.
* That is, everything else should be 'static'.
.name = "eap_sim",
.magic = RLM_MODULE_INIT,
.load = mod_load,
+ .unload = mod_unload,
.instantiate = mod_instantiate, /* Create new submodule instance */
.session_init = mod_session_init, /* Initialise a new EAP session */
.process = mod_process, /* Process next round of EAP method */
--- /dev/null
+rlm_sql_sqlite.db
--- /dev/null
+#
+# Test the eap_sim module
+#
--- /dev/null
+eap {
+ type = sim
+ sim {
+
+ }
+}
--- /dev/null
+#
+# Input packet
+#
+User-Name = "0420032219455258@wlan.mnc003.mcc420.3gppnetwork.org"
+
+#
+# Expected answer
+#
+Response-Packet-Type == Access-Accept
--- /dev/null
+if ("%{sim_id_method:&User-Name}" != 'AKA') {
+ test_fail
+} else {
+ test_pass
+}
+
+if ("%{sim_id_type:&User-Name}" != 'Permanent') {
+ test_fail
+} else {
+ test_pass
+}
+
+#
+# Encrypt the permanent ID
+#
+update control {
+ Tmp-String-0 := '12345678'
+}
+update control {
+ User-Name := "%{3gpp_pseudonym_encrypt_nai:&User-Name &control:Tmp-String-0 6}"
+}
+
+#
+# Can we get the EAP method from the encrypted blob correctly?
+#
+if ("%{sim_id_method:&control:User-Name}" != 'AKA') {
+ test_fail
+} else {
+ test_pass
+}
+
+#
+# Can we identify the encrypted blob correctly?
+#
+if ("%{sim_id_type:&control:User-Name}" != '3GPP-Pseudonym') {
+ test_fail
+} else {
+ test_pass
+}
+
+#
+# We should refuse to re-encrypt an encrypted NAI
+#
+if ("%{3gpp_pseudonym_encrypt_nai:&control:User-Name &control:Tmp-String-0 6}" != '') {
+ test_fail
+} else {
+ test_pass
+}
+
+#
+# Get the original IMSI back again
+#
+update control {
+ Tmp-String-1 := "%{3gpp_pseudonym_decrypt_nai:&control:User-Name &control:Tmp-String-0}"
+}
+
+if (&control:Tmp-String-1 != &User-Name) {
+ test_fail
+} else {
+ test_pass
+}
--- /dev/null
+#
+# Input packet
+#
+User-Name = "foo"
+
+#
+# Expected answer
+#
+Response-Packet-Type == Access-Accept
--- /dev/null
+#
+# No domain separator
+#
+update request {
+ User-Name := '1420032219455258wlan.mnc003.mcc420.3gppnetwork.org'
+}
+
+if ("%{sim_id_method:&User-Name}" != '') {
+ test_fail
+} else {
+ test_pass
+}
+
+#
+# Zero length ID
+#
+update request {
+ User-Name := ''
+}
+
+if ("%{sim_id_method:&User-Name}" != '') {
+ test_fail
+} else {
+ test_pass
+}
--- /dev/null
+#
+# Input packet
+#
+User-Name = "1420032219455258@wlan.mnc003.mcc420.3gppnetwork.org"
+
+#
+# Expected answer
+#
+Response-Packet-Type == Access-Accept
--- /dev/null
+if ("%{sim_id_method:&User-Name}" != 'SIM') {
+ test_fail
+} else {
+ test_pass
+}
+
+if ("%{sim_id_type:&User-Name}" != 'Permanent') {
+ test_fail
+} else {
+ test_pass
+}
+
+#
+# Encrypt the permanent ID
+#
+update control {
+ Tmp-String-0 := '12345678'
+}
+update control {
+ User-Name := "%{3gpp_pseudonym_encrypt_nai:&User-Name &control:Tmp-String-0 6}"
+}
+
+#
+# Can we get the EAP method from the encrypted blob correctly?
+#
+if ("%{sim_id_method:&control:User-Name}" != 'SIM') {
+ test_fail
+} else {
+ test_pass
+}
+
+#
+# Can we identify the encrypted blob correctly?
+#
+if ("%{sim_id_type:&control:User-Name}" != '3GPP-Pseudonym') {
+ test_fail
+} else {
+ test_pass
+}
+
+#
+# We should refuse to re-encrypt an encrypted NAI
+#
+if ("%{3gpp_pseudonym_encrypt_nai:&control:User-Name &control:Tmp-String-0 6}" != '') {
+ test_fail
+} else {
+ test_pass
+}