xref: /freebsd/contrib/wpa/src/eap_peer/eap_fast_pac.c (revision 67350cb56a69468c118bd4ccf6e361b7ebfa9eb4)
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