RADIUS: Attributes with Extended Types (RFC 6929)

Supported extended types for RADIUS attributes for the cases defined in
RFC 6929.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2022-04-15 17:31:48 +03:00
parent 6c9e76e58a
commit 24763e3cd0
2 changed files with 193 additions and 28 deletions

View file

@ -1,6 +1,6 @@
/* /*
* RADIUS message processing * 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. * This software may be distributed under the terms of the BSD license.
* See README for more details. * See README for more details.
@ -159,7 +159,8 @@ static const char *radius_code_string(u8 code)
struct radius_attr_type { struct radius_attr_type {
u8 type; u16 type; /* 0..255 for basic types;
* (241 << 8) | <ext-type> for extended types */
char *name; char *name;
enum { enum {
RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, 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_HEXDUMP },
{ RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher", { RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher",
RADIUS_ATTR_HEXDUMP }, 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) #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; 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) static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
{ {
struct radius_attr_hdr_ext *ext = NULL;
const struct radius_attr_type *attr; const struct radius_attr_type *attr;
int len; int len;
unsigned char *pos; unsigned char *pos;
char buf[1000]; char buf[1000];
attr = radius_get_attr_type(hdr->type); if (hdr->length < sizeof(struct radius_attr_hdr))
wpa_printf(MSG_INFO, " Attribute %d (%s) length=%d",
hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr))
return; return;
len = hdr->length - sizeof(struct radius_attr_hdr); if (radius_is_ext_type(hdr->type)) {
pos = (unsigned char *) (hdr + 1); if (hdr->length < 4) {
wpa_printf(MSG_INFO,
" Invalid attribute %d (too short for extended type)",
hdr->type);
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);
}
if (!attr)
return;
switch (attr->data_type) { switch (attr->data_type) {
case RADIUS_ATTR_TEXT: 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, struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u16 type,
const u8 *data, size_t data_len) const u8 *data, size_t data_len)
{ {
size_t buf_needed; size_t buf_needed, max_len;
struct radius_attr_hdr *attr; struct radius_attr_hdr *attr = NULL;
struct radius_attr_hdr_ext *ext;
u8 ext_type = 0;
if (TEST_FAIL()) if (TEST_FAIL())
return NULL; return NULL;
if (data_len > RADIUS_MAX_ATTR_LEN) { if (type > 255) {
wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)", if (!radius_is_ext_type(type >> 8)) {
(unsigned long) data_len); wpa_printf(MSG_ERROR,
return NULL; "%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) { if (wpabuf_tailroom(msg->buf) < buf_needed) {
/* allocate more space for message buffer */ /* 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); msg->hdr = wpabuf_mhead(msg->buf);
} }
attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr)); if (radius_is_long_ext_type(type)) {
attr->type = type; size_t plen = 255 - sizeof(struct radius_attr_hdr_ext) - 1;
attr->length = sizeof(*attr) + data_len; size_t alen;
wpabuf_put_data(msg->buf, data, data_len);
if (radius_msg_add_attr_to_array(msg, attr)) do {
return NULL; 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; 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, int radius_user_password_hide(struct radius_msg *msg,
const u8 *data, size_t data_len, const u8 *data, size_t data_len,
const u8 *secret, size_t secret_len, const u8 *secret, size_t secret_len,

View file

@ -1,6 +1,6 @@
/* /*
* RADIUS message processing * 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. * This software may be distributed under the terms of the BSD license.
* See README for more details. * See README for more details.
@ -46,7 +46,15 @@ struct radius_attr_hdr {
/* followed by length-2 octets of attribute value */ /* followed by length-2 octets of attribute value */
} STRUCT_PACKED; } 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_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, enum { RADIUS_ATTR_USER_NAME = 1,
RADIUS_ATTR_USER_PASSWORD = 2, RADIUS_ATTR_USER_PASSWORD = 2,
@ -113,6 +121,18 @@ enum { RADIUS_ATTR_USER_NAME = 1,
RADIUS_ATTR_WLAN_GROUP_CIPHER = 187, RADIUS_ATTR_WLAN_GROUP_CIPHER = 187,
RADIUS_ATTR_WLAN_AKM_SUITE = 188, RADIUS_ATTR_WLAN_AKM_SUITE = 188,
RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER = 189, 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, int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
size_t secret_len, size_t secret_len,
int require_message_authenticator); 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); const u8 *data, size_t data_len);
struct radius_msg * radius_msg_parse(const u8 *data, size_t 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, 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); const u8 *recv_key, size_t recv_key_len);
int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data, int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
size_t len); 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, int radius_user_password_hide(struct radius_msg *msg,
const u8 *data, size_t data_len, const u8 *data, size_t data_len,
const u8 *secret, size_t secret_len, const u8 *secret, size_t secret_len,