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