]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add all example packets from RFC4186 to tests
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sun, 3 Dec 2017 01:12:36 +0000 (01:12 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sun, 3 Dec 2017 01:12:36 +0000 (01:12 +0000)
Add special cases for AT_AUTS and AT_RES

Add more length restrictions for some fields

Remove attributes not included in SIM/AKA

Fix array decoding support

Add tests for SIM error cases

Add support for decoding fixed length octet type attributes

share/dictionary.eap.aka
share/dictionary.eap.sim
src/modules/rlm_eap/lib/sim/decode.c
src/modules/rlm_eap/lib/sim/encode.c
src/tests/unit/all.mk
src/tests/unit/eap_aka_decode.txt [new file with mode: 0644]
src/tests/unit/eap_aka_encode.txt [new file with mode: 0644]
src/tests/unit/eap_sim_decode.txt [new file with mode: 0644]
src/tests/unit/eap_sim_encode.txt [moved from src/tests/unit/eap-sim.txt with 54% similarity]
src/tests/unit/eap_sim_error.txt [new file with mode: 0644]

index 02187cf016af1d7a859b90d3edacff8ef54f5a03..e78976efb054611117aa2336616abdaf9ac792c6 100644 (file)
@@ -8,20 +8,19 @@
 #
 #  Non-Skippable attributes
 #
-ATTRIBUTE      EAP-AKA-Rand                            1       octets
-ATTRIBUTE      EAP-AKA-Autn                            2       octets
-ATTRIBUTE      EAP-AKA-Res                             3       octets
-ATTRIBUTE      EAP-AKA-Auts                            4       octets
-ATTRIBUTE      EAP-AKA-Nonce-MT                        7       octets
+ATTRIBUTE      EAP-AKA-RAND                            1       octets[16]      # Not an array, single value
+ATTRIBUTE      EAP-AKA-AUTN                            2       octets[16]
+ATTRIBUTE      EAP-AKA-RES                             3       octets[16]      # Special case (length field with length in bits)
+ATTRIBUTE      EAP-AKA-AUTS                            4       octets[14]      # Special case no reserved field.
 ATTRIBUTE      EAP-AKA-Permanent-ID-Req                10      bool
-ATTRIBUTE      EAP-AKA-Mac                             11      octets[20]
+ATTRIBUTE      EAP-AKA-MAC                             11      octets[16]
 ATTRIBUTE      EAP-AKA-Notification                    12      short
 
-VALUE  EAP-AKA-Notification            General-Failure-After-Authentication 0
-VALUE  EAP-AKA-Notification            Temporarily-Denied      1026
-VALUE  EAP-AKA-Notification            Not-Subscribed          1031
-VALUE  EAP-AKA-Notification            General-Failure         16384
-VALUE  EAP-AKA-Notification            Success                 32768
+VALUE  EAP-AKA-Notification            General-Failure-After-Authentication    0
+VALUE  EAP-AKA-Notification            Temporarily-Denied                      1026
+VALUE  EAP-AKA-Notification            Not-Subscribed                          1031
+VALUE  EAP-AKA-Notification            General-Failure                         16384
+VALUE  EAP-AKA-Notification            Success                                 32768
 
 ATTRIBUTE      EAP-AKA-Any-ID-Req                      13      bool
 ATTRIBUTE      EAP-AKA-Identity                        14      string
@@ -31,30 +30,30 @@ ATTRIBUTE   EAP-AKA-Client-Error-Code               22      short
 ATTRIBUTE      EAP-AKA-KDF-Input                       23      string
 ATTRIBUTE      EAP-AKA-KDF                             24      short
 
-VALUE  EAP-AKA-Client-Error-Code       Unable-To-Process-Packet 0
-VALUE  EAP-AKA-Client-Error-Code       Unsupported-Version     1
-VALUE  EAP-AKA-Client-Error-Code       Insufficient-Challenges 2
-VALUE  EAP-AKA-Client-Error-Code       RANDS-Not-Fresh         3
+VALUE  EAP-AKA-Client-Error-Code       Unable-To-Process-Packet        0
+VALUE  EAP-AKA-Client-Error-Code       Unsupported-Version             1
+VALUE  EAP-AKA-Client-Error-Code       Insufficient-Challenges         2
+VALUE  EAP-AKA-Client-Error-Code       RANDS-Not-Fresh                 3
 
 #
 #  Skippable-Attributes
 #
-ATTRIBUTE      EAP-AKA-IV                              129     octets
+ATTRIBUTE      EAP-AKA-IV                              129     octets[16]
 ATTRIBUTE      EAP-AKA-Encr-Data                       130     tlv encrypt=4
 
 #
 #  These attributes are reversibly encrypted
 #
 BEGIN-TLV      EAP-AKA-Encr-Data
-ATTRIBUTE      EAP-AKA-Padding                         6       octets
+ATTRIBUTE      EAP-AKA-Padding                         6       octets          # 4, 8, 12 bytes length
 ATTRIBUTE      EAP-AKA-Counter                         19      short
 ATTRIBUTE      EAP-AKA-Counter-Too-Small               20      bool
-ATTRIBUTE      EAP-AKA-Nonce-S                         21      octets
+ATTRIBUTE      EAP-AKA-Nonce-S                         21      octets[16]
 ATTRIBUTE      EAP-AKA-Next-Pseudonym                  132     string
 ATTRIBUTE      EAP-AKA-Next-Reauth-ID                  133     string
 END-TLV                EAP-AKA-Encr-Data
 
-ATTRIBUTE      EAP-AKA-Checkcode                       134     octets
+ATTRIBUTE      EAP-AKA-Checkcode                       134     octets          # Zero or Twenty bytes
 ATTRIBUTE      EAP-AKA-Result-Ind                      137     bool
 
 #
@@ -63,13 +62,13 @@ ATTRIBUTE   EAP-AKA-Result-Ind                      137     bool
 FLAGS  internal
 ATTRIBUTE      EAP-AKA-Subtype                         256     integer
 
-VALUE  EAP-AKA-Subtype                 AKA-Challenge           1
-VALUE  EAP-AKA-Subtype                 AKA-Authentication-Reject 2
-VALUE  EAP-AKA-Subtype                 AKA-Synchronization-Failure 4
-VALUE  EAP-AKA-Subtype                 AKA-Identity            5
-VALUE  EAP-AKA-Subtype                 AKA-Notification        12
-VALUE  EAP-AKA-Subtype                 AKA-Reauthentication    13
-VALUE  EAP-AKA-Subtype                 AKA-Client-Error        14
+VALUE  EAP-AKA-Subtype                 AKA-Challenge                   1
+VALUE  EAP-AKA-Subtype                 AKA-Authentication-Reject       2
+VALUE  EAP-AKA-Subtype                 AKA-Synchronization-Failure     4
+VALUE  EAP-AKA-Subtype                 AKA-Identity                    5
+VALUE  EAP-AKA-Subtype                 AKA-Notification                12
+VALUE  EAP-AKA-Subtype                 AKA-Reauthentication            13
+VALUE  EAP-AKA-Subtype                 AKA-Client-Error                14
 
 ATTRIBUTE      EAP-AKA-HMAC                            257     string
 ATTRIBUTE      EAP-AKA-Key                             258     octets
index 1242ae99010c00becbe5b51094b3122ffd6cb8e3..a32cd193eb029dee5d097b0fb297cc451b81778e 100644 (file)
@@ -8,20 +8,17 @@
 #
 #  Non-Skippable attributes
 #
-ATTRIBUTE      EAP-SIM-Rand                            1       octets
-ATTRIBUTE      EAP-SIM-Autn                            2       octets
-ATTRIBUTE      EAP-SIM-Res                             3       octets
-ATTRIBUTE      EAP-SIM-Auts                            4       octets
+ATTRIBUTE      EAP-SIM-RAND                            1       octets[16] array
 ATTRIBUTE      EAP-SIM-Nonce-MT                        7       octets
 ATTRIBUTE      EAP-SIM-Permanent-ID-Req                10      bool
-ATTRIBUTE      EAP-SIM-Mac                             11      octets[20]
+ATTRIBUTE      EAP-SIM-MAC                             11      octets[16]
 ATTRIBUTE      EAP-SIM-Notification                    12      short
 
-VALUE  EAP-SIM-Notification            General-Failure-After-Authentication 0
-VALUE  EAP-SIM-Notification            Temporarily-Denied      1026
-VALUE  EAP-SIM-Notification            Not-Subscribed          1031
-VALUE  EAP-SIM-Notification            General-Failure         16384
-VALUE  EAP-SIM-Notification            Success                 32768
+VALUE  EAP-SIM-Notification            General-Failure-After-Authentication    0
+VALUE  EAP-SIM-Notification            Temporarily-Denied                      1026
+VALUE  EAP-SIM-Notification            Not-Subscribed                          1031
+VALUE  EAP-SIM-Notification            General-Failure                         16384
+VALUE  EAP-SIM-Notification            Success                                 32768
 
 ATTRIBUTE      EAP-SIM-Any-ID-Req                      13      bool
 ATTRIBUTE      EAP-SIM-Identity                        14      string
@@ -31,30 +28,29 @@ ATTRIBUTE   EAP-SIM-Selected-Version                16      short
 ATTRIBUTE      EAP-SIM-Fullauth-ID-Req                 17      bool
 ATTRIBUTE      EAP-SIM-Client-Error-Code               22      short
 
-VALUE  EAP-SIM-Client-Error-Code       Unable-To-Process-Packet 0
-VALUE  EAP-SIM-Client-Error-Code       Unsupported-Version     1
-VALUE  EAP-SIM-Client-Error-Code       Insufficient-Challenges 2
-VALUE  EAP-SIM-Client-Error-Code       RANDS-Not-Fresh         3
+VALUE  EAP-SIM-Client-Error-Code       Unable-To-Process-Packet        0
+VALUE  EAP-SIM-Client-Error-Code       Unsupported-Version             1
+VALUE  EAP-SIM-Client-Error-Code       Insufficient-Challenges         2
+VALUE  EAP-SIM-Client-Error-Code       RANDS-Not-Fresh                 3
 
 #
 #  Skippable-Attributes
 #
-ATTRIBUTE      EAP-SIM-IV                              129     octets
+ATTRIBUTE      EAP-SIM-IV                              129     octets[16]
 ATTRIBUTE      EAP-SIM-Encr-Data                       130     tlv encrypt=4
 
 #
 #  These attributes are reversibly encrypted
 #
 BEGIN-TLV      EAP-SIM-Encr-Data
-ATTRIBUTE      EAP-SIM-Padding                         6       octets
+ATTRIBUTE      EAP-SIM-Padding                         6       octets          # 4, 8, 12 bytes length
 ATTRIBUTE      EAP-SIM-Counter                         19      short
 ATTRIBUTE      EAP-SIM-Counter-Too-Small               20      bool
-ATTRIBUTE      EAP-SIM-Nonce-S                         21      octets
+ATTRIBUTE      EAP-SIM-Nonce-S                         21      octets[16]
 ATTRIBUTE      EAP-SIM-Next-Pseudonym                  132     string
 ATTRIBUTE      EAP-SIM-Next-Reauth-ID                  133     string
 END-TLV                EAP-SIM-Encr-Data
 
-ATTRIBUTE      EAP-SIM-Checkcode                       134     octets
 ATTRIBUTE      EAP-SIM-Result-Ind                      137     bool
 
 #
index 2fda2f842946b66531d2c5e711907a927f38dc28..62a5e088ca04526a140fc402da5e36a3ec447a2c 100644 (file)
@@ -56,7 +56,7 @@ RCSID("$Id$")
  * of 32 bits, and includes the Type/Length fields.
  */
 
-static ssize_t fr_sim_decode_pair_internal(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
+static ssize_t sim_decode_pair_internal(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
                                           uint8_t const *data, size_t data_len, void *decoder_ctx);
 
 static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
@@ -240,18 +240,19 @@ static ssize_t sim_value_decrypt(TALLOC_CTX *ctx, uint8_t **out,
  *     - The number of elements in the array on success.
  *     - < 0 on error (array length not a multiple of element size).
  */
-static int fr_sim_array_members(size_t *out, size_t len, fr_dict_attr_t const *da)
+static int sim_array_members(size_t *out, size_t len, fr_dict_attr_t const *da)
 {
        size_t          element_len;
 
-       *out = len;
-
        /*
         *      Could be an array of bytes, integers, etc.
         */
        switch (da->type) {
        case FR_TYPE_OCTETS:
-               if (da->flags.length == 0) return 1;
+               if (da->flags.length == 0) {
+                       fr_strerror_printf("%s: Octets array must have fixed length elements", __FUNCTION__);
+                       return -1;
+               }
                element_len = da->flags.length;
                break;
 
@@ -260,7 +261,10 @@ static int fr_sim_array_members(size_t *out, size_t len, fr_dict_attr_t const *d
                break;
        }
 
-       if (element_len == 1) return 1; /* Fast path */
+       if (element_len == 1) {
+               *out = 1;
+               return 1;       /* Fast path */
+       }
 
        if (!fr_cond_assert(element_len > 0)) return -1;
 
@@ -274,13 +278,73 @@ static int fr_sim_array_members(size_t *out, size_t len, fr_dict_attr_t const *d
         *      Number of elements must divide exactly
         */
        if (len % element_len) {
-               fr_strerror_printf("%s: Expected array value length to be multiple of %zu, got %zu",
+               fr_strerror_printf("%s: Expected array actual length to be multiple of %zu, got %zu",
                                   __FUNCTION__, element_len, len);
                return -1;
        }
+       *out = element_len;
+
        return len / element_len;
 }
 
+static ssize_t sim_decode_array(TALLOC_CTX *ctx, vp_cursor_t *cursor,
+                               fr_dict_attr_t const *parent,
+                               uint8_t const *data, size_t const attr_len, UNUSED size_t data_len,
+                               void *decoder_ctx)
+{
+       uint8_t const   *p = data, *end = p + attr_len;
+       uint16_t        actual_len;
+       int             elements, i;
+       size_t          element_len;
+       ssize_t         rcode;
+
+       FR_PROTO_TRACE("Array attribute");
+
+       rad_assert(parent->flags.array);
+       rad_assert(attr_len >= 2);              /* Should have been caught earlier */
+
+       /*
+        *      Arrays with fixed length members that
+        *      are a multiple of 4 don't need an
+        *      actual_len value, as we can get the
+        *      number of elements from the attribute
+        *      length.
+        */
+       if (!parent->flags.length || (parent->flags.length % 4)) {
+               actual_len = (p[0] << 8) | p[1];
+               if (actual_len > (attr_len - 2)) {
+                       fr_strerror_printf("%s: Actual length field value (%hu) > attribute value length (%zu)",
+                                          __FUNCTION__, actual_len, attr_len - 2);
+                       return -1;
+               }
+       } else {
+               actual_len = attr_len - 2;      /* -2 for the reserved bytes */
+       }
+       p += 2;
+
+       /*
+        *      Zero length array
+        */
+       if (!actual_len) return p - data;
+
+       /*
+        *      Get the number of elements
+        */
+       elements = sim_array_members(&element_len, actual_len, parent);
+       if (elements < 0) return elements;
+
+       for (i = 0; i < elements; i++) {
+               rcode = sim_decode_pair_value(ctx, cursor, parent, p, element_len, end - p, decoder_ctx);
+               if (rcode < 0) return rcode;
+
+               p += rcode;
+
+               if (!fr_cond_assert(p <= end)) break;
+       }
+
+       return attr_len;        /* Say we consumed attr_len because it may have padding */
+}
+
 /** Break apart a TLV attribute into individual attributes
  *
  * @param[in] ctx              to allocate new attributes in.
@@ -292,7 +356,8 @@ static int fr_sim_array_members(size_t *out, size_t len, fr_dict_attr_t const *d
  * @param[in] decoder_ctx      IVs, keys etc...
  * @return
  *     - Length on success.
- *     - -1 on failure.
+ *     - -1 on malformed child attribute.
+ *     - -2 on malformed TLV (this is ok > 128).
  */
 static ssize_t sim_decode_tlv(TALLOC_CTX *ctx, vp_cursor_t *cursor,
                              fr_dict_attr_t const *parent,
@@ -305,11 +370,11 @@ static ssize_t sim_decode_tlv(TALLOC_CTX *ctx, vp_cursor_t *cursor,
        fr_dict_attr_t const    *child;
        VALUE_PAIR              *head = NULL;
        vp_cursor_t             tlv_cursor;
-       ssize_t                 rcode;
+       ssize_t                 rcode = -2;
 
        if (data_len < 2) {
-               fr_strerror_printf("Insufficient data");
-               return -1; /* minimum attr size */
+               fr_strerror_printf("%s: Insufficient data", __FUNCTION__);
+               return -2; /* minimum attr size */
        }
 
        /*
@@ -324,8 +389,8 @@ static ssize_t sim_decode_tlv(TALLOC_CTX *ctx, vp_cursor_t *cursor,
                FR_PROTO_TRACE("found encrypted attribute '%s'", parent->name);
 
                decr_len = sim_value_decrypt(ctx, &decr, p + 2,
-                                                 attr_len - 2, data_len - 2, decoder_ctx);     /* Skip reserved */
-               if (decr_len < 0) return decr_len;
+                                            attr_len - 2, data_len - 2, decoder_ctx);  /* Skip reserved */
+               if (decr_len < 0) return -2;
 
                p = decr;
                end = p + decr_len;
@@ -348,7 +413,7 @@ static ssize_t sim_decode_tlv(TALLOC_CTX *ctx, vp_cursor_t *cursor,
                error:
                        talloc_free(decr);
                        fr_pair_list_free(&head);
-                       return -1;
+                       return rcode;
                }
 
                /*
@@ -358,18 +423,31 @@ static ssize_t sim_decode_tlv(TALLOC_CTX *ctx, vp_cursor_t *cursor,
                 *      (16 in the case of AES-128-CBC).
                 */
                if (sim_at == FR_SIM_PADDING) {
+                       uint8_t zero = 0;
+                       uint8_t i;
+
                        if (!parent->flags.encrypt) {
                                fr_strerror_printf("%s: Found padding attribute outside of an encrypted TLV",
                                                   __FUNCTION__);
-                               return -1;
+                               goto error;
                        }
 
-                       if (!fr_cond_assert(data_len % 4)) return -1;
+                       if (!fr_cond_assert(data_len % 4)) goto error;
 
                        if (sim_at_len > 12) {
                                fr_strerror_printf("%s: Expected padding attribute length <= 12 bytes, got %zu bytes",
                                                   __FUNCTION__, sim_at_len);
-                               return -1;
+                               goto error;
+                       }
+
+                       /*
+                        *      RFC says we MUST verify that FR_SIM_PADDING
+                        *      data is zeroed out.
+                        */
+                       for (i = 2; i < sim_at_len; i++) zero |= p[i];
+                       if (zero) {
+                               fr_strerror_printf("%s: Padding attribute value not zeroed", __FUNCTION__);
+                               goto error;
                        }
 
                        p += sim_at_len;
@@ -433,7 +511,6 @@ static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_di
 {
        VALUE_PAIR              *vp;
        uint8_t const           *p = data;
-       uint8_t const           *end = p + data_len;
        ssize_t                 rcode;
 
        fr_sim_decode_ctx_t     *packet_ctx = decoder_ctx;
@@ -444,77 +521,130 @@ static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_di
        FR_PROTO_TRACE("Parent %s len %zu", parent->name, attr_len);
        FR_PROTO_HEX_DUMP(__FUNCTION__ , data, attr_len);
 
+       FR_PROTO_TRACE("Type \"%s\" (%u)", fr_int2str(dict_attr_types, parent->type, "?Unknown?"), parent->type);
+
        /*
-        *      It's an array type attribute with a fixed length,
+        *      Special cases, attributes that either have odd formats, or need
+        *      have information we need to decode the packet.
+        */
+       switch (parent->attr) {
+       /*
+        *      We need to record packet_ctx so we can decrypt AT_ENCR attributes.
+        *
+        *      If we don't find it before, then that's fine, we'll try and
+        *      find it in the rest of the packet after the encrypted
+        *      attribute.
+        *
+        *      0                   1                   2                   3
+        *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *      |     AT_IV     | Length = 5    |           Reserved            |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *      |                                                               |
+        *      |                 Initialization Vector                         |
+        *      |                                                               |
+        *      |                                                               |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         */
-       if (parent->flags.array) {
-               uint16_t        actual_len;
-               int             elements, i;
-               size_t          element_len;
+       case FR_SIM_IV:
+               if (sim_iv_extract(&packet_ctx->iv[0], data, attr_len) < 0) return -1;
+               packet_ctx->have_iv = true;
+               break;  /* Now create the attribute */
 
-               FR_PROTO_TRACE("Array attribute");
+       /*
+        *      AT_RES - Special case (RES length is in bits)
+        *
+        *      0                   1                   2                   3
+        *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *      |     AT_RES    |    Length     |          RES Length           |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
+        *      |                                                               |
+        *      |                             RES                               |
+        *      |                                                               |
+        *      |                                                               |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        */
+       case FR_EAP_AKA_RES:
+       {
+               uint16_t res_len;
 
-               if (attr_len < 2) {
-                       fr_strerror_printf("%s: Missing length field", __FUNCTION__);
+               if (attr_len < 2) goto raw;     /* Need at least two bytes for the length field */
+
+               res_len = (p[0] << 8) | p[1];
+               if (res_len % 8) {
+                       fr_strerror_printf("%s: RES Length (%hu) is not a multiple of 8",
+                                          __FUNCTION__, res_len);
                        return -1;
                }
+               res_len /= 8;
 
-               actual_len = (p[0] << 8) | p[1];
-               if (actual_len > (attr_len - 2)) {
-                       fr_strerror_printf("%s: Actual length field value (%hu) > attribute value length (%zu)",
-                                          __FUNCTION__, actual_len, attr_len - 2);
+               if (res_len > (attr_len - 2)) {
+                       fr_strerror_printf("%s: RES Length field value (%hu) > attribute value length (%zu)",
+                                          __FUNCTION__, res_len, (attr_len - 2));
                        return -1;
                }
 
-               /*
-                *      Get the number of elements
-                */
-               elements = fr_sim_array_members(&element_len, actual_len, parent);
-               if (elements < 0) return elements;
-
-               for (i = 0; i < elements; i++) {
-                       rcode = sim_decode_pair_value(ctx, cursor, parent, p, element_len, end - p, decoder_ctx);
-                       if (rcode < 0) return rcode;
-
-                       p += rcode;
-
-                       if (!fr_cond_assert(p <= end)) break;
-               }
+               vp = fr_pair_afrom_da(ctx, parent);
+               if (!vp) return -1;
 
-               return p - data;
+               fr_pair_value_memcpy(vp, p + 2, attr_len - 2);
        }
+               goto done;
 
        /*
-        *      We need to record packet_ctx so we can decrypt AT_ENCR attributes.
+        *      AT_AUTS - Octets type with no reserved field
         *
-        *      If we don't find it before, then that's fine, we'll try and
-        *      find it in the rest of the packet after the encrypted
-        *      attribute.
+        *      0                   1                   2                   3
+        *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|
+        *      |    AT_AUTS    | Length = 4    |                               |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
+        *      |                                                               |
+        *      |                             AUTS                              |
+        *      |                                                               |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         */
-       if (parent->attr == FR_SIM_IV) {
-               if (sim_iv_extract(&packet_ctx->iv[0], data, attr_len) < 0) return -1;
-               packet_ctx->have_iv = true;
-               return attr_len;
-       }
-
-       FR_PROTO_TRACE("Type \"%s\" (%u)", fr_int2str(dict_attr_types, parent->type, "?Unknown?"), parent->type);
-       switch (parent->type) {
-       case FR_TYPE_STRING:
-       case FR_TYPE_OCTETS:
-               if (parent->flags.length && (attr_len != parent->flags.length)) {
+       case FR_EAP_AKA_AUTS:
+               if (parent->flags.length != attr_len) {
+               wrong_len:
                        fr_strerror_printf("%s: Attribute \"%s\" needs a value of exactly %zu bytes, "
                                           "but value was %zu bytes", __FUNCTION__,
                                           parent->name, (size_t)parent->flags.length, attr_len);
                        goto raw;
                }
+               vp = fr_pair_afrom_da(ctx, parent);
+               fr_pair_value_memcpy(vp, p, attr_len);
+               goto done;
+
+       default:
                break;
+       }
 
-       case FR_TYPE_BOOL:
-               if (attr_len != 2) goto raw;
+       switch (parent->type) {
+       case FR_TYPE_STRING:
+               if (attr_len < 2) goto raw;     /* Need at least two bytes for the length field */
+               if (parent->flags.length && (attr_len != parent->flags.length)) goto wrong_len;
+               break;
+
+       case FR_TYPE_OCTETS:
+               /*
+                *      If it's not an array, then the octets fields
+                *      have a two byte reserved prefix.
+                */
+               if (!parent->flags.array) {
+                       if (parent->flags.length && (attr_len != (parent->flags.length + 2))) goto wrong_len;
+               } else {
+                       if (parent->flags.length && (attr_len != (parent->flags.length))) goto wrong_len;
+               }
                break;
 
+       case FR_TYPE_BOOL:
+       case FR_TYPE_UINT8:
        case FR_TYPE_UINT16:
-               if (attr_len != 2) goto raw;
+       case FR_TYPE_UINT32:
+       case FR_TYPE_UINT64:
+               if (attr_len != fr_sim_attr_sizes[parent->type][0]) goto raw;
                break;
 
        case FR_TYPE_TLV:
@@ -525,12 +655,26 @@ static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_di
                 *      attribute, OR they've already been grouped
                 *      into a contiguous memory buffer.
                 */
-               rcode = sim_decode_tlv(ctx, cursor, parent, p, attr_len, data_len, decoder_ctx);        /* +2 for reserved */
-               if (rcode < 0) {
-                       FR_PROTO_TRACE("Failed decoding TLV: %s", fr_strerror());
+               rcode = sim_decode_tlv(ctx, cursor, parent, p, attr_len, data_len, decoder_ctx);
+               switch (rcode) {
+               case 0:
+                       break;
+
+               /*
+                *      TLV malformed (this is OK, we decode as raw)
+                *      as required for attribute numbers > 128.
+                */
+               case -2:
+                       FR_PROTO_TRACE("Failed decoding TLV: %s", fr_strerror_peek());
                        goto raw;
+
+               /*
+                *      Child attribute caused error (this is fatal)
+                */
+               default:
+                       return rcode;
                }
-               return rcode;
+
 
        default:
        raw:
@@ -539,8 +683,8 @@ static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_di
                 *      as we're prohibited from continuing by the SIM RFCs.
                 */
                if (parent->attr <= SIM_SKIPPABLE_MAX) {
-                       fr_strerror_printf("%s: Failed parsing non-skippable attribute '%s'",
-                                          __FUNCTION__, parent->name);
+                       fr_strerror_printf_push("%s: Failed parsing non-skippable attribute '%s'",
+                                               __FUNCTION__, parent->name);
                        return -1;
                }
 
@@ -559,28 +703,52 @@ static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_di
        vp = fr_pair_afrom_da(ctx, parent);
        if (!vp) return -1;
 
+       /*
+        *      For unknown attributes copy the entire value, not skipping
+        *      any reserved bytes.
+        */
+       if (parent->flags.is_unknown || parent->flags.is_raw) {
+               fr_pair_value_memcpy(vp, p, attr_len);
+               vp->vp_length = attr_len;
+               goto done;
+       }
+
        switch (parent->type) {
        /*
-        *      Strings have a two byte 'real length' field in front of the
-        *      actual value, and that gives us the length of the string value.
+        *      0                   1                   2                   3
+        *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *      | AT_<STRING>   | Length        |    Actual <STRING> Length     |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *      |                                                               |
+        *      .                           String                              .
+        *      .                                                               .
+        *      |                                                               |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         */
        case FR_TYPE_STRING:
        {
-               uint16_t str_len = (p[0] << 8) | p[1];
+               uint16_t actual_len = (p[0] << 8) | p[1];
 
-               if (str_len > (attr_len - 2)) {
-                       fr_strerror_printf("%s: String value length (%hu) > attribute value length (%zu)",
-                                          __FUNCTION__, str_len, attr_len - 2);
+               if (actual_len > (attr_len - 2)) {
+                       fr_strerror_printf("%s: Actual length field value (%hu) > attribute value length (%zu)",
+                                          __FUNCTION__, actual_len, attr_len - 2);
                        return -1;
                }
 
-               fr_pair_value_bstrncpy(vp, p + 2, str_len);
+               fr_pair_value_bstrncpy(vp, p + 2, actual_len);
        }
                break;
 
        case FR_TYPE_OCTETS:
-               fr_pair_value_memcpy(vp, p + 2, attr_len - 2);  /* -2 for reserved field */
-               vp->vp_length = attr_len - 2;
+               /*
+                *      Non-array attributes have a 2 byte padding
+                */
+               if (!vp->da->flags.array) {
+                       fr_pair_value_memcpy(vp, p + 2, attr_len - 2);  /* -2 for reserved field */
+               } else {
+                       fr_pair_value_memcpy(vp, p, attr_len);
+               }
                break;
 
        /*
@@ -597,8 +765,35 @@ static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_di
                vp->vp_bool = true;
                break;
 
+       /*
+        *      Numbers are network byte order.
+        *
+        *      In the base RFCs only short (16bit) unsigned integers are used.
+        *      We add support for more, just for completeness.
+        *
+        *      0                   1                   2                   3
+        *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *      |   AT_<SHORT>  | Length = 1    |    Short 1    |    Short 2    |
+        *      +---------------+---------------+-------------------------------+
+        */
+       case FR_TYPE_UINT8:
+               vp->vp_uint8 = p[0];
+               break;
+
        case FR_TYPE_UINT16:
-               vp->vp_uint16 = (p[0] << 8) | p[1];
+               memcpy(&vp->vp_uint16, p, sizeof(vp->vp_uint16));
+               vp->vp_uint16 = ntohs(vp->vp_uint32);
+               break;
+
+       case FR_TYPE_UINT32:
+               memcpy(&vp->vp_uint32, p, sizeof(vp->vp_uint32));
+               vp->vp_uint32 = ntohl(vp->vp_uint32);
+               break;
+
+       case FR_TYPE_UINT64:
+               memcpy(&vp->vp_uint64, p, sizeof(vp->vp_uint64));
+               vp->vp_uint64 = ntohll(vp->vp_uint64);
                break;
 
        default:
@@ -607,6 +802,7 @@ static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_di
                return -1;
        }
 
+done:
        vp->type = VT_DATA;
        fr_pair_cursor_append(cursor, vp);
 
@@ -627,8 +823,8 @@ static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_di
  *     - The number of bytes parsed.
  *     - -1 on error.
  */
-static ssize_t fr_sim_decode_pair_internal(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
-                                          uint8_t const *data, size_t data_len, void *decoder_ctx)
+static ssize_t sim_decode_pair_internal(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
+                                       uint8_t const *data, size_t data_len, void *decoder_ctx)
 {
        uint8_t                 sim_at;
        size_t                  sim_at_len;
@@ -636,16 +832,28 @@ static ssize_t fr_sim_decode_pair_internal(TALLOC_CTX *ctx, vp_cursor_t *cursor,
        ssize_t                 rcode;
        fr_dict_attr_t const    *da;
 
-       sim_at_len = ((size_t)data[1]) << 2;
 
-       if ((data_len < sizeof(uint32_t)) || (sim_at_len > data_len)) {
-               fr_strerror_printf("%s: Insufficient data", __FUNCTION__);
+       /*
+        *      We need at least 2 bytes.  We really need 4 but it's
+        *      useful to print the attribute number in the errors.
+        */
+       if (data_len < 2) {
+               fr_strerror_printf("%s: Insufficient data: Expected >= 2 bytes, got %zu bytes",
+                                  __FUNCTION__, data_len);
                return -1;
        }
 
        sim_at = data[0];
+
+       sim_at_len = ((size_t)data[1]) << 2;
+       if (sim_at_len > data_len) {
+               fr_strerror_printf("%s: Insufficient data for attribute %d: Length field %zu, remaining data %zu",
+                                  __FUNCTION__, sim_at, sim_at_len, data_len);
+               return -1;
+       }
+
        if (sim_at_len == 0) {
-               fr_strerror_printf("%s: Malformed attribute %d: Length field is zero", __FUNCTION__, sim_at);
+               fr_strerror_printf("%s: Malformed attribute %d: Length field 0", __FUNCTION__, sim_at);
                return -1;
        }
 
@@ -669,7 +877,11 @@ static ssize_t fr_sim_decode_pair_internal(TALLOC_CTX *ctx, vp_cursor_t *cursor,
 
        FR_PROTO_TRACE("decode context changed %s -> %s", da->parent->name, da->name);
 
-       rcode = sim_decode_pair_value(ctx, cursor, da, data + 2, sim_at_len - 2, data_len - 2, decoder_ctx);
+       if (da->flags.array) {
+               rcode = sim_decode_array(ctx, cursor, da, data + 2, sim_at_len - 2, data_len - 2, decoder_ctx);
+       } else {
+               rcode = sim_decode_pair_value(ctx, cursor, da, data + 2, sim_at_len - 2, data_len - 2, decoder_ctx);
+       }
        if (rcode < 0) return rcode;
 
        return 2 + rcode;
@@ -693,7 +905,7 @@ ssize_t fr_sim_decode_pair(TALLOC_CTX *ctx, vp_cursor_t *cursor,
 {
        fr_sim_decode_ctx_t     *packet_ctx = decoder_ctx;
 
-       return fr_sim_decode_pair_internal(ctx, cursor, packet_ctx->root, data, data_len, decoder_ctx);
+       return sim_decode_pair_internal(ctx, cursor, packet_ctx->root, data, data_len, decoder_ctx);
 }
 
 /** Decode SIM/AKA/AKA' specific packet data
@@ -813,6 +1025,21 @@ static void *decode_test_ctx_aka(UNUSED TALLOC_CTX *ctx)
        return &test_ctx;
 }
 
+static void *decode_test_ctx_sim_rfc4186(UNUSED TALLOC_CTX *ctx)
+{
+       static fr_sim_decode_ctx_t      test_ctx;
+       static fr_sim_keys_t            keys = {
+                                               .k_encr = { 0x53, 0x6e, 0x5e, 0xbc, 0x44 ,0x65, 0x58, 0x2a,
+                                                           0xa6, 0xa8, 0xec, 0x99, 0x86, 0xeb, 0xb6, 0x20 }
+                                       };
+       fr_sim_global_init();
+
+       test_ctx.root = dict_sim_root;
+       test_ctx.keys = &keys;
+
+       return &test_ctx;
+}
+
 /*
  *     Test points
  */
@@ -822,6 +1049,12 @@ fr_test_point_pair_decode_t sim_tp_decode = {
        .func           = fr_sim_decode_pair
 };
 
+extern fr_test_point_pair_decode_t sim_tp_decode_rfc4186;
+fr_test_point_pair_decode_t sim_tp_decode_rfc4186 = {
+       .test_ctx       = decode_test_ctx_sim_rfc4186,
+       .func           = fr_sim_decode_pair
+};
+
 extern fr_test_point_pair_decode_t aka_tp_decode;
 fr_test_point_pair_decode_t aka_tp_decode = {
        .test_ctx       = decode_test_ctx_aka,
index ef3bfeb187d1e2ad2a121876f5d27f36f1438959..42bc99c19d568b1ea097e504741fffa8622e9ba8 100644 (file)
@@ -298,6 +298,7 @@ static ssize_t encode_value(uint8_t *out, size_t outlen,
        ssize_t                 len;
        VALUE_PAIR const        *vp = fr_pair_cursor_current(cursor);
        fr_dict_attr_t const    *da = tlv_stack[depth];
+       fr_sim_encode_ctx_t     *packet_ctx = encoder_ctx;
 
        VP_VERIFY(vp);
        FR_PROTO_STACK_PRINT(tlv_stack, depth);
@@ -322,28 +323,141 @@ static ssize_t encode_value(uint8_t *out, size_t outlen,
                break;
        }
 
-       switch (da->type) {
-       case FR_TYPE_OCTETS:
+       switch (da->attr) {
+       /*
+        *      Allow manual override of IV - Mostly for testing or debugging
+        *
+        *      0                   1                   2                   3
+        *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *      |     AT_IV     | Length = 5    |           Reserved            |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *      |                                                               |
+        *      |                 Initialization Vector                         |
+        *      |                                                               |
+        *      |                                                               |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        */
+       case FR_SIM_IV:
+               if ((vp->da->flags.length && (da->flags.length != vp->vp_length)) ||
+                   (vp->vp_length != sizeof(packet_ctx->iv))) {
+                       fr_strerror_printf("%s: Attribute \"%s\" needs a value of exactly %zu bytes, "
+                                          "but value was %zu bytes", __FUNCTION__,
+                                          da->name, (size_t)da->flags.length, vp->vp_length);
+                       return -1;
+               }
+               memcpy(packet_ctx->iv, vp->vp_octets, sizeof(packet_ctx->iv));
+               packet_ctx->iv_included = true;
+               break;  /* Encode IV */
+
+       /*
+        *      AT_RES - Special case (RES length is in bits)
+        *
+        *      0                   1                   2                   3
+        *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *      |     AT_RES    |    Length     |          RES Length           |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
+        *      |                                                               |
+        *      |                             RES                               |
+        *      |                                                               |
+        *      |                                                               |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        */
+       case FR_EAP_AKA_RES:
        {
+               uint16_t        res_len = htons(vp->vp_length * 8);     /* Get length in bits */
                size_t          rounded_len = (vp->vp_length + 3) & ~3;
                size_t          pad_len = rounded_len - vp->vp_length;
                uint8_t         *p = out;
 
+               if ((vp->vp_length < 4) || (vp->vp_length > 16)) {
+                       fr_strerror_printf("%s: AKA-RES Length must be between 4-16 bytes, got %zu bytes",
+                                          __FUNCTION__, vp->vp_length);
+                       return -1;
+               }
+
                if ((rounded_len + 2) > outlen) {
                oos:
                        fr_strerror_printf("%s: Attribute exceeds available buffer space", __FUNCTION__);
                        return -1;
                }
 
-               *p++ = 0;       /* Reserved */
-               *p++ = 0;       /* Reserved */
+               memcpy(p, &res_len, sizeof(res_len));                   /* RES Length (bits, big endian) */
+               p += sizeof(res_len);
 
-               if (vp->da->flags.length && (vp->vp_length != vp->da->flags.length)) {
+               memcpy(p, vp->vp_octets, vp->vp_length);
+               p += vp->vp_length;
+
+               if (pad_len) {
+                       memset(p, 0, pad_len);
+                       p += pad_len;
+               }
+
+               len = p - out;
+       }
+               goto done;
+
+       /*
+        *      AT_AUTS - Octets type with no reserved field
+        *
+        *      0                   1                   2                   3
+        *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|
+        *      |    AT_AUTS    | Length = 4    |                               |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
+        *      |                                                               |
+        *      |                             AUTS                              |
+        *      |                                                               |
+        *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        */
+       case FR_EAP_AKA_AUTS:
+               if (vp->da->flags.length && (da->flags.length != vp->vp_length)) {
                        fr_strerror_printf("%s: Attribute \"%s\" needs a value of exactly %zu bytes, "
                                           "but value was %zu bytes", __FUNCTION__,
-                                          vp->da->name, (size_t)vp->da->flags.length, vp->vp_length);
+                                          da->name, (size_t)da->flags.length, vp->vp_length);
                        return -1;
                }
+               if (vp->vp_length > outlen) goto oos;
+
+               memcpy(out, vp->vp_octets, vp->vp_length);
+               len = vp->vp_length;
+               goto done;
+
+       default:
+               break;
+       }
+
+       switch (da->type) {
+       case FR_TYPE_OCTETS:
+       {
+               size_t          rounded_len;
+               size_t          pad_len;
+               uint8_t         *p = out;
+
+               /*
+                *      Autopad attributes
+                */
+               if (vp->da->flags.length && (vp->vp_length != vp->da->flags.length)) {
+                       rounded_len = (vp->vp_length + (vp->da->flags.length - 1)) & ~(vp->da->flags.length - 1);
+                       pad_len = rounded_len - vp->vp_length;
+               } else {
+                       rounded_len = (vp->vp_length + 3) & ~3;
+                       pad_len = rounded_len - vp->vp_length;
+               }
+
+               /*
+                *      Non-array attributes have a 2 byte padding
+                */
+               if (!vp->da->flags.array) {
+                       if ((rounded_len + 2) > outlen) goto oos;
+
+                       *p++ = 0;       /* Reserved */
+                       *p++ = 0;       /* Reserved */
+               /*
+                *      Fixed length array attributes have no padding
+                */
+               } else if (rounded_len > outlen) goto oos;
 
                memcpy(p, vp->vp_octets, vp->vp_length);
                p += vp->vp_length;
@@ -449,6 +563,7 @@ static ssize_t encode_value(uint8_t *out, size_t outlen,
                return -1;
        }
 
+done:
        /*
         *      Rebuilds the TLV stack for encoding the next attribute
         */
@@ -483,19 +598,30 @@ static ssize_t encode_array(uint8_t *out, size_t outlen,
        uint8_t                 *p = out, *end = p + outlen;
        uint8_t                 *value;
        size_t                  pad_len;
+       size_t                  element_len;
        uint16_t                actual_len;
        fr_dict_attr_t const    *da = tlv_stack[depth];
-
        rad_assert(da->flags.array);
 
        p += 2;
        value = p;      /* Space for actual length */
 
+       if (da->type == FR_TYPE_OCTETS) {
+               if (!da->flags.length) {
+                       fr_strerror_printf("Can't encode array type attribute \"%s\" as it does not "
+                                          "have a fixed length", da->name);
+                       return -1;
+               }
+               element_len = da->flags.length;
+       } else {
+               element_len = fr_sim_attr_sizes[da->type][0];
+       }
+
        /*
         *      Keep encoding as long as we have space to
         *      encode things.
         */
-       while (fr_sim_attr_sizes[da->type][0] <= ((size_t)(end - p))) {
+       while (element_len <= ((size_t)(end - p))) {
                VALUE_PAIR      *vp;
                ssize_t         slen;
 
@@ -508,8 +634,20 @@ static ssize_t encode_array(uint8_t *out, size_t outlen,
                if (!vp || (vp->da != da)) break;               /* Stop if we have an attribute of a different type */
        }
 
-       actual_len = htons((p - value) & UINT16_MAX);   /* Length of the elements we encoded */
-       memcpy(out, &actual_len, sizeof(actual_len));
+       /*
+        *      Arrays with an element size which is
+        *      a multiple of 4 don't need an
+        *      actual_length field, because the number
+        *      of elements can be calculated from
+        *      the attribute length.
+        */
+       if (element_len % 4) {
+               actual_len = htons((p - value) & UINT16_MAX);   /* Length of the elements we encoded */
+               memcpy(out, &actual_len, sizeof(actual_len));
+       } else {
+               out[0] = 0;
+               out[1] = 0;
+       }
 
        /*
         *      Pad value a multiple of 4
@@ -966,6 +1104,21 @@ static void *encode_test_ctx_aka(UNUSED TALLOC_CTX *ctx)
        return &test_ctx;
 }
 
+static void *encode_test_ctx_sim_rfc4186(UNUSED TALLOC_CTX *ctx)
+{
+       static fr_sim_encode_ctx_t      test_ctx;
+       static fr_sim_keys_t            keys = {
+                                               .k_encr = { 0x53, 0x6e, 0x5e, 0xbc, 0x44 ,0x65, 0x58, 0x2a,
+                                                           0xa6, 0xa8, 0xec, 0x99, 0x86, 0xeb, 0xb6, 0x20 }
+                                       };
+       fr_sim_global_init();
+
+       test_ctx.root = dict_sim_root;
+       test_ctx.keys = &keys;
+
+       return &test_ctx;
+}
+
 /*
  *     Test points
  */
@@ -980,3 +1133,9 @@ fr_test_point_pair_encode_t aka_tp_encode = {
        .test_ctx       = encode_test_ctx_aka,
        .func           = fr_sim_encode_pair
 };
+
+extern fr_test_point_pair_encode_t sim_tp_encode_rfc4186;
+fr_test_point_pair_encode_t sim_tp_encode_rfc4186 = {
+       .test_ctx       = encode_test_ctx_sim_rfc4186,
+       .func           = fr_sim_encode_pair
+};
index 3000ecf37df046b1e60e9c0a4c685d74a0856761..2959c94c457f034f946d29491c772df5ee05ea60 100644 (file)
@@ -13,7 +13,11 @@ FILES  := \
        radius_lucent.txt \
        radius_wimax.txt \
        radius_vendor.txt \
-       eap-sim.txt \
+       eap_aka_encode.txt \
+       eap_aka_decode.txt \
+       eap_sim_encode.txt \
+       eap_sim_decode.txt \
+       eap_sim_error.txt \
        dhcp.txt \
        tlv.txt \
        tunnel.txt \
diff --git a/src/tests/unit/eap_aka_decode.txt b/src/tests/unit/eap_aka_decode.txt
new file mode 100644 (file)
index 0000000..4dbfbb4
--- /dev/null
@@ -0,0 +1,24 @@
+#
+#  Tests for the EAP-SIM/AKA/AKA' decoder and decoder
+#
+
+# Only run the tests if we have TLS support
+need-feature tls
+
+# Load the EAP-SIM module
+load eap-sim
+
+#
+# Encode some AKA attributes with special formats
+#
+
+# EAP-AKA-Res is an octets type attribute but with a length field containing the length
+# of the value in bits.  It may be between 32 - 128 bits in length.
+decode-pair.aka_tp_decode 03 02 00 20 01 02 03 04
+data EAP-AKA-RES = 0x01020304
+
+decode-pair.aka_tp_decode 03 05 00 80 01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08
+data EAP-AKA-RES = 0x01020304050607080102030405060708
+
+decode-pair.aka_tp_decode 04 04 01 02 03 04 05 06 07 08 09 aa bb cc dd ee
+data EAP-AKA-AUTS = 0x010203040506070809aabbccddee
diff --git a/src/tests/unit/eap_aka_encode.txt b/src/tests/unit/eap_aka_encode.txt
new file mode 100644 (file)
index 0000000..7800214
--- /dev/null
@@ -0,0 +1,24 @@
+#
+#  Tests for the EAP-SIM/AKA/AKA' encoder and decoder
+#
+
+# Only run the tests if we have TLS support
+need-feature tls
+
+# Load the EAP-SIM module
+load eap-sim
+
+#
+# Encode some AKA attributes with special formats
+#
+
+# EAP-AKA-Res is an octets type attribute but with a length field containing the length
+# of the value in bits.  It may be between 32 - 128 bits in length.
+encode-pair.aka_tp_encode EAP-AKA-RES = 0x01020304
+data 03 02 00 20 01 02 03 04
+
+encode-pair.aka_tp_encode EAP-AKA-RES = 0x01020304050607080102030405060708
+data 03 05 00 80 01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08
+
+encode-pair.aka_tp_encode EAP-AKA-AUTS = 0x010203040506070809aabbccddee
+data 04 04 01 02 03 04 05 06 07 08 09 aa bb cc dd ee
diff --git a/src/tests/unit/eap_sim_decode.txt b/src/tests/unit/eap_sim_decode.txt
new file mode 100644 (file)
index 0000000..2a0beca
--- /dev/null
@@ -0,0 +1,99 @@
+#
+#  Tests for the EAP-SIM/AKA/AKA' encoder and decoder
+#
+
+# Encrypted attributes tested in eap_sim_encode
+
+# Only run the tests if we have TLS support
+need-feature tls
+
+# Load the EAP-SIM module
+load eap-sim
+
+# Boolean attribute
+decode-pair.sim_tp_decode 0d 01 00 00
+data EAP-SIM-Any-ID-Req = yes
+
+# Short
+decode-pair.sim_tp_decode 10 01 ff ff
+data EAP-SIM-Selected-Version = 65535
+
+# Octets
+decode-pair.sim_tp_decode 01 05 00 00 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 77
+data EAP-SIM-RAND = 0x00112233445566770011223344556677
+
+# Aliased (found)
+decode-pair.sim_tp_decode 0c 01 04 02
+data EAP-SIM-Notification = Temporarily-Denied
+
+# Aliased (notfound)
+decode-pair.sim_tp_decode 0c 01 ff ff
+data EAP-SIM-Notification = 65535
+
+# No string padding
+decode-pair.sim_tp_decode 0e 05 00 10 74 65 73 74 69 6e 67 31 32 33 40 73 69 6d 66 69
+data EAP-SIM-Identity = "testing123@simfi"
+
+# Three bytes string padding
+decode-pair.sim_tp_decode 0e 05 00 0d 74 65 73 74 69 6e 67 31 32 33 40 73 69 00 00 00
+data EAP-SIM-Identity = "testing123@si"
+
+# Two bytes string padding
+decode-pair.sim_tp_decode 0e 05 00 0e 74 65 73 74 69 6e 67 31 32 33 40 73 69 6d 00 00
+data EAP-SIM-Identity = "testing123@sim"
+
+# One byte string padding
+decode-pair.sim_tp_decode 0e 05 00 0f 74 65 73 74 69 6e 67 31 32 33 40 73 69 6d 66 00
+data EAP-SIM-Identity = "testing123@simf"
+
+# Empty string
+decode-pair.sim_tp_decode 0e 01 00 00
+data EAP-SIM-Identity = ""
+
+# Two attributes
+decode-pair.sim_tp_decode 0e 05 00 10 74 65 73 74 69 6e 67 31 32 33 40 73 69 6d 66 69 0d 01 00 00
+data EAP-SIM-Identity = "testing123@simfi", EAP-SIM-Any-ID-Req = yes
+
+# Array (one element)
+decode-pair.sim_tp_decode 0f 02 00 02 00 02 00 00
+data EAP-SIM-Version-List = 2
+
+# Array (multiple elements, with padding)
+decode-pair.sim_tp_decode 0f 03 00 06 00 02 00 03 00 04 00 00
+data EAP-SIM-Version-List = 2, EAP-SIM-Version-List = 3, EAP-SIM-Version-List = 4
+
+# Array (multiple elements, with no padding)
+decode-pair.sim_tp_decode 0f 03 00 08 00 02 ff ff 00 ff ff 00
+data EAP-SIM-Version-List = 2, EAP-SIM-Version-List = 65535, EAP-SIM-Version-List = 255, EAP-SIM-Version-List = 65280
+
+# Zero length array
+decode-pair.sim_tp_decode 0f 01 00 00
+data
+
+# Empty TLV
+decode-pair.sim_tp_decode 82 01 00 00
+data
+
+# RFC4186 A.3. example
+decode-pair.sim_tp_decode_rfc4186 0f 02 00 02 00 01 00 00
+data EAP-SIM-Version-List = 1
+
+# RFC4186 A.4. example
+decode-pair.sim_tp_decode_rfc4186 07 05 00 00 01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10 10 01 00 01
+data EAP-SIM-Nonce-MT = 0x0123456789abcdeffedcba9876543210, EAP-SIM-Selected-Version = 1
+
+# RFC4186 A.5. example
+decode-pair.sim_tp_decode_rfc4186 01 0d 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 81 05 00 00 9e 18 b0 c2 9a 65 22 63 c0 6e fb 54 dd 00 a8 95 82 2d 00 00 55 f2 93 9b bd b1 b1 9e a1 b4 7f c0 b3 e0 be 4c ab 2c f7 37 2d 98 e3 02 3c 6b b9 24 15 72 3d 58 ba d6 6c e0 84 e1 01 b6 0f 53 58 35 4b d4 21 82 78 ae a7 bf 2c ba ce 33 10 6a ed dc 62 5b 0c 1d 5a a6 7a 41 73 9a e5 b5 79 50 97 3f c7 ff 83 01 07 3c 6f 95 31 50 fc 30 3e a1 52 d1 e1 0a 2d 1f 4f 52 26 da a1 ee 90 05 47 22 52 bd b3 b7 1d 6f 0c 3a 34 90 31 6c 46 92 98 71 bd 45 cd fd bc a6 11 2f 07 f8 be 71 79 90 d2 5f 6d d7 f2 b7 b3 20 bf 4d 5a 99 2e 88 03 31 d7 29 94 5a ec 75 ae 5d 43 c8 ed a5 fe 62 33 fc ac 49 4e e6 7a 0d 50 4d 0b 05 00 00 fe f3 24 ac 39 62 b5 9f 3b d7 82 53 ae 4d cb 6a
+data EAP-SIM-RAND = 0x101112131415161718191a1b1c1d1e1f, EAP-SIM-RAND = 0x202122232425262728292a2b2c2d2e2f, EAP-SIM-RAND = 0x303132333435363738393a3b3c3d3e3f, EAP-SIM-IV = 0x9e18b0c29a652263c06efb54dd00a895, EAP-SIM-Next-Pseudonym = "w8w49PexCazWJ&xCIARmxuMKht5S1sxRDqXSEFBEg3DcZP9cIxTe5J4OyIwNGVzxeJOU1G", EAP-SIM-Next-Reauth-ID = "Y24fNSrz8BP274jOJaF17WfxI8YO7QX00pMXk9XMMVOw7broaNhTczuFq53aEpOkk3L0dm@eapsim.foo", EAP-SIM-MAC = 0xfef324ac3962b59f3bd78253ae4dcb6a
+
+# RFC4186 A.6. example
+decode-pair.sim_tp_decode_rfc4186 0b 05 00 00 f5 6d 64 33 e6 8e d2 97 6a c1 19 37 fc 3d 11 54
+data EAP-SIM-MAC = 0xf56d6433e68ed2976ac11937fc3d1154
+
+# RFC4186 A.9. example
+decode-pair.sim_tp_decode_rfc4186 81 05 00 00 d5 85 ac 77 86 b9 03 36 65 7c 77 b4 65 75 b9 c4 82 1d 00 00 68 62 91 a9 d2 ab c5 8c aa 32 94 b6 e8 5b 44 84 6c 44 e5 dc b2 de 8b 9e 80 d6 9d 49 85 8a 5d b8 4c dc 1c 9b c9 5c 01 b9 6b 6e ca 31 34 74 ae a6 d3 14 16 e1 9d aa 9d f7 0f 05 00 88 41 ca 80 14 96 4d 3b 30 a4 9b cf 43 e4 d3 f1 8e 86 29 5a 4a 2b 38 d9 6c 97 05 c2 bb b0 5c 4a ac e9 7d 5e af f5 64 04 6c 8b d3 0b c3 9b e5 e1 7a ce 2b 10 a6 0b 05 00 00 48 3a 17 99 b8 3d 7c d3 d0 a1 e4 01 d9 ee 47 70
+data EAP-SIM-IV = 0xd585ac7786b90336657c77b46575b9c4, EAP-SIM-Counter = 1, EAP-SIM-Nonce-S = 0x0123456789abcdeffedcba9876543210, EAP-SIM-Next-Reauth-ID = "uta0M0iyIsMwWp5TTdSdnOLvg2XDVf21OYt1vnfiMcs5dnIDHOIFVavIRzMRyzW6vFzdHW@eapsim.foo", EAP-SIM-MAC = 0x483a1799b83d7cd3d0a1e401d9ee4770
+
+# RFC4186 A.10. example
+decode-pair.sim_tp_decode_rfc4186 81 05 00 00 cd f7 ff a6 5d e0 4c 02 6b 56 c8 6b 76 b1 02 ea 82 05 00 00 b6 ed d3 82 79 e2 a1 42 3c 1a fc 5c 45 5c 7d 56 0b 05 00 00 fa f7 6b 71 fb e2 d2 55 b9 6a 35 66 c9 15 c6 17
+data EAP-SIM-IV = 0xcdf7ffa65de04c026b56c86b76b102ea, EAP-SIM-Counter = 1, EAP-SIM-MAC = 0xfaf76b71fbe2d255b96a3566c915c617
similarity index 54%
rename from src/tests/unit/eap-sim.txt
rename to src/tests/unit/eap_sim_encode.txt
index f91dc746781ccbcb732d31775c1ea258a72ab839..25cbdefc3891f9ba03f7cb121c40af69fc9c80ed 100644 (file)
@@ -17,15 +17,15 @@ encode-pair.sim_tp_encode EAP-SIM-Selected-Version = 1
 data 10 01 00 01
 
 # Octets
-encode-pair.sim_tp_encode EAP-SIM-Rand = 0x00112233445566770011223344556677
+encode-pair.sim_tp_encode EAP-SIM-RAND = 0x00112233445566770011223344556677
 data 01 05 00 00 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 77
 
 # Octets (autopad)
-encode-pair.sim_tp_encode EAP-SIM-Rand = 0x11
-data 01 02 00 00 11 00 00 00
+encode-pair.sim_tp_encode EAP-SIM-RAND = 0x11
+data 01 05 00 00 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 
 # Empty octets
-encode-pair.sim_tp_encode EAP-SIM-Rand = 0x
+encode-pair.sim_tp_encode EAP-SIM-RAND = 0x
 data 01 01 00 00
 
 # Aliased
@@ -96,3 +96,22 @@ data 0f 03 00 06 00 01 00 02 00 03 00 00
 encode-pair.sim_tp_encode EAP-SIM-Version-List = 1, EAP-SIM-Version-List = 2, EAP-SIM-Version-List = 3, EAP-SIM-Version-List = 4
 data 0f 03 00 08 00 01 00 02 00 03 00 04
 
+# RFC4186 A.3. example
+encode-pair.sim_tp_encode_rfc4186 EAP-SIM-Version-List = 1
+data 0f 02 00 02 00 01 00 00
+
+# RFC4186 A.4. example
+encode-pair.sim_tp_encode_rfc4186 EAP-SIM-Nonce-MT = 0x0123456789abcdeffedcba9876543210, EAP-SIM-Selected-Version = 1
+data 07 05 00 00 01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10 10 01 00 01
+
+# RFC4186 A.5. example
+encode-pair.sim_tp_encode_rfc4186 EAP-SIM-RAND = 0x101112131415161718191a1b1c1d1e1f, EAP-SIM-RAND = 0x202122232425262728292a2b2c2d2e2f, EAP-SIM-RAND = 0x303132333435363738393a3b3c3d3e3f, EAP-SIM-IV = 0x9e18b0c29a652263c06efb54dd00a895, EAP-SIM-Next-Pseudonym = "w8w49PexCazWJ&xCIARmxuMKht5S1sxRDqXSEFBEg3DcZP9cIxTe5J4OyIwNGVzxeJOU1G", EAP-SIM-Next-Reauth-ID = "Y24fNSrz8BP274jOJaF17WfxI8YO7QX00pMXk9XMMVOw7broaNhTczuFq53aEpOkk3L0dm@eapsim.foo"
+data 01 0d 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 81 05 00 00 9e 18 b0 c2 9a 65 22 63 c0 6e fb 54 dd 00 a8 95 82 2d 00 00 55 f2 93 9b bd b1 b1 9e a1 b4 7f c0 b3 e0 be 4c ab 2c f7 37 2d 98 e3 02 3c 6b b9 24 15 72 3d 58 ba d6 6c e0 84 e1 01 b6 0f 53 58 35 4b d4 21 82 78 ae a7 bf 2c ba ce 33 10 6a ed dc 62 5b 0c 1d 5a a6 7a 41 73 9a e5 b5 79 50 97 3f c7 ff 83 01 07 3c 6f 95 31 50 fc 30 3e a1 52 d1 e1 0a 2d 1f 4f 52 26 da a1 ee 90 05 47 22 52 bd b3 b7 1d 6f 0c 3a 34 90 31 6c 46 92 98 71 bd 45 cd fd bc a6 11 2f 07 f8 be 71 79 90 d2 5f 6d d7 f2 b7 b3 20 bf 4d 5a 99 2e 88 03 31 d7 29 94 5a ec 75 ae 5d 43 c8 ed a5 fe 62 33 fc ac 49 4e e6 7a 0d 50 4d
+
+# RFC4186 A.9. example
+encode-pair.sim_tp_encode_rfc4186 EAP-SIM-IV = 0xd585ac7786b90336657c77b46575b9c4, EAP-SIM-Counter = 1, EAP-SIM-Nonce-S = 0x0123456789abcdeffedcba9876543210, EAP-SIM-Next-Reauth-ID = "uta0M0iyIsMwWp5TTdSdnOLvg2XDVf21OYt1vnfiMcs5dnIDHOIFVavIRzMRyzW6vFzdHW@eapsim.foo"
+data 81 05 00 00 d5 85 ac 77 86 b9 03 36 65 7c 77 b4 65 75 b9 c4 82 1d 00 00 68 62 91 a9 d2 ab c5 8c aa 32 94 b6 e8 5b 44 84 6c 44 e5 dc b2 de 8b 9e 80 d6 9d 49 85 8a 5d b8 4c dc 1c 9b c9 5c 01 b9 6b 6e ca 31 34 74 ae a6 d3 14 16 e1 9d aa 9d f7 0f 05 00 88 41 ca 80 14 96 4d 3b 30 a4 9b cf 43 e4 d3 f1 8e 86 29 5a 4a 2b 38 d9 6c 97 05 c2 bb b0 5c 4a ac e9 7d 5e af f5 64 04 6c 8b d3 0b c3 9b e5 e1 7a ce 2b 10 a6
+
+# RFC4186 A.10. example
+encode-pair.sim_tp_encode_rfc4186 EAP-SIM-IV = 0xcdf7ffa65de04c026b56c86b76b102ea, EAP-SIM-Counter = 1
+data 81 05 00 00 cd f7 ff a6 5d e0 4c 02 6b 56 c8 6b 76 b1 02 ea 82 05 00 00 b6 ed d3 82 79 e2 a1 42 3c 1a fc 5c 45 5c 7d 56
diff --git a/src/tests/unit/eap_sim_error.txt b/src/tests/unit/eap_sim_error.txt
new file mode 100644 (file)
index 0000000..6d4cf06
--- /dev/null
@@ -0,0 +1,72 @@
+#
+#  Tests for the EAP-SIM/AKA/AKA' encoder and decoder
+#
+
+# Encrypted attributes tested in eap_sim_encode
+
+# Only run the tests if we have TLS support
+need-feature tls
+
+# Load the EAP-SIM module
+load eap-sim
+
+# Zero length attribute
+decode-pair.sim_tp_decode 0d 00
+data sim_decode_pair_internal: Malformed attribute 13: Length field 0
+
+# No length field
+decode-pair.sim_tp_decode 0d
+data sim_decode_pair_internal: Insufficient data: Expected >= 2 bytes, got 1 bytes
+
+# Boolean attribute (without reserved bytes)
+decode-pair.sim_tp_decode 0d 01
+data sim_decode_pair_internal: Insufficient data for attribute 13: Length field 4, remaining data 2
+
+# Boolean attribute (invalid length field)
+decode-pair.sim_tp_decode 0d 00 00 00
+data sim_decode_pair_internal: Malformed attribute 13: Length field 0
+
+# Boolean attribute (invalid length field)
+decode-pair.sim_tp_decode 0d 02 00 00
+data sim_decode_pair_internal: Insufficient data for attribute 13: Length field 8, remaining data 4
+
+# Unknown non-skippable attribute (99)
+decode-pair.sim_tp_decode 63 01 00 00
+data Unknown (non-skippable) attribute 99
+
+# Unknown skippable attribute (150) - Ensure the 'reserved bytes' in a normal octets
+# attribute are not skipped.
+decode-pair.sim_tp_decode 96 02 00 00 ff ff ff ff
+data Attr-1220.150 = 0x0000ffffffff
+
+# Array missing length field
+decode-pair.sim_tp_decode 0f 01
+data sim_decode_pair_internal: Insufficient data for attribute 15: Length field 4, remaining data 2
+
+# Array actual length field > remaining data
+decode-pair.sim_tp_decode 0f 01 00 01
+data sim_decode_array: Actual length field value (1) > attribute value length (0)
+
+# Array actual length not a multiple of the data type
+decode-pair.sim_tp_decode 0f 02 00 03 00 00 00 00
+data sim_array_members: Expected array actual length to be multiple of 2, got 3
+
+# Array actual length < element length
+decode-pair.sim_tp_decode 0f 02 00 01 00 00 00 00
+data sim_array_members: Element length (2) > array length (1)
+
+# String actual length greater than value length
+decode-pair.sim_tp_decode 0e 05 00 11 74 65 73 74 69 6e 67 31 32 33 40 73 69 00 00 00
+data sim_decode_pair_value: Actual length field value (17) > attribute value length (16)
+
+# String actual length (possible overflow)
+decode-pair.sim_tp_decode 0e 05 ff ff 74 65 73 74 69 6e 67 31 32 33 40 73 69 00 00 00
+data sim_decode_pair_value: Actual length field value (65535) > attribute value length (16)
+
+# TLV encrypted data not a multiple of 16 (raw attribute)
+decode-pair.sim_tp_decode 82 02 00 00 00 00 00 00
+data Attr-1220.130 = 0x000000000000
+
+# TLV encrypted data multiple of 16, zeroed out
+decode-pair.sim_tp_decode 82 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+data Attr-1220.130 = 0x000000000000000000000000000000000000