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