This SA breaks mpd5 with MS-CHAPv2. No workaround available but to replace libradius* with pre-SA version. Setup: if there is a dial in server using * mpd5 * external radius server in different jail (freeradius3) * MS-CHAPv2 for authentication (done by freeradius3) authentication succeeds, but mpd5 disconnects immediately because of alleged missing MS-CHAP2-Success attributes. Logging of mpd5 shows: mpd[10012]: [L_l2tp] RADIUS: Authenticating user 'username' mpd[10012]: [L_l2tp] RADIUS: Rec'd RAD_ACCESS_ACCEPT for user 'username' mpd[10012]: [L_l2tp] RADIUS: PANIC no MS-CHAP2-Success received from server! Checking this at freeradius3 server and packet capture show that the attribute indeed exists but seems to be ignored by mpd5/libradius. Replacing libradius on log in server with pre-SA version makes mpd5 work again: mpd[96202]: [L_l2tp] RADIUS: Authenticating user 'user' mpd[96202]: [L_l2tp] RADIUS: Rec'd RAD_ACCESS_ACCEPT for user 'user' mpd[96202]: [L_l2tp] AUTH: RADIUS returned: authenticated mpd[96202]: [L_l2tp] CHAP: Auth return status: authenticated mpd[96202]: [L_l2tp] CHAP: Reply message: S=XXXXXXXX mpd[96202]: [L_l2tp] CHAP: sending SUCCESS #1 len: 46 I haven't found out which part of fix is to be blamed but this situation is rather unpleasant (especially since mpd5 is the main application of libradius).
The CVE fix broke the following function: int rad_get_attr(struct rad_handle *h, const void **value, size_t *lenp) { int len, type; if (h->in_pos >= h->in_len) return 0; if (h->in_pos + 2 > h->in_len) { generr(h, "Malformed attribute in response"); return -1; } type = h->in[h->in_pos++]; len = h->in[h->in_pos++]; if (len < 2 || h->in_pos + len > h->in_len) { generr(h, "Malformed attribute in response"); return -1; } *lenp = len; *value = &h->in[h->in_pos]; h->in_pos += len; return type; } The failure occurs after len = h->in[h->in_pos++]; This len is the total length of the attribute, **including** the 2 byte header. All lines below assume that len excludes the header, so lenp is 2 byte too long and h->in_pos is shifted 2 bytes too far. When you call rad_get_attr() for the first time, the returned data is just 2 bytes to long (unless there is only 1 attribute, in which case you get a "Malformed attribute" error because of the "missing" 2 extra bytes). But: as h->in_pos is located 2 bytes within the second attribute on return, all subsequent calls to rad_get_attr() will return garbage. A possible fix is: int rad_get_attr(struct rad_handle *h, const void **value, size_t *lenp) { int len, type; if (h->in_pos >= h->in_len) return 0; if (h->in_pos + 2 > h->in_len) { generr(h, "Malformed attribute in response"); return -1; } type = h->in[h->in_pos]; len = h->in[h->in_pos + 1]; if (len < 2 || h->in_pos + len > h->in_len) { generr(h, "Malformed attribute in response"); return -1; } *lenp = len - 2; *value = &h->in[h->in_pos + 2]; h->in_pos += len; return type; } The missing piece is: how to properly distribute this fix of the broken CVE fix?
We've patched the tree to fix this already, see https://cgit.freebsd.org/src/commit/?id=6bb5699d2b59491097bc21ffa3c097cdd4853f89 We are working on releasing an EN to update affected hosts. I apologize for the breakage.
A patch has been published as FreeBSD-EN-21:17.libradius.
Thank you very much for the quick response and the swift fix. It's really nice to see how fast such problems are solved in this community.