]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add xlats to encrypt/decrypt 3gpp pseudonyms
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 28 Nov 2017 19:04:02 +0000 (19:04 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 28 Nov 2017 19:04:09 +0000 (19:04 +0000)
17 files changed:
share/dictionary.freeradius.internal
src/modules/rlm_eap/lib/sim/all.mk
src/modules/rlm_eap/lib/sim/id.c
src/modules/rlm_eap/lib/sim/id.h [new file with mode: 0644]
src/modules/rlm_eap/lib/sim/sim_proto.h
src/modules/rlm_eap/lib/sim/xlat.c [new file with mode: 0644]
src/modules/rlm_eap/types/rlm_eap_aka/rlm_eap_aka.c
src/modules/rlm_eap/types/rlm_eap_sim/rlm_eap_sim.c
src/tests/modules/eap_sim/.gitignore [new file with mode: 0644]
src/tests/modules/eap_sim/all.mk [new file with mode: 0644]
src/tests/modules/eap_sim/module.conf [new file with mode: 0644]
src/tests/modules/eap_sim/sim_xlat_id_aka.attrs [new file with mode: 0644]
src/tests/modules/eap_sim/sim_xlat_id_aka.unlang [new file with mode: 0644]
src/tests/modules/eap_sim/sim_xlat_id_error.attrs [new file with mode: 0644]
src/tests/modules/eap_sim/sim_xlat_id_error.unlang [new file with mode: 0644]
src/tests/modules/eap_sim/sim_xlat_id_sim.attrs [new file with mode: 0644]
src/tests/modules/eap_sim/sim_xlat_id_sim.unlang [new file with mode: 0644]

index 755fe568830c84888e9ceaa567d81baebb9c9f3b..12f5c33614e72f2f9a546553dccd867d9b55ff3e 100644 (file)
@@ -380,6 +380,19 @@ ATTRIBUTE  SIM-IMSI                                1201    string
 
 # 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
index 88672b5e66398fb2530cb083c9d78b811bb2727e..a056db88cee3c54e91b22d865c2e9f103293e855 100644 (file)
@@ -6,7 +6,9 @@ SOURCES := \
        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/
index d049de6d6a14c37f9f712a1ad6aebca013b58ebc..778f6969c9a9840f9bb2f1105e0ec6a2022c1a62 100644 (file)
@@ -24,6 +24,7 @@
 #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
@@ -72,49 +73,85 @@ char const *fr_sim_domain(char const *nai, size_t nai_len)
  * @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;
        }
 
@@ -126,14 +163,14 @@ int fr_sim_id_type(fr_sim_identity_type_t *type, fr_sim_method_hint_t *hint,
 
                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:
@@ -151,14 +188,14 @@ int fr_sim_id_type(fr_sim_identity_type_t *type, fr_sim_method_hint_t *hint,
 
                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:
@@ -171,32 +208,32 @@ int fr_sim_id_type(fr_sim_identity_type_t *type, fr_sim_method_hint_t *hint,
         *      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;
        }
 }
@@ -204,24 +241,24 @@ int fr_sim_id_type(fr_sim_identity_type_t *type, fr_sim_method_hint_t *hint,
 /** 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) */
@@ -229,30 +266,31 @@ int fr_sim_id_3gpp_pseudonym_encrypt(uint8_t out[SIM_3GPP_PSEUDONYM_LEN],
 
        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).
@@ -266,7 +304,7 @@ int fr_sim_id_3gpp_pseudonym_encrypt(uint8_t out[SIM_3GPP_PSEUDONYM_LEN],
         */
        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;
                }
 
@@ -293,7 +331,7 @@ int fr_sim_id_3gpp_pseudonym_encrypt(uint8_t out[SIM_3GPP_PSEUDONYM_LEN],
                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);
@@ -336,56 +374,75 @@ int fr_sim_id_3gpp_pseudonym_encrypt(uint8_t out[SIM_3GPP_PSEUDONYM_LEN],
        /*
         *      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;
@@ -399,11 +456,6 @@ int fr_sim_id_3gpp_pseudonym_decypt(uint8_t out[SIM_IMSI_MAX_LEN], uint8_t *tag_
        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");
@@ -411,11 +463,6 @@ int fr_sim_id_3gpp_pseudonym_decypt(uint8_t out[SIM_IMSI_MAX_LEN], uint8_t *tag_
                }
        }
 
-       /*
-        *      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;
 
@@ -427,18 +474,13 @@ int fr_sim_id_3gpp_pseudonym_decypt(uint8_t out[SIM_IMSI_MAX_LEN], uint8_t *tag_
                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);
@@ -483,21 +525,21 @@ int fr_sim_id_3gpp_pseudonym_decypt(uint8_t out[SIM_IMSI_MAX_LEN], uint8_t *tag_
         *      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>
@@ -509,54 +551,53 @@ void test_encrypt_decypt_key0(void)
 {
        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);
 }
 
@@ -586,21 +627,23 @@ void test_encrypt_decypt_key16(void)
        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);
 }
 
diff --git a/src/modules/rlm_eap/lib/sim/id.h b/src/modules/rlm_eap/lib/sim/id.h
new file mode 100644 (file)
index 0000000..36779e3
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *   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 */
index 7f32350b045464edea0a4aa33079ce5799b423d2..f092e6b062d9674bab590ff7b08686da1f3debf0 100644 (file)
@@ -32,6 +32,7 @@ RCSIDH(sim_h, "$Id$")
 
 #include <assert.h>
 #include "dict.h"
+#include "id.h"
 #include "eap_types.h"
 #include "eap_sim_common.h"
 #include "eap_aka_common.h"
@@ -210,4 +211,10 @@ int                fr_sim_vector_umts_from_attrs(eap_session_t *eap_session, VALUE_PAIR *vps,
 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 */
diff --git a/src/modules/rlm_eap/lib/sim/xlat.c b/src/modules/rlm_eap/lib/sim/xlat.c
new file mode 100644 (file)
index 0000000..814dd25
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ *   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;
+}
index 7ee488feb213d87f590bfadee138a913b782e94a..56647a72c62d1be0ef0c25a5c98eb4ba61adf37e 100644 (file)
@@ -407,9 +407,16 @@ static int mod_load(void)
                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'.
@@ -419,6 +426,7 @@ rlm_eap_submodule_t rlm_eap_aka = {
        .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 */
 };
index 82e15f8f3488dea8150cd4ecc7d494d0c57ccbcf..6eb7ca2dffe2820b362395c0b8bbe1bb7e6ddbf1 100644 (file)
@@ -589,9 +589,16 @@ static int mod_load(void)
                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'.
@@ -601,6 +608,7 @@ rlm_eap_submodule_t rlm_eap_sim = {
        .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 */
diff --git a/src/tests/modules/eap_sim/.gitignore b/src/tests/modules/eap_sim/.gitignore
new file mode 100644 (file)
index 0000000..405551a
--- /dev/null
@@ -0,0 +1 @@
+rlm_sql_sqlite.db
diff --git a/src/tests/modules/eap_sim/all.mk b/src/tests/modules/eap_sim/all.mk
new file mode 100644 (file)
index 0000000..ec75fd4
--- /dev/null
@@ -0,0 +1,3 @@
+#
+#  Test the eap_sim module
+#
diff --git a/src/tests/modules/eap_sim/module.conf b/src/tests/modules/eap_sim/module.conf
new file mode 100644 (file)
index 0000000..f6b1e68
--- /dev/null
@@ -0,0 +1,6 @@
+eap {
+       type = sim
+       sim {
+
+       }
+}
diff --git a/src/tests/modules/eap_sim/sim_xlat_id_aka.attrs b/src/tests/modules/eap_sim/sim_xlat_id_aka.attrs
new file mode 100644 (file)
index 0000000..7723ccb
--- /dev/null
@@ -0,0 +1,9 @@
+#
+#  Input packet
+#
+User-Name = "0420032219455258@wlan.mnc003.mcc420.3gppnetwork.org"
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/eap_sim/sim_xlat_id_aka.unlang b/src/tests/modules/eap_sim/sim_xlat_id_aka.unlang
new file mode 100644 (file)
index 0000000..ce74db4
--- /dev/null
@@ -0,0 +1,61 @@
+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
+}
diff --git a/src/tests/modules/eap_sim/sim_xlat_id_error.attrs b/src/tests/modules/eap_sim/sim_xlat_id_error.attrs
new file mode 100644 (file)
index 0000000..354fe91
--- /dev/null
@@ -0,0 +1,9 @@
+#
+#  Input packet
+#
+User-Name = "foo"
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/eap_sim/sim_xlat_id_error.unlang b/src/tests/modules/eap_sim/sim_xlat_id_error.unlang
new file mode 100644 (file)
index 0000000..0da78e6
--- /dev/null
@@ -0,0 +1,25 @@
+#
+#  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
+}
diff --git a/src/tests/modules/eap_sim/sim_xlat_id_sim.attrs b/src/tests/modules/eap_sim/sim_xlat_id_sim.attrs
new file mode 100644 (file)
index 0000000..272590d
--- /dev/null
@@ -0,0 +1,9 @@
+#
+#  Input packet
+#
+User-Name = "1420032219455258@wlan.mnc003.mcc420.3gppnetwork.org"
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/eap_sim/sim_xlat_id_sim.unlang b/src/tests/modules/eap_sim/sim_xlat_id_sim.unlang
new file mode 100644 (file)
index 0000000..12df061
--- /dev/null
@@ -0,0 +1,48 @@
+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
+}