]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
RADIUS: Attributes with Extended Types (RFC 6929)
authorJouni Malinen <j@w1.fi>
Fri, 15 Apr 2022 14:31:48 +0000 (17:31 +0300)
committerJouni Malinen <j@w1.fi>
Fri, 15 Apr 2022 15:40:55 +0000 (18:40 +0300)
Supported extended types for RADIUS attributes for the cases defined in
RFC 6929.

Signed-off-by: Jouni Malinen <j@w1.fi>
src/radius/radius.c
src/radius/radius.h

index be16e27b9d7c30c0ab43f0dd50a6298fb106d48a..a64228067e0ed605caf18414aacf416fff5af18f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2011-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2011-2022, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -159,7 +159,8 @@ static const char *radius_code_string(u8 code)
 
 
 struct radius_attr_type {
-       u8 type;
+       u16 type; /* 0..255 for basic types;
+                  * (241 << 8) | <ext-type> for extended types */
        char *name;
        enum {
                RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
@@ -260,11 +261,31 @@ static const struct radius_attr_type radius_attrs[] =
          RADIUS_ATTR_HEXDUMP },
        { RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher",
          RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_EXT_TYPE_1, "Extended-Type-1", RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_EXT_TYPE_2, "Extended-Type-2", RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_EXT_TYPE_3, "Extended-Type-3", RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_EXT_TYPE_4, "Extended-Type-4", RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_LONG_EXT_TYPE_1, "Long-Extended-Type-1",
+         RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_LONG_EXT_TYPE_2, "Long-Extended-Type-2",
+         RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_1, "Extended-Vendor-Specific-1",
+         RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_2, "Extended-Vendor-Specific-2",
+         RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_3, "Extended-Vendor-Specific-3",
+         RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_4, "Extended-Vendor-Specific-4",
+         RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5, "Extended-Vendor-Specific-5",
+         RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_6, "Extended-Vendor-Specific-6",
+         RADIUS_ATTR_UNDIST },
 };
 #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs)
 
 
-static const struct radius_attr_type *radius_get_attr_type(u8 type)
+static const struct radius_attr_type * radius_get_attr_type(u16 type)
 {
        size_t i;
 
@@ -277,23 +298,60 @@ static const struct radius_attr_type *radius_get_attr_type(u8 type)
 }
 
 
+static bool radius_is_long_ext_type(u8 type)
+{
+       return type == RADIUS_ATTR_LONG_EXT_TYPE_1 ||
+               type == RADIUS_ATTR_LONG_EXT_TYPE_2;
+}
+
+
+static bool radius_is_ext_type(u8 type)
+{
+       return type >= RADIUS_ATTR_EXT_TYPE_1 &&
+               type <= RADIUS_ATTR_LONG_EXT_TYPE_2;
+}
+
+
 static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
 {
+       struct radius_attr_hdr_ext *ext = NULL;
        const struct radius_attr_type *attr;
        int len;
        unsigned char *pos;
        char buf[1000];
 
-       attr = radius_get_attr_type(hdr->type);
+       if (hdr->length < sizeof(struct radius_attr_hdr))
+               return;
 
-       wpa_printf(MSG_INFO, "   Attribute %d (%s) length=%d",
-                  hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
+       if (radius_is_ext_type(hdr->type)) {
+               if (hdr->length < 4) {
+                       wpa_printf(MSG_INFO,
+                                  "   Invalid attribute %d (too short for extended type)",
+                               hdr->type);
+                       return;
+               }
 
-       if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr))
-               return;
+               ext = (struct radius_attr_hdr_ext *) hdr;
+       }
+
+       if (ext) {
+               attr = radius_get_attr_type((ext->type << 8) | ext->ext_type);
+               wpa_printf(MSG_INFO, "   Attribute %d.%d (%s) length=%d",
+                          ext->type, ext->ext_type,
+                          attr ? attr->name : "?Unknown?", ext->length);
+               pos = (unsigned char *) (ext + 1);
+               len = ext->length - sizeof(struct radius_attr_hdr_ext);
+       } else {
+               attr = radius_get_attr_type(hdr->type);
+               wpa_printf(MSG_INFO, "   Attribute %d (%s) length=%d",
+                          hdr->type, attr ? attr->name : "?Unknown?",
+                          hdr->length);
+               pos = (unsigned char *) (hdr + 1);
+               len = hdr->length - sizeof(struct radius_attr_hdr);
+       }
 
-       len = hdr->length - sizeof(struct radius_attr_hdr);
-       pos = (unsigned char *) (hdr + 1);
+       if (!attr)
+               return;
 
        switch (attr->data_type) {
        case RADIUS_ATTR_TEXT:
@@ -627,22 +685,54 @@ static int radius_msg_add_attr_to_array(struct radius_msg *msg,
 }
 
 
-struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
-                                           const u8 *data, size_t data_len)
+struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u16 type,
+                                            const u8 *data, size_t data_len)
 {
-       size_t buf_needed;
-       struct radius_attr_hdr *attr;
+       size_t buf_needed, max_len;
+       struct radius_attr_hdr *attr = NULL;
+       struct radius_attr_hdr_ext *ext;
+       u8 ext_type = 0;
 
        if (TEST_FAIL())
                return NULL;
 
-       if (data_len > RADIUS_MAX_ATTR_LEN) {
-               wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)",
-                      (unsigned long) data_len);
-               return NULL;
+       if (type > 255) {
+               if (!radius_is_ext_type(type >> 8)) {
+                       wpa_printf(MSG_ERROR,
+                                  "%s: Undefined extended type %d.%d",
+                                  __func__, type >> 8, type & 0xff);
+                       return NULL;
+               }
+               ext_type = type & 0xff;
+               type >>= 8;
+       } else if (radius_is_ext_type(type)) {
+               wpa_printf(MSG_ERROR, "%s: Unexpected extended type use for %d",
+                          __func__, type);
        }
 
-       buf_needed = sizeof(*attr) + data_len;
+       if (radius_is_long_ext_type(type)) {
+               size_t hdr_len = sizeof(struct radius_attr_hdr_ext) + 1;
+               size_t plen = 255 - hdr_len;
+               size_t num;
+
+               max_len = 4096;
+               num = (data_len + plen - 1) / plen;
+               if (num == 0)
+                       num = 1;
+               buf_needed = num * hdr_len + data_len;
+       } else if (radius_is_ext_type(type)) {
+               max_len = RADIUS_MAX_EXT_ATTR_LEN;
+               buf_needed = sizeof(struct radius_attr_hdr_ext) + data_len;
+       } else {
+               max_len = RADIUS_MAX_ATTR_LEN;
+               buf_needed = sizeof(*attr) + data_len;
+       }
+       if (data_len > max_len) {
+               wpa_printf(MSG_ERROR,
+                          "%s: too long attribute (%zu > %zu bytes)",
+                          __func__, data_len, max_len);
+               return NULL;
+       }
 
        if (wpabuf_tailroom(msg->buf) < buf_needed) {
                /* allocate more space for message buffer */
@@ -651,13 +741,44 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
                msg->hdr = wpabuf_mhead(msg->buf);
        }
 
-       attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
-       attr->type = type;
-       attr->length = sizeof(*attr) + data_len;
-       wpabuf_put_data(msg->buf, data, data_len);
-
-       if (radius_msg_add_attr_to_array(msg, attr))
-               return NULL;
+       if (radius_is_long_ext_type(type)) {
+               size_t plen = 255 - sizeof(struct radius_attr_hdr_ext) - 1;
+               size_t alen;
+
+               do {
+                       alen = data_len > plen ? plen : data_len;
+                       ext = wpabuf_put(msg->buf,
+                                        sizeof(struct radius_attr_hdr_ext));
+                       if (!attr)
+                               attr = (struct radius_attr_hdr *) ext;
+                       ext->type = type;
+                       ext->length = sizeof(*ext) + 1 + alen;
+                       ext->ext_type = ext_type;
+                       wpabuf_put_u8(msg->buf, data_len > alen ? 0x80 : 0);
+                       wpabuf_put_data(msg->buf, data, data_len);
+                       data += alen;
+                       data_len -= alen;
+                       if (radius_msg_add_attr_to_array(
+                                   msg, (struct radius_attr_hdr *) ext))
+                               return NULL;
+               } while (data_len > 0);
+       } else if (radius_is_ext_type(type)) {
+               ext = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr_ext));
+               attr = (struct radius_attr_hdr *) ext;
+               ext->type = type;
+               ext->length = sizeof(*ext) + data_len;
+               ext->ext_type = ext_type;
+               wpabuf_put_data(msg->buf, data, data_len);
+               if (radius_msg_add_attr_to_array(msg, attr))
+                       return NULL;
+       } else {
+               attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
+               attr->type = type;
+               attr->length = sizeof(*attr) + data_len;
+               wpabuf_put_data(msg->buf, data, data_len);
+               if (radius_msg_add_attr_to_array(msg, attr))
+                       return NULL;
+       }
 
        return attr;
 }
@@ -1285,6 +1406,28 @@ int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
 }
 
 
+int radius_msg_add_ext_vs(struct radius_msg *msg, u16 type, u32 vendor_id,
+                         u8 vendor_type, const u8 *data, size_t len)
+{
+       struct radius_attr_hdr *attr;
+       u8 *buf, *pos;
+       size_t alen;
+
+       alen = 4 + 1 + len;
+       buf = os_malloc(alen);
+       if (!buf)
+               return 0;
+       pos = buf;
+       WPA_PUT_BE32(pos, vendor_id);
+       pos += 4;
+       *pos++ = vendor_type;
+       os_memcpy(pos, data, len);
+       attr = radius_msg_add_attr(msg, type, buf, alen);
+       os_free(buf);
+       return attr != NULL;
+}
+
+
 int radius_user_password_hide(struct radius_msg *msg,
                              const u8 *data, size_t data_len,
                              const u8 *secret, size_t secret_len,
index fb814818072a0947c536e9d8fa61ad8fc118f394..490c8d1f6782af0a786f0006d2604a5d086d53d4 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2012, 2014-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, 2014-2022, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -46,7 +46,15 @@ struct radius_attr_hdr {
        /* followed by length-2 octets of attribute value */
 } STRUCT_PACKED;
 
+struct radius_attr_hdr_ext {
+       u8 type;
+       u8 length; /* including this header */
+       u8 ext_type;
+       /* followed by length-3 octets of attribute value */
+} STRUCT_PACKED;
+
 #define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr))
+#define RADIUS_MAX_EXT_ATTR_LEN (255 - sizeof(struct radius_attr_hdr_ext))
 
 enum { RADIUS_ATTR_USER_NAME = 1,
        RADIUS_ATTR_USER_PASSWORD = 2,
@@ -113,6 +121,18 @@ enum { RADIUS_ATTR_USER_NAME = 1,
        RADIUS_ATTR_WLAN_GROUP_CIPHER = 187,
        RADIUS_ATTR_WLAN_AKM_SUITE = 188,
        RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER = 189,
+       RADIUS_ATTR_EXT_TYPE_1 = 241,
+       RADIUS_ATTR_EXT_TYPE_2 = 242,
+       RADIUS_ATTR_EXT_TYPE_3 = 243,
+       RADIUS_ATTR_EXT_TYPE_4 = 244,
+       RADIUS_ATTR_LONG_EXT_TYPE_1 = 245,
+       RADIUS_ATTR_LONG_EXT_TYPE_2 = 246,
+       RADIUS_ATTR_EXT_VENDOR_SPECIFIC_1 = (241 << 8) | 26,
+       RADIUS_ATTR_EXT_VENDOR_SPECIFIC_2 = (242 << 8) | 26,
+       RADIUS_ATTR_EXT_VENDOR_SPECIFIC_3 = (243 << 8) | 26,
+       RADIUS_ATTR_EXT_VENDOR_SPECIFIC_4 = (244 << 8) | 26,
+       RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5 = (245 << 8) | 26,
+       RADIUS_ATTR_EXT_VENDOR_SPECIFIC_6 = (246 << 8) | 26,
 };
 
 
@@ -257,7 +277,7 @@ int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
 int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
                              size_t secret_len,
                              int require_message_authenticator);
-struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type,
+struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u16 type,
                                             const u8 *data, size_t data_len);
 struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
 int radius_msg_add_eap(struct radius_msg *msg, const u8 *data,
@@ -284,6 +304,8 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg,
                             const u8 *recv_key, size_t recv_key_len);
 int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
                       size_t len);
+int radius_msg_add_ext_vs(struct radius_msg *msg, u16 type, u32 vendor_id,
+                         u8 vendor_type, const u8 *data, size_t len);
 int radius_user_password_hide(struct radius_msg *msg,
                              const u8 *data, size_t data_len,
                              const u8 *secret, size_t secret_len,