diff options
| author | Rohini Sangam <rsangam@mvista.com> | 2024-10-15 12:27:59 +0530 |
|---|---|---|
| committer | Armin Kuster <akuster808@gmail.com> | 2024-12-08 14:39:17 -0500 |
| commit | 61b0967009b1743637c8f02f21c2e3eef08d648e (patch) | |
| tree | e2e86a00fa5e23dc727728b3975ca7b3957ef63c /meta-networking/recipes-connectivity | |
| parent | 82a9ac867d2f661b77435e13842ab42a63a352ac (diff) | |
| download | meta-openembedded-61b0967009b1743637c8f02f21c2e3eef08d648e.tar.gz | |
freeradius: Security fix for CVE-2024-3596
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>
Signed-off-by: Armin Kuster <akuster808@gmail.com>
Diffstat (limited to 'meta-networking/recipes-connectivity')
| -rw-r--r-- | meta-networking/recipes-connectivity/freeradius/files/CVE-2024-3596.patch | 1506 | ||||
| -rw-r--r-- | meta-networking/recipes-connectivity/freeradius/freeradius_3.0.21.bb | 1 |
2 files changed, 1507 insertions, 0 deletions
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 0000000000..1778e8e927 --- /dev/null +++ b/meta-networking/recipes-connectivity/freeradius/files/CVE-2024-3596.patch | |||
| @@ -0,0 +1,1506 @@ | |||
| 1 | From 441967ba1d1ec28aa9582ab0253ad01e14b42148 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Arran Cudbard-Bell <a.cudbardb@freeradius.org> | ||
| 3 | Date: Sun, 30 Jun 2024 14:03:17 -0600 | ||
| 4 | Subject: [PATCH] CVE-2024-3596: Backport fix for BlastRADIUS | ||
| 5 | |||
| 6 | Upstream-Status: Backport from v3.0.x branch, commit range 3a00a6ecc188629b0441fd45ad61ca8986de156e..da643f1edc267ce95260dc36069e6f1a7a4d66f8 | ||
| 7 | CVE: CVE-2024-3596 | ||
| 8 | |||
| 9 | Signed-off-by: Rohini Sangam <rsangam@mvista.com> | ||
| 10 | --- | ||
| 11 | man/man1/radclient.1 | 10 ++- | ||
| 12 | man/man1/radtest.1 | 11 ++- | ||
| 13 | raddb/clients.conf | 47 ++++++++-- | ||
| 14 | raddb/proxy.conf | 19 +++++ | ||
| 15 | raddb/radiusd.conf.in | 185 ++++++++++++++++++++++++++++++++++++++++ | ||
| 16 | src/include/clients.h | 6 +- | ||
| 17 | src/include/conffile.h | 1 + | ||
| 18 | src/include/libradius.h | 19 ++++- | ||
| 19 | src/include/radius.h | 1 + | ||
| 20 | src/include/radiusd.h | 6 ++ | ||
| 21 | src/include/realms.h | 1 + | ||
| 22 | src/lib/radius.c | 87 +++++++++++++++++-- | ||
| 23 | src/main/client.c | 45 ++++++++-- | ||
| 24 | src/main/conffile.c | 4 +- | ||
| 25 | src/main/listen.c | 141 +++++++++++++++++++++++++++++- | ||
| 26 | src/main/mainconfig.c | 70 +++++++++++++++ | ||
| 27 | src/main/process.c | 65 ++++++++++++++ | ||
| 28 | src/main/radclient.c | 147 ++++++++++++++++++++++++++++++- | ||
| 29 | src/main/radtest.in | 6 +- | ||
| 30 | src/main/realms.c | 11 +++ | ||
| 31 | src/main/tls_listen.c | 5 ++ | ||
| 32 | 21 files changed, 855 insertions(+), 32 deletions(-) | ||
| 33 | |||
| 34 | diff --git a/man/man1/radclient.1 b/man/man1/radclient.1 | ||
| 35 | index 229dcae0c7..b83bee931a 100644 | ||
| 36 | --- a/man/man1/radclient.1 | ||
| 37 | +++ b/man/man1/radclient.1 | ||
| 38 | @@ -1,10 +1,11 @@ | ||
| 39 | -.TH RADCLIENT 1 "22 March 2019" "" "FreeRADIUS Daemon" | ||
| 40 | +.TH RADCLIENT 1 "21 May 2024" "" "FreeRADIUS Daemon" | ||
| 41 | .SH NAME | ||
| 42 | radclient - send packets to a RADIUS server, show reply | ||
| 43 | .SH SYNOPSIS | ||
| 44 | .B radclient | ||
| 45 | .RB [ \-4 ] | ||
| 46 | .RB [ \-6 ] | ||
| 47 | +.RB [ \-b ] | ||
| 48 | .RB [ \-c | ||
| 49 | .IR count ] | ||
| 50 | .RB [ \-d | ||
| 51 | @@ -52,6 +53,13 @@ automatically encrypted before the packet is sent to the server. | ||
| 52 | Use IPv4 (default) | ||
| 53 | .IP \-6 | ||
| 54 | Use IPv6 | ||
| 55 | +.IP \-b | ||
| 56 | +Enforce the Blast RADIUS checks. All replies to an Access-Request packet | ||
| 57 | +must contain a Message-Authenticator as the first attribute. | ||
| 58 | + | ||
| 59 | +For compatibility with old servers, this flag is not set by default. | ||
| 60 | +However, radclient still checks for the Blast RADIUS signature, and | ||
| 61 | +discards packets which match the attack. | ||
| 62 | .IP \-c\ \fIcount\fP | ||
| 63 | Send each packet \fIcount\fP times. | ||
| 64 | .IP \-d\ \fIraddb_directory\fP | ||
| 65 | diff --git a/man/man1/radtest.1 b/man/man1/radtest.1 | ||
| 66 | index b3184779c0..6bfab75944 100644 | ||
| 67 | --- a/man/man1/radtest.1 | ||
| 68 | +++ b/man/man1/radtest.1 | ||
| 69 | @@ -1,4 +1,4 @@ | ||
| 70 | -.TH RADTEST 1 "5 April 2010" "" "FreeRADIUS Daemon" | ||
| 71 | +.TH RADTEST 1 "21 May 2024" "" "FreeRADIUS Daemon" | ||
| 72 | .SH NAME | ||
| 73 | radtest - send packets to a RADIUS server, show reply | ||
| 74 | .SH SYNOPSIS | ||
| 75 | @@ -15,6 +15,8 @@ radtest - send packets to a RADIUS server, show reply | ||
| 76 | .IR ] | ||
| 77 | .RB [ \-6 | ||
| 78 | .IR ] | ||
| 79 | +.RB [ \-b | ||
| 80 | +.IR | ||
| 81 | .I user password radius-server nas-port-number secret | ||
| 82 | .RB [ ppphint ] | ||
| 83 | .RB [ nasname ] | ||
| 84 | @@ -26,6 +28,13 @@ way to test a radius server. | ||
| 85 | |||
| 86 | .SH OPTIONS | ||
| 87 | |||
| 88 | +.IP \-b | ||
| 89 | +Enforce the Blast RADIUS checks. All replies to an Access-Request packet | ||
| 90 | +must contain a Message-Authenticator as the first attribute. | ||
| 91 | + | ||
| 92 | +For compatibility with old servers, this flag is not set by default. | ||
| 93 | +However, radclient still checks for the Blast RADIUS signature, and | ||
| 94 | +discards packets which match the attack. | ||
| 95 | .IP "\-d \fIraddb_directory\fP" | ||
| 96 | The directory that contains the RADIUS dictionary files. Defaults to | ||
| 97 | \fI/etc/raddb\fP. | ||
| 98 | diff --git a/raddb/clients.conf b/raddb/clients.conf | ||
| 99 | index 76b300d3c5..d55414b7d2 100644 | ||
| 100 | --- a/raddb/clients.conf | ||
| 101 | +++ b/raddb/clients.conf | ||
| 102 | @@ -100,15 +100,44 @@ client localhost { | ||
| 103 | secret = testing123 | ||
| 104 | |||
| 105 | # | ||
| 106 | - # Old-style clients do not send a Message-Authenticator | ||
| 107 | - # in an Access-Request. RFC 5080 suggests that all clients | ||
| 108 | - # SHOULD include it in an Access-Request. The configuration | ||
| 109 | - # item below allows the server to require it. If a client | ||
| 110 | - # is required to include a Message-Authenticator and it does | ||
| 111 | - # not, then the packet will be silently discarded. | ||
| 112 | - # | ||
| 113 | - # allowed values: yes, no | ||
| 114 | - require_message_authenticator = no | ||
| 115 | + # The global configuration "security.require_message_authenticator" | ||
| 116 | + # flag sets the default for all clients. That default can be | ||
| 117 | + # over-ridden here, by setting it to a value. If no value is set, | ||
| 118 | + # then the default from the "radiusd.conf" file is used. | ||
| 119 | + # | ||
| 120 | + # See that file for full documentation on the flag, along | ||
| 121 | + # with allowed values and meanings. | ||
| 122 | + # | ||
| 123 | + # This flag exists solely for legacy clients which do not send | ||
| 124 | + # Message-Authenticator in all Access-Request packets. We do not | ||
| 125 | + # recommend setting it to "no". | ||
| 126 | + # | ||
| 127 | + # The number one way to protect yourself from the BlastRADIUS | ||
| 128 | + # attack is to update all RADIUS servers, and then set this | ||
| 129 | + # flag to "yes". If all RADIUS servers are updated, and if | ||
| 130 | + # all of them have this flag set to "yes" for all clients, | ||
| 131 | + # then your network is safe. You can then upgrade the | ||
| 132 | + # clients when it is convenient, instead of rushing the | ||
| 133 | + # upgrades. | ||
| 134 | + # | ||
| 135 | + # allowed values: yes, no, auto | ||
| 136 | +# require_message_authenticator = no | ||
| 137 | + | ||
| 138 | + # | ||
| 139 | + # The global configuration "security.limit_proxy_state" | ||
| 140 | + # flag sets the default for all clients. That default can be | ||
| 141 | + # over-ridden here, by setting it to "no". | ||
| 142 | + # | ||
| 143 | + # See that file for full documentation on the flag, along | ||
| 144 | + # with allowed values,and meanings. | ||
| 145 | + # | ||
| 146 | + # This flag exists solely for legacy clients which do not send | ||
| 147 | + # Message-Authenticator in all Access-Request packets. We do not | ||
| 148 | + # recommend setting it to "no". | ||
| 149 | + # | ||
| 150 | + # allowed values: yes, no, auto | ||
| 151 | + # | ||
| 152 | +# limit_proxy_state = yes | ||
| 153 | |||
| 154 | # | ||
| 155 | # The short name is used as an alias for the fully qualified | ||
| 156 | diff --git a/raddb/proxy.conf b/raddb/proxy.conf | ||
| 157 | index 91b4b37930..fa362b8a74 100644 | ||
| 158 | --- a/raddb/proxy.conf | ||
| 159 | +++ b/raddb/proxy.conf | ||
| 160 | @@ -204,6 +204,25 @@ home_server localhost { | ||
| 161 | # | ||
| 162 | secret = testing123 | ||
| 163 | |||
| 164 | + | ||
| 165 | + # | ||
| 166 | + # The global configuration "security.require_message_authenticator" | ||
| 167 | + # flag sets the default for all home servers. That default can be | ||
| 168 | + # over-ridden here, by setting it to a value. If no value is set, | ||
| 169 | + # then the default from the "radiusd.conf" file is used. | ||
| 170 | + # | ||
| 171 | + # See that file for full documentation on the flag, along | ||
| 172 | + # with allowed values and meanings. | ||
| 173 | + # | ||
| 174 | + # This flag exists solely for legacy home servers which do | ||
| 175 | + # not send Message-Authenticator in all Access-Accept, | ||
| 176 | + # Access-Reject, or Access-Challenge packets. We do not | ||
| 177 | + # recommend setting it to "no". | ||
| 178 | + # | ||
| 179 | + # allowed values: yes, no, auto | ||
| 180 | + # | ||
| 181 | +# require_message_authenticator = no | ||
| 182 | + | ||
| 183 | ############################################################ | ||
| 184 | # | ||
| 185 | # The rest of the configuration items listed here are optional, | ||
| 186 | diff --git a/raddb/radiusd.conf.in b/raddb/radiusd.conf.in | ||
| 187 | index e8aee3c001..5b8800bfc8 100644 | ||
| 188 | --- a/raddb/radiusd.conf.in | ||
| 189 | +++ b/raddb/radiusd.conf.in | ||
| 190 | @@ -564,6 +564,191 @@ security { | ||
| 191 | # | ||
| 192 | status_server = yes | ||
| 193 | |||
| 194 | + # | ||
| 195 | + # Global configuration for requiring Message-Authenticator in | ||
| 196 | + # all Access-* packets sent over UDP or TCP. This flag is | ||
| 197 | + # ignored for TLS. | ||
| 198 | + # | ||
| 199 | + # The number one way to protect yourself from the BlastRADIUS | ||
| 200 | + # attack is to update all RADIUS servers, and then set this | ||
| 201 | + # flag to "yes". If all RADIUS servers are updated, and if | ||
| 202 | + # all of them have this flag set to "yes" for all clients, | ||
| 203 | + # then your network is safe. You can then upgrade the | ||
| 204 | + # clients when it is convenient, instead of rushing the | ||
| 205 | + # upgrades. | ||
| 206 | + # | ||
| 207 | + # This flag sets the global default for all clients and home | ||
| 208 | + # servers. It can be over-ridden in an individual client or | ||
| 209 | + # home_server definition by adding the same flag to that | ||
| 210 | + # section with an appropriate value. | ||
| 211 | + # | ||
| 212 | + # All upgraded RADIUS implementations should send | ||
| 213 | + # Message-Authenticator in all Access-Request, Access-Accept, | ||
| 214 | + # Access-Reject, and Access-Challenge packets. Once all | ||
| 215 | + # systems are upgraded, setting this flag to "yes" is the | ||
| 216 | + # best protection from the attack. | ||
| 217 | + # | ||
| 218 | + # The possible values and meanings for | ||
| 219 | + # "require_message_authenticator" are; | ||
| 220 | + # | ||
| 221 | + # * "no" - allow Access-* packet which do not contain | ||
| 222 | + # Message-Authenticator | ||
| 223 | + # | ||
| 224 | + # For a client, if this flag is set to "no", then the | ||
| 225 | + # "limit_proxy_state" flag, below, is also checked. | ||
| 226 | + # | ||
| 227 | + # For a home_server, if this flag is set to "no", then the | ||
| 228 | + # Access-Accept, Access-Reject, and Access-Challenge | ||
| 229 | + # packets do not need to contain Message-Authenticator. | ||
| 230 | + # | ||
| 231 | + # The only reason to set this flag to "no" is when the | ||
| 232 | + # RADIUS client or home server has not been updated. It is | ||
| 233 | + # always safer to set this flag "no" in the individual | ||
| 234 | + # client or home_server definition. The global flag SHOULD | ||
| 235 | + # still be set to a safe value: "yes". | ||
| 236 | + # | ||
| 237 | + # WARNING: Setting this flag and the "limit_proxy_state" | ||
| 238 | + # flag to "no" will allow MITM attackers to create fake | ||
| 239 | + # Access-Accept packets to the NAS! At least one of them | ||
| 240 | + # MUST be set to "yes" for the system to have any | ||
| 241 | + # protection against the attack. | ||
| 242 | + # | ||
| 243 | + # * "yes" - Require that all Access-* packets (client and | ||
| 244 | + # home_server) contain Message-Authenticator. If a packet | ||
| 245 | + # does not contain Message-Authenticator, then it is | ||
| 246 | + # discarded. | ||
| 247 | + # | ||
| 248 | + # * "auto" - Automatically determine the value of the flag, | ||
| 249 | + # based on the first packet received from that client or | ||
| 250 | + # home_server. | ||
| 251 | + # | ||
| 252 | + # If the packet does not contain Message-Authenticator, | ||
| 253 | + # then the value of the flag is automatically switched to | ||
| 254 | + # "no". | ||
| 255 | + # | ||
| 256 | + # If the packet contains Message-Authenticator but not | ||
| 257 | + # EAP-Message, then the value of the flag is automatically | ||
| 258 | + # switched to "yes". The server has to check for | ||
| 259 | + # EAP-Message, because the previous RFCs require that the | ||
| 260 | + # packet contains Message-Authenticator when it also | ||
| 261 | + # contains EAP-Message. So having a Message-Authenticator | ||
| 262 | + # in those packets doesn't give the server enough | ||
| 263 | + # information to determined if the client or home_server | ||
| 264 | + # has been updated. | ||
| 265 | + # | ||
| 266 | + # If the packet contains Message-Authenticator and | ||
| 267 | + # EAP-Message, then the flag is left at the "auto" value. | ||
| 268 | + # | ||
| 269 | + # WARNING: This switch is done for the first packet | ||
| 270 | + # received from that client or home server. The change | ||
| 271 | + # does NOT persist across server restarts. You MUST change | ||
| 272 | + # the to "yes" manually, in order to make a permanent | ||
| 273 | + # change to the configuration. | ||
| 274 | + # | ||
| 275 | + # WARNING: If there are multiple NASes with the same source | ||
| 276 | + # IP and client definitions, BUT the NASes have different | ||
| 277 | + # behavior, then this flag WILL LIKELY BREAK YOUR NETWORK. | ||
| 278 | + # | ||
| 279 | + # That is, when there are multiple different RADIUS clients | ||
| 280 | + # behind one NATed IP address, then these security settings | ||
| 281 | + # have to be set to allow the MOST INSECURE packets to be | ||
| 282 | + # processed. This is a terrible idea, and will leave your | ||
| 283 | + # network vulnerable to the attack. Please upgrade all | ||
| 284 | + # clients immediately. | ||
| 285 | + # | ||
| 286 | + # The only solution to that rare configuration is to set | ||
| 287 | + # this flag to "no", in which case the network will work, | ||
| 288 | + # but will be vulnerable to the attack. | ||
| 289 | + # | ||
| 290 | + require_message_authenticator = auto | ||
| 291 | + | ||
| 292 | + # | ||
| 293 | + # Global configuration for limiting the combination of | ||
| 294 | + # Proxy-State and Message-Authenticator. This flag only | ||
| 295 | + # applies to packets sent over UDP or TCP. This flag is | ||
| 296 | + # ignored for TLS. | ||
| 297 | + # | ||
| 298 | + # This flag sets the global default for all clients. It can | ||
| 299 | + # be over-ridden in an individual client definition by adding | ||
| 300 | + # the same flag to that section with an appropriate value. | ||
| 301 | + # | ||
| 302 | + # If "require_message_authenticator" is set to "yes", this | ||
| 303 | + # configuration item is ignored. | ||
| 304 | + # | ||
| 305 | + # If "require_message_authenticator" is set to "no", this | ||
| 306 | + # configuration item is checked. | ||
| 307 | + # | ||
| 308 | + # The possible values and meanings for "limit_proxy_state" are; | ||
| 309 | + # | ||
| 310 | + # * "no" - allow any packets from the client, even packets | ||
| 311 | + # which contain the BlastRADIUS attack. Please be aware | ||
| 312 | + # that in this configuration the server will complain for | ||
| 313 | + # EVERY packet which it receives. | ||
| 314 | + # | ||
| 315 | + # The only reason to set this flag to "no" is when the | ||
| 316 | + # client is a proxy, AND the proxy does not send | ||
| 317 | + # Message-Authenticator in Access-Request packets. Even | ||
| 318 | + # then, the best approach to fix the issue is to (1) update | ||
| 319 | + # the proxy to send Message-Authenticator, and if that | ||
| 320 | + # can't be done, then (2) set this flag to "no", but ONLY | ||
| 321 | + # for that one client. The global flag SHOULD still be set | ||
| 322 | + # to a safe value: "yes". | ||
| 323 | + # | ||
| 324 | + # WARNING: Setting both this flag and the | ||
| 325 | + # "require_message_authenticator" flag to "no" will allow | ||
| 326 | + # MITM attackers to create fake Access-Accept packets to the | ||
| 327 | + # NAS! At least one of them MUST be set to "yes" for the | ||
| 328 | + # system to have any protection against the attack. | ||
| 329 | + # | ||
| 330 | + # * "yes" - Allow packets without Message-Authenticator, | ||
| 331 | + # but only when they do not contain Proxy-State. | ||
| 332 | + # packets which contain Proxy-State MUST also contain | ||
| 333 | + # Message-Authenticator, otherwise they are discarded. | ||
| 334 | + # | ||
| 335 | + # This setting is safe for most NASes, GGSNs, BRAS, etc. | ||
| 336 | + # Most regular RADIUS clients do not send Proxy-State | ||
| 337 | + # attributes for Access-Request packets that they originate. | ||
| 338 | + # However some aggregators (e.g. Wireless LAN Controllers) | ||
| 339 | + # may act as a RADIUS proxy for requests from their cohort | ||
| 340 | + # of managed devices, and in such cases will provide a | ||
| 341 | + # Proxy-State attribute. For those systems, you _must_ look | ||
| 342 | + # at the actual packets to determine what to do. It may be | ||
| 343 | + # that the only way to fix the vulnerability is to upgrade | ||
| 344 | + # the WLC, and set "require_message_authenticator" to "yes". | ||
| 345 | + # | ||
| 346 | + # * "auto" - Automatically determine the value of the flag, | ||
| 347 | + # based on the first packet received from that client. | ||
| 348 | + # | ||
| 349 | + # If the packet contains Proxy-State but no | ||
| 350 | + # Message-Authenticator, then the value of the flag is | ||
| 351 | + # automatically switched to "no". | ||
| 352 | + # | ||
| 353 | + # For all other situations, the value of the flag is | ||
| 354 | + # automatically switched to "yes". | ||
| 355 | + # | ||
| 356 | + # WARNING: This switch is done for the first packet | ||
| 357 | + # received from that client. The change does NOT persist | ||
| 358 | + # across server restarts. You MUST change the to "yes" | ||
| 359 | + # manually, in order to make a permanent change to the | ||
| 360 | + # configuration. | ||
| 361 | + # | ||
| 362 | + # WARNING: If there are multiple NASes with the same source | ||
| 363 | + # IP and client definitions, BUT the NASes have different | ||
| 364 | + # behavior, then this flag WILL LIKELY BREAK YOUR NETWORK. | ||
| 365 | + # | ||
| 366 | + # That is, when there are multiple different RADIUS clients | ||
| 367 | + # behind one NATed IP address, then these security settings | ||
| 368 | + # have to be set to allow the MOST INSECURE packets to be | ||
| 369 | + # processed. This is a terrible idea, and will leave your | ||
| 370 | + # network vulnerable to the attack. Please upgrade all | ||
| 371 | + # clients immediately. | ||
| 372 | + # | ||
| 373 | + # The only solution to that rare configuration is to set | ||
| 374 | + # this flag to "no", in which case the network will work, | ||
| 375 | + # but will be vulnerable to the attack. | ||
| 376 | + # | ||
| 377 | + limit_proxy_state = auto | ||
| 378 | + | ||
| 379 | @openssl_version_check_config@ | ||
| 380 | } | ||
| 381 | |||
| 382 | diff --git a/src/include/clients.h b/src/include/clients.h | ||
| 383 | index 560211557f..0aeb1da8d9 100644 | ||
| 384 | --- a/src/include/clients.h | ||
| 385 | +++ b/src/include/clients.h | ||
| 386 | @@ -39,7 +39,11 @@ typedef struct radclient { | ||
| 387 | |||
| 388 | char const *secret; //!< Secret PSK. | ||
| 389 | |||
| 390 | - bool message_authenticator; //!< Require RADIUS message authenticator in requests. | ||
| 391 | + fr_bool_auto_t require_ma; //!< Require RADIUS message authenticator in requests. | ||
| 392 | + | ||
| 393 | + bool dynamic_require_ma; //!< for dynamic clients | ||
| 394 | + | ||
| 395 | + fr_bool_auto_t limit_proxy_state; //!< Limit Proxy-State in requests | ||
| 396 | |||
| 397 | char const *nas_type; //!< Type of client (arbitrary). | ||
| 398 | |||
| 399 | diff --git a/src/include/conffile.h b/src/include/conffile.h | ||
| 400 | index 8cb045c946..ddbcae4e4f 100644 | ||
| 401 | --- a/src/include/conffile.h | ||
| 402 | +++ b/src/include/conffile.h | ||
| 403 | @@ -140,6 +140,7 @@ typedef struct timeval _timeval_t; | ||
| 404 | #define PW_TYPE_MULTI (1 << 18) //!< CONF_PAIR can have multiple copies. | ||
| 405 | #define PW_TYPE_NOT_EMPTY (1 << 19) //!< CONF_PAIR is required to have a non zero length value. | ||
| 406 | #define PW_TYPE_FILE_EXISTS ((1 << 20) | PW_TYPE_STRING) //!< File matching value must exist | ||
| 407 | +#define PW_TYPE_IGNORE_DEFAULT (1 << 21) //!< don't set from .dflt if the CONF_PAIR is missing | ||
| 408 | /* @} **/ | ||
| 409 | |||
| 410 | #define FR_INTEGER_COND_CHECK(_name, _var, _cond, _new)\ | ||
| 411 | diff --git a/src/include/libradius.h b/src/include/libradius.h | ||
| 412 | index ce2f713de1..2efef8b1d3 100644 | ||
| 413 | --- a/src/include/libradius.h | ||
| 414 | +++ b/src/include/libradius.h | ||
| 415 | @@ -402,6 +402,10 @@ typedef struct radius_packet { | ||
| 416 | size_t partial; | ||
| 417 | int proto; | ||
| 418 | #endif | ||
| 419 | + bool tls; //!< uses secure transport | ||
| 420 | + bool message_authenticator; | ||
| 421 | + bool proxy_state; | ||
| 422 | + bool eap_message; | ||
| 423 | } RADIUS_PACKET; | ||
| 424 | |||
| 425 | typedef enum { | ||
| 426 | @@ -507,6 +511,13 @@ DICT_VENDOR *dict_vendorbyvalue(int vendor); | ||
| 427 | /* radius.c */ | ||
| 428 | int rad_send(RADIUS_PACKET *, RADIUS_PACKET const *, char const *secret); | ||
| 429 | bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason); | ||
| 430 | + | ||
| 431 | +/* | ||
| 432 | + * 1 == require_ma | ||
| 433 | + * 2 == msg_peek | ||
| 434 | + * 4 == limit_proxy_state | ||
| 435 | + * 8 == require_ma for Access-* replies and Protocol-Error | ||
| 436 | + */ | ||
| 437 | RADIUS_PACKET *rad_recv(TALLOC_CTX *ctx, int fd, int flags); | ||
| 438 | ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, int *code); | ||
| 439 | void rad_recv_discard(int sockfd); | ||
| 440 | @@ -694,7 +705,7 @@ extern bool fr_dns_lookups; /* do IP -> hostname lookups? */ | ||
| 441 | extern bool fr_hostname_lookups; /* do hostname -> IP lookups? */ | ||
| 442 | extern int fr_debug_lvl; /* 0 = no debugging information */ | ||
| 443 | extern uint32_t fr_max_attributes; /* per incoming packet */ | ||
| 444 | -#define FR_MAX_PACKET_CODE (52) | ||
| 445 | +#define FR_MAX_PACKET_CODE (53) | ||
| 446 | extern char const *fr_packet_codes[FR_MAX_PACKET_CODE]; | ||
| 447 | #define is_radius_code(_x) ((_x > 0) && (_x < FR_MAX_PACKET_CODE)) | ||
| 448 | extern FILE *fr_log_fp; | ||
| 449 | @@ -932,6 +943,12 @@ int fr_socket_wait_for_connect(int sockfd, struct timeval *timeout); | ||
| 450 | } | ||
| 451 | #endif | ||
| 452 | |||
| 453 | +typedef enum { | ||
| 454 | + FR_BOOL_FALSE = 0, | ||
| 455 | + FR_BOOL_TRUE, | ||
| 456 | + FR_BOOL_AUTO, | ||
| 457 | +} fr_bool_auto_t; | ||
| 458 | + | ||
| 459 | #include <freeradius-devel/packet.h> | ||
| 460 | |||
| 461 | #ifdef WITH_TCP | ||
| 462 | diff --git a/src/include/radius.h b/src/include/radius.h | ||
| 463 | index 473528d65d..147d674eed 100644 | ||
| 464 | --- a/src/include/radius.h | ||
| 465 | +++ b/src/include/radius.h | ||
| 466 | @@ -61,6 +61,7 @@ typedef enum { | ||
| 467 | PW_CODE_COA_REQUEST = 43, //!< RFC3575/RFC5176 - CoA-Request | ||
| 468 | PW_CODE_COA_ACK = 44, //!< RFC3575/RFC5176 - CoA-Ack (positive) | ||
| 469 | PW_CODE_COA_NAK = 45, //!< RFC3575/RFC5176 - CoA-Nak (not willing to perform) | ||
| 470 | + PW_CODE_PROTOCOL_ERROR = 52, //!< RFC7930 - Protocol layer issue | ||
| 471 | PW_CODE_MAX = 255, //!< Maximum possible code | ||
| 472 | } PW_CODE; | ||
| 473 | |||
| 474 | diff --git a/src/include/radiusd.h b/src/include/radiusd.h | ||
| 475 | index b2a0a0f642..e429c5be7a 100644 | ||
| 476 | --- a/src/include/radiusd.h | ||
| 477 | +++ b/src/include/radiusd.h | ||
| 478 | @@ -171,6 +171,10 @@ typedef struct main_config { | ||
| 479 | |||
| 480 | bool exiting; //!< are we exiting? | ||
| 481 | |||
| 482 | + fr_bool_auto_t require_ma; //!< global configuration for all clients and home servers | ||
| 483 | + | ||
| 484 | + fr_bool_auto_t limit_proxy_state; //!< global configuration for all clients | ||
| 485 | + | ||
| 486 | |||
| 487 | #ifdef ENABLE_OPENSSL_VERSION_CHECK | ||
| 488 | char const *allow_vulnerable_openssl; //!< The CVE number of the last security issue acknowledged. | ||
| 489 | @@ -558,6 +562,8 @@ int main_config_free(void); | ||
| 490 | void main_config_hup(void); | ||
| 491 | void hup_logfile(void); | ||
| 492 | |||
| 493 | +int fr_bool_auto_parse(CONF_PAIR *cp, fr_bool_auto_t *out, char const *str); | ||
| 494 | + | ||
| 495 | /* listen.c */ | ||
| 496 | void listen_free(rad_listen_t **head); | ||
| 497 | int listen_init(CONF_SECTION *cs, rad_listen_t **head, bool spawn_flag); | ||
| 498 | diff --git a/src/include/realms.h b/src/include/realms.h | ||
| 499 | index 6dae8b4f85..e643818e43 100644 | ||
| 500 | --- a/src/include/realms.h | ||
| 501 | +++ b/src/include/realms.h | ||
| 502 | @@ -59,6 +59,7 @@ typedef struct home_server { | ||
| 503 | //!< stats or when specifying home servers for a pool. | ||
| 504 | |||
| 505 | bool dual; //!< One of a pair of homeservers on consecutive ports. | ||
| 506 | + fr_bool_auto_t require_ma; //!< for all replies to Access-Request and Status-Server | ||
| 507 | char const *server; //!< For internal proxying | ||
| 508 | char const *parent_server; | ||
| 509 | |||
| 510 | diff --git a/src/lib/radius.c b/src/lib/radius.c | ||
| 511 | index 3881111f7d..7b91a4bde2 100644 | ||
| 512 | --- a/src/lib/radius.c | ||
| 513 | +++ b/src/lib/radius.c | ||
| 514 | @@ -142,8 +142,9 @@ char const *fr_packet_codes[FR_MAX_PACKET_CODE] = { | ||
| 515 | "47", | ||
| 516 | "48", | ||
| 517 | "49", | ||
| 518 | - "IP-Address-Allocate", | ||
| 519 | - "IP-Address-Release", //!< 50 | ||
| 520 | + "IP-Address-Allocate", //!< 50 | ||
| 521 | + "IP-Address-Release", | ||
| 522 | + "Protocol-Error", | ||
| 523 | }; | ||
| 524 | |||
| 525 | |||
| 526 | @@ -1700,6 +1701,15 @@ int rad_vp2attr(RADIUS_PACKET const *packet, RADIUS_PACKET const *original, | ||
| 527 | return rad_vp2vsa(packet, original, secret, pvp, start, room); | ||
| 528 | } | ||
| 529 | |||
| 530 | +static const bool code2ma[FR_MAX_PACKET_CODE] = { | ||
| 531 | + [ PW_CODE_ACCESS_REQUEST ] = true, | ||
| 532 | + [ PW_CODE_ACCESS_ACCEPT ] = true, | ||
| 533 | + [ PW_CODE_ACCESS_REJECT ] = true, | ||
| 534 | + [ PW_CODE_ACCESS_CHALLENGE ] = true, | ||
| 535 | + [ PW_CODE_STATUS_SERVER ] = true, | ||
| 536 | + [ PW_CODE_PROTOCOL_ERROR ] = true, | ||
| 537 | +}; | ||
| 538 | + | ||
| 539 | |||
| 540 | /** Encode a packet | ||
| 541 | * | ||
| 542 | @@ -1712,6 +1722,7 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, | ||
| 543 | uint16_t total_length; | ||
| 544 | int len; | ||
| 545 | VALUE_PAIR const *reply; | ||
| 546 | + bool seen_ma = false; | ||
| 547 | |||
| 548 | /* | ||
| 549 | * A 4K packet, aligned on 64-bits. | ||
| 550 | @@ -1775,6 +1786,27 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, | ||
| 551 | * memcpy. | ||
| 552 | */ | ||
| 553 | |||
| 554 | + /* | ||
| 555 | + * Always add Message-Authenticator for replies to | ||
| 556 | + * Access-Request packets, and for all Access-Accept, | ||
| 557 | + * Access-Reject, Access-Challenge. | ||
| 558 | + * | ||
| 559 | + * It must be the FIRST attribute in the packet. | ||
| 560 | + */ | ||
| 561 | + if (!packet->tls && | ||
| 562 | + ((code2ma[packet->code]) || (original && code2ma[original->code]))) { | ||
| 563 | + seen_ma = true; | ||
| 564 | + | ||
| 565 | + packet->offset = RADIUS_HDR_LEN; | ||
| 566 | + | ||
| 567 | + ptr[0] = PW_MESSAGE_AUTHENTICATOR; | ||
| 568 | + ptr[1] = 18; | ||
| 569 | + memset(ptr + 2, 0, 16); | ||
| 570 | + | ||
| 571 | + ptr += 18; | ||
| 572 | + total_length += 18; | ||
| 573 | + } | ||
| 574 | + | ||
| 575 | /* | ||
| 576 | * Loop over the reply attributes for the packet. | ||
| 577 | */ | ||
| 578 | @@ -1832,6 +1864,13 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, | ||
| 579 | * length and initial value. | ||
| 580 | */ | ||
| 581 | if (!reply->da->vendor && (reply->da->attr == PW_MESSAGE_AUTHENTICATOR)) { | ||
| 582 | + /* | ||
| 583 | + * We have already encoded the Message-Authenticator, don't do it again. | ||
| 584 | + */ | ||
| 585 | + if (seen_ma) { | ||
| 586 | + reply = reply->next; | ||
| 587 | + continue; | ||
| 588 | + } | ||
| 589 | if (room < 18) break; | ||
| 590 | |||
| 591 | /* | ||
| 592 | @@ -2323,6 +2362,8 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) | ||
| 593 | radius_packet_t *hdr; | ||
| 594 | char host_ipaddr[128]; | ||
| 595 | bool require_ma = false; | ||
| 596 | + bool limit_proxy_state = false; | ||
| 597 | + bool seen_proxy_state = false; | ||
| 598 | bool seen_ma = false; | ||
| 599 | uint32_t num_attributes; | ||
| 600 | decode_fail_t failure = DECODE_FAIL_NONE; | ||
| 601 | @@ -2371,15 +2412,26 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) | ||
| 602 | } | ||
| 603 | |||
| 604 | /* | ||
| 605 | - * Message-Authenticator is required in Status-Server | ||
| 606 | - * packets, otherwise they can be trivially forged. | ||
| 607 | + * If the caller requires Message-Authenticator, then set | ||
| 608 | + * the flag. | ||
| 609 | */ | ||
| 610 | - if (hdr->code == PW_CODE_STATUS_SERVER) require_ma = true; | ||
| 611 | |||
| 612 | /* | ||
| 613 | - * It's also required if the caller asks for it. | ||
| 614 | + * We also require Message-Authenticator if the packet | ||
| 615 | + * code is Status-Server. | ||
| 616 | + * | ||
| 617 | + * If we're receiving packets from a proxy socket, then | ||
| 618 | + * require Message-Authenticator for Access-* replies, | ||
| 619 | + * and for Protocol-Error. | ||
| 620 | */ | ||
| 621 | - if (flags) require_ma = true; | ||
| 622 | + require_ma = ((flags & 0x01) != 0) || (hdr->code == PW_CODE_STATUS_SERVER) || (((flags & 0x08) != 0) && code2ma[hdr->code]); | ||
| 623 | + | ||
| 624 | + /* | ||
| 625 | + * | ||
| 626 | + * We only limit Proxy-State if we're not requiring | ||
| 627 | + * Message-Authenticator. | ||
| 628 | + */ | ||
| 629 | + limit_proxy_state = ((flags & 0x04) != 0) && !require_ma; | ||
| 630 | |||
| 631 | /* | ||
| 632 | * Repeat the length checks. This time, instead of | ||
| 633 | @@ -2534,6 +2586,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) | ||
| 634 | case PW_EAP_MESSAGE: | ||
| 635 | require_ma = true; | ||
| 636 | eap = true; | ||
| 637 | + packet->eap_message = true; | ||
| 638 | break; | ||
| 639 | |||
| 640 | case PW_USER_PASSWORD: | ||
| 641 | @@ -2542,6 +2595,11 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) | ||
| 642 | non_eap = true; | ||
| 643 | break; | ||
| 644 | |||
| 645 | + case PW_PROXY_STATE: | ||
| 646 | + seen_proxy_state = true; | ||
| 647 | + packet->proxy_state = true; | ||
| 648 | + break; | ||
| 649 | + | ||
| 650 | case PW_MESSAGE_AUTHENTICATOR: | ||
| 651 | if (attr[1] != 2 + AUTH_VECTOR_LEN) { | ||
| 652 | FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d", | ||
| 653 | @@ -2553,6 +2611,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) | ||
| 654 | goto finish; | ||
| 655 | } | ||
| 656 | seen_ma = true; | ||
| 657 | + packet->message_authenticator = true; | ||
| 658 | break; | ||
| 659 | } | ||
| 660 | |||
| 661 | @@ -2609,7 +2668,19 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) | ||
| 662 | * Message-Authenticator attributes. | ||
| 663 | */ | ||
| 664 | if (require_ma && !seen_ma) { | ||
| 665 | - FR_DEBUG_STRERROR_PRINTF("Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute", | ||
| 666 | + 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.", | ||
| 667 | + inet_ntop(packet->src_ipaddr.af, | ||
| 668 | + &packet->src_ipaddr.ipaddr, | ||
| 669 | + host_ipaddr, sizeof(host_ipaddr))); | ||
| 670 | + failure = DECODE_FAIL_MA_MISSING; | ||
| 671 | + goto finish; | ||
| 672 | + } | ||
| 673 | + | ||
| 674 | + /* | ||
| 675 | + * The client is a NAS which shouldn't send Proxy-State, but it did! | ||
| 676 | + */ | ||
| 677 | + if (limit_proxy_state && seen_proxy_state && !seen_ma) { | ||
| 678 | + 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", | ||
| 679 | inet_ntop(packet->src_ipaddr.af, | ||
| 680 | &packet->src_ipaddr.ipaddr, | ||
| 681 | host_ipaddr, sizeof(host_ipaddr))); | ||
| 682 | diff --git a/src/main/client.c b/src/main/client.c | ||
| 683 | index 6228438c47..875dc37d60 100644 | ||
| 684 | --- a/src/main/client.c | ||
| 685 | +++ b/src/main/client.c | ||
| 686 | @@ -283,7 +283,8 @@ bool client_add(RADCLIENT_LIST *clients, RADCLIENT *client) | ||
| 687 | (old->coa_server == client->coa_server) && | ||
| 688 | (old->coa_pool == client->coa_pool) && | ||
| 689 | #endif | ||
| 690 | - (old->message_authenticator == client->message_authenticator)) { | ||
| 691 | + (old->require_ma == client->require_ma) && | ||
| 692 | + (old->limit_proxy_state == client->limit_proxy_state)) { | ||
| 693 | WARN("Ignoring duplicate client %s", client->longname); | ||
| 694 | client_free(client); | ||
| 695 | return true; | ||
| 696 | @@ -445,6 +446,8 @@ static fr_ipaddr_t cl_ipaddr; | ||
| 697 | static uint32_t cl_netmask; | ||
| 698 | static char const *cl_srcipaddr = NULL; | ||
| 699 | static char const *hs_proto = NULL; | ||
| 700 | +static char const *require_message_authenticator = NULL; | ||
| 701 | +static char const *limit_proxy_state = NULL; | ||
| 702 | |||
| 703 | #ifdef WITH_TCP | ||
| 704 | static CONF_PARSER limit_config[] = { | ||
| 705 | @@ -467,7 +470,8 @@ static const CONF_PARSER client_config[] = { | ||
| 706 | |||
| 707 | { "src_ipaddr", FR_CONF_POINTER(PW_TYPE_STRING, &cl_srcipaddr), NULL }, | ||
| 708 | |||
| 709 | - { "require_message_authenticator", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), "no" }, | ||
| 710 | + { "require_message_authenticator", FR_CONF_POINTER(PW_TYPE_STRING| PW_TYPE_IGNORE_DEFAULT, &require_message_authenticator), NULL }, | ||
| 711 | + { "limit_proxy_state", FR_CONF_POINTER(PW_TYPE_STRING| PW_TYPE_IGNORE_DEFAULT, &limit_proxy_state), NULL }, | ||
| 712 | |||
| 713 | { "secret", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, RADCLIENT, secret), NULL }, | ||
| 714 | { "shortname", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, shortname), NULL }, | ||
| 715 | @@ -663,7 +667,7 @@ static const CONF_PARSER dynamic_config[] = { | ||
| 716 | { "FreeRADIUS-Client-Src-IP-Address", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, RADCLIENT, src_ipaddr), NULL }, | ||
| 717 | { "FreeRADIUS-Client-Src-IPv6-Address", FR_CONF_OFFSET(PW_TYPE_IPV6_ADDR, RADCLIENT, src_ipaddr), NULL }, | ||
| 718 | |||
| 719 | - { "FreeRADIUS-Client-Require-MA", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), NULL }, | ||
| 720 | + { "FreeRADIUS-Client-Require-MA", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, dynamic_require_ma), NULL }, | ||
| 721 | |||
| 722 | { "FreeRADIUS-Client-Secret", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, secret), "" }, | ||
| 723 | { "FreeRADIUS-Client-Shortname", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, shortname), "" }, | ||
| 724 | @@ -845,8 +849,19 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bo | ||
| 725 | c = talloc_zero(ctx, RADCLIENT); | ||
| 726 | c->cs = cs; | ||
| 727 | |||
| 728 | + /* | ||
| 729 | + * Set the "require message authenticator" and "limit | ||
| 730 | + * proxy state" flags from the global default. If the | ||
| 731 | + * configuration item exists, AND is set, it will | ||
| 732 | + * over-ride the flag. | ||
| 733 | + */ | ||
| 734 | + c->require_ma = main_config.require_ma; | ||
| 735 | + c->limit_proxy_state = main_config.limit_proxy_state; | ||
| 736 | + | ||
| 737 | memset(&cl_ipaddr, 0, sizeof(cl_ipaddr)); | ||
| 738 | cl_netmask = 255; | ||
| 739 | + require_message_authenticator = NULL; | ||
| 740 | + limit_proxy_state = NULL; | ||
| 741 | |||
| 742 | if (cf_section_parse(cs, c, client_config) < 0) { | ||
| 743 | cf_log_err_cs(cs, "Error parsing client section"); | ||
| 744 | @@ -857,6 +872,9 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bo | ||
| 745 | cl_srcipaddr = NULL; | ||
| 746 | #endif | ||
| 747 | |||
| 748 | + require_message_authenticator = NULL; | ||
| 749 | + limit_proxy_state = NULL; | ||
| 750 | + | ||
| 751 | return NULL; | ||
| 752 | } | ||
| 753 | |||
| 754 | @@ -1114,6 +1132,16 @@ done_coa: | ||
| 755 | } | ||
| 756 | #endif | ||
| 757 | |||
| 758 | + if (fr_bool_auto_parse(cf_pair_find(cs, "require_message_authenticator"), &c->require_ma, require_message_authenticator) < 0) { | ||
| 759 | + goto error; | ||
| 760 | + } | ||
| 761 | + | ||
| 762 | + if (c->require_ma != FR_BOOL_TRUE) { | ||
| 763 | + if (fr_bool_auto_parse(cf_pair_find(cs, "limit_proxy_state"), &c->limit_proxy_state, limit_proxy_state) < 0) { | ||
| 764 | + goto error; | ||
| 765 | + } | ||
| 766 | + } | ||
| 767 | + | ||
| 768 | return c; | ||
| 769 | } | ||
| 770 | |||
| 771 | @@ -1158,7 +1186,7 @@ RADCLIENT *client_afrom_query(TALLOC_CTX *ctx, char const *identifier, char cons | ||
| 772 | if (shortname) c->shortname = talloc_typed_strdup(c, shortname); | ||
| 773 | if (type) c->nas_type = talloc_typed_strdup(c, type); | ||
| 774 | if (server) c->server = talloc_typed_strdup(c, server); | ||
| 775 | - c->message_authenticator = require_ma; | ||
| 776 | + c->require_ma = require_ma; | ||
| 777 | |||
| 778 | return c; | ||
| 779 | } | ||
| 780 | @@ -1344,10 +1372,10 @@ RADCLIENT *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request) | ||
| 781 | *pi = vp->vp_integer; | ||
| 782 | |||
| 783 | /* | ||
| 784 | - * Same nastiness as above. | ||
| 785 | + * Same nastiness as above, but hard-coded for require Message-Authenticator. | ||
| 786 | */ | ||
| 787 | for (parse = client_config; parse->name; parse++) { | ||
| 788 | - if (parse->offset == dynamic_config[i].offset) break; | ||
| 789 | + if (parse->type == PW_TYPE_BOOLEAN) break; | ||
| 790 | } | ||
| 791 | if (!parse) break; | ||
| 792 | |||
| 793 | @@ -1436,6 +1464,11 @@ validate: | ||
| 794 | goto error; | ||
| 795 | } | ||
| 796 | |||
| 797 | + /* | ||
| 798 | + * It can't be set to "auto". Too bad. | ||
| 799 | + */ | ||
| 800 | + c->require_ma = (fr_bool_auto_t) c->dynamic_require_ma; | ||
| 801 | + | ||
| 802 | if (!client_add_dynamic(clients, request->client, c)) { | ||
| 803 | return NULL; | ||
| 804 | } | ||
| 805 | diff --git a/src/main/conffile.c b/src/main/conffile.c | ||
| 806 | index a8c667bfb5..61754e991f 100644 | ||
| 807 | --- a/src/main/conffile.c | ||
| 808 | +++ b/src/main/conffile.c | ||
| 809 | @@ -1418,6 +1418,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d | ||
| 810 | { | ||
| 811 | int rcode; | ||
| 812 | bool deprecated, required, attribute, secret, file_input, cant_be_empty, tmpl, multi, file_exists; | ||
| 813 | + bool ignore_dflt; | ||
| 814 | char **q; | ||
| 815 | char const *value; | ||
| 816 | CONF_PAIR *cp = NULL; | ||
| 817 | @@ -1441,6 +1442,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d | ||
| 818 | cant_be_empty = (type & PW_TYPE_NOT_EMPTY); | ||
| 819 | tmpl = (type & PW_TYPE_TMPL); | ||
| 820 | multi = (type & PW_TYPE_MULTI); | ||
| 821 | + ignore_dflt = (type & PW_TYPE_IGNORE_DEFAULT); | ||
| 822 | |||
| 823 | if (attribute) required = true; | ||
| 824 | if (required) cant_be_empty = true; /* May want to review this in the future... */ | ||
| 825 | @@ -1464,7 +1466,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d | ||
| 826 | * section, use the default value. | ||
| 827 | */ | ||
| 828 | if (!cp) { | ||
| 829 | - if (deprecated) return 0; /* Don't set the default value */ | ||
| 830 | + if (deprecated || ignore_dflt) return 0; /* Don't set the default value */ | ||
| 831 | |||
| 832 | rcode = 1; | ||
| 833 | value = dflt; | ||
| 834 | diff --git a/src/main/listen.c b/src/main/listen.c | ||
| 835 | index ebf7f5221c..c20fea243d 100644 | ||
| 836 | --- a/src/main/listen.c | ||
| 837 | +++ b/src/main/listen.c | ||
| 838 | @@ -456,6 +456,122 @@ int rad_status_server(REQUEST *request) | ||
| 839 | return 0; | ||
| 840 | } | ||
| 841 | |||
| 842 | +static void blastradius_checks(RADIUS_PACKET *packet, RADCLIENT *client) | ||
| 843 | +{ | ||
| 844 | + if (client->require_ma == FR_BOOL_TRUE) return; | ||
| 845 | + | ||
| 846 | + if (client->require_ma == FR_BOOL_AUTO) { | ||
| 847 | + if (!packet->message_authenticator) { | ||
| 848 | + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 849 | + ERROR("BlastRADIUS check: Received packet without Message-Authenticator."); | ||
| 850 | + ERROR("Setting \"require_message_authenticator = false\" for client %s", client->shortname); | ||
| 851 | + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 852 | + ERROR("UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK."); | ||
| 853 | + ERROR("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname); | ||
| 854 | + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 855 | + client->require_ma = FR_BOOL_FALSE; | ||
| 856 | + | ||
| 857 | + /* | ||
| 858 | + * And fall through to the | ||
| 859 | + * limit_proxy_state checks, which might | ||
| 860 | + * complain again. Oh well, maybe that | ||
| 861 | + * will make people read the messages. | ||
| 862 | + */ | ||
| 863 | + | ||
| 864 | + } else if (packet->eap_message) { | ||
| 865 | + /* | ||
| 866 | + * Don't set it to "true" for packets | ||
| 867 | + * with EAP-Message. It's already | ||
| 868 | + * required there, and we might get a | ||
| 869 | + * non-EAP packet with (or without) | ||
| 870 | + * Message-Authenticator | ||
| 871 | + */ | ||
| 872 | + return; | ||
| 873 | + } else { | ||
| 874 | + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 875 | + ERROR("BlastRADIUS check: Received packet with Message-Authenticator."); | ||
| 876 | + ERROR("Setting \"require_message_authenticator = true\" for client %s", client->shortname); | ||
| 877 | + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 878 | + ERROR("It looks like the client has been updated to protect from the BlastRADIUS attack."); | ||
| 879 | + ERROR("Please set \"require_message_authenticator = true\" for client %s", client->shortname); | ||
| 880 | + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 881 | + | ||
| 882 | + client->require_ma = FR_BOOL_TRUE; | ||
| 883 | + return; | ||
| 884 | + } | ||
| 885 | + | ||
| 886 | + } | ||
| 887 | + | ||
| 888 | + /* | ||
| 889 | + * If all of the checks are turned off, then complain for every packet we receive. | ||
| 890 | + */ | ||
| 891 | + if (client->limit_proxy_state == FR_BOOL_FALSE) { | ||
| 892 | + /* | ||
| 893 | + * We have a Message-Authenticator, and it's valid. We don't need to compain. | ||
| 894 | + */ | ||
| 895 | + if (!fr_debug_lvl) return; /* easier than checking for each line below */ | ||
| 896 | + | ||
| 897 | + DEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 898 | + DEBUG("BlastRADIUS check: Received packet without Message-Authenticator."); | ||
| 899 | + DEBUG("YOU MUST SET \"require_message_authenticator = true\", or"); | ||
| 900 | + DEBUG("YOU MUST SET \"limit_proxy_state = true\" for client %s", client->shortname); | ||
| 901 | + DEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 902 | + DEBUG("The packet does not contain Message-Authenticator, which is a security issue"); | ||
| 903 | + DEBUG("UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK."); | ||
| 904 | + DEBUG("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname); | ||
| 905 | + DEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 906 | + return; | ||
| 907 | + } | ||
| 908 | + | ||
| 909 | + /* | ||
| 910 | + * Don't complain here. rad_packet_ok() will instead | ||
| 911 | + * complain about every packet with Proxy-State but which | ||
| 912 | + * is missing Message-Authenticator. | ||
| 913 | + */ | ||
| 914 | + if (client->limit_proxy_state == FR_BOOL_TRUE) { | ||
| 915 | + return; | ||
| 916 | + } | ||
| 917 | + | ||
| 918 | + if (packet->proxy_state && !packet->message_authenticator) { | ||
| 919 | + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 920 | + ERROR("BlastRADIUS check: Received packet with Proxy-State, but without Message-Authenticator."); | ||
| 921 | + ERROR("This is either a BlastRADIUS attack, OR"); | ||
| 922 | + ERROR("the client is a proxy RADIUS server which has not been upgraded."); | ||
| 923 | + ERROR("Setting \"limit_proxy_state = false\" for client %s", client->shortname); | ||
| 924 | + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 925 | + ERROR("UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK."); | ||
| 926 | + DEBUG("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname); | ||
| 927 | + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 928 | + | ||
| 929 | + client->limit_proxy_state = FR_BOOL_FALSE; | ||
| 930 | + | ||
| 931 | + } else { | ||
| 932 | + client->limit_proxy_state = FR_BOOL_TRUE; | ||
| 933 | + | ||
| 934 | + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 935 | + if (!packet->proxy_state) { | ||
| 936 | + ERROR("BlastRADIUS check: Received packet without Proxy-State."); | ||
| 937 | + } else { | ||
| 938 | + ERROR("BlastRADIUS check: Received packet with Proxy-State and Message-Authenticator."); | ||
| 939 | + } | ||
| 940 | + | ||
| 941 | + ERROR("Setting \"limit_proxy_state = true\" for client %s", client->shortname); | ||
| 942 | + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 943 | + | ||
| 944 | + if (!packet->message_authenticator) { | ||
| 945 | + ERROR("The packet does not contain Message-Authenticator, which is a security issue."); | ||
| 946 | + ERROR("UPGRADE THE CLIENT AS YOUR NETWORK MAY BE VULNERABLE TO THE BLASTRADIUS ATTACK."); | ||
| 947 | + DEBUG("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname); | ||
| 948 | + } else { | ||
| 949 | + ERROR("The packet contains Message-Authenticator."); | ||
| 950 | + if (!packet->eap_message) ERROR("The client has likely been upgraded to protect from the attack."); | ||
| 951 | + ERROR("Please set \"require_message_authenticator = true\" for client %s", client->shortname); | ||
| 952 | + } | ||
| 953 | + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 954 | + } | ||
| 955 | +} | ||
| 956 | + | ||
| 957 | + | ||
| 958 | #ifdef WITH_TCP | ||
| 959 | static int dual_tcp_recv(rad_listen_t *listener) | ||
| 960 | { | ||
| 961 | @@ -532,6 +648,21 @@ static int dual_tcp_recv(rad_listen_t *listener) | ||
| 962 | switch (packet->code) { | ||
| 963 | case PW_CODE_ACCESS_REQUEST: | ||
| 964 | if (listener->type != RAD_LISTEN_AUTH) goto bad_packet; | ||
| 965 | + | ||
| 966 | + /* | ||
| 967 | + * Enforce BlastRADIUS checks on TCP, too. | ||
| 968 | + */ | ||
| 969 | + if (!rad_packet_ok(packet, (client->require_ma == FR_BOOL_TRUE) | ((client->limit_proxy_state == FR_BOOL_TRUE) << 2), NULL)) { | ||
| 970 | + FR_STATS_INC(auth, total_malformed_requests); | ||
| 971 | + rad_free(&sock->packet); | ||
| 972 | + return 0; | ||
| 973 | + } | ||
| 974 | + | ||
| 975 | + /* | ||
| 976 | + * Perform BlastRADIUS checks and warnings. | ||
| 977 | + */ | ||
| 978 | + if (packet->code == PW_CODE_ACCESS_REQUEST) blastradius_checks(packet, client); | ||
| 979 | + | ||
| 980 | FR_STATS_INC(auth, total_requests); | ||
| 981 | fun = rad_authenticate; | ||
| 982 | break; | ||
| 983 | @@ -1562,7 +1693,7 @@ static int auth_socket_recv(rad_listen_t *listener) | ||
| 984 | * Now that we've sanity checked everything, receive the | ||
| 985 | * packet. | ||
| 986 | */ | ||
| 987 | - packet = rad_recv(ctx, listener->fd, client->message_authenticator); | ||
| 988 | + packet = rad_recv(ctx, listener->fd, (client->require_ma == FR_BOOL_TRUE) | ((client->limit_proxy_state == FR_BOOL_TRUE) << 2)); | ||
| 989 | if (!packet) { | ||
| 990 | FR_STATS_INC(auth, total_malformed_requests); | ||
| 991 | if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror()); | ||
| 992 | @@ -1570,6 +1701,12 @@ static int auth_socket_recv(rad_listen_t *listener) | ||
| 993 | return 0; | ||
| 994 | } | ||
| 995 | |||
| 996 | + | ||
| 997 | + /* | ||
| 998 | + * Perform BlastRADIUS checks and warnings. | ||
| 999 | + */ | ||
| 1000 | + if (packet->code == PW_CODE_ACCESS_REQUEST) blastradius_checks(packet, client); | ||
| 1001 | + | ||
| 1002 | #ifdef __APPLE__ | ||
| 1003 | #ifdef WITH_UDPFROMTO | ||
| 1004 | /* | ||
| 1005 | @@ -1955,7 +2092,7 @@ static int coa_socket_recv(rad_listen_t *listener) | ||
| 1006 | * Now that we've sanity checked everything, receive the | ||
| 1007 | * packet. | ||
| 1008 | */ | ||
| 1009 | - packet = rad_recv(ctx, listener->fd, client->message_authenticator); | ||
| 1010 | + packet = rad_recv(ctx, listener->fd, client->require_ma | (((int) client->limit_proxy_state) << 2)); | ||
| 1011 | if (!packet) { | ||
| 1012 | FR_STATS_INC(coa, total_malformed_requests); | ||
| 1013 | if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror()); | ||
| 1014 | diff --git a/src/main/mainconfig.c b/src/main/mainconfig.c | ||
| 1015 | index e9dd412dee..520d7fa474 100644 | ||
| 1016 | --- a/src/main/mainconfig.c | ||
| 1017 | +++ b/src/main/mainconfig.c | ||
| 1018 | @@ -73,6 +73,8 @@ static char const *gid_name = NULL; | ||
| 1019 | static char const *chroot_dir = NULL; | ||
| 1020 | static bool allow_core_dumps = false; | ||
| 1021 | static char const *radlog_dest = NULL; | ||
| 1022 | +static char const *require_message_authenticator = NULL; | ||
| 1023 | +static char const *limit_proxy_state = NULL; | ||
| 1024 | |||
| 1025 | /* | ||
| 1026 | * These are not used anywhere else.. | ||
| 1027 | @@ -87,6 +89,53 @@ static bool do_colourise = false; | ||
| 1028 | |||
| 1029 | static char const *radius_dir = NULL; //!< Path to raddb directory | ||
| 1030 | |||
| 1031 | +static const FR_NAME_NUMBER fr_bool_auto_names[] = { | ||
| 1032 | + { "false", FR_BOOL_FALSE }, | ||
| 1033 | + { "no", FR_BOOL_FALSE }, | ||
| 1034 | + { "0", FR_BOOL_FALSE }, | ||
| 1035 | + | ||
| 1036 | + { "true", FR_BOOL_TRUE }, | ||
| 1037 | + { "yes", FR_BOOL_TRUE }, | ||
| 1038 | + { "1", FR_BOOL_TRUE }, | ||
| 1039 | + | ||
| 1040 | + { "auto", FR_BOOL_AUTO }, | ||
| 1041 | + | ||
| 1042 | + { NULL, 0 } | ||
| 1043 | +}; | ||
| 1044 | + | ||
| 1045 | +/* | ||
| 1046 | + * Get decent values for false / true / auto | ||
| 1047 | + */ | ||
| 1048 | +int fr_bool_auto_parse(CONF_PAIR *cp, fr_bool_auto_t *out, char const *str) | ||
| 1049 | +{ | ||
| 1050 | + int value; | ||
| 1051 | + | ||
| 1052 | + /* | ||
| 1053 | + * Don't change anything. | ||
| 1054 | + */ | ||
| 1055 | + if (!str) return 0; | ||
| 1056 | + | ||
| 1057 | + value = fr_str2int(fr_bool_auto_names, str, -1); | ||
| 1058 | + if (value >= 0) { | ||
| 1059 | + *out = value; | ||
| 1060 | + return 0; | ||
| 1061 | + } | ||
| 1062 | + | ||
| 1063 | + /* | ||
| 1064 | + * This should never happen, as the defaults are in the | ||
| 1065 | + * source code. If there's no CONF_PAIR, and there's a | ||
| 1066 | + * parse error, then the source code is wrong. | ||
| 1067 | + */ | ||
| 1068 | + if (!cp) { | ||
| 1069 | + fprintf(stderr, "%s: Error - Invalid value in configuration", main_config.name); | ||
| 1070 | + return -1; | ||
| 1071 | + } | ||
| 1072 | + | ||
| 1073 | + cf_log_err(cf_pair_to_item(cp), "Invalid value for \"%s\"", cf_pair_attr(cp)); | ||
| 1074 | + return -1; | ||
| 1075 | +} | ||
| 1076 | + | ||
| 1077 | + | ||
| 1078 | /********************************************************************** | ||
| 1079 | * | ||
| 1080 | * We need to figure out where the logs go, before doing anything | ||
| 1081 | @@ -159,6 +208,8 @@ static const CONF_PARSER security_config[] = { | ||
| 1082 | { "max_attributes", FR_CONF_POINTER(PW_TYPE_INTEGER, &fr_max_attributes), STRINGIFY(0) }, | ||
| 1083 | { "reject_delay", FR_CONF_POINTER(PW_TYPE_TIMEVAL, &main_config.reject_delay), STRINGIFY(0) }, | ||
| 1084 | { "status_server", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.status_server), "no"}, | ||
| 1085 | + { "require_message_authenticator", FR_CONF_POINTER(PW_TYPE_STRING, &require_message_authenticator), "auto"}, | ||
| 1086 | + { "limit_proxy_state", FR_CONF_POINTER(PW_TYPE_STRING, &limit_proxy_state), "auto"}, | ||
| 1087 | #ifdef ENABLE_OPENSSL_VERSION_CHECK | ||
| 1088 | { "allow_vulnerable_openssl", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.allow_vulnerable_openssl), "no"}, | ||
| 1089 | #endif | ||
| 1090 | @@ -838,6 +889,8 @@ int main_config_init(void) | ||
| 1091 | if (!main_config.dictionary_dir) { | ||
| 1092 | main_config.dictionary_dir = DICTDIR; | ||
| 1093 | } | ||
| 1094 | + main_config.require_ma = FR_BOOL_AUTO; | ||
| 1095 | + main_config.limit_proxy_state = FR_BOOL_AUTO; | ||
| 1096 | |||
| 1097 | /* | ||
| 1098 | * About sizeof(REQUEST) + sizeof(RADIUS_PACKET) * 2 + sizeof(VALUE_PAIR) * 400 | ||
| 1099 | @@ -1127,6 +1180,23 @@ do {\ | ||
| 1100 | main_config.init_delay.tv_sec = 0; | ||
| 1101 | main_config.init_delay.tv_usec = 2* (1000000 / 3); | ||
| 1102 | |||
| 1103 | + { | ||
| 1104 | + CONF_PAIR *cp = NULL; | ||
| 1105 | + | ||
| 1106 | + subcs = cf_section_sub_find(cs, "security"); | ||
| 1107 | + if (subcs) cp = cf_pair_find(subcs, "require_message_authenticator"); | ||
| 1108 | + if (fr_bool_auto_parse(cp, &main_config.require_ma, require_message_authenticator) < 0) { | ||
| 1109 | + cf_file_free(cs); | ||
| 1110 | + return -1; | ||
| 1111 | + } | ||
| 1112 | + | ||
| 1113 | + if (subcs) cp = cf_pair_find(subcs, "limit_proxy_state"); | ||
| 1114 | + if (fr_bool_auto_parse(cp, &main_config.limit_proxy_state, limit_proxy_state) < 0) { | ||
| 1115 | + cf_file_free(cs); | ||
| 1116 | + return -1; | ||
| 1117 | + } | ||
| 1118 | + } | ||
| 1119 | + | ||
| 1120 | /* | ||
| 1121 | * Free the old configuration items, and replace them | ||
| 1122 | * with the new ones. | ||
| 1123 | diff --git a/src/main/process.c b/src/main/process.c | ||
| 1124 | index 1a48517d43..401033bdd6 100644 | ||
| 1125 | --- a/src/main/process.c | ||
| 1126 | +++ b/src/main/process.c | ||
| 1127 | @@ -2593,6 +2593,23 @@ int request_proxy_reply(RADIUS_PACKET *packet) | ||
| 1128 | |||
| 1129 | PTHREAD_MUTEX_UNLOCK(&proxy_mutex); | ||
| 1130 | |||
| 1131 | + if (!request->proxy_reply) { | ||
| 1132 | + decode_fail_t reason; | ||
| 1133 | + | ||
| 1134 | + /* | ||
| 1135 | + * If the home server configuration requires a Message-Authenticator, then set the flag, | ||
| 1136 | + * but only if the proxied packet is Access-Request or Status-Sercer. | ||
| 1137 | + * | ||
| 1138 | + * The realms.c file already clears require_ma for TLS connections. | ||
| 1139 | + */ | ||
| 1140 | + bool require_ma = (request->home_server->require_ma == FR_BOOL_TRUE) && (request->proxy->code == PW_CODE_ACCESS_REQUEST); | ||
| 1141 | + | ||
| 1142 | + if(!rad_packet_ok(packet, require_ma, &reason)) { | ||
| 1143 | + DEBUG("Ignoring invalid packet - %s", fr_strerror()); | ||
| 1144 | + return 0; | ||
| 1145 | + } | ||
| 1146 | + } | ||
| 1147 | + | ||
| 1148 | /* | ||
| 1149 | * No reply, BUT the current packet fails verification: | ||
| 1150 | * ignore it. This does the MD5 calculations in the | ||
| 1151 | @@ -2618,6 +2635,54 @@ int request_proxy_reply(RADIUS_PACKET *packet) | ||
| 1152 | return 0; | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | + | ||
| 1156 | + /* | ||
| 1157 | + * BlastRADIUS checks. We're running in the main | ||
| 1158 | + * listener thread, so there's no conflict | ||
| 1159 | + * checking or setting these fields. | ||
| 1160 | + */ | ||
| 1161 | + if (!request->proxy_reply && (request->proxy->code == PW_CODE_ACCESS_REQUEST) && | ||
| 1162 | +#ifdef WITH_TLS | ||
| 1163 | + !request->home_server->tls && | ||
| 1164 | +#endif | ||
| 1165 | + !packet->eap_message) { | ||
| 1166 | + if (request->home_server->require_ma == FR_BOOL_AUTO) { | ||
| 1167 | + if (!packet->message_authenticator) { | ||
| 1168 | + RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 1169 | + RERROR("BlastRADIUS check: Received response to Access-Request without Message-Authenticator."); | ||
| 1170 | + RERROR("Setting \"require_message_authenticator = false\" for home_server %s", request->home_server->name); | ||
| 1171 | + RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 1172 | + RERROR("UPGRADE THE HOME SERVER AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK."); | ||
| 1173 | + RERROR("Once the home_server is upgraded, set \"require_message_authenticator = true\" for home_server %s.", request->home_server->name); | ||
| 1174 | + RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 1175 | + | ||
| 1176 | + request->home_server->require_ma = FR_BOOL_FALSE; | ||
| 1177 | + } else { | ||
| 1178 | + RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 1179 | + RERROR("BlastRADIUS check: Received response to Access-Request with Message-Authenticator."); | ||
| 1180 | + RERROR("Setting \"require_message_authenticator = true\" for home_server %s", request->home_server->name); | ||
| 1181 | + RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 1182 | + RERROR("It looks like the home server has been updated to protect from the BlastRADIUS attack."); | ||
| 1183 | + RERROR("Please set \"require_message_authenticator = true\" for home_server %s", request->home_server->name); | ||
| 1184 | + RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 1185 | + | ||
| 1186 | + request->home_server->require_ma = FR_BOOL_TRUE; | ||
| 1187 | + } | ||
| 1188 | + | ||
| 1189 | + } else if (fr_debug_lvl && (request->home_server->require_ma == FR_BOOL_FALSE) && !packet->message_authenticator) { | ||
| 1190 | + /* | ||
| 1191 | + * If it's "no" AND we don't have a Message-Authenticator, then complain on every packet. | ||
| 1192 | + */ | ||
| 1193 | + RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 1194 | + RDEBUG("BlastRADIUS check: Received packet without Message-Authenticator from home_server %s", request->home_server->name); | ||
| 1195 | + RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 1196 | + RDEBUG("The packet does not contain Message-Authenticator, which is a security issue"); | ||
| 1197 | + RDEBUG("UPGRADE THE HOME SERVER AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK."); | ||
| 1198 | + RERROR("Once the home_server is upgraded, set \"require_message_authenticator = true\" for home_server %s.", request->home_server->name); | ||
| 1199 | + RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | ||
| 1200 | + } | ||
| 1201 | + } | ||
| 1202 | + | ||
| 1203 | /* | ||
| 1204 | * This shouldn't happen, but threads and race | ||
| 1205 | * conditions. | ||
| 1206 | diff --git a/src/main/radclient.c b/src/main/radclient.c | ||
| 1207 | index 52d2872b13..47d5f07785 100644 | ||
| 1208 | --- a/src/main/radclient.c | ||
| 1209 | +++ b/src/main/radclient.c | ||
| 1210 | @@ -54,6 +54,7 @@ static fr_ipaddr_t server_ipaddr; | ||
| 1211 | static int resend_count = 1; | ||
| 1212 | static bool done = true; | ||
| 1213 | static bool print_filename = false; | ||
| 1214 | +static bool blast_radius = false; | ||
| 1215 | |||
| 1216 | static fr_ipaddr_t client_ipaddr; | ||
| 1217 | static uint16_t client_port = 0; | ||
| 1218 | @@ -89,6 +90,7 @@ static void NEVER_RETURNS usage(void) | ||
| 1219 | fprintf(stderr, " <command> One of auth, acct, status, coa, disconnect or auto.\n"); | ||
| 1220 | fprintf(stderr, " -4 Use IPv4 address of server\n"); | ||
| 1221 | fprintf(stderr, " -6 Use IPv6 address of server.\n"); | ||
| 1222 | + fprintf(stderr, " -b Mandate checks for Blast RADIUS (this is not set by default).\n"); | ||
| 1223 | fprintf(stderr, " -c <count> Send each packet 'count' times.\n"); | ||
| 1224 | fprintf(stderr, " -d <raddb> Set user dictionary directory (defaults to " RADDBDIR ").\n"); | ||
| 1225 | fprintf(stderr, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n"); | ||
| 1226 | @@ -1000,6 +1002,130 @@ static int send_one_packet(rc_request_t *request) | ||
| 1227 | return 0; | ||
| 1228 | } | ||
| 1229 | |||
| 1230 | +/* | ||
| 1231 | + * Do Blast RADIUS checks. | ||
| 1232 | + * | ||
| 1233 | + * The request is an Access-Request, and does NOT contain Proxy-State. | ||
| 1234 | + * | ||
| 1235 | + * The reply is a raw packet, and is NOT yet decoded. | ||
| 1236 | + */ | ||
| 1237 | +static int blast_radius_check(rc_request_t *request, RADIUS_PACKET *reply) | ||
| 1238 | +{ | ||
| 1239 | + uint8_t *attr, *end; | ||
| 1240 | + VALUE_PAIR *vp; | ||
| 1241 | + bool have_message_authenticator = false; | ||
| 1242 | + | ||
| 1243 | + /* | ||
| 1244 | + * We've received a raw packet. Nothing has (as of yet) checked | ||
| 1245 | + * anything in it other than the length, and that it's a | ||
| 1246 | + * well-formed RADIUS packet. | ||
| 1247 | + */ | ||
| 1248 | + switch (reply->data[0]) { | ||
| 1249 | + case PW_CODE_ACCESS_ACCEPT: | ||
| 1250 | + case PW_CODE_ACCESS_REJECT: | ||
| 1251 | + case PW_CODE_ACCESS_CHALLENGE: | ||
| 1252 | + if (reply->data[1] != request->packet->id) { | ||
| 1253 | + ERROR("Invalid reply ID %d to Access-Request ID %d", reply->data[1], request->packet->id); | ||
| 1254 | + return -1; | ||
| 1255 | + } | ||
| 1256 | + break; | ||
| 1257 | + | ||
| 1258 | + default: | ||
| 1259 | + ERROR("Invalid reply code %d to Access-Request", reply->data[0]); | ||
| 1260 | + return -1; | ||
| 1261 | + } | ||
| 1262 | + | ||
| 1263 | + /* | ||
| 1264 | + * If the reply has a Message-Authenticator, then it MIGHT be fine. | ||
| 1265 | + */ | ||
| 1266 | + attr = reply->data + 20; | ||
| 1267 | + end = reply->data + reply->data_len; | ||
| 1268 | + | ||
| 1269 | + /* | ||
| 1270 | + * It should be the first attribute, so we warn if it isn't there. | ||
| 1271 | + * | ||
| 1272 | + * But it's not a fatal error. | ||
| 1273 | + */ | ||
| 1274 | + if (blast_radius && (attr[0] != PW_MESSAGE_AUTHENTICATOR)) { | ||
| 1275 | + RDEBUG("WARNING The %s reply packet does not have Message-Authenticator as the first attribute. The packet may be vulnerable to Blast RADIUS attacks.", | ||
| 1276 | + fr_packet_codes[reply->data[0]]); | ||
| 1277 | + } | ||
| 1278 | + | ||
| 1279 | + /* | ||
| 1280 | + * Set up for Proxy-State checks. | ||
| 1281 | + * | ||
| 1282 | + * If we see a Proxy-State in the reply which we didn't send, then it's a Blast RADIUS attack. | ||
| 1283 | + */ | ||
| 1284 | + vp = fr_pair_find_by_num(request->packet->vps, PW_PROXY_STATE, 0, TAG_ANY); | ||
| 1285 | + | ||
| 1286 | + while (attr < end) { | ||
| 1287 | + /* | ||
| 1288 | + * Blast RADIUS work-arounds require that | ||
| 1289 | + * Message-Authenticator is the first attribute in the | ||
| 1290 | + * reply. Note that we don't check for it being the | ||
| 1291 | + * first attribute, but simply that it exists. | ||
| 1292 | + * | ||
| 1293 | + * That check is a balance between securing the reply | ||
| 1294 | + * packet from attacks, and not violating the RFCs which | ||
| 1295 | + * say that there is no order to attributes in the | ||
| 1296 | + * packet. | ||
| 1297 | + * | ||
| 1298 | + * However, no matter the status of the '-b' flag we | ||
| 1299 | + * still can check for the signature of the attack, and | ||
| 1300 | + * discard packets which are suspicious. This behavior | ||
| 1301 | + * protects radclient from the attack, without mandating | ||
| 1302 | + * new behavior on the server side. | ||
| 1303 | + * | ||
| 1304 | + * Note that we don't set the '-b' flag by default. | ||
| 1305 | + * radclient is intended for testing / debugging, and is | ||
| 1306 | + * not intended to be used as part of a secure login / | ||
| 1307 | + * user checking system. | ||
| 1308 | + */ | ||
| 1309 | + if (attr[0] == PW_MESSAGE_AUTHENTICATOR) { | ||
| 1310 | + have_message_authenticator = true; | ||
| 1311 | + goto next; | ||
| 1312 | + } | ||
| 1313 | + | ||
| 1314 | + /* | ||
| 1315 | + * If there are Proxy-State attributes in the reply, they must | ||
| 1316 | + * match EXACTLY the Proxy-State attributes in the request. | ||
| 1317 | + * | ||
| 1318 | + * Note that we don't care if there are more Proxy-States | ||
| 1319 | + * in the request than in the reply. The Blast RADIUS | ||
| 1320 | + * issue requires _adding_ Proxy-State attributes, and | ||
| 1321 | + * cannot work when the server _deletes_ Proxy-State | ||
| 1322 | + * attributes. | ||
| 1323 | + */ | ||
| 1324 | + if (attr[0] == PW_PROXY_STATE) { | ||
| 1325 | + if (!vp || (vp->length != (size_t) (attr[1] - 2)) || (memcmp(vp->vp_octets, attr + 2, vp->length) != 0)) { | ||
| 1326 | + ERROR("Invalid reply to Access-Request ID %d - Discarding packet due to Blast RADIUS attack being detected.", request->packet->id); | ||
| 1327 | + ERROR("We received a Proxy-State in the reply which we did not send, or which is different from what we sent."); | ||
| 1328 | + return -1; | ||
| 1329 | + } | ||
| 1330 | + | ||
| 1331 | + vp = fr_pair_find_by_num(vp->next, PW_PROXY_STATE, 0, TAG_ANY); | ||
| 1332 | + } | ||
| 1333 | + | ||
| 1334 | + next: | ||
| 1335 | + attr += attr[1]; | ||
| 1336 | + } | ||
| 1337 | + | ||
| 1338 | + /* | ||
| 1339 | + * If "-b" is set, then we require Message-Authenticator in the reply. | ||
| 1340 | + */ | ||
| 1341 | + if (blast_radius && !have_message_authenticator) { | ||
| 1342 | + ERROR("The %s reply packet does not contain Message-Authenticator - discarding packet due to Blast RADIUS checks.", | ||
| 1343 | + fr_packet_codes[reply->data[0]]); | ||
| 1344 | + return -1; | ||
| 1345 | + } | ||
| 1346 | + | ||
| 1347 | + /* | ||
| 1348 | + * The packet doesn't look like it's a Blast RADIUS attack. The | ||
| 1349 | + * caller will now verify the packet signature. | ||
| 1350 | + */ | ||
| 1351 | + return 0; | ||
| 1352 | +} | ||
| 1353 | + | ||
| 1354 | /* | ||
| 1355 | * Receive one packet, maybe. | ||
| 1356 | */ | ||
| 1357 | @@ -1051,6 +1177,21 @@ static int recv_one_packet(int wait_time) | ||
| 1358 | } | ||
| 1359 | request = fr_packet2myptr(rc_request_t, packet, packet_p); | ||
| 1360 | |||
| 1361 | + | ||
| 1362 | + /* | ||
| 1363 | + * We want radclient to be able to send any packet, including | ||
| 1364 | + * imperfect ones. However, we do NOT want to be vulnerable to | ||
| 1365 | + * the "Blast RADIUS" issue. Instead of adding command-line | ||
| 1366 | + * flags to enable/disable similar flags to what the server | ||
| 1367 | + * sends, we just do a few more smart checks to double-check | ||
| 1368 | + * things. | ||
| 1369 | + */ | ||
| 1370 | + if ((request->packet->code == PW_CODE_ACCESS_REQUEST) && | ||
| 1371 | + blast_radius_check(request, reply) < 0) { | ||
| 1372 | + rad_free(&reply); | ||
| 1373 | + return -1; | ||
| 1374 | + } | ||
| 1375 | + | ||
| 1376 | /* | ||
| 1377 | * Fails the signature validation: not a real reply. | ||
| 1378 | * FIXME: Silently drop it and listen for another packet. | ||
| 1379 | @@ -1183,7 +1324,7 @@ int main(int argc, char **argv) | ||
| 1380 | exit(1); | ||
| 1381 | } | ||
| 1382 | |||
| 1383 | - while ((c = getopt(argc, argv, "46c:d:D:f:Fhn:p:qr:sS:t:vx" | ||
| 1384 | + while ((c = getopt(argc, argv, "46bc:d:D:f:Fhn:p:qr:sS:t:vx" | ||
| 1385 | #ifdef WITH_TCP | ||
| 1386 | "P:" | ||
| 1387 | #endif | ||
| 1388 | @@ -1192,6 +1333,10 @@ int main(int argc, char **argv) | ||
| 1389 | force_af = AF_INET; | ||
| 1390 | break; | ||
| 1391 | |||
| 1392 | + case 'b': | ||
| 1393 | + blast_radius = true; | ||
| 1394 | + break; | ||
| 1395 | + | ||
| 1396 | case '6': | ||
| 1397 | force_af = AF_INET6; | ||
| 1398 | break; | ||
| 1399 | diff --git a/src/main/radtest.in b/src/main/radtest.in | ||
| 1400 | index 38b1ba9a0f..8a6741a26c 100644 | ||
| 1401 | --- a/src/main/radtest.in | ||
| 1402 | +++ b/src/main/radtest.in | ||
| 1403 | @@ -19,6 +19,7 @@ usage() { | ||
| 1404 | echo " -x Enable debug output" >&2 | ||
| 1405 | echo " -4 Use IPv4 for the NAS address (default)" >&2 | ||
| 1406 | echo " -6 Use IPv6 for the NAS address" >&2 | ||
| 1407 | + echo " -6 Mandate checks for Blast RADIUS (this is not set by default)." >&2 | ||
| 1408 | exit 1 | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | @@ -55,6 +56,10 @@ do | ||
| 1412 | NAS_ADDR_ATTR="NAS-IPv6-Address" | ||
| 1413 | shift | ||
| 1414 | ;; | ||
| 1415 | + -b) | ||
| 1416 | + OPTIONS="$OPTIONS -b" | ||
| 1417 | + shift | ||
| 1418 | + ;; | ||
| 1419 | -d) | ||
| 1420 | OPTIONS="$OPTIONS -d $2" | ||
| 1421 | shift;shift | ||
| 1422 | @@ -120,7 +125,6 @@ fi | ||
| 1423 | echo "$PASSWORD = \"$2\"" | ||
| 1424 | echo "$NAS_ADDR_ATTR = $nas" | ||
| 1425 | echo "NAS-Port = $4" | ||
| 1426 | - echo "Message-Authenticator = 0x00" | ||
| 1427 | if [ "$radclient" = "$radeapclient" ] | ||
| 1428 | then | ||
| 1429 | echo "EAP-Code = Response" | ||
| 1430 | diff --git a/src/main/realms.c b/src/main/realms.c | ||
| 1431 | index eb42598116..5e1215c0bb 100644 | ||
| 1432 | --- a/src/main/realms.c | ||
| 1433 | +++ b/src/main/realms.c | ||
| 1434 | @@ -366,7 +366,10 @@ static CONF_PARSER home_server_coa[] = { | ||
| 1435 | }; | ||
| 1436 | #endif | ||
| 1437 | |||
| 1438 | +static const char *require_message_authenticator = NULL; | ||
| 1439 | + | ||
| 1440 | static CONF_PARSER home_server_config[] = { | ||
| 1441 | + { "require_message_authenticator", FR_CONF_POINTER(PW_TYPE_STRING| PW_TYPE_IGNORE_DEFAULT, &require_message_authenticator), NULL }, | ||
| 1442 | { "ipaddr", FR_CONF_OFFSET(PW_TYPE_COMBO_IP_ADDR, home_server_t, ipaddr), NULL }, | ||
| 1443 | { "ipv4addr", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, home_server_t, ipaddr), NULL }, | ||
| 1444 | { "ipv6addr", FR_CONF_OFFSET(PW_TYPE_IPV6_ADDR, home_server_t, ipaddr), NULL }, | ||
| 1445 | @@ -640,6 +643,9 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE | ||
| 1446 | home->cs = cs; | ||
| 1447 | home->state = HOME_STATE_UNKNOWN; | ||
| 1448 | home->proto = IPPROTO_UDP; | ||
| 1449 | + home->require_ma = main_config.require_ma; | ||
| 1450 | + | ||
| 1451 | + require_message_authenticator = false; | ||
| 1452 | |||
| 1453 | /* | ||
| 1454 | * Parse the configuration into the home server | ||
| 1455 | @@ -647,6 +653,10 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE | ||
| 1456 | */ | ||
| 1457 | if (cf_section_parse(cs, home, home_server_config) < 0) goto error; | ||
| 1458 | |||
| 1459 | + if (fr_bool_auto_parse(cf_pair_find(cs, "require_message_authenticator"), &home->require_ma, require_message_authenticator) < 0) { | ||
| 1460 | + goto error; | ||
| 1461 | + } | ||
| 1462 | + | ||
| 1463 | /* | ||
| 1464 | * It has an IP address, it must be a remote server. | ||
| 1465 | */ | ||
| 1466 | @@ -924,6 +934,7 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE | ||
| 1467 | * Parse the SSL client configuration. | ||
| 1468 | */ | ||
| 1469 | if (tls) { | ||
| 1470 | + home->require_ma = false; | ||
| 1471 | home->tls = tls_client_conf_parse(tls); | ||
| 1472 | if (!home->tls) { | ||
| 1473 | goto error; | ||
| 1474 | diff --git a/src/main/tls_listen.c b/src/main/tls_listen.c | ||
| 1475 | index 0eed87b64f..4ae3c5b975 100644 | ||
| 1476 | --- a/src/main/tls_listen.c | ||
| 1477 | +++ b/src/main/tls_listen.c | ||
| 1478 | @@ -299,6 +299,8 @@ get_application_data: | ||
| 1479 | packet->vps = NULL; | ||
| 1480 | PTHREAD_MUTEX_UNLOCK(&sock->mutex); | ||
| 1481 | |||
| 1482 | + packet->tls = true; | ||
| 1483 | + | ||
| 1484 | if (!rad_packet_ok(packet, 0, NULL)) { | ||
| 1485 | if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror()); | ||
| 1486 | DEBUG("Closing TLS socket from client"); | ||
| 1487 | @@ -713,6 +715,8 @@ int proxy_tls_recv(rad_listen_t *listener) | ||
| 1488 | memcpy(packet->data, data, packet->data_len); | ||
| 1489 | memcpy(packet->vector, packet->data + 4, 16); | ||
| 1490 | |||
| 1491 | + packet->tls = true; | ||
| 1492 | + | ||
| 1493 | /* | ||
| 1494 | * FIXME: Client MIB updates? | ||
| 1495 | */ | ||
| 1496 | @@ -765,6 +769,7 @@ int proxy_tls_send(rad_listen_t *listener, REQUEST *request) | ||
| 1497 | * if there's no packet, encode it here. | ||
| 1498 | */ | ||
| 1499 | if (!request->proxy->data) { | ||
| 1500 | + request->reply->tls = true; | ||
| 1501 | request->proxy_listener->encode(request->proxy_listener, | ||
| 1502 | request); | ||
| 1503 | } | ||
| 1504 | -- | ||
| 1505 | 2.35.7 | ||
| 1506 | |||
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 db37f65918..01d23fdf83 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 | |||
| 35 | file://0001-version.c-don-t-print-build-flags.patch \ | 35 | file://0001-version.c-don-t-print-build-flags.patch \ |
| 36 | file://CVE-2022-41860.patch \ | 36 | file://CVE-2022-41860.patch \ |
| 37 | file://CVE-2022-41861.patch \ | 37 | file://CVE-2022-41861.patch \ |
| 38 | file://CVE-2024-3596.patch \ | ||
| 38 | " | 39 | " |
| 39 | 40 | ||
| 40 | raddbdir="${sysconfdir}/${MLPREFIX}raddb" | 41 | raddbdir="${sysconfdir}/${MLPREFIX}raddb" |
