Branch data Line data Source code
1 : : /*
2 : : * RADIUS message processing
3 : : * Copyright (c) 2002-2009, 2011-2012, 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 : 3080 : struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg)
53 : : {
54 : 3080 : return msg->hdr;
55 : : }
56 : :
57 : :
58 : 641 : struct wpabuf * radius_msg_get_buf(struct radius_msg *msg)
59 : : {
60 : 641 : return msg->buf;
61 : : }
62 : :
63 : :
64 : : static struct radius_attr_hdr *
65 : 15655 : radius_get_attr_hdr(struct radius_msg *msg, int idx)
66 : : {
67 : 15655 : return (struct radius_attr_hdr *)
68 : 15655 : (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]);
69 : : }
70 : :
71 : :
72 : 638 : static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
73 : : {
74 : 638 : msg->hdr->code = code;
75 : 638 : msg->hdr->identifier = identifier;
76 : 638 : }
77 : :
78 : :
79 : 1271 : static int radius_msg_initialize(struct radius_msg *msg)
80 : : {
81 : 1271 : msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT,
82 : : sizeof(*msg->attr_pos));
83 [ - + ]: 1271 : if (msg->attr_pos == NULL)
84 : 0 : return -1;
85 : :
86 : 1271 : msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
87 : 1271 : msg->attr_used = 0;
88 : :
89 : 1271 : return 0;
90 : : }
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 : 638 : struct radius_msg * radius_msg_new(u8 code, u8 identifier)
103 : : {
104 : : struct radius_msg *msg;
105 : :
106 : 638 : msg = os_zalloc(sizeof(*msg));
107 [ - + ]: 638 : if (msg == NULL)
108 : 0 : return NULL;
109 : :
110 : 638 : msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE);
111 [ + - ][ - + ]: 638 : if (msg->buf == NULL || radius_msg_initialize(msg)) {
112 : 0 : radius_msg_free(msg);
113 : 0 : return NULL;
114 : : }
115 : 638 : msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr));
116 : :
117 : 638 : radius_msg_set_hdr(msg, code, identifier);
118 : :
119 : 638 : return msg;
120 : : }
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 : 1416 : void radius_msg_free(struct radius_msg *msg)
128 : : {
129 [ + + ]: 1416 : if (msg == NULL)
130 : 1416 : return;
131 : :
132 : 1271 : wpabuf_free(msg->buf);
133 : 1271 : os_free(msg->attr_pos);
134 : 1271 : os_free(msg);
135 : : }
136 : :
137 : :
138 : 0 : static const char *radius_code_string(u8 code)
139 : : {
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 : : }
158 : : }
159 : :
160 : :
161 : : struct radius_attr_type {
162 : : u8 type;
163 : : char *name;
164 : : enum {
165 : : RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
166 : : RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6
167 : : } data_type;
168 : : };
169 : :
170 : : static struct radius_attr_type radius_attrs[] =
171 : : {
172 : : { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
173 : : { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
174 : : { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
175 : : { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
176 : : { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
177 : : { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
178 : : { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
179 : : { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
180 : : { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
181 : : { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
182 : : { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
183 : : { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
184 : : RADIUS_ATTR_INT32 },
185 : : { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
186 : : RADIUS_ATTR_TEXT },
187 : : { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
188 : : RADIUS_ATTR_TEXT },
189 : : { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
190 : : { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST },
191 : : { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
192 : : RADIUS_ATTR_INT32 },
193 : : { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
194 : : { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
195 : : RADIUS_ATTR_INT32 },
196 : : { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
197 : : RADIUS_ATTR_INT32 },
198 : : { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
199 : : { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
200 : : { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
201 : : RADIUS_ATTR_INT32 },
202 : : { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
203 : : RADIUS_ATTR_INT32 },
204 : : { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
205 : : RADIUS_ATTR_INT32 },
206 : : { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
207 : : RADIUS_ATTR_INT32 },
208 : : { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
209 : : RADIUS_ATTR_TEXT },
210 : : { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
211 : : { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords",
212 : : RADIUS_ATTR_INT32 },
213 : : { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
214 : : RADIUS_ATTR_INT32 },
215 : : { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
216 : : RADIUS_ATTR_INT32 },
217 : : { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
218 : : { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
219 : : { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
220 : : RADIUS_ATTR_HEXDUMP },
221 : : { RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password",
222 : : RADIUS_ATTR_UNDIST },
223 : : { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
224 : : { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
225 : : { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
226 : : RADIUS_ATTR_UNDIST },
227 : : { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id",
228 : : RADIUS_ATTR_HEXDUMP },
229 : : { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
230 : : RADIUS_ATTR_INT32 },
231 : : { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity",
232 : : RADIUS_ATTR_TEXT },
233 : : { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
234 : : { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }
235 : : };
236 : : #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs)
237 : :
238 : :
239 : 0 : static struct radius_attr_type *radius_get_attr_type(u8 type)
240 : : {
241 : : size_t i;
242 : :
243 [ # # ]: 0 : for (i = 0; i < RADIUS_ATTRS; i++) {
244 [ # # ]: 0 : if (type == radius_attrs[i].type)
245 : 0 : return &radius_attrs[i];
246 : : }
247 : :
248 : 0 : return NULL;
249 : : }
250 : :
251 : :
252 : 0 : static void print_char(char c)
253 : : {
254 [ # # ][ # # ]: 0 : if (c >= 32 && c < 127)
255 : 0 : printf("%c", c);
256 : : else
257 : 0 : printf("<%02x>", c);
258 : 0 : }
259 : :
260 : :
261 : 0 : static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
262 : : {
263 : : struct radius_attr_type *attr;
264 : : int i, len;
265 : : unsigned char *pos;
266 : :
267 : 0 : attr = radius_get_attr_type(hdr->type);
268 : :
269 [ # # ]: 0 : printf(" Attribute %d (%s) length=%d\n",
270 : 0 : hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
271 : :
272 [ # # ][ # # ]: 0 : if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr))
273 : 0 : return;
274 : :
275 : 0 : len = hdr->length - sizeof(struct radius_attr_hdr);
276 : 0 : pos = (unsigned char *) (hdr + 1);
277 : :
278 [ # # # # : 0 : switch (attr->data_type) {
# # ]
279 : : case RADIUS_ATTR_TEXT:
280 : 0 : printf(" Value: '");
281 [ # # ]: 0 : for (i = 0; i < len; i++)
282 : 0 : print_char(pos[i]);
283 : 0 : printf("'\n");
284 : 0 : break;
285 : :
286 : : case RADIUS_ATTR_IP:
287 [ # # ]: 0 : if (len == 4) {
288 : : struct in_addr addr;
289 : 0 : os_memcpy(&addr, pos, 4);
290 : 0 : printf(" Value: %s\n", inet_ntoa(addr));
291 : : } else
292 : 0 : printf(" Invalid IP address length %d\n", len);
293 : 0 : break;
294 : :
295 : : #ifdef CONFIG_IPV6
296 : : case RADIUS_ATTR_IPV6:
297 [ # # ]: 0 : if (len == 16) {
298 : : char buf[128];
299 : : const char *atxt;
300 : 0 : struct in6_addr *addr = (struct in6_addr *) pos;
301 : 0 : atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
302 [ # # ]: 0 : printf(" Value: %s\n", atxt ? atxt : "?");
303 : : } else
304 : 0 : printf(" Invalid IPv6 address length %d\n", len);
305 : 0 : break;
306 : : #endif /* CONFIG_IPV6 */
307 : :
308 : : case RADIUS_ATTR_HEXDUMP:
309 : : case RADIUS_ATTR_UNDIST:
310 : 0 : printf(" Value:");
311 [ # # ]: 0 : for (i = 0; i < len; i++)
312 : 0 : printf(" %02x", pos[i]);
313 : 0 : printf("\n");
314 : 0 : break;
315 : :
316 : : case RADIUS_ATTR_INT32:
317 [ # # ]: 0 : if (len == 4)
318 : 0 : printf(" Value: %u\n", WPA_GET_BE32(pos));
319 : : else
320 : 0 : printf(" Invalid INT32 length %d\n", len);
321 : 0 : break;
322 : :
323 : : default:
324 : 0 : break;
325 : : }
326 : : }
327 : :
328 : :
329 : 0 : void radius_msg_dump(struct radius_msg *msg)
330 : : {
331 : : size_t i;
332 : :
333 : 0 : printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
334 : 0 : msg->hdr->code, radius_code_string(msg->hdr->code),
335 : 0 : msg->hdr->identifier, be_to_host16(msg->hdr->length));
336 : :
337 [ # # ]: 0 : for (i = 0; i < msg->attr_used; i++) {
338 : 0 : struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
339 : 0 : radius_msg_dump_attr(attr);
340 : : }
341 : 0 : }
342 : :
343 : :
344 : 634 : int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
345 : : size_t secret_len)
346 : : {
347 [ + - ]: 634 : if (secret) {
348 : : u8 auth[MD5_MAC_LEN];
349 : : struct radius_attr_hdr *attr;
350 : :
351 : 634 : os_memset(auth, 0, MD5_MAC_LEN);
352 : 634 : attr = radius_msg_add_attr(msg,
353 : : RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
354 : : auth, MD5_MAC_LEN);
355 [ - + ]: 634 : if (attr == NULL) {
356 : 0 : wpa_printf(MSG_WARNING, "RADIUS: Could not add "
357 : : "Message-Authenticator");
358 : 0 : return -1;
359 : : }
360 : 634 : msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
361 : 634 : hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
362 : 634 : wpabuf_len(msg->buf), (u8 *) (attr + 1));
363 : : } else
364 : 0 : msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
365 : :
366 [ - + ]: 634 : if (wpabuf_len(msg->buf) > 0xffff) {
367 : 0 : wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
368 : 0 : (unsigned long) wpabuf_len(msg->buf));
369 : 0 : return -1;
370 : : }
371 : 634 : return 0;
372 : : }
373 : :
374 : :
375 : 0 : int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
376 : : size_t secret_len, const u8 *req_authenticator)
377 : : {
378 : : u8 auth[MD5_MAC_LEN];
379 : : struct radius_attr_hdr *attr;
380 : : const u8 *addr[4];
381 : : size_t len[4];
382 : :
383 : 0 : os_memset(auth, 0, MD5_MAC_LEN);
384 : 0 : attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
385 : : auth, MD5_MAC_LEN);
386 [ # # ]: 0 : if (attr == NULL) {
387 : 0 : printf("WARNING: Could not add Message-Authenticator\n");
388 : 0 : return -1;
389 : : }
390 : 0 : msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
391 : 0 : os_memcpy(msg->hdr->authenticator, req_authenticator,
392 : : sizeof(msg->hdr->authenticator));
393 : 0 : hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
394 : 0 : wpabuf_len(msg->buf), (u8 *) (attr + 1));
395 : :
396 : : /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
397 : 0 : addr[0] = (u8 *) msg->hdr;
398 : 0 : len[0] = 1 + 1 + 2;
399 : 0 : addr[1] = req_authenticator;
400 : 0 : len[1] = MD5_MAC_LEN;
401 : 0 : addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
402 : 0 : len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
403 : 0 : addr[3] = secret;
404 : 0 : len[3] = secret_len;
405 : 0 : md5_vector(4, addr, len, msg->hdr->authenticator);
406 : :
407 [ # # ]: 0 : if (wpabuf_len(msg->buf) > 0xffff) {
408 : 0 : wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
409 : 0 : (unsigned long) wpabuf_len(msg->buf));
410 : 0 : return -1;
411 : : }
412 : 0 : return 0;
413 : : }
414 : :
415 : :
416 : 0 : int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
417 : : size_t secret_len,
418 : : const struct radius_hdr *req_hdr)
419 : : {
420 : : const u8 *addr[2];
421 : : size_t len[2];
422 : : u8 auth[MD5_MAC_LEN];
423 : : struct radius_attr_hdr *attr;
424 : :
425 : 0 : os_memset(auth, 0, MD5_MAC_LEN);
426 : 0 : attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
427 : : auth, MD5_MAC_LEN);
428 [ # # ]: 0 : if (attr == NULL) {
429 : 0 : wpa_printf(MSG_WARNING, "Could not add Message-Authenticator");
430 : 0 : return -1;
431 : : }
432 : :
433 : 0 : msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
434 : 0 : os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16);
435 : 0 : hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
436 : 0 : wpabuf_len(msg->buf), (u8 *) (attr + 1));
437 : :
438 : : /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
439 : 0 : addr[0] = wpabuf_head_u8(msg->buf);
440 : 0 : len[0] = wpabuf_len(msg->buf);
441 : 0 : addr[1] = secret;
442 : 0 : len[1] = secret_len;
443 [ # # ]: 0 : if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0)
444 : 0 : return -1;
445 : :
446 [ # # ]: 0 : if (wpabuf_len(msg->buf) > 0xffff) {
447 : 0 : wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
448 : 0 : (unsigned long) wpabuf_len(msg->buf));
449 : 0 : return -1;
450 : : }
451 : 0 : return 0;
452 : : }
453 : :
454 : :
455 : 4 : void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
456 : : size_t secret_len)
457 : : {
458 : : const u8 *addr[2];
459 : : size_t len[2];
460 : :
461 : 4 : msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
462 : 4 : os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
463 : 4 : addr[0] = wpabuf_head(msg->buf);
464 : 4 : len[0] = wpabuf_len(msg->buf);
465 : 4 : addr[1] = secret;
466 : 4 : len[1] = secret_len;
467 : 4 : md5_vector(2, addr, len, msg->hdr->authenticator);
468 : :
469 [ - + ]: 4 : if (wpabuf_len(msg->buf) > 0xffff) {
470 : 0 : wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
471 : 0 : (unsigned long) wpabuf_len(msg->buf));
472 : : }
473 : 4 : }
474 : :
475 : :
476 : 0 : int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
477 : : size_t secret_len)
478 : : {
479 : : const u8 *addr[4];
480 : : size_t len[4];
481 : : u8 zero[MD5_MAC_LEN];
482 : : u8 hash[MD5_MAC_LEN];
483 : :
484 : 0 : os_memset(zero, 0, sizeof(zero));
485 : 0 : addr[0] = (u8 *) msg->hdr;
486 : 0 : len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
487 : 0 : addr[1] = zero;
488 : 0 : len[1] = MD5_MAC_LEN;
489 : 0 : addr[2] = (u8 *) (msg->hdr + 1);
490 : 0 : len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
491 : 0 : addr[3] = secret;
492 : 0 : len[3] = secret_len;
493 : 0 : md5_vector(4, addr, len, hash);
494 : 0 : return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
495 : : }
496 : :
497 : :
498 : 0 : int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
499 : : size_t secret_len)
500 : : {
501 : : const u8 *addr[4];
502 : : size_t len[4];
503 : : u8 zero[MD5_MAC_LEN];
504 : : u8 hash[MD5_MAC_LEN];
505 : : u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
506 : : u8 orig_authenticator[16];
507 : :
508 : 0 : struct radius_attr_hdr *attr = NULL, *tmp;
509 : : size_t i;
510 : :
511 : 0 : os_memset(zero, 0, sizeof(zero));
512 : 0 : addr[0] = (u8 *) msg->hdr;
513 : 0 : len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
514 : 0 : addr[1] = zero;
515 : 0 : len[1] = MD5_MAC_LEN;
516 : 0 : addr[2] = (u8 *) (msg->hdr + 1);
517 : 0 : len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
518 : 0 : addr[3] = secret;
519 : 0 : len[3] = secret_len;
520 : 0 : md5_vector(4, addr, len, hash);
521 [ # # ]: 0 : if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
522 : 0 : return 1;
523 : :
524 [ # # ]: 0 : for (i = 0; i < msg->attr_used; i++) {
525 : 0 : tmp = radius_get_attr_hdr(msg, i);
526 [ # # ]: 0 : if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
527 [ # # ]: 0 : if (attr != NULL) {
528 : 0 : wpa_printf(MSG_WARNING, "Multiple "
529 : : "Message-Authenticator attributes "
530 : : "in RADIUS message");
531 : 0 : return 1;
532 : : }
533 : 0 : attr = tmp;
534 : : }
535 : : }
536 : :
537 [ # # ]: 0 : if (attr == NULL) {
538 : : /* Message-Authenticator is MAY; not required */
539 : 0 : return 0;
540 : : }
541 : :
542 : 0 : os_memcpy(orig, attr + 1, MD5_MAC_LEN);
543 : 0 : os_memset(attr + 1, 0, MD5_MAC_LEN);
544 : 0 : os_memcpy(orig_authenticator, msg->hdr->authenticator,
545 : : sizeof(orig_authenticator));
546 : 0 : os_memset(msg->hdr->authenticator, 0,
547 : : sizeof(msg->hdr->authenticator));
548 : 0 : hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
549 : 0 : wpabuf_len(msg->buf), auth);
550 : 0 : os_memcpy(attr + 1, orig, MD5_MAC_LEN);
551 : 0 : os_memcpy(msg->hdr->authenticator, orig_authenticator,
552 : : sizeof(orig_authenticator));
553 : :
554 : 0 : return os_memcmp(orig, auth, MD5_MAC_LEN) != 0;
555 : : }
556 : :
557 : :
558 : 9799 : static int radius_msg_add_attr_to_array(struct radius_msg *msg,
559 : : struct radius_attr_hdr *attr)
560 : : {
561 [ + + ]: 9799 : if (msg->attr_used >= msg->attr_size) {
562 : : size_t *nattr_pos;
563 : 5 : int nlen = msg->attr_size * 2;
564 : :
565 : 5 : nattr_pos = os_realloc_array(msg->attr_pos, nlen,
566 : : sizeof(*msg->attr_pos));
567 [ - + ]: 5 : if (nattr_pos == NULL)
568 : 0 : return -1;
569 : :
570 : 5 : msg->attr_pos = nattr_pos;
571 : 5 : msg->attr_size = nlen;
572 : : }
573 : :
574 : 19598 : msg->attr_pos[msg->attr_used++] =
575 : 9799 : (unsigned char *) attr - wpabuf_head_u8(msg->buf);
576 : :
577 : 9799 : return 0;
578 : : }
579 : :
580 : :
581 : 7417 : struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
582 : : const u8 *data, size_t data_len)
583 : : {
584 : : size_t buf_needed;
585 : : struct radius_attr_hdr *attr;
586 : :
587 [ - + ]: 7417 : if (data_len > RADIUS_MAX_ATTR_LEN) {
588 : 0 : printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
589 : : (unsigned long) data_len);
590 : 0 : return NULL;
591 : : }
592 : :
593 : 7417 : buf_needed = sizeof(*attr) + data_len;
594 : :
595 [ + + ]: 7417 : if (wpabuf_tailroom(msg->buf) < buf_needed) {
596 : : /* allocate more space for message buffer */
597 [ - + ]: 25 : if (wpabuf_resize(&msg->buf, buf_needed) < 0)
598 : 0 : return NULL;
599 : 25 : msg->hdr = wpabuf_mhead(msg->buf);
600 : : }
601 : :
602 : 7417 : attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
603 : 7417 : attr->type = type;
604 : 7417 : attr->length = sizeof(*attr) + data_len;
605 : 7417 : wpabuf_put_data(msg->buf, data, data_len);
606 : :
607 [ - + ]: 7417 : if (radius_msg_add_attr_to_array(msg, attr))
608 : 0 : return NULL;
609 : :
610 : 7417 : return attr;
611 : : }
612 : :
613 : :
614 : : /**
615 : : * radius_msg_parse - Parse a RADIUS message
616 : : * @data: RADIUS message to be parsed
617 : : * @len: Length of data buffer in octets
618 : : * Returns: Parsed RADIUS message or %NULL on failure
619 : : *
620 : : * This parses a RADIUS message and makes a copy of its data. The caller is
621 : : * responsible for freeing the returned data with radius_msg_free().
622 : : */
623 : 633 : struct radius_msg * radius_msg_parse(const u8 *data, size_t len)
624 : : {
625 : : struct radius_msg *msg;
626 : : struct radius_hdr *hdr;
627 : : struct radius_attr_hdr *attr;
628 : : size_t msg_len;
629 : : unsigned char *pos, *end;
630 : :
631 [ + - ][ - + ]: 633 : if (data == NULL || len < sizeof(*hdr))
632 : 0 : return NULL;
633 : :
634 : 633 : hdr = (struct radius_hdr *) data;
635 : :
636 : 633 : msg_len = be_to_host16(hdr->length);
637 [ + - ][ - + ]: 633 : if (msg_len < sizeof(*hdr) || msg_len > len) {
638 : 0 : wpa_printf(MSG_INFO, "RADIUS: Invalid message length");
639 : 0 : return NULL;
640 : : }
641 : :
642 [ - + ]: 633 : if (msg_len < len) {
643 : 0 : wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after "
644 : : "RADIUS message", (unsigned long) len - msg_len);
645 : : }
646 : :
647 : 633 : msg = os_zalloc(sizeof(*msg));
648 [ - + ]: 633 : if (msg == NULL)
649 : 0 : return NULL;
650 : :
651 : 633 : msg->buf = wpabuf_alloc_copy(data, msg_len);
652 [ + - ][ - + ]: 633 : if (msg->buf == NULL || radius_msg_initialize(msg)) {
653 : 0 : radius_msg_free(msg);
654 : 0 : return NULL;
655 : : }
656 : 633 : msg->hdr = wpabuf_mhead(msg->buf);
657 : :
658 : : /* parse attributes */
659 : 633 : pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr);
660 : 633 : end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf);
661 [ + + ]: 3015 : while (pos < end) {
662 [ - + ]: 2382 : if ((size_t) (end - pos) < sizeof(*attr))
663 : 0 : goto fail;
664 : :
665 : 2382 : attr = (struct radius_attr_hdr *) pos;
666 : :
667 [ + - ][ + - ]: 2382 : if (pos + attr->length > end || attr->length < sizeof(*attr))
668 : : goto fail;
669 : :
670 : : /* TODO: check that attr->length is suitable for attr->type */
671 : :
672 [ - + ]: 2382 : if (radius_msg_add_attr_to_array(msg, attr))
673 : 0 : goto fail;
674 : :
675 : 2382 : pos += attr->length;
676 : : }
677 : :
678 : 633 : return msg;
679 : :
680 : : fail:
681 : 0 : radius_msg_free(msg);
682 : 633 : return NULL;
683 : : }
684 : :
685 : :
686 : 634 : int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
687 : : {
688 : 634 : const u8 *pos = data;
689 : 634 : size_t left = data_len;
690 : :
691 [ + + ]: 1328 : while (left > 0) {
692 : : int len;
693 [ + + ]: 694 : if (left > RADIUS_MAX_ATTR_LEN)
694 : 60 : len = RADIUS_MAX_ATTR_LEN;
695 : : else
696 : 634 : len = left;
697 : :
698 [ - + ]: 694 : if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
699 : : pos, len))
700 : 0 : return 0;
701 : :
702 : 694 : pos += len;
703 : 694 : left -= len;
704 : : }
705 : :
706 : 634 : return 1;
707 : : }
708 : :
709 : :
710 : 633 : struct wpabuf * radius_msg_get_eap(struct radius_msg *msg)
711 : : {
712 : : struct wpabuf *eap;
713 : : size_t len, i;
714 : : struct radius_attr_hdr *attr;
715 : :
716 [ - + ]: 633 : if (msg == NULL)
717 : 0 : return NULL;
718 : :
719 : 633 : len = 0;
720 [ + + ]: 3015 : for (i = 0; i < msg->attr_used; i++) {
721 : 2382 : attr = radius_get_attr_hdr(msg, i);
722 [ + + ][ + - ]: 2382 : if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
723 : 1026 : attr->length > sizeof(struct radius_attr_hdr))
724 : 1026 : len += attr->length - sizeof(struct radius_attr_hdr);
725 : : }
726 : :
727 [ - + ]: 633 : if (len == 0)
728 : 0 : return NULL;
729 : :
730 : 633 : eap = wpabuf_alloc(len);
731 [ - + ]: 633 : if (eap == NULL)
732 : 0 : return NULL;
733 : :
734 [ + + ]: 3015 : for (i = 0; i < msg->attr_used; i++) {
735 : 2382 : attr = radius_get_attr_hdr(msg, i);
736 [ + + ][ + - ]: 2382 : if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
737 : 1026 : attr->length > sizeof(struct radius_attr_hdr)) {
738 : 1026 : int flen = attr->length - sizeof(*attr);
739 : 1026 : wpabuf_put_data(eap, attr + 1, flen);
740 : : }
741 : : }
742 : :
743 : 633 : return eap;
744 : : }
745 : :
746 : :
747 : 633 : int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
748 : : size_t secret_len, const u8 *req_auth)
749 : : {
750 : : u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
751 : : u8 orig_authenticator[16];
752 : 633 : struct radius_attr_hdr *attr = NULL, *tmp;
753 : : size_t i;
754 : :
755 [ + + ]: 3015 : for (i = 0; i < msg->attr_used; i++) {
756 : 2382 : tmp = radius_get_attr_hdr(msg, i);
757 [ + + ]: 2382 : if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
758 [ - + ]: 633 : if (attr != NULL) {
759 : 0 : printf("Multiple Message-Authenticator "
760 : : "attributes in RADIUS message\n");
761 : 0 : return 1;
762 : : }
763 : 633 : attr = tmp;
764 : : }
765 : : }
766 : :
767 [ - + ]: 633 : if (attr == NULL) {
768 : 0 : printf("No Message-Authenticator attribute found\n");
769 : 0 : return 1;
770 : : }
771 : :
772 : 633 : os_memcpy(orig, attr + 1, MD5_MAC_LEN);
773 : 633 : os_memset(attr + 1, 0, MD5_MAC_LEN);
774 [ + - ]: 633 : if (req_auth) {
775 : 633 : os_memcpy(orig_authenticator, msg->hdr->authenticator,
776 : : sizeof(orig_authenticator));
777 : 633 : os_memcpy(msg->hdr->authenticator, req_auth,
778 : : sizeof(msg->hdr->authenticator));
779 : : }
780 : 633 : hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
781 : 633 : wpabuf_len(msg->buf), auth);
782 : 633 : os_memcpy(attr + 1, orig, MD5_MAC_LEN);
783 [ + - ]: 633 : if (req_auth) {
784 : 633 : os_memcpy(msg->hdr->authenticator, orig_authenticator,
785 : : sizeof(orig_authenticator));
786 : : }
787 : :
788 [ - + ]: 633 : if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
789 : 0 : printf("Invalid Message-Authenticator!\n");
790 : 0 : return 1;
791 : : }
792 : :
793 : 633 : return 0;
794 : : }
795 : :
796 : :
797 : 633 : int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
798 : : size_t secret_len, struct radius_msg *sent_msg, int auth)
799 : : {
800 : : const u8 *addr[4];
801 : : size_t len[4];
802 : : u8 hash[MD5_MAC_LEN];
803 : :
804 [ - + ]: 633 : if (sent_msg == NULL) {
805 : 0 : printf("No matching Access-Request message found\n");
806 : 0 : return 1;
807 : : }
808 : :
809 [ + - - + ]: 1266 : if (auth &&
810 : 633 : radius_msg_verify_msg_auth(msg, secret, secret_len,
811 : 633 : sent_msg->hdr->authenticator)) {
812 : 0 : return 1;
813 : : }
814 : :
815 : : /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
816 : 633 : addr[0] = (u8 *) msg->hdr;
817 : 633 : len[0] = 1 + 1 + 2;
818 : 633 : addr[1] = sent_msg->hdr->authenticator;
819 : 633 : len[1] = MD5_MAC_LEN;
820 : 633 : addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
821 : 633 : len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
822 : 633 : addr[3] = secret;
823 : 633 : len[3] = secret_len;
824 : 633 : md5_vector(4, addr, len, hash);
825 [ - + ]: 633 : if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
826 : 0 : printf("Response Authenticator invalid!\n");
827 : 0 : return 1;
828 : : }
829 : :
830 : 633 : return 0;
831 : : }
832 : :
833 : :
834 : 511 : int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
835 : : u8 type)
836 : : {
837 : : struct radius_attr_hdr *attr;
838 : : size_t i;
839 : 511 : int count = 0;
840 : :
841 [ + + ]: 2437 : for (i = 0; i < src->attr_used; i++) {
842 : 1926 : attr = radius_get_attr_hdr(src, i);
843 [ + + ][ + - ]: 1926 : if (attr->type == type && attr->length >= sizeof(*attr)) {
844 [ - + ]: 511 : if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
845 : 511 : attr->length - sizeof(*attr)))
846 : 0 : return -1;
847 : 511 : count++;
848 : : }
849 : : }
850 : :
851 : 511 : return count;
852 : : }
853 : :
854 : :
855 : : /* Create Request Authenticator. The value should be unique over the lifetime
856 : : * of the shared secret between authenticator and authentication server.
857 : : * Use one-way MD5 hash calculated from current timestamp and some data given
858 : : * by the caller. */
859 : 638 : void radius_msg_make_authenticator(struct radius_msg *msg,
860 : : const u8 *data, size_t len)
861 : : {
862 : : struct os_time tv;
863 : : long int l;
864 : : const u8 *addr[3];
865 : : size_t elen[3];
866 : :
867 : 638 : os_get_time(&tv);
868 : 638 : l = os_random();
869 : 638 : addr[0] = (u8 *) &tv;
870 : 638 : elen[0] = sizeof(tv);
871 : 638 : addr[1] = data;
872 : 638 : elen[1] = len;
873 : 638 : addr[2] = (u8 *) &l;
874 : 638 : elen[2] = sizeof(l);
875 : 638 : md5_vector(3, addr, elen, msg->hdr->authenticator);
876 : 638 : }
877 : :
878 : :
879 : : /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
880 : : * Returns the Attribute payload and sets alen to indicate the length of the
881 : : * payload if a vendor attribute with subtype is found, otherwise returns NULL.
882 : : * The returned payload is allocated with os_malloc() and caller must free it
883 : : * by calling os_free().
884 : : */
885 : 210 : static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
886 : : u8 subtype, size_t *alen)
887 : : {
888 : : u8 *data, *pos;
889 : : size_t i, len;
890 : :
891 [ - + ]: 210 : if (msg == NULL)
892 : 0 : return NULL;
893 : :
894 [ + - ]: 525 : for (i = 0; i < msg->attr_used; i++) {
895 : 525 : struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
896 : : size_t left;
897 : : u32 vendor_id;
898 : : struct radius_attr_vendor *vhdr;
899 : :
900 [ + + ][ - + ]: 525 : if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC ||
901 : 315 : attr->length < sizeof(*attr))
902 : 210 : continue;
903 : :
904 : 315 : left = attr->length - sizeof(*attr);
905 [ - + ]: 315 : if (left < 4)
906 : 0 : continue;
907 : :
908 : 315 : pos = (u8 *) (attr + 1);
909 : :
910 : 315 : os_memcpy(&vendor_id, pos, 4);
911 : 315 : pos += 4;
912 : 315 : left -= 4;
913 : :
914 [ - + ]: 315 : if (ntohl(vendor_id) != vendor)
915 : 0 : continue;
916 : :
917 [ + + ]: 420 : while (left >= sizeof(*vhdr)) {
918 : 315 : vhdr = (struct radius_attr_vendor *) pos;
919 [ + - ][ - + ]: 315 : if (vhdr->vendor_length > left ||
920 : 315 : vhdr->vendor_length < sizeof(*vhdr)) {
921 : 0 : left = 0;
922 : 0 : break;
923 : : }
924 [ + + ]: 315 : if (vhdr->vendor_type != subtype) {
925 : 105 : pos += vhdr->vendor_length;
926 : 105 : left -= vhdr->vendor_length;
927 : 105 : continue;
928 : : }
929 : :
930 : 210 : len = vhdr->vendor_length - sizeof(*vhdr);
931 : 210 : data = os_malloc(len);
932 [ - + ]: 210 : if (data == NULL)
933 : 0 : return NULL;
934 : 210 : os_memcpy(data, pos + sizeof(*vhdr), len);
935 [ + - ]: 210 : if (alen)
936 : 210 : *alen = len;
937 : 210 : return data;
938 : : }
939 : : }
940 : :
941 : 210 : return NULL;
942 : : }
943 : :
944 : :
945 : 210 : static u8 * decrypt_ms_key(const u8 *key, size_t len,
946 : : const u8 *req_authenticator,
947 : : const u8 *secret, size_t secret_len, size_t *reslen)
948 : : {
949 : : u8 *plain, *ppos, *res;
950 : : const u8 *pos;
951 : : size_t left, plen;
952 : : u8 hash[MD5_MAC_LEN];
953 : 210 : int i, first = 1;
954 : : const u8 *addr[3];
955 : : size_t elen[3];
956 : :
957 : : /* key: 16-bit salt followed by encrypted key info */
958 : :
959 [ - + ]: 210 : if (len < 2 + 16)
960 : 0 : return NULL;
961 : :
962 : 210 : pos = key + 2;
963 : 210 : left = len - 2;
964 [ - + ]: 210 : if (left % 16) {
965 : 0 : printf("Invalid ms key len %lu\n", (unsigned long) left);
966 : 0 : return NULL;
967 : : }
968 : :
969 : 210 : plen = left;
970 : 210 : ppos = plain = os_malloc(plen);
971 [ - + ]: 210 : if (plain == NULL)
972 : 0 : return NULL;
973 : 210 : plain[0] = 0;
974 : :
975 [ + + ]: 840 : while (left > 0) {
976 : : /* b(1) = MD5(Secret + Request-Authenticator + Salt)
977 : : * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
978 : :
979 : 630 : addr[0] = secret;
980 : 630 : elen[0] = secret_len;
981 [ + + ]: 630 : if (first) {
982 : 210 : addr[1] = req_authenticator;
983 : 210 : elen[1] = MD5_MAC_LEN;
984 : 210 : addr[2] = key;
985 : 210 : elen[2] = 2; /* Salt */
986 : : } else {
987 : 420 : addr[1] = pos - MD5_MAC_LEN;
988 : 420 : elen[1] = MD5_MAC_LEN;
989 : : }
990 [ + + ]: 630 : md5_vector(first ? 3 : 2, addr, elen, hash);
991 : 630 : first = 0;
992 : :
993 [ + + ]: 10710 : for (i = 0; i < MD5_MAC_LEN; i++)
994 : 10080 : *ppos++ = *pos++ ^ hash[i];
995 : 630 : left -= MD5_MAC_LEN;
996 : : }
997 : :
998 [ + - ][ - + ]: 210 : if (plain[0] == 0 || plain[0] > plen - 1) {
999 : 0 : printf("Failed to decrypt MPPE key\n");
1000 : 0 : os_free(plain);
1001 : 0 : return NULL;
1002 : : }
1003 : :
1004 : 210 : res = os_malloc(plain[0]);
1005 [ - + ]: 210 : if (res == NULL) {
1006 : 0 : os_free(plain);
1007 : 0 : return NULL;
1008 : : }
1009 : 210 : os_memcpy(res, plain + 1, plain[0]);
1010 [ + - ]: 210 : if (reslen)
1011 : 210 : *reslen = plain[0];
1012 : 210 : os_free(plain);
1013 : 210 : return res;
1014 : : }
1015 : :
1016 : :
1017 : 0 : static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
1018 : : const u8 *req_authenticator,
1019 : : const u8 *secret, size_t secret_len,
1020 : : u8 *ebuf, size_t *elen)
1021 : : {
1022 : 0 : int i, len, first = 1;
1023 : : u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
1024 : : const u8 *addr[3];
1025 : : size_t _len[3];
1026 : :
1027 : 0 : WPA_PUT_BE16(saltbuf, salt);
1028 : :
1029 : 0 : len = 1 + key_len;
1030 [ # # ]: 0 : if (len & 0x0f) {
1031 : 0 : len = (len & 0xf0) + 16;
1032 : : }
1033 : 0 : os_memset(ebuf, 0, len);
1034 : 0 : ebuf[0] = key_len;
1035 : 0 : os_memcpy(ebuf + 1, key, key_len);
1036 : :
1037 : 0 : *elen = len;
1038 : :
1039 : 0 : pos = ebuf;
1040 [ # # ]: 0 : while (len > 0) {
1041 : : /* b(1) = MD5(Secret + Request-Authenticator + Salt)
1042 : : * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
1043 : 0 : addr[0] = secret;
1044 : 0 : _len[0] = secret_len;
1045 [ # # ]: 0 : if (first) {
1046 : 0 : addr[1] = req_authenticator;
1047 : 0 : _len[1] = MD5_MAC_LEN;
1048 : 0 : addr[2] = saltbuf;
1049 : 0 : _len[2] = sizeof(saltbuf);
1050 : : } else {
1051 : 0 : addr[1] = pos - MD5_MAC_LEN;
1052 : 0 : _len[1] = MD5_MAC_LEN;
1053 : : }
1054 [ # # ]: 0 : md5_vector(first ? 3 : 2, addr, _len, hash);
1055 : 0 : first = 0;
1056 : :
1057 [ # # ]: 0 : for (i = 0; i < MD5_MAC_LEN; i++)
1058 : 0 : *pos++ ^= hash[i];
1059 : :
1060 : 0 : len -= MD5_MAC_LEN;
1061 : : }
1062 : 0 : }
1063 : :
1064 : :
1065 : : struct radius_ms_mppe_keys *
1066 : 105 : radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
1067 : : const u8 *secret, size_t secret_len)
1068 : : {
1069 : : u8 *key;
1070 : : size_t keylen;
1071 : : struct radius_ms_mppe_keys *keys;
1072 : :
1073 [ + - ][ - + ]: 105 : if (msg == NULL || sent_msg == NULL)
1074 : 0 : return NULL;
1075 : :
1076 : 105 : keys = os_zalloc(sizeof(*keys));
1077 [ - + ]: 105 : if (keys == NULL)
1078 : 0 : return NULL;
1079 : :
1080 : 105 : key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
1081 : : RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
1082 : : &keylen);
1083 [ + - ]: 105 : if (key) {
1084 : 105 : keys->send = decrypt_ms_key(key, keylen,
1085 : 105 : sent_msg->hdr->authenticator,
1086 : : secret, secret_len,
1087 : : &keys->send_len);
1088 : 105 : os_free(key);
1089 : : }
1090 : :
1091 : 105 : key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
1092 : : RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
1093 : : &keylen);
1094 [ + - ]: 105 : if (key) {
1095 : 105 : keys->recv = decrypt_ms_key(key, keylen,
1096 : 105 : sent_msg->hdr->authenticator,
1097 : : secret, secret_len,
1098 : : &keys->recv_len);
1099 : 105 : os_free(key);
1100 : : }
1101 : :
1102 : 105 : return keys;
1103 : : }
1104 : :
1105 : :
1106 : : struct radius_ms_mppe_keys *
1107 : 0 : radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
1108 : : const u8 *secret, size_t secret_len)
1109 : : {
1110 : : u8 *key;
1111 : : size_t keylen;
1112 : : struct radius_ms_mppe_keys *keys;
1113 : :
1114 [ # # ][ # # ]: 0 : if (msg == NULL || sent_msg == NULL)
1115 : 0 : return NULL;
1116 : :
1117 : 0 : keys = os_zalloc(sizeof(*keys));
1118 [ # # ]: 0 : if (keys == NULL)
1119 : 0 : return NULL;
1120 : :
1121 : 0 : key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
1122 : : RADIUS_CISCO_AV_PAIR, &keylen);
1123 [ # # ][ # # ]: 0 : if (key && keylen == 51 &&
[ # # ]
1124 : 0 : os_memcmp(key, "leap:session-key=", 17) == 0) {
1125 : 0 : keys->recv = decrypt_ms_key(key + 17, keylen - 17,
1126 : 0 : sent_msg->hdr->authenticator,
1127 : : secret, secret_len,
1128 : : &keys->recv_len);
1129 : : }
1130 : 0 : os_free(key);
1131 : :
1132 : 0 : return keys;
1133 : : }
1134 : :
1135 : :
1136 : 0 : int radius_msg_add_mppe_keys(struct radius_msg *msg,
1137 : : const u8 *req_authenticator,
1138 : : const u8 *secret, size_t secret_len,
1139 : : const u8 *send_key, size_t send_key_len,
1140 : : const u8 *recv_key, size_t recv_key_len)
1141 : : {
1142 : : struct radius_attr_hdr *attr;
1143 : 0 : u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
1144 : : u8 *buf;
1145 : : struct radius_attr_vendor *vhdr;
1146 : : u8 *pos;
1147 : : size_t elen;
1148 : : int hlen;
1149 : : u16 salt;
1150 : :
1151 : 0 : hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
1152 : :
1153 : : /* MS-MPPE-Send-Key */
1154 : 0 : buf = os_malloc(hlen + send_key_len + 16);
1155 [ # # ]: 0 : if (buf == NULL) {
1156 : 0 : return 0;
1157 : : }
1158 : 0 : pos = buf;
1159 : 0 : os_memcpy(pos, &vendor_id, sizeof(vendor_id));
1160 : 0 : pos += sizeof(vendor_id);
1161 : 0 : vhdr = (struct radius_attr_vendor *) pos;
1162 : 0 : vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
1163 : 0 : pos = (u8 *) (vhdr + 1);
1164 : 0 : salt = os_random() | 0x8000;
1165 : 0 : WPA_PUT_BE16(pos, salt);
1166 : 0 : pos += 2;
1167 : 0 : encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
1168 : : secret_len, pos, &elen);
1169 : 0 : vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
1170 : :
1171 : 0 : attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
1172 : : buf, hlen + elen);
1173 : 0 : os_free(buf);
1174 [ # # ]: 0 : if (attr == NULL) {
1175 : 0 : return 0;
1176 : : }
1177 : :
1178 : : /* MS-MPPE-Recv-Key */
1179 : 0 : buf = os_malloc(hlen + send_key_len + 16);
1180 [ # # ]: 0 : if (buf == NULL) {
1181 : 0 : return 0;
1182 : : }
1183 : 0 : pos = buf;
1184 : 0 : os_memcpy(pos, &vendor_id, sizeof(vendor_id));
1185 : 0 : pos += sizeof(vendor_id);
1186 : 0 : vhdr = (struct radius_attr_vendor *) pos;
1187 : 0 : vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
1188 : 0 : pos = (u8 *) (vhdr + 1);
1189 : 0 : salt ^= 1;
1190 : 0 : WPA_PUT_BE16(pos, salt);
1191 : 0 : pos += 2;
1192 : 0 : encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
1193 : : secret_len, pos, &elen);
1194 : 0 : vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
1195 : :
1196 : 0 : attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
1197 : : buf, hlen + elen);
1198 : 0 : os_free(buf);
1199 [ # # ]: 0 : if (attr == NULL) {
1200 : 0 : return 0;
1201 : : }
1202 : :
1203 : 0 : return 1;
1204 : : }
1205 : :
1206 : :
1207 : : /* Add User-Password attribute to a RADIUS message and encrypt it as specified
1208 : : * in RFC 2865, Chap. 5.2 */
1209 : : struct radius_attr_hdr *
1210 : 0 : radius_msg_add_attr_user_password(struct radius_msg *msg,
1211 : : const u8 *data, size_t data_len,
1212 : : const u8 *secret, size_t secret_len)
1213 : : {
1214 : : u8 buf[128];
1215 : : size_t padlen, i, buf_len, pos;
1216 : : const u8 *addr[2];
1217 : : size_t len[2];
1218 : : u8 hash[16];
1219 : :
1220 [ # # ]: 0 : if (data_len > 128)
1221 : 0 : return NULL;
1222 : :
1223 : 0 : os_memcpy(buf, data, data_len);
1224 : 0 : buf_len = data_len;
1225 : :
1226 : 0 : padlen = data_len % 16;
1227 [ # # ][ # # ]: 0 : if (padlen && data_len < sizeof(buf)) {
1228 : 0 : padlen = 16 - padlen;
1229 : 0 : os_memset(buf + data_len, 0, padlen);
1230 : 0 : buf_len += padlen;
1231 : : }
1232 : :
1233 : 0 : addr[0] = secret;
1234 : 0 : len[0] = secret_len;
1235 : 0 : addr[1] = msg->hdr->authenticator;
1236 : 0 : len[1] = 16;
1237 : 0 : md5_vector(2, addr, len, hash);
1238 : :
1239 [ # # ]: 0 : for (i = 0; i < 16; i++)
1240 : 0 : buf[i] ^= hash[i];
1241 : 0 : pos = 16;
1242 : :
1243 [ # # ]: 0 : while (pos < buf_len) {
1244 : 0 : addr[0] = secret;
1245 : 0 : len[0] = secret_len;
1246 : 0 : addr[1] = &buf[pos - 16];
1247 : 0 : len[1] = 16;
1248 : 0 : md5_vector(2, addr, len, hash);
1249 : :
1250 [ # # ]: 0 : for (i = 0; i < 16; i++)
1251 : 0 : buf[pos + i] ^= hash[i];
1252 : :
1253 : 0 : pos += 16;
1254 : : }
1255 : :
1256 : 0 : return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
1257 : : buf, buf_len);
1258 : : }
1259 : :
1260 : :
1261 : 1386 : int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
1262 : : {
1263 : 1386 : struct radius_attr_hdr *attr = NULL, *tmp;
1264 : : size_t i, dlen;
1265 : :
1266 [ + + ]: 6585 : for (i = 0; i < msg->attr_used; i++) {
1267 : 5214 : tmp = radius_get_attr_hdr(msg, i);
1268 [ + + ]: 5214 : if (tmp->type == type) {
1269 : 15 : attr = tmp;
1270 : 15 : break;
1271 : : }
1272 : : }
1273 : :
1274 [ + + ][ - + ]: 1386 : if (!attr || attr->length < sizeof(*attr))
1275 : 1371 : return -1;
1276 : :
1277 : 15 : dlen = attr->length - sizeof(*attr);
1278 [ - + ]: 15 : if (buf)
1279 : 0 : os_memcpy(buf, (attr + 1), dlen > len ? len : dlen);
1280 : 1386 : return dlen;
1281 : : }
1282 : :
1283 : :
1284 : 210 : int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
1285 : : size_t *len, const u8 *start)
1286 : : {
1287 : : size_t i;
1288 : 210 : struct radius_attr_hdr *attr = NULL, *tmp;
1289 : :
1290 [ + + ]: 1050 : for (i = 0; i < msg->attr_used; i++) {
1291 : 840 : tmp = radius_get_attr_hdr(msg, i);
1292 [ - + ][ # # ]: 840 : if (tmp->type == type &&
1293 [ # # ]: 0 : (start == NULL || (u8 *) tmp > start)) {
1294 : 0 : attr = tmp;
1295 : 0 : break;
1296 : : }
1297 : : }
1298 : :
1299 [ - + ][ # # ]: 210 : if (!attr || attr->length < sizeof(*attr))
1300 : 210 : return -1;
1301 : :
1302 : 0 : *buf = (u8 *) (attr + 1);
1303 : 0 : *len = attr->length - sizeof(*attr);
1304 : 210 : return 0;
1305 : : }
1306 : :
1307 : :
1308 : 1 : int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len)
1309 : : {
1310 : : size_t i;
1311 : : int count;
1312 : :
1313 [ + + ]: 5 : for (count = 0, i = 0; i < msg->attr_used; i++) {
1314 : 4 : struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
1315 [ - + ][ # # ]: 4 : if (attr->type == type &&
1316 : 0 : attr->length >= sizeof(struct radius_attr_hdr) + min_len)
1317 : 0 : count++;
1318 : : }
1319 : :
1320 : 1 : return count;
1321 : : }
1322 : :
1323 : :
1324 : : struct radius_tunnel_attrs {
1325 : : int tag_used;
1326 : : int type; /* Tunnel-Type */
1327 : : int medium_type; /* Tunnel-Medium-Type */
1328 : : int vlanid;
1329 : : };
1330 : :
1331 : :
1332 : : /**
1333 : : * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
1334 : : * @msg: RADIUS message
1335 : : * Returns: VLAN ID for the first tunnel configuration of -1 if none is found
1336 : : */
1337 : 0 : int radius_msg_get_vlanid(struct radius_msg *msg)
1338 : : {
1339 : : struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun;
1340 : : size_t i;
1341 : 0 : struct radius_attr_hdr *attr = NULL;
1342 : : const u8 *data;
1343 : : char buf[10];
1344 : : size_t dlen;
1345 : :
1346 : 0 : os_memset(&tunnel, 0, sizeof(tunnel));
1347 : :
1348 [ # # ]: 0 : for (i = 0; i < msg->attr_used; i++) {
1349 : 0 : attr = radius_get_attr_hdr(msg, i);
1350 [ # # ]: 0 : if (attr->length < sizeof(*attr))
1351 : 0 : return -1;
1352 : 0 : data = (const u8 *) (attr + 1);
1353 : 0 : dlen = attr->length - sizeof(*attr);
1354 [ # # ]: 0 : if (attr->length < 3)
1355 : 0 : continue;
1356 [ # # ]: 0 : if (data[0] >= RADIUS_TUNNEL_TAGS)
1357 : 0 : tun = &tunnel[0];
1358 : : else
1359 : 0 : tun = &tunnel[data[0]];
1360 : :
1361 [ # # # # ]: 0 : switch (attr->type) {
1362 : : case RADIUS_ATTR_TUNNEL_TYPE:
1363 [ # # ]: 0 : if (attr->length != 6)
1364 : 0 : break;
1365 : 0 : tun->tag_used++;
1366 : 0 : tun->type = WPA_GET_BE24(data + 1);
1367 : 0 : break;
1368 : : case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
1369 [ # # ]: 0 : if (attr->length != 6)
1370 : 0 : break;
1371 : 0 : tun->tag_used++;
1372 : 0 : tun->medium_type = WPA_GET_BE24(data + 1);
1373 : 0 : break;
1374 : : case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
1375 [ # # ]: 0 : if (data[0] < RADIUS_TUNNEL_TAGS) {
1376 : 0 : data++;
1377 : 0 : dlen--;
1378 : : }
1379 [ # # ]: 0 : if (dlen >= sizeof(buf))
1380 : 0 : break;
1381 : 0 : os_memcpy(buf, data, dlen);
1382 : 0 : buf[dlen] = '\0';
1383 : 0 : tun->tag_used++;
1384 : 0 : tun->vlanid = atoi(buf);
1385 : 0 : break;
1386 : : }
1387 : : }
1388 : :
1389 [ # # ]: 0 : for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) {
1390 : 0 : tun = &tunnel[i];
1391 [ # # ][ # # ]: 0 : if (tun->tag_used &&
1392 [ # # ]: 0 : tun->type == RADIUS_TUNNEL_TYPE_VLAN &&
1393 [ # # ]: 0 : tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 &&
1394 : 0 : tun->vlanid > 0)
1395 : 0 : return tun->vlanid;
1396 : : }
1397 : :
1398 : 0 : return -1;
1399 : : }
1400 : :
1401 : :
1402 : : /**
1403 : : * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password
1404 : : * @msg: Received RADIUS message
1405 : : * @keylen: Length of returned password
1406 : : * @secret: RADIUS shared secret
1407 : : * @secret_len: Length of secret
1408 : : * @sent_msg: Sent RADIUS message
1409 : : * @n: Number of password attribute to return (starting with 0)
1410 : : * Returns: Pointer to n-th password (free with os_free) or %NULL
1411 : : */
1412 : 0 : char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
1413 : : const u8 *secret, size_t secret_len,
1414 : : struct radius_msg *sent_msg, size_t n)
1415 : : {
1416 : 0 : u8 *buf = NULL;
1417 : : size_t buflen;
1418 : : const u8 *salt;
1419 : : u8 *str;
1420 : : const u8 *addr[3];
1421 : : size_t len[3];
1422 : : u8 hash[16];
1423 : : u8 *pos;
1424 : 0 : size_t i, j = 0;
1425 : : struct radius_attr_hdr *attr;
1426 : : const u8 *data;
1427 : : size_t dlen;
1428 : 0 : const u8 *fdata = NULL; /* points to found item */
1429 : 0 : size_t fdlen = -1;
1430 : 0 : char *ret = NULL;
1431 : :
1432 : : /* find n-th valid Tunnel-Password attribute */
1433 [ # # ]: 0 : for (i = 0; i < msg->attr_used; i++) {
1434 : 0 : attr = radius_get_attr_hdr(msg, i);
1435 [ # # ][ # # ]: 0 : if (attr == NULL ||
1436 : 0 : attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) {
1437 : 0 : continue;
1438 : : }
1439 [ # # ]: 0 : if (attr->length <= 5)
1440 : 0 : continue;
1441 : 0 : data = (const u8 *) (attr + 1);
1442 : 0 : dlen = attr->length - sizeof(*attr);
1443 [ # # ][ # # ]: 0 : if (dlen <= 3 || dlen % 16 != 3)
1444 : 0 : continue;
1445 : 0 : j++;
1446 [ # # ]: 0 : if (j <= n)
1447 : 0 : continue;
1448 : :
1449 : 0 : fdata = data;
1450 : 0 : fdlen = dlen;
1451 : 0 : break;
1452 : : }
1453 [ # # ]: 0 : if (fdata == NULL)
1454 : 0 : goto out;
1455 : :
1456 : : /* alloc writable memory for decryption */
1457 : 0 : buf = os_malloc(fdlen);
1458 [ # # ]: 0 : if (buf == NULL)
1459 : 0 : goto out;
1460 : 0 : os_memcpy(buf, fdata, fdlen);
1461 : 0 : buflen = fdlen;
1462 : :
1463 : : /* init pointers */
1464 : 0 : salt = buf + 1;
1465 : 0 : str = buf + 3;
1466 : :
1467 : : /* decrypt blocks */
1468 : 0 : pos = buf + buflen - 16; /* last block */
1469 [ # # ]: 0 : while (pos >= str + 16) { /* all but the first block */
1470 : 0 : addr[0] = secret;
1471 : 0 : len[0] = secret_len;
1472 : 0 : addr[1] = pos - 16;
1473 : 0 : len[1] = 16;
1474 : 0 : md5_vector(2, addr, len, hash);
1475 : :
1476 [ # # ]: 0 : for (i = 0; i < 16; i++)
1477 : 0 : pos[i] ^= hash[i];
1478 : :
1479 : 0 : pos -= 16;
1480 : : }
1481 : :
1482 : : /* decrypt first block */
1483 [ # # ]: 0 : if (str != pos)
1484 : 0 : goto out;
1485 : 0 : addr[0] = secret;
1486 : 0 : len[0] = secret_len;
1487 : 0 : addr[1] = sent_msg->hdr->authenticator;
1488 : 0 : len[1] = 16;
1489 : 0 : addr[2] = salt;
1490 : 0 : len[2] = 2;
1491 : 0 : md5_vector(3, addr, len, hash);
1492 : :
1493 [ # # ]: 0 : for (i = 0; i < 16; i++)
1494 : 0 : pos[i] ^= hash[i];
1495 : :
1496 : : /* derive plaintext length from first subfield */
1497 : 0 : *keylen = (unsigned char) str[0];
1498 [ # # ]: 0 : if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) {
1499 : : /* decryption error - invalid key length */
1500 : 0 : goto out;
1501 : : }
1502 [ # # ]: 0 : if (*keylen == 0) {
1503 : : /* empty password */
1504 : 0 : goto out;
1505 : : }
1506 : :
1507 : : /* copy passphrase into new buffer */
1508 : 0 : ret = os_malloc(*keylen);
1509 [ # # ]: 0 : if (ret)
1510 : 0 : os_memcpy(ret, str + 1, *keylen);
1511 : :
1512 : : out:
1513 : : /* return new buffer */
1514 : 0 : os_free(buf);
1515 : 0 : return ret;
1516 : : }
1517 : :
1518 : :
1519 : 249 : void radius_free_class(struct radius_class_data *c)
1520 : : {
1521 : : size_t i;
1522 [ - + ]: 249 : if (c == NULL)
1523 : 249 : return;
1524 [ - + ]: 249 : for (i = 0; i < c->count; i++)
1525 : 0 : os_free(c->attr[i].data);
1526 : 249 : os_free(c->attr);
1527 : 249 : c->attr = NULL;
1528 : 249 : c->count = 0;
1529 : : }
1530 : :
1531 : :
1532 : 105 : int radius_copy_class(struct radius_class_data *dst,
1533 : : const struct radius_class_data *src)
1534 : : {
1535 : : size_t i;
1536 : :
1537 [ + - ]: 105 : if (src->attr == NULL)
1538 : 105 : return 0;
1539 : :
1540 : 0 : dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data));
1541 [ # # ]: 0 : if (dst->attr == NULL)
1542 : 0 : return -1;
1543 : :
1544 : 0 : dst->count = 0;
1545 : :
1546 [ # # ]: 0 : for (i = 0; i < src->count; i++) {
1547 : 0 : dst->attr[i].data = os_malloc(src->attr[i].len);
1548 [ # # ]: 0 : if (dst->attr[i].data == NULL)
1549 : 0 : break;
1550 : 0 : dst->count++;
1551 : 0 : os_memcpy(dst->attr[i].data, src->attr[i].data,
1552 : : src->attr[i].len);
1553 : 0 : dst->attr[i].len = src->attr[i].len;
1554 : : }
1555 : :
1556 : 105 : return 0;
1557 : : }
1558 : :
1559 : :
1560 : 0 : u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs)
1561 : : {
1562 : : size_t i, j;
1563 : : struct radius_attr_hdr *attr;
1564 : :
1565 [ # # ]: 0 : for (i = 0; i < msg->attr_used; i++) {
1566 : 0 : attr = radius_get_attr_hdr(msg, i);
1567 : :
1568 [ # # ]: 0 : for (j = 0; attrs[j]; j++) {
1569 [ # # ]: 0 : if (attr->type == attrs[j])
1570 : 0 : break;
1571 : : }
1572 : :
1573 [ # # ]: 0 : if (attrs[j] == 0)
1574 : 0 : return attr->type; /* unlisted attr */
1575 : : }
1576 : :
1577 : 0 : return 0;
1578 : : }
|