/src/hostap/src/radius/radius.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * RADIUS message processing |
3 | | * Copyright (c) 2002-2009, 2011-2022, Jouni Malinen <j@w1.fi> |
4 | | * |
5 | | * This software may be distributed under the terms of the BSD license. |
6 | | * See README for more details. |
7 | | */ |
8 | | |
9 | | #include "utils/includes.h" |
10 | | |
11 | | #include "utils/common.h" |
12 | | #include "utils/wpabuf.h" |
13 | | #include "crypto/md5.h" |
14 | | #include "crypto/crypto.h" |
15 | | #include "radius.h" |
16 | | |
17 | | |
18 | | /** |
19 | | * struct radius_msg - RADIUS message structure for new and parsed messages |
20 | | */ |
21 | | struct radius_msg { |
22 | | /** |
23 | | * buf - Allocated buffer for RADIUS message |
24 | | */ |
25 | | struct wpabuf *buf; |
26 | | |
27 | | /** |
28 | | * hdr - Pointer to the RADIUS header in buf |
29 | | */ |
30 | | struct radius_hdr *hdr; |
31 | | |
32 | | /** |
33 | | * attr_pos - Array of indexes to attributes |
34 | | * |
35 | | * The values are number of bytes from buf to the beginning of |
36 | | * struct radius_attr_hdr. |
37 | | */ |
38 | | size_t *attr_pos; |
39 | | |
40 | | /** |
41 | | * attr_size - Total size of the attribute pointer array |
42 | | */ |
43 | | size_t attr_size; |
44 | | |
45 | | /** |
46 | | * attr_used - Total number of attributes in the array |
47 | | */ |
48 | | size_t attr_used; |
49 | | }; |
50 | | |
51 | | |
52 | | struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg) |
53 | 0 | { |
54 | 0 | return msg->hdr; |
55 | 0 | } |
56 | | |
57 | | |
58 | | struct wpabuf * radius_msg_get_buf(struct radius_msg *msg) |
59 | 0 | { |
60 | 0 | return msg->buf; |
61 | 0 | } |
62 | | |
63 | | |
64 | | static struct radius_attr_hdr * |
65 | | radius_get_attr_hdr(struct radius_msg *msg, int idx) |
66 | 0 | { |
67 | 0 | return (struct radius_attr_hdr *) |
68 | 0 | (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]); |
69 | 0 | } |
70 | | |
71 | | |
72 | | static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) |
73 | 0 | { |
74 | 0 | msg->hdr->code = code; |
75 | 0 | msg->hdr->identifier = identifier; |
76 | 0 | } |
77 | | |
78 | | |
79 | | static int radius_msg_initialize(struct radius_msg *msg) |
80 | 0 | { |
81 | 0 | msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT, |
82 | 0 | sizeof(*msg->attr_pos)); |
83 | 0 | if (msg->attr_pos == NULL) |
84 | 0 | return -1; |
85 | | |
86 | 0 | msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT; |
87 | 0 | msg->attr_used = 0; |
88 | |
|
89 | 0 | return 0; |
90 | 0 | } |
91 | | |
92 | | |
93 | | /** |
94 | | * radius_msg_new - Create a new RADIUS message |
95 | | * @code: Code for RADIUS header |
96 | | * @identifier: Identifier for RADIUS header |
97 | | * Returns: Context for RADIUS message or %NULL on failure |
98 | | * |
99 | | * The caller is responsible for freeing the returned data with |
100 | | * radius_msg_free(). |
101 | | */ |
102 | | struct radius_msg * radius_msg_new(u8 code, u8 identifier) |
103 | 0 | { |
104 | 0 | struct radius_msg *msg; |
105 | |
|
106 | 0 | msg = os_zalloc(sizeof(*msg)); |
107 | 0 | if (msg == NULL) |
108 | 0 | return NULL; |
109 | | |
110 | 0 | msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE); |
111 | 0 | if (msg->buf == NULL || radius_msg_initialize(msg)) { |
112 | 0 | radius_msg_free(msg); |
113 | 0 | return NULL; |
114 | 0 | } |
115 | 0 | msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr)); |
116 | |
|
117 | 0 | radius_msg_set_hdr(msg, code, identifier); |
118 | |
|
119 | 0 | return msg; |
120 | 0 | } |
121 | | |
122 | | |
123 | | /** |
124 | | * radius_msg_free - Free a RADIUS message |
125 | | * @msg: RADIUS message from radius_msg_new() or radius_msg_parse() |
126 | | */ |
127 | | void radius_msg_free(struct radius_msg *msg) |
128 | 0 | { |
129 | 0 | if (msg == NULL) |
130 | 0 | return; |
131 | | |
132 | 0 | wpabuf_free(msg->buf); |
133 | 0 | os_free(msg->attr_pos); |
134 | 0 | os_free(msg); |
135 | 0 | } |
136 | | |
137 | | |
138 | | static const char *radius_code_string(u8 code) |
139 | 0 | { |
140 | 0 | switch (code) { |
141 | 0 | case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request"; |
142 | 0 | case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept"; |
143 | 0 | case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject"; |
144 | 0 | case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request"; |
145 | 0 | case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response"; |
146 | 0 | case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge"; |
147 | 0 | case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; |
148 | 0 | case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; |
149 | 0 | case RADIUS_CODE_RESERVED: return "Reserved"; |
150 | 0 | case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request"; |
151 | 0 | case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK"; |
152 | 0 | case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK"; |
153 | 0 | case RADIUS_CODE_COA_REQUEST: return "CoA-Request"; |
154 | 0 | case RADIUS_CODE_COA_ACK: return "CoA-ACK"; |
155 | 0 | case RADIUS_CODE_COA_NAK: return "CoA-NAK"; |
156 | 0 | default: return "?Unknown?"; |
157 | 0 | } |
158 | 0 | } |
159 | | |
160 | | |
161 | | struct radius_attr_type { |
162 | | u16 type; /* 0..255 for basic types; |
163 | | * (241 << 8) | <ext-type> for extended types */ |
164 | | char *name; |
165 | | enum { |
166 | | RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, |
167 | | RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6 |
168 | | } data_type; |
169 | | }; |
170 | | |
171 | | static const struct radius_attr_type radius_attrs[] = |
172 | | { |
173 | | { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT }, |
174 | | { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, |
175 | | { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP }, |
176 | | { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 }, |
177 | | { RADIUS_ATTR_SERVICE_TYPE, "Service-Type", RADIUS_ATTR_INT32 }, |
178 | | { RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP }, |
179 | | { RADIUS_ATTR_FILTER_ID, "Filter-Id", RADIUS_ATTR_TEXT }, |
180 | | { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, |
181 | | { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT }, |
182 | | { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, |
183 | | { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST }, |
184 | | { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST }, |
185 | | { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 }, |
186 | | { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 }, |
187 | | { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action", |
188 | | RADIUS_ATTR_INT32 }, |
189 | | { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id", |
190 | | RADIUS_ATTR_TEXT }, |
191 | | { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id", |
192 | | RADIUS_ATTR_TEXT }, |
193 | | { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT }, |
194 | | { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST }, |
195 | | { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type", |
196 | | RADIUS_ATTR_INT32 }, |
197 | | { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 }, |
198 | | { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets", |
199 | | RADIUS_ATTR_INT32 }, |
200 | | { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets", |
201 | | RADIUS_ATTR_INT32 }, |
202 | | { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT }, |
203 | | { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 }, |
204 | | { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time", |
205 | | RADIUS_ATTR_INT32 }, |
206 | | { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets", |
207 | | RADIUS_ATTR_INT32 }, |
208 | | { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets", |
209 | | RADIUS_ATTR_INT32 }, |
210 | | { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause", |
211 | | RADIUS_ATTR_INT32 }, |
212 | | { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id", |
213 | | RADIUS_ATTR_TEXT }, |
214 | | { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 }, |
215 | | { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", |
216 | | RADIUS_ATTR_INT32 }, |
217 | | { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords", |
218 | | RADIUS_ATTR_INT32 }, |
219 | | { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", |
220 | | RADIUS_ATTR_INT32 }, |
221 | | { RADIUS_ATTR_EGRESS_VLANID, "EGRESS-VLANID", RADIUS_ATTR_HEXDUMP }, |
222 | | { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, |
223 | | { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, |
224 | | { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", |
225 | | RADIUS_ATTR_HEXDUMP }, |
226 | | { RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password", |
227 | | RADIUS_ATTR_UNDIST }, |
228 | | { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, |
229 | | { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, |
230 | | { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", |
231 | | RADIUS_ATTR_UNDIST }, |
232 | | { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id", |
233 | | RADIUS_ATTR_HEXDUMP }, |
234 | | { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", |
235 | | RADIUS_ATTR_INT32 }, |
236 | | { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity", |
237 | | RADIUS_ATTR_TEXT }, |
238 | | { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, |
239 | | { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }, |
240 | | { RADIUS_ATTR_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP }, |
241 | | { RADIUS_ATTR_OPERATOR_NAME, "Operator-Name", RADIUS_ATTR_TEXT }, |
242 | | { RADIUS_ATTR_LOCATION_INFO, "Location-Information", |
243 | | RADIUS_ATTR_HEXDUMP }, |
244 | | { RADIUS_ATTR_LOCATION_DATA, "Location-Data", RADIUS_ATTR_HEXDUMP }, |
245 | | { RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES, |
246 | | "Basic-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP }, |
247 | | { RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES, |
248 | | "Extended-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP }, |
249 | | { RADIUS_ATTR_LOCATION_CAPABLE, "Location-Capable", RADIUS_ATTR_INT32 }, |
250 | | { RADIUS_ATTR_REQUESTED_LOCATION_INFO, "Requested-Location-Info", |
251 | | RADIUS_ATTR_INT32 }, |
252 | | { RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id", |
253 | | RADIUS_ATTR_INT32 }, |
254 | | { RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT }, |
255 | | { RADIUS_ATTR_WLAN_REASON_CODE, "WLAN-Reason-Code", |
256 | | RADIUS_ATTR_INT32 }, |
257 | | { RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher", |
258 | | RADIUS_ATTR_HEXDUMP }, |
259 | | { RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher", |
260 | | RADIUS_ATTR_HEXDUMP }, |
261 | | { RADIUS_ATTR_WLAN_AKM_SUITE, "WLAN-AKM-Suite", |
262 | | RADIUS_ATTR_HEXDUMP }, |
263 | | { RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher", |
264 | | RADIUS_ATTR_HEXDUMP }, |
265 | | { RADIUS_ATTR_EXT_TYPE_1, "Extended-Type-1", RADIUS_ATTR_UNDIST }, |
266 | | { RADIUS_ATTR_EXT_TYPE_2, "Extended-Type-2", RADIUS_ATTR_UNDIST }, |
267 | | { RADIUS_ATTR_EXT_TYPE_3, "Extended-Type-3", RADIUS_ATTR_UNDIST }, |
268 | | { RADIUS_ATTR_EXT_TYPE_4, "Extended-Type-4", RADIUS_ATTR_UNDIST }, |
269 | | { RADIUS_ATTR_LONG_EXT_TYPE_1, "Long-Extended-Type-1", |
270 | | RADIUS_ATTR_UNDIST }, |
271 | | { RADIUS_ATTR_LONG_EXT_TYPE_2, "Long-Extended-Type-2", |
272 | | RADIUS_ATTR_UNDIST }, |
273 | | { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_1, "Extended-Vendor-Specific-1", |
274 | | RADIUS_ATTR_UNDIST }, |
275 | | { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_2, "Extended-Vendor-Specific-2", |
276 | | RADIUS_ATTR_UNDIST }, |
277 | | { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_3, "Extended-Vendor-Specific-3", |
278 | | RADIUS_ATTR_UNDIST }, |
279 | | { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_4, "Extended-Vendor-Specific-4", |
280 | | RADIUS_ATTR_UNDIST }, |
281 | | { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5, "Extended-Vendor-Specific-5", |
282 | | RADIUS_ATTR_UNDIST }, |
283 | | { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_6, "Extended-Vendor-Specific-6", |
284 | | RADIUS_ATTR_UNDIST }, |
285 | | }; |
286 | 0 | #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs) |
287 | | |
288 | | |
289 | | static const struct radius_attr_type * radius_get_attr_type(u16 type) |
290 | 0 | { |
291 | 0 | size_t i; |
292 | |
|
293 | 0 | for (i = 0; i < RADIUS_ATTRS; i++) { |
294 | 0 | if (type == radius_attrs[i].type) |
295 | 0 | return &radius_attrs[i]; |
296 | 0 | } |
297 | | |
298 | 0 | return NULL; |
299 | 0 | } |
300 | | |
301 | | |
302 | | static bool radius_is_long_ext_type(u8 type) |
303 | 0 | { |
304 | 0 | return type == RADIUS_ATTR_LONG_EXT_TYPE_1 || |
305 | 0 | type == RADIUS_ATTR_LONG_EXT_TYPE_2; |
306 | 0 | } |
307 | | |
308 | | |
309 | | static bool radius_is_ext_type(u8 type) |
310 | 0 | { |
311 | 0 | return type >= RADIUS_ATTR_EXT_TYPE_1 && |
312 | 0 | type <= RADIUS_ATTR_LONG_EXT_TYPE_2; |
313 | 0 | } |
314 | | |
315 | | |
316 | | static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) |
317 | 0 | { |
318 | 0 | struct radius_attr_hdr_ext *ext = NULL; |
319 | 0 | const struct radius_attr_type *attr; |
320 | 0 | int len; |
321 | 0 | unsigned char *pos; |
322 | 0 | char buf[1000]; |
323 | |
|
324 | 0 | if (hdr->length < sizeof(struct radius_attr_hdr)) |
325 | 0 | return; |
326 | | |
327 | 0 | if (radius_is_ext_type(hdr->type)) { |
328 | 0 | if (hdr->length < 4) { |
329 | 0 | wpa_printf(MSG_INFO, |
330 | 0 | " Invalid attribute %d (too short for extended type)", |
331 | 0 | hdr->type); |
332 | 0 | return; |
333 | 0 | } |
334 | | |
335 | 0 | ext = (struct radius_attr_hdr_ext *) hdr; |
336 | 0 | } |
337 | | |
338 | 0 | if (ext) { |
339 | 0 | attr = radius_get_attr_type((ext->type << 8) | ext->ext_type); |
340 | 0 | wpa_printf(MSG_INFO, " Attribute %d.%d (%s) length=%d", |
341 | 0 | ext->type, ext->ext_type, |
342 | 0 | attr ? attr->name : "?Unknown?", ext->length); |
343 | 0 | pos = (unsigned char *) (ext + 1); |
344 | 0 | len = ext->length - sizeof(struct radius_attr_hdr_ext); |
345 | 0 | } else { |
346 | 0 | attr = radius_get_attr_type(hdr->type); |
347 | 0 | wpa_printf(MSG_INFO, " Attribute %d (%s) length=%d", |
348 | 0 | hdr->type, attr ? attr->name : "?Unknown?", |
349 | 0 | hdr->length); |
350 | 0 | pos = (unsigned char *) (hdr + 1); |
351 | 0 | len = hdr->length - sizeof(struct radius_attr_hdr); |
352 | 0 | } |
353 | |
|
354 | 0 | if (!attr) |
355 | 0 | return; |
356 | | |
357 | 0 | switch (attr->data_type) { |
358 | 0 | case RADIUS_ATTR_TEXT: |
359 | 0 | printf_encode(buf, sizeof(buf), pos, len); |
360 | 0 | wpa_printf(MSG_INFO, " Value: '%s'", buf); |
361 | 0 | break; |
362 | | |
363 | 0 | case RADIUS_ATTR_IP: |
364 | 0 | if (len == 4) { |
365 | 0 | struct in_addr addr; |
366 | 0 | os_memcpy(&addr, pos, 4); |
367 | 0 | wpa_printf(MSG_INFO, " Value: %s", |
368 | 0 | inet_ntoa(addr)); |
369 | 0 | } else { |
370 | 0 | wpa_printf(MSG_INFO, " Invalid IP address length %d", |
371 | 0 | len); |
372 | 0 | } |
373 | 0 | break; |
374 | | |
375 | 0 | #ifdef CONFIG_IPV6 |
376 | 0 | case RADIUS_ATTR_IPV6: |
377 | 0 | if (len == 16) { |
378 | 0 | const char *atxt; |
379 | 0 | struct in6_addr *addr = (struct in6_addr *) pos; |
380 | 0 | atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); |
381 | 0 | wpa_printf(MSG_INFO, " Value: %s", |
382 | 0 | atxt ? atxt : "?"); |
383 | 0 | } else { |
384 | 0 | wpa_printf(MSG_INFO, " Invalid IPv6 address length %d", |
385 | 0 | len); |
386 | 0 | } |
387 | 0 | break; |
388 | 0 | #endif /* CONFIG_IPV6 */ |
389 | | |
390 | 0 | case RADIUS_ATTR_HEXDUMP: |
391 | 0 | case RADIUS_ATTR_UNDIST: |
392 | 0 | wpa_snprintf_hex(buf, sizeof(buf), pos, len); |
393 | 0 | wpa_printf(MSG_INFO, " Value: %s", buf); |
394 | 0 | break; |
395 | | |
396 | 0 | case RADIUS_ATTR_INT32: |
397 | 0 | if (len == 4) |
398 | 0 | wpa_printf(MSG_INFO, " Value: %u", |
399 | 0 | WPA_GET_BE32(pos)); |
400 | 0 | else |
401 | 0 | wpa_printf(MSG_INFO, " Invalid INT32 length %d", |
402 | 0 | len); |
403 | 0 | break; |
404 | | |
405 | 0 | default: |
406 | 0 | break; |
407 | 0 | } |
408 | 0 | } |
409 | | |
410 | | |
411 | | void radius_msg_dump(struct radius_msg *msg) |
412 | 0 | { |
413 | 0 | size_t i; |
414 | |
|
415 | 0 | wpa_printf(MSG_INFO, "RADIUS message: code=%d (%s) identifier=%d length=%d", |
416 | 0 | msg->hdr->code, radius_code_string(msg->hdr->code), |
417 | 0 | msg->hdr->identifier, be_to_host16(msg->hdr->length)); |
418 | |
|
419 | 0 | for (i = 0; i < msg->attr_used; i++) { |
420 | 0 | struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); |
421 | 0 | radius_msg_dump_attr(attr); |
422 | 0 | } |
423 | 0 | } |
424 | | |
425 | | |
426 | | u8 * radius_msg_add_msg_auth(struct radius_msg *msg) |
427 | 0 | { |
428 | 0 | u8 auth[MD5_MAC_LEN]; |
429 | 0 | struct radius_attr_hdr *attr; |
430 | |
|
431 | 0 | os_memset(auth, 0, MD5_MAC_LEN); |
432 | 0 | attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, |
433 | 0 | auth, MD5_MAC_LEN); |
434 | 0 | if (!attr) { |
435 | 0 | wpa_printf(MSG_ERROR, |
436 | 0 | "WARNING: Could not add Message-Authenticator"); |
437 | 0 | return NULL; |
438 | 0 | } |
439 | | |
440 | 0 | return (u8 *) (attr + 1); |
441 | 0 | } |
442 | | |
443 | | |
444 | | static u8 * radius_msg_auth_pos(struct radius_msg *msg) |
445 | 0 | { |
446 | 0 | u8 *pos; |
447 | 0 | size_t alen; |
448 | |
|
449 | 0 | if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, |
450 | 0 | &pos, &alen, NULL) == 0 && |
451 | 0 | alen == MD5_MAC_LEN) { |
452 | | /* Use already added Message-Authenticator attribute */ |
453 | 0 | return pos; |
454 | 0 | } |
455 | | |
456 | | /* Add a Message-Authenticator attribute */ |
457 | 0 | return radius_msg_add_msg_auth(msg); |
458 | 0 | } |
459 | | |
460 | | |
461 | | int radius_msg_finish(struct radius_msg *msg, const u8 *secret, |
462 | | size_t secret_len) |
463 | 0 | { |
464 | 0 | if (secret) { |
465 | 0 | u8 *pos; |
466 | |
|
467 | 0 | pos = radius_msg_auth_pos(msg); |
468 | 0 | if (!pos) |
469 | 0 | return -1; |
470 | 0 | msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); |
471 | 0 | if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), |
472 | 0 | wpabuf_len(msg->buf), pos) < 0) { |
473 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
474 | 0 | return -1; |
475 | 0 | } |
476 | 0 | } else |
477 | 0 | msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); |
478 | | |
479 | 0 | if (wpabuf_len(msg->buf) > 0xffff) { |
480 | 0 | wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", |
481 | 0 | (unsigned long) wpabuf_len(msg->buf)); |
482 | 0 | return -1; |
483 | 0 | } |
484 | 0 | return 0; |
485 | 0 | } |
486 | | |
487 | | |
488 | | int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, |
489 | | size_t secret_len, const u8 *req_authenticator) |
490 | 0 | { |
491 | 0 | const u8 *addr[4]; |
492 | 0 | size_t len[4]; |
493 | 0 | u8 *pos; |
494 | |
|
495 | 0 | pos = radius_msg_auth_pos(msg); |
496 | 0 | if (!pos) |
497 | 0 | return -1; |
498 | 0 | msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); |
499 | 0 | os_memcpy(msg->hdr->authenticator, req_authenticator, |
500 | 0 | sizeof(msg->hdr->authenticator)); |
501 | 0 | if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), |
502 | 0 | wpabuf_len(msg->buf), pos) < 0) { |
503 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
504 | 0 | return -1; |
505 | 0 | } |
506 | | |
507 | | /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ |
508 | 0 | addr[0] = (u8 *) msg->hdr; |
509 | 0 | len[0] = 1 + 1 + 2; |
510 | 0 | addr[1] = req_authenticator; |
511 | 0 | len[1] = MD5_MAC_LEN; |
512 | 0 | addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); |
513 | 0 | len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); |
514 | 0 | addr[3] = secret; |
515 | 0 | len[3] = secret_len; |
516 | 0 | if (md5_vector(4, addr, len, msg->hdr->authenticator) < 0) { |
517 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
518 | 0 | return -1; |
519 | 0 | } |
520 | | |
521 | 0 | if (wpabuf_len(msg->buf) > 0xffff) { |
522 | 0 | wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", |
523 | 0 | (unsigned long) wpabuf_len(msg->buf)); |
524 | 0 | return -1; |
525 | 0 | } |
526 | 0 | return 0; |
527 | 0 | } |
528 | | |
529 | | |
530 | | int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, |
531 | | size_t secret_len, |
532 | | const struct radius_hdr *req_hdr) |
533 | 0 | { |
534 | 0 | const u8 *addr[2]; |
535 | 0 | size_t len[2]; |
536 | 0 | u8 *pos; |
537 | |
|
538 | 0 | pos = radius_msg_auth_pos(msg); |
539 | 0 | if (!pos) |
540 | 0 | return -1; |
541 | | |
542 | 0 | msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); |
543 | 0 | os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16); |
544 | 0 | if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), |
545 | 0 | wpabuf_len(msg->buf), pos) < 0) { |
546 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
547 | 0 | return -1; |
548 | 0 | } |
549 | | |
550 | | /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ |
551 | 0 | addr[0] = wpabuf_head_u8(msg->buf); |
552 | 0 | len[0] = wpabuf_len(msg->buf); |
553 | 0 | addr[1] = secret; |
554 | 0 | len[1] = secret_len; |
555 | 0 | if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) { |
556 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
557 | 0 | return -1; |
558 | 0 | } |
559 | | |
560 | 0 | if (wpabuf_len(msg->buf) > 0xffff) { |
561 | 0 | wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", |
562 | 0 | (unsigned long) wpabuf_len(msg->buf)); |
563 | 0 | return -1; |
564 | 0 | } |
565 | 0 | return 0; |
566 | 0 | } |
567 | | |
568 | | |
569 | | int radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, |
570 | | size_t secret_len) |
571 | 0 | { |
572 | 0 | const u8 *addr[2]; |
573 | 0 | size_t len[2]; |
574 | |
|
575 | 0 | msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); |
576 | 0 | os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); |
577 | 0 | addr[0] = wpabuf_head(msg->buf); |
578 | 0 | len[0] = wpabuf_len(msg->buf); |
579 | 0 | addr[1] = secret; |
580 | 0 | len[1] = secret_len; |
581 | 0 | if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) { |
582 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
583 | 0 | return -1; |
584 | 0 | } |
585 | | |
586 | 0 | if (wpabuf_len(msg->buf) > 0xffff) { |
587 | 0 | wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", |
588 | 0 | (unsigned long) wpabuf_len(msg->buf)); |
589 | 0 | return -1; |
590 | 0 | } |
591 | 0 | return 0; |
592 | 0 | } |
593 | | |
594 | | |
595 | | int radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret, |
596 | | size_t secret_len, const u8 *req_authenticator) |
597 | 0 | { |
598 | 0 | const u8 *addr[2]; |
599 | 0 | size_t len[2]; |
600 | |
|
601 | 0 | msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); |
602 | 0 | os_memcpy(msg->hdr->authenticator, req_authenticator, MD5_MAC_LEN); |
603 | 0 | addr[0] = wpabuf_head(msg->buf); |
604 | 0 | len[0] = wpabuf_len(msg->buf); |
605 | 0 | addr[1] = secret; |
606 | 0 | len[1] = secret_len; |
607 | 0 | if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) { |
608 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
609 | 0 | return -1; |
610 | 0 | } |
611 | | |
612 | 0 | if (wpabuf_len(msg->buf) > 0xffff) { |
613 | 0 | wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", |
614 | 0 | (unsigned long) wpabuf_len(msg->buf)); |
615 | 0 | return -1; |
616 | 0 | } |
617 | 0 | return 0; |
618 | 0 | } |
619 | | |
620 | | |
621 | | int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, |
622 | | size_t secret_len) |
623 | 0 | { |
624 | 0 | const u8 *addr[4]; |
625 | 0 | size_t len[4]; |
626 | 0 | u8 zero[MD5_MAC_LEN]; |
627 | 0 | u8 hash[MD5_MAC_LEN]; |
628 | |
|
629 | 0 | os_memset(zero, 0, sizeof(zero)); |
630 | 0 | addr[0] = (u8 *) msg->hdr; |
631 | 0 | len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; |
632 | 0 | addr[1] = zero; |
633 | 0 | len[1] = MD5_MAC_LEN; |
634 | 0 | addr[2] = (u8 *) (msg->hdr + 1); |
635 | 0 | len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); |
636 | 0 | addr[3] = secret; |
637 | 0 | len[3] = secret_len; |
638 | 0 | if (md5_vector(4, addr, len, hash) < 0) { |
639 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
640 | 0 | return 1; |
641 | 0 | } |
642 | 0 | return os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0; |
643 | 0 | } |
644 | | |
645 | | |
646 | | int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, |
647 | | size_t secret_len, |
648 | | int require_message_authenticator) |
649 | 0 | { |
650 | 0 | const u8 *addr[4]; |
651 | 0 | size_t len[4]; |
652 | 0 | u8 zero[MD5_MAC_LEN]; |
653 | 0 | u8 hash[MD5_MAC_LEN]; |
654 | 0 | u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; |
655 | 0 | u8 orig_authenticator[16]; |
656 | |
|
657 | 0 | struct radius_attr_hdr *attr = NULL, *tmp; |
658 | 0 | size_t i; |
659 | |
|
660 | 0 | os_memset(zero, 0, sizeof(zero)); |
661 | 0 | addr[0] = (u8 *) msg->hdr; |
662 | 0 | len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; |
663 | 0 | addr[1] = zero; |
664 | 0 | len[1] = MD5_MAC_LEN; |
665 | 0 | addr[2] = (u8 *) (msg->hdr + 1); |
666 | 0 | len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); |
667 | 0 | addr[3] = secret; |
668 | 0 | len[3] = secret_len; |
669 | 0 | if (md5_vector(4, addr, len, hash) < 0) { |
670 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
671 | 0 | return 1; |
672 | 0 | } |
673 | 0 | if (os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0) |
674 | 0 | return 1; |
675 | | |
676 | 0 | for (i = 0; i < msg->attr_used; i++) { |
677 | 0 | tmp = radius_get_attr_hdr(msg, i); |
678 | 0 | if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { |
679 | 0 | if (attr != NULL) { |
680 | 0 | wpa_printf(MSG_WARNING, "Multiple " |
681 | 0 | "Message-Authenticator attributes " |
682 | 0 | "in RADIUS message"); |
683 | 0 | return 1; |
684 | 0 | } |
685 | 0 | attr = tmp; |
686 | 0 | } |
687 | 0 | } |
688 | | |
689 | 0 | if (attr == NULL) { |
690 | 0 | if (require_message_authenticator) { |
691 | 0 | wpa_printf(MSG_WARNING, |
692 | 0 | "Missing Message-Authenticator attribute in RADIUS message"); |
693 | 0 | return 1; |
694 | 0 | } |
695 | 0 | return 0; |
696 | 0 | } |
697 | | |
698 | 0 | os_memcpy(orig, attr + 1, MD5_MAC_LEN); |
699 | 0 | os_memset(attr + 1, 0, MD5_MAC_LEN); |
700 | 0 | os_memcpy(orig_authenticator, msg->hdr->authenticator, |
701 | 0 | sizeof(orig_authenticator)); |
702 | 0 | os_memset(msg->hdr->authenticator, 0, |
703 | 0 | sizeof(msg->hdr->authenticator)); |
704 | 0 | if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), |
705 | 0 | wpabuf_len(msg->buf), auth) < 0) { |
706 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
707 | 0 | return 1; |
708 | 0 | } |
709 | 0 | os_memcpy(attr + 1, orig, MD5_MAC_LEN); |
710 | 0 | os_memcpy(msg->hdr->authenticator, orig_authenticator, |
711 | 0 | sizeof(orig_authenticator)); |
712 | |
|
713 | 0 | return os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0; |
714 | 0 | } |
715 | | |
716 | | |
717 | | static int radius_msg_add_attr_to_array(struct radius_msg *msg, |
718 | | struct radius_attr_hdr *attr) |
719 | 0 | { |
720 | 0 | if (msg->attr_used >= msg->attr_size) { |
721 | 0 | size_t *nattr_pos; |
722 | 0 | size_t nlen = msg->attr_size * 2; |
723 | |
|
724 | 0 | nattr_pos = os_realloc_array(msg->attr_pos, nlen, |
725 | 0 | sizeof(*msg->attr_pos)); |
726 | 0 | if (nattr_pos == NULL) |
727 | 0 | return -1; |
728 | | |
729 | 0 | msg->attr_pos = nattr_pos; |
730 | 0 | msg->attr_size = nlen; |
731 | 0 | } |
732 | | |
733 | 0 | msg->attr_pos[msg->attr_used++] = |
734 | 0 | (unsigned char *) attr - wpabuf_head_u8(msg->buf); |
735 | |
|
736 | 0 | return 0; |
737 | 0 | } |
738 | | |
739 | | |
740 | | struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u16 type, |
741 | | const u8 *data, size_t data_len) |
742 | 0 | { |
743 | 0 | size_t buf_needed, max_len; |
744 | 0 | struct radius_attr_hdr *attr = NULL; |
745 | 0 | struct radius_attr_hdr_ext *ext; |
746 | 0 | u8 ext_type = 0; |
747 | |
|
748 | 0 | if (TEST_FAIL()) |
749 | 0 | return NULL; |
750 | | |
751 | 0 | if (type > 255) { |
752 | 0 | if (!radius_is_ext_type(type >> 8)) { |
753 | 0 | wpa_printf(MSG_ERROR, |
754 | 0 | "%s: Undefined extended type %d.%d", |
755 | 0 | __func__, type >> 8, type & 0xff); |
756 | 0 | return NULL; |
757 | 0 | } |
758 | 0 | ext_type = type & 0xff; |
759 | 0 | type >>= 8; |
760 | 0 | } else if (radius_is_ext_type(type)) { |
761 | 0 | wpa_printf(MSG_ERROR, "%s: Unexpected extended type use for %d", |
762 | 0 | __func__, type); |
763 | 0 | } |
764 | | |
765 | 0 | if (radius_is_long_ext_type(type)) { |
766 | 0 | size_t hdr_len = sizeof(struct radius_attr_hdr_ext) + 1; |
767 | 0 | size_t plen = 255 - hdr_len; |
768 | 0 | size_t num; |
769 | |
|
770 | 0 | max_len = 4096; |
771 | 0 | num = (data_len + plen - 1) / plen; |
772 | 0 | if (num == 0) |
773 | 0 | num = 1; |
774 | 0 | buf_needed = num * hdr_len + data_len; |
775 | 0 | } else if (radius_is_ext_type(type)) { |
776 | 0 | max_len = RADIUS_MAX_EXT_ATTR_LEN; |
777 | 0 | buf_needed = sizeof(struct radius_attr_hdr_ext) + data_len; |
778 | 0 | } else { |
779 | 0 | max_len = RADIUS_MAX_ATTR_LEN; |
780 | 0 | buf_needed = sizeof(*attr) + data_len; |
781 | 0 | } |
782 | 0 | if (data_len > max_len) { |
783 | 0 | wpa_printf(MSG_ERROR, |
784 | 0 | "%s: too long attribute (%zu > %zu bytes)", |
785 | 0 | __func__, data_len, max_len); |
786 | 0 | return NULL; |
787 | 0 | } |
788 | | |
789 | 0 | if (wpabuf_tailroom(msg->buf) < buf_needed) { |
790 | | /* allocate more space for message buffer */ |
791 | 0 | if (wpabuf_resize(&msg->buf, buf_needed) < 0) |
792 | 0 | return NULL; |
793 | 0 | msg->hdr = wpabuf_mhead(msg->buf); |
794 | 0 | } |
795 | | |
796 | 0 | if (radius_is_long_ext_type(type)) { |
797 | 0 | size_t plen = 255 - sizeof(struct radius_attr_hdr_ext) - 1; |
798 | 0 | size_t alen; |
799 | |
|
800 | 0 | do { |
801 | 0 | alen = data_len > plen ? plen : data_len; |
802 | 0 | ext = wpabuf_put(msg->buf, |
803 | 0 | sizeof(struct radius_attr_hdr_ext)); |
804 | 0 | if (!attr) |
805 | 0 | attr = (struct radius_attr_hdr *) ext; |
806 | 0 | ext->type = type; |
807 | 0 | ext->length = sizeof(*ext) + 1 + alen; |
808 | 0 | ext->ext_type = ext_type; |
809 | 0 | wpabuf_put_u8(msg->buf, data_len > alen ? 0x80 : 0); |
810 | 0 | wpabuf_put_data(msg->buf, data, data_len); |
811 | 0 | data += alen; |
812 | 0 | data_len -= alen; |
813 | 0 | if (radius_msg_add_attr_to_array( |
814 | 0 | msg, (struct radius_attr_hdr *) ext)) |
815 | 0 | return NULL; |
816 | 0 | } while (data_len > 0); |
817 | 0 | } else if (radius_is_ext_type(type)) { |
818 | 0 | ext = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr_ext)); |
819 | 0 | attr = (struct radius_attr_hdr *) ext; |
820 | 0 | ext->type = type; |
821 | 0 | ext->length = sizeof(*ext) + data_len; |
822 | 0 | ext->ext_type = ext_type; |
823 | 0 | wpabuf_put_data(msg->buf, data, data_len); |
824 | 0 | if (radius_msg_add_attr_to_array(msg, attr)) |
825 | 0 | return NULL; |
826 | 0 | } else { |
827 | 0 | attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr)); |
828 | 0 | attr->type = type; |
829 | 0 | attr->length = sizeof(*attr) + data_len; |
830 | 0 | wpabuf_put_data(msg->buf, data, data_len); |
831 | 0 | if (radius_msg_add_attr_to_array(msg, attr)) |
832 | 0 | return NULL; |
833 | 0 | } |
834 | | |
835 | 0 | return attr; |
836 | 0 | } |
837 | | |
838 | | |
839 | | /** |
840 | | * radius_msg_parse - Parse a RADIUS message |
841 | | * @data: RADIUS message to be parsed |
842 | | * @len: Length of data buffer in octets |
843 | | * Returns: Parsed RADIUS message or %NULL on failure |
844 | | * |
845 | | * This parses a RADIUS message and makes a copy of its data. The caller is |
846 | | * responsible for freeing the returned data with radius_msg_free(). |
847 | | */ |
848 | | struct radius_msg * radius_msg_parse(const u8 *data, size_t len) |
849 | 0 | { |
850 | 0 | struct radius_msg *msg; |
851 | 0 | struct radius_hdr *hdr; |
852 | 0 | struct radius_attr_hdr *attr; |
853 | 0 | size_t msg_len; |
854 | 0 | unsigned char *pos, *end; |
855 | |
|
856 | 0 | if (data == NULL || len < sizeof(*hdr)) |
857 | 0 | return NULL; |
858 | | |
859 | 0 | hdr = (struct radius_hdr *) data; |
860 | |
|
861 | 0 | msg_len = be_to_host16(hdr->length); |
862 | 0 | if (msg_len < sizeof(*hdr) || msg_len > len) { |
863 | 0 | wpa_printf(MSG_INFO, "RADIUS: Invalid message length"); |
864 | 0 | return NULL; |
865 | 0 | } |
866 | | |
867 | 0 | if (msg_len < len) { |
868 | 0 | wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after " |
869 | 0 | "RADIUS message", (unsigned long) len - msg_len); |
870 | 0 | } |
871 | |
|
872 | 0 | msg = os_zalloc(sizeof(*msg)); |
873 | 0 | if (msg == NULL) |
874 | 0 | return NULL; |
875 | | |
876 | 0 | msg->buf = wpabuf_alloc_copy(data, msg_len); |
877 | 0 | if (msg->buf == NULL || radius_msg_initialize(msg)) { |
878 | 0 | radius_msg_free(msg); |
879 | 0 | return NULL; |
880 | 0 | } |
881 | 0 | msg->hdr = wpabuf_mhead(msg->buf); |
882 | | |
883 | | /* parse attributes */ |
884 | 0 | pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr); |
885 | 0 | end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf); |
886 | 0 | while (pos < end) { |
887 | 0 | if ((size_t) (end - pos) < sizeof(*attr)) |
888 | 0 | goto fail; |
889 | | |
890 | 0 | attr = (struct radius_attr_hdr *) pos; |
891 | |
|
892 | 0 | if (attr->length > end - pos || attr->length < sizeof(*attr)) |
893 | 0 | goto fail; |
894 | | |
895 | | /* TODO: check that attr->length is suitable for attr->type */ |
896 | | |
897 | 0 | if (radius_msg_add_attr_to_array(msg, attr)) |
898 | 0 | goto fail; |
899 | | |
900 | 0 | pos += attr->length; |
901 | 0 | } |
902 | | |
903 | 0 | return msg; |
904 | | |
905 | 0 | fail: |
906 | 0 | radius_msg_free(msg); |
907 | 0 | return NULL; |
908 | 0 | } |
909 | | |
910 | | |
911 | | int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) |
912 | 0 | { |
913 | 0 | const u8 *pos = data; |
914 | 0 | size_t left = data_len; |
915 | |
|
916 | 0 | while (left > 0) { |
917 | 0 | int len; |
918 | 0 | if (left > RADIUS_MAX_ATTR_LEN) |
919 | 0 | len = RADIUS_MAX_ATTR_LEN; |
920 | 0 | else |
921 | 0 | len = left; |
922 | |
|
923 | 0 | if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE, |
924 | 0 | pos, len)) |
925 | 0 | return 0; |
926 | | |
927 | 0 | pos += len; |
928 | 0 | left -= len; |
929 | 0 | } |
930 | | |
931 | 0 | return 1; |
932 | 0 | } |
933 | | |
934 | | |
935 | | struct wpabuf * radius_msg_get_eap(struct radius_msg *msg) |
936 | 0 | { |
937 | 0 | struct wpabuf *eap; |
938 | 0 | size_t len, i; |
939 | 0 | struct radius_attr_hdr *attr; |
940 | |
|
941 | 0 | if (msg == NULL) |
942 | 0 | return NULL; |
943 | | |
944 | 0 | len = 0; |
945 | 0 | for (i = 0; i < msg->attr_used; i++) { |
946 | 0 | attr = radius_get_attr_hdr(msg, i); |
947 | 0 | if (attr->type == RADIUS_ATTR_EAP_MESSAGE && |
948 | 0 | attr->length > sizeof(struct radius_attr_hdr)) |
949 | 0 | len += attr->length - sizeof(struct radius_attr_hdr); |
950 | 0 | } |
951 | |
|
952 | 0 | if (len == 0) |
953 | 0 | return NULL; |
954 | | |
955 | 0 | eap = wpabuf_alloc(len); |
956 | 0 | if (eap == NULL) |
957 | 0 | return NULL; |
958 | | |
959 | 0 | for (i = 0; i < msg->attr_used; i++) { |
960 | 0 | attr = radius_get_attr_hdr(msg, i); |
961 | 0 | if (attr->type == RADIUS_ATTR_EAP_MESSAGE && |
962 | 0 | attr->length > sizeof(struct radius_attr_hdr)) { |
963 | 0 | int flen = attr->length - sizeof(*attr); |
964 | 0 | wpabuf_put_data(eap, attr + 1, flen); |
965 | 0 | } |
966 | 0 | } |
967 | |
|
968 | 0 | return eap; |
969 | 0 | } |
970 | | |
971 | | |
972 | | int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, |
973 | | size_t secret_len, const u8 *req_auth) |
974 | 0 | { |
975 | 0 | u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; |
976 | 0 | u8 orig_authenticator[16]; |
977 | 0 | struct radius_attr_hdr *attr = NULL, *tmp; |
978 | 0 | size_t i; |
979 | |
|
980 | 0 | for (i = 0; i < msg->attr_used; i++) { |
981 | 0 | tmp = radius_get_attr_hdr(msg, i); |
982 | 0 | if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { |
983 | 0 | if (attr != NULL) { |
984 | 0 | wpa_printf(MSG_INFO, "Multiple Message-Authenticator attributes in RADIUS message"); |
985 | 0 | return 1; |
986 | 0 | } |
987 | 0 | attr = tmp; |
988 | 0 | } |
989 | 0 | } |
990 | | |
991 | 0 | if (attr == NULL) { |
992 | 0 | wpa_printf(MSG_INFO, "No Message-Authenticator attribute found"); |
993 | 0 | return 1; |
994 | 0 | } |
995 | | |
996 | 0 | os_memcpy(orig, attr + 1, MD5_MAC_LEN); |
997 | 0 | os_memset(attr + 1, 0, MD5_MAC_LEN); |
998 | 0 | if (req_auth) { |
999 | 0 | os_memcpy(orig_authenticator, msg->hdr->authenticator, |
1000 | 0 | sizeof(orig_authenticator)); |
1001 | 0 | os_memcpy(msg->hdr->authenticator, req_auth, |
1002 | 0 | sizeof(msg->hdr->authenticator)); |
1003 | 0 | } |
1004 | 0 | if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), |
1005 | 0 | wpabuf_len(msg->buf), auth) < 0) { |
1006 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
1007 | 0 | return 1; |
1008 | 0 | } |
1009 | 0 | os_memcpy(attr + 1, orig, MD5_MAC_LEN); |
1010 | 0 | if (req_auth) { |
1011 | 0 | os_memcpy(msg->hdr->authenticator, orig_authenticator, |
1012 | 0 | sizeof(orig_authenticator)); |
1013 | 0 | } |
1014 | |
|
1015 | 0 | if (os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0) { |
1016 | 0 | wpa_printf(MSG_INFO, "Invalid Message-Authenticator!"); |
1017 | 0 | return 1; |
1018 | 0 | } |
1019 | | |
1020 | 0 | return 0; |
1021 | 0 | } |
1022 | | |
1023 | | |
1024 | | int radius_msg_verify(struct radius_msg *msg, const u8 *secret, |
1025 | | size_t secret_len, struct radius_msg *sent_msg, int auth) |
1026 | 0 | { |
1027 | 0 | const u8 *addr[4]; |
1028 | 0 | size_t len[4]; |
1029 | 0 | u8 hash[MD5_MAC_LEN]; |
1030 | |
|
1031 | 0 | if (sent_msg == NULL) { |
1032 | 0 | wpa_printf(MSG_INFO, "No matching Access-Request message found"); |
1033 | 0 | return 1; |
1034 | 0 | } |
1035 | | |
1036 | 0 | if (!auth) { |
1037 | 0 | u8 *pos; |
1038 | 0 | size_t alen; |
1039 | |
|
1040 | 0 | if (radius_msg_get_attr_ptr(msg, |
1041 | 0 | RADIUS_ATTR_MESSAGE_AUTHENTICATOR, |
1042 | 0 | &pos, &alen, NULL) == 0) { |
1043 | | /* Check the Message-Authenticator attribute since it |
1044 | | * was included even if we are configured to not |
1045 | | * require it. */ |
1046 | 0 | auth = 1; |
1047 | 0 | } |
1048 | 0 | } |
1049 | |
|
1050 | 0 | if (auth && |
1051 | 0 | radius_msg_verify_msg_auth(msg, secret, secret_len, |
1052 | 0 | sent_msg->hdr->authenticator)) { |
1053 | 0 | return 1; |
1054 | 0 | } |
1055 | | |
1056 | | /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ |
1057 | 0 | addr[0] = (u8 *) msg->hdr; |
1058 | 0 | len[0] = 1 + 1 + 2; |
1059 | 0 | addr[1] = sent_msg->hdr->authenticator; |
1060 | 0 | len[1] = MD5_MAC_LEN; |
1061 | 0 | addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); |
1062 | 0 | len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); |
1063 | 0 | addr[3] = secret; |
1064 | 0 | len[3] = secret_len; |
1065 | 0 | if (md5_vector(4, addr, len, hash) < 0 || |
1066 | 0 | os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { |
1067 | 0 | wpa_printf(MSG_INFO, "Response Authenticator invalid!"); |
1068 | 0 | return 1; |
1069 | 0 | } |
1070 | | |
1071 | 0 | return 0; |
1072 | 0 | } |
1073 | | |
1074 | | |
1075 | | int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, |
1076 | | u8 type) |
1077 | 0 | { |
1078 | 0 | struct radius_attr_hdr *attr; |
1079 | 0 | size_t i; |
1080 | 0 | int count = 0; |
1081 | |
|
1082 | 0 | for (i = 0; i < src->attr_used; i++) { |
1083 | 0 | attr = radius_get_attr_hdr(src, i); |
1084 | 0 | if (attr->type == type && attr->length >= sizeof(*attr)) { |
1085 | 0 | if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), |
1086 | 0 | attr->length - sizeof(*attr))) |
1087 | 0 | return -1; |
1088 | 0 | count++; |
1089 | 0 | } |
1090 | 0 | } |
1091 | | |
1092 | 0 | return count; |
1093 | 0 | } |
1094 | | |
1095 | | |
1096 | | /* Create Request Authenticator. The value should be unique over the lifetime |
1097 | | * of the shared secret between authenticator and authentication server. |
1098 | | */ |
1099 | | int radius_msg_make_authenticator(struct radius_msg *msg) |
1100 | 0 | { |
1101 | 0 | return os_get_random((u8 *) &msg->hdr->authenticator, |
1102 | 0 | sizeof(msg->hdr->authenticator)); |
1103 | 0 | } |
1104 | | |
1105 | | |
1106 | | /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message. |
1107 | | * Returns the Attribute payload and sets alen to indicate the length of the |
1108 | | * payload if a vendor attribute with subtype is found, otherwise returns NULL. |
1109 | | * The returned payload is allocated with os_malloc() and caller must free it |
1110 | | * by calling os_free(). |
1111 | | */ |
1112 | | static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, |
1113 | | u8 subtype, size_t *alen) |
1114 | 0 | { |
1115 | 0 | u8 *data, *pos; |
1116 | 0 | size_t i, len; |
1117 | |
|
1118 | 0 | if (msg == NULL) |
1119 | 0 | return NULL; |
1120 | | |
1121 | 0 | for (i = 0; i < msg->attr_used; i++) { |
1122 | 0 | struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); |
1123 | 0 | size_t left; |
1124 | 0 | u32 vendor_id; |
1125 | 0 | struct radius_attr_vendor *vhdr; |
1126 | |
|
1127 | 0 | if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC || |
1128 | 0 | attr->length < sizeof(*attr)) |
1129 | 0 | continue; |
1130 | | |
1131 | 0 | left = attr->length - sizeof(*attr); |
1132 | 0 | if (left < 4) |
1133 | 0 | continue; |
1134 | | |
1135 | 0 | pos = (u8 *) (attr + 1); |
1136 | |
|
1137 | 0 | os_memcpy(&vendor_id, pos, 4); |
1138 | 0 | pos += 4; |
1139 | 0 | left -= 4; |
1140 | |
|
1141 | 0 | if (ntohl(vendor_id) != vendor) |
1142 | 0 | continue; |
1143 | | |
1144 | 0 | while (left >= sizeof(*vhdr)) { |
1145 | 0 | vhdr = (struct radius_attr_vendor *) pos; |
1146 | 0 | if (vhdr->vendor_length > left || |
1147 | 0 | vhdr->vendor_length < sizeof(*vhdr)) { |
1148 | 0 | break; |
1149 | 0 | } |
1150 | 0 | if (vhdr->vendor_type != subtype) { |
1151 | 0 | pos += vhdr->vendor_length; |
1152 | 0 | left -= vhdr->vendor_length; |
1153 | 0 | continue; |
1154 | 0 | } |
1155 | | |
1156 | 0 | len = vhdr->vendor_length - sizeof(*vhdr); |
1157 | 0 | data = os_memdup(pos + sizeof(*vhdr), len); |
1158 | 0 | if (data == NULL) |
1159 | 0 | return NULL; |
1160 | 0 | if (alen) |
1161 | 0 | *alen = len; |
1162 | 0 | return data; |
1163 | 0 | } |
1164 | 0 | } |
1165 | | |
1166 | 0 | return NULL; |
1167 | 0 | } |
1168 | | |
1169 | | |
1170 | | static u8 * decrypt_ms_key(const u8 *key, size_t len, |
1171 | | const u8 *req_authenticator, |
1172 | | const u8 *secret, size_t secret_len, size_t *reslen) |
1173 | 0 | { |
1174 | 0 | u8 *plain, *ppos, *res; |
1175 | 0 | const u8 *pos; |
1176 | 0 | size_t left, plen; |
1177 | 0 | u8 hash[MD5_MAC_LEN]; |
1178 | 0 | int i, first = 1; |
1179 | 0 | const u8 *addr[3]; |
1180 | 0 | size_t elen[3]; |
1181 | | |
1182 | | /* key: 16-bit salt followed by encrypted key info */ |
1183 | |
|
1184 | 0 | if (len < 2 + 16) { |
1185 | 0 | wpa_printf(MSG_DEBUG, "RADIUS: %s: Len is too small: %d", |
1186 | 0 | __func__, (int) len); |
1187 | 0 | return NULL; |
1188 | 0 | } |
1189 | | |
1190 | 0 | pos = key + 2; |
1191 | 0 | left = len - 2; |
1192 | 0 | if (left % 16) { |
1193 | 0 | wpa_printf(MSG_INFO, "RADIUS: Invalid ms key len %lu", |
1194 | 0 | (unsigned long) left); |
1195 | 0 | return NULL; |
1196 | 0 | } |
1197 | | |
1198 | 0 | plen = left; |
1199 | 0 | ppos = plain = os_malloc(plen); |
1200 | 0 | if (plain == NULL) |
1201 | 0 | return NULL; |
1202 | 0 | plain[0] = 0; |
1203 | |
|
1204 | 0 | while (left > 0) { |
1205 | | /* b(1) = MD5(Secret + Request-Authenticator + Salt) |
1206 | | * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ |
1207 | |
|
1208 | 0 | addr[0] = secret; |
1209 | 0 | elen[0] = secret_len; |
1210 | 0 | if (first) { |
1211 | 0 | addr[1] = req_authenticator; |
1212 | 0 | elen[1] = MD5_MAC_LEN; |
1213 | 0 | addr[2] = key; |
1214 | 0 | elen[2] = 2; /* Salt */ |
1215 | 0 | } else { |
1216 | 0 | addr[1] = pos - MD5_MAC_LEN; |
1217 | 0 | elen[1] = MD5_MAC_LEN; |
1218 | 0 | } |
1219 | 0 | if (md5_vector(first ? 3 : 2, addr, elen, hash) < 0) { |
1220 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
1221 | 0 | os_free(plain); |
1222 | 0 | return NULL; |
1223 | 0 | } |
1224 | 0 | first = 0; |
1225 | |
|
1226 | 0 | for (i = 0; i < MD5_MAC_LEN; i++) |
1227 | 0 | *ppos++ = *pos++ ^ hash[i]; |
1228 | 0 | left -= MD5_MAC_LEN; |
1229 | 0 | } |
1230 | | |
1231 | 0 | if (plain[0] == 0 || plain[0] > plen - 1) { |
1232 | 0 | wpa_printf(MSG_INFO, "RADIUS: Failed to decrypt MPPE key"); |
1233 | 0 | os_free(plain); |
1234 | 0 | return NULL; |
1235 | 0 | } |
1236 | | |
1237 | 0 | res = os_memdup(plain + 1, plain[0]); |
1238 | 0 | if (res == NULL) { |
1239 | 0 | os_free(plain); |
1240 | 0 | return NULL; |
1241 | 0 | } |
1242 | 0 | if (reslen) |
1243 | 0 | *reslen = plain[0]; |
1244 | 0 | os_free(plain); |
1245 | 0 | return res; |
1246 | 0 | } |
1247 | | |
1248 | | |
1249 | | static int encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, |
1250 | | const u8 *req_authenticator, |
1251 | | const u8 *secret, size_t secret_len, |
1252 | | u8 *ebuf, size_t *elen) |
1253 | 0 | { |
1254 | 0 | int i, len, first = 1; |
1255 | 0 | u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; |
1256 | 0 | const u8 *addr[3]; |
1257 | 0 | size_t _len[3]; |
1258 | |
|
1259 | 0 | WPA_PUT_BE16(saltbuf, salt); |
1260 | |
|
1261 | 0 | len = 1 + key_len; |
1262 | 0 | if (len & 0x0f) { |
1263 | 0 | len = (len & 0xf0) + 16; |
1264 | 0 | } |
1265 | 0 | os_memset(ebuf, 0, len); |
1266 | 0 | ebuf[0] = key_len; |
1267 | 0 | os_memcpy(ebuf + 1, key, key_len); |
1268 | |
|
1269 | 0 | *elen = len; |
1270 | |
|
1271 | 0 | pos = ebuf; |
1272 | 0 | while (len > 0) { |
1273 | | /* b(1) = MD5(Secret + Request-Authenticator + Salt) |
1274 | | * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ |
1275 | 0 | addr[0] = secret; |
1276 | 0 | _len[0] = secret_len; |
1277 | 0 | if (first) { |
1278 | 0 | addr[1] = req_authenticator; |
1279 | 0 | _len[1] = MD5_MAC_LEN; |
1280 | 0 | addr[2] = saltbuf; |
1281 | 0 | _len[2] = sizeof(saltbuf); |
1282 | 0 | } else { |
1283 | 0 | addr[1] = pos - MD5_MAC_LEN; |
1284 | 0 | _len[1] = MD5_MAC_LEN; |
1285 | 0 | } |
1286 | 0 | if (md5_vector(first ? 3 : 2, addr, _len, hash) < 0) { |
1287 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
1288 | 0 | return -1; |
1289 | 0 | } |
1290 | 0 | first = 0; |
1291 | |
|
1292 | 0 | for (i = 0; i < MD5_MAC_LEN; i++) |
1293 | 0 | *pos++ ^= hash[i]; |
1294 | |
|
1295 | 0 | len -= MD5_MAC_LEN; |
1296 | 0 | } |
1297 | | |
1298 | 0 | return 0; |
1299 | 0 | } |
1300 | | |
1301 | | |
1302 | | struct radius_ms_mppe_keys * |
1303 | | radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, |
1304 | | const u8 *secret, size_t secret_len) |
1305 | 0 | { |
1306 | 0 | u8 *key; |
1307 | 0 | size_t keylen; |
1308 | 0 | struct radius_ms_mppe_keys *keys; |
1309 | |
|
1310 | 0 | if (msg == NULL || sent_msg == NULL) |
1311 | 0 | return NULL; |
1312 | | |
1313 | 0 | keys = os_zalloc(sizeof(*keys)); |
1314 | 0 | if (keys == NULL) |
1315 | 0 | return NULL; |
1316 | | |
1317 | 0 | key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, |
1318 | 0 | RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY, |
1319 | 0 | &keylen); |
1320 | 0 | if (key) { |
1321 | 0 | keys->send = decrypt_ms_key(key, keylen, |
1322 | 0 | sent_msg->hdr->authenticator, |
1323 | 0 | secret, secret_len, |
1324 | 0 | &keys->send_len); |
1325 | 0 | if (!keys->send) { |
1326 | 0 | wpa_printf(MSG_DEBUG, |
1327 | 0 | "RADIUS: Failed to decrypt send key"); |
1328 | 0 | } |
1329 | 0 | os_free(key); |
1330 | 0 | } |
1331 | |
|
1332 | 0 | key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, |
1333 | 0 | RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY, |
1334 | 0 | &keylen); |
1335 | 0 | if (key) { |
1336 | 0 | keys->recv = decrypt_ms_key(key, keylen, |
1337 | 0 | sent_msg->hdr->authenticator, |
1338 | 0 | secret, secret_len, |
1339 | 0 | &keys->recv_len); |
1340 | 0 | if (!keys->recv) { |
1341 | 0 | wpa_printf(MSG_DEBUG, |
1342 | 0 | "RADIUS: Failed to decrypt recv key"); |
1343 | 0 | } |
1344 | 0 | os_free(key); |
1345 | 0 | } |
1346 | |
|
1347 | 0 | return keys; |
1348 | 0 | } |
1349 | | |
1350 | | |
1351 | | struct radius_ms_mppe_keys * |
1352 | | radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, |
1353 | | const u8 *secret, size_t secret_len) |
1354 | 0 | { |
1355 | 0 | u8 *key; |
1356 | 0 | size_t keylen; |
1357 | 0 | struct radius_ms_mppe_keys *keys; |
1358 | |
|
1359 | 0 | if (msg == NULL || sent_msg == NULL) |
1360 | 0 | return NULL; |
1361 | | |
1362 | 0 | keys = os_zalloc(sizeof(*keys)); |
1363 | 0 | if (keys == NULL) |
1364 | 0 | return NULL; |
1365 | | |
1366 | 0 | key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO, |
1367 | 0 | RADIUS_CISCO_AV_PAIR, &keylen); |
1368 | 0 | if (key && keylen == 51 && |
1369 | 0 | os_memcmp(key, "leap:session-key=", 17) == 0) { |
1370 | 0 | keys->recv = decrypt_ms_key(key + 17, keylen - 17, |
1371 | 0 | sent_msg->hdr->authenticator, |
1372 | 0 | secret, secret_len, |
1373 | 0 | &keys->recv_len); |
1374 | 0 | } |
1375 | 0 | os_free(key); |
1376 | |
|
1377 | 0 | return keys; |
1378 | 0 | } |
1379 | | |
1380 | | |
1381 | | int radius_msg_add_mppe_keys(struct radius_msg *msg, |
1382 | | const u8 *req_authenticator, |
1383 | | const u8 *secret, size_t secret_len, |
1384 | | const u8 *send_key, size_t send_key_len, |
1385 | | const u8 *recv_key, size_t recv_key_len) |
1386 | 0 | { |
1387 | 0 | struct radius_attr_hdr *attr; |
1388 | 0 | u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT); |
1389 | 0 | u8 *buf; |
1390 | 0 | struct radius_attr_vendor *vhdr; |
1391 | 0 | u8 *pos; |
1392 | 0 | size_t elen; |
1393 | 0 | int hlen; |
1394 | 0 | u16 salt; |
1395 | |
|
1396 | 0 | hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2; |
1397 | | |
1398 | | /* MS-MPPE-Send-Key */ |
1399 | 0 | buf = os_malloc(hlen + send_key_len + 16); |
1400 | 0 | if (buf == NULL) { |
1401 | 0 | return 0; |
1402 | 0 | } |
1403 | 0 | pos = buf; |
1404 | 0 | os_memcpy(pos, &vendor_id, sizeof(vendor_id)); |
1405 | 0 | pos += sizeof(vendor_id); |
1406 | 0 | vhdr = (struct radius_attr_vendor *) pos; |
1407 | 0 | vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; |
1408 | 0 | pos = (u8 *) (vhdr + 1); |
1409 | 0 | if (os_get_random((u8 *) &salt, sizeof(salt)) < 0) { |
1410 | 0 | os_free(buf); |
1411 | 0 | return 0; |
1412 | 0 | } |
1413 | 0 | salt |= 0x8000; |
1414 | 0 | WPA_PUT_BE16(pos, salt); |
1415 | 0 | pos += 2; |
1416 | 0 | if (encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, |
1417 | 0 | secret, secret_len, pos, &elen) < 0) |
1418 | 0 | return 0; |
1419 | 0 | vhdr->vendor_length = hlen + elen - sizeof(vendor_id); |
1420 | |
|
1421 | 0 | attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, |
1422 | 0 | buf, hlen + elen); |
1423 | 0 | os_free(buf); |
1424 | 0 | if (attr == NULL) { |
1425 | 0 | return 0; |
1426 | 0 | } |
1427 | | |
1428 | | /* MS-MPPE-Recv-Key */ |
1429 | 0 | buf = os_malloc(hlen + recv_key_len + 16); |
1430 | 0 | if (buf == NULL) { |
1431 | 0 | return 0; |
1432 | 0 | } |
1433 | 0 | pos = buf; |
1434 | 0 | os_memcpy(pos, &vendor_id, sizeof(vendor_id)); |
1435 | 0 | pos += sizeof(vendor_id); |
1436 | 0 | vhdr = (struct radius_attr_vendor *) pos; |
1437 | 0 | vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY; |
1438 | 0 | pos = (u8 *) (vhdr + 1); |
1439 | 0 | salt ^= 1; |
1440 | 0 | WPA_PUT_BE16(pos, salt); |
1441 | 0 | pos += 2; |
1442 | 0 | if (encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, |
1443 | 0 | secret, secret_len, pos, &elen) < 0) |
1444 | 0 | return 0; |
1445 | 0 | vhdr->vendor_length = hlen + elen - sizeof(vendor_id); |
1446 | |
|
1447 | 0 | attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, |
1448 | 0 | buf, hlen + elen); |
1449 | 0 | os_free(buf); |
1450 | 0 | if (attr == NULL) { |
1451 | 0 | return 0; |
1452 | 0 | } |
1453 | | |
1454 | 0 | return 1; |
1455 | 0 | } |
1456 | | |
1457 | | |
1458 | | int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data, |
1459 | | size_t len) |
1460 | 0 | { |
1461 | 0 | struct radius_attr_hdr *attr; |
1462 | 0 | u8 *buf, *pos; |
1463 | 0 | size_t alen; |
1464 | |
|
1465 | 0 | alen = 4 + 2 + len; |
1466 | 0 | buf = os_malloc(alen); |
1467 | 0 | if (buf == NULL) |
1468 | 0 | return 0; |
1469 | 0 | pos = buf; |
1470 | 0 | WPA_PUT_BE32(pos, RADIUS_VENDOR_ID_WFA); |
1471 | 0 | pos += 4; |
1472 | 0 | *pos++ = subtype; |
1473 | 0 | *pos++ = 2 + len; |
1474 | 0 | os_memcpy(pos, data, len); |
1475 | 0 | attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, |
1476 | 0 | buf, alen); |
1477 | 0 | os_free(buf); |
1478 | 0 | if (attr == NULL) |
1479 | 0 | return 0; |
1480 | | |
1481 | 0 | return 1; |
1482 | 0 | } |
1483 | | |
1484 | | |
1485 | | int radius_msg_add_ext_vs(struct radius_msg *msg, u16 type, u32 vendor_id, |
1486 | | u8 vendor_type, const u8 *data, size_t len) |
1487 | 0 | { |
1488 | 0 | struct radius_attr_hdr *attr; |
1489 | 0 | u8 *buf, *pos; |
1490 | 0 | size_t alen; |
1491 | |
|
1492 | 0 | alen = 4 + 1 + len; |
1493 | 0 | buf = os_malloc(alen); |
1494 | 0 | if (!buf) |
1495 | 0 | return 0; |
1496 | 0 | pos = buf; |
1497 | 0 | WPA_PUT_BE32(pos, vendor_id); |
1498 | 0 | pos += 4; |
1499 | 0 | *pos++ = vendor_type; |
1500 | 0 | os_memcpy(pos, data, len); |
1501 | 0 | attr = radius_msg_add_attr(msg, type, buf, alen); |
1502 | 0 | os_free(buf); |
1503 | 0 | return attr != NULL; |
1504 | 0 | } |
1505 | | |
1506 | | |
1507 | | int radius_user_password_hide(struct radius_msg *msg, |
1508 | | const u8 *data, size_t data_len, |
1509 | | const u8 *secret, size_t secret_len, |
1510 | | u8 *buf, size_t buf_len) |
1511 | 0 | { |
1512 | 0 | size_t padlen, i, pos; |
1513 | 0 | const u8 *addr[2]; |
1514 | 0 | size_t len[2]; |
1515 | 0 | u8 hash[16]; |
1516 | |
|
1517 | 0 | if (data_len + 16 > buf_len) |
1518 | 0 | return -1; |
1519 | | |
1520 | 0 | os_memcpy(buf, data, data_len); |
1521 | |
|
1522 | 0 | padlen = data_len % 16; |
1523 | 0 | if (padlen && data_len < buf_len) { |
1524 | 0 | padlen = 16 - padlen; |
1525 | 0 | os_memset(buf + data_len, 0, padlen); |
1526 | 0 | buf_len = data_len + padlen; |
1527 | 0 | } else { |
1528 | 0 | buf_len = data_len; |
1529 | 0 | } |
1530 | |
|
1531 | 0 | addr[0] = secret; |
1532 | 0 | len[0] = secret_len; |
1533 | 0 | addr[1] = msg->hdr->authenticator; |
1534 | 0 | len[1] = 16; |
1535 | 0 | if (md5_vector(2, addr, len, hash) < 0) { |
1536 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
1537 | 0 | return -1; |
1538 | 0 | } |
1539 | | |
1540 | 0 | for (i = 0; i < 16; i++) |
1541 | 0 | buf[i] ^= hash[i]; |
1542 | 0 | pos = 16; |
1543 | |
|
1544 | 0 | while (pos < buf_len) { |
1545 | 0 | addr[0] = secret; |
1546 | 0 | len[0] = secret_len; |
1547 | 0 | addr[1] = &buf[pos - 16]; |
1548 | 0 | len[1] = 16; |
1549 | 0 | if (md5_vector(2, addr, len, hash) < 0) { |
1550 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
1551 | 0 | return -1; |
1552 | 0 | } |
1553 | | |
1554 | 0 | for (i = 0; i < 16; i++) |
1555 | 0 | buf[pos + i] ^= hash[i]; |
1556 | |
|
1557 | 0 | pos += 16; |
1558 | 0 | } |
1559 | | |
1560 | 0 | return buf_len; |
1561 | 0 | } |
1562 | | |
1563 | | |
1564 | | /* Add User-Password attribute to a RADIUS message and encrypt it as specified |
1565 | | * in RFC 2865, Chap. 5.2 */ |
1566 | | struct radius_attr_hdr * |
1567 | | radius_msg_add_attr_user_password(struct radius_msg *msg, |
1568 | | const u8 *data, size_t data_len, |
1569 | | const u8 *secret, size_t secret_len) |
1570 | 0 | { |
1571 | 0 | u8 buf[128]; |
1572 | 0 | int res; |
1573 | |
|
1574 | 0 | res = radius_user_password_hide(msg, data, data_len, |
1575 | 0 | secret, secret_len, buf, sizeof(buf)); |
1576 | 0 | if (res < 0) |
1577 | 0 | return NULL; |
1578 | | |
1579 | 0 | return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD, |
1580 | 0 | buf, res); |
1581 | 0 | } |
1582 | | |
1583 | | |
1584 | | int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) |
1585 | 0 | { |
1586 | 0 | struct radius_attr_hdr *attr = NULL, *tmp; |
1587 | 0 | size_t i, dlen; |
1588 | |
|
1589 | 0 | for (i = 0; i < msg->attr_used; i++) { |
1590 | 0 | tmp = radius_get_attr_hdr(msg, i); |
1591 | 0 | if (tmp->type == type) { |
1592 | 0 | attr = tmp; |
1593 | 0 | break; |
1594 | 0 | } |
1595 | 0 | } |
1596 | |
|
1597 | 0 | if (!attr || attr->length < sizeof(*attr)) |
1598 | 0 | return -1; |
1599 | | |
1600 | 0 | dlen = attr->length - sizeof(*attr); |
1601 | 0 | if (buf) |
1602 | 0 | os_memcpy(buf, (attr + 1), dlen > len ? len : dlen); |
1603 | 0 | return dlen; |
1604 | 0 | } |
1605 | | |
1606 | | |
1607 | | int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, |
1608 | | size_t *len, const u8 *start) |
1609 | 0 | { |
1610 | 0 | size_t i; |
1611 | 0 | struct radius_attr_hdr *attr = NULL, *tmp; |
1612 | |
|
1613 | 0 | for (i = 0; i < msg->attr_used; i++) { |
1614 | 0 | tmp = radius_get_attr_hdr(msg, i); |
1615 | 0 | if (tmp->type == type && |
1616 | 0 | (start == NULL || (u8 *) tmp > start)) { |
1617 | 0 | attr = tmp; |
1618 | 0 | break; |
1619 | 0 | } |
1620 | 0 | } |
1621 | |
|
1622 | 0 | if (!attr || attr->length < sizeof(*attr)) |
1623 | 0 | return -1; |
1624 | | |
1625 | 0 | *buf = (u8 *) (attr + 1); |
1626 | 0 | *len = attr->length - sizeof(*attr); |
1627 | 0 | return 0; |
1628 | 0 | } |
1629 | | |
1630 | | |
1631 | | int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) |
1632 | 0 | { |
1633 | 0 | size_t i; |
1634 | 0 | int count; |
1635 | |
|
1636 | 0 | for (count = 0, i = 0; i < msg->attr_used; i++) { |
1637 | 0 | struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); |
1638 | 0 | if (attr->type == type && |
1639 | 0 | attr->length >= sizeof(struct radius_attr_hdr) + min_len) |
1640 | 0 | count++; |
1641 | 0 | } |
1642 | |
|
1643 | 0 | return count; |
1644 | 0 | } |
1645 | | |
1646 | | |
1647 | | struct radius_tunnel_attrs { |
1648 | | int tag_used; |
1649 | | int type; /* Tunnel-Type */ |
1650 | | int medium_type; /* Tunnel-Medium-Type */ |
1651 | | int vlanid; |
1652 | | }; |
1653 | | |
1654 | | |
1655 | | static int cmp_int(const void *a, const void *b) |
1656 | 0 | { |
1657 | 0 | int x, y; |
1658 | |
|
1659 | 0 | x = *((int *) a); |
1660 | 0 | y = *((int *) b); |
1661 | 0 | return (x - y); |
1662 | 0 | } |
1663 | | |
1664 | | |
1665 | | /** |
1666 | | * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information |
1667 | | * The k tagged vlans found are sorted by vlan_id and stored in the first k |
1668 | | * items of tagged. |
1669 | | * |
1670 | | * @msg: RADIUS message |
1671 | | * @untagged: Pointer to store untagged vid |
1672 | | * @numtagged: Size of tagged |
1673 | | * @tagged: Pointer to store tagged list |
1674 | | * |
1675 | | * Returns: 0 if neither tagged nor untagged configuration is found, 1 otherwise |
1676 | | */ |
1677 | | int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged, |
1678 | | int *tagged) |
1679 | 0 | { |
1680 | 0 | struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; |
1681 | 0 | size_t i; |
1682 | 0 | struct radius_attr_hdr *attr = NULL; |
1683 | 0 | const u8 *data; |
1684 | 0 | char buf[10]; |
1685 | 0 | size_t dlen; |
1686 | 0 | int j, taggedidx = 0, vlan_id; |
1687 | |
|
1688 | 0 | os_memset(&tunnel, 0, sizeof(tunnel)); |
1689 | 0 | for (j = 0; j < numtagged; j++) |
1690 | 0 | tagged[j] = 0; |
1691 | 0 | *untagged = 0; |
1692 | |
|
1693 | 0 | for (i = 0; i < msg->attr_used; i++) { |
1694 | 0 | attr = radius_get_attr_hdr(msg, i); |
1695 | 0 | if (attr->length < sizeof(*attr)) |
1696 | 0 | return -1; |
1697 | 0 | data = (const u8 *) (attr + 1); |
1698 | 0 | dlen = attr->length - sizeof(*attr); |
1699 | 0 | if (attr->length < 3) |
1700 | 0 | continue; |
1701 | 0 | if (data[0] >= RADIUS_TUNNEL_TAGS) |
1702 | 0 | tun = &tunnel[0]; |
1703 | 0 | else |
1704 | 0 | tun = &tunnel[data[0]]; |
1705 | |
|
1706 | 0 | switch (attr->type) { |
1707 | 0 | case RADIUS_ATTR_TUNNEL_TYPE: |
1708 | 0 | if (attr->length != 6) |
1709 | 0 | break; |
1710 | 0 | tun->tag_used++; |
1711 | 0 | tun->type = WPA_GET_BE24(data + 1); |
1712 | 0 | break; |
1713 | 0 | case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE: |
1714 | 0 | if (attr->length != 6) |
1715 | 0 | break; |
1716 | 0 | tun->tag_used++; |
1717 | 0 | tun->medium_type = WPA_GET_BE24(data + 1); |
1718 | 0 | break; |
1719 | 0 | case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: |
1720 | 0 | if (data[0] < RADIUS_TUNNEL_TAGS) { |
1721 | 0 | data++; |
1722 | 0 | dlen--; |
1723 | 0 | } |
1724 | 0 | if (dlen >= sizeof(buf)) |
1725 | 0 | break; |
1726 | 0 | os_memcpy(buf, data, dlen); |
1727 | 0 | buf[dlen] = '\0'; |
1728 | 0 | vlan_id = atoi(buf); |
1729 | 0 | if (vlan_id <= 0) |
1730 | 0 | break; |
1731 | 0 | tun->tag_used++; |
1732 | 0 | tun->vlanid = vlan_id; |
1733 | 0 | break; |
1734 | 0 | case RADIUS_ATTR_EGRESS_VLANID: /* RFC 4675 */ |
1735 | 0 | if (attr->length != 6) |
1736 | 0 | break; |
1737 | 0 | vlan_id = WPA_GET_BE24(data + 1); |
1738 | 0 | if (vlan_id <= 0) |
1739 | 0 | break; |
1740 | 0 | if (data[0] == 0x32) |
1741 | 0 | *untagged = vlan_id; |
1742 | 0 | else if (data[0] == 0x31 && tagged && |
1743 | 0 | taggedidx < numtagged) |
1744 | 0 | tagged[taggedidx++] = vlan_id; |
1745 | 0 | break; |
1746 | 0 | } |
1747 | 0 | } |
1748 | | |
1749 | | /* Use tunnel with the lowest tag for untagged VLAN id */ |
1750 | 0 | for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { |
1751 | 0 | tun = &tunnel[i]; |
1752 | 0 | if (tun->tag_used && |
1753 | 0 | tun->type == RADIUS_TUNNEL_TYPE_VLAN && |
1754 | 0 | tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && |
1755 | 0 | tun->vlanid > 0) { |
1756 | 0 | *untagged = tun->vlanid; |
1757 | 0 | break; |
1758 | 0 | } |
1759 | 0 | } |
1760 | |
|
1761 | 0 | if (taggedidx) |
1762 | 0 | qsort(tagged, taggedidx, sizeof(int), cmp_int); |
1763 | |
|
1764 | 0 | if (*untagged > 0 || taggedidx) |
1765 | 0 | return 1; |
1766 | 0 | return 0; |
1767 | 0 | } |
1768 | | |
1769 | | |
1770 | | /** |
1771 | | * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password |
1772 | | * @msg: Received RADIUS message |
1773 | | * @keylen: Length of returned password |
1774 | | * @secret: RADIUS shared secret |
1775 | | * @secret_len: Length of secret |
1776 | | * @sent_msg: Sent RADIUS message |
1777 | | * @n: Number of password attribute to return (starting with 0) |
1778 | | * Returns: Pointer to n-th password (free with os_free) or %NULL |
1779 | | */ |
1780 | | char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, |
1781 | | const u8 *secret, size_t secret_len, |
1782 | | struct radius_msg *sent_msg, size_t n) |
1783 | 0 | { |
1784 | 0 | u8 *buf = NULL; |
1785 | 0 | size_t buflen; |
1786 | 0 | const u8 *salt; |
1787 | 0 | u8 *str; |
1788 | 0 | const u8 *addr[3]; |
1789 | 0 | size_t len[3]; |
1790 | 0 | u8 hash[16]; |
1791 | 0 | u8 *pos; |
1792 | 0 | size_t i, j = 0; |
1793 | 0 | struct radius_attr_hdr *attr; |
1794 | 0 | const u8 *data; |
1795 | 0 | size_t dlen; |
1796 | 0 | const u8 *fdata = NULL; /* points to found item */ |
1797 | 0 | size_t fdlen = -1; |
1798 | 0 | char *ret = NULL; |
1799 | | |
1800 | | /* find n-th valid Tunnel-Password attribute */ |
1801 | 0 | for (i = 0; i < msg->attr_used; i++) { |
1802 | 0 | attr = radius_get_attr_hdr(msg, i); |
1803 | 0 | if (attr == NULL || |
1804 | 0 | attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) { |
1805 | 0 | continue; |
1806 | 0 | } |
1807 | 0 | if (attr->length <= 5) |
1808 | 0 | continue; |
1809 | 0 | data = (const u8 *) (attr + 1); |
1810 | 0 | dlen = attr->length - sizeof(*attr); |
1811 | 0 | if (dlen <= 3 || dlen % 16 != 3) |
1812 | 0 | continue; |
1813 | 0 | j++; |
1814 | 0 | if (j <= n) |
1815 | 0 | continue; |
1816 | | |
1817 | 0 | fdata = data; |
1818 | 0 | fdlen = dlen; |
1819 | 0 | break; |
1820 | 0 | } |
1821 | 0 | if (fdata == NULL) |
1822 | 0 | goto out; |
1823 | | |
1824 | | /* alloc writable memory for decryption */ |
1825 | 0 | buf = os_memdup(fdata, fdlen); |
1826 | 0 | if (buf == NULL) |
1827 | 0 | goto out; |
1828 | 0 | buflen = fdlen; |
1829 | | |
1830 | | /* init pointers */ |
1831 | 0 | salt = buf + 1; |
1832 | 0 | str = buf + 3; |
1833 | | |
1834 | | /* decrypt blocks */ |
1835 | 0 | pos = buf + buflen - 16; /* last block */ |
1836 | 0 | while (pos >= str + 16) { /* all but the first block */ |
1837 | 0 | addr[0] = secret; |
1838 | 0 | len[0] = secret_len; |
1839 | 0 | addr[1] = pos - 16; |
1840 | 0 | len[1] = 16; |
1841 | 0 | if (md5_vector(2, addr, len, hash) < 0) { |
1842 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
1843 | 0 | goto out; |
1844 | 0 | } |
1845 | | |
1846 | 0 | for (i = 0; i < 16; i++) |
1847 | 0 | pos[i] ^= hash[i]; |
1848 | |
|
1849 | 0 | pos -= 16; |
1850 | 0 | } |
1851 | | |
1852 | | /* decrypt first block */ |
1853 | 0 | if (str != pos) |
1854 | 0 | goto out; |
1855 | 0 | addr[0] = secret; |
1856 | 0 | len[0] = secret_len; |
1857 | 0 | addr[1] = sent_msg->hdr->authenticator; |
1858 | 0 | len[1] = 16; |
1859 | 0 | addr[2] = salt; |
1860 | 0 | len[2] = 2; |
1861 | 0 | if (md5_vector(3, addr, len, hash) < 0) { |
1862 | 0 | wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); |
1863 | 0 | goto out; |
1864 | 0 | } |
1865 | | |
1866 | 0 | for (i = 0; i < 16; i++) |
1867 | 0 | pos[i] ^= hash[i]; |
1868 | | |
1869 | | /* derive plaintext length from first subfield */ |
1870 | 0 | *keylen = (unsigned char) str[0]; |
1871 | 0 | if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) { |
1872 | | /* decryption error - invalid key length */ |
1873 | 0 | goto out; |
1874 | 0 | } |
1875 | 0 | if (*keylen == 0) { |
1876 | | /* empty password */ |
1877 | 0 | goto out; |
1878 | 0 | } |
1879 | | |
1880 | | /* copy passphrase into new buffer */ |
1881 | 0 | ret = os_malloc(*keylen); |
1882 | 0 | if (ret) |
1883 | 0 | os_memcpy(ret, str + 1, *keylen); |
1884 | |
|
1885 | 0 | out: |
1886 | | /* return new buffer */ |
1887 | 0 | os_free(buf); |
1888 | 0 | return ret; |
1889 | 0 | } |
1890 | | |
1891 | | |
1892 | | void radius_free_class(struct radius_class_data *c) |
1893 | 0 | { |
1894 | 0 | size_t i; |
1895 | 0 | if (c == NULL) |
1896 | 0 | return; |
1897 | 0 | for (i = 0; i < c->count; i++) |
1898 | 0 | os_free(c->attr[i].data); |
1899 | 0 | os_free(c->attr); |
1900 | 0 | c->attr = NULL; |
1901 | 0 | c->count = 0; |
1902 | 0 | } |
1903 | | |
1904 | | |
1905 | | int radius_copy_class(struct radius_class_data *dst, |
1906 | | const struct radius_class_data *src) |
1907 | 0 | { |
1908 | 0 | size_t i; |
1909 | |
|
1910 | 0 | if (src->attr == NULL) |
1911 | 0 | return 0; |
1912 | | |
1913 | 0 | dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data)); |
1914 | 0 | if (dst->attr == NULL) |
1915 | 0 | return -1; |
1916 | | |
1917 | 0 | dst->count = 0; |
1918 | |
|
1919 | 0 | for (i = 0; i < src->count; i++) { |
1920 | 0 | dst->attr[i].data = os_memdup(src->attr[i].data, |
1921 | 0 | src->attr[i].len); |
1922 | 0 | if (dst->attr[i].data == NULL) |
1923 | 0 | break; |
1924 | 0 | dst->count++; |
1925 | 0 | dst->attr[i].len = src->attr[i].len; |
1926 | 0 | } |
1927 | |
|
1928 | 0 | return 0; |
1929 | 0 | } |
1930 | | |
1931 | | |
1932 | | u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs) |
1933 | 0 | { |
1934 | 0 | size_t i, j; |
1935 | 0 | struct radius_attr_hdr *attr; |
1936 | |
|
1937 | 0 | for (i = 0; i < msg->attr_used; i++) { |
1938 | 0 | attr = radius_get_attr_hdr(msg, i); |
1939 | |
|
1940 | 0 | for (j = 0; attrs[j]; j++) { |
1941 | 0 | if (attr->type == attrs[j]) |
1942 | 0 | break; |
1943 | 0 | } |
1944 | |
|
1945 | 0 | if (attrs[j] == 0) |
1946 | 0 | return attr->type; /* unlisted attr */ |
1947 | 0 | } |
1948 | | |
1949 | 0 | return 0; |
1950 | 0 | } |
1951 | | |
1952 | | |
1953 | | int radius_gen_session_id(u8 *id, size_t len) |
1954 | 0 | { |
1955 | | /* |
1956 | | * Acct-Session-Id and Acct-Multi-Session-Id should be globally and |
1957 | | * temporarily unique. A high quality random number is required |
1958 | | * therefore. This could be be improved by switching to a GUID. |
1959 | | */ |
1960 | 0 | return os_get_random(id, len); |
1961 | 0 | } |