1/*
2 * EAP peer method: EAP-FAST PAC file processing
3 * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "eap_config.h"
13#include "eap_i.h"
14#include "eap_fast_pac.h"
15
16/* TODO: encrypt PAC-Key in the PAC file */
17
18
19/* Text data format */
20static const char *pac_file_hdr =
21	"wpa_supplicant EAP-FAST PAC file - version 1";
22
23/*
24 * Binary data format
25 * 4-octet magic value: 6A E4 92 0C
26 * 2-octet version (big endian)
27 * <version specific data>
28 *
29 * version=0:
30 * Sequence of PAC entries:
31 *   2-octet PAC-Type (big endian)
32 *   32-octet PAC-Key
33 *   2-octet PAC-Opaque length (big endian)
34 *   <variable len> PAC-Opaque data (length bytes)
35 *   2-octet PAC-Info length (big endian)
36 *   <variable len> PAC-Info data (length bytes)
37 */
38
39#define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c
40#define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0
41
42
43/**
44 * eap_fast_free_pac - Free PAC data
45 * @pac: Pointer to the PAC entry
46 *
47 * Note that the PAC entry must not be in a list since this function does not
48 * remove the list links.
49 */
50void eap_fast_free_pac(struct eap_fast_pac *pac)
51{
52	os_free(pac->pac_opaque);
53	os_free(pac->pac_info);
54	os_free(pac->a_id);
55	os_free(pac->i_id);
56	os_free(pac->a_id_info);
57	os_free(pac);
58}
59
60
61/**
62 * eap_fast_get_pac - Get a PAC entry based on A-ID
63 * @pac_root: Pointer to root of the PAC list
64 * @a_id: A-ID to search for
65 * @a_id_len: Length of A-ID
66 * @pac_type: PAC-Type to search for
67 * Returns: Pointer to the PAC entry, or %NULL if A-ID not found
68 */
69struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
70				       const u8 *a_id, size_t a_id_len,
71				       u16 pac_type)
72{
73	struct eap_fast_pac *pac = pac_root;
74
75	while (pac) {
76		if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
77		    os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
78			return pac;
79		}
80		pac = pac->next;
81	}
82	return NULL;
83}
84
85
86static void eap_fast_remove_pac(struct eap_fast_pac **pac_root,
87				struct eap_fast_pac **pac_current,
88				const u8 *a_id, size_t a_id_len, u16 pac_type)
89{
90	struct eap_fast_pac *pac, *prev;
91
92	pac = *pac_root;
93	prev = NULL;
94
95	while (pac) {
96		if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
97		    os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
98			if (prev == NULL)
99				*pac_root = pac->next;
100			else
101				prev->next = pac->next;
102			if (*pac_current == pac)
103				*pac_current = NULL;
104			eap_fast_free_pac(pac);
105			break;
106		}
107		prev = pac;
108		pac = pac->next;
109	}
110}
111
112
113static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
114			     const u8 *src, size_t src_len)
115{
116	if (src) {
117		*dst = os_memdup(src, src_len);
118		if (*dst == NULL)
119			return -1;
120		*dst_len = src_len;
121	}
122	return 0;
123}
124
125
126/**
127 * eap_fast_add_pac - Add a copy of a PAC entry to a list
128 * @pac_root: Pointer to PAC list root pointer
129 * @pac_current: Pointer to the current PAC pointer
130 * @entry: New entry to clone and add to the list
131 * Returns: 0 on success, -1 on failure
132 *
133 * This function makes a clone of the given PAC entry and adds this copied
134 * entry to the list (pac_root). If an old entry for the same A-ID is found,
135 * it will be removed from the PAC list and in this case, pac_current entry
136 * is set to %NULL if it was the removed entry.
137 */
138int eap_fast_add_pac(struct eap_fast_pac **pac_root,
139		     struct eap_fast_pac **pac_current,
140		     struct eap_fast_pac *entry)
141{
142	struct eap_fast_pac *pac;
143
144	if (entry == NULL || entry->a_id == NULL)
145		return -1;
146
147	/* Remove a possible old entry for the matching A-ID. */
148	eap_fast_remove_pac(pac_root, pac_current,
149			    entry->a_id, entry->a_id_len, entry->pac_type);
150
151	/* Allocate a new entry and add it to the list of PACs. */
152	pac = os_zalloc(sizeof(*pac));
153	if (pac == NULL)
154		return -1;
155
156	pac->pac_type = entry->pac_type;
157	os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
158	if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
159			      entry->pac_opaque, entry->pac_opaque_len) < 0 ||
160	    eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
161			      entry->pac_info, entry->pac_info_len) < 0 ||
162	    eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
163			      entry->a_id, entry->a_id_len) < 0 ||
164	    eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
165			      entry->i_id, entry->i_id_len) < 0 ||
166	    eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
167			      entry->a_id_info, entry->a_id_info_len) < 0) {
168		eap_fast_free_pac(pac);
169		return -1;
170	}
171
172	pac->next = *pac_root;
173	*pac_root = pac;
174
175	return 0;
176}
177
178
179struct eap_fast_read_ctx {
180	FILE *f;
181	const char *pos;
182	const char *end;
183	int line;
184	char *buf;
185	size_t buf_len;
186};
187
188static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
189{
190	char *pos;
191
192	rc->line++;
193	if (rc->f) {
194		if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
195			return -1;
196	} else {
197		const char *l_end;
198		size_t len;
199		if (rc->pos >= rc->end)
200			return -1;
201		l_end = rc->pos;
202		while (l_end < rc->end && *l_end != '\n')
203			l_end++;
204		len = l_end - rc->pos;
205		if (len >= rc->buf_len)
206			len = rc->buf_len - 1;
207		os_memcpy(rc->buf, rc->pos, len);
208		rc->buf[len] = '\0';
209		rc->pos = l_end + 1;
210	}
211
212	rc->buf[rc->buf_len - 1] = '\0';
213	pos = rc->buf;
214	while (*pos != '\0') {
215		if (*pos == '\n' || *pos == '\r') {
216			*pos = '\0';
217			break;
218		}
219		pos++;
220	}
221
222	pos = os_strchr(rc->buf, '=');
223	if (pos)
224		*pos++ = '\0';
225	*value = pos;
226
227	return 0;
228}
229
230
231static u8 * eap_fast_parse_hex(const char *value, size_t *len)
232{
233	int hlen;
234	u8 *buf;
235
236	if (value == NULL)
237		return NULL;
238	hlen = os_strlen(value);
239	if (hlen & 1)
240		return NULL;
241	*len = hlen / 2;
242	buf = os_malloc(*len);
243	if (buf == NULL)
244		return NULL;
245	if (hexstr2bin(value, buf, *len)) {
246		os_free(buf);
247		return NULL;
248	}
249	return buf;
250}
251
252
253static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
254				  struct eap_fast_read_ctx *rc)
255{
256	os_memset(rc, 0, sizeof(*rc));
257
258	rc->buf_len = 2048;
259	rc->buf = os_malloc(rc->buf_len);
260	if (rc->buf == NULL)
261		return -1;
262
263	if (os_strncmp(pac_file, "blob://", 7) == 0) {
264		const struct wpa_config_blob *blob;
265		blob = eap_get_config_blob(sm, pac_file + 7);
266		if (blob == NULL) {
267			wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
268				   "assume no PAC entries have been "
269				   "provisioned", pac_file + 7);
270			os_free(rc->buf);
271			return -1;
272		}
273		rc->pos = (char *) blob->data;
274		rc->end = (char *) blob->data + blob->len;
275	} else {
276		rc->f = fopen(pac_file, "rb");
277		if (rc->f == NULL) {
278			wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
279				   "assume no PAC entries have been "
280				   "provisioned", pac_file);
281			os_free(rc->buf);
282			return -1;
283		}
284	}
285
286	return 0;
287}
288
289
290static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
291{
292	os_free(rc->buf);
293	if (rc->f)
294		fclose(rc->f);
295}
296
297
298static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
299{
300	if (*pac)
301		return "START line without END";
302
303	*pac = os_zalloc(sizeof(struct eap_fast_pac));
304	if (*pac == NULL)
305		return "No memory for PAC entry";
306	(*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
307	return NULL;
308}
309
310
311static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
312				       struct eap_fast_pac **pac)
313{
314	if (*pac == NULL)
315		return "END line without START";
316	if (*pac_root) {
317		struct eap_fast_pac *end = *pac_root;
318		while (end->next)
319			end = end->next;
320		end->next = *pac;
321	} else
322		*pac_root = *pac;
323
324	*pac = NULL;
325	return NULL;
326}
327
328
329static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
330					    char *pos)
331{
332	if (!pos)
333		return "Cannot parse pac type";
334	pac->pac_type = atoi(pos);
335	if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
336	    pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
337	    pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION)
338		return "Unrecognized PAC-Type";
339
340	return NULL;
341}
342
343
344static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos)
345{
346	u8 *key;
347	size_t key_len;
348
349	key = eap_fast_parse_hex(pos, &key_len);
350	if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
351		os_free(key);
352		return "Invalid PAC-Key";
353	}
354
355	os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
356	os_free(key);
357
358	return NULL;
359}
360
361
362static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
363					      char *pos)
364{
365	os_free(pac->pac_opaque);
366	pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
367	if (pac->pac_opaque == NULL)
368		return "Invalid PAC-Opaque";
369	return NULL;
370}
371
372
373static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
374{
375	os_free(pac->a_id);
376	pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
377	if (pac->a_id == NULL)
378		return "Invalid A-ID";
379	return NULL;
380}
381
382
383static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
384{
385	os_free(pac->i_id);
386	pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
387	if (pac->i_id == NULL)
388		return "Invalid I-ID";
389	return NULL;
390}
391
392
393static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
394					     char *pos)
395{
396	os_free(pac->a_id_info);
397	pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
398	if (pac->a_id_info == NULL)
399		return "Invalid A-ID-Info";
400	return NULL;
401}
402
403
404/**
405 * eap_fast_load_pac - Load PAC entries (text format)
406 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
407 * @pac_root: Pointer to root of the PAC list (to be filled)
408 * @pac_file: Name of the PAC file/blob to load
409 * Returns: 0 on success, -1 on failure
410 */
411int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
412		      const char *pac_file)
413{
414	struct eap_fast_read_ctx rc;
415	struct eap_fast_pac *pac = NULL;
416	int count = 0;
417	char *pos;
418	const char *err = NULL;
419
420	if (pac_file == NULL)
421		return -1;
422
423	if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
424		return 0;
425
426	if (eap_fast_read_line(&rc, &pos) < 0) {
427		/* empty file - assume it is fine to overwrite */
428		eap_fast_deinit_pac_data(&rc);
429		return 0;
430	}
431	if (os_strcmp(pac_file_hdr, rc.buf) != 0)
432		err = "Unrecognized header line";
433
434	while (!err && eap_fast_read_line(&rc, &pos) == 0) {
435		if (os_strcmp(rc.buf, "START") == 0)
436			err = eap_fast_parse_start(&pac);
437		else if (os_strcmp(rc.buf, "END") == 0) {
438			err = eap_fast_parse_end(pac_root, &pac);
439			count++;
440		} else if (!pac)
441			err = "Unexpected line outside START/END block";
442		else if (os_strcmp(rc.buf, "PAC-Type") == 0)
443			err = eap_fast_parse_pac_type(pac, pos);
444		else if (os_strcmp(rc.buf, "PAC-Key") == 0)
445			err = eap_fast_parse_pac_key(pac, pos);
446		else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
447			err = eap_fast_parse_pac_opaque(pac, pos);
448		else if (os_strcmp(rc.buf, "A-ID") == 0)
449			err = eap_fast_parse_a_id(pac, pos);
450		else if (os_strcmp(rc.buf, "I-ID") == 0)
451			err = eap_fast_parse_i_id(pac, pos);
452		else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
453			err = eap_fast_parse_a_id_info(pac, pos);
454	}
455
456	if (pac) {
457		if (!err)
458			err = "PAC block not terminated with END";
459		eap_fast_free_pac(pac);
460	}
461
462	eap_fast_deinit_pac_data(&rc);
463
464	if (err) {
465		wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'",
466			   err, pac_file, rc.line);
467		return -1;
468	}
469
470	wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
471		   count, pac_file);
472
473	return 0;
474}
475
476
477static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
478			   const char *field, const u8 *data,
479			   size_t len, int txt)
480{
481	size_t i, need;
482	int ret;
483	char *end;
484
485	if (data == NULL || buf == NULL || *buf == NULL ||
486	    pos == NULL || *pos == NULL || *pos < *buf)
487		return;
488
489	need = os_strlen(field) + len * 2 + 30;
490	if (txt)
491		need += os_strlen(field) + len + 20;
492
493	if (*pos - *buf + need > *buf_len) {
494		char *nbuf = os_realloc(*buf, *buf_len + need);
495		if (nbuf == NULL) {
496			os_free(*buf);
497			*buf = NULL;
498			return;
499		}
500		*pos = nbuf + (*pos - *buf);
501		*buf = nbuf;
502		*buf_len += need;
503	}
504	end = *buf + *buf_len;
505
506	ret = os_snprintf(*pos, end - *pos, "%s=", field);
507	if (os_snprintf_error(end - *pos, ret))
508		return;
509	*pos += ret;
510	*pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
511	ret = os_snprintf(*pos, end - *pos, "\n");
512	if (os_snprintf_error(end - *pos, ret))
513		return;
514	*pos += ret;
515
516	if (txt) {
517		ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
518		if (os_snprintf_error(end - *pos, ret))
519			return;
520		*pos += ret;
521		for (i = 0; i < len; i++) {
522			ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
523			if (os_snprintf_error(end - *pos, ret))
524				return;
525			*pos += ret;
526		}
527		ret = os_snprintf(*pos, end - *pos, "\n");
528		if (os_snprintf_error(end - *pos, ret))
529			return;
530		*pos += ret;
531	}
532}
533
534
535static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
536			      char *buf, size_t len)
537{
538	if (os_strncmp(pac_file, "blob://", 7) == 0) {
539		struct wpa_config_blob *blob;
540		blob = os_zalloc(sizeof(*blob));
541		if (blob == NULL)
542			return -1;
543		blob->data = (u8 *) buf;
544		blob->len = len;
545		buf = NULL;
546		blob->name = os_strdup(pac_file + 7);
547		if (blob->name == NULL) {
548			os_free(blob);
549			return -1;
550		}
551		eap_set_config_blob(sm, blob);
552	} else {
553		FILE *f;
554		f = fopen(pac_file, "wb");
555		if (f == NULL) {
556			wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC "
557				   "file '%s' for writing", pac_file);
558			return -1;
559		}
560		if (fwrite(buf, 1, len, f) != len) {
561			wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all "
562				   "PACs into '%s'", pac_file);
563			fclose(f);
564			return -1;
565		}
566		os_free(buf);
567		fclose(f);
568	}
569
570	return 0;
571}
572
573
574static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
575				 char **pos, size_t *buf_len)
576{
577	int ret;
578
579	ret = os_snprintf(*pos, *buf + *buf_len - *pos,
580			  "START\nPAC-Type=%d\n", pac->pac_type);
581	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
582		return -1;
583
584	*pos += ret;
585	eap_fast_write(buf, pos, buf_len, "PAC-Key",
586		       pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
587	eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
588		       pac->pac_opaque, pac->pac_opaque_len, 0);
589	eap_fast_write(buf, pos, buf_len, "PAC-Info",
590		       pac->pac_info, pac->pac_info_len, 0);
591	eap_fast_write(buf, pos, buf_len, "A-ID",
592		       pac->a_id, pac->a_id_len, 0);
593	eap_fast_write(buf, pos, buf_len, "I-ID",
594		       pac->i_id, pac->i_id_len, 1);
595	eap_fast_write(buf, pos, buf_len, "A-ID-Info",
596		       pac->a_id_info, pac->a_id_info_len, 1);
597	if (*buf == NULL) {
598		wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
599			   "data");
600		return -1;
601	}
602	ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
603	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
604		return -1;
605	*pos += ret;
606
607	return 0;
608}
609
610
611/**
612 * eap_fast_save_pac - Save PAC entries (text format)
613 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
614 * @pac_root: Root of the PAC list
615 * @pac_file: Name of the PAC file/blob
616 * Returns: 0 on success, -1 on failure
617 */
618int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
619		      const char *pac_file)
620{
621	struct eap_fast_pac *pac;
622	int ret, count = 0;
623	char *buf, *pos;
624	size_t buf_len;
625
626	if (pac_file == NULL)
627		return -1;
628
629	buf_len = 1024;
630	pos = buf = os_malloc(buf_len);
631	if (buf == NULL)
632		return -1;
633
634	ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
635	if (os_snprintf_error(buf + buf_len - pos, ret)) {
636		os_free(buf);
637		return -1;
638	}
639	pos += ret;
640
641	pac = pac_root;
642	while (pac) {
643		if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
644			os_free(buf);
645			return -1;
646		}
647		count++;
648		pac = pac->next;
649	}
650
651	if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
652		os_free(buf);
653		return -1;
654	}
655
656	wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
657		   count, pac_file);
658
659	return 0;
660}
661
662
663/**
664 * eap_fast_pac_list_truncate - Truncate a PAC list to the given length
665 * @pac_root: Root of the PAC list
666 * @max_len: Maximum length of the list (>= 1)
667 * Returns: Number of PAC entries removed
668 */
669size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
670				  size_t max_len)
671{
672	struct eap_fast_pac *pac, *prev;
673	size_t count;
674
675	pac = pac_root;
676	prev = NULL;
677	count = 0;
678
679	while (pac) {
680		count++;
681		if (count > max_len)
682			break;
683		prev = pac;
684		pac = pac->next;
685	}
686
687	if (count <= max_len || prev == NULL)
688		return 0;
689
690	count = 0;
691	prev->next = NULL;
692
693	while (pac) {
694		prev = pac;
695		pac = pac->next;
696		eap_fast_free_pac(prev);
697		count++;
698	}
699
700	return count;
701}
702
703
704static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
705{
706	u8 *pos, *end;
707	u16 type, len;
708
709	pos = pac->pac_info;
710	end = pos + pac->pac_info_len;
711
712	while (end - pos > 4) {
713		type = WPA_GET_BE16(pos);
714		pos += 2;
715		len = WPA_GET_BE16(pos);
716		pos += 2;
717		if (len > (unsigned int) (end - pos))
718			break;
719
720		if (type == PAC_TYPE_A_ID) {
721			os_free(pac->a_id);
722			pac->a_id = os_memdup(pos, len);
723			if (pac->a_id == NULL)
724				break;
725			pac->a_id_len = len;
726		}
727
728		if (type == PAC_TYPE_A_ID_INFO) {
729			os_free(pac->a_id_info);
730			pac->a_id_info = os_memdup(pos, len);
731			if (pac->a_id_info == NULL)
732				break;
733			pac->a_id_info_len = len;
734		}
735
736		pos += len;
737	}
738}
739
740
741/**
742 * eap_fast_load_pac_bin - Load PAC entries (binary format)
743 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
744 * @pac_root: Pointer to root of the PAC list (to be filled)
745 * @pac_file: Name of the PAC file/blob to load
746 * Returns: 0 on success, -1 on failure
747 */
748int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
749			  const char *pac_file)
750{
751	const struct wpa_config_blob *blob = NULL;
752	u8 *buf, *end, *pos;
753	size_t len, count = 0;
754	struct eap_fast_pac *pac, *prev;
755
756	*pac_root = NULL;
757
758	if (pac_file == NULL)
759		return -1;
760
761	if (os_strncmp(pac_file, "blob://", 7) == 0) {
762		blob = eap_get_config_blob(sm, pac_file + 7);
763		if (blob == NULL) {
764			wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
765				   "assume no PAC entries have been "
766				   "provisioned", pac_file + 7);
767			return 0;
768		}
769		buf = blob->data;
770		len = blob->len;
771	} else {
772		buf = (u8 *) os_readfile(pac_file, &len);
773		if (buf == NULL) {
774			wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
775				   "assume no PAC entries have been "
776				   "provisioned", pac_file);
777			return 0;
778		}
779	}
780
781	if (len == 0) {
782		if (blob == NULL)
783			os_free(buf);
784		return 0;
785	}
786
787	if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC ||
788	    WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) {
789		wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)",
790			   pac_file);
791		if (blob == NULL)
792			os_free(buf);
793		return -1;
794	}
795
796	pac = prev = NULL;
797	pos = buf + 6;
798	end = buf + len;
799	while (pos < end) {
800		u16 val;
801
802		if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) {
803			pac = NULL;
804			goto parse_fail;
805		}
806
807		pac = os_zalloc(sizeof(*pac));
808		if (pac == NULL)
809			goto parse_fail;
810
811		pac->pac_type = WPA_GET_BE16(pos);
812		pos += 2;
813		os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
814		pos += EAP_FAST_PAC_KEY_LEN;
815		val = WPA_GET_BE16(pos);
816		pos += 2;
817		if (val > end - pos)
818			goto parse_fail;
819		pac->pac_opaque_len = val;
820		pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len);
821		if (pac->pac_opaque == NULL)
822			goto parse_fail;
823		pos += pac->pac_opaque_len;
824		if (2 > end - pos)
825			goto parse_fail;
826		val = WPA_GET_BE16(pos);
827		pos += 2;
828		if (val > end - pos)
829			goto parse_fail;
830		pac->pac_info_len = val;
831		pac->pac_info = os_memdup(pos, pac->pac_info_len);
832		if (pac->pac_info == NULL)
833			goto parse_fail;
834		pos += pac->pac_info_len;
835		eap_fast_pac_get_a_id(pac);
836
837		count++;
838		if (prev)
839			prev->next = pac;
840		else
841			*pac_root = pac;
842		prev = pac;
843	}
844
845	if (blob == NULL)
846		os_free(buf);
847
848	wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)",
849		   (unsigned long) count, pac_file);
850
851	return 0;
852
853parse_fail:
854	wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)",
855		   pac_file);
856	if (blob == NULL)
857		os_free(buf);
858	if (pac)
859		eap_fast_free_pac(pac);
860	return -1;
861}
862
863
864/**
865 * eap_fast_save_pac_bin - Save PAC entries (binary format)
866 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
867 * @pac_root: Root of the PAC list
868 * @pac_file: Name of the PAC file/blob
869 * Returns: 0 on success, -1 on failure
870 */
871int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
872			  const char *pac_file)
873{
874	size_t len, count = 0;
875	struct eap_fast_pac *pac;
876	u8 *buf, *pos;
877
878	len = 6;
879	pac = pac_root;
880	while (pac) {
881		if (pac->pac_opaque_len > 65535 ||
882		    pac->pac_info_len > 65535)
883			return -1;
884		len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
885			2 + pac->pac_info_len;
886		pac = pac->next;
887	}
888
889	buf = os_malloc(len);
890	if (buf == NULL)
891		return -1;
892
893	pos = buf;
894	WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
895	pos += 4;
896	WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
897	pos += 2;
898
899	pac = pac_root;
900	while (pac) {
901		WPA_PUT_BE16(pos, pac->pac_type);
902		pos += 2;
903		os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
904		pos += EAP_FAST_PAC_KEY_LEN;
905		WPA_PUT_BE16(pos, pac->pac_opaque_len);
906		pos += 2;
907		os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
908		pos += pac->pac_opaque_len;
909		WPA_PUT_BE16(pos, pac->pac_info_len);
910		pos += 2;
911		os_memcpy(pos, pac->pac_info, pac->pac_info_len);
912		pos += pac->pac_info_len;
913
914		pac = pac->next;
915		count++;
916	}
917
918	if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
919		os_free(buf);
920		return -1;
921	}
922
923	wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' "
924		   "(bin)", (unsigned long) count, pac_file);
925
926	return 0;
927}
928