Skip to content

Commit

Permalink
Add Message-Authenticator support to libkrad
Browse files Browse the repository at this point in the history
draft-ietf-radext-deprecating-radius-03 describes multiple mitigation
measures for recent vulnerabilities discovered in the RADIUS protocol,
the BlastRADIUS (CVE-2024-3596).  One of these measures is enforcement
of the Message-Authenticator attribute.

Message-Authenticator was initially defined in RFC2869 (2000) as an
extension to improve authenticity and integrity verification.  The
BlastRADIUS vulnerability is now pushing vendors to generalize the use
of this attibute.  FreeRADIUS enforces Message-Authenticator as of
versions 3.2.5 and 3.0.27.  libkrad has to support
Message-Authenticator in order to remain compatible with these
implementations.

[ghudson@mit.edu: adjusted style and naming; simplified some
functions; edited commit message]
  • Loading branch information
jrisc authored and greghudson committed Oct 4, 2024
1 parent 1bfcf57 commit 221422e
Show file tree
Hide file tree
Showing 9 changed files with 348 additions and 58 deletions.
5 changes: 5 additions & 0 deletions src/include/k5-int.h
Original file line number Diff line number Diff line change
Expand Up @@ -2403,4 +2403,9 @@ krb5_boolean
k5_sname_compare(krb5_context context, krb5_const_principal sname,
krb5_const_principal princ);

/* Generate an HMAC-MD5 keyed checksum as defined by RFC 2104. */
krb5_error_code
k5_hmac_md5(const krb5_data *key, const krb5_crypto_iov *data, size_t num_data,
krb5_data *output);

#endif /* _KRB5_INT_H */
28 changes: 28 additions & 0 deletions src/lib/crypto/krb/checksum_hmac_md5.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,31 @@ krb5_error_code krb5int_hmacmd5_checksum(const struct krb5_cksumtypes *ctp,
free(hash_iov);
return ret;
}

krb5_error_code
k5_hmac_md5(const krb5_data *key, const krb5_crypto_iov *data, size_t num_data,
krb5_data *output)
{
krb5_error_code ret;
const struct krb5_hash_provider *hash = &krb5int_hash_md5;
krb5_keyblock keyblock = { 0 };
krb5_data hashed_key;
uint8_t hkeybuf[16];
krb5_crypto_iov iov;

/* Hash the key if it is longer than the block size. */
if (key->length > hash->blocksize) {
hashed_key = make_data(hkeybuf, sizeof(hkeybuf));
iov.flags = KRB5_CRYPTO_TYPE_DATA;
iov.data = *key;
ret = hash->hash(&iov, 1, &hashed_key);
if (ret)
return ret;
key = &hashed_key;
}

keyblock.magic = KV5M_KEYBLOCK;
keyblock.length = key->length;
keyblock.contents = (uint8_t *)key->data;
return krb5int_hmac_keyblock(hash, &keyblock, data, num_data, output);
}
1 change: 1 addition & 0 deletions src/lib/crypto/libk5crypto.exports
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,4 @@ krb5_c_prfplus
krb5_c_derive_prfplus
k5_enctype_to_ssf
krb5int_c_deprecated_enctype
k5_hmac_md5
17 changes: 17 additions & 0 deletions src/lib/krad/attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,23 @@ static const attribute_record attributes[UCHAR_MAX] = {
{"NAS-Port-Type", 4, 4, NULL, NULL},
{"Port-Limit", 4, 4, NULL, NULL},
{"Login-LAT-Port", 1, MAX_ATTRSIZE, NULL, NULL},
{NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
{NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
{NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
{NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
{NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
{NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
{NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
{NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
{NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
{NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
{NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
{NULL, 0, 0, NULL, NULL}, /* Password-Retry */
{NULL, 0, 0, NULL, NULL}, /* Prompt */
{NULL, 0, 0, NULL, NULL}, /* Connect-Info */
{NULL, 0, 0, NULL, NULL}, /* Configuration-Token */
{NULL, 0, 0, NULL, NULL}, /* EAP-Message */
{"Message-Authenticator", MD5_DIGEST_SIZE, MD5_DIGEST_SIZE, NULL, NULL},
};

/* Encode User-Password attribute. */
Expand Down
58 changes: 44 additions & 14 deletions src/lib/krad/attrset.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,34 +164,64 @@ krad_attrset_copy(const krad_attrset *set, krad_attrset **copy)
return 0;
}

/* Place an encoded attributes into outbuf at position *i. Increment *i by the
* length of the encoding. */
static krb5_error_code
append_attr(krb5_context ctx, const char *secret,
const uint8_t *auth, krad_attr type, const krb5_data *data,
uint8_t outbuf[MAX_ATTRSETSIZE], size_t *i)
{
uint8_t buffer[MAX_ATTRSIZE];
size_t attrlen;
krb5_error_code retval;

retval = kr_attr_encode(ctx, secret, auth, type, data, buffer, &attrlen);
if (retval)
return retval;

if (attrlen > MAX_ATTRSETSIZE - *i - 2)
return EMSGSIZE;

outbuf[(*i)++] = type;
outbuf[(*i)++] = attrlen + 2;
memcpy(outbuf + *i, buffer, attrlen);
*i += attrlen;

return 0;
}

krb5_error_code
kr_attrset_encode(const krad_attrset *set, const char *secret,
const unsigned char *auth,
const uint8_t *auth, krb5_boolean add_msgauth,
unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen)
{
unsigned char buffer[MAX_ATTRSIZE];
krb5_error_code retval;
size_t i = 0, attrlen;
krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator");
const uint8_t zeroes[MD5_DIGEST_SIZE] = { 0 };
krb5_data zerodata;
size_t i = 0;
attr *a;

if (set == NULL) {
*outlen = 0;
return 0;
}

K5_TAILQ_FOREACH(a, &set->list, list) {
retval = kr_attr_encode(set->ctx, secret, auth, a->type, &a->attr,
buffer, &attrlen);
if (retval != 0)
if (add_msgauth) {
/* Encode Message-Authenticator as the first attribute, per
* draft-ietf-radext-deprecating-radius-03 section 5.2. */
zerodata = make_data((uint8_t *)zeroes, MD5_DIGEST_SIZE);
retval = append_attr(set->ctx, secret, auth, msgauth_type, &zerodata,
outbuf, &i);
if (retval)
return retval;
}

if (i + attrlen + 2 > MAX_ATTRSETSIZE)
return EMSGSIZE;

outbuf[i++] = a->type;
outbuf[i++] = attrlen + 2;
memcpy(&outbuf[i], buffer, attrlen);
i += attrlen;
K5_TAILQ_FOREACH(a, &set->list, list) {
retval = append_attr(set->ctx, secret, auth, a->type, &a->attr,
outbuf, &i);
if (retval)
return retval;
}

*outlen = i;
Expand Down
7 changes: 5 additions & 2 deletions src/lib/krad/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
#define UCHAR_MAX 255
#endif

#define MD5_DIGEST_SIZE 16

/* RFC 2865 */
#define MAX_ATTRSIZE (UCHAR_MAX - 2)
#define MAX_ATTRSETSIZE (KRAD_PACKET_SIZE_MAX - 20)
Expand All @@ -65,10 +67,11 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
krad_attr type, const krb5_data *in,
unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);

/* Encode the attributes into the buffer. */
/* Encode the attributes into outbuf. If add_msgauth is true, include a zeroed
* Message-Authenticator as the first attribute. */
krb5_error_code
kr_attrset_encode(const krad_attrset *set, const char *secret,
const unsigned char *auth,
const uint8_t *auth, krb5_boolean add_msgauth,
unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen);

/* Decode attributes from a buffer. */
Expand Down
Loading

0 comments on commit 221422e

Please sign in to comment.