diff mbox series

[meta-networking,kirkstone] freeradius: Security fix for CVE-2024-3596

Message ID 20241015065759.26148-1-rsangam@mvista.com
State New
Headers show
Series [meta-networking,kirkstone] freeradius: Security fix for CVE-2024-3596 | expand

Commit Message

Rohini Sangam Oct. 15, 2024, 6:57 a.m. UTC
CVE fixed:
- CVE-2024-3596 freeradius: forgery attack
Upstream-Status: Backport from v3.0.x branch, commit range 3a00a6ecc188629b0441fd45ad61ca8986de156e..da643f1edc267ce95260dc36069e6f1a7a4d66f8

Signed-off-by: Rohini Sangam <rsangam@mvista.com>
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
---
 .../freeradius/files/CVE-2024-3596.patch      | 1506 +++++++++++++++++
 .../freeradius/freeradius_3.0.21.bb           |    1 +
 2 files changed, 1507 insertions(+)
 create mode 100644 meta-networking/recipes-connectivity/freeradius/files/CVE-2024-3596.patch
diff mbox series

Patch

diff --git a/meta-networking/recipes-connectivity/freeradius/files/CVE-2024-3596.patch b/meta-networking/recipes-connectivity/freeradius/files/CVE-2024-3596.patch
new file mode 100644
index 000000000..1778e8e92
--- /dev/null
+++ b/meta-networking/recipes-connectivity/freeradius/files/CVE-2024-3596.patch
@@ -0,0 +1,1506 @@ 
+From 441967ba1d1ec28aa9582ab0253ad01e14b42148 Mon Sep 17 00:00:00 2001
+From: Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+Date: Sun, 30 Jun 2024 14:03:17 -0600
+Subject: [PATCH] CVE-2024-3596: Backport fix for BlastRADIUS
+
+Upstream-Status: Backport from v3.0.x branch, commit range 3a00a6ecc188629b0441fd45ad61ca8986de156e..da643f1edc267ce95260dc36069e6f1a7a4d66f8
+CVE: CVE-2024-3596
+
+Signed-off-by: Rohini Sangam <rsangam@mvista.com>
+---
+ man/man1/radclient.1    |  10 ++-
+ man/man1/radtest.1      |  11 ++-
+ raddb/clients.conf      |  47 ++++++++--
+ raddb/proxy.conf        |  19 +++++
+ raddb/radiusd.conf.in   | 185 ++++++++++++++++++++++++++++++++++++++++
+ src/include/clients.h   |   6 +-
+ src/include/conffile.h  |   1 +
+ src/include/libradius.h |  19 ++++-
+ src/include/radius.h    |   1 +
+ src/include/radiusd.h   |   6 ++
+ src/include/realms.h    |   1 +
+ src/lib/radius.c        |  87 +++++++++++++++++--
+ src/main/client.c       |  45 ++++++++--
+ src/main/conffile.c     |   4 +-
+ src/main/listen.c       | 141 +++++++++++++++++++++++++++++-
+ src/main/mainconfig.c   |  70 +++++++++++++++
+ src/main/process.c      |  65 ++++++++++++++
+ src/main/radclient.c    | 147 ++++++++++++++++++++++++++++++-
+ src/main/radtest.in     |   6 +-
+ src/main/realms.c       |  11 +++
+ src/main/tls_listen.c   |   5 ++
+ 21 files changed, 855 insertions(+), 32 deletions(-)
+
+diff --git a/man/man1/radclient.1 b/man/man1/radclient.1
+index 229dcae0c7..b83bee931a 100644
+--- a/man/man1/radclient.1
++++ b/man/man1/radclient.1
+@@ -1,10 +1,11 @@
+-.TH RADCLIENT 1 "22 March 2019" "" "FreeRADIUS Daemon"
++.TH RADCLIENT 1 "21 May 2024" "" "FreeRADIUS Daemon"
+ .SH NAME
+ radclient - send packets to a RADIUS server, show reply
+ .SH SYNOPSIS
+ .B radclient
+ .RB [ \-4 ]
+ .RB [ \-6 ]
++.RB [ \-b ]
+ .RB [ \-c
+ .IR count ]
+ .RB [ \-d
+@@ -52,6 +53,13 @@ automatically encrypted before the packet is sent to the server.
+ Use IPv4 (default)
+ .IP \-6
+ Use IPv6
++.IP \-b
++Enforce the Blast RADIUS checks.  All replies to an Access-Request packet
++must contain a Message-Authenticator as the first attribute.
++
++For compatibility with old servers, this flag is not set by default.
++However, radclient still checks for the Blast RADIUS signature, and
++discards packets which match the attack.
+ .IP \-c\ \fIcount\fP
+ Send each packet \fIcount\fP times.
+ .IP \-d\ \fIraddb_directory\fP
+diff --git a/man/man1/radtest.1 b/man/man1/radtest.1
+index b3184779c0..6bfab75944 100644
+--- a/man/man1/radtest.1
++++ b/man/man1/radtest.1
+@@ -1,4 +1,4 @@
+-.TH RADTEST 1 "5 April 2010" "" "FreeRADIUS Daemon"
++.TH RADTEST 1 "21 May 2024" "" "FreeRADIUS Daemon"
+ .SH NAME
+ radtest - send packets to a RADIUS server, show reply
+ .SH SYNOPSIS
+@@ -15,6 +15,8 @@ radtest - send packets to a RADIUS server, show reply
+ .IR ]
+ .RB [ \-6
+ .IR ]
++.RB [ \-b
++.IR
+ .I user password radius-server nas-port-number secret
+ .RB [ ppphint ]
+ .RB [ nasname ]
+@@ -26,6 +28,13 @@ way to test a radius server.
+ 
+ .SH OPTIONS
+ 
++.IP \-b
++Enforce the Blast RADIUS checks.  All replies to an Access-Request packet
++must contain a Message-Authenticator as the first attribute.
++
++For compatibility with old servers, this flag is not set by default.
++However, radclient still checks for the Blast RADIUS signature, and
++discards packets which match the attack.
+ .IP "\-d \fIraddb_directory\fP"
+ The directory that contains the RADIUS dictionary files. Defaults to
+ \fI/etc/raddb\fP.
+diff --git a/raddb/clients.conf b/raddb/clients.conf
+index 76b300d3c5..d55414b7d2 100644
+--- a/raddb/clients.conf
++++ b/raddb/clients.conf
+@@ -100,15 +100,44 @@ client localhost {
+ 	secret = testing123
+ 
+ 	#
+-	#  Old-style clients do not send a Message-Authenticator
+-	#  in an Access-Request.  RFC 5080 suggests that all clients
+-	#  SHOULD include it in an Access-Request.  The configuration
+-	#  item below allows the server to require it.  If a client
+-	#  is required to include a Message-Authenticator and it does
+-	#  not, then the packet will be silently discarded.
+-	#
+-	#  allowed values: yes, no
+-	require_message_authenticator = no
++	#  The global configuration "security.require_message_authenticator"
++	#  flag sets the default for all clients.  That default can be
++	#  over-ridden here, by setting it to a value.  If no value is set,
++	#  then the default from the "radiusd.conf" file is used.
++	#
++	#  See that file for full documentation on the flag, along
++	#  with allowed values and meanings.
++	#
++	#  This flag exists solely for legacy clients which do not send
++	#  Message-Authenticator in all Access-Request packets.  We do not
++	#  recommend setting it to "no".
++	#
++	#  The number one way to protect yourself from the BlastRADIUS
++	#  attack is to update all RADIUS servers, and then set this
++	#  flag to "yes".  If all RADIUS servers are updated, and if
++	#  all of them have this flag set to "yes" for all clients,
++	#  then your network is safe.  You can then upgrade the
++	#  clients when it is convenient, instead of rushing the
++	#  upgrades.
++	#
++	#  allowed values: yes, no, auto
++#	require_message_authenticator = no
++
++	#
++	#  The global configuration "security.limit_proxy_state"
++	#  flag sets the default for all clients.  That default can be
++	#  over-ridden here, by setting it to "no".
++	#
++	#  See that file for full documentation on the flag, along
++	#  with allowed values,and meanings.
++	#
++	#  This flag exists solely for legacy clients which do not send
++	#  Message-Authenticator in all Access-Request packets.  We do not
++	#  recommend setting it to "no".
++	#
++	#  allowed values: yes, no, auto
++	#
++#	limit_proxy_state = yes
+ 
+ 	#
+ 	#  The short name is used as an alias for the fully qualified
+diff --git a/raddb/proxy.conf b/raddb/proxy.conf
+index 91b4b37930..fa362b8a74 100644
+--- a/raddb/proxy.conf
++++ b/raddb/proxy.conf
+@@ -204,6 +204,25 @@ home_server localhost {
+ 	#
+ 	secret = testing123
+ 
++
++	#
++	#  The global configuration "security.require_message_authenticator"
++	#  flag sets the default for all home servers.  That default can be
++	#  over-ridden here, by setting it to a value.  If no value is set,
++	#  then the default from the "radiusd.conf" file is used.
++	#
++	#  See that file for full documentation on the flag, along
++	#  with allowed values and meanings.
++	#
++	#  This flag exists solely for legacy home servers which do
++	#  not send Message-Authenticator in all Access-Accept,
++	#  Access-Reject, or Access-Challenge packets.  We do not
++	#  recommend setting it to "no".
++	#
++	#  allowed values: yes, no, auto
++	#
++#	require_message_authenticator = no
++
+ 	############################################################
+ 	#
+ 	#  The rest of the configuration items listed here are optional,
+diff --git a/raddb/radiusd.conf.in b/raddb/radiusd.conf.in
+index e8aee3c001..5b8800bfc8 100644
+--- a/raddb/radiusd.conf.in
++++ b/raddb/radiusd.conf.in
+@@ -564,6 +564,191 @@ security {
+ 	#
+ 	status_server = yes
+ 
++	#
++	#  Global configuration for requiring Message-Authenticator in
++	#  all Access-* packets sent over UDP or TCP.  This flag is
++	#  ignored for TLS.
++	#
++	#  The number one way to protect yourself from the BlastRADIUS
++	#  attack is to update all RADIUS servers, and then set this
++	#  flag to "yes".  If all RADIUS servers are updated, and if
++	#  all of them have this flag set to "yes" for all clients,
++	#  then your network is safe.  You can then upgrade the
++	#  clients when it is convenient, instead of rushing the
++	#  upgrades.
++	#
++	#  This flag sets the global default for all clients and home
++	#  servers.  It can be over-ridden in an individual client or
++	#  home_server definition by adding the same flag to that
++	#  section with an appropriate value.
++	#
++	#  All upgraded RADIUS implementations should send
++	#  Message-Authenticator in all Access-Request, Access-Accept,
++	#  Access-Reject, and Access-Challenge packets.  Once all
++	#  systems are upgraded, setting this flag to "yes" is the
++	#  best protection from the attack.
++	#
++	#  The possible values and meanings for
++	#  "require_message_authenticator" are;
++	#
++	#  * "no" - allow Access-* packet which do not contain
++	#    Message-Authenticator
++	#
++	#    For a client, if this flag is set to "no", then the
++	#    "limit_proxy_state" flag, below, is also checked.
++	#
++	#    For a home_server, if this flag is set to "no", then the
++	#    Access-Accept, Access-Reject, and Access-Challenge
++	#    packets do not need to contain Message-Authenticator.
++	#
++	#    The only reason to set this flag to "no" is when the
++	#    RADIUS client or home server has not been updated.  It is
++	#    always safer to set this flag "no" in the individual
++	#    client or home_server definition.  The global flag SHOULD
++	#    still be set to a safe value: "yes".
++	#
++	#    WARNING: Setting this flag and the "limit_proxy_state"
++	#    flag to "no" will allow MITM attackers to create fake
++	#    Access-Accept packets to the NAS!  At least one of them
++	#    MUST be set to "yes" for the system to have any
++	#    protection against the attack.
++	#
++	#  * "yes" - Require that all Access-* packets (client and
++	#    home_server) contain Message-Authenticator.  If a packet
++	#    does not contain Message-Authenticator, then it is
++	#    discarded.
++	#
++	#  * "auto" - Automatically determine the value of the flag,
++	#    based on the first packet received from that client or
++	#    home_server.
++	#
++	#    If the packet does not contain Message-Authenticator,
++	#    then the value of the flag is automatically switched to
++	#    "no".
++	#
++	#    If the packet contains Message-Authenticator but not
++	#    EAP-Message, then the value of the flag is automatically
++	#    switched to "yes".  The server has to check for
++	#    EAP-Message, because the previous RFCs require that the
++	#    packet contains Message-Authenticator when it also
++	#    contains EAP-Message.  So having a Message-Authenticator
++	#    in those packets doesn't give the server enough
++	#    information to determined if the client or home_server
++	#    has been updated.
++	#
++	#    If the packet contains Message-Authenticator and
++	#    EAP-Message, then the flag is left at the "auto" value.
++	#
++	#    WARNING: This switch is done for the first packet
++	#    received from that client or home server.  The change
++	#    does NOT persist across server restarts.  You MUST change
++	#    the to "yes" manually, in order to make a permanent
++	#    change to the configuration.
++	#
++	#    WARNING: If there are multiple NASes with the same source
++	#    IP and client definitions, BUT the NASes have different
++	#    behavior, then this flag WILL LIKELY BREAK YOUR NETWORK.
++	#
++	#    That is, when there are multiple different RADIUS clients
++	#    behind one NATed IP address, then these security settings
++	#    have to be set to allow the MOST INSECURE packets to be
++	#    processed.  This is a terrible idea, and will leave your
++	#    network vulnerable to the attack.  Please upgrade all
++	#    clients immediately.
++	#
++	#    The only solution to that rare configuration is to set
++	#    this flag to "no", in which case the network will work,
++	#    but will be vulnerable to the attack.
++	#
++	require_message_authenticator = auto
++
++	#
++	#  Global configuration for limiting the combination of
++	#  Proxy-State and Message-Authenticator.  This flag only
++	#  applies to packets sent over UDP or TCP.  This flag is
++	#  ignored for TLS.
++	#
++	#  This flag sets the global default for all clients.  It can
++	#  be over-ridden in an individual client definition by adding
++	#  the same flag to that section with an appropriate value.
++	#
++	#  If "require_message_authenticator" is set to "yes", this
++	#  configuration item is ignored.
++	#
++	#  If "require_message_authenticator" is set to "no", this
++	#  configuration item is checked.
++	#
++	#  The possible values and meanings for "limit_proxy_state" are;
++	#
++	#  * "no" - allow any packets from the client, even packets
++	#    which contain the BlastRADIUS attack.  Please be aware
++	#    that in this configuration the server will complain for
++	#    EVERY packet which it receives.
++	#
++	#    The only reason to set this flag to "no" is when the
++	#    client is a proxy, AND the proxy does not send
++	#    Message-Authenticator in Access-Request packets.  Even
++	#    then, the best approach to fix the issue is to (1) update
++	#    the proxy to send Message-Authenticator, and if that
++	#    can't be done, then (2) set this flag to "no", but ONLY
++	#    for that one client.  The global flag SHOULD still be set
++	#    to a safe value: "yes".
++	#
++	#    WARNING: Setting both this flag and the
++	#    "require_message_authenticator" flag to "no" will allow
++	#    MITM attackers to create fake Access-Accept packets to the
++	#    NAS!  At least one of them MUST be set to "yes" for the
++	#    system to have any protection against the attack.
++	#
++	#  * "yes" - Allow packets without Message-Authenticator,
++	#    but only when they do not contain Proxy-State.
++	#    packets which contain Proxy-State MUST also contain
++	#    Message-Authenticator, otherwise they are discarded.
++	#
++	#    This setting is safe for most NASes, GGSNs, BRAS, etc.
++	#    Most regular RADIUS clients do not send Proxy-State
++	#    attributes for Access-Request packets that they originate.
++	#    However some aggregators (e.g. Wireless LAN Controllers)
++	#    may act as a RADIUS proxy for requests from their cohort
++	#    of managed devices, and in such cases will provide a
++	#    Proxy-State attribute. For those systems, you _must_ look
++	#    at the actual packets to determine what to do. It may be
++	#    that the only way to fix the vulnerability is to upgrade
++	#    the WLC, and set "require_message_authenticator" to "yes".
++	#
++	#  * "auto" - Automatically determine the value of the flag,
++	#    based on the first packet received from that client.
++	#
++	#    If the packet contains Proxy-State but no
++	#    Message-Authenticator, then the value of the flag is
++	#    automatically switched to "no".
++	#
++	#    For all other situations, the value of the flag is
++	#    automatically switched to "yes".
++	#
++	#    WARNING: This switch is done for the first packet
++	#    received from that client.  The change does NOT persist
++	#    across server restarts.  You MUST change the to "yes"
++	#    manually, in order to make a permanent change to the
++	#    configuration.
++	#
++	#    WARNING: If there are multiple NASes with the same source
++	#    IP and client definitions, BUT the NASes have different
++	#    behavior, then this flag WILL LIKELY BREAK YOUR NETWORK.
++	#
++	#    That is, when there are multiple different RADIUS clients
++	#    behind one NATed IP address, then these security settings
++	#    have to be set to allow the MOST INSECURE packets to be
++	#    processed.  This is a terrible idea, and will leave your
++	#    network vulnerable to the attack.  Please upgrade all
++	#    clients immediately.
++	#
++	#    The only solution to that rare configuration is to set
++	#    this flag to "no", in which case the network will work,
++	#    but will be vulnerable to the attack.
++	#
++	limit_proxy_state = auto
++
+ @openssl_version_check_config@
+ }
+ 
+diff --git a/src/include/clients.h b/src/include/clients.h
+index 560211557f..0aeb1da8d9 100644
+--- a/src/include/clients.h
++++ b/src/include/clients.h
+@@ -39,7 +39,11 @@ typedef struct radclient {
+ 
+ 	char const		*secret;		//!< Secret PSK.
+ 
+-	bool			message_authenticator;	//!< Require RADIUS message authenticator in requests.
++	fr_bool_auto_t 		require_ma;		//!< Require RADIUS message authenticator in requests.
++
++	bool			dynamic_require_ma;	//!< for dynamic clients
++
++	fr_bool_auto_t	        limit_proxy_state;     	//!< Limit Proxy-State in requests
+ 
+ 	char const		*nas_type;		//!< Type of client (arbitrary).
+ 
+diff --git a/src/include/conffile.h b/src/include/conffile.h
+index 8cb045c946..ddbcae4e4f 100644
+--- a/src/include/conffile.h
++++ b/src/include/conffile.h
+@@ -140,6 +140,7 @@ typedef struct timeval _timeval_t;
+ #define PW_TYPE_MULTI		(1 << 18) //!< CONF_PAIR can have multiple copies.
+ #define PW_TYPE_NOT_EMPTY	(1 << 19) //!< CONF_PAIR is required to have a non zero length value.
+ #define PW_TYPE_FILE_EXISTS	((1 << 20) | PW_TYPE_STRING) //!< File matching value must exist
++#define PW_TYPE_IGNORE_DEFAULT	(1 << 21) //!< don't set from .dflt if the CONF_PAIR is missing
+ /* @} **/
+ 
+ #define FR_INTEGER_COND_CHECK(_name, _var, _cond, _new)\
+diff --git a/src/include/libradius.h b/src/include/libradius.h
+index ce2f713de1..2efef8b1d3 100644
+--- a/src/include/libradius.h
++++ b/src/include/libradius.h
+@@ -402,6 +402,10 @@ typedef struct radius_packet {
+ 	size_t			partial;
+ 	int			proto;
+ #endif
++	bool			tls;		//!< uses secure transport
++	bool			message_authenticator;
++	bool			proxy_state;
++	bool			eap_message;
+ } RADIUS_PACKET;
+ 
+ typedef enum {
+@@ -507,6 +511,13 @@ DICT_VENDOR	*dict_vendorbyvalue(int vendor);
+ /* radius.c */
+ int		rad_send(RADIUS_PACKET *, RADIUS_PACKET const *, char const *secret);
+ bool		rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason);
++
++/*
++ *	1 == require_ma
++ *	2 == msg_peek
++ *	4 == limit_proxy_state
++ *	8 == require_ma for Access-* replies and Protocol-Error
++ */
+ RADIUS_PACKET	*rad_recv(TALLOC_CTX *ctx, int fd, int flags);
+ ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, int *code);
+ void		rad_recv_discard(int sockfd);
+@@ -694,7 +705,7 @@ extern bool	fr_dns_lookups;	/* do IP -> hostname lookups? */
+ extern bool	fr_hostname_lookups; /* do hostname -> IP lookups? */
+ extern int	fr_debug_lvl;	/* 0 = no debugging information */
+ extern uint32_t	fr_max_attributes; /* per incoming packet */
+-#define	FR_MAX_PACKET_CODE (52)
++#define	FR_MAX_PACKET_CODE (53)
+ extern char const *fr_packet_codes[FR_MAX_PACKET_CODE];
+ #define is_radius_code(_x) ((_x > 0) && (_x < FR_MAX_PACKET_CODE))
+ extern FILE	*fr_log_fp;
+@@ -932,6 +943,12 @@ int		fr_socket_wait_for_connect(int sockfd, struct timeval *timeout);
+ }
+ #endif
+ 
++typedef enum {
++	FR_BOOL_FALSE = 0,
++	FR_BOOL_TRUE,
++	FR_BOOL_AUTO,
++} fr_bool_auto_t;
++
+ #include <freeradius-devel/packet.h>
+ 
+ #ifdef WITH_TCP
+diff --git a/src/include/radius.h b/src/include/radius.h
+index 473528d65d..147d674eed 100644
+--- a/src/include/radius.h
++++ b/src/include/radius.h
+@@ -61,6 +61,7 @@ typedef enum {
+ 	PW_CODE_COA_REQUEST		= 43,	//!< RFC3575/RFC5176 - CoA-Request
+ 	PW_CODE_COA_ACK			= 44,	//!< RFC3575/RFC5176 - CoA-Ack (positive)
+ 	PW_CODE_COA_NAK			= 45,	//!< RFC3575/RFC5176 - CoA-Nak (not willing to perform)
++	PW_CODE_PROTOCOL_ERROR 		= 52,	//!< RFC7930 - Protocol layer issue
+ 	PW_CODE_MAX			= 255,	//!< Maximum possible code
+ } PW_CODE;
+ 
+diff --git a/src/include/radiusd.h b/src/include/radiusd.h
+index b2a0a0f642..e429c5be7a 100644
+--- a/src/include/radiusd.h
++++ b/src/include/radiusd.h
+@@ -171,6 +171,10 @@ typedef struct main_config {
+ 
+ 	bool		exiting;			//!< are we exiting?
+ 
++	fr_bool_auto_t		require_ma;			//!< global configuration for all clients and home servers
++
++	fr_bool_auto_t		limit_proxy_state;     		//!< global configuration for all clients
++
+ 
+ #ifdef ENABLE_OPENSSL_VERSION_CHECK
+ 	char const	*allow_vulnerable_openssl;	//!< The CVE number of the last security issue acknowledged.
+@@ -558,6 +562,8 @@ int main_config_free(void);
+ void main_config_hup(void);
+ void hup_logfile(void);
+ 
++int	fr_bool_auto_parse(CONF_PAIR *cp, fr_bool_auto_t *out, char const *str);
++
+ /* listen.c */
+ void listen_free(rad_listen_t **head);
+ int listen_init(CONF_SECTION *cs, rad_listen_t **head, bool spawn_flag);
+diff --git a/src/include/realms.h b/src/include/realms.h
+index 6dae8b4f85..e643818e43 100644
+--- a/src/include/realms.h
++++ b/src/include/realms.h
+@@ -59,6 +59,7 @@ typedef struct home_server {
+ 							//!< stats or when specifying home servers for a pool.
+ 
+ 	bool			dual;			//!< One of a pair of homeservers on consecutive ports.
++	fr_bool_auto_t		require_ma;		//!< for all replies to Access-Request and Status-Server
+ 	char const		*server;		//!< For internal proxying
+ 	char const		*parent_server;
+ 
+diff --git a/src/lib/radius.c b/src/lib/radius.c
+index 3881111f7d..7b91a4bde2 100644
+--- a/src/lib/radius.c
++++ b/src/lib/radius.c
+@@ -142,8 +142,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",
+ };
+ 
+ 
+@@ -1700,6 +1701,15 @@ 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
+  *
+@@ -1712,6 +1722,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.
+@@ -1775,6 +1786,27 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+ 	 *	memcpy.
+ 	 */
+ 
++	/*
++	 *	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.
+ 	 */
+@@ -1832,6 +1864,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)) {
++			/*
++			 *	We have already encoded the Message-Authenticator, don't do it again.
++			 */
++			if (seen_ma) {
++				reply = reply->next;
++				continue;
++			}
+ 			if (room < 18) break;
+ 
+ 			/*
+@@ -2323,6 +2362,8 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
+ 	radius_packet_t		*hdr;
+ 	char			host_ipaddr[128];
+ 	bool			require_ma = false;
++	bool			limit_proxy_state = false;
++	bool			seen_proxy_state = false;
+ 	bool			seen_ma = false;
+ 	uint32_t		num_attributes;
+ 	decode_fail_t		failure = DECODE_FAIL_NONE;
+@@ -2371,15 +2412,26 @@ 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.
+ 	 */
+-	if (hdr->code == PW_CODE_STATUS_SERVER) require_ma = true;
+ 
+ 	/*
+-	 *	It's also required if the caller asks for it.
++	 *	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 (flags) require_ma = true;
++	require_ma = ((flags & 0x01) != 0) || (hdr->code == PW_CODE_STATUS_SERVER) || (((flags & 0x08) != 0) && code2ma[hdr->code]);
++
++	/*
++	 *
++	 *	We only limit Proxy-State if we're not requiring
++	 *	Message-Authenticator.
++	 */
++	limit_proxy_state = ((flags & 0x04) != 0) && !require_ma;
+ 
+ 	/*
+ 	 *	Repeat the length checks.  This time, instead of
+@@ -2534,6 +2586,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:
+@@ -2542,6 +2595,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:
+ 			if (attr[1] != 2 + AUTH_VECTOR_LEN) {
+ 				FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d",
+@@ -2553,6 +2611,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
+ 				goto finish;
+ 			}
+ 			seen_ma = true;
++			packet->message_authenticator = true;
+ 			break;
+ 		}
+ 
+@@ -2609,7 +2668,19 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
+ 	 *	Message-Authenticator attributes.
+ 	 */
+ 	if (require_ma && !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)));
++		failure = DECODE_FAIL_MA_MISSING;
++		goto finish;
++	}
++
++	/*
++	 *	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)));
+diff --git a/src/main/client.c b/src/main/client.c
+index 6228438c47..875dc37d60 100644
+--- a/src/main/client.c
++++ b/src/main/client.c
+@@ -283,7 +283,8 @@ bool client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
+ 		    (old->coa_server == client->coa_server) &&
+ 		    (old->coa_pool == client->coa_pool) &&
+ #endif
+-		    (old->message_authenticator == client->message_authenticator)) {
++		    (old->require_ma == client->require_ma) &&
++		    (old->limit_proxy_state == client->limit_proxy_state)) {
+ 			WARN("Ignoring duplicate client %s", client->longname);
+ 			client_free(client);
+ 			return true;
+@@ -445,6 +446,8 @@ static fr_ipaddr_t cl_ipaddr;
+ static uint32_t cl_netmask;
+ static char const *cl_srcipaddr = NULL;
+ static char const *hs_proto = NULL;
++static char const *require_message_authenticator = NULL;
++static char const *limit_proxy_state = NULL;
+ 
+ #ifdef WITH_TCP
+ static CONF_PARSER limit_config[] = {
+@@ -467,7 +470,8 @@ static const CONF_PARSER client_config[] = {
+ 
+ 	{ "src_ipaddr", FR_CONF_POINTER(PW_TYPE_STRING, &cl_srcipaddr), NULL },
+ 
+-	{ "require_message_authenticator",  FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), "no" },
++	{ "require_message_authenticator", FR_CONF_POINTER(PW_TYPE_STRING| PW_TYPE_IGNORE_DEFAULT, &require_message_authenticator), NULL },
++	{ "limit_proxy_state", FR_CONF_POINTER(PW_TYPE_STRING| PW_TYPE_IGNORE_DEFAULT, &limit_proxy_state), NULL },
+ 
+ 	{ "secret", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, RADCLIENT, secret), NULL },
+ 	{ "shortname", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, shortname), NULL },
+@@ -663,7 +667,7 @@ static const CONF_PARSER dynamic_config[] = {
+ 	{ "FreeRADIUS-Client-Src-IP-Address", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, RADCLIENT, src_ipaddr), NULL },
+ 	{ "FreeRADIUS-Client-Src-IPv6-Address", FR_CONF_OFFSET(PW_TYPE_IPV6_ADDR, RADCLIENT, src_ipaddr), NULL },
+ 
+-	{ "FreeRADIUS-Client-Require-MA", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), NULL },
++	{ "FreeRADIUS-Client-Require-MA", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, dynamic_require_ma), NULL },
+ 
+ 	{ "FreeRADIUS-Client-Secret",  FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, secret), "" },
+ 	{ "FreeRADIUS-Client-Shortname",  FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, shortname), "" },
+@@ -845,8 +849,19 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bo
+ 	c = talloc_zero(ctx, RADCLIENT);
+ 	c->cs = cs;
+ 
++	/*
++	 *	Set the "require message authenticator" and "limit
++	 *	proxy state" flags from the global default.  If the
++	 *	configuration item exists, AND is set, it will
++	 *	over-ride the flag.
++	 */
++	c->require_ma = main_config.require_ma;
++	c->limit_proxy_state = main_config.limit_proxy_state;
++
+ 	memset(&cl_ipaddr, 0, sizeof(cl_ipaddr));
+ 	cl_netmask = 255;
++	require_message_authenticator = NULL;
++	limit_proxy_state = NULL;
+ 
+ 	if (cf_section_parse(cs, c, client_config) < 0) {
+ 		cf_log_err_cs(cs, "Error parsing client section");
+@@ -857,6 +872,9 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bo
+ 		cl_srcipaddr = NULL;
+ #endif
+ 
++		require_message_authenticator = NULL;
++		limit_proxy_state = NULL;
++
+ 		return NULL;
+ 	}
+ 
+@@ -1114,6 +1132,16 @@ done_coa:
+ 	}
+ #endif
+ 
++	if (fr_bool_auto_parse(cf_pair_find(cs, "require_message_authenticator"), &c->require_ma, require_message_authenticator) < 0) {
++		goto error;
++	}
++
++	if (c->require_ma != FR_BOOL_TRUE) {
++		if (fr_bool_auto_parse(cf_pair_find(cs, "limit_proxy_state"), &c->limit_proxy_state, limit_proxy_state) < 0) {
++			goto error;
++		}
++	}
++
+ 	return c;
+ }
+ 
+@@ -1158,7 +1186,7 @@ RADCLIENT *client_afrom_query(TALLOC_CTX *ctx, char const *identifier, char cons
+ 	if (shortname) c->shortname = talloc_typed_strdup(c, shortname);
+ 	if (type) c->nas_type = talloc_typed_strdup(c, type);
+ 	if (server) c->server = talloc_typed_strdup(c, server);
+-	c->message_authenticator = require_ma;
++	c->require_ma = require_ma;
+ 
+ 	return c;
+ }
+@@ -1344,10 +1372,10 @@ RADCLIENT *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request)
+ 			*pi = vp->vp_integer;
+ 
+ 			/*
+-			 *	Same nastiness as above.
++			 *	Same nastiness as above, but hard-coded for require Message-Authenticator.
+ 			 */
+ 			for (parse = client_config; parse->name; parse++) {
+-				if (parse->offset == dynamic_config[i].offset) break;
++				if (parse->type == PW_TYPE_BOOLEAN) break;
+ 			}
+ 			if (!parse) break;
+ 
+@@ -1436,6 +1464,11 @@ validate:
+ 		goto error;
+ 	}
+ 
++	/*
++	 *	It can't be set to "auto".  Too bad.
++	 */
++	c->require_ma = (fr_bool_auto_t) c->dynamic_require_ma;
++
+ 	if (!client_add_dynamic(clients, request->client, c)) {
+ 		return NULL;
+ 	}
+diff --git a/src/main/conffile.c b/src/main/conffile.c
+index a8c667bfb5..61754e991f 100644
+--- a/src/main/conffile.c
++++ b/src/main/conffile.c
+@@ -1418,6 +1418,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
+ {
+ 	int rcode;
+ 	bool deprecated, required, attribute, secret, file_input, cant_be_empty, tmpl, multi, file_exists;
++	bool ignore_dflt;
+ 	char **q;
+ 	char const *value;
+ 	CONF_PAIR *cp = NULL;
+@@ -1441,6 +1442,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
+ 	cant_be_empty = (type & PW_TYPE_NOT_EMPTY);
+ 	tmpl = (type & PW_TYPE_TMPL);
+ 	multi = (type & PW_TYPE_MULTI);
++	ignore_dflt = (type & PW_TYPE_IGNORE_DEFAULT);
+ 
+ 	if (attribute) required = true;
+ 	if (required) cant_be_empty = true;	/* May want to review this in the future... */
+@@ -1464,7 +1466,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
+ 	 *	section, use the default value.
+ 	 */
+ 	if (!cp) {
+-		if (deprecated) return 0;	/* Don't set the default value */
++		if (deprecated || ignore_dflt) return 0;	/* Don't set the default value */
+ 
+ 		rcode = 1;
+ 		value = dflt;
+diff --git a/src/main/listen.c b/src/main/listen.c
+index ebf7f5221c..c20fea243d 100644
+--- a/src/main/listen.c
++++ b/src/main/listen.c
+@@ -456,6 +456,122 @@ int rad_status_server(REQUEST *request)
+ 	return 0;
+ }
+ 
++static void blastradius_checks(RADIUS_PACKET *packet, RADCLIENT *client)
++{
++	if (client->require_ma == FR_BOOL_TRUE) return;
++
++	if (client->require_ma == FR_BOOL_AUTO) {
++		if (!packet->message_authenticator) {
++			ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++			ERROR("BlastRADIUS check: Received packet without Message-Authenticator.");
++			ERROR("Setting \"require_message_authenticator = false\" for client %s", client->shortname);
++			ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++			ERROR("UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK.");
++			ERROR("Once the client is upgraded, set \"require_message_authenticator = true\" for  client %s", client->shortname);
++			ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++			client->require_ma = FR_BOOL_FALSE;
++
++			/*
++			 *	And fall through to the
++			 *	limit_proxy_state checks, which might
++			 *	complain again.  Oh well, maybe that
++			 *	will make people read the messages.
++			 */
++
++		} else if (packet->eap_message) {
++			/*
++			 *	Don't set it to "true" for packets
++			 *	with EAP-Message.  It's already
++			 *	required there, and we might get a
++			 *	non-EAP packet with (or without)
++			 *	Message-Authenticator
++			 */
++			return;
++		} else {
++			ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++			ERROR("BlastRADIUS check: Received packet with Message-Authenticator.");
++			ERROR("Setting \"require_message_authenticator = true\" for client %s", client->shortname);
++			ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++			ERROR("It looks like the client has been updated to protect from the BlastRADIUS attack.");
++			ERROR("Please set \"require_message_authenticator = true\" for client %s", client->shortname);
++			ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++
++			client->require_ma = FR_BOOL_TRUE;
++			return;
++		}
++
++	}
++
++	/*
++	 *	If all of the checks are turned off, then complain for every packet we receive.
++	 */
++	if (client->limit_proxy_state == FR_BOOL_FALSE) {
++		/*
++		 *	We have a Message-Authenticator, and it's valid.  We don't need to compain.
++		 */
++		if (!fr_debug_lvl) return; /* easier than checking for each line below */
++
++		DEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++		DEBUG("BlastRADIUS check: Received packet without Message-Authenticator.");
++		DEBUG("YOU MUST SET \"require_message_authenticator = true\", or");
++		DEBUG("YOU MUST SET \"limit_proxy_state = true\" for client %s", client->shortname);
++		DEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++		DEBUG("The packet does not contain Message-Authenticator, which is a security issue");
++		DEBUG("UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK.");
++		DEBUG("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname);
++		DEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++		return;
++	}
++
++	/*
++	 *	Don't complain here.  rad_packet_ok() will instead
++	 *	complain about every packet with Proxy-State but which
++	 *	is missing Message-Authenticator.
++	 */
++	if (client->limit_proxy_state == FR_BOOL_TRUE) {
++		return;
++	}
++
++	if (packet->proxy_state && !packet->message_authenticator) {
++		ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++		ERROR("BlastRADIUS check: Received packet with Proxy-State, but without Message-Authenticator.");
++		ERROR("This is either a BlastRADIUS attack, OR");
++		ERROR("the client is a proxy RADIUS server which has not been upgraded.");
++		ERROR("Setting \"limit_proxy_state = false\" for client %s", client->shortname);
++		ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++		ERROR("UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK.");
++		DEBUG("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname);
++		ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++
++		client->limit_proxy_state = FR_BOOL_FALSE;
++
++	} else {
++		client->limit_proxy_state = FR_BOOL_TRUE;
++
++		ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++		if (!packet->proxy_state) {
++			ERROR("BlastRADIUS check: Received packet without Proxy-State.");
++		} else {
++			ERROR("BlastRADIUS check: Received packet with Proxy-State and Message-Authenticator.");
++		}
++
++		ERROR("Setting \"limit_proxy_state = true\" for client %s", client->shortname);
++		ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++
++		if (!packet->message_authenticator) {
++			ERROR("The packet does not contain Message-Authenticator, which is a security issue.");
++			ERROR("UPGRADE THE CLIENT AS YOUR NETWORK MAY BE VULNERABLE TO THE BLASTRADIUS ATTACK.");
++			DEBUG("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname);
++		} else {
++			ERROR("The packet contains Message-Authenticator.");
++			if (!packet->eap_message) ERROR("The client has likely been upgraded to protect from the attack.");
++			ERROR("Please set \"require_message_authenticator = true\" for client %s", client->shortname);
++		}
++		ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++	}
++}
++
++
+ #ifdef WITH_TCP
+ static int dual_tcp_recv(rad_listen_t *listener)
+ {
+@@ -532,6 +648,21 @@ static int dual_tcp_recv(rad_listen_t *listener)
+ 	switch (packet->code) {
+ 	case PW_CODE_ACCESS_REQUEST:
+ 		if (listener->type != RAD_LISTEN_AUTH) goto bad_packet;
++
++		/*
++		 *	Enforce BlastRADIUS checks on TCP, too.
++		 */
++		if (!rad_packet_ok(packet, (client->require_ma == FR_BOOL_TRUE) | ((client->limit_proxy_state == FR_BOOL_TRUE) << 2), NULL)) {
++			FR_STATS_INC(auth, total_malformed_requests);
++			rad_free(&sock->packet);
++			return 0;
++		}
++
++		/*
++		 *	Perform BlastRADIUS checks and warnings.
++		 */
++		if (packet->code == PW_CODE_ACCESS_REQUEST) blastradius_checks(packet, client);
++
+ 		FR_STATS_INC(auth, total_requests);
+ 		fun = rad_authenticate;
+ 		break;
+@@ -1562,7 +1693,7 @@ static int auth_socket_recv(rad_listen_t *listener)
+ 	 *	Now that we've sanity checked everything, receive the
+ 	 *	packet.
+ 	 */
+-	packet = rad_recv(ctx, listener->fd, client->message_authenticator);
++	packet = rad_recv(ctx, listener->fd, (client->require_ma == FR_BOOL_TRUE) | ((client->limit_proxy_state == FR_BOOL_TRUE) << 2));
+ 	if (!packet) {
+ 		FR_STATS_INC(auth, total_malformed_requests);
+ 		if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
+@@ -1570,6 +1701,12 @@ static int auth_socket_recv(rad_listen_t *listener)
+ 		return 0;
+ 	}
+ 
++
++	/*
++	 *	Perform BlastRADIUS checks and warnings.
++	 */
++	if (packet->code == PW_CODE_ACCESS_REQUEST) blastradius_checks(packet, client);
++
+ #ifdef __APPLE__
+ #ifdef WITH_UDPFROMTO
+ 	/*
+@@ -1955,7 +2092,7 @@ static int coa_socket_recv(rad_listen_t *listener)
+ 	 *	Now that we've sanity checked everything, receive the
+ 	 *	packet.
+ 	 */
+-	packet = rad_recv(ctx, listener->fd, client->message_authenticator);
++	packet = rad_recv(ctx, listener->fd, client->require_ma | (((int) client->limit_proxy_state) << 2));
+ 	if (!packet) {
+ 		FR_STATS_INC(coa, total_malformed_requests);
+ 		if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
+diff --git a/src/main/mainconfig.c b/src/main/mainconfig.c
+index e9dd412dee..520d7fa474 100644
+--- a/src/main/mainconfig.c
++++ b/src/main/mainconfig.c
+@@ -73,6 +73,8 @@ static char const *gid_name = NULL;
+ static char const *chroot_dir = NULL;
+ static bool allow_core_dumps = false;
+ static char const *radlog_dest = NULL;
++static char const *require_message_authenticator = NULL;
++static char const *limit_proxy_state = NULL;
+ 
+ /*
+  *	These are not used anywhere else..
+@@ -87,6 +89,53 @@ static bool		do_colourise = false;
+ 
+ static char const	*radius_dir = NULL;	//!< Path to raddb directory
+ 
++static const FR_NAME_NUMBER fr_bool_auto_names[] = {
++	{ "false",	FR_BOOL_FALSE     },
++	{ "no",		FR_BOOL_FALSE     },
++	{ "0",		FR_BOOL_FALSE     },
++
++	{ "true",	FR_BOOL_TRUE      },
++	{ "yes",       	FR_BOOL_TRUE      },
++	{ "1",		FR_BOOL_TRUE      },
++
++	{ "auto",	FR_BOOL_AUTO      },
++
++	{ NULL,	0 }
++};
++
++/*
++ *	Get decent values for false / true / auto
++ */
++int fr_bool_auto_parse(CONF_PAIR *cp, fr_bool_auto_t *out, char const *str)
++{
++	int value;
++
++	/*
++	 *	Don't change anything.
++	 */
++	if (!str) return 0;
++
++	value = fr_str2int(fr_bool_auto_names, str, -1);
++	if (value >= 0) {
++		*out = value;
++		return 0;
++	}
++
++	/*
++	 *	This should never happen, as the defaults are in the
++	 *	source code.  If there's no CONF_PAIR, and there's a
++	 *	parse error, then the source code is wrong.
++	 */
++	if (!cp) {
++		fprintf(stderr, "%s: Error - Invalid value in configuration", main_config.name);
++		return -1;
++	}
++
++	cf_log_err(cf_pair_to_item(cp), "Invalid value for \"%s\"", cf_pair_attr(cp));
++	return -1;
++}
++
++
+ /**********************************************************************
+  *
+  *	We need to figure out where the logs go, before doing anything
+@@ -159,6 +208,8 @@ static const CONF_PARSER security_config[] = {
+ 	{ "max_attributes",  FR_CONF_POINTER(PW_TYPE_INTEGER, &fr_max_attributes), STRINGIFY(0) },
+ 	{ "reject_delay",  FR_CONF_POINTER(PW_TYPE_TIMEVAL, &main_config.reject_delay), STRINGIFY(0) },
+ 	{ "status_server", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.status_server), "no"},
++	{ "require_message_authenticator", FR_CONF_POINTER(PW_TYPE_STRING, &require_message_authenticator), "auto"},
++	{ "limit_proxy_state", FR_CONF_POINTER(PW_TYPE_STRING, &limit_proxy_state), "auto"},
+ #ifdef ENABLE_OPENSSL_VERSION_CHECK
+ 	{ "allow_vulnerable_openssl", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.allow_vulnerable_openssl), "no"},
+ #endif
+@@ -838,6 +889,8 @@ int main_config_init(void)
+ 	if (!main_config.dictionary_dir) {
+ 		main_config.dictionary_dir = DICTDIR;
+ 	}
++	main_config.require_ma = FR_BOOL_AUTO;
++	main_config.limit_proxy_state = FR_BOOL_AUTO;
+ 
+ 	/*
+ 	 *	About sizeof(REQUEST) + sizeof(RADIUS_PACKET) * 2 + sizeof(VALUE_PAIR) * 400
+@@ -1127,6 +1180,23 @@ do {\
+ 	main_config.init_delay.tv_sec = 0;
+ 	main_config.init_delay.tv_usec = 2* (1000000 / 3);
+ 
++	{
++		CONF_PAIR *cp = NULL;
++
++		subcs = cf_section_sub_find(cs, "security");
++		if (subcs) cp = cf_pair_find(subcs, "require_message_authenticator");
++		if (fr_bool_auto_parse(cp, &main_config.require_ma, require_message_authenticator) < 0) {
++			cf_file_free(cs);
++			return -1;
++		}
++
++		if (subcs) cp = cf_pair_find(subcs, "limit_proxy_state");
++		if (fr_bool_auto_parse(cp, &main_config.limit_proxy_state, limit_proxy_state) < 0) {
++			cf_file_free(cs);
++			return -1;
++		}
++	}
++
+ 	/*
+ 	 *	Free the old configuration items, and replace them
+ 	 *	with the new ones.
+diff --git a/src/main/process.c b/src/main/process.c
+index 1a48517d43..401033bdd6 100644
+--- a/src/main/process.c
++++ b/src/main/process.c
+@@ -2593,6 +2593,23 @@ int request_proxy_reply(RADIUS_PACKET *packet)
+ 
+ 	PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ 
++	if (!request->proxy_reply) {
++		decode_fail_t reason;
++
++		/*
++		 *	If the home server configuration requires a Message-Authenticator, then set the flag,
++		 *	but only if the proxied packet is Access-Request or Status-Sercer.
++		 *
++		 *	The realms.c file already clears require_ma for TLS connections.
++		 */
++		bool require_ma = (request->home_server->require_ma == FR_BOOL_TRUE) && (request->proxy->code == PW_CODE_ACCESS_REQUEST);
++
++		if(!rad_packet_ok(packet, require_ma, &reason)) {
++			DEBUG("Ignoring invalid packet - %s", fr_strerror());
++			return 0;
++		}
++	}
++
+ 	/*
+ 	 *	No reply, BUT the current packet fails verification:
+ 	 *	ignore it.  This does the MD5 calculations in the
+@@ -2618,6 +2635,54 @@ int request_proxy_reply(RADIUS_PACKET *packet)
+ 		return 0;
+ 	}
+ 
++
++		/*
++		 *	BlastRADIUS checks.  We're running in the main
++		 *	listener thread, so there's no conflict
++		 *	checking or setting these fields.
++		 */
++		if (!request->proxy_reply && (request->proxy->code == PW_CODE_ACCESS_REQUEST) &&
++#ifdef WITH_TLS
++		    !request->home_server->tls &&
++#endif
++		    !packet->eap_message) {
++			if (request->home_server->require_ma == FR_BOOL_AUTO) {
++				if (!packet->message_authenticator) {
++					RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++					RERROR("BlastRADIUS check: Received response to Access-Request without Message-Authenticator.");
++					RERROR("Setting \"require_message_authenticator = false\" for home_server %s", request->home_server->name);
++					RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++					RERROR("UPGRADE THE HOME SERVER AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK.");
++					RERROR("Once the home_server is upgraded, set \"require_message_authenticator = true\" for home_server %s.", request->home_server->name);
++					RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++
++					request->home_server->require_ma = FR_BOOL_FALSE;
++				} else {
++					RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++					RERROR("BlastRADIUS check: Received response to Access-Request with Message-Authenticator.");
++					RERROR("Setting \"require_message_authenticator = true\" for home_server %s", request->home_server->name);
++					RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++					RERROR("It looks like the home server has been updated to protect from the BlastRADIUS attack.");
++					RERROR("Please set \"require_message_authenticator = true\" for home_server %s", request->home_server->name);
++					RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++
++					request->home_server->require_ma = FR_BOOL_TRUE;
++				}
++
++			} else if (fr_debug_lvl && (request->home_server->require_ma == FR_BOOL_FALSE) && !packet->message_authenticator) {
++				/*
++				 *	If it's "no" AND we don't have a Message-Authenticator, then complain on every packet.
++				 */
++				RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++				RDEBUG("BlastRADIUS check: Received packet without Message-Authenticator from home_server %s", request->home_server->name);
++				RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++				RDEBUG("The packet does not contain Message-Authenticator, which is a security issue");
++				RDEBUG("UPGRADE THE HOME SERVER AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK.");
++				RERROR("Once the home_server is upgraded, set \"require_message_authenticator = true\" for home_server %s.", request->home_server->name);
++				RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
++			}
++		}
++
+ 	/*
+ 	 *	This shouldn't happen, but threads and race
+ 	 *	conditions.
+diff --git a/src/main/radclient.c b/src/main/radclient.c
+index 52d2872b13..47d5f07785 100644
+--- a/src/main/radclient.c
++++ b/src/main/radclient.c
+@@ -54,6 +54,7 @@ static fr_ipaddr_t server_ipaddr;
+ static int resend_count = 1;
+ static bool done = true;
+ static bool print_filename = false;
++static bool blast_radius = false;
+ 
+ static fr_ipaddr_t client_ipaddr;
+ static uint16_t client_port = 0;
+@@ -89,6 +90,7 @@ static void NEVER_RETURNS usage(void)
+ 	fprintf(stderr, "  <command>              One of auth, acct, status, coa, disconnect or auto.\n");
+ 	fprintf(stderr, "  -4                     Use IPv4 address of server\n");
+ 	fprintf(stderr, "  -6                     Use IPv6 address of server.\n");
++	fprintf(stderr, "  -b                     Mandate checks for Blast RADIUS (this is not set by default).\n");
+ 	fprintf(stderr, "  -c <count>             Send each packet 'count' times.\n");
+ 	fprintf(stderr, "  -d <raddb>             Set user dictionary directory (defaults to " RADDBDIR ").\n");
+ 	fprintf(stderr, "  -D <dictdir>           Set main dictionary directory (defaults to " DICTDIR ").\n");
+@@ -1000,6 +1002,130 @@ static int send_one_packet(rc_request_t *request)
+ 	return 0;
+ }
+ 
++/*
++ *	Do Blast RADIUS checks.
++ *
++ *	The request is an Access-Request, and does NOT contain Proxy-State.
++ *
++ *	The reply is a raw packet, and is NOT yet decoded.
++ */
++static int blast_radius_check(rc_request_t *request, RADIUS_PACKET *reply)
++{
++	uint8_t *attr, *end;
++	VALUE_PAIR *vp;
++	bool have_message_authenticator = false;
++
++	/*
++	 *	We've received a raw packet.  Nothing has (as of yet) checked
++	 *	anything in it other than the length, and that it's a
++	 *	well-formed RADIUS packet.
++	 */
++	switch (reply->data[0]) {
++	case PW_CODE_ACCESS_ACCEPT:
++	case PW_CODE_ACCESS_REJECT:
++	case PW_CODE_ACCESS_CHALLENGE:
++		if (reply->data[1] != request->packet->id) {
++			ERROR("Invalid reply ID %d to Access-Request ID %d", reply->data[1], request->packet->id);
++			return -1;
++		}
++		break;
++
++	default:
++		ERROR("Invalid reply code %d to Access-Request", reply->data[0]);
++		return -1;
++	}
++
++	/*
++	 *	If the reply has a Message-Authenticator, then it MIGHT be fine.
++	 */
++	attr = reply->data + 20;
++	end = reply->data + reply->data_len;
++
++	/*
++	 *	It should be the first attribute, so we warn if it isn't there.
++	 *
++	 *	But it's not a fatal error.
++	 */
++	if (blast_radius && (attr[0] != PW_MESSAGE_AUTHENTICATOR)) {
++		RDEBUG("WARNING The %s reply packet does not have Message-Authenticator as the first attribute.  The packet may be vulnerable to Blast RADIUS attacks.",
++		       fr_packet_codes[reply->data[0]]);
++	}
++
++	/*
++	 *	Set up for Proxy-State checks.
++	 *
++	 *	If we see a Proxy-State in the reply which we didn't send, then it's a Blast RADIUS attack.
++	 */
++	vp = fr_pair_find_by_num(request->packet->vps, PW_PROXY_STATE, 0, TAG_ANY);
++
++	while (attr < end) {
++		/*
++		 *	Blast RADIUS work-arounds require that
++		 *	Message-Authenticator is the first attribute in the
++		 *	reply.  Note that we don't check for it being the
++		 *	first attribute, but simply that it exists.
++		 *
++		 *	That check is a balance between securing the reply
++		 *	packet from attacks, and not violating the RFCs which
++		 *	say that there is no order to attributes in the
++		 *	packet.
++		 *
++		 *	However, no matter the status of the '-b' flag we
++		 *	still can check for the signature of the attack, and
++		 *	discard packets which are suspicious.  This behavior
++		 *	protects radclient from the attack, without mandating
++		 *	new behavior on the server side.
++		 *
++		 *	Note that we don't set the '-b' flag by default.
++		 *	radclient is intended for testing / debugging, and is
++		 *	not intended to be used as part of a secure login /
++		 *	user checking system.
++		 */
++		if (attr[0] == PW_MESSAGE_AUTHENTICATOR) {
++			have_message_authenticator = true;
++			goto next;
++		}
++
++		/*
++		 *	If there are Proxy-State attributes in the reply, they must
++		 *	match EXACTLY the Proxy-State attributes in the request.
++		 *
++		 *	Note that we don't care if there are more Proxy-States
++		 *	in the request than in the reply.  The Blast RADIUS
++		 *	issue requires _adding_ Proxy-State attributes, and
++		 *	cannot work when the server _deletes_ Proxy-State
++		 *	attributes.
++		 */
++		if (attr[0] == PW_PROXY_STATE) {
++			if (!vp || (vp->length != (size_t) (attr[1] - 2)) || (memcmp(vp->vp_octets, attr + 2, vp->length) != 0)) {
++				ERROR("Invalid reply to Access-Request ID %d - Discarding packet due to Blast RADIUS attack being detected.", request->packet->id);
++				ERROR("We received a Proxy-State in the reply which we did not send, or which is different from what we sent.");
++				return -1;
++			}
++
++			vp = fr_pair_find_by_num(vp->next, PW_PROXY_STATE, 0, TAG_ANY);
++		}
++
++	next:
++		attr += attr[1];
++	}
++
++	/*
++	 *	If "-b" is set, then we require Message-Authenticator in the reply.
++	 */
++	if (blast_radius && !have_message_authenticator) {
++		ERROR("The %s reply packet does not contain Message-Authenticator - discarding packet due to Blast RADIUS checks.",
++		      fr_packet_codes[reply->data[0]]);
++		return -1;
++	}
++
++	/*
++	 *	The packet doesn't look like it's a Blast RADIUS attack.  The
++	 *	caller will now verify the packet signature.
++	 */
++	return 0;
++}
++
+ /*
+  *	Receive one packet, maybe.
+  */
+@@ -1051,6 +1177,21 @@ static int recv_one_packet(int wait_time)
+ 	}
+ 	request = fr_packet2myptr(rc_request_t, packet, packet_p);
+ 
++
++	/*
++	 *	We want radclient to be able to send any packet, including
++	 *	imperfect ones.  However, we do NOT want to be vulnerable to
++	 *	the "Blast RADIUS" issue.  Instead of adding command-line
++	 *	flags to enable/disable similar flags to what the server
++	 *	sends, we just do a few more smart checks to double-check
++	 *	things.
++	 */
++	if ((request->packet->code == PW_CODE_ACCESS_REQUEST) &&
++	    blast_radius_check(request, reply) < 0) {
++		rad_free(&reply);
++		return -1;
++	}
++
+ 	/*
+ 	 *	Fails the signature validation: not a real reply.
+ 	 *	FIXME: Silently drop it and listen for another packet.
+@@ -1183,7 +1324,7 @@ int main(int argc, char **argv)
+ 		exit(1);
+ 	}
+ 
+-	while ((c = getopt(argc, argv, "46c:d:D:f:Fhn:p:qr:sS:t:vx"
++	while ((c = getopt(argc, argv, "46bc:d:D:f:Fhn:p:qr:sS:t:vx"
+ #ifdef WITH_TCP
+ 		"P:"
+ #endif
+@@ -1192,6 +1333,10 @@ int main(int argc, char **argv)
+ 			force_af = AF_INET;
+ 			break;
+ 
++		case 'b':
++			blast_radius = true;
++			break;
++
+ 		case '6':
+ 			force_af = AF_INET6;
+ 			break;
+diff --git a/src/main/radtest.in b/src/main/radtest.in
+index 38b1ba9a0f..8a6741a26c 100644
+--- a/src/main/radtest.in
++++ b/src/main/radtest.in
+@@ -19,6 +19,7 @@ usage() {
+ 	echo "        -x                  Enable debug output" >&2
+ 	echo "        -4                  Use IPv4 for the NAS address (default)" >&2
+ 	echo "        -6                  Use IPv6 for the NAS address" >&2
++	echo "        -6                  Mandate checks for Blast RADIUS (this is not set by default)." >&2
+ 	exit 1
+ }
+ 
+@@ -55,6 +56,10 @@ do
+ 		NAS_ADDR_ATTR="NAS-IPv6-Address"
+ 		shift
+ 		;;
++	-b)
++		OPTIONS="$OPTIONS -b"
++		shift
++		;;
+ 	-d) 
+ 		OPTIONS="$OPTIONS -d $2"
+ 		shift;shift
+@@ -120,7 +125,6 @@ fi
+ 	echo "$PASSWORD = \"$2\""
+ 	echo "$NAS_ADDR_ATTR = $nas"
+ 	echo "NAS-Port = $4"
+-	echo "Message-Authenticator = 0x00"
+ 	if [ "$radclient" = "$radeapclient" ]
+ 	then
+ 	    echo "EAP-Code = Response"
+diff --git a/src/main/realms.c b/src/main/realms.c
+index eb42598116..5e1215c0bb 100644
+--- a/src/main/realms.c
++++ b/src/main/realms.c
+@@ -366,7 +366,10 @@ static CONF_PARSER home_server_coa[] = {
+ };
+ #endif
+ 
++static const char *require_message_authenticator = NULL;
++
+ static CONF_PARSER home_server_config[] = {
++	{ "require_message_authenticator", FR_CONF_POINTER(PW_TYPE_STRING| PW_TYPE_IGNORE_DEFAULT, &require_message_authenticator), NULL },
+ 	{ "ipaddr", FR_CONF_OFFSET(PW_TYPE_COMBO_IP_ADDR, home_server_t, ipaddr), NULL },
+ 	{ "ipv4addr", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, home_server_t, ipaddr), NULL },
+ 	{ "ipv6addr", FR_CONF_OFFSET(PW_TYPE_IPV6_ADDR, home_server_t, ipaddr), NULL },
+@@ -640,6 +643,9 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
+ 	home->cs = cs;
+ 	home->state = HOME_STATE_UNKNOWN;
+ 	home->proto = IPPROTO_UDP;
++	home->require_ma = main_config.require_ma;
++
++	require_message_authenticator = false;
+ 
+ 	/*
+ 	 *	Parse the configuration into the home server
+@@ -647,6 +653,10 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
+ 	 */
+ 	if (cf_section_parse(cs, home, home_server_config) < 0) goto error;
+ 
++	if (fr_bool_auto_parse(cf_pair_find(cs, "require_message_authenticator"), &home->require_ma, require_message_authenticator) < 0) {
++		goto error;
++	}
++
+ 	/*
+ 	 *	It has an IP address, it must be a remote server.
+ 	 */
+@@ -924,6 +934,7 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
+ 		 *	Parse the SSL client configuration.
+ 		 */
+ 		if (tls) {
++			home->require_ma = false;
+ 			home->tls = tls_client_conf_parse(tls);
+ 			if (!home->tls) {
+ 				goto error;
+diff --git a/src/main/tls_listen.c b/src/main/tls_listen.c
+index 0eed87b64f..4ae3c5b975 100644
+--- a/src/main/tls_listen.c
++++ b/src/main/tls_listen.c
+@@ -299,6 +299,8 @@ get_application_data:
+ 	packet->vps = NULL;
+ 	PTHREAD_MUTEX_UNLOCK(&sock->mutex);
+ 
++	packet->tls = true;
++
+ 	if (!rad_packet_ok(packet, 0, NULL)) {
+ 		if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
+ 		DEBUG("Closing TLS socket from client");
+@@ -713,6 +715,8 @@ int proxy_tls_recv(rad_listen_t *listener)
+ 	memcpy(packet->data, data, packet->data_len);
+ 	memcpy(packet->vector, packet->data + 4, 16);
+ 
++	packet->tls = true;
++
+ 	/*
+ 	 *	FIXME: Client MIB updates?
+ 	 */
+@@ -765,6 +769,7 @@ int proxy_tls_send(rad_listen_t *listener, REQUEST *request)
+ 	 *	if there's no packet, encode it here.
+ 	 */
+ 	if (!request->proxy->data) {
++		request->reply->tls = true;
+ 		request->proxy_listener->encode(request->proxy_listener,
+ 						request);
+ 	}
+-- 
+2.35.7
+
diff --git a/meta-networking/recipes-connectivity/freeradius/freeradius_3.0.21.bb b/meta-networking/recipes-connectivity/freeradius/freeradius_3.0.21.bb
index db37f6591..01d23fdf8 100644
--- a/meta-networking/recipes-connectivity/freeradius/freeradius_3.0.21.bb
+++ b/meta-networking/recipes-connectivity/freeradius/freeradius_3.0.21.bb
@@ -35,6 +35,7 @@  SRC_URI = "git://github.com/FreeRADIUS/freeradius-server.git;branch=v3.0.x;lfs=0
     file://0001-version.c-don-t-print-build-flags.patch \
     file://CVE-2022-41860.patch \
     file://CVE-2022-41861.patch \
+    file://CVE-2024-3596.patch \
 "
 
 raddbdir="${sysconfdir}/${MLPREFIX}raddb"