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 | |
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>
-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" |