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