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