xref: /netbsd/external/bsd/wpa/dist/src/radius/radius.c (revision 6550d01e)
1 /*
2  * RADIUS message processing
3  * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14 
15 #include "utils/includes.h"
16 
17 #include "utils/common.h"
18 #include "utils/wpabuf.h"
19 #include "crypto/md5.h"
20 #include "crypto/crypto.h"
21 #include "radius.h"
22 
23 
24 /**
25  * struct radius_msg - RADIUS message structure for new and parsed messages
26  */
27 struct radius_msg {
28 	/**
29 	 * buf - Allocated buffer for RADIUS message
30 	 */
31 	struct wpabuf *buf;
32 
33 	/**
34 	 * hdr - Pointer to the RADIUS header in buf
35 	 */
36 	struct radius_hdr *hdr;
37 
38 	/**
39 	 * attr_pos - Array of indexes to attributes
40 	 *
41 	 * The values are number of bytes from buf to the beginning of
42 	 * struct radius_attr_hdr.
43 	 */
44 	size_t *attr_pos;
45 
46 	/**
47 	 * attr_size - Total size of the attribute pointer array
48 	 */
49 	size_t attr_size;
50 
51 	/**
52 	 * attr_used - Total number of attributes in the array
53 	 */
54 	size_t attr_used;
55 };
56 
57 
58 struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg)
59 {
60 	return msg->hdr;
61 }
62 
63 
64 struct wpabuf * radius_msg_get_buf(struct radius_msg *msg)
65 {
66 	return msg->buf;
67 }
68 
69 
70 static struct radius_attr_hdr *
71 radius_get_attr_hdr(struct radius_msg *msg, int idx)
72 {
73 	return (struct radius_attr_hdr *)
74 		(wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]);
75 }
76 
77 
78 static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
79 {
80 	msg->hdr->code = code;
81 	msg->hdr->identifier = identifier;
82 }
83 
84 
85 static int radius_msg_initialize(struct radius_msg *msg)
86 {
87 	msg->attr_pos =
88 		os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos));
89 	if (msg->attr_pos == NULL)
90 		return -1;
91 
92 	msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
93 	msg->attr_used = 0;
94 
95 	return 0;
96 }
97 
98 
99 /**
100  * radius_msg_new - Create a new RADIUS message
101  * @code: Code for RADIUS header
102  * @identifier: Identifier for RADIUS header
103  * Returns: Context for RADIUS message or %NULL on failure
104  *
105  * The caller is responsible for freeing the returned data with
106  * radius_msg_free().
107  */
108 struct radius_msg * radius_msg_new(u8 code, u8 identifier)
109 {
110 	struct radius_msg *msg;
111 
112 	msg = os_zalloc(sizeof(*msg));
113 	if (msg == NULL)
114 		return NULL;
115 
116 	msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE);
117 	if (msg->buf == NULL || radius_msg_initialize(msg)) {
118 		radius_msg_free(msg);
119 		return NULL;
120 	}
121 	msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr));
122 
123 	radius_msg_set_hdr(msg, code, identifier);
124 
125 	return msg;
126 }
127 
128 
129 /**
130  * radius_msg_free - Free a RADIUS message
131  * @msg: RADIUS message from radius_msg_new() or radius_msg_parse()
132  */
133 void radius_msg_free(struct radius_msg *msg)
134 {
135 	if (msg == NULL)
136 		return;
137 
138 	wpabuf_free(msg->buf);
139 	os_free(msg->attr_pos);
140 	os_free(msg);
141 }
142 
143 
144 static const char *radius_code_string(u8 code)
145 {
146 	switch (code) {
147 	case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request";
148 	case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept";
149 	case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject";
150 	case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request";
151 	case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response";
152 	case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge";
153 	case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
154 	case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
155 	case RADIUS_CODE_RESERVED: return "Reserved";
156 	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_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
222 	{ RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
223 	{ RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
224 	  RADIUS_ATTR_UNDIST },
225 	{ RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id",
226 	  RADIUS_ATTR_HEXDUMP },
227 	{ RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
228 	  RADIUS_ATTR_INT32 },
229 	{ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
230 	  RADIUS_ATTR_TEXT },
231 	{ RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
232 };
233 #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
234 
235 
236 static struct radius_attr_type *radius_get_attr_type(u8 type)
237 {
238 	size_t i;
239 
240 	for (i = 0; i < RADIUS_ATTRS; i++) {
241 		if (type == radius_attrs[i].type)
242 			return &radius_attrs[i];
243 	}
244 
245 	return NULL;
246 }
247 
248 
249 static void print_char(char c)
250 {
251 	if (c >= 32 && c < 127)
252 		printf("%c", c);
253 	else
254 		printf("<%02x>", c);
255 }
256 
257 
258 static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
259 {
260 	struct radius_attr_type *attr;
261 	int i, len;
262 	unsigned char *pos;
263 
264 	attr = radius_get_attr_type(hdr->type);
265 
266 	printf("   Attribute %d (%s) length=%d\n",
267 	       hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
268 
269 	if (attr == NULL)
270 		return;
271 
272 	len = hdr->length - sizeof(struct radius_attr_hdr);
273 	pos = (unsigned char *) (hdr + 1);
274 
275 	switch (attr->data_type) {
276 	case RADIUS_ATTR_TEXT:
277 		printf("      Value: '");
278 		for (i = 0; i < len; i++)
279 			print_char(pos[i]);
280 		printf("'\n");
281 		break;
282 
283 	case RADIUS_ATTR_IP:
284 		if (len == 4) {
285 			struct in_addr addr;
286 			os_memcpy(&addr, pos, 4);
287 			printf("      Value: %s\n", inet_ntoa(addr));
288 		} else
289 			printf("      Invalid IP address length %d\n", len);
290 		break;
291 
292 #ifdef CONFIG_IPV6
293 	case RADIUS_ATTR_IPV6:
294 		if (len == 16) {
295 			char buf[128];
296 			const char *atxt;
297 			struct in6_addr *addr = (struct in6_addr *) pos;
298 			atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
299 			printf("      Value: %s\n", atxt ? atxt : "?");
300 		} else
301 			printf("      Invalid IPv6 address length %d\n", len);
302 		break;
303 #endif /* CONFIG_IPV6 */
304 
305 	case RADIUS_ATTR_HEXDUMP:
306 	case RADIUS_ATTR_UNDIST:
307 		printf("      Value:");
308 		for (i = 0; i < len; i++)
309 			printf(" %02x", pos[i]);
310 		printf("\n");
311 		break;
312 
313 	case RADIUS_ATTR_INT32:
314 		if (len == 4)
315 			printf("      Value: %u\n", WPA_GET_BE32(pos));
316 		else
317 			printf("      Invalid INT32 length %d\n", len);
318 		break;
319 
320 	default:
321 		break;
322 	}
323 }
324 
325 
326 void radius_msg_dump(struct radius_msg *msg)
327 {
328 	size_t i;
329 
330 	printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
331 	       msg->hdr->code, radius_code_string(msg->hdr->code),
332 	       msg->hdr->identifier, ntohs(msg->hdr->length));
333 
334 	for (i = 0; i < msg->attr_used; i++) {
335 		struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
336 		radius_msg_dump_attr(attr);
337 	}
338 }
339 
340 
341 int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
342 		      size_t secret_len)
343 {
344 	if (secret) {
345 		u8 auth[MD5_MAC_LEN];
346 		struct radius_attr_hdr *attr;
347 
348 		os_memset(auth, 0, MD5_MAC_LEN);
349 		attr = radius_msg_add_attr(msg,
350 					   RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
351 					   auth, MD5_MAC_LEN);
352 		if (attr == NULL) {
353 			wpa_printf(MSG_WARNING, "RADIUS: Could not add "
354 				   "Message-Authenticator");
355 			return -1;
356 		}
357 		msg->hdr->length = htons(wpabuf_len(msg->buf));
358 		hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
359 			 wpabuf_len(msg->buf), (u8 *) (attr + 1));
360 	} else
361 		msg->hdr->length = htons(wpabuf_len(msg->buf));
362 
363 	if (wpabuf_len(msg->buf) > 0xffff) {
364 		wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
365 			   (unsigned long) wpabuf_len(msg->buf));
366 		return -1;
367 	}
368 	return 0;
369 }
370 
371 
372 int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
373 			  size_t secret_len, const u8 *req_authenticator)
374 {
375 	u8 auth[MD5_MAC_LEN];
376 	struct radius_attr_hdr *attr;
377 	const u8 *addr[4];
378 	size_t len[4];
379 
380 	os_memset(auth, 0, MD5_MAC_LEN);
381 	attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
382 				   auth, MD5_MAC_LEN);
383 	if (attr == NULL) {
384 		printf("WARNING: Could not add Message-Authenticator\n");
385 		return -1;
386 	}
387 	msg->hdr->length = htons(wpabuf_len(msg->buf));
388 	os_memcpy(msg->hdr->authenticator, req_authenticator,
389 		  sizeof(msg->hdr->authenticator));
390 	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
391 		 wpabuf_len(msg->buf), (u8 *) (attr + 1));
392 
393 	/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
394 	addr[0] = (u8 *) msg->hdr;
395 	len[0] = 1 + 1 + 2;
396 	addr[1] = req_authenticator;
397 	len[1] = MD5_MAC_LEN;
398 	addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
399 	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
400 	addr[3] = secret;
401 	len[3] = secret_len;
402 	md5_vector(4, addr, len, msg->hdr->authenticator);
403 
404 	if (wpabuf_len(msg->buf) > 0xffff) {
405 		wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
406 			   (unsigned long) wpabuf_len(msg->buf));
407 		return -1;
408 	}
409 	return 0;
410 }
411 
412 
413 void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
414 			    size_t secret_len)
415 {
416 	const u8 *addr[2];
417 	size_t len[2];
418 
419 	msg->hdr->length = htons(wpabuf_len(msg->buf));
420 	os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
421 	addr[0] = wpabuf_head(msg->buf);
422 	len[0] = wpabuf_len(msg->buf);
423 	addr[1] = secret;
424 	len[1] = secret_len;
425 	md5_vector(2, addr, len, msg->hdr->authenticator);
426 
427 	if (wpabuf_len(msg->buf) > 0xffff) {
428 		wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
429 			   (unsigned long) wpabuf_len(msg->buf));
430 	}
431 }
432 
433 
434 static int radius_msg_add_attr_to_array(struct radius_msg *msg,
435 					struct radius_attr_hdr *attr)
436 {
437 	if (msg->attr_used >= msg->attr_size) {
438 		size_t *nattr_pos;
439 		int nlen = msg->attr_size * 2;
440 
441 		nattr_pos = os_realloc(msg->attr_pos,
442 				       nlen * sizeof(*msg->attr_pos));
443 		if (nattr_pos == NULL)
444 			return -1;
445 
446 		msg->attr_pos = nattr_pos;
447 		msg->attr_size = nlen;
448 	}
449 
450 	msg->attr_pos[msg->attr_used++] =
451 		(unsigned char *) attr - wpabuf_head_u8(msg->buf);
452 
453 	return 0;
454 }
455 
456 
457 struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
458 					    const u8 *data, size_t data_len)
459 {
460 	size_t buf_needed;
461 	struct radius_attr_hdr *attr;
462 
463 	if (data_len > RADIUS_MAX_ATTR_LEN) {
464 		printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
465 		       (unsigned long) data_len);
466 		return NULL;
467 	}
468 
469 	buf_needed = sizeof(*attr) + data_len;
470 
471 	if (wpabuf_tailroom(msg->buf) < buf_needed) {
472 		/* allocate more space for message buffer */
473 		if (wpabuf_resize(&msg->buf, buf_needed) < 0)
474 			return NULL;
475 		msg->hdr = wpabuf_mhead(msg->buf);
476 	}
477 
478 	attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
479 	attr->type = type;
480 	attr->length = sizeof(*attr) + data_len;
481 	wpabuf_put_data(msg->buf, data, data_len);
482 
483 	if (radius_msg_add_attr_to_array(msg, attr))
484 		return NULL;
485 
486 	return attr;
487 }
488 
489 
490 /**
491  * radius_msg_parse - Parse a RADIUS message
492  * @data: RADIUS message to be parsed
493  * @len: Length of data buffer in octets
494  * Returns: Parsed RADIUS message or %NULL on failure
495  *
496  * This parses a RADIUS message and makes a copy of its data. The caller is
497  * responsible for freeing the returned data with radius_msg_free().
498  */
499 struct radius_msg * radius_msg_parse(const u8 *data, size_t len)
500 {
501 	struct radius_msg *msg;
502 	struct radius_hdr *hdr;
503 	struct radius_attr_hdr *attr;
504 	size_t msg_len;
505 	unsigned char *pos, *end;
506 
507 	if (data == NULL || len < sizeof(*hdr))
508 		return NULL;
509 
510 	hdr = (struct radius_hdr *) data;
511 
512 	msg_len = ntohs(hdr->length);
513 	if (msg_len < sizeof(*hdr) || msg_len > len) {
514 		wpa_printf(MSG_INFO, "RADIUS: Invalid message length");
515 		return NULL;
516 	}
517 
518 	if (msg_len < len) {
519 		wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after "
520 			   "RADIUS message", (unsigned long) len - msg_len);
521 	}
522 
523 	msg = os_zalloc(sizeof(*msg));
524 	if (msg == NULL)
525 		return NULL;
526 
527 	msg->buf = wpabuf_alloc_copy(data, msg_len);
528 	if (msg->buf == NULL || radius_msg_initialize(msg)) {
529 		radius_msg_free(msg);
530 		return NULL;
531 	}
532 	msg->hdr = wpabuf_mhead(msg->buf);
533 
534 	/* parse attributes */
535 	pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr);
536 	end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf);
537 	while (pos < end) {
538 		if ((size_t) (end - pos) < sizeof(*attr))
539 			goto fail;
540 
541 		attr = (struct radius_attr_hdr *) pos;
542 
543 		if (pos + attr->length > end || attr->length < sizeof(*attr))
544 			goto fail;
545 
546 		/* TODO: check that attr->length is suitable for attr->type */
547 
548 		if (radius_msg_add_attr_to_array(msg, attr))
549 			goto fail;
550 
551 		pos += attr->length;
552 	}
553 
554 	return msg;
555 
556  fail:
557 	radius_msg_free(msg);
558 	return NULL;
559 }
560 
561 
562 int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
563 {
564 	const u8 *pos = data;
565 	size_t left = data_len;
566 
567 	while (left > 0) {
568 		int len;
569 		if (left > RADIUS_MAX_ATTR_LEN)
570 			len = RADIUS_MAX_ATTR_LEN;
571 		else
572 			len = left;
573 
574 		if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
575 					 pos, len))
576 			return 0;
577 
578 		pos += len;
579 		left -= len;
580 	}
581 
582 	return 1;
583 }
584 
585 
586 u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
587 {
588 	u8 *eap, *pos;
589 	size_t len, i;
590 	struct radius_attr_hdr *attr;
591 
592 	if (msg == NULL)
593 		return NULL;
594 
595 	len = 0;
596 	for (i = 0; i < msg->attr_used; i++) {
597 		attr = radius_get_attr_hdr(msg, i);
598 		if (attr->type == RADIUS_ATTR_EAP_MESSAGE)
599 			len += attr->length - sizeof(struct radius_attr_hdr);
600 	}
601 
602 	if (len == 0)
603 		return NULL;
604 
605 	eap = os_malloc(len);
606 	if (eap == NULL)
607 		return NULL;
608 
609 	pos = eap;
610 	for (i = 0; i < msg->attr_used; i++) {
611 		attr = radius_get_attr_hdr(msg, i);
612 		if (attr->type == RADIUS_ATTR_EAP_MESSAGE) {
613 			int flen = attr->length - sizeof(*attr);
614 			os_memcpy(pos, attr + 1, flen);
615 			pos += flen;
616 		}
617 	}
618 
619 	if (eap_len)
620 		*eap_len = len;
621 
622 	return eap;
623 }
624 
625 
626 int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
627 			       size_t secret_len, const u8 *req_auth)
628 {
629 	u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
630 	u8 orig_authenticator[16];
631 	struct radius_attr_hdr *attr = NULL, *tmp;
632 	size_t i;
633 
634 	for (i = 0; i < msg->attr_used; i++) {
635 		tmp = radius_get_attr_hdr(msg, i);
636 		if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
637 			if (attr != NULL) {
638 				printf("Multiple Message-Authenticator "
639 				       "attributes in RADIUS message\n");
640 				return 1;
641 			}
642 			attr = tmp;
643 		}
644 	}
645 
646 	if (attr == NULL) {
647 		printf("No Message-Authenticator attribute found\n");
648 		return 1;
649 	}
650 
651 	os_memcpy(orig, attr + 1, MD5_MAC_LEN);
652 	os_memset(attr + 1, 0, MD5_MAC_LEN);
653 	if (req_auth) {
654 		os_memcpy(orig_authenticator, msg->hdr->authenticator,
655 			  sizeof(orig_authenticator));
656 		os_memcpy(msg->hdr->authenticator, req_auth,
657 			  sizeof(msg->hdr->authenticator));
658 	}
659 	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
660 		 wpabuf_len(msg->buf), auth);
661 	os_memcpy(attr + 1, orig, MD5_MAC_LEN);
662 	if (req_auth) {
663 		os_memcpy(msg->hdr->authenticator, orig_authenticator,
664 			  sizeof(orig_authenticator));
665 	}
666 
667 	if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
668 		printf("Invalid Message-Authenticator!\n");
669 		return 1;
670 	}
671 
672 	return 0;
673 }
674 
675 
676 int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
677 		      size_t secret_len, struct radius_msg *sent_msg, int auth)
678 {
679 	const u8 *addr[4];
680 	size_t len[4];
681 	u8 hash[MD5_MAC_LEN];
682 
683 	if (sent_msg == NULL) {
684 		printf("No matching Access-Request message found\n");
685 		return 1;
686 	}
687 
688 	if (auth &&
689 	    radius_msg_verify_msg_auth(msg, secret, secret_len,
690 				       sent_msg->hdr->authenticator)) {
691 		return 1;
692 	}
693 
694 	/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
695 	addr[0] = (u8 *) msg->hdr;
696 	len[0] = 1 + 1 + 2;
697 	addr[1] = sent_msg->hdr->authenticator;
698 	len[1] = MD5_MAC_LEN;
699 	addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
700 	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
701 	addr[3] = secret;
702 	len[3] = secret_len;
703 	md5_vector(4, addr, len, hash);
704 	if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
705 		printf("Response Authenticator invalid!\n");
706 		return 1;
707 	}
708 
709 	return 0;
710 }
711 
712 
713 int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
714 			 u8 type)
715 {
716 	struct radius_attr_hdr *attr;
717 	size_t i;
718 	int count = 0;
719 
720 	for (i = 0; i < src->attr_used; i++) {
721 		attr = radius_get_attr_hdr(src, i);
722 		if (attr->type == type) {
723 			if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
724 						 attr->length - sizeof(*attr)))
725 				return -1;
726 			count++;
727 		}
728 	}
729 
730 	return count;
731 }
732 
733 
734 /* Create Request Authenticator. The value should be unique over the lifetime
735  * of the shared secret between authenticator and authentication server.
736  * Use one-way MD5 hash calculated from current timestamp and some data given
737  * by the caller. */
738 void radius_msg_make_authenticator(struct radius_msg *msg,
739 				   const u8 *data, size_t len)
740 {
741 	struct os_time tv;
742 	long int l;
743 	const u8 *addr[3];
744 	size_t elen[3];
745 
746 	os_get_time(&tv);
747 	l = os_random();
748 	addr[0] = (u8 *) &tv;
749 	elen[0] = sizeof(tv);
750 	addr[1] = data;
751 	elen[1] = len;
752 	addr[2] = (u8 *) &l;
753 	elen[2] = sizeof(l);
754 	md5_vector(3, addr, elen, msg->hdr->authenticator);
755 }
756 
757 
758 /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
759  * Returns the Attribute payload and sets alen to indicate the length of the
760  * payload if a vendor attribute with subtype is found, otherwise returns NULL.
761  * The returned payload is allocated with os_malloc() and caller must free it
762  * by calling os_free().
763  */
764 static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
765 				      u8 subtype, size_t *alen)
766 {
767 	u8 *data, *pos;
768 	size_t i, len;
769 
770 	if (msg == NULL)
771 		return NULL;
772 
773 	for (i = 0; i < msg->attr_used; i++) {
774 		struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
775 		size_t left;
776 		u32 vendor_id;
777 		struct radius_attr_vendor *vhdr;
778 
779 		if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
780 			continue;
781 
782 		left = attr->length - sizeof(*attr);
783 		if (left < 4)
784 			continue;
785 
786 		pos = (u8 *) (attr + 1);
787 
788 		os_memcpy(&vendor_id, pos, 4);
789 		pos += 4;
790 		left -= 4;
791 
792 		if (ntohl(vendor_id) != vendor)
793 			continue;
794 
795 		while (left >= sizeof(*vhdr)) {
796 			vhdr = (struct radius_attr_vendor *) pos;
797 			if (vhdr->vendor_length > left ||
798 			    vhdr->vendor_length < sizeof(*vhdr)) {
799 				left = 0;
800 				break;
801 			}
802 			if (vhdr->vendor_type != subtype) {
803 				pos += vhdr->vendor_length;
804 				left -= vhdr->vendor_length;
805 				continue;
806 			}
807 
808 			len = vhdr->vendor_length - sizeof(*vhdr);
809 			data = os_malloc(len);
810 			if (data == NULL)
811 				return NULL;
812 			os_memcpy(data, pos + sizeof(*vhdr), len);
813 			if (alen)
814 				*alen = len;
815 			return data;
816 		}
817 	}
818 
819 	return NULL;
820 }
821 
822 
823 static u8 * decrypt_ms_key(const u8 *key, size_t len,
824 			   const u8 *req_authenticator,
825 			   const u8 *secret, size_t secret_len, size_t *reslen)
826 {
827 	u8 *plain, *ppos, *res;
828 	const u8 *pos;
829 	size_t left, plen;
830 	u8 hash[MD5_MAC_LEN];
831 	int i, first = 1;
832 	const u8 *addr[3];
833 	size_t elen[3];
834 
835 	/* key: 16-bit salt followed by encrypted key info */
836 
837 	if (len < 2 + 16)
838 		return NULL;
839 
840 	pos = key + 2;
841 	left = len - 2;
842 	if (left % 16) {
843 		printf("Invalid ms key len %lu\n", (unsigned long) left);
844 		return NULL;
845 	}
846 
847 	plen = left;
848 	ppos = plain = os_malloc(plen);
849 	if (plain == NULL)
850 		return NULL;
851 	plain[0] = 0;
852 
853 	while (left > 0) {
854 		/* b(1) = MD5(Secret + Request-Authenticator + Salt)
855 		 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
856 
857 		addr[0] = secret;
858 		elen[0] = secret_len;
859 		if (first) {
860 			addr[1] = req_authenticator;
861 			elen[1] = MD5_MAC_LEN;
862 			addr[2] = key;
863 			elen[2] = 2; /* Salt */
864 		} else {
865 			addr[1] = pos - MD5_MAC_LEN;
866 			elen[1] = MD5_MAC_LEN;
867 		}
868 		md5_vector(first ? 3 : 2, addr, elen, hash);
869 		first = 0;
870 
871 		for (i = 0; i < MD5_MAC_LEN; i++)
872 			*ppos++ = *pos++ ^ hash[i];
873 		left -= MD5_MAC_LEN;
874 	}
875 
876 	if (plain[0] == 0 || plain[0] > plen - 1) {
877 		printf("Failed to decrypt MPPE key\n");
878 		os_free(plain);
879 		return NULL;
880 	}
881 
882 	res = os_malloc(plain[0]);
883 	if (res == NULL) {
884 		os_free(plain);
885 		return NULL;
886 	}
887 	os_memcpy(res, plain + 1, plain[0]);
888 	if (reslen)
889 		*reslen = plain[0];
890 	os_free(plain);
891 	return res;
892 }
893 
894 
895 static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
896 			   const u8 *req_authenticator,
897 			   const u8 *secret, size_t secret_len,
898 			   u8 *ebuf, size_t *elen)
899 {
900 	int i, len, first = 1;
901 	u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
902 	const u8 *addr[3];
903 	size_t _len[3];
904 
905 	WPA_PUT_BE16(saltbuf, salt);
906 
907 	len = 1 + key_len;
908 	if (len & 0x0f) {
909 		len = (len & 0xf0) + 16;
910 	}
911 	os_memset(ebuf, 0, len);
912 	ebuf[0] = key_len;
913 	os_memcpy(ebuf + 1, key, key_len);
914 
915 	*elen = len;
916 
917 	pos = ebuf;
918 	while (len > 0) {
919 		/* b(1) = MD5(Secret + Request-Authenticator + Salt)
920 		 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
921 		addr[0] = secret;
922 		_len[0] = secret_len;
923 		if (first) {
924 			addr[1] = req_authenticator;
925 			_len[1] = MD5_MAC_LEN;
926 			addr[2] = saltbuf;
927 			_len[2] = sizeof(saltbuf);
928 		} else {
929 			addr[1] = pos - MD5_MAC_LEN;
930 			_len[1] = MD5_MAC_LEN;
931 		}
932 		md5_vector(first ? 3 : 2, addr, _len, hash);
933 		first = 0;
934 
935 		for (i = 0; i < MD5_MAC_LEN; i++)
936 			*pos++ ^= hash[i];
937 
938 		len -= MD5_MAC_LEN;
939 	}
940 }
941 
942 
943 struct radius_ms_mppe_keys *
944 radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
945 		       const u8 *secret, size_t secret_len)
946 {
947 	u8 *key;
948 	size_t keylen;
949 	struct radius_ms_mppe_keys *keys;
950 
951 	if (msg == NULL || sent_msg == NULL)
952 		return NULL;
953 
954 	keys = os_zalloc(sizeof(*keys));
955 	if (keys == NULL)
956 		return NULL;
957 
958 	key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
959 					 RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
960 					 &keylen);
961 	if (key) {
962 		keys->send = decrypt_ms_key(key, keylen,
963 					    sent_msg->hdr->authenticator,
964 					    secret, secret_len,
965 					    &keys->send_len);
966 		os_free(key);
967 	}
968 
969 	key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
970 					 RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
971 					 &keylen);
972 	if (key) {
973 		keys->recv = decrypt_ms_key(key, keylen,
974 					    sent_msg->hdr->authenticator,
975 					    secret, secret_len,
976 					    &keys->recv_len);
977 		os_free(key);
978 	}
979 
980 	return keys;
981 }
982 
983 
984 struct radius_ms_mppe_keys *
985 radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
986 			  const u8 *secret, size_t secret_len)
987 {
988 	u8 *key;
989 	size_t keylen;
990 	struct radius_ms_mppe_keys *keys;
991 
992 	if (msg == NULL || sent_msg == NULL)
993 		return NULL;
994 
995 	keys = os_zalloc(sizeof(*keys));
996 	if (keys == NULL)
997 		return NULL;
998 
999 	key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
1000 					 RADIUS_CISCO_AV_PAIR, &keylen);
1001 	if (key && keylen == 51 &&
1002 	    os_memcmp(key, "leap:session-key=", 17) == 0) {
1003 		keys->recv = decrypt_ms_key(key + 17, keylen - 17,
1004 					    sent_msg->hdr->authenticator,
1005 					    secret, secret_len,
1006 					    &keys->recv_len);
1007 	}
1008 	os_free(key);
1009 
1010 	return keys;
1011 }
1012 
1013 
1014 int radius_msg_add_mppe_keys(struct radius_msg *msg,
1015 			     const u8 *req_authenticator,
1016 			     const u8 *secret, size_t secret_len,
1017 			     const u8 *send_key, size_t send_key_len,
1018 			     const u8 *recv_key, size_t recv_key_len)
1019 {
1020 	struct radius_attr_hdr *attr;
1021 	u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
1022 	u8 *buf;
1023 	struct radius_attr_vendor *vhdr;
1024 	u8 *pos;
1025 	size_t elen;
1026 	int hlen;
1027 	u16 salt;
1028 
1029 	hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
1030 
1031 	/* MS-MPPE-Send-Key */
1032 	buf = os_malloc(hlen + send_key_len + 16);
1033 	if (buf == NULL) {
1034 		return 0;
1035 	}
1036 	pos = buf;
1037 	os_memcpy(pos, &vendor_id, sizeof(vendor_id));
1038 	pos += sizeof(vendor_id);
1039 	vhdr = (struct radius_attr_vendor *) pos;
1040 	vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
1041 	pos = (u8 *) (vhdr + 1);
1042 	salt = os_random() | 0x8000;
1043 	WPA_PUT_BE16(pos, salt);
1044 	pos += 2;
1045 	encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
1046 		       secret_len, pos, &elen);
1047 	vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
1048 
1049 	attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
1050 				   buf, hlen + elen);
1051 	os_free(buf);
1052 	if (attr == NULL) {
1053 		return 0;
1054 	}
1055 
1056 	/* MS-MPPE-Recv-Key */
1057 	buf = os_malloc(hlen + send_key_len + 16);
1058 	if (buf == NULL) {
1059 		return 0;
1060 	}
1061 	pos = buf;
1062 	os_memcpy(pos, &vendor_id, sizeof(vendor_id));
1063 	pos += sizeof(vendor_id);
1064 	vhdr = (struct radius_attr_vendor *) pos;
1065 	vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
1066 	pos = (u8 *) (vhdr + 1);
1067 	salt ^= 1;
1068 	WPA_PUT_BE16(pos, salt);
1069 	pos += 2;
1070 	encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
1071 		       secret_len, pos, &elen);
1072 	vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
1073 
1074 	attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
1075 				   buf, hlen + elen);
1076 	os_free(buf);
1077 	if (attr == NULL) {
1078 		return 0;
1079 	}
1080 
1081 	return 1;
1082 }
1083 
1084 
1085 /* Add User-Password attribute to a RADIUS message and encrypt it as specified
1086  * in RFC 2865, Chap. 5.2 */
1087 struct radius_attr_hdr *
1088 radius_msg_add_attr_user_password(struct radius_msg *msg,
1089 				  const u8 *data, size_t data_len,
1090 				  const u8 *secret, size_t secret_len)
1091 {
1092 	u8 buf[128];
1093 	int padlen, i;
1094 	size_t buf_len, pos;
1095 	const u8 *addr[2];
1096 	size_t len[2];
1097 	u8 hash[16];
1098 
1099 	if (data_len > 128)
1100 		return NULL;
1101 
1102 	os_memcpy(buf, data, data_len);
1103 	buf_len = data_len;
1104 
1105 	padlen = data_len % 16;
1106 	if (padlen) {
1107 		padlen = 16 - padlen;
1108 		os_memset(buf + data_len, 0, padlen);
1109 		buf_len += padlen;
1110 	}
1111 
1112 	addr[0] = secret;
1113 	len[0] = secret_len;
1114 	addr[1] = msg->hdr->authenticator;
1115 	len[1] = 16;
1116 	md5_vector(2, addr, len, hash);
1117 
1118 	for (i = 0; i < 16; i++)
1119 		buf[i] ^= hash[i];
1120 	pos = 16;
1121 
1122 	while (pos < buf_len) {
1123 		addr[0] = secret;
1124 		len[0] = secret_len;
1125 		addr[1] = &buf[pos - 16];
1126 		len[1] = 16;
1127 		md5_vector(2, addr, len, hash);
1128 
1129 		for (i = 0; i < 16; i++)
1130 			buf[pos + i] ^= hash[i];
1131 
1132 		pos += 16;
1133 	}
1134 
1135 	return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
1136 				   buf, buf_len);
1137 }
1138 
1139 
1140 int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
1141 {
1142 	struct radius_attr_hdr *attr = NULL, *tmp;
1143 	size_t i, dlen;
1144 
1145 	for (i = 0; i < msg->attr_used; i++) {
1146 		tmp = radius_get_attr_hdr(msg, i);
1147 		if (tmp->type == type) {
1148 			attr = tmp;
1149 			break;
1150 		}
1151 	}
1152 
1153 	if (!attr)
1154 		return -1;
1155 
1156 	dlen = attr->length - sizeof(*attr);
1157 	if (buf)
1158 		os_memcpy(buf, (attr + 1), dlen > len ? len : dlen);
1159 	return dlen;
1160 }
1161 
1162 
1163 int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
1164 			    size_t *len, const u8 *start)
1165 {
1166 	size_t i;
1167 	struct radius_attr_hdr *attr = NULL, *tmp;
1168 
1169 	for (i = 0; i < msg->attr_used; i++) {
1170 		tmp = radius_get_attr_hdr(msg, i);
1171 		if (tmp->type == type &&
1172 		    (start == NULL || (u8 *) tmp > start)) {
1173 			attr = tmp;
1174 			break;
1175 		}
1176 	}
1177 
1178 	if (!attr)
1179 		return -1;
1180 
1181 	*buf = (u8 *) (attr + 1);
1182 	*len = attr->length - sizeof(*attr);
1183 	return 0;
1184 }
1185 
1186 
1187 int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len)
1188 {
1189 	size_t i;
1190 	int count;
1191 
1192 	for (count = 0, i = 0; i < msg->attr_used; i++) {
1193 		struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
1194 		if (attr->type == type &&
1195 		    attr->length >= sizeof(struct radius_attr_hdr) + min_len)
1196 			count++;
1197 	}
1198 
1199 	return count;
1200 }
1201 
1202 
1203 struct radius_tunnel_attrs {
1204 	int tag_used;
1205 	int type; /* Tunnel-Type */
1206 	int medium_type; /* Tunnel-Medium-Type */
1207 	int vlanid;
1208 };
1209 
1210 
1211 /**
1212  * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
1213  * @msg: RADIUS message
1214  * Returns: VLAN ID for the first tunnel configuration of -1 if none is found
1215  */
1216 int radius_msg_get_vlanid(struct radius_msg *msg)
1217 {
1218 	struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun;
1219 	size_t i;
1220 	struct radius_attr_hdr *attr = NULL;
1221 	const u8 *data;
1222 	char buf[10];
1223 	size_t dlen;
1224 
1225 	os_memset(&tunnel, 0, sizeof(tunnel));
1226 
1227 	for (i = 0; i < msg->attr_used; i++) {
1228 		attr = radius_get_attr_hdr(msg, i);
1229 		data = (const u8 *) (attr + 1);
1230 		dlen = attr->length - sizeof(*attr);
1231 		if (attr->length < 3)
1232 			continue;
1233 		if (data[0] >= RADIUS_TUNNEL_TAGS)
1234 			tun = &tunnel[0];
1235 		else
1236 			tun = &tunnel[data[0]];
1237 
1238 		switch (attr->type) {
1239 		case RADIUS_ATTR_TUNNEL_TYPE:
1240 			if (attr->length != 6)
1241 				break;
1242 			tun->tag_used++;
1243 			tun->type = WPA_GET_BE24(data + 1);
1244 			break;
1245 		case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
1246 			if (attr->length != 6)
1247 				break;
1248 			tun->tag_used++;
1249 			tun->medium_type = WPA_GET_BE24(data + 1);
1250 			break;
1251 		case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
1252 			if (data[0] < RADIUS_TUNNEL_TAGS) {
1253 				data++;
1254 				dlen--;
1255 			}
1256 			if (dlen >= sizeof(buf))
1257 				break;
1258 			os_memcpy(buf, data, dlen);
1259 			buf[dlen] = '\0';
1260 			tun->tag_used++;
1261 			tun->vlanid = atoi(buf);
1262 			break;
1263 		}
1264 	}
1265 
1266 	for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) {
1267 		tun = &tunnel[i];
1268 		if (tun->tag_used &&
1269 		    tun->type == RADIUS_TUNNEL_TYPE_VLAN &&
1270 		    tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 &&
1271 		    tun->vlanid > 0)
1272 			return tun->vlanid;
1273 	}
1274 
1275 	return -1;
1276 }
1277 
1278 
1279 void radius_free_class(struct radius_class_data *c)
1280 {
1281 	size_t i;
1282 	if (c == NULL)
1283 		return;
1284 	for (i = 0; i < c->count; i++)
1285 		os_free(c->attr[i].data);
1286 	os_free(c->attr);
1287 	c->attr = NULL;
1288 	c->count = 0;
1289 }
1290 
1291 
1292 int radius_copy_class(struct radius_class_data *dst,
1293 		      const struct radius_class_data *src)
1294 {
1295 	size_t i;
1296 
1297 	if (src->attr == NULL)
1298 		return 0;
1299 
1300 	dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data));
1301 	if (dst->attr == NULL)
1302 		return -1;
1303 
1304 	dst->count = 0;
1305 
1306 	for (i = 0; i < src->count; i++) {
1307 		dst->attr[i].data = os_malloc(src->attr[i].len);
1308 		if (dst->attr[i].data == NULL)
1309 			break;
1310 		dst->count++;
1311 		os_memcpy(dst->attr[i].data, src->attr[i].data,
1312 			  src->attr[i].len);
1313 		dst->attr[i].len = src->attr[i].len;
1314 	}
1315 
1316 	return 0;
1317 }
1318