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 */
20  static 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   */
eap_fast_free_pac(struct eap_fast_pac * pac)50  void 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   */
eap_fast_get_pac(struct eap_fast_pac * pac_root,const u8 * a_id,size_t a_id_len,u16 pac_type)69  struct 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  
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)86  static 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  
eap_fast_copy_buf(u8 ** dst,size_t * dst_len,const u8 * src,size_t src_len)113  static 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   */
eap_fast_add_pac(struct eap_fast_pac ** pac_root,struct eap_fast_pac ** pac_current,struct eap_fast_pac * entry)138  int 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  
179  struct 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  
eap_fast_read_line(struct eap_fast_read_ctx * rc,char ** value)188  static 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  
eap_fast_parse_hex(const char * value,size_t * len)231  static 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  
eap_fast_init_pac_data(struct eap_sm * sm,const char * pac_file,struct eap_fast_read_ctx * rc)253  static 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  
eap_fast_deinit_pac_data(struct eap_fast_read_ctx * rc)290  static 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  
eap_fast_parse_start(struct eap_fast_pac ** pac)298  static 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  
eap_fast_parse_end(struct eap_fast_pac ** pac_root,struct eap_fast_pac ** pac)311  static 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  
eap_fast_parse_pac_type(struct eap_fast_pac * pac,char * pos)329  static 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  
eap_fast_parse_pac_key(struct eap_fast_pac * pac,char * pos)344  static 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  
eap_fast_parse_pac_opaque(struct eap_fast_pac * pac,char * pos)362  static 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  
eap_fast_parse_a_id(struct eap_fast_pac * pac,char * pos)373  static 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  
eap_fast_parse_i_id(struct eap_fast_pac * pac,char * pos)383  static 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  
eap_fast_parse_a_id_info(struct eap_fast_pac * pac,char * pos)393  static 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   */
eap_fast_load_pac(struct eap_sm * sm,struct eap_fast_pac ** pac_root,const char * pac_file)411  int 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  
eap_fast_write(char ** buf,char ** pos,size_t * buf_len,const char * field,const u8 * data,size_t len,int txt)477  static 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  
eap_fast_write_pac(struct eap_sm * sm,const char * pac_file,char * buf,size_t len)535  static 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  
eap_fast_add_pac_data(struct eap_fast_pac * pac,char ** buf,char ** pos,size_t * buf_len)574  static 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   */
eap_fast_save_pac(struct eap_sm * sm,struct eap_fast_pac * pac_root,const char * pac_file)618  int 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   */
eap_fast_pac_list_truncate(struct eap_fast_pac * pac_root,size_t max_len)669  size_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  
eap_fast_pac_get_a_id(struct eap_fast_pac * pac)704  static 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   */
eap_fast_load_pac_bin(struct eap_sm * sm,struct eap_fast_pac ** pac_root,const char * pac_file)748  int 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  
853  parse_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   */
eap_fast_save_pac_bin(struct eap_sm * sm,struct eap_fast_pac * pac_root,const char * pac_file)871  int 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