/src/freeradius-server/src/protocols/radius/base.c
Line | Count | Source |
1 | | /* |
2 | | * This library is free software; you can redistribute it and/or |
3 | | * modify it under the terms of the GNU Lesser General Public |
4 | | * License as published by the Free Software Foundation; either |
5 | | * version 2.1 of the License, or (at your option) any later version. |
6 | | * |
7 | | * This library is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
10 | | * Lesser General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU Lesser General Public |
13 | | * License along with this library; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** |
18 | | * $Id: 67362bd5b8ce28d82d833f91f081c0f39b634983 $ |
19 | | * |
20 | | * @file protocols/radius/base.c |
21 | | * @brief Functions to send/receive radius packets. |
22 | | * |
23 | | * @copyright 2000-2003,2006 The FreeRADIUS server project |
24 | | */ |
25 | | RCSID("$Id: 67362bd5b8ce28d82d833f91f081c0f39b634983 $") |
26 | | |
27 | | #include <fcntl.h> |
28 | | #include <ctype.h> |
29 | | |
30 | | #include "attrs.h" |
31 | | #include "radius.h" |
32 | | |
33 | | #include <freeradius-devel/io/pair.h> |
34 | | #include <freeradius-devel/util/md5.h> |
35 | | #include <freeradius-devel/util/net.h> |
36 | | #include <freeradius-devel/util/proto.h> |
37 | | #include <freeradius-devel/util/udp.h> |
38 | | #include <freeradius-devel/protocol/radius/freeradius.internal.h> |
39 | | |
40 | | static uint32_t instance_count = 0; |
41 | | static bool instantiated = false; |
42 | | |
43 | | fr_dict_t const *dict_freeradius; |
44 | | fr_dict_t const *dict_radius; |
45 | | |
46 | | extern fr_dict_autoload_t libfreeradius_radius_dict[]; |
47 | | fr_dict_autoload_t libfreeradius_radius_dict[] = { |
48 | | { .out = &dict_freeradius, .proto = "freeradius" }, |
49 | | { .out = &dict_radius, .proto = "radius" }, |
50 | | |
51 | | DICT_AUTOLOAD_TERMINATOR |
52 | | }; |
53 | | |
54 | | fr_dict_attr_t const *attr_packet_type; |
55 | | fr_dict_attr_t const *attr_packet_authentication_vector; |
56 | | fr_dict_attr_t const *attr_chap_challenge; |
57 | | fr_dict_attr_t const *attr_chargeable_user_identity; |
58 | | fr_dict_attr_t const *attr_eap_message; |
59 | | fr_dict_attr_t const *attr_message_authenticator; |
60 | | fr_dict_attr_t const *attr_state; |
61 | | fr_dict_attr_t const *attr_vendor_specific; |
62 | | fr_dict_attr_t const *attr_nas_filter_rule; |
63 | | |
64 | | extern fr_dict_attr_autoload_t libfreeradius_radius_dict_attr[]; |
65 | | fr_dict_attr_autoload_t libfreeradius_radius_dict_attr[] = { |
66 | | { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_radius }, |
67 | | { .out = &attr_packet_authentication_vector, .name = "Packet-Authentication-Vector", .type = FR_TYPE_OCTETS, .dict = &dict_radius }, |
68 | | { .out = &attr_chap_challenge, .name = "CHAP-Challenge", .type = FR_TYPE_OCTETS, .dict = &dict_radius }, |
69 | | { .out = &attr_chargeable_user_identity, .name = "Chargeable-User-Identity", .type = FR_TYPE_OCTETS, .dict = &dict_radius }, |
70 | | |
71 | | { .out = &attr_eap_message, .name = "EAP-Message", .type = FR_TYPE_OCTETS, .dict = &dict_radius }, |
72 | | { .out = &attr_message_authenticator, .name = "Message-Authenticator", .type = FR_TYPE_OCTETS, .dict = &dict_radius }, |
73 | | { .out = &attr_state, .name = "State", .type = FR_TYPE_OCTETS, .dict = &dict_radius }, |
74 | | { .out = &attr_vendor_specific, .name = "Vendor-Specific", .type = FR_TYPE_VSA, .dict = &dict_radius }, |
75 | | { .out = &attr_nas_filter_rule, .name = "NAS-Filter-Rule", .type = FR_TYPE_STRING, .dict = &dict_radius }, |
76 | | |
77 | | DICT_AUTOLOAD_TERMINATOR |
78 | | }; |
79 | | |
80 | | /* |
81 | | * Some messages get printed out only in debugging mode. |
82 | | */ |
83 | 0 | #define FR_DEBUG_STRERROR_PRINTF if (fr_debug_lvl) fr_strerror_printf |
84 | 0 | #define FR_DEBUG_STRERROR_PRINTF_PUSH if (fr_debug_lvl) fr_strerror_printf_push |
85 | | |
86 | | fr_table_num_sorted_t const fr_radius_require_ma_table[] = { |
87 | | { L("auto"), FR_RADIUS_REQUIRE_MA_AUTO }, |
88 | | { L("false"), FR_RADIUS_REQUIRE_MA_NO }, |
89 | | { L("no"), FR_RADIUS_REQUIRE_MA_NO }, |
90 | | { L("true"), FR_RADIUS_REQUIRE_MA_YES }, |
91 | | { L("yes"), FR_RADIUS_REQUIRE_MA_YES }, |
92 | | }; |
93 | | size_t fr_radius_require_ma_table_len = NUM_ELEMENTS(fr_radius_require_ma_table); |
94 | | |
95 | | fr_table_num_sorted_t const fr_radius_limit_proxy_state_table[] = { |
96 | | { L("auto"), FR_RADIUS_LIMIT_PROXY_STATE_AUTO }, |
97 | | { L("false"), FR_RADIUS_LIMIT_PROXY_STATE_NO }, |
98 | | { L("no"), FR_RADIUS_LIMIT_PROXY_STATE_NO }, |
99 | | { L("true"), FR_RADIUS_LIMIT_PROXY_STATE_YES }, |
100 | | { L("yes"), FR_RADIUS_LIMIT_PROXY_STATE_YES }, |
101 | | }; |
102 | | size_t fr_radius_limit_proxy_state_table_len = NUM_ELEMENTS(fr_radius_limit_proxy_state_table); |
103 | | |
104 | | fr_table_num_sorted_t const fr_radius_request_name_table[] = { |
105 | | { L("acct"), FR_RADIUS_CODE_ACCOUNTING_REQUEST }, |
106 | | { L("auth"), FR_RADIUS_CODE_ACCESS_REQUEST }, |
107 | | { L("auto"), FR_RADIUS_CODE_UNDEFINED }, |
108 | | { L("challenge"), FR_RADIUS_CODE_ACCESS_CHALLENGE }, |
109 | | { L("coa"), FR_RADIUS_CODE_COA_REQUEST }, |
110 | | { L("disconnect"), FR_RADIUS_CODE_DISCONNECT_REQUEST }, |
111 | | { L("status"), FR_RADIUS_CODE_STATUS_SERVER } |
112 | | }; |
113 | | size_t fr_radius_request_name_table_len = NUM_ELEMENTS(fr_radius_request_name_table); |
114 | | |
115 | | char const *fr_radius_packet_name[FR_RADIUS_CODE_MAX] = { |
116 | | "", //!< 0 |
117 | | "Access-Request", |
118 | | "Access-Accept", |
119 | | "Access-Reject", |
120 | | "Accounting-Request", |
121 | | "Accounting-Response", |
122 | | "Accounting-Status", |
123 | | "Password-Request", |
124 | | "Password-Accept", |
125 | | "Password-Reject", |
126 | | "Accounting-Message", //!< 10 |
127 | | "Access-Challenge", |
128 | | "Status-Server", |
129 | | "Status-Client", |
130 | | "14", |
131 | | "15", |
132 | | "16", |
133 | | "17", |
134 | | "18", |
135 | | "19", |
136 | | "20", //!< 20 |
137 | | "Resource-Free-Request", |
138 | | "Resource-Free-Response", |
139 | | "Resource-Query-Request", |
140 | | "Resource-Query-Response", |
141 | | "Alternate-Resource-Reclaim-Request", |
142 | | "NAS-Reboot-Request", |
143 | | "NAS-Reboot-Response", |
144 | | "28", |
145 | | "Next-Passcode", |
146 | | "New-Pin", //!< 30 |
147 | | "Terminate-Session", |
148 | | "Password-Expired", |
149 | | "Event-Request", |
150 | | "Event-Response", |
151 | | "35", |
152 | | "36", |
153 | | "37", |
154 | | "38", |
155 | | "39", |
156 | | "Disconnect-Request", //!< 40 |
157 | | "Disconnect-ACK", |
158 | | "Disconnect-NAK", |
159 | | "CoA-Request", |
160 | | "CoA-ACK", |
161 | | "CoA-NAK", |
162 | | "46", |
163 | | "47", |
164 | | "48", |
165 | | "49", |
166 | | "IP-Address-Allocate", //!< 50 |
167 | | "IP-Address-Release", |
168 | | "Protocol-Error", |
169 | | }; |
170 | | |
171 | | |
172 | | /** If we get a reply, the request must come from one of a small |
173 | | * number of packet types. |
174 | | */ |
175 | | const fr_radius_packet_code_t allowed_replies[FR_RADIUS_CODE_MAX] = { |
176 | | [FR_RADIUS_CODE_ACCESS_ACCEPT] = FR_RADIUS_CODE_ACCESS_REQUEST, |
177 | | [FR_RADIUS_CODE_ACCESS_CHALLENGE] = FR_RADIUS_CODE_ACCESS_REQUEST, |
178 | | [FR_RADIUS_CODE_ACCESS_REJECT] = FR_RADIUS_CODE_ACCESS_REQUEST, |
179 | | |
180 | | [FR_RADIUS_CODE_ACCOUNTING_RESPONSE] = FR_RADIUS_CODE_ACCOUNTING_REQUEST, |
181 | | |
182 | | [FR_RADIUS_CODE_COA_ACK] = FR_RADIUS_CODE_COA_REQUEST, |
183 | | [FR_RADIUS_CODE_COA_NAK] = FR_RADIUS_CODE_COA_REQUEST, |
184 | | |
185 | | [FR_RADIUS_CODE_DISCONNECT_ACK] = FR_RADIUS_CODE_DISCONNECT_REQUEST, |
186 | | [FR_RADIUS_CODE_DISCONNECT_NAK] = FR_RADIUS_CODE_DISCONNECT_REQUEST, |
187 | | |
188 | | [FR_RADIUS_CODE_PROTOCOL_ERROR] = FR_RADIUS_CODE_PROTOCOL_ERROR, /* Any */ |
189 | | }; |
190 | | |
191 | | FR_DICT_ATTR_FLAG_FUNC(fr_radius_attr_flags_t, abinary) |
192 | | FR_DICT_ATTR_FLAG_FUNC(fr_radius_attr_flags_t, concat) |
193 | | |
194 | | static int dict_flag_encrypt(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules) |
195 | 440 | { |
196 | 440 | static fr_table_num_sorted_t const encrypted[] = { |
197 | 440 | { L("Ascend-Secret"), RADIUS_FLAG_ENCRYPT_ASCEND_SECRET }, |
198 | 440 | { L("Tunnel-Password"), RADIUS_FLAG_ENCRYPT_TUNNEL_PASSWORD }, |
199 | 440 | { L("User-Password"), RADIUS_FLAG_ENCRYPT_USER_PASSWORD} |
200 | 440 | }; |
201 | 440 | static size_t encrypted_len = NUM_ELEMENTS(encrypted); |
202 | | |
203 | 440 | fr_radius_attr_flags_encrypt_t encrypt; |
204 | 440 | fr_radius_attr_flags_t *flags = fr_dict_attr_ext(*da_p, FR_DICT_ATTR_EXT_PROTOCOL_SPECIFIC); |
205 | | |
206 | 440 | encrypt = fr_table_value_by_str(encrypted, value, RADIUS_FLAG_ENCRYPT_INVALID); |
207 | 440 | if (encrypt == RADIUS_FLAG_ENCRYPT_INVALID) { |
208 | 0 | fr_strerror_printf("Unknown encryption type '%s'", value); |
209 | 0 | return -1; |
210 | 0 | } |
211 | | |
212 | 440 | flags->encrypt = encrypt; |
213 | | |
214 | 440 | return 0; |
215 | 440 | } |
216 | | |
217 | | FR_DICT_ATTR_FLAG_FUNC(fr_radius_attr_flags_t, extended) |
218 | | FR_DICT_ATTR_FLAG_FUNC(fr_radius_attr_flags_t, has_tag) |
219 | | FR_DICT_ATTR_FLAG_FUNC(fr_radius_attr_flags_t, long_extended) |
220 | | |
221 | | static fr_dict_flag_parser_t const radius_flags[] = { |
222 | | { L("abinary"), { .func = dict_flag_abinary } }, |
223 | | { L("concat"), { .func = dict_flag_concat } }, |
224 | | { L("encrypt"), { .func = dict_flag_encrypt, .needs_value = true } }, |
225 | | { L("extended"), { .func = dict_flag_extended } }, |
226 | | { L("has_tag"), { .func = dict_flag_has_tag } }, |
227 | | { L("long_extended"), { .func = dict_flag_long_extended } } |
228 | | }; |
229 | | |
230 | | int fr_radius_allow_reply(int code, bool allowed[static FR_RADIUS_CODE_MAX]) |
231 | 0 | { |
232 | 0 | int i; |
233 | |
|
234 | 0 | if ((code <= 0) || (code >= FR_RADIUS_CODE_MAX)) return -1; |
235 | | |
236 | 0 | for (i = 1; i < FR_RADIUS_CODE_MAX; i++) { |
237 | 0 | allowed[i] |= (allowed_replies[i] == (fr_radius_packet_code_t) code); |
238 | 0 | } |
239 | |
|
240 | 0 | return 0; |
241 | 0 | } |
242 | | |
243 | | /** Do Ascend-Send / Recv-Secret calculation. |
244 | | * |
245 | | * The secret is hidden by xoring with a MD5 digest created from |
246 | | * the RADIUS shared secret and the authentication vector. |
247 | | * We put them into MD5 in the reverse order from that used when |
248 | | * encrypting passwords to RADIUS. |
249 | | */ |
250 | | ssize_t fr_radius_ascend_secret(fr_dbuff_t *dbuff, uint8_t const *in, size_t inlen, |
251 | | char const *secret, size_t secret_len, uint8_t const *vector) |
252 | 149 | { |
253 | 149 | fr_md5_ctx_t *md5_ctx; |
254 | 149 | size_t i; |
255 | 149 | uint8_t digest[MD5_DIGEST_LENGTH]; |
256 | 149 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
257 | | |
258 | 149 | FR_DBUFF_EXTEND_LOWAT_OR_RETURN(&work_dbuff, sizeof(digest)); |
259 | | |
260 | 149 | md5_ctx = fr_md5_ctx_alloc_from_list(); |
261 | 149 | fr_md5_update(md5_ctx, vector, RADIUS_AUTH_VECTOR_LENGTH); |
262 | 149 | fr_md5_update(md5_ctx, (uint8_t const *) secret, secret_len); |
263 | 149 | fr_md5_final(digest, md5_ctx); |
264 | 149 | fr_md5_ctx_free_from_list(&md5_ctx); |
265 | | |
266 | 149 | if (inlen > sizeof(digest)) inlen = sizeof(digest); |
267 | 1.36k | for (i = 0; i < inlen; i++) digest[i] ^= in[i]; |
268 | | |
269 | 149 | fr_dbuff_in_memcpy(&work_dbuff, digest, sizeof(digest)); |
270 | | |
271 | 149 | return fr_dbuff_set(dbuff, &work_dbuff); |
272 | 149 | } |
273 | | |
274 | | /** Basic validation of RADIUS packet header |
275 | | * |
276 | | * @note fr_strerror errors are only available if fr_debug_lvl > 0. This is to reduce CPU time |
277 | | * consumed when discarding malformed packet. |
278 | | * |
279 | | * @param[in] sockfd we're reading from. |
280 | | * @param[out] src_ipaddr of the packet. |
281 | | * @param[out] src_port of the packet. |
282 | | * @param[out] code Pointer to where to write the packet code. |
283 | | * @return |
284 | | * - -1 on failure. |
285 | | * - 1 on decode error. |
286 | | * - >= RADIUS_HEADER_LENGTH on success. This is the packet length as specified in the header. |
287 | | */ |
288 | | ssize_t fr_radius_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, unsigned int *code) |
289 | 0 | { |
290 | 0 | ssize_t data_len, packet_len; |
291 | 0 | uint8_t header[4]; |
292 | |
|
293 | 0 | data_len = udp_recv_peek(sockfd, header, sizeof(header), UDP_FLAGS_PEEK, src_ipaddr, src_port); |
294 | 0 | if (data_len < 0) { |
295 | 0 | if ((errno == EAGAIN) || (errno == EINTR)) return 0; |
296 | 0 | return -1; |
297 | 0 | } |
298 | | |
299 | | /* |
300 | | * Too little data is available, discard the packet. |
301 | | */ |
302 | 0 | if (data_len < 4) { |
303 | 0 | char buffer[INET6_ADDRSTRLEN]; |
304 | |
|
305 | 0 | FR_DEBUG_STRERROR_PRINTF("Expected at least 4 bytes of header data, got %zd bytes", data_len); |
306 | 0 | invalid: |
307 | 0 | FR_DEBUG_STRERROR_PRINTF_PUSH("Invalid data from %s", |
308 | 0 | inet_ntop(src_ipaddr->af, &src_ipaddr->addr, buffer, sizeof(buffer))); |
309 | 0 | (void) udp_recv_discard(sockfd); |
310 | |
|
311 | 0 | return 0; |
312 | 0 | } |
313 | | |
314 | | /* |
315 | | * See how long the packet says it is. |
316 | | */ |
317 | 0 | packet_len = (header[2] * 256) + header[3]; |
318 | | |
319 | | /* |
320 | | * The length in the packet says it's less than |
321 | | * a RADIUS header length: discard it. |
322 | | */ |
323 | 0 | if (packet_len < RADIUS_HEADER_LENGTH) { |
324 | 0 | FR_DEBUG_STRERROR_PRINTF("Expected at least " STRINGIFY(RADIUS_HEADER_LENGTH) " bytes of packet " |
325 | 0 | "data, got %zd bytes", packet_len); |
326 | 0 | goto invalid; |
327 | 0 | } |
328 | | |
329 | | /* |
330 | | * Enforce RFC requirements, for sanity. |
331 | | * Anything after 4k will be discarded. |
332 | | */ |
333 | 0 | if (packet_len > MAX_PACKET_LEN) { |
334 | 0 | FR_DEBUG_STRERROR_PRINTF("Length field value too large, expected maximum of " |
335 | 0 | STRINGIFY(MAX_PACKET_LEN) " bytes, got %zd bytes", packet_len); |
336 | 0 | goto invalid; |
337 | 0 | } |
338 | | |
339 | 0 | *code = header[0]; |
340 | | |
341 | | /* |
342 | | * The packet says it's this long, but the actual UDP |
343 | | * size could still be smaller. |
344 | | */ |
345 | 0 | return packet_len; |
346 | 0 | } |
347 | | |
348 | | /** Sign a previously encoded packet |
349 | | * |
350 | | * Calculates the request/response authenticator for packets which need it, and fills |
351 | | * in the message-authenticator value if the attribute is present in the encoded packet. |
352 | | * |
353 | | * @param[in,out] packet (request or response). |
354 | | * @param[in] vector original packet vector to use |
355 | | * @param[in] secret to sign the packet with. |
356 | | * @param[in] secret_len The length of the secret. |
357 | | * @return |
358 | | * - <0 on error |
359 | | * - 0 on success |
360 | | */ |
361 | | int fr_radius_sign(uint8_t *packet, uint8_t const *vector, |
362 | | uint8_t const *secret, size_t secret_len) |
363 | 0 | { |
364 | 0 | uint8_t *msg, *end; |
365 | 0 | size_t packet_len = fr_nbo_to_uint16(packet + 2); |
366 | | |
367 | | /* |
368 | | * No real limit on secret length, this is just |
369 | | * to catch uninitialised fields. |
370 | | */ |
371 | 0 | if (!fr_cond_assert(secret_len <= UINT16_MAX)) { |
372 | 0 | fr_strerror_printf("Secret is too long. Expected <= %u, got %zu", |
373 | 0 | (unsigned int) UINT16_MAX, secret_len); |
374 | 0 | return -1; |
375 | 0 | } |
376 | | |
377 | 0 | if (packet_len < RADIUS_HEADER_LENGTH) { |
378 | 0 | fr_strerror_const("Packet must be encoded before calling fr_radius_sign()"); |
379 | 0 | return -1; |
380 | 0 | } |
381 | | |
382 | | /* |
383 | | * Find Message-Authenticator. Its value has to be |
384 | | * calculated before we calculate the Request |
385 | | * Authenticator or the Response Authenticator. |
386 | | */ |
387 | 0 | msg = packet + RADIUS_HEADER_LENGTH; |
388 | 0 | end = packet + packet_len; |
389 | |
|
390 | 0 | while (msg < end) { |
391 | 0 | if ((end - msg) < 2) goto invalid_attribute; |
392 | | |
393 | 0 | if (msg[0] != FR_MESSAGE_AUTHENTICATOR) { |
394 | 0 | if (msg[1] < 2) goto invalid_attribute; |
395 | | |
396 | 0 | if ((msg + msg[1]) > end) { |
397 | 0 | invalid_attribute: |
398 | 0 | fr_strerror_printf("Invalid attribute at offset %zd", msg - packet); |
399 | 0 | return -1; |
400 | 0 | } |
401 | 0 | msg += msg[1]; |
402 | 0 | continue; |
403 | 0 | } |
404 | | |
405 | 0 | if (msg[1] < 18) { |
406 | 0 | fr_strerror_const("Message-Authenticator is too small"); |
407 | 0 | return -1; |
408 | 0 | } |
409 | | |
410 | 0 | switch (packet[0]) { |
411 | 0 | case FR_RADIUS_CODE_ACCOUNTING_REQUEST: |
412 | 0 | case FR_RADIUS_CODE_DISCONNECT_REQUEST: |
413 | 0 | case FR_RADIUS_CODE_COA_REQUEST: |
414 | 0 | memset(packet + 4, 0, RADIUS_AUTH_VECTOR_LENGTH); |
415 | 0 | break; |
416 | | |
417 | 0 | case FR_RADIUS_CODE_ACCESS_ACCEPT: |
418 | 0 | case FR_RADIUS_CODE_ACCESS_REJECT: |
419 | 0 | case FR_RADIUS_CODE_ACCESS_CHALLENGE: |
420 | 0 | case FR_RADIUS_CODE_ACCOUNTING_RESPONSE: |
421 | 0 | case FR_RADIUS_CODE_DISCONNECT_ACK: |
422 | 0 | case FR_RADIUS_CODE_DISCONNECT_NAK: |
423 | 0 | case FR_RADIUS_CODE_COA_ACK: |
424 | 0 | case FR_RADIUS_CODE_COA_NAK: |
425 | 0 | case FR_RADIUS_CODE_PROTOCOL_ERROR: |
426 | 0 | if (!vector) goto need_original; |
427 | 0 | memcpy(packet + 4, vector, RADIUS_AUTH_VECTOR_LENGTH); |
428 | 0 | break; |
429 | | |
430 | 0 | case FR_RADIUS_CODE_ACCESS_REQUEST: |
431 | 0 | case FR_RADIUS_CODE_STATUS_SERVER: |
432 | | /* packet + 4 MUST be the Request Authenticator filled with random data */ |
433 | 0 | break; |
434 | | |
435 | 0 | default: |
436 | 0 | goto bad_packet; |
437 | 0 | } |
438 | | |
439 | | /* |
440 | | * Force Message-Authenticator to be zero, |
441 | | * calculate the HMAC, and put it into the |
442 | | * Message-Authenticator attribute. |
443 | | */ |
444 | 0 | memset(msg + 2, 0, RADIUS_AUTH_VECTOR_LENGTH); |
445 | 0 | fr_hmac_md5(msg + 2, packet, packet_len, secret, secret_len); |
446 | 0 | break; |
447 | 0 | } |
448 | | |
449 | | /* |
450 | | * Initialize the request authenticator. |
451 | | */ |
452 | 0 | switch (packet[0]) { |
453 | 0 | case FR_RADIUS_CODE_ACCOUNTING_REQUEST: |
454 | 0 | case FR_RADIUS_CODE_DISCONNECT_REQUEST: |
455 | 0 | case FR_RADIUS_CODE_COA_REQUEST: |
456 | 0 | memset(packet + 4, 0, RADIUS_AUTH_VECTOR_LENGTH); |
457 | 0 | break; |
458 | | |
459 | 0 | case FR_RADIUS_CODE_ACCESS_ACCEPT: |
460 | 0 | case FR_RADIUS_CODE_ACCESS_REJECT: |
461 | 0 | case FR_RADIUS_CODE_ACCESS_CHALLENGE: |
462 | 0 | case FR_RADIUS_CODE_ACCOUNTING_RESPONSE: |
463 | 0 | case FR_RADIUS_CODE_DISCONNECT_ACK: |
464 | 0 | case FR_RADIUS_CODE_DISCONNECT_NAK: |
465 | 0 | case FR_RADIUS_CODE_COA_ACK: |
466 | 0 | case FR_RADIUS_CODE_COA_NAK: |
467 | 0 | case FR_RADIUS_CODE_PROTOCOL_ERROR: |
468 | 0 | if (!vector) { |
469 | 0 | need_original: |
470 | 0 | fr_strerror_const("Cannot sign response packet without a request packet"); |
471 | 0 | return -1; |
472 | 0 | } |
473 | 0 | memcpy(packet + 4, vector, RADIUS_AUTH_VECTOR_LENGTH); |
474 | 0 | break; |
475 | | |
476 | | /* |
477 | | * The Request Authenticator is random numbers. |
478 | | * We don't need to sign anything else, so |
479 | | * return. |
480 | | */ |
481 | 0 | case FR_RADIUS_CODE_ACCESS_REQUEST: |
482 | 0 | case FR_RADIUS_CODE_STATUS_SERVER: |
483 | 0 | return 0; |
484 | | |
485 | 0 | default: |
486 | 0 | bad_packet: |
487 | 0 | fr_strerror_printf("Cannot sign unknown packet code %u", packet[0]); |
488 | 0 | return -1; |
489 | 0 | } |
490 | | |
491 | | /* |
492 | | * Request / Response Authenticator = MD5(packet + secret) |
493 | | */ |
494 | 0 | { |
495 | 0 | fr_md5_ctx_t *md5_ctx; |
496 | |
|
497 | 0 | md5_ctx = fr_md5_ctx_alloc_from_list(); |
498 | 0 | fr_md5_update(md5_ctx, packet, packet_len); |
499 | 0 | fr_md5_update(md5_ctx, secret, secret_len); |
500 | 0 | fr_md5_final(packet + 4, md5_ctx); |
501 | 0 | fr_md5_ctx_free_from_list(&md5_ctx); |
502 | 0 | } |
503 | |
|
504 | 0 | return 0; |
505 | 0 | } |
506 | | |
507 | | char const *fr_radius_decode_fail_reason[FR_RADIUS_FAIL_MAX + 1] = { |
508 | | [FR_RADIUS_FAIL_NONE] = "none", |
509 | | [FR_RADIUS_FAIL_MIN_LENGTH_PACKET] = "packet is smaller than the minimum packet length", |
510 | | [FR_RADIUS_FAIL_MAX_LENGTH_PACKET] = "packet is larger than the maximum packet length", |
511 | | [FR_RADIUS_FAIL_MIN_LENGTH_FIELD] = "header 'length' field has a value smaller than the minimum packet length", |
512 | | [FR_RADIUS_FAIL_MIN_LENGTH_MISMATCH] = "header 'length' field has a value larger than the received data", |
513 | | [FR_RADIUS_FAIL_UNKNOWN_PACKET_CODE] = "unknown packet code", |
514 | | [FR_RADIUS_FAIL_UNEXPECTED_REQUEST_CODE] = "unexpected request code", |
515 | | [FR_RADIUS_FAIL_UNEXPECTED_RESPONSE_CODE] = "unexpected response code", |
516 | | [FR_RADIUS_FAIL_TOO_MANY_ATTRIBUTES] = "packet contains too many attributes", |
517 | | |
518 | | [FR_RADIUS_FAIL_INVALID_ATTRIBUTE] = "attribute number 0 is invalid", |
519 | | |
520 | | [FR_RADIUS_FAIL_HEADER_OVERFLOW] = "attribute header overflows the packet", |
521 | | [FR_RADIUS_FAIL_ATTRIBUTE_TOO_SHORT] = "attribute 'length' field contains invalid value", |
522 | | [FR_RADIUS_FAIL_ATTRIBUTE_OVERFLOW] = "attribute 'length' field overflows the packet", |
523 | | [FR_RADIUS_FAIL_ATTRIBUTE_DECODE] = "unable to decode attributes", |
524 | | |
525 | | [FR_RADIUS_FAIL_MA_INVALID_LENGTH] = "Message-Authenticator has invalid length", |
526 | | [FR_RADIUS_FAIL_MA_MISSING] = "Message-Authenticator is required for this packet, but it is missing", |
527 | | [FR_RADIUS_FAIL_MA_INVALID] = "Message-Authenticator fails verification. shared secret is incorrect", |
528 | | [FR_RADIUS_FAIL_MA_TOO_MANY] = "More than one Message-Authenticator in a packet is invalid", |
529 | | [FR_RADIUS_FAIL_PROXY_STATE_MISSING_MA] = "The packet contains Proxy-State, but no Message-Authenticator", |
530 | | |
531 | | [FR_RADIUS_FAIL_VERIFY] = "packet fails verification, shared secret is incorrect", |
532 | | [FR_RADIUS_FAIL_NO_MATCHING_REQUEST] = "did not find request which matched response", |
533 | | [FR_RADIUS_FAIL_IO_ERROR] = "IO error", |
534 | | [FR_RADIUS_FAIL_MAX] = "???", |
535 | | }; |
536 | | |
537 | | /** See if the data pointed to by PTR is a valid RADIUS packet. |
538 | | * |
539 | | * @param[in] packet to check. |
540 | | * @param[in,out] packet_len_p The size of the packet data. |
541 | | * @param[in] max_attributes to allow in the packet. |
542 | | * @param[in] require_message_authenticator whether we require Message-Authenticator. |
543 | | * @param[in] reason if not NULL, will have the failure reason written to where it points. |
544 | | * @return |
545 | | * - True on success. |
546 | | * - False on failure. |
547 | | */ |
548 | | bool fr_radius_ok(uint8_t const *packet, size_t *packet_len_p, |
549 | | uint32_t max_attributes, bool require_message_authenticator, fr_radius_decode_fail_t *reason) |
550 | 4.88k | { |
551 | 4.88k | uint8_t const *attr, *end; |
552 | 4.88k | size_t totallen; |
553 | 4.88k | bool seen_ma = false; |
554 | 4.88k | uint32_t num_attributes; |
555 | 4.88k | fr_radius_decode_fail_t failure = FR_RADIUS_FAIL_NONE; |
556 | 4.88k | size_t packet_len = *packet_len_p; |
557 | | |
558 | | /* |
559 | | * Check for packets smaller than the packet header. |
560 | | * |
561 | | * RFC 2865, Section 3., subsection 'length' says: |
562 | | * |
563 | | * "The minimum length is 20 ..." |
564 | | */ |
565 | 4.88k | if (packet_len < RADIUS_HEADER_LENGTH) { |
566 | 9 | failure = FR_RADIUS_FAIL_MIN_LENGTH_PACKET; |
567 | 9 | goto finish; |
568 | 9 | } |
569 | | |
570 | | |
571 | | /* |
572 | | * Check for packets with mismatched size. |
573 | | * i.e. We've received 128 bytes, and the packet header |
574 | | * says it's 256 bytes long. |
575 | | */ |
576 | 4.87k | totallen = fr_nbo_to_uint16(packet + 2); |
577 | | |
578 | | /* |
579 | | * Code of 0 is not understood. |
580 | | * Code of 16 or greater is not understood. |
581 | | */ |
582 | 4.87k | if ((packet[0] == 0) || |
583 | 4.87k | (packet[0] >= FR_RADIUS_CODE_MAX)) { |
584 | 10 | failure = FR_RADIUS_FAIL_UNKNOWN_PACKET_CODE; |
585 | 10 | goto finish; |
586 | 10 | } |
587 | | |
588 | 4.86k | switch (packet[0]) { |
589 | | /* |
590 | | * Message-Authenticator is required in Status-Server |
591 | | * packets, otherwise they can be trivially forged. |
592 | | */ |
593 | 6 | case FR_RADIUS_CODE_STATUS_SERVER: |
594 | 6 | require_message_authenticator = true; |
595 | 6 | break; |
596 | | |
597 | | /* |
598 | | * Message-Authenticator may or may not be |
599 | | * required for Access-* packets. |
600 | | */ |
601 | 556 | case FR_RADIUS_CODE_ACCESS_REQUEST: |
602 | 969 | case FR_RADIUS_CODE_ACCESS_ACCEPT: |
603 | 1.34k | case FR_RADIUS_CODE_ACCESS_CHALLENGE: |
604 | 1.52k | case FR_RADIUS_CODE_ACCESS_REJECT: |
605 | 1.62k | case FR_RADIUS_CODE_PROTOCOL_ERROR: |
606 | 1.62k | break; |
607 | | |
608 | | /* |
609 | | * Message-Authenticator is not required for all other packets, but is required if the |
610 | | * caller asks for it. |
611 | | */ |
612 | 334 | case FR_RADIUS_CODE_ACCOUNTING_REQUEST: |
613 | 655 | case FR_RADIUS_CODE_ACCOUNTING_RESPONSE: |
614 | | |
615 | 1.28k | case FR_RADIUS_CODE_COA_REQUEST: |
616 | 1.71k | case FR_RADIUS_CODE_COA_ACK: |
617 | 2.45k | case FR_RADIUS_CODE_COA_NAK: |
618 | | |
619 | 2.65k | case FR_RADIUS_CODE_DISCONNECT_REQUEST: |
620 | 2.89k | case FR_RADIUS_CODE_DISCONNECT_ACK: |
621 | 3.22k | case FR_RADIUS_CODE_DISCONNECT_NAK: |
622 | 3.22k | break; |
623 | | |
624 | | /* |
625 | | * All other packet codes are not handled by the encoder, so we reject them. |
626 | | */ |
627 | 12 | default: |
628 | 12 | failure = FR_RADIUS_FAIL_UNKNOWN_PACKET_CODE; |
629 | 12 | goto finish; |
630 | 4.86k | } |
631 | | |
632 | | /* |
633 | | * Repeat the length checks. This time, instead of |
634 | | * looking at the data we received, look at the value |
635 | | * of the 'length' field inside of the packet. |
636 | | * |
637 | | * Check for packets smaller than the packet header. |
638 | | * |
639 | | * RFC 2865, Section 3., subsection 'length' says: |
640 | | * |
641 | | * "The minimum length is 20 ..." |
642 | | */ |
643 | 4.85k | if (totallen < RADIUS_HEADER_LENGTH) { |
644 | 5 | failure = FR_RADIUS_FAIL_MIN_LENGTH_FIELD; |
645 | 5 | goto finish; |
646 | 5 | } |
647 | | |
648 | | /* |
649 | | * And again, for the value of the 'length' field. |
650 | | * |
651 | | * RFC 2865, Section 3., subsection 'length' says: |
652 | | * |
653 | | * " ... and maximum length is 4096." |
654 | | * |
655 | | * HOWEVER. This requirement is for the network layer. |
656 | | * If the code gets here, we assume that a well-formed |
657 | | * packet is an OK packet. |
658 | | * |
659 | | * We allow both the UDP data length, and the RADIUS |
660 | | * "length" field to contain up to 64K of data. |
661 | | */ |
662 | | |
663 | | /* |
664 | | * RFC 2865, Section 3., subsection 'length' says: |
665 | | * |
666 | | * "If the packet is shorter than the Length field |
667 | | * indicates, it MUST be silently discarded." |
668 | | * |
669 | | * i.e. No response to the NAS. |
670 | | */ |
671 | 4.84k | if (totallen > packet_len) { |
672 | 21 | failure = FR_RADIUS_FAIL_MIN_LENGTH_MISMATCH; |
673 | 21 | goto finish; |
674 | 21 | } |
675 | | |
676 | | /* |
677 | | * RFC 2865, Section 3., subsection 'length' says: |
678 | | * |
679 | | * "Octets outside the range of the Length field MUST be |
680 | | * treated as padding and ignored on reception." |
681 | | */ |
682 | 4.82k | if (totallen < packet_len) { |
683 | 162 | *packet_len_p = packet_len = totallen; |
684 | 162 | } |
685 | | |
686 | | /* |
687 | | * Walk through the packet's attributes, ensuring that |
688 | | * they add up EXACTLY to the size of the packet. |
689 | | * |
690 | | * If they don't, then the attributes either under-fill |
691 | | * or over-fill the packet. Any parsing of the packet |
692 | | * is impossible, and will result in unknown side effects. |
693 | | * |
694 | | * This would ONLY happen with buggy RADIUS implementations, |
695 | | * or with an intentional attack. Either way, we do NOT want |
696 | | * to be vulnerable to this problem. |
697 | | */ |
698 | 4.82k | attr = packet + RADIUS_HEADER_LENGTH; |
699 | 4.82k | end = packet + packet_len; |
700 | 4.82k | num_attributes = 0; |
701 | | |
702 | 67.0k | while (attr < end) { |
703 | | /* |
704 | | * We need at least 2 bytes to check the |
705 | | * attribute header. |
706 | | */ |
707 | 62.2k | if ((end - attr) < 2) { |
708 | 3 | failure = FR_RADIUS_FAIL_HEADER_OVERFLOW; |
709 | 3 | goto finish; |
710 | 3 | } |
711 | | |
712 | | /* |
713 | | * Attribute number zero is NOT defined. |
714 | | */ |
715 | 62.2k | if (attr[0] == 0) { |
716 | 11 | failure = FR_RADIUS_FAIL_INVALID_ATTRIBUTE; |
717 | 11 | goto finish; |
718 | 11 | } |
719 | | |
720 | | /* |
721 | | * Attributes are at LEAST as long as the ID & length |
722 | | * fields. Anything shorter is an invalid attribute. |
723 | | */ |
724 | 62.2k | if (attr[1] < 2) { |
725 | 8 | failure = FR_RADIUS_FAIL_ATTRIBUTE_TOO_SHORT; |
726 | 8 | goto finish; |
727 | 8 | } |
728 | | |
729 | | /* |
730 | | * If there are fewer bytes in the packet than in the |
731 | | * attribute, it's a bad packet. |
732 | | */ |
733 | 62.2k | if ((attr + attr[1]) > end) { |
734 | 10 | failure = FR_RADIUS_FAIL_ATTRIBUTE_OVERFLOW; |
735 | 10 | goto finish; |
736 | 10 | } |
737 | | |
738 | | /* |
739 | | * Sanity check the attributes for length. |
740 | | */ |
741 | 62.1k | switch (attr[0]) { |
742 | 61.8k | default: /* don't do anything by default */ |
743 | 61.8k | break; |
744 | | |
745 | | /* |
746 | | * If there's an EAP-Message, we require |
747 | | * a Message-Authenticator. |
748 | | */ |
749 | 61.8k | case FR_EAP_MESSAGE: |
750 | 293 | require_message_authenticator = true; |
751 | 293 | break; |
752 | | |
753 | 15 | case FR_MESSAGE_AUTHENTICATOR: |
754 | 15 | if (attr[1] != 2 + RADIUS_AUTH_VECTOR_LENGTH) { |
755 | 12 | failure = FR_RADIUS_FAIL_MA_INVALID_LENGTH; |
756 | 12 | goto finish; |
757 | 12 | } |
758 | | |
759 | | /* |
760 | | * Can't have two of them. |
761 | | */ |
762 | 3 | if (seen_ma) { |
763 | 1 | failure = FR_RADIUS_FAIL_MA_TOO_MANY; |
764 | 1 | goto finish; |
765 | 1 | } |
766 | | |
767 | 2 | seen_ma = true; |
768 | 2 | break; |
769 | 62.1k | } |
770 | | |
771 | 62.1k | attr += attr[1]; |
772 | 62.1k | num_attributes++; /* seen one more attribute */ |
773 | | |
774 | | /* |
775 | | * If we're configured to look for a maximum number of |
776 | | * attributes, and we've seen more than that maximum, |
777 | | * then throw the packet away, as a possible DoS. |
778 | | */ |
779 | 62.1k | if (num_attributes > max_attributes) { |
780 | 1 | failure = FR_RADIUS_FAIL_TOO_MANY_ATTRIBUTES; |
781 | 1 | goto finish; |
782 | 1 | } |
783 | 62.1k | } |
784 | | |
785 | | /* |
786 | | * http://www.freeradius.org/rfc/rfc2869.html#EAP-Message |
787 | | * |
788 | | * A packet with an EAP-Message attribute MUST also have |
789 | | * a Message-Authenticator attribute. |
790 | | * |
791 | | * A Message-Authenticator all by itself is OK, though. |
792 | | * |
793 | | * Similarly, Status-Server packets MUST contain |
794 | | * Message-Authenticator attributes. |
795 | | */ |
796 | 4.78k | if (require_message_authenticator && !seen_ma) { |
797 | 9 | failure = FR_RADIUS_FAIL_MA_MISSING; |
798 | 9 | goto finish; |
799 | 9 | } |
800 | | |
801 | 4.88k | finish: |
802 | | |
803 | 4.88k | if (reason) *reason = failure; |
804 | | |
805 | 4.88k | return (failure == FR_RADIUS_FAIL_NONE); |
806 | 4.78k | } |
807 | | |
808 | | |
809 | | /** Verify the signature of a request / response packet |
810 | | * |
811 | | * This function does its work by calling fr_radius_sign(), and then comparing the signature in the packet |
812 | | * with the one we calculated. If they differ, there's a problem. |
813 | | * |
814 | | * @note - We rely on the security of the shared secret for UDP and TCP transport. For TLS transport, we |
815 | | * rely on TLS to make the RADIUS packets both secure and private. |
816 | | * |
817 | | * @note - The BlastRADIUS mitigations require that "require_message_authenticator" and "limit_proxy_state" |
818 | | * are used only for Access-Request packets. These mitigations MUST NOT be used for any any other packet |
819 | | * codes. |
820 | | * |
821 | | * The caller should have called fr_radius_packet_ok() to see if the packet is well-formed, at least for the |
822 | | * base RFC attributes. |
823 | | * |
824 | | * @param[in] packet the raw RADIUS packet (request or response) |
825 | | * @param[in] vector the original packet vector |
826 | | * @param[in] secret the shared secret |
827 | | * @param[in] secret_len the length of the secret |
828 | | * @param[in] require_message_authenticator whether we require Message-Authenticator. |
829 | | * @param[in] limit_proxy_state whether we allow Proxy-State without Message-Authenticator. |
830 | | * @return |
831 | | * < <0 on error (negative fr_radius_decode_fail_t) |
832 | | * - 0 on success. |
833 | | */ |
834 | | int fr_radius_verify(uint8_t *packet, uint8_t const *vector, |
835 | | uint8_t const *secret, size_t secret_len, |
836 | | bool require_message_authenticator, bool limit_proxy_state) |
837 | 0 | { |
838 | 0 | bool found_message_authenticator = false; |
839 | 0 | bool found_proxy_state = false; |
840 | 0 | int rcode; |
841 | 0 | int code; |
842 | 0 | uint8_t *attr, *msg, *end; |
843 | 0 | size_t packet_len = fr_nbo_to_uint16(packet + 2); |
844 | 0 | uint8_t request_authenticator[RADIUS_AUTH_VECTOR_LENGTH]; |
845 | 0 | uint8_t message_authenticator[RADIUS_AUTH_VECTOR_LENGTH]; |
846 | |
|
847 | 0 | if (packet_len < RADIUS_HEADER_LENGTH) { |
848 | 0 | fr_strerror_printf("invalid packet length %zu", packet_len); |
849 | 0 | return -FR_RADIUS_FAIL_MIN_LENGTH_PACKET; |
850 | 0 | } |
851 | | |
852 | 0 | code = packet[0]; |
853 | 0 | if (!code || (code >= FR_RADIUS_CODE_MAX)) { |
854 | 0 | fr_strerror_printf("Unknown reply code %d", code); |
855 | 0 | return -FR_RADIUS_FAIL_UNKNOWN_PACKET_CODE; |
856 | 0 | } |
857 | | |
858 | | /* |
859 | | * RFC 5997 says that all Status-Server packets MUST contain Message-Authenticator. |
860 | | */ |
861 | 0 | require_message_authenticator |= (code == FR_RADIUS_CODE_STATUS_SERVER); |
862 | |
|
863 | 0 | memcpy(request_authenticator, packet + 4, sizeof(request_authenticator)); |
864 | | |
865 | | /* |
866 | | * Find Message-Authenticator. Its value has to be |
867 | | * calculated before we calculate the Request |
868 | | * Authenticator or the Response Authenticator. |
869 | | */ |
870 | 0 | msg = NULL; |
871 | 0 | attr = packet + RADIUS_HEADER_LENGTH; |
872 | 0 | end = packet + packet_len; |
873 | | |
874 | | /* |
875 | | * See what we need to do in order to verify the packet. |
876 | | * |
877 | | * Note that fr_radius_packet_ok() also does these checks, but it's worth re-doing them here so |
878 | | * that potential API mis-use is safe. i.e. we do "defense in depth". |
879 | | */ |
880 | 0 | while (attr < end) { |
881 | 0 | if ((end - attr) < 2) goto invalid_attribute; |
882 | | |
883 | 0 | if (attr[1] < 2) goto invalid_attribute; |
884 | | |
885 | 0 | if ((attr + attr[1]) > end) { |
886 | 0 | invalid_attribute: |
887 | 0 | fr_strerror_printf("invalid attribute at offset %zd", attr - packet); |
888 | 0 | return -FR_RADIUS_FAIL_INVALID_ATTRIBUTE; |
889 | 0 | } |
890 | | |
891 | | /* |
892 | | * If there's no Message-Authenticator, then we need to check Proxy-State, but only if |
893 | | * the caller asked us to limit Proxy-State. |
894 | | */ |
895 | 0 | if (attr[0] == FR_PROXY_STATE) { |
896 | 0 | found_proxy_state = limit_proxy_state; |
897 | 0 | goto next; |
898 | 0 | } |
899 | | |
900 | | /* |
901 | | * Check the contents of Message-Authenticator |
902 | | */ |
903 | 0 | if (attr[0] == FR_MESSAGE_AUTHENTICATOR) { |
904 | 0 | if (found_message_authenticator) { |
905 | 0 | fr_strerror_const("Multiple Message-Authenticators are invalid"); |
906 | 0 | return -FR_RADIUS_FAIL_MA_TOO_MANY; |
907 | 0 | } |
908 | | |
909 | | |
910 | 0 | if (attr[1] != 18) { |
911 | 0 | fr_strerror_const("too small Message-Authenticator"); |
912 | 0 | return -FR_RADIUS_FAIL_MA_INVALID_LENGTH; |
913 | 0 | } |
914 | | |
915 | | /* |
916 | | * If we have found Message-Authenticator, then we verify that. We also can stop |
917 | | * processing the packet, as we don't care about the contents of Proxy-State. |
918 | | */ |
919 | 0 | memcpy(message_authenticator, attr + 2, sizeof(message_authenticator)); |
920 | 0 | found_message_authenticator = true; |
921 | 0 | msg = attr; |
922 | 0 | goto next; |
923 | 0 | } |
924 | | |
925 | | /* |
926 | | * RFC 3579 Section 3.1 requires Message-Authenticator if the packet contains |
927 | | * EAP-Message. |
928 | | */ |
929 | 0 | if (attr[0] == FR_EAP_MESSAGE) require_message_authenticator = true; |
930 | |
|
931 | 0 | next: |
932 | 0 | attr += attr[1]; |
933 | 0 | } |
934 | | |
935 | | /* |
936 | | * Enforce limit_proxy_state for Access-Request packets. |
937 | | */ |
938 | 0 | if (code == FR_RADIUS_CODE_ACCESS_REQUEST) { |
939 | 0 | if (limit_proxy_state && found_proxy_state && !found_message_authenticator) { |
940 | 0 | fr_strerror_const("Proxy-State is not allowed without Message-Authenticator"); |
941 | 0 | return -FR_RADIUS_FAIL_PROXY_STATE_MISSING_MA; |
942 | 0 | } |
943 | 0 | } |
944 | | |
945 | | /* |
946 | | * The require_message_authenticator flag can be set for any of these packet types. For other |
947 | | * packet types, we check it if it exists, but we allow packets to not contain it. |
948 | | */ |
949 | 0 | if (require_message_authenticator && !found_message_authenticator) { |
950 | 0 | fr_strerror_printf("%s is missing the required Message-Authenticator attribute", |
951 | 0 | fr_radius_packet_name[code]); |
952 | 0 | return -FR_RADIUS_FAIL_MA_MISSING; |
953 | 0 | } |
954 | | |
955 | | /* |
956 | | * Overwrite the contents of Message-Authenticator |
957 | | * with the one we calculate. |
958 | | */ |
959 | 0 | rcode = fr_radius_sign(packet, vector, secret, secret_len); |
960 | 0 | if (rcode < 0) { |
961 | 0 | fr_strerror_const_push("Failed calculating correct authenticator"); |
962 | 0 | return -FR_RADIUS_FAIL_VERIFY; |
963 | 0 | } |
964 | | |
965 | | /* |
966 | | * Check the Message-Authenticator first. |
967 | | * |
968 | | * If it's invalid, restore the original |
969 | | * Message-Authenticator and Request Authenticator |
970 | | * fields. |
971 | | * |
972 | | * If it's valid the original and calculated |
973 | | * message authenticators are the same, so we don't |
974 | | * need to do anything. |
975 | | */ |
976 | 0 | if (msg && |
977 | 0 | (fr_digest_cmp(message_authenticator, msg + 2, sizeof(message_authenticator)) != 0)) { |
978 | 0 | memcpy(msg + 2, message_authenticator, sizeof(message_authenticator)); |
979 | 0 | memcpy(packet + 4, request_authenticator, sizeof(request_authenticator)); |
980 | |
|
981 | 0 | fr_strerror_const("invalid Message-Authenticator (shared secret is incorrect)"); |
982 | 0 | return -FR_RADIUS_FAIL_MA_INVALID; |
983 | 0 | } |
984 | | |
985 | | /* |
986 | | * These are random numbers, so there's no point in |
987 | | * comparing them. |
988 | | */ |
989 | 0 | if ((code == FR_RADIUS_CODE_ACCESS_REQUEST) || (code == FR_RADIUS_CODE_STATUS_SERVER)) { |
990 | 0 | return 0; |
991 | 0 | } |
992 | | |
993 | | /* |
994 | | * Check the Request Authenticator. |
995 | | */ |
996 | 0 | if (fr_digest_cmp(request_authenticator, packet + 4, sizeof(request_authenticator)) != 0) { |
997 | 0 | memcpy(packet + 4, request_authenticator, sizeof(request_authenticator)); |
998 | 0 | if (vector) { |
999 | 0 | fr_strerror_const("invalid Response Authenticator (shared secret is incorrect)"); |
1000 | 0 | } else { |
1001 | 0 | fr_strerror_const("invalid Request Authenticator (shared secret is incorrect)"); |
1002 | 0 | } |
1003 | 0 | return -FR_RADIUS_FAIL_VERIFY; |
1004 | 0 | } |
1005 | | |
1006 | 0 | return 0; |
1007 | 0 | } |
1008 | | |
1009 | | void *fr_radius_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx); |
1010 | | |
1011 | | void *fr_radius_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx) |
1012 | 0 | { |
1013 | 0 | fr_pair_t *c = current; |
1014 | 0 | fr_dict_t *dict = talloc_get_type_abort(uctx, fr_dict_t); |
1015 | |
|
1016 | 0 | while ((c = fr_dcursor_list_next(cursor, c))) { |
1017 | 0 | PAIR_VERIFY(c); |
1018 | 0 | if ((c->da->dict == dict) && |
1019 | 0 | (!c->da->flags.internal || ((c->da->attr > FR_TAG_BASE) && (c->da->attr < (FR_TAG_BASE + 0x20))))) { |
1020 | 0 | break; |
1021 | 0 | } |
1022 | 0 | } |
1023 | |
|
1024 | 0 | return c; |
1025 | 0 | } |
1026 | | |
1027 | | |
1028 | | ssize_t fr_radius_encode(fr_dbuff_t *dbuff, fr_pair_list_t *vps, fr_radius_encode_ctx_t *packet_ctx) |
1029 | 0 | { |
1030 | 0 | ssize_t slen; |
1031 | 0 | fr_pair_t const *vp; |
1032 | 0 | fr_dcursor_t cursor; |
1033 | 0 | fr_dbuff_t work_dbuff, length_dbuff; |
1034 | | |
1035 | | /* |
1036 | | * The RADIUS header can't do more than 64K of data. |
1037 | | */ |
1038 | 0 | work_dbuff = FR_DBUFF_MAX(dbuff, 65535); |
1039 | |
|
1040 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, packet_ctx->code, packet_ctx->id); |
1041 | 0 | length_dbuff = FR_DBUFF(&work_dbuff); |
1042 | 0 | FR_DBUFF_IN_RETURN(&work_dbuff, (uint16_t) RADIUS_HEADER_LENGTH); |
1043 | | |
1044 | 0 | switch (packet_ctx->code) { |
1045 | 0 | case FR_RADIUS_CODE_ACCESS_REQUEST: |
1046 | 0 | case FR_RADIUS_CODE_STATUS_SERVER: |
1047 | 0 | packet_ctx->request_authenticator = fr_dbuff_current(&work_dbuff); |
1048 | | |
1049 | | /* |
1050 | | * Allow over-rides of the authentication vector for testing. |
1051 | | */ |
1052 | 0 | vp = fr_pair_find_by_da(vps, NULL, attr_packet_authentication_vector); |
1053 | 0 | if (vp && (vp->vp_length >= RADIUS_AUTH_VECTOR_LENGTH)) { |
1054 | 0 | FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_octets, RADIUS_AUTH_VECTOR_LENGTH); |
1055 | 0 | } else { |
1056 | 0 | int i; |
1057 | |
|
1058 | 0 | for (i = 0; i < 4; i++) { |
1059 | 0 | FR_DBUFF_IN_RETURN(&work_dbuff, (uint32_t) fr_rand()); |
1060 | 0 | } |
1061 | 0 | } |
1062 | 0 | break; |
1063 | | |
1064 | 0 | case FR_RADIUS_CODE_ACCESS_ACCEPT: |
1065 | 0 | case FR_RADIUS_CODE_ACCESS_REJECT: |
1066 | 0 | case FR_RADIUS_CODE_ACCESS_CHALLENGE: |
1067 | 0 | case FR_RADIUS_CODE_ACCOUNTING_RESPONSE: |
1068 | 0 | case FR_RADIUS_CODE_DISCONNECT_ACK: |
1069 | 0 | case FR_RADIUS_CODE_DISCONNECT_NAK: |
1070 | 0 | case FR_RADIUS_CODE_COA_ACK: |
1071 | 0 | case FR_RADIUS_CODE_COA_NAK: |
1072 | 0 | case FR_RADIUS_CODE_PROTOCOL_ERROR: |
1073 | 0 | if (!packet_ctx->request_authenticator) { |
1074 | 0 | fr_strerror_const("Cannot encode response without request"); |
1075 | 0 | return -1; |
1076 | 0 | } |
1077 | 0 | FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, packet_ctx->request_authenticator, RADIUS_AUTH_VECTOR_LENGTH); |
1078 | 0 | break; |
1079 | | |
1080 | 0 | case FR_RADIUS_CODE_ACCOUNTING_REQUEST: |
1081 | 0 | case FR_RADIUS_CODE_DISCONNECT_REQUEST: |
1082 | | /* |
1083 | | * Tunnel-Password encoded attributes are allowed |
1084 | | * in CoA-Request packets, by RFC 5176 Section |
1085 | | * 3.6. HOWEVER, the tunnel passwords are |
1086 | | * "encrypted" using the Request Authenticator, |
1087 | | * which is all zeros! That makes them much |
1088 | | * easier to decrypt. The only solution here is |
1089 | | * to say "don't do that!" |
1090 | | */ |
1091 | 0 | case FR_RADIUS_CODE_COA_REQUEST: |
1092 | 0 | packet_ctx->request_authenticator = fr_dbuff_current(&work_dbuff); |
1093 | |
|
1094 | 0 | FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, RADIUS_AUTH_VECTOR_LENGTH); |
1095 | 0 | break; |
1096 | | |
1097 | 0 | default: |
1098 | 0 | fr_strerror_printf("Cannot encode unknown packet code %d", packet_ctx->code); |
1099 | 0 | return -1; |
1100 | 0 | } |
1101 | | |
1102 | | /* |
1103 | | * Always add Message-Authenticator after the packet |
1104 | | * header for insecure transport protocols. |
1105 | | */ |
1106 | 0 | if (!packet_ctx->common->secure_transport) switch (packet_ctx->code) { |
1107 | 0 | case FR_RADIUS_CODE_ACCESS_ACCEPT: |
1108 | 0 | case FR_RADIUS_CODE_ACCESS_REJECT: |
1109 | 0 | case FR_RADIUS_CODE_ACCESS_CHALLENGE: |
1110 | | #ifdef NAS_VIOLATES_RFC |
1111 | | /* |
1112 | | * Allow ridiculous behavior for vendors who violate the RFCs. |
1113 | | * |
1114 | | * But only if there's no EAP-Message in the packet. |
1115 | | */ |
1116 | | if (packet_ctx->allow_vulnerable_clients && !fr_pair_find_by_da(vps, NULL, attr_eap_message)) { |
1117 | | break; |
1118 | | } |
1119 | | FALL_THROUGH; |
1120 | | #endif |
1121 | |
|
1122 | 0 | case FR_RADIUS_CODE_ACCESS_REQUEST: |
1123 | 0 | case FR_RADIUS_CODE_STATUS_SERVER: |
1124 | 0 | case FR_RADIUS_CODE_PROTOCOL_ERROR: |
1125 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, FR_MESSAGE_AUTHENTICATOR, 0x12, |
1126 | 0 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
1127 | 0 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); |
1128 | 0 | packet_ctx->seen_message_authenticator = true; |
1129 | 0 | break; |
1130 | | |
1131 | 0 | default: |
1132 | 0 | break; |
1133 | 0 | } |
1134 | | |
1135 | | /* |
1136 | | * If we're sending Protocol-Error, add in |
1137 | | * Original-Packet-Code manually. If the user adds it |
1138 | | * later themselves, well, too bad. |
1139 | | */ |
1140 | 0 | if (packet_ctx->code == FR_RADIUS_CODE_PROTOCOL_ERROR) { |
1141 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, FR_EXTENDED_ATTRIBUTE_1, 0x07, 0x04 /* Original-Packet-Code */, |
1142 | 0 | 0x00, 0x00, 0x00, packet_ctx->request_code); |
1143 | 0 | } |
1144 | | |
1145 | | /* |
1146 | | * Loop over the reply attributes for the packet. |
1147 | | */ |
1148 | 0 | fr_pair_dcursor_iter_init(&cursor, vps, fr_radius_next_encodable, dict_radius); |
1149 | 0 | while ((vp = fr_dcursor_current(&cursor))) { |
1150 | 0 | PAIR_VERIFY(vp); |
1151 | | |
1152 | | /* |
1153 | | * Encode an individual VP |
1154 | | */ |
1155 | 0 | slen = fr_radius_encode_pair(&work_dbuff, &cursor, packet_ctx); |
1156 | 0 | if (slen < 0) return slen; |
1157 | 0 | } /* done looping over all attributes */ |
1158 | | |
1159 | | /* |
1160 | | * Add Proxy-State to the end of the packet if the caller requested it. |
1161 | | */ |
1162 | 0 | if (packet_ctx->add_proxy_state) { |
1163 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, FR_PROXY_STATE, (uint8_t) (2 + sizeof(packet_ctx->common->proxy_state))); |
1164 | 0 | FR_DBUFF_IN_RETURN(&work_dbuff, packet_ctx->common->proxy_state); |
1165 | 0 | } |
1166 | | |
1167 | | /* |
1168 | | * Fill in the length field we zeroed out earlier. |
1169 | | * |
1170 | | */ |
1171 | 0 | fr_dbuff_in(&length_dbuff, (uint16_t) (fr_dbuff_used(&work_dbuff))); |
1172 | |
|
1173 | 0 | FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "%s encoded packet", __FUNCTION__); |
1174 | |
|
1175 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
1176 | 0 | } |
1177 | | |
1178 | | ssize_t fr_radius_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, |
1179 | | uint8_t *packet, size_t packet_len, |
1180 | | fr_radius_decode_ctx_t *decode_ctx) |
1181 | 4.77k | { |
1182 | 4.77k | ssize_t slen; |
1183 | 4.77k | uint8_t const *attr, *end; |
1184 | 4.77k | static const uint8_t zeros[RADIUS_AUTH_VECTOR_LENGTH] = {}; |
1185 | | |
1186 | 4.77k | decode_ctx->reason = FR_RADIUS_FAIL_NONE; |
1187 | | |
1188 | 4.77k | if (!decode_ctx->request_authenticator) { |
1189 | 0 | switch (packet[0]) { |
1190 | 0 | case FR_RADIUS_CODE_ACCESS_REQUEST: |
1191 | 0 | case FR_RADIUS_CODE_STATUS_SERVER: |
1192 | 0 | decode_ctx->request_authenticator = packet + 4; |
1193 | 0 | break; |
1194 | | |
1195 | 0 | case FR_RADIUS_CODE_ACCOUNTING_REQUEST: |
1196 | 0 | case FR_RADIUS_CODE_COA_REQUEST: |
1197 | 0 | case FR_RADIUS_CODE_DISCONNECT_REQUEST: |
1198 | 0 | decode_ctx->request_authenticator = zeros; |
1199 | 0 | break; |
1200 | | |
1201 | 0 | default: |
1202 | 0 | fr_strerror_const("No authentication vector passed for packet decode"); |
1203 | 0 | decode_ctx->reason = FR_RADIUS_FAIL_NO_MATCHING_REQUEST; |
1204 | 0 | return -1; |
1205 | 0 | } |
1206 | 0 | } |
1207 | | |
1208 | 4.77k | if (decode_ctx->request_code) { |
1209 | 0 | unsigned int code = packet[0]; |
1210 | |
|
1211 | 0 | if (code >= FR_RADIUS_CODE_MAX) { |
1212 | 0 | decode_ctx->reason = FR_RADIUS_FAIL_UNKNOWN_PACKET_CODE; |
1213 | 0 | return -1; |
1214 | 0 | } |
1215 | 0 | if (decode_ctx->request_code >= FR_RADIUS_CODE_MAX) { |
1216 | 0 | decode_ctx->reason = FR_RADIUS_FAIL_UNKNOWN_PACKET_CODE; |
1217 | 0 | return -1; |
1218 | 0 | } |
1219 | | |
1220 | 0 | if (!allowed_replies[code]) { |
1221 | 0 | decode_ctx->reason = FR_RADIUS_FAIL_UNEXPECTED_RESPONSE_CODE; |
1222 | 0 | return -1; |
1223 | 0 | } |
1224 | | |
1225 | | /* |
1226 | | * Protocol error can reply to any packet. |
1227 | | * |
1228 | | * Status-Server can get any reply. |
1229 | | * |
1230 | | * Otherwise the reply code must be associated with the request code we sent. |
1231 | | */ |
1232 | 0 | if ((allowed_replies[code] != decode_ctx->request_code) && |
1233 | 0 | (code != FR_RADIUS_CODE_PROTOCOL_ERROR) && |
1234 | 0 | (decode_ctx->request_code != FR_RADIUS_CODE_STATUS_SERVER)) { |
1235 | 0 | decode_ctx->reason = FR_RADIUS_FAIL_UNEXPECTED_RESPONSE_CODE; |
1236 | 0 | return -1; |
1237 | 0 | } |
1238 | 0 | } |
1239 | | |
1240 | | /* |
1241 | | * We can skip verification for dynamic client checks, and where packets are unsigned as with |
1242 | | * RADIUS/1.1. |
1243 | | */ |
1244 | 4.77k | if (decode_ctx->verify) { |
1245 | 0 | if (!decode_ctx->request_authenticator) decode_ctx->request_authenticator = zeros; |
1246 | |
|
1247 | 0 | if (fr_radius_verify(packet, decode_ctx->request_authenticator, |
1248 | 0 | (uint8_t const *) decode_ctx->common->secret, decode_ctx->common->secret_length, |
1249 | 0 | decode_ctx->require_message_authenticator, decode_ctx->limit_proxy_state) < 0) { |
1250 | 0 | decode_ctx->reason = FR_RADIUS_FAIL_VERIFY; |
1251 | 0 | return -1; |
1252 | 0 | } |
1253 | 0 | } |
1254 | | |
1255 | 4.77k | attr = packet + 20; |
1256 | 4.77k | end = packet + packet_len; |
1257 | | |
1258 | | /* |
1259 | | * The caller MUST have called fr_radius_ok() first. If |
1260 | | * he doesn't, all hell breaks loose. |
1261 | | */ |
1262 | 61.8k | while (attr < end) { |
1263 | 57.1k | slen = fr_radius_decode_pair(ctx, out, attr, (end - attr), decode_ctx); |
1264 | 57.1k | if (slen < 0) { |
1265 | 72 | decode_ctx->reason = FR_RADIUS_FAIL_ATTRIBUTE_DECODE; |
1266 | 72 | return slen; |
1267 | 72 | } |
1268 | | |
1269 | | /* |
1270 | | * If slen is larger than the room in the packet, |
1271 | | * all kinds of bad things happen. |
1272 | | */ |
1273 | 57.1k | if (!fr_cond_assert(slen <= (end - attr))) { |
1274 | 0 | return -slen; |
1275 | 0 | } |
1276 | | |
1277 | 57.1k | attr += slen; |
1278 | 57.1k | talloc_free_children(decode_ctx->tmp_ctx); |
1279 | 57.1k | } |
1280 | | |
1281 | | /* |
1282 | | * We've parsed the whole packet, return that. |
1283 | | */ |
1284 | 4.70k | return packet_len; |
1285 | 4.77k | } |
1286 | | |
1287 | | /** Simple wrapper for callers who just need a shared secret |
1288 | | * |
1289 | | * @note - All callers verify the packet via fr_radius_packet_verify() before calling this function. |
1290 | | */ |
1291 | | ssize_t fr_radius_decode_simple(TALLOC_CTX *ctx, fr_pair_list_t *out, |
1292 | | uint8_t *packet, size_t packet_len, |
1293 | | uint8_t const *vector, char const *secret) |
1294 | 0 | { |
1295 | 0 | ssize_t rcode; |
1296 | 0 | fr_radius_ctx_t common_ctx = {}; |
1297 | 0 | fr_radius_decode_ctx_t packet_ctx = {}; |
1298 | |
|
1299 | 0 | common_ctx.secret = secret; |
1300 | 0 | common_ctx.secret_length = strlen(secret); |
1301 | |
|
1302 | 0 | packet_ctx.common = &common_ctx; |
1303 | 0 | packet_ctx.tmp_ctx = talloc(ctx, uint8_t); |
1304 | 0 | packet_ctx.request_authenticator = vector; |
1305 | 0 | packet_ctx.end = packet + packet_len; |
1306 | |
|
1307 | 0 | rcode = fr_radius_decode(ctx, out, packet, packet_len, &packet_ctx); |
1308 | 0 | talloc_free(packet_ctx.tmp_ctx); |
1309 | |
|
1310 | 0 | return rcode; |
1311 | 0 | } |
1312 | | |
1313 | | int fr_radius_global_init(void) |
1314 | 10 | { |
1315 | 10 | if (instance_count > 0) { |
1316 | 2 | instance_count++; |
1317 | 2 | return 0; |
1318 | 2 | } |
1319 | | |
1320 | 8 | instance_count++; |
1321 | | |
1322 | 8 | if (fr_dict_autoload(libfreeradius_radius_dict) < 0) { |
1323 | 0 | fail: |
1324 | 0 | instance_count--; |
1325 | 0 | return -1; |
1326 | 0 | } |
1327 | | |
1328 | 8 | if (fr_dict_attr_autoload(libfreeradius_radius_dict_attr) < 0) { |
1329 | 0 | fr_dict_autofree(libfreeradius_radius_dict); |
1330 | 0 | goto fail; |
1331 | 0 | } |
1332 | | |
1333 | 8 | instantiated = true; |
1334 | 8 | return 0; |
1335 | 8 | } |
1336 | | |
1337 | | void fr_radius_global_free(void) |
1338 | 10 | { |
1339 | 10 | if (!instantiated) return; |
1340 | | |
1341 | 10 | if (--instance_count != 0) return; |
1342 | | |
1343 | 8 | fr_dict_autofree(libfreeradius_radius_dict); |
1344 | | |
1345 | 8 | instantiated = false; |
1346 | 8 | } |
1347 | | |
1348 | | static bool attr_valid(fr_dict_attr_t *da) |
1349 | 69.1k | { |
1350 | 69.1k | fr_radius_attr_flags_t const *flags = fr_radius_attr_flags(da); |
1351 | | |
1352 | 69.1k | if (da->parent->type == FR_TYPE_STRUCT) { |
1353 | 1.38k | if (flags->extended) { |
1354 | 0 | fr_strerror_const("Attributes with 'extended' flag cannot be used inside of a 'struct'"); |
1355 | 0 | return false; |
1356 | 0 | } |
1357 | | |
1358 | 1.38k | if (flags->long_extended) { |
1359 | 0 | fr_strerror_const("Attributes with 'long_extended' flag cannot be used inside of a 'struct'"); |
1360 | 0 | return false; |
1361 | 0 | } |
1362 | | |
1363 | | |
1364 | 1.38k | if (flags->concat) { |
1365 | 0 | fr_strerror_const("Attributes with 'concat' flag cannot be used inside of a 'struct'"); |
1366 | 0 | return false; |
1367 | 0 | } |
1368 | | |
1369 | 1.38k | if (flags->has_tag) { |
1370 | 0 | fr_strerror_const("Attributes with 'tag' flag cannot be used inside of a 'struct'"); |
1371 | 0 | return false; |
1372 | 0 | } |
1373 | | |
1374 | 1.38k | if (flags->abinary) { |
1375 | 0 | fr_strerror_const("Attributes with 'abinary' flag cannot be used inside of a 'struct'"); |
1376 | 0 | return false; |
1377 | 0 | } |
1378 | | |
1379 | 1.38k | if (flags->encrypt > 0) { |
1380 | 0 | fr_strerror_const("Attributes with 'encrypt' flag cannot be used inside of a 'struct'"); |
1381 | 0 | return false; |
1382 | 0 | } |
1383 | | |
1384 | 1.38k | return true; |
1385 | 1.38k | } |
1386 | | |
1387 | 67.8k | if (da->flags.length > 253) { |
1388 | 0 | fr_strerror_printf("Attributes cannot be more than 253 octets in length"); |
1389 | 0 | return false; |
1390 | 0 | } |
1391 | | /* |
1392 | | * Secret things are secret. |
1393 | | */ |
1394 | 67.8k | if (flags->encrypt != 0) da->flags.secret = true; |
1395 | | |
1396 | 67.8k | if (flags->concat) { |
1397 | 32 | if (!da->parent->flags.is_root) { |
1398 | 0 | fr_strerror_const("Attributes with the 'concat' flag MUST be at the root of the dictionary"); |
1399 | 0 | return false; |
1400 | 0 | } |
1401 | | |
1402 | 32 | if (da->type != FR_TYPE_OCTETS) { |
1403 | 0 | fr_strerror_const("Attributes with the 'concat' flag MUST be of data type 'octets'"); |
1404 | 0 | return false; |
1405 | 0 | } |
1406 | | |
1407 | 32 | return true; /* can't use any other flag */ |
1408 | 32 | } |
1409 | | |
1410 | | /* |
1411 | | * Tagged attributes can only be of two data types. They |
1412 | | * can, however, be VSAs. |
1413 | | */ |
1414 | 67.7k | if (flags->has_tag) { |
1415 | 336 | if ((da->type != FR_TYPE_UINT32) && (da->type != FR_TYPE_STRING)) { |
1416 | 0 | fr_strerror_printf("The 'has_tag' flag can only be used for attributes of type 'integer' " |
1417 | 0 | "or 'string'"); |
1418 | 0 | return false; |
1419 | 0 | } |
1420 | | |
1421 | 336 | if (!(da->parent->flags.is_root || |
1422 | 256 | ((da->parent->type == FR_TYPE_VENDOR) && |
1423 | 256 | (da->parent->parent && da->parent->parent->type == FR_TYPE_VSA)))) { |
1424 | 0 | fr_strerror_const("The 'has_tag' flag can only be used with RFC and VSA attributes"); |
1425 | 0 | return false; |
1426 | 0 | } |
1427 | | |
1428 | 336 | return true; |
1429 | 336 | } |
1430 | | |
1431 | 67.4k | if (flags->extended) { |
1432 | 32 | if (da->type != FR_TYPE_TLV) { |
1433 | 0 | fr_strerror_const("The 'long' or 'extended' flag can only be used for attributes of type 'tlv'"); |
1434 | 0 | return false; |
1435 | 0 | } |
1436 | | |
1437 | 32 | if (!da->parent->flags.is_root) { |
1438 | 0 | fr_strerror_const("The 'long' flag can only be used for top-level RFC attributes"); |
1439 | 0 | return false; |
1440 | 0 | } |
1441 | | |
1442 | 32 | return true; |
1443 | 32 | } |
1444 | | |
1445 | | /* |
1446 | | * Stupid hacks for MS-CHAP-MPPE-Keys. The User-Password |
1447 | | * encryption method has no provisions for encoding the |
1448 | | * length of the data. For User-Password, the data is |
1449 | | * (presumably) all printable non-zero data. For |
1450 | | * MS-CHAP-MPPE-Keys, the data is binary crap. So... we |
1451 | | * MUST specify a length in the dictionary. |
1452 | | */ |
1453 | 67.4k | if ((flags->encrypt == RADIUS_FLAG_ENCRYPT_USER_PASSWORD) && (da->type != FR_TYPE_STRING)) { |
1454 | 8 | if (da->type != FR_TYPE_OCTETS) { |
1455 | 0 | fr_strerror_printf("The 'encrypt=User-Password' flag can only be used with " |
1456 | 0 | "attributes of type 'string'"); |
1457 | 0 | return false; |
1458 | 0 | } |
1459 | | |
1460 | 8 | if (da->flags.length == 0) { |
1461 | 0 | fr_strerror_printf("The 'encrypt=User-Password' flag MUST be used with an explicit length for " |
1462 | 0 | "'octets' data types"); |
1463 | 0 | return false; |
1464 | 0 | } |
1465 | 8 | } |
1466 | | |
1467 | 67.4k | switch (da->type) { |
1468 | 23.8k | case FR_TYPE_STRING: |
1469 | 23.8k | break; |
1470 | | |
1471 | 656 | case FR_TYPE_TLV: |
1472 | 3.27k | case FR_TYPE_IPV4_ADDR: |
1473 | 27.7k | case FR_TYPE_UINT32: |
1474 | 30.5k | case FR_TYPE_OCTETS: |
1475 | 30.5k | if (flags->encrypt != RADIUS_FLAG_ENCRYPT_ASCEND_SECRET) break; |
1476 | 0 | FALL_THROUGH; |
1477 | |
|
1478 | 13.0k | default: |
1479 | 13.0k | if (flags->encrypt) { |
1480 | 0 | fr_strerror_printf("The 'encrypt' flag cannot be used with attributes of type '%s'", |
1481 | 0 | fr_type_to_str(da->type)); |
1482 | 0 | return false; |
1483 | 0 | } |
1484 | 67.4k | } |
1485 | | |
1486 | 67.4k | return true; |
1487 | 67.4k | } |
1488 | | |
1489 | | fr_dict_protocol_t libfreeradius_radius_dict_protocol = { |
1490 | | .name = "radius", |
1491 | | .default_type_size = 1, |
1492 | | .default_type_length = 1, |
1493 | | .attr = { |
1494 | | .flags = { |
1495 | | .table = radius_flags, |
1496 | | .table_len = NUM_ELEMENTS(radius_flags), |
1497 | | .len = sizeof(fr_radius_attr_flags_t), |
1498 | | }, |
1499 | | .valid = attr_valid, |
1500 | | }, |
1501 | | |
1502 | | .init = fr_radius_global_init, |
1503 | | .free = fr_radius_global_free, |
1504 | | |
1505 | | .decode = fr_radius_decode_foreign, |
1506 | | .encode = fr_radius_encode_foreign, |
1507 | | }; |