Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP2:GA
freeradius-server.34053
CVE-2024-3596.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File CVE-2024-3596.patch of Package freeradius-server.34053
Index: freeradius-server-3.0.16/raddb/radiusd.conf.in =================================================================== --- freeradius-server-3.0.16.orig/raddb/radiusd.conf.in +++ freeradius-server-3.0.16/raddb/radiusd.conf.in @@ -480,6 +480,64 @@ 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. + + # + # 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 a flag to that section: + # + # require_message_authenticator = no + # + # If the server produces error message which says "Packet + # does not contain required Message-Authenticator attribute", + # then this configuration item has to be updated. + # + # WARNING: This item should always be left as "yes", + # otherwise it is possible for MITM attackers to create fake + # Access-Accept packets to the NAS! + # + require_message_authenticator = yes + + # + # Global configuration for requiring Message-Authenticator + # Access-Request packets from a NAS, but only if those + # packets also contain Proxy-State. 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 + # a flag to that section: + # + # limit_proxy_state = no + # + # 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. + # + # This configuration item should ALWAYS be set to "yes". + # + # The only reason to set it 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 on a per-client basis. + # + # 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. + # + limit_proxy_state = yes + @openssl_version_check_config@ } Index: freeradius-server-3.0.16/src/include/radiusd.h =================================================================== --- freeradius-server-3.0.16.orig/src/include/radiusd.h +++ freeradius-server-3.0.16/src/include/radiusd.h @@ -169,6 +169,9 @@ typedef struct main_config { bool exiting; //!< are we exiting? + bool require_ma; //!< global configuration for all clients and home servers + + bool 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. Index: freeradius-server-3.0.16/src/main/mainconfig.c =================================================================== --- freeradius-server-3.0.16.orig/src/main/mainconfig.c +++ freeradius-server-3.0.16/src/main/mainconfig.c @@ -154,6 +154,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_BOOLEAN, &main_config.require_ma), "yes"}, + { "limit_proxy_state", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.limit_proxy_state), "yes"}, #ifdef ENABLE_OPENSSL_VERSION_CHECK { "allow_vulnerable_openssl", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.allow_vulnerable_openssl), "no"}, #endif Index: freeradius-server-3.0.16/src/include/clients.h =================================================================== --- freeradius-server-3.0.16.orig/src/include/clients.h +++ freeradius-server-3.0.16/src/include/clients.h @@ -39,7 +39,9 @@ typedef struct radclient { char const *secret; //!< Secret PSK. - bool message_authenticator; //!< Require RADIUS message authenticator in requests. + bool require_ma; //!< Require RADIUS message authenticator in requests. + + bool limit_proxy_state; //!< Limit Proxy-State in requests char const *nas_type; //!< Type of client (arbitrary). Index: freeradius-server-3.0.16/src/main/client.c =================================================================== --- freeradius-server-3.0.16.orig/src/main/client.c +++ freeradius-server-3.0.16/src/main/client.c @@ -315,7 +315,8 @@ bool client_add(RADCLIENT_LIST *clients, (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; @@ -501,7 +502,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_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_IGNORE_DEFAULT, RADCLIENT, require_ma), NULL }, + { "limit_proxy_state", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_IGNORE_DEFAULT, RADCLIENT, 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 }, @@ -697,7 +699,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, 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), "" }, @@ -879,6 +881,15 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *c 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; @@ -1192,7 +1203,7 @@ RADCLIENT *client_afrom_query(TALLOC_CTX 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; } Index: freeradius-server-3.0.16/src/main/listen.c =================================================================== --- freeradius-server-3.0.16.orig/src/main/listen.c +++ freeradius-server-3.0.16/src/main/listen.c @@ -1536,7 +1536,7 @@ static int auth_socket_recv(rad_listen_t * 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(auth, total_malformed_requests); if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror()); @@ -1929,7 +1929,7 @@ static int coa_socket_recv(rad_listen_t * 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); if (!packet) { FR_STATS_INC(coa, total_malformed_requests); if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror()); Index: freeradius-server-3.0.16/src/include/conffile.h =================================================================== --- freeradius-server-3.0.16.orig/src/include/conffile.h +++ freeradius-server-3.0.16/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)\ Index: freeradius-server-3.0.16/src/main/conffile.c =================================================================== --- freeradius-server-3.0.16.orig/src/main/conffile.c +++ freeradius-server-3.0.16/src/main/conffile.c @@ -1388,6 +1388,7 @@ int cf_item_parse(CONF_SECTION *cs, char { 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; @@ -1408,6 +1409,7 @@ int cf_item_parse(CONF_SECTION *cs, char 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... */ @@ -1431,7 +1433,7 @@ int cf_item_parse(CONF_SECTION *cs, char * 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; Index: freeradius-server-3.0.16/raddb/clients.conf =================================================================== --- freeradius-server-3.0.16.orig/raddb/clients.conf +++ freeradius-server-3.0.16/raddb/clients.conf @@ -100,15 +100,30 @@ 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. + # 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 "no". + # + # 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 + # +# require_message_authenticator = yes + + # + # 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". + # + # 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 - require_message_authenticator = no + # +# limit_proxy_state = yes # # The short name is used as an alias for the fully qualified Index: freeradius-server-3.0.16/src/include/libradius.h =================================================================== --- freeradius-server-3.0.16.orig/src/include/libradius.h +++ freeradius-server-3.0.16/src/include/libradius.h @@ -404,6 +404,7 @@ typedef struct radius_packet { size_t partial; int proto; #endif + bool tls; //!< uses secure transport } RADIUS_PACKET; typedef enum { @@ -509,6 +510,11 @@ DICT_VENDOR *dict_vendorbyvalue(int vend /* 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 + * 3 == limit_proxy_state + */ 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); Index: freeradius-server-3.0.16/src/main/tls_listen.c =================================================================== --- freeradius-server-3.0.16.orig/src/main/tls_listen.c +++ freeradius-server-3.0.16/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"); @@ -712,6 +714,8 @@ int proxy_tls_recv(rad_listen_t *listene memcpy(packet->data, data, packet->data_len); memcpy(packet->vector, packet->data + 4, 16); + packet->tls = true; + /* * FIXME: Client MIB updates? */ @@ -764,6 +768,7 @@ int proxy_tls_send(rad_listen_t *listene * if there's no packet, encode it here. */ if (!request->proxy->data) { + request->reply->tls = true; request->proxy_listener->encode(request->proxy_listener, request); } Index: freeradius-server-3.0.16/src/lib/radius.c =================================================================== --- freeradius-server-3.0.16.orig/src/lib/radius.c +++ freeradius-server-3.0.16/src/lib/radius.c @@ -1740,6 +1740,7 @@ int rad_encode(RADIUS_PACKET *packet, RA uint16_t total_length; int len; VALUE_PAIR const *reply; + bool seen_ma = false; /* * A 4K packet, aligned on 64-bits. @@ -1804,6 +1805,27 @@ int rad_encode(RADIUS_PACKET *packet, RA */ /* + * Always add Message-Authenticator for replies to + * Access-Request packets. + * + * It must be the FIRST attribute in the packet. + */ + if (!packet->tls && + ((original && (original->code == PW_CODE_ACCESS_REQUEST)) || + (packet->code == PW_CODE_ACCESS_REQUEST))) { + seen_ma = true; + + packet->offset = RADIUS_HDR_LEN; + + ptr[0] = PW_MESSAGE_AUTHENTICATOR; + ptr[1] = 18; + memset(ptr + 2, 0, 16); + + ptr += 18; + total_length += 18; + } + + /* * Loop over the reply attributes for the packet. */ reply = packet->vps; @@ -1850,11 +1872,20 @@ int rad_encode(RADIUS_PACKET *packet, RA } } + room = ((uint8_t *) data) + sizeof(data) - ptr; + /* * Set the Message-Authenticator to the correct * length and initial value. */ if (!reply->da->vendor && (reply->da->attr == PW_MESSAGE_AUTHENTICATOR)) { + // don't encode Message-Authenticator twice + if (seen_ma) { + reply = reply->next; + continue; + } + if (room < 18) break; + /* * Cache the offset to the * Message-Authenticator @@ -1866,8 +1897,6 @@ int rad_encode(RADIUS_PACKET *packet, RA } last_name = reply->da->name; - room = ((uint8_t *) data) + sizeof(data) - ptr; - if (room <= 2) break; len = rad_vp2attr(packet, original, secret, &reply, ptr, room); @@ -2341,6 +2370,8 @@ bool rad_packet_ok(RADIUS_PACKET *packet 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; @@ -2391,13 +2422,14 @@ bool rad_packet_ok(RADIUS_PACKET *packet /* * Message-Authenticator is required in Status-Server * packets, otherwise they can be trivially forged. - */ - if (hdr->code == PW_CODE_STATUS_SERVER) require_ma = true; - - /* + * * It's also required if the caller asks for it. + * + * We only limit Proxy-State if we're not requiring + * Message-Authenticator. */ - if (flags) require_ma = true; + require_ma = ((flags & 0x01) != 0) || (hdr->code == PW_CODE_STATUS_SERVER); + limit_proxy_state = ((flags & 0x04) != 0) & !require_ma; /* * Repeat the length checks. This time, instead of @@ -2560,6 +2592,10 @@ bool rad_packet_ok(RADIUS_PACKET *packet non_eap = true; break; + case PW_PROXY_STATE: + seen_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", @@ -2631,6 +2667,18 @@ bool rad_packet_ok(RADIUS_PACKET *packet 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))); failure = DECODE_FAIL_MA_MISSING; goto finish; } Index: freeradius-server-3.0.16/raddb/proxy.conf =================================================================== --- freeradius-server-3.0.16.orig/raddb/proxy.conf +++ freeradius-server-3.0.16/raddb/proxy.conf @@ -204,6 +204,20 @@ 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 "no". + # + # 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 + # + #require_message_authenticator = no + + ############################################################ # # The rest of the configuration items listed here are optional, Index: freeradius-server-3.0.16/src/include/realms.h =================================================================== --- freeradius-server-3.0.16.orig/src/include/realms.h +++ freeradius-server-3.0.16/src/include/realms.h @@ -58,6 +58,8 @@ typedef struct home_server { //!< stats or when specifying home servers for a pool. bool dual; //!< One of a pair of homeservers on consecutive ports. + bool require_ma; //!< for all replies to Access-Request and Status-Server + char const *server; //!< For internal proxying char const *parent_server; Index: freeradius-server-3.0.16/src/main/process.c =================================================================== --- freeradius-server-3.0.16.orig/src/main/process.c +++ freeradius-server-3.0.16/src/main/process.c @@ -2489,11 +2489,32 @@ int request_proxy_reply(RADIUS_PACKET *p * ignore it. This does the MD5 calculations in the * server core, but I guess we can fix that later. */ - if (!request->proxy_reply && - (rad_verify(packet, request->proxy, - request->home_server->secret) != 0)) { - DEBUG("Ignoring spoofed proxy reply. Signature is invalid"); - return 0; + 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 && (request->proxy->code == PW_CODE_ACCESS_REQUEST); + + if (!request->home_server) { + proxy_reply_too_late(request); + return 0; + } + + if (!rad_packet_ok(packet, require_ma, &reason)) { + DEBUG("Ignoring invalid packet - %s", fr_strerror()); + return 0; + } + + if (rad_verify(packet, request->proxy, + request->home_server->secret) != 0) { + DEBUG("Ignoring spoofed proxy reply. Signature is invalid"); + return 0; + } } /* Index: freeradius-server-3.0.16/src/main/realms.c =================================================================== --- freeradius-server-3.0.16.orig/src/main/realms.c +++ freeradius-server-3.0.16/src/main/realms.c @@ -363,6 +363,7 @@ static CONF_PARSER home_server_coa[] = { #endif static CONF_PARSER home_server_config[] = { + { "require_message_authenticator", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_IGNORE_DEFAULT, home_server_t, require_ma), 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 }, @@ -636,6 +637,7 @@ home_server_t *home_server_afrom_cs(TALL home->cs = cs; home->state = HOME_STATE_UNKNOWN; home->proto = IPPROTO_UDP; + home->require_ma = main_config.require_ma; /* * Parse the configuration into the home server @@ -920,6 +922,11 @@ home_server_t *home_server_afrom_cs(TALL * Parse the SSL client configuration. */ if (tls) { + /* + * We don't require this for TLS connections. + */ + home->require_ma = false; + home->tls = tls_client_conf_parse(tls); if (!home->tls) { goto error; Index: freeradius-server-3.0.16/man/man1/radclient.1 =================================================================== --- freeradius-server-3.0.16.orig/man/man1/radclient.1 +++ freeradius-server-3.0.16/man/man1/radclient.1 @@ -5,6 +5,7 @@ radclient - send packets to a RADIUS ser .B radclient .RB [ \-4 ] .RB [ \-6 ] +.RB [ \-b ] .RB [ \-c .IR count ] .RB [ \-d @@ -52,6 +53,13 @@ automatically encrypted before the packe 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 Index: freeradius-server-3.0.16/src/main/radclient.c =================================================================== --- freeradius-server-3.0.16.orig/src/main/radclient.c +++ freeradius-server-3.0.16/src/main/radclient.c @@ -53,6 +53,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 issue (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"); @@ -989,6 +991,131 @@ static int send_one_packet(rc_request_t } /* + * 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. */ static int recv_one_packet(int wait_time) @@ -1066,6 +1193,20 @@ 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. */ @@ -1195,7 +1336,7 @@ int main(int argc, char **argv) exit(1); } - while ((c = getopt(argc, argv, "46c:d:D:f:Fhi:n:p:qr:sS:t:vx" + while ((c = getopt(argc, argv, "46bc:d:D:f:Fhi:n:p:qr:sS:t:vx" #ifdef WITH_TCP "P:" #endif @@ -1208,6 +1349,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();
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor