b3ac7a5c16
Manual backport from v3.0.x branch. Resolves: RHEL-46572 Signed-off-by: Antonio Torres <antorres@redhat.com>
1503 lines
56 KiB
Diff
1503 lines
56 KiB
Diff
From: Antonio Torres <antorres@redhat.com>
|
|
Date: Thu, 11 Jul, 2024
|
|
Subject: Backport fix for BlastRADIUS
|
|
|
|
Manual backport from v3.0.x branch, commit range 3a00a6ecc188629b0441fd45ad61ca8986de156e..da643f1edc267ce95260dc36069e6f1a7a4d66f8
|
|
|
|
Resolves: RHEL-46572
|
|
Signed-off-by: Antonio Torres <antorres@redhat.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..db32e7f68d 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..6fa9ded0f9 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..2cde3a2e37 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 0af3f51458..bd0aa668d0 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.
|
|
@@ -557,6 +561,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..ea5665c56c 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..556f3b961e 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 abcc15d225..af5b122af5 100644
|
|
--- a/src/main/client.c
|
|
+++ b/src/main/client.c
|
|
@@ -279,7 +279,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;
|
|
@@ -443,6 +444,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[] = {
|
|
@@ -465,7 +468,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 },
|
|
@@ -661,7 +665,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), "" },
|
|
@@ -843,8 +847,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");
|
|
@@ -855,6 +870,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;
|
|
}
|
|
|
|
@@ -1112,6 +1130,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;
|
|
}
|
|
|
|
@@ -1156,7 +1184,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;
|
|
}
|
|
@@ -1342,10 +1370,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;
|
|
|
|
@@ -1434,6 +1462,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 6d6441a55b..0e1ced2385 100644
|
|
--- a/src/main/conffile.c
|
|
+++ b/src/main/conffile.c
|
|
@@ -1417,6 +1417,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;
|
|
@@ -1440,6 +1441,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... */
|
|
@@ -1463,7 +1465,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..0d178138c9 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..854c7155aa 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 78c6d8a9e5..a478026024 100644
|
|
--- a/src/main/process.c
|
|
+++ b/src/main/process.c
|
|
@@ -2594,6 +2594,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
|
|
@@ -2619,6 +2636,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..dd2b342ba2 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
|
|
@@ -1196,6 +1337,10 @@ int main(int argc, char **argv)
|
|
force_af = AF_INET6;
|
|
break;
|
|
|
|
+ case 'b':
|
|
+ blast_radius = true;
|
|
+ break;
|
|
+
|
|
case 'c':
|
|
if (!isdigit((int) *optarg))
|
|
usage();
|
|
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..c82d2eb1ea 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);
|
|
}
|