From 221422ef0885091fc29606922632f0ac6cba6760 Mon Sep 17 00:00:00 2001 From: Julien Rische Date: Thu, 22 Aug 2024 17:15:50 +0200 Subject: [PATCH] Add Message-Authenticator support to libkrad 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] --- src/include/k5-int.h | 5 + src/lib/crypto/krb/checksum_hmac_md5.c | 28 +++ src/lib/crypto/libk5crypto.exports | 1 + src/lib/krad/attr.c | 17 ++ src/lib/krad/attrset.c | 58 +++-- src/lib/krad/internal.h | 7 +- src/lib/krad/packet.c | 284 +++++++++++++++++++++---- src/lib/krad/t_daemon.py | 3 +- src/tests/t_otp.py | 3 + 9 files changed, 348 insertions(+), 58 deletions(-) diff --git a/src/include/k5-int.h b/src/include/k5-int.h index 80c966ec52b..99b053d718d 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -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 */ diff --git a/src/lib/crypto/krb/checksum_hmac_md5.c b/src/lib/crypto/krb/checksum_hmac_md5.c index ec024f39661..a809388549f 100644 --- a/src/lib/crypto/krb/checksum_hmac_md5.c +++ b/src/lib/crypto/krb/checksum_hmac_md5.c @@ -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); +} diff --git a/src/lib/crypto/libk5crypto.exports b/src/lib/crypto/libk5crypto.exports index 052f4d4b510..2b27028a0f5 100644 --- a/src/lib/crypto/libk5crypto.exports +++ b/src/lib/crypto/libk5crypto.exports @@ -103,3 +103,4 @@ krb5_c_prfplus krb5_c_derive_prfplus k5_enctype_to_ssf krb5int_c_deprecated_enctype +k5_hmac_md5 diff --git a/src/lib/krad/attr.c b/src/lib/krad/attr.c index 9c13d9d7556..4ad32122a8e 100644 --- a/src/lib/krad/attr.c +++ b/src/lib/krad/attr.c @@ -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. */ diff --git a/src/lib/krad/attrset.c b/src/lib/krad/attrset.c index f309f1581c1..488bfce7bbb 100644 --- a/src/lib/krad/attrset.c +++ b/src/lib/krad/attrset.c @@ -164,14 +164,42 @@ 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) { @@ -179,19 +207,21 @@ kr_attrset_encode(const krad_attrset *set, const char *secret, 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; diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h index 7619563fc56..1edb9482b61 100644 --- a/src/lib/krad/internal.h +++ b/src/lib/krad/internal.h @@ -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) @@ -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. */ diff --git a/src/lib/krad/packet.c b/src/lib/krad/packet.c index aee830b651f..f2dac0d9607 100644 --- a/src/lib/krad/packet.c +++ b/src/lib/krad/packet.c @@ -36,11 +36,13 @@ typedef unsigned char uchar; /* RFC 2865 */ +#define MSGAUTH_SIZE (2 + MD5_DIGEST_SIZE) #define OFFSET_CODE 0 #define OFFSET_ID 1 #define OFFSET_LENGTH 2 #define OFFSET_AUTH 4 #define OFFSET_ATTR 20 +#define OFFSET_RESP_MSGAUTH (OFFSET_ATTR + MSGAUTH_SIZE) #define AUTH_FIELD_SIZE (OFFSET_ATTR - OFFSET_AUTH) #define offset(d, o) (&(d)->data[o]) @@ -222,6 +224,113 @@ packet_set_attrset(krb5_context ctx, const char *secret, krad_packet *pkt) return kr_attrset_decode(ctx, &tmp, secret, pkt_auth(pkt), &pkt->attrset); } +/* Determines if a request or response requires a Message-Authenticator + * attribute. */ +static inline krb5_boolean +requires_msgauth(const char *secret, krad_code code) +{ + /* If no secret is provided, assume that the transport is a UNIX socket. + * Message-Authenticator is required only on UDP and TCP connections. */ + if (*secret == '\0') + return FALSE; + + /* + * draft-ietf-radext-deprecating-radius-03 (section 7.2): + * RADIUS clients MUST include the Message-Authenticator in all + * Access-Request packets when UDP or TCP transport is used. + * + * draft-ietf-radext-deprecating-radius-03 (section 5.2.4): + * Servers MUST add Message-Authenticator as the first attribute in all + * responses to Access-Request packets. That is, all Access-Accept, + * Access-Reject, Access-Challenge, and Protocol-Error packets. + */ + return code == krad_code_name2num("Access-Request") || + code == krad_code_name2num("Access-Reject") || + code == krad_code_name2num("Access-Accept") || + code == krad_code_name2num("Access-Challenge") || + code == krad_code_name2num("Protocol-Error"); +} + +/* Check if the packet has a Message-Authenticator attribute. */ +static inline krb5_boolean +has_pkt_msgauth(const krad_packet *pkt) +{ + krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator"); + + return krad_attrset_get(pkt->attrset, msgauth_type, 0) != NULL; +} + +/* Return the beginning of the Message-Authenticator attribute in pkt, or NULL + * if no such attribute is present. */ +static const uint8_t * +lookup_msgauth_addr(const krad_packet *pkt) +{ + krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator"); + size_t i; + uint8_t *p; + + i = OFFSET_ATTR; + while (i + 2 < pkt->pkt.length) { + p = (uint8_t *)offset(&pkt->pkt, i); + if (msgauth_type == (krad_attr)*p) + return p; + i += p[1]; + } + + return NULL; +} + +/* + * Calculate the message authenticator MAC for pkt as specified in RFC 2869 + * section 5.14, placing the result in mac_out. Use the provided authenticator + * auth, which may be from pkt or from a corresponding request. + */ +static krb5_error_code +calculate_mac(const char *secret, const krad_packet *pkt, + const uint8_t auth[AUTH_FIELD_SIZE], + uint8_t mac_out[MD5_DIGEST_SIZE]) +{ + uint8_t zeroed_msgauth[MSGAUTH_SIZE]; + krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator"); + const uint8_t *msgauth_attr, *msgauth_end, *pkt_end; + krb5_crypto_iov input[5]; + krb5_data ksecr, mac; + + msgauth_attr = lookup_msgauth_addr(pkt); + if (msgauth_attr == NULL) + return EINVAL; + msgauth_end = msgauth_attr + MSGAUTH_SIZE; + pkt_end = (const uint8_t *)pkt->pkt.data + pkt->pkt.length; + + /* Keep code, id, and length as they are. */ + input[0].flags = KRB5_CRYPTO_TYPE_DATA; + input[0].data = make_data(pkt->pkt.data, OFFSET_AUTH); + + /* Use authenticator from the argument, or from the packet. */ + input[1].flags = KRB5_CRYPTO_TYPE_DATA; + input[1].data = make_data((uint8_t *)auth, AUTH_FIELD_SIZE); + + /* Read any attributes before Message-Authenticator. */ + input[2].flags = KRB5_CRYPTO_TYPE_DATA; + input[2].data = make_data(pkt_attr(pkt), msgauth_attr - pkt_attr(pkt)); + + /* Read Message-Authenticator with the data bytes all set to zero, per RFC + * 2869 section 5.14. */ + zeroed_msgauth[0] = msgauth_type; + zeroed_msgauth[1] = MSGAUTH_SIZE; + memset(zeroed_msgauth + 2, 0, MD5_DIGEST_SIZE); + input[3].flags = KRB5_CRYPTO_TYPE_DATA; + input[3].data = make_data(zeroed_msgauth, MSGAUTH_SIZE); + + /* Read any attributes after Message-Authenticator. */ + input[4].flags = KRB5_CRYPTO_TYPE_DATA; + input[4].data = make_data((uint8_t *)msgauth_end, pkt_end - msgauth_end); + + mac = make_data(mac_out, MD5_DIGEST_SIZE); + ksecr = string2data((char *)secret); + return k5_hmac_md5(&ksecr, input, 5, &mac); +} + ssize_t krad_packet_bytes_needed(const krb5_data *buffer) { @@ -252,49 +361,62 @@ krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code, void *data, krad_packet **request) { krb5_error_code retval; - krad_packet *pkt; + krad_packet *pkt = NULL; uchar id; size_t attrset_len; + krb5_boolean msgauth_required; pkt = packet_new(); if (pkt == NULL) { if (cb != NULL) (*cb)(data, TRUE); - return ENOMEM; + retval = ENOMEM; + goto cleanup; } /* Generate the ID. */ retval = id_generate(ctx, cb, data, &id); if (retval != 0) - goto error; + goto cleanup; pkt_id_set(pkt, id); /* Generate the authenticator. */ retval = auth_generate_random(ctx, pkt_auth(pkt)); if (retval != 0) - goto error; + goto cleanup; + + /* Determine if Message-Authenticator is required. */ + msgauth_required = (*secret != '\0' && + code == krad_code_name2num("Access-Request")); /* Encode the attributes. */ - retval = kr_attrset_encode(set, secret, pkt_auth(pkt), pkt_attr(pkt), - &attrset_len); + retval = kr_attrset_encode(set, secret, pkt_auth(pkt), msgauth_required, + pkt_attr(pkt), &attrset_len); if (retval != 0) - goto error; + goto cleanup; /* Set the code, ID and length. */ pkt->pkt.length = attrset_len + OFFSET_ATTR; pkt_code_set(pkt, code); pkt_len_set(pkt, pkt->pkt.length); + if (msgauth_required) { + /* Calculate and set actual Message-Authenticator. */ + retval = calculate_mac(secret, pkt, pkt_auth(pkt), pkt_attr(pkt) + 2); + if (retval != 0) + goto cleanup; + } + /* Copy the attrset for future use. */ retval = packet_set_attrset(ctx, secret, pkt); if (retval != 0) - goto error; + goto cleanup; *request = pkt; - return 0; + pkt = NULL; -error: - free(pkt); +cleanup: + krad_packet_free(pkt); return retval; } @@ -305,18 +427,22 @@ krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code, krad_packet **response) { krb5_error_code retval; - krad_packet *pkt; + krad_packet *pkt = NULL; size_t attrset_len; + krb5_boolean msgauth_required; pkt = packet_new(); if (pkt == NULL) return ENOMEM; + /* Determine if Message-Authenticator is required. */ + msgauth_required = requires_msgauth(secret, code); + /* Encode the attributes. */ - retval = kr_attrset_encode(set, secret, pkt_auth(request), pkt_attr(pkt), - &attrset_len); + retval = kr_attrset_encode(set, secret, pkt_auth(request), + msgauth_required, pkt_attr(pkt), &attrset_len); if (retval != 0) - goto error; + goto cleanup; /* Set the code, ID and length. */ pkt->pkt.length = attrset_len + OFFSET_ATTR; @@ -328,21 +454,66 @@ krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code, retval = auth_generate_response(ctx, secret, pkt, pkt_auth(request), pkt_auth(pkt)); if (retval != 0) - goto error; + goto cleanup; /* Copy the attrset for future use. */ retval = packet_set_attrset(ctx, secret, pkt); if (retval != 0) - goto error; + goto cleanup; + + if (msgauth_required) { + /* + * Calculate and set actual Message-Authenticator. Use authenticator + * from the request, not from the response. + * + * RFC2869 (section 5.14): + * For Access-Challenge, Access-Accept, and Access-Reject packets, + * the Message-Authenticator is calculated [...] using the + * Request-Authenticator from the Access-Request this packet is in + * reply to. + */ + retval = calculate_mac(secret, pkt, pkt_auth(request), + pkt_attr(pkt) + 2); + if (retval != 0) + goto cleanup; + } *response = pkt; - return 0; + pkt = NULL; -error: - free(pkt); +cleanup: + krad_packet_free(pkt); return retval; } +/* Verify the Message-Authenticator value in pkt, using the provided + * authenticator (which may be from pkt or from a corresponding request). */ +static krb5_error_code +verify_msgauth(const char *secret, const krad_packet *pkt, + const uint8_t auth[AUTH_FIELD_SIZE]) +{ + uint8_t mac[MD5_DIGEST_SIZE]; + krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator"); + const krb5_data *msgauth; + krb5_error_code retval; + + msgauth = krad_packet_get_attr(pkt, msgauth_type, 0); + if (msgauth == NULL) + return ENODATA; + + retval = calculate_mac(secret, pkt, auth, mac); + if (retval) + return retval; + + if (msgauth->length != MD5_DIGEST_SIZE) + return EMSGSIZE; + + if (k5_bcmp(mac, msgauth->data, MD5_DIGEST_SIZE) != 0) + return EBADMSG; + + return 0; +} + /* Decode a packet. */ static krb5_error_code decode_packet(krb5_context ctx, const char *secret, const krb5_data *buffer, @@ -397,18 +568,30 @@ krad_packet_decode_request(krb5_context ctx, const char *secret, krb5_error_code retval; retval = decode_packet(ctx, secret, buffer, reqpkt); - if (cb != NULL && retval == 0) { - for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) { + if (retval) + return retval; + + /* Verify Message-Authenticator if present. */ + if (has_pkt_msgauth(*reqpkt)) { + retval = verify_msgauth(secret, *reqpkt, pkt_auth(*reqpkt)); + if (retval) + return retval; + } else if (requires_msgauth(secret, pkt_code_get(*reqpkt))) { + return ENODATA; + } + + if (cb != NULL) { + while ((tmp = (*cb)(data, FALSE)) != NULL) { if (pkt_id_get(*reqpkt) == pkt_id_get(tmp)) break; } - } - if (cb != NULL && (retval != 0 || tmp != NULL)) - (*cb)(data, TRUE); + if (tmp != NULL) + (*cb)(data, TRUE); + } *duppkt = tmp; - return retval; + return 0; } krb5_error_code @@ -418,33 +601,52 @@ krad_packet_decode_response(krb5_context ctx, const char *secret, krad_packet **rsppkt) { uchar auth[AUTH_FIELD_SIZE]; - const krad_packet *tmp = NULL; + const krad_packet *req = NULL; + krad_packet *rsp = NULL; krb5_error_code retval; - retval = decode_packet(ctx, secret, buffer, rsppkt); - if (cb != NULL && retval == 0) { - for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) { - if (pkt_id_get(*rsppkt) != pkt_id_get(tmp)) + retval = decode_packet(ctx, secret, buffer, &rsp); + if (retval != 0) + goto cleanup; + + if (cb != NULL) { + while ((req = (*cb)(data, FALSE)) != NULL) { + if (pkt_id_get(rsp) != pkt_id_get(req)) continue; /* Response */ - retval = auth_generate_response(ctx, secret, *rsppkt, - pkt_auth(tmp), auth); - if (retval != 0) { - krad_packet_free(*rsppkt); - break; - } + retval = auth_generate_response(ctx, secret, rsp, pkt_auth(req), + auth); + if (retval != 0) + goto cleanup; /* If the authenticator matches, then the response is valid. */ - if (memcmp(pkt_auth(*rsppkt), auth, sizeof(auth)) == 0) - break; + if (memcmp(pkt_auth(rsp), auth, sizeof(auth)) != 0) + continue; + + /* Verify Message-Authenticator if present. */ + if (has_pkt_msgauth(rsp)) { + retval = verify_msgauth(secret, rsp, pkt_auth(req)); + if (retval != 0) + goto cleanup; + } else if (requires_msgauth(secret, pkt_code_get(rsp))) { + retval = ENODATA; + goto cleanup; + } + + break; } } - if (cb != NULL && (retval != 0 || tmp != NULL)) + *reqpkt = req; + *rsppkt = rsp; + +cleanup: + if (cb != NULL && (retval != 0 || req != NULL)) (*cb)(data, TRUE); + if (retval != 0) + krad_packet_free(rsp); - *reqpkt = tmp; return retval; } diff --git a/src/lib/krad/t_daemon.py b/src/lib/krad/t_daemon.py index 4a3de079c7d..647d4894eb8 100755 --- a/src/lib/krad/t_daemon.py +++ b/src/lib/krad/t_daemon.py @@ -40,6 +40,7 @@ ATTRIBUTE\tUser-Name\t1\tstring ATTRIBUTE\tUser-Password\t2\toctets ATTRIBUTE\tNAS-Identifier\t32\tstring +ATTRIBUTE\tMessage-Authenticator\t80\toctets """ class TestServer(server.Server): @@ -52,7 +53,7 @@ def _HandleAuthPacket(self, pkt): if key == "User-Password": passwd = [pkt.PwDecrypt(x) for x in pkt[key]] - reply = self.CreateReplyPacket(pkt) + reply = self.CreateReplyPacket(pkt, message_authenticator=True) if passwd == ['accept']: reply.code = packet.AccessAccept else: diff --git a/src/tests/t_otp.py b/src/tests/t_otp.py index c3b820a411d..dd5cdc5c26d 100755 --- a/src/tests/t_otp.py +++ b/src/tests/t_otp.py @@ -49,6 +49,7 @@ ATTRIBUTE User-Password 2 octets ATTRIBUTE Service-Type 6 integer ATTRIBUTE NAS-Identifier 32 string +ATTRIBUTE Message-Authenticator 80 octets ''' class RadiusDaemon(Process): @@ -97,6 +98,8 @@ def run(self): reply.code = packet.AccessReject replyq['reply'] = False + reply.add_message_authenticator() + outq.put(replyq) if addr is None: sock.send(reply.ReplyPacket())