From: Alan T. DeKok Date: Fri, 7 May 2021 14:21:42 +0000 (-0400) Subject: let's actually add code for this X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=36d045a464090893eea52182182297989f77e568;p=thirdparty%2Ffreeradius-server.git let's actually add code for this --- diff --git a/src/protocols/radius/attrs.h b/src/protocols/radius/attrs.h index 83ba9ac68a6..df049bd2f08 100644 --- a/src/protocols/radius/attrs.h +++ b/src/protocols/radius/attrs.h @@ -40,3 +40,4 @@ extern fr_dict_attr_t const *attr_eap_message; extern fr_dict_attr_t const *attr_message_authenticator; extern fr_dict_attr_t const *attr_state; extern fr_dict_attr_t const *attr_vendor_specific; +extern fr_dict_attr_t const *attr_nas_filter_rule; diff --git a/src/protocols/radius/base.c b/src/protocols/radius/base.c index 41185c62441..39a2965fd4a 100644 --- a/src/protocols/radius/base.c +++ b/src/protocols/radius/base.c @@ -60,6 +60,7 @@ fr_dict_attr_t const *attr_eap_message; fr_dict_attr_t const *attr_message_authenticator; fr_dict_attr_t const *attr_state; fr_dict_attr_t const *attr_vendor_specific; +fr_dict_attr_t const *attr_nas_filter_rule; extern fr_dict_attr_autoload_t libfreeradius_radius_dict_attr[]; fr_dict_attr_autoload_t libfreeradius_radius_dict_attr[] = { @@ -72,6 +73,7 @@ fr_dict_attr_autoload_t libfreeradius_radius_dict_attr[] = { { .out = &attr_message_authenticator, .name = "Message-Authenticator", .type = FR_TYPE_OCTETS, .dict = &dict_radius }, { .out = &attr_state, .name = "State", .type = FR_TYPE_OCTETS, .dict = &dict_radius }, { .out = &attr_vendor_specific, .name = "Vendor-Specific", .type = FR_TYPE_VSA, .dict = &dict_radius }, + { .out = &attr_nas_filter_rule, .name = "NAS-Filter-Rule", .type = FR_TYPE_STRING, .dict = &dict_radius }, { NULL } }; diff --git a/src/protocols/radius/encode.c b/src/protocols/radius/encode.c index e215bb33d42..0ba34c186d9 100644 --- a/src/protocols/radius/encode.c +++ b/src/protocols/radius/encode.c @@ -1227,6 +1227,111 @@ static ssize_t encode_vsa(fr_dbuff_t *dbuff, return fr_dbuff_set(dbuff, &work_dbuff); } +/** Encode NAS-Filter-Rule + * + * Concatenating the string attributes together, separated by a 0x00 byte, + */ +static ssize_t encode_nas_filter_rule(fr_dbuff_t *dbuff, + fr_da_stack_t *da_stack, UNUSED unsigned int depth, + fr_dcursor_t *cursor, UNUSED void *encode_ctx) +{ + fr_dbuff_t work_dbuff = FR_DBUFF_NO_ADVANCE(dbuff); + fr_dbuff_marker_t hdr, frag_hdr; + fr_pair_t *vp = fr_dcursor_current(cursor); + size_t attr_len = 2; + + FR_PROTO_STACK_PRINT(da_stack, depth); + + fr_dbuff_marker(&hdr, &work_dbuff); + fr_dbuff_marker(&frag_hdr, &work_dbuff); + fr_dbuff_advance(&hdr, 1); + FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t)vp->da->attr, 0x00); + + fr_assert(vp->da == attr_nas_filter_rule); + + while (vp) { + size_t data_len = vp->vp_length; + size_t frag_len; + char const *p = vp->vp_strvalue; + + /* + * Keep encoding this attribute until it's done. + */ + while (data_len > 0) { + frag_len = data_len; + + /* + * This fragment doesn't overflow the + * attribute. Copy it over, update the + * length, but leave the marker at the + * current header. + */ + if ((attr_len + frag_len) <= UINT8_MAX) { + FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, p, frag_len); + attr_len += frag_len; + + fr_dbuff_set(&frag_hdr, &hdr); + fr_dbuff_in(&frag_hdr, (uint8_t) attr_len); /* there's no fr_dbuff_in_no_advance() */ + break; + } + + /* + * This fragment overflows the attribute. + * Copy the fragment in, and create a new + * attribute header. + */ + frag_len = UINT8_MAX - attr_len; + FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, p, frag_len); + fr_dbuff_in(&hdr, (uint8_t) UINT8_MAX); + + fr_dbuff_marker(&hdr, &work_dbuff); + fr_dbuff_advance(&hdr, 1); + FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t)vp->da->attr, 0x02); + attr_len = 2; + + p += frag_len; + data_len -= frag_len; + } + + /* + * If we have nothing more to do here, then stop. + */ + vp = fr_dcursor_next(cursor); + if (!vp || (vp->da != attr_nas_filter_rule)) { + break; + } + + /* + * We have to add a zero byte. If it doesn't + * overflow the current attribute, then just add + * it in. + */ + if (attr_len < UINT8_MAX) { + attr_len++; + FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, 0x00); + + fr_dbuff_set(&frag_hdr, &hdr); + fr_dbuff_in(&frag_hdr, (uint8_t) attr_len); /* there's no fr_dbuff_in_no_advance() */ + continue; + } + + /* + * The zero byte causes the current attribute to + * overflow. Create a new header with the zero + * byte already populated, and keep going. + */ + fr_dbuff_marker(&hdr, &work_dbuff); + fr_dbuff_advance(&hdr, 1); + FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t)vp->da->attr, 0x00, 0x00); + attr_len = 3; + } + + vp = fr_dcursor_current(cursor); + fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL); + + return fr_dbuff_set(dbuff, &work_dbuff); +} + /** Encode an RFC standard attribute 1..255 * * This function is not the same as encode_attribute(), because this @@ -1300,6 +1405,14 @@ static ssize_t encode_rfc(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned i return fr_dbuff_set(dbuff, &work_dbuff); } + /* + * NAS-Filter-Rule has a stupid format in order to save + * one byte per attribute. + */ + if (vp->da == attr_nas_filter_rule) { + return encode_nas_filter_rule(dbuff, da_stack, depth, cursor, encode_ctx); + } + /* * Once we've checked for various top-level magic, RFC attributes are just TLVs. */