Skip to content

Commit

Permalink
Generate and verify message MACs in libkrad
Browse files Browse the repository at this point in the history
Implement some of the measures specified in
draft-ietf-radext-deprecating-radius-03 for mitigating the BlastRADIUS
attack (CVE-2024-3596):

* Include a Message-Authenticator MAC as the first attribute when
  generating a packet of type Access-Request, Access-Reject,
  Access-Accept, or Access-Challenge (sections 5.2.1 and 5.2.4), if
  the secret is non-empty.  (An empty secret indicates the use of Unix
  domain socket transport.)

* Validate the Message-Authenticator MAC in received packets, if
  present.

FreeRADIUS enforces Message-Authenticator as of versions 3.2.5 and
3.0.27.  libkrad must generate Message-Authenticator attributes in
order to remain compatible with these implementations.

[ghudson@mit.edu: adjusted style and naming; simplified some
functions; edited commit message]

ticket: 9142 (new)
tags: pullup
target_version: 1.21-next
  • Loading branch information
jrisc authored and greghudson committed Oct 14, 2024
1 parent 331e393 commit faf2477
Show file tree
Hide file tree
Showing 11 changed files with 339 additions and 44 deletions.
5 changes: 5 additions & 0 deletions src/include/k5-int.h
Original file line number Diff line number Diff line change
Expand Up @@ -2404,4 +2404,9 @@ krb5_boolean
k5_sname_compare(krb5_context context, krb5_const_principal sname,
krb5_const_principal princ);

/* Generate an HMAC-MD5 keyed checksum as specified 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 set 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 faf2477

Please sign in to comment.