radius: the reply hashed with the secret, and taken only from the server asked

The reply digest copied the shared secret right after the received
bytes, so a reply of 4100 bytes with a secret of 17 or more characters
wrote up to 47 bytes of it past the reply buffer on the stack, before
the reply was authenticated. The digest is now taken over the reply
and the secret in place, with nothing written past the reply, and it
fails closed when the hash cannot be made. A reply is also taken only
from the address and port of the server the request went to, and only
when its identifier and length field match the request, all before any
digest work; the reply's sender was never checked before.
This commit is contained in:
Vladimir Dubrovin 2026-10-30 13:47:12 +03:00
parent e9714fa4df
commit a75b488aaf

View File

@ -198,12 +198,20 @@ static int calc_replydigest(char *packet, char *original, const char *secret, in
int secretlen;
uint8_t calc_digest[AUTH_VECTOR_LEN];
uint8_t calc_vector[AUTH_VECTOR_LEN];
unsigned int dlen = AUTH_VECTOR_LEN;
mdh_ctx *ctx;
memcpy(calc_vector, packet + 4, AUTH_VECTOR_LEN);
memcpy(packet + 4, original, AUTH_VECTOR_LEN);
secretlen = strlen(secret);
memcpy(packet + len, secret, secretlen);
md5_calc(calc_digest, (u_char *)packet, len + secretlen);
/* hash the reply, then the secret: nothing is written past the reply */
if(!(ctx = mdh_init(MDH_MD5, 16))) return 2;
if(!mdh_update(ctx, packet, len) || !mdh_update(ctx, secret, secretlen) ||
!mdh_final(ctx, calc_digest, &dlen)) {
mdh_free(ctx);
return 2;
}
mdh_free(ctx);
/*
* Return 0 if OK, 2 if not OK.
@ -298,6 +306,7 @@ int radsend(struct clientparam * param, int auth, int stop){
int total_length;
int len;
PROXYSOCKADDRTYPE saremote;
PROXYSOCKADDRTYPE safrom;
struct pollfd fds[1];
char vector[AUTH_VECTOR_LEN];
radius_packet_t packet, rpacket;
@ -568,15 +577,25 @@ int radsend(struct clientparam * param, int auth, int stop){
continue;
}
salen = sizeof(saremote);
salen = sizeof(safrom);
data_len = so._recvfrom(so.state, remsock, (char *)&rpacket, sizeof(packet)-16,
0, (struct sockaddr *)&saremote, &salen);
0, (struct sockaddr *)&safrom, &salen);
if (data_len < 20) {
continue;
}
/* the reply must come from the server we asked */
if(*SAFAMILY(&safrom) != *SAFAMILY(&saremote) ||
memcmp(SAADDR(&safrom), SAADDR(&saremote), SAADDRLEN(&saremote)) ||
*SAPORT(&safrom) != *SAPORT(&saremote)) {
continue;
}
/* and answer this request, and be of its own length */
if(data_len != ntohs(rpacket.length) || rpacket.id != packet.id) {
continue;
}
if( auth && rpacket.code != PW_AUTHENTICATION_ACK &&
rpacket.code != PW_AUTHENTICATION_REJECT ){