xref: /dflybsd-src/contrib/wpa_supplicant/src/eap_peer/eap_fast_pac.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
16d49e1aeSJan Lentfer /*
26d49e1aeSJan Lentfer  * EAP peer method: EAP-FAST PAC file processing
36d49e1aeSJan Lentfer  * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
46d49e1aeSJan Lentfer  *
53ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino  * See README for more details.
76d49e1aeSJan Lentfer  */
86d49e1aeSJan Lentfer 
96d49e1aeSJan Lentfer #include "includes.h"
106d49e1aeSJan Lentfer 
116d49e1aeSJan Lentfer #include "common.h"
126d49e1aeSJan Lentfer #include "eap_config.h"
136d49e1aeSJan Lentfer #include "eap_i.h"
146d49e1aeSJan Lentfer #include "eap_fast_pac.h"
156d49e1aeSJan Lentfer 
166d49e1aeSJan Lentfer /* TODO: encrypt PAC-Key in the PAC file */
176d49e1aeSJan Lentfer 
186d49e1aeSJan Lentfer 
196d49e1aeSJan Lentfer /* Text data format */
206d49e1aeSJan Lentfer static const char *pac_file_hdr =
216d49e1aeSJan Lentfer 	"wpa_supplicant EAP-FAST PAC file - version 1";
226d49e1aeSJan Lentfer 
236d49e1aeSJan Lentfer /*
246d49e1aeSJan Lentfer  * Binary data format
256d49e1aeSJan Lentfer  * 4-octet magic value: 6A E4 92 0C
266d49e1aeSJan Lentfer  * 2-octet version (big endian)
276d49e1aeSJan Lentfer  * <version specific data>
286d49e1aeSJan Lentfer  *
296d49e1aeSJan Lentfer  * version=0:
306d49e1aeSJan Lentfer  * Sequence of PAC entries:
316d49e1aeSJan Lentfer  *   2-octet PAC-Type (big endian)
326d49e1aeSJan Lentfer  *   32-octet PAC-Key
336d49e1aeSJan Lentfer  *   2-octet PAC-Opaque length (big endian)
346d49e1aeSJan Lentfer  *   <variable len> PAC-Opaque data (length bytes)
356d49e1aeSJan Lentfer  *   2-octet PAC-Info length (big endian)
366d49e1aeSJan Lentfer  *   <variable len> PAC-Info data (length bytes)
376d49e1aeSJan Lentfer  */
386d49e1aeSJan Lentfer 
396d49e1aeSJan Lentfer #define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c
406d49e1aeSJan Lentfer #define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0
416d49e1aeSJan Lentfer 
426d49e1aeSJan Lentfer 
436d49e1aeSJan Lentfer /**
446d49e1aeSJan Lentfer  * eap_fast_free_pac - Free PAC data
456d49e1aeSJan Lentfer  * @pac: Pointer to the PAC entry
466d49e1aeSJan Lentfer  *
476d49e1aeSJan Lentfer  * Note that the PAC entry must not be in a list since this function does not
486d49e1aeSJan Lentfer  * remove the list links.
496d49e1aeSJan Lentfer  */
eap_fast_free_pac(struct eap_fast_pac * pac)506d49e1aeSJan Lentfer void eap_fast_free_pac(struct eap_fast_pac *pac)
516d49e1aeSJan Lentfer {
526d49e1aeSJan Lentfer 	os_free(pac->pac_opaque);
536d49e1aeSJan Lentfer 	os_free(pac->pac_info);
546d49e1aeSJan Lentfer 	os_free(pac->a_id);
556d49e1aeSJan Lentfer 	os_free(pac->i_id);
566d49e1aeSJan Lentfer 	os_free(pac->a_id_info);
576d49e1aeSJan Lentfer 	os_free(pac);
586d49e1aeSJan Lentfer }
596d49e1aeSJan Lentfer 
606d49e1aeSJan Lentfer 
616d49e1aeSJan Lentfer /**
626d49e1aeSJan Lentfer  * eap_fast_get_pac - Get a PAC entry based on A-ID
636d49e1aeSJan Lentfer  * @pac_root: Pointer to root of the PAC list
646d49e1aeSJan Lentfer  * @a_id: A-ID to search for
656d49e1aeSJan Lentfer  * @a_id_len: Length of A-ID
666d49e1aeSJan Lentfer  * @pac_type: PAC-Type to search for
676d49e1aeSJan Lentfer  * Returns: Pointer to the PAC entry, or %NULL if A-ID not found
686d49e1aeSJan Lentfer  */
eap_fast_get_pac(struct eap_fast_pac * pac_root,const u8 * a_id,size_t a_id_len,u16 pac_type)696d49e1aeSJan Lentfer struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
706d49e1aeSJan Lentfer 				       const u8 *a_id, size_t a_id_len,
716d49e1aeSJan Lentfer 				       u16 pac_type)
726d49e1aeSJan Lentfer {
736d49e1aeSJan Lentfer 	struct eap_fast_pac *pac = pac_root;
746d49e1aeSJan Lentfer 
756d49e1aeSJan Lentfer 	while (pac) {
766d49e1aeSJan Lentfer 		if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
776d49e1aeSJan Lentfer 		    os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
786d49e1aeSJan Lentfer 			return pac;
796d49e1aeSJan Lentfer 		}
806d49e1aeSJan Lentfer 		pac = pac->next;
816d49e1aeSJan Lentfer 	}
826d49e1aeSJan Lentfer 	return NULL;
836d49e1aeSJan Lentfer }
846d49e1aeSJan Lentfer 
856d49e1aeSJan Lentfer 
eap_fast_remove_pac(struct eap_fast_pac ** pac_root,struct eap_fast_pac ** pac_current,const u8 * a_id,size_t a_id_len,u16 pac_type)866d49e1aeSJan Lentfer static void eap_fast_remove_pac(struct eap_fast_pac **pac_root,
876d49e1aeSJan Lentfer 				struct eap_fast_pac **pac_current,
886d49e1aeSJan Lentfer 				const u8 *a_id, size_t a_id_len, u16 pac_type)
896d49e1aeSJan Lentfer {
906d49e1aeSJan Lentfer 	struct eap_fast_pac *pac, *prev;
916d49e1aeSJan Lentfer 
926d49e1aeSJan Lentfer 	pac = *pac_root;
936d49e1aeSJan Lentfer 	prev = NULL;
946d49e1aeSJan Lentfer 
956d49e1aeSJan Lentfer 	while (pac) {
966d49e1aeSJan Lentfer 		if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
976d49e1aeSJan Lentfer 		    os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
986d49e1aeSJan Lentfer 			if (prev == NULL)
996d49e1aeSJan Lentfer 				*pac_root = pac->next;
1006d49e1aeSJan Lentfer 			else
1016d49e1aeSJan Lentfer 				prev->next = pac->next;
1026d49e1aeSJan Lentfer 			if (*pac_current == pac)
1036d49e1aeSJan Lentfer 				*pac_current = NULL;
1046d49e1aeSJan Lentfer 			eap_fast_free_pac(pac);
1056d49e1aeSJan Lentfer 			break;
1066d49e1aeSJan Lentfer 		}
1076d49e1aeSJan Lentfer 		prev = pac;
1086d49e1aeSJan Lentfer 		pac = pac->next;
1096d49e1aeSJan Lentfer 	}
1106d49e1aeSJan Lentfer }
1116d49e1aeSJan Lentfer 
1126d49e1aeSJan Lentfer 
eap_fast_copy_buf(u8 ** dst,size_t * dst_len,const u8 * src,size_t src_len)1136d49e1aeSJan Lentfer static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
1146d49e1aeSJan Lentfer 			     const u8 *src, size_t src_len)
1156d49e1aeSJan Lentfer {
1166d49e1aeSJan Lentfer 	if (src) {
117*a1157835SDaniel Fojt 		*dst = os_memdup(src, src_len);
1186d49e1aeSJan Lentfer 		if (*dst == NULL)
1196d49e1aeSJan Lentfer 			return -1;
1206d49e1aeSJan Lentfer 		*dst_len = src_len;
1216d49e1aeSJan Lentfer 	}
1226d49e1aeSJan Lentfer 	return 0;
1236d49e1aeSJan Lentfer }
1246d49e1aeSJan Lentfer 
1256d49e1aeSJan Lentfer 
1266d49e1aeSJan Lentfer /**
1276d49e1aeSJan Lentfer  * eap_fast_add_pac - Add a copy of a PAC entry to a list
1286d49e1aeSJan Lentfer  * @pac_root: Pointer to PAC list root pointer
1296d49e1aeSJan Lentfer  * @pac_current: Pointer to the current PAC pointer
1306d49e1aeSJan Lentfer  * @entry: New entry to clone and add to the list
1316d49e1aeSJan Lentfer  * Returns: 0 on success, -1 on failure
1326d49e1aeSJan Lentfer  *
1336d49e1aeSJan Lentfer  * This function makes a clone of the given PAC entry and adds this copied
1346d49e1aeSJan Lentfer  * entry to the list (pac_root). If an old entry for the same A-ID is found,
1356d49e1aeSJan Lentfer  * it will be removed from the PAC list and in this case, pac_current entry
1366d49e1aeSJan Lentfer  * is set to %NULL if it was the removed entry.
1376d49e1aeSJan Lentfer  */
eap_fast_add_pac(struct eap_fast_pac ** pac_root,struct eap_fast_pac ** pac_current,struct eap_fast_pac * entry)1386d49e1aeSJan Lentfer int eap_fast_add_pac(struct eap_fast_pac **pac_root,
1396d49e1aeSJan Lentfer 		     struct eap_fast_pac **pac_current,
1406d49e1aeSJan Lentfer 		     struct eap_fast_pac *entry)
1416d49e1aeSJan Lentfer {
1426d49e1aeSJan Lentfer 	struct eap_fast_pac *pac;
1436d49e1aeSJan Lentfer 
1446d49e1aeSJan Lentfer 	if (entry == NULL || entry->a_id == NULL)
1456d49e1aeSJan Lentfer 		return -1;
1466d49e1aeSJan Lentfer 
1476d49e1aeSJan Lentfer 	/* Remove a possible old entry for the matching A-ID. */
1486d49e1aeSJan Lentfer 	eap_fast_remove_pac(pac_root, pac_current,
1496d49e1aeSJan Lentfer 			    entry->a_id, entry->a_id_len, entry->pac_type);
1506d49e1aeSJan Lentfer 
1516d49e1aeSJan Lentfer 	/* Allocate a new entry and add it to the list of PACs. */
1526d49e1aeSJan Lentfer 	pac = os_zalloc(sizeof(*pac));
1536d49e1aeSJan Lentfer 	if (pac == NULL)
1546d49e1aeSJan Lentfer 		return -1;
1556d49e1aeSJan Lentfer 
1566d49e1aeSJan Lentfer 	pac->pac_type = entry->pac_type;
1576d49e1aeSJan Lentfer 	os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
1586d49e1aeSJan Lentfer 	if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
1596d49e1aeSJan Lentfer 			      entry->pac_opaque, entry->pac_opaque_len) < 0 ||
1606d49e1aeSJan Lentfer 	    eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
1616d49e1aeSJan Lentfer 			      entry->pac_info, entry->pac_info_len) < 0 ||
1626d49e1aeSJan Lentfer 	    eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
1636d49e1aeSJan Lentfer 			      entry->a_id, entry->a_id_len) < 0 ||
1646d49e1aeSJan Lentfer 	    eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
1656d49e1aeSJan Lentfer 			      entry->i_id, entry->i_id_len) < 0 ||
1666d49e1aeSJan Lentfer 	    eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
1676d49e1aeSJan Lentfer 			      entry->a_id_info, entry->a_id_info_len) < 0) {
1686d49e1aeSJan Lentfer 		eap_fast_free_pac(pac);
1696d49e1aeSJan Lentfer 		return -1;
1706d49e1aeSJan Lentfer 	}
1716d49e1aeSJan Lentfer 
1726d49e1aeSJan Lentfer 	pac->next = *pac_root;
1736d49e1aeSJan Lentfer 	*pac_root = pac;
1746d49e1aeSJan Lentfer 
1756d49e1aeSJan Lentfer 	return 0;
1766d49e1aeSJan Lentfer }
1776d49e1aeSJan Lentfer 
1786d49e1aeSJan Lentfer 
1796d49e1aeSJan Lentfer struct eap_fast_read_ctx {
1806d49e1aeSJan Lentfer 	FILE *f;
1816d49e1aeSJan Lentfer 	const char *pos;
1826d49e1aeSJan Lentfer 	const char *end;
1836d49e1aeSJan Lentfer 	int line;
1846d49e1aeSJan Lentfer 	char *buf;
1856d49e1aeSJan Lentfer 	size_t buf_len;
1866d49e1aeSJan Lentfer };
1876d49e1aeSJan Lentfer 
eap_fast_read_line(struct eap_fast_read_ctx * rc,char ** value)1886d49e1aeSJan Lentfer static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
1896d49e1aeSJan Lentfer {
1906d49e1aeSJan Lentfer 	char *pos;
1916d49e1aeSJan Lentfer 
1926d49e1aeSJan Lentfer 	rc->line++;
1936d49e1aeSJan Lentfer 	if (rc->f) {
1946d49e1aeSJan Lentfer 		if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
1956d49e1aeSJan Lentfer 			return -1;
1966d49e1aeSJan Lentfer 	} else {
1976d49e1aeSJan Lentfer 		const char *l_end;
1986d49e1aeSJan Lentfer 		size_t len;
1996d49e1aeSJan Lentfer 		if (rc->pos >= rc->end)
2006d49e1aeSJan Lentfer 			return -1;
2016d49e1aeSJan Lentfer 		l_end = rc->pos;
2026d49e1aeSJan Lentfer 		while (l_end < rc->end && *l_end != '\n')
2036d49e1aeSJan Lentfer 			l_end++;
2046d49e1aeSJan Lentfer 		len = l_end - rc->pos;
2056d49e1aeSJan Lentfer 		if (len >= rc->buf_len)
2066d49e1aeSJan Lentfer 			len = rc->buf_len - 1;
2076d49e1aeSJan Lentfer 		os_memcpy(rc->buf, rc->pos, len);
2086d49e1aeSJan Lentfer 		rc->buf[len] = '\0';
2096d49e1aeSJan Lentfer 		rc->pos = l_end + 1;
2106d49e1aeSJan Lentfer 	}
2116d49e1aeSJan Lentfer 
2126d49e1aeSJan Lentfer 	rc->buf[rc->buf_len - 1] = '\0';
2136d49e1aeSJan Lentfer 	pos = rc->buf;
2146d49e1aeSJan Lentfer 	while (*pos != '\0') {
2156d49e1aeSJan Lentfer 		if (*pos == '\n' || *pos == '\r') {
2166d49e1aeSJan Lentfer 			*pos = '\0';
2176d49e1aeSJan Lentfer 			break;
2186d49e1aeSJan Lentfer 		}
2196d49e1aeSJan Lentfer 		pos++;
2206d49e1aeSJan Lentfer 	}
2216d49e1aeSJan Lentfer 
2226d49e1aeSJan Lentfer 	pos = os_strchr(rc->buf, '=');
2236d49e1aeSJan Lentfer 	if (pos)
2246d49e1aeSJan Lentfer 		*pos++ = '\0';
2256d49e1aeSJan Lentfer 	*value = pos;
2266d49e1aeSJan Lentfer 
2276d49e1aeSJan Lentfer 	return 0;
2286d49e1aeSJan Lentfer }
2296d49e1aeSJan Lentfer 
2306d49e1aeSJan Lentfer 
eap_fast_parse_hex(const char * value,size_t * len)2316d49e1aeSJan Lentfer static u8 * eap_fast_parse_hex(const char *value, size_t *len)
2326d49e1aeSJan Lentfer {
2336d49e1aeSJan Lentfer 	int hlen;
2346d49e1aeSJan Lentfer 	u8 *buf;
2356d49e1aeSJan Lentfer 
2366d49e1aeSJan Lentfer 	if (value == NULL)
2376d49e1aeSJan Lentfer 		return NULL;
2386d49e1aeSJan Lentfer 	hlen = os_strlen(value);
2396d49e1aeSJan Lentfer 	if (hlen & 1)
2406d49e1aeSJan Lentfer 		return NULL;
2416d49e1aeSJan Lentfer 	*len = hlen / 2;
2426d49e1aeSJan Lentfer 	buf = os_malloc(*len);
2436d49e1aeSJan Lentfer 	if (buf == NULL)
2446d49e1aeSJan Lentfer 		return NULL;
2456d49e1aeSJan Lentfer 	if (hexstr2bin(value, buf, *len)) {
2466d49e1aeSJan Lentfer 		os_free(buf);
2476d49e1aeSJan Lentfer 		return NULL;
2486d49e1aeSJan Lentfer 	}
2496d49e1aeSJan Lentfer 	return buf;
2506d49e1aeSJan Lentfer }
2516d49e1aeSJan Lentfer 
2526d49e1aeSJan Lentfer 
eap_fast_init_pac_data(struct eap_sm * sm,const char * pac_file,struct eap_fast_read_ctx * rc)2536d49e1aeSJan Lentfer static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
2546d49e1aeSJan Lentfer 				  struct eap_fast_read_ctx *rc)
2556d49e1aeSJan Lentfer {
2566d49e1aeSJan Lentfer 	os_memset(rc, 0, sizeof(*rc));
2576d49e1aeSJan Lentfer 
2586d49e1aeSJan Lentfer 	rc->buf_len = 2048;
2596d49e1aeSJan Lentfer 	rc->buf = os_malloc(rc->buf_len);
2606d49e1aeSJan Lentfer 	if (rc->buf == NULL)
2616d49e1aeSJan Lentfer 		return -1;
2626d49e1aeSJan Lentfer 
2636d49e1aeSJan Lentfer 	if (os_strncmp(pac_file, "blob://", 7) == 0) {
2646d49e1aeSJan Lentfer 		const struct wpa_config_blob *blob;
2656d49e1aeSJan Lentfer 		blob = eap_get_config_blob(sm, pac_file + 7);
2666d49e1aeSJan Lentfer 		if (blob == NULL) {
2676d49e1aeSJan Lentfer 			wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
2686d49e1aeSJan Lentfer 				   "assume no PAC entries have been "
2696d49e1aeSJan Lentfer 				   "provisioned", pac_file + 7);
2706d49e1aeSJan Lentfer 			os_free(rc->buf);
2716d49e1aeSJan Lentfer 			return -1;
2726d49e1aeSJan Lentfer 		}
2736d49e1aeSJan Lentfer 		rc->pos = (char *) blob->data;
2746d49e1aeSJan Lentfer 		rc->end = (char *) blob->data + blob->len;
2756d49e1aeSJan Lentfer 	} else {
2766d49e1aeSJan Lentfer 		rc->f = fopen(pac_file, "rb");
2776d49e1aeSJan Lentfer 		if (rc->f == NULL) {
2786d49e1aeSJan Lentfer 			wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
2796d49e1aeSJan Lentfer 				   "assume no PAC entries have been "
2806d49e1aeSJan Lentfer 				   "provisioned", pac_file);
2816d49e1aeSJan Lentfer 			os_free(rc->buf);
2826d49e1aeSJan Lentfer 			return -1;
2836d49e1aeSJan Lentfer 		}
2846d49e1aeSJan Lentfer 	}
2856d49e1aeSJan Lentfer 
2866d49e1aeSJan Lentfer 	return 0;
2876d49e1aeSJan Lentfer }
2886d49e1aeSJan Lentfer 
2896d49e1aeSJan Lentfer 
eap_fast_deinit_pac_data(struct eap_fast_read_ctx * rc)2906d49e1aeSJan Lentfer static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
2916d49e1aeSJan Lentfer {
2926d49e1aeSJan Lentfer 	os_free(rc->buf);
2936d49e1aeSJan Lentfer 	if (rc->f)
2946d49e1aeSJan Lentfer 		fclose(rc->f);
2956d49e1aeSJan Lentfer }
2966d49e1aeSJan Lentfer 
2976d49e1aeSJan Lentfer 
eap_fast_parse_start(struct eap_fast_pac ** pac)2986d49e1aeSJan Lentfer static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
2996d49e1aeSJan Lentfer {
3006d49e1aeSJan Lentfer 	if (*pac)
3016d49e1aeSJan Lentfer 		return "START line without END";
3026d49e1aeSJan Lentfer 
3036d49e1aeSJan Lentfer 	*pac = os_zalloc(sizeof(struct eap_fast_pac));
3046d49e1aeSJan Lentfer 	if (*pac == NULL)
3056d49e1aeSJan Lentfer 		return "No memory for PAC entry";
3066d49e1aeSJan Lentfer 	(*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
3076d49e1aeSJan Lentfer 	return NULL;
3086d49e1aeSJan Lentfer }
3096d49e1aeSJan Lentfer 
3106d49e1aeSJan Lentfer 
eap_fast_parse_end(struct eap_fast_pac ** pac_root,struct eap_fast_pac ** pac)3116d49e1aeSJan Lentfer static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
3126d49e1aeSJan Lentfer 				       struct eap_fast_pac **pac)
3136d49e1aeSJan Lentfer {
3146d49e1aeSJan Lentfer 	if (*pac == NULL)
3156d49e1aeSJan Lentfer 		return "END line without START";
3166d49e1aeSJan Lentfer 	if (*pac_root) {
3176d49e1aeSJan Lentfer 		struct eap_fast_pac *end = *pac_root;
3186d49e1aeSJan Lentfer 		while (end->next)
3196d49e1aeSJan Lentfer 			end = end->next;
3206d49e1aeSJan Lentfer 		end->next = *pac;
3216d49e1aeSJan Lentfer 	} else
3226d49e1aeSJan Lentfer 		*pac_root = *pac;
3236d49e1aeSJan Lentfer 
3246d49e1aeSJan Lentfer 	*pac = NULL;
3256d49e1aeSJan Lentfer 	return NULL;
3266d49e1aeSJan Lentfer }
3276d49e1aeSJan Lentfer 
3286d49e1aeSJan Lentfer 
eap_fast_parse_pac_type(struct eap_fast_pac * pac,char * pos)3296d49e1aeSJan Lentfer static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
3306d49e1aeSJan Lentfer 					    char *pos)
3316d49e1aeSJan Lentfer {
332*a1157835SDaniel Fojt 	if (!pos)
333*a1157835SDaniel Fojt 		return "Cannot parse pac type";
3346d49e1aeSJan Lentfer 	pac->pac_type = atoi(pos);
3356d49e1aeSJan Lentfer 	if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
3366d49e1aeSJan Lentfer 	    pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
3376d49e1aeSJan Lentfer 	    pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION)
3386d49e1aeSJan Lentfer 		return "Unrecognized PAC-Type";
3396d49e1aeSJan Lentfer 
3406d49e1aeSJan Lentfer 	return NULL;
3416d49e1aeSJan Lentfer }
3426d49e1aeSJan Lentfer 
3436d49e1aeSJan Lentfer 
eap_fast_parse_pac_key(struct eap_fast_pac * pac,char * pos)3446d49e1aeSJan Lentfer static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos)
3456d49e1aeSJan Lentfer {
3466d49e1aeSJan Lentfer 	u8 *key;
3476d49e1aeSJan Lentfer 	size_t key_len;
3486d49e1aeSJan Lentfer 
3496d49e1aeSJan Lentfer 	key = eap_fast_parse_hex(pos, &key_len);
3506d49e1aeSJan Lentfer 	if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
3516d49e1aeSJan Lentfer 		os_free(key);
3526d49e1aeSJan Lentfer 		return "Invalid PAC-Key";
3536d49e1aeSJan Lentfer 	}
3546d49e1aeSJan Lentfer 
3556d49e1aeSJan Lentfer 	os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
3566d49e1aeSJan Lentfer 	os_free(key);
3576d49e1aeSJan Lentfer 
3586d49e1aeSJan Lentfer 	return NULL;
3596d49e1aeSJan Lentfer }
3606d49e1aeSJan Lentfer 
3616d49e1aeSJan Lentfer 
eap_fast_parse_pac_opaque(struct eap_fast_pac * pac,char * pos)3626d49e1aeSJan Lentfer static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
3636d49e1aeSJan Lentfer 					      char *pos)
3646d49e1aeSJan Lentfer {
3656d49e1aeSJan Lentfer 	os_free(pac->pac_opaque);
3666d49e1aeSJan Lentfer 	pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
3676d49e1aeSJan Lentfer 	if (pac->pac_opaque == NULL)
3686d49e1aeSJan Lentfer 		return "Invalid PAC-Opaque";
3696d49e1aeSJan Lentfer 	return NULL;
3706d49e1aeSJan Lentfer }
3716d49e1aeSJan Lentfer 
3726d49e1aeSJan Lentfer 
eap_fast_parse_a_id(struct eap_fast_pac * pac,char * pos)3736d49e1aeSJan Lentfer static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
3746d49e1aeSJan Lentfer {
3756d49e1aeSJan Lentfer 	os_free(pac->a_id);
3766d49e1aeSJan Lentfer 	pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
3776d49e1aeSJan Lentfer 	if (pac->a_id == NULL)
3786d49e1aeSJan Lentfer 		return "Invalid A-ID";
3796d49e1aeSJan Lentfer 	return NULL;
3806d49e1aeSJan Lentfer }
3816d49e1aeSJan Lentfer 
3826d49e1aeSJan Lentfer 
eap_fast_parse_i_id(struct eap_fast_pac * pac,char * pos)3836d49e1aeSJan Lentfer static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
3846d49e1aeSJan Lentfer {
3856d49e1aeSJan Lentfer 	os_free(pac->i_id);
3866d49e1aeSJan Lentfer 	pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
3876d49e1aeSJan Lentfer 	if (pac->i_id == NULL)
3886d49e1aeSJan Lentfer 		return "Invalid I-ID";
3896d49e1aeSJan Lentfer 	return NULL;
3906d49e1aeSJan Lentfer }
3916d49e1aeSJan Lentfer 
3926d49e1aeSJan Lentfer 
eap_fast_parse_a_id_info(struct eap_fast_pac * pac,char * pos)3936d49e1aeSJan Lentfer static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
3946d49e1aeSJan Lentfer 					     char *pos)
3956d49e1aeSJan Lentfer {
3966d49e1aeSJan Lentfer 	os_free(pac->a_id_info);
3976d49e1aeSJan Lentfer 	pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
3986d49e1aeSJan Lentfer 	if (pac->a_id_info == NULL)
3996d49e1aeSJan Lentfer 		return "Invalid A-ID-Info";
4006d49e1aeSJan Lentfer 	return NULL;
4016d49e1aeSJan Lentfer }
4026d49e1aeSJan Lentfer 
4036d49e1aeSJan Lentfer 
4046d49e1aeSJan Lentfer /**
4056d49e1aeSJan Lentfer  * eap_fast_load_pac - Load PAC entries (text format)
4066d49e1aeSJan Lentfer  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
4076d49e1aeSJan Lentfer  * @pac_root: Pointer to root of the PAC list (to be filled)
4086d49e1aeSJan Lentfer  * @pac_file: Name of the PAC file/blob to load
4096d49e1aeSJan Lentfer  * Returns: 0 on success, -1 on failure
4106d49e1aeSJan Lentfer  */
eap_fast_load_pac(struct eap_sm * sm,struct eap_fast_pac ** pac_root,const char * pac_file)4116d49e1aeSJan Lentfer int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
4126d49e1aeSJan Lentfer 		      const char *pac_file)
4136d49e1aeSJan Lentfer {
4146d49e1aeSJan Lentfer 	struct eap_fast_read_ctx rc;
4156d49e1aeSJan Lentfer 	struct eap_fast_pac *pac = NULL;
4166d49e1aeSJan Lentfer 	int count = 0;
4176d49e1aeSJan Lentfer 	char *pos;
4186d49e1aeSJan Lentfer 	const char *err = NULL;
4196d49e1aeSJan Lentfer 
4206d49e1aeSJan Lentfer 	if (pac_file == NULL)
4216d49e1aeSJan Lentfer 		return -1;
4226d49e1aeSJan Lentfer 
4236d49e1aeSJan Lentfer 	if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
4246d49e1aeSJan Lentfer 		return 0;
4256d49e1aeSJan Lentfer 
4263ff40c12SJohn Marino 	if (eap_fast_read_line(&rc, &pos) < 0) {
4273ff40c12SJohn Marino 		/* empty file - assume it is fine to overwrite */
4283ff40c12SJohn Marino 		eap_fast_deinit_pac_data(&rc);
4293ff40c12SJohn Marino 		return 0;
4303ff40c12SJohn Marino 	}
4313ff40c12SJohn Marino 	if (os_strcmp(pac_file_hdr, rc.buf) != 0)
4326d49e1aeSJan Lentfer 		err = "Unrecognized header line";
4336d49e1aeSJan Lentfer 
4346d49e1aeSJan Lentfer 	while (!err && eap_fast_read_line(&rc, &pos) == 0) {
4356d49e1aeSJan Lentfer 		if (os_strcmp(rc.buf, "START") == 0)
4366d49e1aeSJan Lentfer 			err = eap_fast_parse_start(&pac);
4376d49e1aeSJan Lentfer 		else if (os_strcmp(rc.buf, "END") == 0) {
4386d49e1aeSJan Lentfer 			err = eap_fast_parse_end(pac_root, &pac);
4396d49e1aeSJan Lentfer 			count++;
4406d49e1aeSJan Lentfer 		} else if (!pac)
4416d49e1aeSJan Lentfer 			err = "Unexpected line outside START/END block";
4426d49e1aeSJan Lentfer 		else if (os_strcmp(rc.buf, "PAC-Type") == 0)
4436d49e1aeSJan Lentfer 			err = eap_fast_parse_pac_type(pac, pos);
4446d49e1aeSJan Lentfer 		else if (os_strcmp(rc.buf, "PAC-Key") == 0)
4456d49e1aeSJan Lentfer 			err = eap_fast_parse_pac_key(pac, pos);
4466d49e1aeSJan Lentfer 		else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
4476d49e1aeSJan Lentfer 			err = eap_fast_parse_pac_opaque(pac, pos);
4486d49e1aeSJan Lentfer 		else if (os_strcmp(rc.buf, "A-ID") == 0)
4496d49e1aeSJan Lentfer 			err = eap_fast_parse_a_id(pac, pos);
4506d49e1aeSJan Lentfer 		else if (os_strcmp(rc.buf, "I-ID") == 0)
4516d49e1aeSJan Lentfer 			err = eap_fast_parse_i_id(pac, pos);
4526d49e1aeSJan Lentfer 		else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
4536d49e1aeSJan Lentfer 			err = eap_fast_parse_a_id_info(pac, pos);
4546d49e1aeSJan Lentfer 	}
4556d49e1aeSJan Lentfer 
4566d49e1aeSJan Lentfer 	if (pac) {
457*a1157835SDaniel Fojt 		if (!err)
4586d49e1aeSJan Lentfer 			err = "PAC block not terminated with END";
4596d49e1aeSJan Lentfer 		eap_fast_free_pac(pac);
4606d49e1aeSJan Lentfer 	}
4616d49e1aeSJan Lentfer 
4626d49e1aeSJan Lentfer 	eap_fast_deinit_pac_data(&rc);
4636d49e1aeSJan Lentfer 
4646d49e1aeSJan Lentfer 	if (err) {
4656d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'",
4666d49e1aeSJan Lentfer 			   err, pac_file, rc.line);
4676d49e1aeSJan Lentfer 		return -1;
4686d49e1aeSJan Lentfer 	}
4696d49e1aeSJan Lentfer 
4706d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
4716d49e1aeSJan Lentfer 		   count, pac_file);
4726d49e1aeSJan Lentfer 
4736d49e1aeSJan Lentfer 	return 0;
4746d49e1aeSJan Lentfer }
4756d49e1aeSJan Lentfer 
4766d49e1aeSJan Lentfer 
eap_fast_write(char ** buf,char ** pos,size_t * buf_len,const char * field,const u8 * data,size_t len,int txt)4776d49e1aeSJan Lentfer static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
4786d49e1aeSJan Lentfer 			   const char *field, const u8 *data,
4796d49e1aeSJan Lentfer 			   size_t len, int txt)
4806d49e1aeSJan Lentfer {
4816d49e1aeSJan Lentfer 	size_t i, need;
4826d49e1aeSJan Lentfer 	int ret;
4833ff40c12SJohn Marino 	char *end;
4846d49e1aeSJan Lentfer 
4853ff40c12SJohn Marino 	if (data == NULL || buf == NULL || *buf == NULL ||
4863ff40c12SJohn Marino 	    pos == NULL || *pos == NULL || *pos < *buf)
4876d49e1aeSJan Lentfer 		return;
4886d49e1aeSJan Lentfer 
4896d49e1aeSJan Lentfer 	need = os_strlen(field) + len * 2 + 30;
4906d49e1aeSJan Lentfer 	if (txt)
4916d49e1aeSJan Lentfer 		need += os_strlen(field) + len + 20;
4926d49e1aeSJan Lentfer 
4936d49e1aeSJan Lentfer 	if (*pos - *buf + need > *buf_len) {
4946d49e1aeSJan Lentfer 		char *nbuf = os_realloc(*buf, *buf_len + need);
4956d49e1aeSJan Lentfer 		if (nbuf == NULL) {
4966d49e1aeSJan Lentfer 			os_free(*buf);
4976d49e1aeSJan Lentfer 			*buf = NULL;
4986d49e1aeSJan Lentfer 			return;
4996d49e1aeSJan Lentfer 		}
5003ff40c12SJohn Marino 		*pos = nbuf + (*pos - *buf);
5016d49e1aeSJan Lentfer 		*buf = nbuf;
5026d49e1aeSJan Lentfer 		*buf_len += need;
5036d49e1aeSJan Lentfer 	}
5043ff40c12SJohn Marino 	end = *buf + *buf_len;
5056d49e1aeSJan Lentfer 
5063ff40c12SJohn Marino 	ret = os_snprintf(*pos, end - *pos, "%s=", field);
507*a1157835SDaniel Fojt 	if (os_snprintf_error(end - *pos, ret))
5086d49e1aeSJan Lentfer 		return;
5096d49e1aeSJan Lentfer 	*pos += ret;
5103ff40c12SJohn Marino 	*pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
5113ff40c12SJohn Marino 	ret = os_snprintf(*pos, end - *pos, "\n");
512*a1157835SDaniel Fojt 	if (os_snprintf_error(end - *pos, ret))
5136d49e1aeSJan Lentfer 		return;
5146d49e1aeSJan Lentfer 	*pos += ret;
5156d49e1aeSJan Lentfer 
5166d49e1aeSJan Lentfer 	if (txt) {
5173ff40c12SJohn Marino 		ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
518*a1157835SDaniel Fojt 		if (os_snprintf_error(end - *pos, ret))
5196d49e1aeSJan Lentfer 			return;
5206d49e1aeSJan Lentfer 		*pos += ret;
5216d49e1aeSJan Lentfer 		for (i = 0; i < len; i++) {
5223ff40c12SJohn Marino 			ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
523*a1157835SDaniel Fojt 			if (os_snprintf_error(end - *pos, ret))
5246d49e1aeSJan Lentfer 				return;
5256d49e1aeSJan Lentfer 			*pos += ret;
5266d49e1aeSJan Lentfer 		}
5273ff40c12SJohn Marino 		ret = os_snprintf(*pos, end - *pos, "\n");
528*a1157835SDaniel Fojt 		if (os_snprintf_error(end - *pos, ret))
5296d49e1aeSJan Lentfer 			return;
5306d49e1aeSJan Lentfer 		*pos += ret;
5316d49e1aeSJan Lentfer 	}
5326d49e1aeSJan Lentfer }
5336d49e1aeSJan Lentfer 
5346d49e1aeSJan Lentfer 
eap_fast_write_pac(struct eap_sm * sm,const char * pac_file,char * buf,size_t len)5356d49e1aeSJan Lentfer static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
5366d49e1aeSJan Lentfer 			      char *buf, size_t len)
5376d49e1aeSJan Lentfer {
5386d49e1aeSJan Lentfer 	if (os_strncmp(pac_file, "blob://", 7) == 0) {
5396d49e1aeSJan Lentfer 		struct wpa_config_blob *blob;
5406d49e1aeSJan Lentfer 		blob = os_zalloc(sizeof(*blob));
5416d49e1aeSJan Lentfer 		if (blob == NULL)
5426d49e1aeSJan Lentfer 			return -1;
5436d49e1aeSJan Lentfer 		blob->data = (u8 *) buf;
5446d49e1aeSJan Lentfer 		blob->len = len;
5456d49e1aeSJan Lentfer 		buf = NULL;
5466d49e1aeSJan Lentfer 		blob->name = os_strdup(pac_file + 7);
5476d49e1aeSJan Lentfer 		if (blob->name == NULL) {
5486d49e1aeSJan Lentfer 			os_free(blob);
5496d49e1aeSJan Lentfer 			return -1;
5506d49e1aeSJan Lentfer 		}
5516d49e1aeSJan Lentfer 		eap_set_config_blob(sm, blob);
5526d49e1aeSJan Lentfer 	} else {
5536d49e1aeSJan Lentfer 		FILE *f;
5546d49e1aeSJan Lentfer 		f = fopen(pac_file, "wb");
5556d49e1aeSJan Lentfer 		if (f == NULL) {
5566d49e1aeSJan Lentfer 			wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC "
5576d49e1aeSJan Lentfer 				   "file '%s' for writing", pac_file);
5586d49e1aeSJan Lentfer 			return -1;
5596d49e1aeSJan Lentfer 		}
5606d49e1aeSJan Lentfer 		if (fwrite(buf, 1, len, f) != len) {
5616d49e1aeSJan Lentfer 			wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all "
5626d49e1aeSJan Lentfer 				   "PACs into '%s'", pac_file);
5636d49e1aeSJan Lentfer 			fclose(f);
5646d49e1aeSJan Lentfer 			return -1;
5656d49e1aeSJan Lentfer 		}
5666d49e1aeSJan Lentfer 		os_free(buf);
5676d49e1aeSJan Lentfer 		fclose(f);
5686d49e1aeSJan Lentfer 	}
5696d49e1aeSJan Lentfer 
5706d49e1aeSJan Lentfer 	return 0;
5716d49e1aeSJan Lentfer }
5726d49e1aeSJan Lentfer 
5736d49e1aeSJan Lentfer 
eap_fast_add_pac_data(struct eap_fast_pac * pac,char ** buf,char ** pos,size_t * buf_len)5746d49e1aeSJan Lentfer static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
5756d49e1aeSJan Lentfer 				 char **pos, size_t *buf_len)
5766d49e1aeSJan Lentfer {
5776d49e1aeSJan Lentfer 	int ret;
5786d49e1aeSJan Lentfer 
5796d49e1aeSJan Lentfer 	ret = os_snprintf(*pos, *buf + *buf_len - *pos,
5806d49e1aeSJan Lentfer 			  "START\nPAC-Type=%d\n", pac->pac_type);
581*a1157835SDaniel Fojt 	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
5826d49e1aeSJan Lentfer 		return -1;
5836d49e1aeSJan Lentfer 
5846d49e1aeSJan Lentfer 	*pos += ret;
5856d49e1aeSJan Lentfer 	eap_fast_write(buf, pos, buf_len, "PAC-Key",
5866d49e1aeSJan Lentfer 		       pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
5876d49e1aeSJan Lentfer 	eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
5886d49e1aeSJan Lentfer 		       pac->pac_opaque, pac->pac_opaque_len, 0);
5896d49e1aeSJan Lentfer 	eap_fast_write(buf, pos, buf_len, "PAC-Info",
5906d49e1aeSJan Lentfer 		       pac->pac_info, pac->pac_info_len, 0);
5916d49e1aeSJan Lentfer 	eap_fast_write(buf, pos, buf_len, "A-ID",
5926d49e1aeSJan Lentfer 		       pac->a_id, pac->a_id_len, 0);
5936d49e1aeSJan Lentfer 	eap_fast_write(buf, pos, buf_len, "I-ID",
5946d49e1aeSJan Lentfer 		       pac->i_id, pac->i_id_len, 1);
5956d49e1aeSJan Lentfer 	eap_fast_write(buf, pos, buf_len, "A-ID-Info",
5966d49e1aeSJan Lentfer 		       pac->a_id_info, pac->a_id_info_len, 1);
5976d49e1aeSJan Lentfer 	if (*buf == NULL) {
5986d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
5996d49e1aeSJan Lentfer 			   "data");
6006d49e1aeSJan Lentfer 		return -1;
6016d49e1aeSJan Lentfer 	}
6026d49e1aeSJan Lentfer 	ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
603*a1157835SDaniel Fojt 	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
6046d49e1aeSJan Lentfer 		return -1;
6056d49e1aeSJan Lentfer 	*pos += ret;
6066d49e1aeSJan Lentfer 
6076d49e1aeSJan Lentfer 	return 0;
6086d49e1aeSJan Lentfer }
6096d49e1aeSJan Lentfer 
6106d49e1aeSJan Lentfer 
6116d49e1aeSJan Lentfer /**
6126d49e1aeSJan Lentfer  * eap_fast_save_pac - Save PAC entries (text format)
6136d49e1aeSJan Lentfer  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
6146d49e1aeSJan Lentfer  * @pac_root: Root of the PAC list
6156d49e1aeSJan Lentfer  * @pac_file: Name of the PAC file/blob
6166d49e1aeSJan Lentfer  * Returns: 0 on success, -1 on failure
6176d49e1aeSJan Lentfer  */
eap_fast_save_pac(struct eap_sm * sm,struct eap_fast_pac * pac_root,const char * pac_file)6186d49e1aeSJan Lentfer int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
6196d49e1aeSJan Lentfer 		      const char *pac_file)
6206d49e1aeSJan Lentfer {
6216d49e1aeSJan Lentfer 	struct eap_fast_pac *pac;
6226d49e1aeSJan Lentfer 	int ret, count = 0;
6236d49e1aeSJan Lentfer 	char *buf, *pos;
6246d49e1aeSJan Lentfer 	size_t buf_len;
6256d49e1aeSJan Lentfer 
6266d49e1aeSJan Lentfer 	if (pac_file == NULL)
6276d49e1aeSJan Lentfer 		return -1;
6286d49e1aeSJan Lentfer 
6296d49e1aeSJan Lentfer 	buf_len = 1024;
6306d49e1aeSJan Lentfer 	pos = buf = os_malloc(buf_len);
6316d49e1aeSJan Lentfer 	if (buf == NULL)
6326d49e1aeSJan Lentfer 		return -1;
6336d49e1aeSJan Lentfer 
6346d49e1aeSJan Lentfer 	ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
635*a1157835SDaniel Fojt 	if (os_snprintf_error(buf + buf_len - pos, ret)) {
6366d49e1aeSJan Lentfer 		os_free(buf);
6376d49e1aeSJan Lentfer 		return -1;
6386d49e1aeSJan Lentfer 	}
6396d49e1aeSJan Lentfer 	pos += ret;
6406d49e1aeSJan Lentfer 
6416d49e1aeSJan Lentfer 	pac = pac_root;
6426d49e1aeSJan Lentfer 	while (pac) {
6436d49e1aeSJan Lentfer 		if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
6446d49e1aeSJan Lentfer 			os_free(buf);
6456d49e1aeSJan Lentfer 			return -1;
6466d49e1aeSJan Lentfer 		}
6476d49e1aeSJan Lentfer 		count++;
6486d49e1aeSJan Lentfer 		pac = pac->next;
6496d49e1aeSJan Lentfer 	}
6506d49e1aeSJan Lentfer 
6516d49e1aeSJan Lentfer 	if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
6526d49e1aeSJan Lentfer 		os_free(buf);
6536d49e1aeSJan Lentfer 		return -1;
6546d49e1aeSJan Lentfer 	}
6556d49e1aeSJan Lentfer 
6566d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
6576d49e1aeSJan Lentfer 		   count, pac_file);
6586d49e1aeSJan Lentfer 
6596d49e1aeSJan Lentfer 	return 0;
6606d49e1aeSJan Lentfer }
6616d49e1aeSJan Lentfer 
6626d49e1aeSJan Lentfer 
6636d49e1aeSJan Lentfer /**
6646d49e1aeSJan Lentfer  * eap_fast_pac_list_truncate - Truncate a PAC list to the given length
6656d49e1aeSJan Lentfer  * @pac_root: Root of the PAC list
6666d49e1aeSJan Lentfer  * @max_len: Maximum length of the list (>= 1)
6676d49e1aeSJan Lentfer  * Returns: Number of PAC entries removed
6686d49e1aeSJan Lentfer  */
eap_fast_pac_list_truncate(struct eap_fast_pac * pac_root,size_t max_len)6696d49e1aeSJan Lentfer size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
6706d49e1aeSJan Lentfer 				  size_t max_len)
6716d49e1aeSJan Lentfer {
6726d49e1aeSJan Lentfer 	struct eap_fast_pac *pac, *prev;
6736d49e1aeSJan Lentfer 	size_t count;
6746d49e1aeSJan Lentfer 
6756d49e1aeSJan Lentfer 	pac = pac_root;
6766d49e1aeSJan Lentfer 	prev = NULL;
6776d49e1aeSJan Lentfer 	count = 0;
6786d49e1aeSJan Lentfer 
6796d49e1aeSJan Lentfer 	while (pac) {
6806d49e1aeSJan Lentfer 		count++;
6816d49e1aeSJan Lentfer 		if (count > max_len)
6826d49e1aeSJan Lentfer 			break;
6836d49e1aeSJan Lentfer 		prev = pac;
6846d49e1aeSJan Lentfer 		pac = pac->next;
6856d49e1aeSJan Lentfer 	}
6866d49e1aeSJan Lentfer 
6876d49e1aeSJan Lentfer 	if (count <= max_len || prev == NULL)
6886d49e1aeSJan Lentfer 		return 0;
6896d49e1aeSJan Lentfer 
6906d49e1aeSJan Lentfer 	count = 0;
6916d49e1aeSJan Lentfer 	prev->next = NULL;
6926d49e1aeSJan Lentfer 
6936d49e1aeSJan Lentfer 	while (pac) {
6946d49e1aeSJan Lentfer 		prev = pac;
6956d49e1aeSJan Lentfer 		pac = pac->next;
6966d49e1aeSJan Lentfer 		eap_fast_free_pac(prev);
6976d49e1aeSJan Lentfer 		count++;
6986d49e1aeSJan Lentfer 	}
6996d49e1aeSJan Lentfer 
7006d49e1aeSJan Lentfer 	return count;
7016d49e1aeSJan Lentfer }
7026d49e1aeSJan Lentfer 
7036d49e1aeSJan Lentfer 
eap_fast_pac_get_a_id(struct eap_fast_pac * pac)7046d49e1aeSJan Lentfer static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
7056d49e1aeSJan Lentfer {
7066d49e1aeSJan Lentfer 	u8 *pos, *end;
7076d49e1aeSJan Lentfer 	u16 type, len;
7086d49e1aeSJan Lentfer 
7096d49e1aeSJan Lentfer 	pos = pac->pac_info;
7106d49e1aeSJan Lentfer 	end = pos + pac->pac_info_len;
7116d49e1aeSJan Lentfer 
712*a1157835SDaniel Fojt 	while (end - pos > 4) {
7136d49e1aeSJan Lentfer 		type = WPA_GET_BE16(pos);
7146d49e1aeSJan Lentfer 		pos += 2;
7156d49e1aeSJan Lentfer 		len = WPA_GET_BE16(pos);
7166d49e1aeSJan Lentfer 		pos += 2;
717*a1157835SDaniel Fojt 		if (len > (unsigned int) (end - pos))
7186d49e1aeSJan Lentfer 			break;
7196d49e1aeSJan Lentfer 
7206d49e1aeSJan Lentfer 		if (type == PAC_TYPE_A_ID) {
7216d49e1aeSJan Lentfer 			os_free(pac->a_id);
722*a1157835SDaniel Fojt 			pac->a_id = os_memdup(pos, len);
7236d49e1aeSJan Lentfer 			if (pac->a_id == NULL)
7246d49e1aeSJan Lentfer 				break;
7256d49e1aeSJan Lentfer 			pac->a_id_len = len;
7266d49e1aeSJan Lentfer 		}
7276d49e1aeSJan Lentfer 
7286d49e1aeSJan Lentfer 		if (type == PAC_TYPE_A_ID_INFO) {
7296d49e1aeSJan Lentfer 			os_free(pac->a_id_info);
730*a1157835SDaniel Fojt 			pac->a_id_info = os_memdup(pos, len);
7316d49e1aeSJan Lentfer 			if (pac->a_id_info == NULL)
7326d49e1aeSJan Lentfer 				break;
7336d49e1aeSJan Lentfer 			pac->a_id_info_len = len;
7346d49e1aeSJan Lentfer 		}
7356d49e1aeSJan Lentfer 
7366d49e1aeSJan Lentfer 		pos += len;
7376d49e1aeSJan Lentfer 	}
7386d49e1aeSJan Lentfer }
7396d49e1aeSJan Lentfer 
7406d49e1aeSJan Lentfer 
7416d49e1aeSJan Lentfer /**
7426d49e1aeSJan Lentfer  * eap_fast_load_pac_bin - Load PAC entries (binary format)
7436d49e1aeSJan Lentfer  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
7446d49e1aeSJan Lentfer  * @pac_root: Pointer to root of the PAC list (to be filled)
7456d49e1aeSJan Lentfer  * @pac_file: Name of the PAC file/blob to load
7466d49e1aeSJan Lentfer  * Returns: 0 on success, -1 on failure
7476d49e1aeSJan Lentfer  */
eap_fast_load_pac_bin(struct eap_sm * sm,struct eap_fast_pac ** pac_root,const char * pac_file)7486d49e1aeSJan Lentfer int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
7496d49e1aeSJan Lentfer 			  const char *pac_file)
7506d49e1aeSJan Lentfer {
7516d49e1aeSJan Lentfer 	const struct wpa_config_blob *blob = NULL;
7526d49e1aeSJan Lentfer 	u8 *buf, *end, *pos;
7536d49e1aeSJan Lentfer 	size_t len, count = 0;
7546d49e1aeSJan Lentfer 	struct eap_fast_pac *pac, *prev;
7556d49e1aeSJan Lentfer 
7566d49e1aeSJan Lentfer 	*pac_root = NULL;
7576d49e1aeSJan Lentfer 
7586d49e1aeSJan Lentfer 	if (pac_file == NULL)
7596d49e1aeSJan Lentfer 		return -1;
7606d49e1aeSJan Lentfer 
7616d49e1aeSJan Lentfer 	if (os_strncmp(pac_file, "blob://", 7) == 0) {
7626d49e1aeSJan Lentfer 		blob = eap_get_config_blob(sm, pac_file + 7);
7636d49e1aeSJan Lentfer 		if (blob == NULL) {
7646d49e1aeSJan Lentfer 			wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
7656d49e1aeSJan Lentfer 				   "assume no PAC entries have been "
7666d49e1aeSJan Lentfer 				   "provisioned", pac_file + 7);
7676d49e1aeSJan Lentfer 			return 0;
7686d49e1aeSJan Lentfer 		}
7696d49e1aeSJan Lentfer 		buf = blob->data;
7706d49e1aeSJan Lentfer 		len = blob->len;
7716d49e1aeSJan Lentfer 	} else {
7726d49e1aeSJan Lentfer 		buf = (u8 *) os_readfile(pac_file, &len);
7736d49e1aeSJan Lentfer 		if (buf == NULL) {
7746d49e1aeSJan Lentfer 			wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
7756d49e1aeSJan Lentfer 				   "assume no PAC entries have been "
7766d49e1aeSJan Lentfer 				   "provisioned", pac_file);
7776d49e1aeSJan Lentfer 			return 0;
7786d49e1aeSJan Lentfer 		}
7796d49e1aeSJan Lentfer 	}
7806d49e1aeSJan Lentfer 
7816d49e1aeSJan Lentfer 	if (len == 0) {
7826d49e1aeSJan Lentfer 		if (blob == NULL)
7836d49e1aeSJan Lentfer 			os_free(buf);
7846d49e1aeSJan Lentfer 		return 0;
7856d49e1aeSJan Lentfer 	}
7866d49e1aeSJan Lentfer 
7876d49e1aeSJan Lentfer 	if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC ||
7886d49e1aeSJan Lentfer 	    WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) {
7896d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)",
7906d49e1aeSJan Lentfer 			   pac_file);
7916d49e1aeSJan Lentfer 		if (blob == NULL)
7926d49e1aeSJan Lentfer 			os_free(buf);
7936d49e1aeSJan Lentfer 		return -1;
7946d49e1aeSJan Lentfer 	}
7956d49e1aeSJan Lentfer 
7966d49e1aeSJan Lentfer 	pac = prev = NULL;
7976d49e1aeSJan Lentfer 	pos = buf + 6;
7986d49e1aeSJan Lentfer 	end = buf + len;
7996d49e1aeSJan Lentfer 	while (pos < end) {
800*a1157835SDaniel Fojt 		u16 val;
801*a1157835SDaniel Fojt 
802*a1157835SDaniel Fojt 		if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) {
803*a1157835SDaniel Fojt 			pac = NULL;
8046d49e1aeSJan Lentfer 			goto parse_fail;
805*a1157835SDaniel Fojt 		}
8066d49e1aeSJan Lentfer 
8076d49e1aeSJan Lentfer 		pac = os_zalloc(sizeof(*pac));
8086d49e1aeSJan Lentfer 		if (pac == NULL)
8096d49e1aeSJan Lentfer 			goto parse_fail;
8106d49e1aeSJan Lentfer 
8116d49e1aeSJan Lentfer 		pac->pac_type = WPA_GET_BE16(pos);
8126d49e1aeSJan Lentfer 		pos += 2;
8136d49e1aeSJan Lentfer 		os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
8146d49e1aeSJan Lentfer 		pos += EAP_FAST_PAC_KEY_LEN;
815*a1157835SDaniel Fojt 		val = WPA_GET_BE16(pos);
8166d49e1aeSJan Lentfer 		pos += 2;
817*a1157835SDaniel Fojt 		if (val > end - pos)
8186d49e1aeSJan Lentfer 			goto parse_fail;
819*a1157835SDaniel Fojt 		pac->pac_opaque_len = val;
820*a1157835SDaniel Fojt 		pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len);
8216d49e1aeSJan Lentfer 		if (pac->pac_opaque == NULL)
8226d49e1aeSJan Lentfer 			goto parse_fail;
8236d49e1aeSJan Lentfer 		pos += pac->pac_opaque_len;
824*a1157835SDaniel Fojt 		if (2 > end - pos)
8256d49e1aeSJan Lentfer 			goto parse_fail;
826*a1157835SDaniel Fojt 		val = WPA_GET_BE16(pos);
827*a1157835SDaniel Fojt 		pos += 2;
828*a1157835SDaniel Fojt 		if (val > end - pos)
829*a1157835SDaniel Fojt 			goto parse_fail;
830*a1157835SDaniel Fojt 		pac->pac_info_len = val;
831*a1157835SDaniel Fojt 		pac->pac_info = os_memdup(pos, pac->pac_info_len);
8326d49e1aeSJan Lentfer 		if (pac->pac_info == NULL)
8336d49e1aeSJan Lentfer 			goto parse_fail;
8346d49e1aeSJan Lentfer 		pos += pac->pac_info_len;
8356d49e1aeSJan Lentfer 		eap_fast_pac_get_a_id(pac);
8366d49e1aeSJan Lentfer 
8376d49e1aeSJan Lentfer 		count++;
8386d49e1aeSJan Lentfer 		if (prev)
8396d49e1aeSJan Lentfer 			prev->next = pac;
8406d49e1aeSJan Lentfer 		else
8416d49e1aeSJan Lentfer 			*pac_root = pac;
8426d49e1aeSJan Lentfer 		prev = pac;
8436d49e1aeSJan Lentfer 	}
8446d49e1aeSJan Lentfer 
8456d49e1aeSJan Lentfer 	if (blob == NULL)
8466d49e1aeSJan Lentfer 		os_free(buf);
8476d49e1aeSJan Lentfer 
8486d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)",
8496d49e1aeSJan Lentfer 		   (unsigned long) count, pac_file);
8506d49e1aeSJan Lentfer 
8516d49e1aeSJan Lentfer 	return 0;
8526d49e1aeSJan Lentfer 
8536d49e1aeSJan Lentfer parse_fail:
8546d49e1aeSJan Lentfer 	wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)",
8556d49e1aeSJan Lentfer 		   pac_file);
8566d49e1aeSJan Lentfer 	if (blob == NULL)
8576d49e1aeSJan Lentfer 		os_free(buf);
8586d49e1aeSJan Lentfer 	if (pac)
8596d49e1aeSJan Lentfer 		eap_fast_free_pac(pac);
8606d49e1aeSJan Lentfer 	return -1;
8616d49e1aeSJan Lentfer }
8626d49e1aeSJan Lentfer 
8636d49e1aeSJan Lentfer 
8646d49e1aeSJan Lentfer /**
8656d49e1aeSJan Lentfer  * eap_fast_save_pac_bin - Save PAC entries (binary format)
8666d49e1aeSJan Lentfer  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
8676d49e1aeSJan Lentfer  * @pac_root: Root of the PAC list
8686d49e1aeSJan Lentfer  * @pac_file: Name of the PAC file/blob
8696d49e1aeSJan Lentfer  * Returns: 0 on success, -1 on failure
8706d49e1aeSJan Lentfer  */
eap_fast_save_pac_bin(struct eap_sm * sm,struct eap_fast_pac * pac_root,const char * pac_file)8716d49e1aeSJan Lentfer int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
8726d49e1aeSJan Lentfer 			  const char *pac_file)
8736d49e1aeSJan Lentfer {
8746d49e1aeSJan Lentfer 	size_t len, count = 0;
8756d49e1aeSJan Lentfer 	struct eap_fast_pac *pac;
8766d49e1aeSJan Lentfer 	u8 *buf, *pos;
8776d49e1aeSJan Lentfer 
8786d49e1aeSJan Lentfer 	len = 6;
8796d49e1aeSJan Lentfer 	pac = pac_root;
8806d49e1aeSJan Lentfer 	while (pac) {
8816d49e1aeSJan Lentfer 		if (pac->pac_opaque_len > 65535 ||
8826d49e1aeSJan Lentfer 		    pac->pac_info_len > 65535)
8836d49e1aeSJan Lentfer 			return -1;
8846d49e1aeSJan Lentfer 		len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
8856d49e1aeSJan Lentfer 			2 + pac->pac_info_len;
8866d49e1aeSJan Lentfer 		pac = pac->next;
8876d49e1aeSJan Lentfer 	}
8886d49e1aeSJan Lentfer 
8896d49e1aeSJan Lentfer 	buf = os_malloc(len);
8906d49e1aeSJan Lentfer 	if (buf == NULL)
8916d49e1aeSJan Lentfer 		return -1;
8926d49e1aeSJan Lentfer 
8936d49e1aeSJan Lentfer 	pos = buf;
8946d49e1aeSJan Lentfer 	WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
8956d49e1aeSJan Lentfer 	pos += 4;
8966d49e1aeSJan Lentfer 	WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
8976d49e1aeSJan Lentfer 	pos += 2;
8986d49e1aeSJan Lentfer 
8996d49e1aeSJan Lentfer 	pac = pac_root;
9006d49e1aeSJan Lentfer 	while (pac) {
9016d49e1aeSJan Lentfer 		WPA_PUT_BE16(pos, pac->pac_type);
9026d49e1aeSJan Lentfer 		pos += 2;
9036d49e1aeSJan Lentfer 		os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
9046d49e1aeSJan Lentfer 		pos += EAP_FAST_PAC_KEY_LEN;
9056d49e1aeSJan Lentfer 		WPA_PUT_BE16(pos, pac->pac_opaque_len);
9066d49e1aeSJan Lentfer 		pos += 2;
9076d49e1aeSJan Lentfer 		os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
9086d49e1aeSJan Lentfer 		pos += pac->pac_opaque_len;
9096d49e1aeSJan Lentfer 		WPA_PUT_BE16(pos, pac->pac_info_len);
9106d49e1aeSJan Lentfer 		pos += 2;
9116d49e1aeSJan Lentfer 		os_memcpy(pos, pac->pac_info, pac->pac_info_len);
9126d49e1aeSJan Lentfer 		pos += pac->pac_info_len;
9136d49e1aeSJan Lentfer 
9146d49e1aeSJan Lentfer 		pac = pac->next;
9156d49e1aeSJan Lentfer 		count++;
9166d49e1aeSJan Lentfer 	}
9176d49e1aeSJan Lentfer 
9186d49e1aeSJan Lentfer 	if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
9196d49e1aeSJan Lentfer 		os_free(buf);
9206d49e1aeSJan Lentfer 		return -1;
9216d49e1aeSJan Lentfer 	}
9226d49e1aeSJan Lentfer 
9236d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' "
9246d49e1aeSJan Lentfer 		   "(bin)", (unsigned long) count, pac_file);
9256d49e1aeSJan Lentfer 
9266d49e1aeSJan Lentfer 	return 0;
9276d49e1aeSJan Lentfer }
928