diff options
Diffstat (limited to 'src/lib/radius.c')
-rw-r--r-- | src/lib/radius.c | 154 |
1 files changed, 102 insertions, 52 deletions
diff --git a/src/lib/radius.c b/src/lib/radius.c index b2de15b..a19c9a4 100644 --- a/src/lib/radius.c +++ b/src/lib/radius.c @@ -145,8 +145,9 @@ char const *fr_packet_codes[FR_MAX_PACKET_CODE] = { "47", "48", "49", - "IP-Address-Allocate", - "IP-Address-Release", //!< 50 + "IP-Address-Allocate", //!< 50 + "IP-Address-Release", + "Protocol-Error", }; @@ -536,7 +537,7 @@ static void make_secret(uint8_t *digest, uint8_t const *vector, fr_md5_destroy(&context); } -#define MAX_PASS_LEN (128) +#define MAX_PASS_LEN (256) static void make_passwd(uint8_t *output, ssize_t *outlen, uint8_t const *input, size_t inlen, char const *secret, uint8_t const *vector) @@ -1819,6 +1820,14 @@ int rad_vp2attr(RADIUS_PACKET const *packet, RADIUS_PACKET const *original, return rad_vp2vsa(packet, original, secret, pvp, start, room); } +static const bool code2ma[FR_MAX_PACKET_CODE] = { + [ PW_CODE_ACCESS_REQUEST ] = true, + [ PW_CODE_ACCESS_ACCEPT ] = true, + [ PW_CODE_ACCESS_REJECT ] = true, + [ PW_CODE_ACCESS_CHALLENGE ] = true, + [ PW_CODE_STATUS_SERVER ] = true, + [ PW_CODE_PROTOCOL_ERROR ] = true, +}; /** Encode a packet * @@ -1831,6 +1840,7 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, uint16_t total_length; int len; VALUE_PAIR const *reply; + bool seen_ma = false; /* * A 4K packet, aligned on 64-bits. @@ -1883,6 +1893,12 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, id = htonl(id); memcpy(hdr->vector, &id, sizeof(id)); memset(hdr->vector + sizeof(id), 0, sizeof(hdr->vector) - sizeof(id)); + + /* + * We don't encode Message-Authenticator + */ + seen_ma = true; + packet->offset = -1; } else #endif { @@ -1909,6 +1925,27 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, */ /* + * Always add Message-Authenticator for replies to + * Access-Request packets, and for all Access-Accept, + * Access-Reject, Access-Challenge. + * + * It must be the FIRST attribute in the packet. + */ + if (!packet->tls && + ((code2ma[packet->code]) || (original && code2ma[original->code]))) { + seen_ma = true; + + packet->offset = RADIUS_HDR_LEN; + + ptr[0] = PW_MESSAGE_AUTHENTICATOR; + ptr[1] = 18; + memset(ptr + 2, 0, 16); + + ptr += 18; + total_length += 18; + } + + /* * Loop over the reply attributes for the packet. */ reply = packet->vps; @@ -1943,18 +1980,9 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, #ifdef WITH_RADIUSV11 /* - * Do not encode Message-Authenticator for RADIUS/1.1 - */ - if ((reply->da->vendor == 0) && (reply->da->attr == PW_MESSAGE_AUTHENTICATOR)) { - reply = reply->next; - continue; - } - - - /* * Do not encode Original-Packet-Code for RADIUS/1.1 */ - if (reply->da->vendor == ((unsigned int) PW_EXTENDED_ATTRIBUTE_1 << 24) && (reply->da->attr == 4)) { + if (packet->radiusv11 && reply->da->vendor == ((unsigned int) PW_EXTENDED_ATTRIBUTE_1 << 24) && (reply->da->attr == 4)) { reply = reply->next; continue; } @@ -1984,15 +2012,13 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, * length and initial value. */ if (!reply->da->vendor && (reply->da->attr == PW_MESSAGE_AUTHENTICATOR)) { -#ifdef WITH_RADIUSV11 /* - * RADIUSV11 does not encode or verify Message-Authenticator. + * We have already encoded the Message-Authenticator, don't do it again. */ - if (packet->radiusv11) { + if (seen_ma) { reply = reply->next; continue; } -#endif if (room < 18) break; @@ -2152,11 +2178,7 @@ int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original, case PW_CODE_ACCOUNTING_REQUEST: case PW_CODE_DISCONNECT_REQUEST: - case PW_CODE_DISCONNECT_ACK: - case PW_CODE_DISCONNECT_NAK: case PW_CODE_COA_REQUEST: - case PW_CODE_COA_ACK: - case PW_CODE_COA_NAK: memset(hdr->vector, 0, AUTH_VECTOR_LEN); break; @@ -2164,6 +2186,10 @@ int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original, case PW_CODE_ACCESS_ACCEPT: case PW_CODE_ACCESS_REJECT: case PW_CODE_ACCESS_CHALLENGE: + case PW_CODE_DISCONNECT_ACK: + case PW_CODE_DISCONNECT_NAK: + case PW_CODE_COA_ACK: + case PW_CODE_COA_NAK: memcpy(hdr->vector, original->vector, AUTH_VECTOR_LEN); break; @@ -2510,6 +2536,8 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) char host_ipaddr[128]; #ifndef WITH_RADIUSV11_ONLY bool require_ma = false; + bool limit_proxy_state = false; + bool seen_proxy_state = false; bool seen_ma = false; bool eap = false; bool non_eap = false; @@ -2559,15 +2587,23 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) } /* - * Message-Authenticator is required in Status-Server - * packets, otherwise they can be trivially forged. + * If the caller requires Message-Authenticator, then set + * the flag. + * + * We also require Message-Authenticator if the packet + * code is Status-Server. + * + * If we're receiving packets from a proxy socket, then + * require Message-Authenticator for Access-* replies, + * and for Protocol-Error. */ - if (hdr->code == PW_CODE_STATUS_SERVER) require_ma = true; + require_ma = ((flags & 0x01) != 0) || (hdr->code == PW_CODE_STATUS_SERVER) || (((flags & 0x08) != 0) && code2ma[hdr->code]); /* - * It's also required if the caller asks for it. + * We only limit Proxy-State if we're not requiring + * Message-Authenticator. */ - if (flags) require_ma = true; + limit_proxy_state = ((flags & 0x04) != 0) && !require_ma; /* * Repeat the length checks. This time, instead of @@ -2723,6 +2759,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) case PW_EAP_MESSAGE: require_ma = true; eap = true; + packet->eap_message = true; break; case PW_USER_PASSWORD: @@ -2731,6 +2768,11 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) non_eap = true; break; + case PW_PROXY_STATE: + seen_proxy_state = true; + packet->proxy_state = true; + break; + case PW_MESSAGE_AUTHENTICATOR: #ifdef WITH_RADIUSV11 /* @@ -2749,6 +2791,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) goto finish; } seen_ma = true; + packet->message_authenticator = true; break; } #endif @@ -2813,7 +2856,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) !packet->radiusv11 && #endif !seen_ma) { - FR_DEBUG_STRERROR_PRINTF("Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute", + FR_DEBUG_STRERROR_PRINTF("Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute. You may need to set \"require_message_authenticator = no\" in the configuration.", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr))); @@ -2822,6 +2865,18 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) } #ifndef WITH_RADIUSV11_ONLY + /* + * The client is a NAS which shouldn't send Proxy-State, but it did! + */ + if (limit_proxy_state && seen_proxy_state && !seen_ma) { + FR_DEBUG_STRERROR_PRINTF("Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute, but still has one or more Proxy-State attributes", + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + host_ipaddr, sizeof(host_ipaddr))); + failure = DECODE_FAIL_MA_MISSING; + goto finish; + } + if (eap && non_eap) { FR_DEBUG_STRERROR_PRINTF("Bad packet from host %s: Packet contains EAP-Message and non-EAP authentication attribute", inet_ntop(packet->src_ipaddr.af, @@ -3938,7 +3993,7 @@ ssize_t data2vp(TALLOC_CTX *ctx, VALUE_PAIR *vp; uint8_t const *data = start; char *p; - uint8_t buffer[256]; + uint8_t buffer[MAX_PASS_LEN]; /* * FIXME: Attrlen can be larger than 253 for extended attrs! @@ -4054,7 +4109,7 @@ ssize_t data2vp(TALLOC_CTX *ctx, attrlen, secret, packet->vector); } - buffer[253] = '\0'; + buffer[attrlen] = '\0'; /* * MS-CHAP-MPPE-Keys are 24 octets, and @@ -4654,22 +4709,24 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, ssize_t my_len; #ifdef WITH_RADIUSV11 - /* - * Don't decode Message-Authenticator - */ - if (ptr[0] == PW_MESSAGE_AUTHENTICATOR) { - packet_length -= ptr[1]; - ptr += ptr[1]; - continue; - } + if (packet->radiusv11) { + /* + * Don't decode Message-Authenticator + */ + if (ptr[0] == PW_MESSAGE_AUTHENTICATOR) { + packet_length -= ptr[1]; + ptr += ptr[1]; + continue; + } - /* - * Don't decode Original-Packet-Code - */ - if ((ptr[0] == PW_EXTENDED_ATTRIBUTE_1) && (ptr[1] >= 3) && (ptr[2] == 4)) { - packet_length -= ptr[1]; - ptr += ptr[1]; - continue; + /* + * Don't decode Original-Packet-Code + */ + if ((ptr[0] == PW_EXTENDED_ATTRIBUTE_1) && (ptr[1] >= 3) && (ptr[2] == 4)) { + packet_length -= ptr[1]; + ptr += ptr[1]; + continue; + } } #endif @@ -4761,7 +4818,7 @@ int rad_pwencode(char *passwd, size_t *pwlen, char const *secret, */ len = *pwlen; - if (len > 128) len = 128; + if (len > MAX_STRING_LEN) len = MAX_STRING_LEN; if (len == 0) { memset(passwd, 0, AUTH_PASS_LEN); @@ -4821,13 +4878,6 @@ int rad_pwdecode(char *passwd, size_t pwlen, char const *secret, size_t n, secretlen; /* - * The RFC's say that the maximum is 128. - * The buffer we're putting it into above is 254, so - * we don't need to do any length checking. - */ - if (pwlen > 128) pwlen = 128; - - /* * Catch idiots. */ if (pwlen == 0) goto done; |