| /* |
| * EAP peer method: EAP-FAST PAC file processing |
| * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> |
| * |
| * This software may be distributed under the terms of the BSD license. |
| * See README for more details. |
| */ |
| |
| #include "includes.h" |
| |
| #include "common.h" |
| #include "eap_config.h" |
| #include "eap_i.h" |
| #include "eap_fast_pac.h" |
| |
| /* TODO: encrypt PAC-Key in the PAC file */ |
| |
| |
| /* Text data format */ |
| static const char *pac_file_hdr = |
| "wpa_supplicant EAP-FAST PAC file - version 1"; |
| |
| /* |
| * Binary data format |
| * 4-octet magic value: 6A E4 92 0C |
| * 2-octet version (big endian) |
| * <version specific data> |
| * |
| * version=0: |
| * Sequence of PAC entries: |
| * 2-octet PAC-Type (big endian) |
| * 32-octet PAC-Key |
| * 2-octet PAC-Opaque length (big endian) |
| * <variable len> PAC-Opaque data (length bytes) |
| * 2-octet PAC-Info length (big endian) |
| * <variable len> PAC-Info data (length bytes) |
| */ |
| |
| #define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c |
| #define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0 |
| |
| |
| /** |
| * eap_fast_free_pac - Free PAC data |
| * @pac: Pointer to the PAC entry |
| * |
| * Note that the PAC entry must not be in a list since this function does not |
| * remove the list links. |
| */ |
| void eap_fast_free_pac(struct eap_fast_pac *pac) |
| { |
| os_free(pac->pac_opaque); |
| os_free(pac->pac_info); |
| os_free(pac->a_id); |
| os_free(pac->i_id); |
| os_free(pac->a_id_info); |
| os_free(pac); |
| } |
| |
| |
| /** |
| * eap_fast_get_pac - Get a PAC entry based on A-ID |
| * @pac_root: Pointer to root of the PAC list |
| * @a_id: A-ID to search for |
| * @a_id_len: Length of A-ID |
| * @pac_type: PAC-Type to search for |
| * Returns: Pointer to the PAC entry, or %NULL if A-ID not found |
| */ |
| struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root, |
| const u8 *a_id, size_t a_id_len, |
| u16 pac_type) |
| { |
| struct eap_fast_pac *pac = pac_root; |
| |
| while (pac) { |
| if (pac->pac_type == pac_type && pac->a_id_len == a_id_len && |
| os_memcmp(pac->a_id, a_id, a_id_len) == 0) { |
| return pac; |
| } |
| pac = pac->next; |
| } |
| return NULL; |
| } |
| |
| |
| static void 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) |
| { |
| struct eap_fast_pac *pac, *prev; |
| |
| pac = *pac_root; |
| prev = NULL; |
| |
| while (pac) { |
| if (pac->pac_type == pac_type && pac->a_id_len == a_id_len && |
| os_memcmp(pac->a_id, a_id, a_id_len) == 0) { |
| if (prev == NULL) |
| *pac_root = pac->next; |
| else |
| prev->next = pac->next; |
| if (*pac_current == pac) |
| *pac_current = NULL; |
| eap_fast_free_pac(pac); |
| break; |
| } |
| prev = pac; |
| pac = pac->next; |
| } |
| } |
| |
| |
| static int eap_fast_copy_buf(u8 **dst, size_t *dst_len, |
| const u8 *src, size_t src_len) |
| { |
| if (src) { |
| *dst = os_malloc(src_len); |
| if (*dst == NULL) |
| return -1; |
| os_memcpy(*dst, src, src_len); |
| *dst_len = src_len; |
| } |
| return 0; |
| } |
| |
| |
| /** |
| * eap_fast_add_pac - Add a copy of a PAC entry to a list |
| * @pac_root: Pointer to PAC list root pointer |
| * @pac_current: Pointer to the current PAC pointer |
| * @entry: New entry to clone and add to the list |
| * Returns: 0 on success, -1 on failure |
| * |
| * This function makes a clone of the given PAC entry and adds this copied |
| * entry to the list (pac_root). If an old entry for the same A-ID is found, |
| * it will be removed from the PAC list and in this case, pac_current entry |
| * is set to %NULL if it was the removed entry. |
| */ |
| int eap_fast_add_pac(struct eap_fast_pac **pac_root, |
| struct eap_fast_pac **pac_current, |
| struct eap_fast_pac *entry) |
| { |
| struct eap_fast_pac *pac; |
| |
| if (entry == NULL || entry->a_id == NULL) |
| return -1; |
| |
| /* Remove a possible old entry for the matching A-ID. */ |
| eap_fast_remove_pac(pac_root, pac_current, |
| entry->a_id, entry->a_id_len, entry->pac_type); |
| |
| /* Allocate a new entry and add it to the list of PACs. */ |
| pac = os_zalloc(sizeof(*pac)); |
| if (pac == NULL) |
| return -1; |
| |
| pac->pac_type = entry->pac_type; |
| os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN); |
| if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len, |
| entry->pac_opaque, entry->pac_opaque_len) < 0 || |
| eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len, |
| entry->pac_info, entry->pac_info_len) < 0 || |
| eap_fast_copy_buf(&pac->a_id, &pac->a_id_len, |
| entry->a_id, entry->a_id_len) < 0 || |
| eap_fast_copy_buf(&pac->i_id, &pac->i_id_len, |
| entry->i_id, entry->i_id_len) < 0 || |
| eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len, |
| entry->a_id_info, entry->a_id_info_len) < 0) { |
| eap_fast_free_pac(pac); |
| return -1; |
| } |
| |
| pac->next = *pac_root; |
| *pac_root = pac; |
| |
| return 0; |
| } |
| |
| |
| struct eap_fast_read_ctx { |
| FILE *f; |
| const char *pos; |
| const char *end; |
| int line; |
| char *buf; |
| size_t buf_len; |
| }; |
| |
| static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value) |
| { |
| char *pos; |
| |
| rc->line++; |
| if (rc->f) { |
| if (fgets(rc->buf, rc->buf_len, rc->f) == NULL) |
| return -1; |
| } else { |
| const char *l_end; |
| size_t len; |
| if (rc->pos >= rc->end) |
| return -1; |
| l_end = rc->pos; |
| while (l_end < rc->end && *l_end != '\n') |
| l_end++; |
| len = l_end - rc->pos; |
| if (len >= rc->buf_len) |
| len = rc->buf_len - 1; |
| os_memcpy(rc->buf, rc->pos, len); |
| rc->buf[len] = '\0'; |
| rc->pos = l_end + 1; |
| } |
| |
| rc->buf[rc->buf_len - 1] = '\0'; |
| pos = rc->buf; |
| while (*pos != '\0') { |
| if (*pos == '\n' || *pos == '\r') { |
| *pos = '\0'; |
| break; |
| } |
| pos++; |
| } |
| |
| pos = os_strchr(rc->buf, '='); |
| if (pos) |
| *pos++ = '\0'; |
| *value = pos; |
| |
| return 0; |
| } |
| |
| |
| static u8 * eap_fast_parse_hex(const char *value, size_t *len) |
| { |
| int hlen; |
| u8 *buf; |
| |
| if (value == NULL) |
| return NULL; |
| hlen = os_strlen(value); |
| if (hlen & 1) |
| return NULL; |
| *len = hlen / 2; |
| buf = os_malloc(*len); |
| if (buf == NULL) |
| return NULL; |
| if (hexstr2bin(value, buf, *len)) { |
| os_free(buf); |
| return NULL; |
| } |
| return buf; |
| } |
| |
| |
| static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file, |
| struct eap_fast_read_ctx *rc) |
| { |
| os_memset(rc, 0, sizeof(*rc)); |
| |
| rc->buf_len = 2048; |
| rc->buf = os_malloc(rc->buf_len); |
| if (rc->buf == NULL) |
| return -1; |
| |
| if (os_strncmp(pac_file, "blob://", 7) == 0) { |
| const struct wpa_config_blob *blob; |
| blob = eap_get_config_blob(sm, pac_file + 7); |
| if (blob == NULL) { |
| wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - " |
| "assume no PAC entries have been " |
| "provisioned", pac_file + 7); |
| os_free(rc->buf); |
| return -1; |
| } |
| rc->pos = (char *) blob->data; |
| rc->end = (char *) blob->data + blob->len; |
| } else { |
| rc->f = fopen(pac_file, "rb"); |
| if (rc->f == NULL) { |
| wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - " |
| "assume no PAC entries have been " |
| "provisioned", pac_file); |
| os_free(rc->buf); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc) |
| { |
| os_free(rc->buf); |
| if (rc->f) |
| fclose(rc->f); |
| } |
| |
| |
| static const char * eap_fast_parse_start(struct eap_fast_pac **pac) |
| { |
| if (*pac) |
| return "START line without END"; |
| |
| *pac = os_zalloc(sizeof(struct eap_fast_pac)); |
| if (*pac == NULL) |
| return "No memory for PAC entry"; |
| (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC; |
| return NULL; |
| } |
| |
| |
| static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root, |
| struct eap_fast_pac **pac) |
| { |
| if (*pac == NULL) |
| return "END line without START"; |
| if (*pac_root) { |
| struct eap_fast_pac *end = *pac_root; |
| while (end->next) |
| end = end->next; |
| end->next = *pac; |
| } else |
| *pac_root = *pac; |
| |
| *pac = NULL; |
| return NULL; |
| } |
| |
| |
| static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac, |
| char *pos) |
| { |
| pac->pac_type = atoi(pos); |
| if (pac->pac_type != PAC_TYPE_TUNNEL_PAC && |
| pac->pac_type != PAC_TYPE_USER_AUTHORIZATION && |
| pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) |
| return "Unrecognized PAC-Type"; |
| |
| return NULL; |
| } |
| |
| |
| static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos) |
| { |
| u8 *key; |
| size_t key_len; |
| |
| key = eap_fast_parse_hex(pos, &key_len); |
| if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) { |
| os_free(key); |
| return "Invalid PAC-Key"; |
| } |
| |
| os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN); |
| os_free(key); |
| |
| return NULL; |
| } |
| |
| |
| static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac, |
| char *pos) |
| { |
| os_free(pac->pac_opaque); |
| pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len); |
| if (pac->pac_opaque == NULL) |
| return "Invalid PAC-Opaque"; |
| return NULL; |
| } |
| |
| |
| static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos) |
| { |
| os_free(pac->a_id); |
| pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len); |
| if (pac->a_id == NULL) |
| return "Invalid A-ID"; |
| return NULL; |
| } |
| |
| |
| static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos) |
| { |
| os_free(pac->i_id); |
| pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len); |
| if (pac->i_id == NULL) |
| return "Invalid I-ID"; |
| return NULL; |
| } |
| |
| |
| static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac, |
| char *pos) |
| { |
| os_free(pac->a_id_info); |
| pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len); |
| if (pac->a_id_info == NULL) |
| return "Invalid A-ID-Info"; |
| return NULL; |
| } |
| |
| |
| /** |
| * eap_fast_load_pac - Load PAC entries (text format) |
| * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() |
| * @pac_root: Pointer to root of the PAC list (to be filled) |
| * @pac_file: Name of the PAC file/blob to load |
| * Returns: 0 on success, -1 on failure |
| */ |
| int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root, |
| const char *pac_file) |
| { |
| struct eap_fast_read_ctx rc; |
| struct eap_fast_pac *pac = NULL; |
| int count = 0; |
| char *pos; |
| const char *err = NULL; |
| |
| if (pac_file == NULL) |
| return -1; |
| |
| if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0) |
| return 0; |
| |
| if (eap_fast_read_line(&rc, &pos) < 0) { |
| /* empty file - assume it is fine to overwrite */ |
| eap_fast_deinit_pac_data(&rc); |
| return 0; |
| } |
| if (os_strcmp(pac_file_hdr, rc.buf) != 0) |
| err = "Unrecognized header line"; |
| |
| while (!err && eap_fast_read_line(&rc, &pos) == 0) { |
| if (os_strcmp(rc.buf, "START") == 0) |
| err = eap_fast_parse_start(&pac); |
| else if (os_strcmp(rc.buf, "END") == 0) { |
| err = eap_fast_parse_end(pac_root, &pac); |
| count++; |
| } else if (!pac) |
| err = "Unexpected line outside START/END block"; |
| else if (os_strcmp(rc.buf, "PAC-Type") == 0) |
| err = eap_fast_parse_pac_type(pac, pos); |
| else if (os_strcmp(rc.buf, "PAC-Key") == 0) |
| err = eap_fast_parse_pac_key(pac, pos); |
| else if (os_strcmp(rc.buf, "PAC-Opaque") == 0) |
| err = eap_fast_parse_pac_opaque(pac, pos); |
| else if (os_strcmp(rc.buf, "A-ID") == 0) |
| err = eap_fast_parse_a_id(pac, pos); |
| else if (os_strcmp(rc.buf, "I-ID") == 0) |
| err = eap_fast_parse_i_id(pac, pos); |
| else if (os_strcmp(rc.buf, "A-ID-Info") == 0) |
| err = eap_fast_parse_a_id_info(pac, pos); |
| } |
| |
| if (pac) { |
| err = "PAC block not terminated with END"; |
| eap_fast_free_pac(pac); |
| } |
| |
| eap_fast_deinit_pac_data(&rc); |
| |
| if (err) { |
| wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'", |
| err, pac_file, rc.line); |
| return -1; |
| } |
| |
| wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'", |
| count, pac_file); |
| |
| return 0; |
| } |
| |
| |
| static void eap_fast_write(char **buf, char **pos, size_t *buf_len, |
| const char *field, const u8 *data, |
| size_t len, int txt) |
| { |
| size_t i, need; |
| int ret; |
| char *end; |
| |
| if (data == NULL || buf == NULL || *buf == NULL || |
| pos == NULL || *pos == NULL || *pos < *buf) |
| return; |
| |
| need = os_strlen(field) + len * 2 + 30; |
| if (txt) |
| need += os_strlen(field) + len + 20; |
| |
| if (*pos - *buf + need > *buf_len) { |
| char *nbuf = os_realloc(*buf, *buf_len + need); |
| if (nbuf == NULL) { |
| os_free(*buf); |
| *buf = NULL; |
| return; |
| } |
| *pos = nbuf + (*pos - *buf); |
| *buf = nbuf; |
| *buf_len += need; |
| } |
| end = *buf + *buf_len; |
| |
| ret = os_snprintf(*pos, end - *pos, "%s=", field); |
| if (ret < 0 || ret >= end - *pos) |
| return; |
| *pos += ret; |
| *pos += wpa_snprintf_hex(*pos, end - *pos, data, len); |
| ret = os_snprintf(*pos, end - *pos, "\n"); |
| if (ret < 0 || ret >= end - *pos) |
| return; |
| *pos += ret; |
| |
| if (txt) { |
| ret = os_snprintf(*pos, end - *pos, "%s-txt=", field); |
| if (ret < 0 || ret >= end - *pos) |
| return; |
| *pos += ret; |
| for (i = 0; i < len; i++) { |
| ret = os_snprintf(*pos, end - *pos, "%c", data[i]); |
| if (ret < 0 || ret >= end - *pos) |
| return; |
| *pos += ret; |
| } |
| ret = os_snprintf(*pos, end - *pos, "\n"); |
| if (ret < 0 || ret >= end - *pos) |
| return; |
| *pos += ret; |
| } |
| } |
| |
| |
| static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file, |
| char *buf, size_t len) |
| { |
| if (os_strncmp(pac_file, "blob://", 7) == 0) { |
| struct wpa_config_blob *blob; |
| blob = os_zalloc(sizeof(*blob)); |
| if (blob == NULL) |
| return -1; |
| blob->data = (u8 *) buf; |
| blob->len = len; |
| buf = NULL; |
| blob->name = os_strdup(pac_file + 7); |
| if (blob->name == NULL) { |
| os_free(blob); |
| return -1; |
| } |
| eap_set_config_blob(sm, blob); |
| } else { |
| FILE *f; |
| f = fopen(pac_file, "wb"); |
| if (f == NULL) { |
| wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC " |
| "file '%s' for writing", pac_file); |
| return -1; |
| } |
| if (fwrite(buf, 1, len, f) != len) { |
| wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all " |
| "PACs into '%s'", pac_file); |
| fclose(f); |
| return -1; |
| } |
| os_free(buf); |
| fclose(f); |
| } |
| |
| return 0; |
| } |
| |
| |
| static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf, |
| char **pos, size_t *buf_len) |
| { |
| int ret; |
| |
| ret = os_snprintf(*pos, *buf + *buf_len - *pos, |
| "START\nPAC-Type=%d\n", pac->pac_type); |
| if (ret < 0 || ret >= *buf + *buf_len - *pos) |
| return -1; |
| |
| *pos += ret; |
| eap_fast_write(buf, pos, buf_len, "PAC-Key", |
| pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0); |
| eap_fast_write(buf, pos, buf_len, "PAC-Opaque", |
| pac->pac_opaque, pac->pac_opaque_len, 0); |
| eap_fast_write(buf, pos, buf_len, "PAC-Info", |
| pac->pac_info, pac->pac_info_len, 0); |
| eap_fast_write(buf, pos, buf_len, "A-ID", |
| pac->a_id, pac->a_id_len, 0); |
| eap_fast_write(buf, pos, buf_len, "I-ID", |
| pac->i_id, pac->i_id_len, 1); |
| eap_fast_write(buf, pos, buf_len, "A-ID-Info", |
| pac->a_id_info, pac->a_id_info_len, 1); |
| if (*buf == NULL) { |
| wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC " |
| "data"); |
| return -1; |
| } |
| ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n"); |
| if (ret < 0 || ret >= *buf + *buf_len - *pos) |
| return -1; |
| *pos += ret; |
| |
| return 0; |
| } |
| |
| |
| /** |
| * eap_fast_save_pac - Save PAC entries (text format) |
| * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() |
| * @pac_root: Root of the PAC list |
| * @pac_file: Name of the PAC file/blob |
| * Returns: 0 on success, -1 on failure |
| */ |
| int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root, |
| const char *pac_file) |
| { |
| struct eap_fast_pac *pac; |
| int ret, count = 0; |
| char *buf, *pos; |
| size_t buf_len; |
| |
| if (pac_file == NULL) |
| return -1; |
| |
| buf_len = 1024; |
| pos = buf = os_malloc(buf_len); |
| if (buf == NULL) |
| return -1; |
| |
| ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr); |
| if (ret < 0 || ret >= buf + buf_len - pos) { |
| os_free(buf); |
| return -1; |
| } |
| pos += ret; |
| |
| pac = pac_root; |
| while (pac) { |
| if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) { |
| os_free(buf); |
| return -1; |
| } |
| count++; |
| pac = pac->next; |
| } |
| |
| if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) { |
| os_free(buf); |
| return -1; |
| } |
| |
| wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'", |
| count, pac_file); |
| |
| return 0; |
| } |
| |
| |
| /** |
| * eap_fast_pac_list_truncate - Truncate a PAC list to the given length |
| * @pac_root: Root of the PAC list |
| * @max_len: Maximum length of the list (>= 1) |
| * Returns: Number of PAC entries removed |
| */ |
| size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root, |
| size_t max_len) |
| { |
| struct eap_fast_pac *pac, *prev; |
| size_t count; |
| |
| pac = pac_root; |
| prev = NULL; |
| count = 0; |
| |
| while (pac) { |
| count++; |
| if (count > max_len) |
| break; |
| prev = pac; |
| pac = pac->next; |
| } |
| |
| if (count <= max_len || prev == NULL) |
| return 0; |
| |
| count = 0; |
| prev->next = NULL; |
| |
| while (pac) { |
| prev = pac; |
| pac = pac->next; |
| eap_fast_free_pac(prev); |
| count++; |
| } |
| |
| return count; |
| } |
| |
| |
| static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac) |
| { |
| u8 *pos, *end; |
| u16 type, len; |
| |
| pos = pac->pac_info; |
| end = pos + pac->pac_info_len; |
| |
| while (pos + 4 < end) { |
| type = WPA_GET_BE16(pos); |
| pos += 2; |
| len = WPA_GET_BE16(pos); |
| pos += 2; |
| if (pos + len > end) |
| break; |
| |
| if (type == PAC_TYPE_A_ID) { |
| os_free(pac->a_id); |
| pac->a_id = os_malloc(len); |
| if (pac->a_id == NULL) |
| break; |
| os_memcpy(pac->a_id, pos, len); |
| pac->a_id_len = len; |
| } |
| |
| if (type == PAC_TYPE_A_ID_INFO) { |
| os_free(pac->a_id_info); |
| pac->a_id_info = os_malloc(len); |
| if (pac->a_id_info == NULL) |
| break; |
| os_memcpy(pac->a_id_info, pos, len); |
| pac->a_id_info_len = len; |
| } |
| |
| pos += len; |
| } |
| } |
| |
| |
| /** |
| * eap_fast_load_pac_bin - Load PAC entries (binary format) |
| * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() |
| * @pac_root: Pointer to root of the PAC list (to be filled) |
| * @pac_file: Name of the PAC file/blob to load |
| * Returns: 0 on success, -1 on failure |
| */ |
| int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, |
| const char *pac_file) |
| { |
| const struct wpa_config_blob *blob = NULL; |
| u8 *buf, *end, *pos; |
| size_t len, count = 0; |
| struct eap_fast_pac *pac, *prev; |
| |
| *pac_root = NULL; |
| |
| if (pac_file == NULL) |
| return -1; |
| |
| if (os_strncmp(pac_file, "blob://", 7) == 0) { |
| blob = eap_get_config_blob(sm, pac_file + 7); |
| if (blob == NULL) { |
| wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - " |
| "assume no PAC entries have been " |
| "provisioned", pac_file + 7); |
| return 0; |
| } |
| buf = blob->data; |
| len = blob->len; |
| } else { |
| buf = (u8 *) os_readfile(pac_file, &len); |
| if (buf == NULL) { |
| wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - " |
| "assume no PAC entries have been " |
| "provisioned", pac_file); |
| return 0; |
| } |
| } |
| |
| if (len == 0) { |
| if (blob == NULL) |
| os_free(buf); |
| return 0; |
| } |
| |
| if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC || |
| WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) { |
| wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)", |
| pac_file); |
| if (blob == NULL) |
| os_free(buf); |
| return -1; |
| } |
| |
| pac = prev = NULL; |
| pos = buf + 6; |
| end = buf + len; |
| while (pos < end) { |
| if (end - pos < 2 + 32 + 2 + 2) |
| goto parse_fail; |
| |
| pac = os_zalloc(sizeof(*pac)); |
| if (pac == NULL) |
| goto parse_fail; |
| |
| pac->pac_type = WPA_GET_BE16(pos); |
| pos += 2; |
| os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN); |
| pos += EAP_FAST_PAC_KEY_LEN; |
| pac->pac_opaque_len = WPA_GET_BE16(pos); |
| pos += 2; |
| if (pos + pac->pac_opaque_len + 2 > end) |
| goto parse_fail; |
| pac->pac_opaque = os_malloc(pac->pac_opaque_len); |
| if (pac->pac_opaque == NULL) |
| goto parse_fail; |
| os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len); |
| pos += pac->pac_opaque_len; |
| pac->pac_info_len = WPA_GET_BE16(pos); |
| pos += 2; |
| if (pos + pac->pac_info_len > end) |
| goto parse_fail; |
| pac->pac_info = os_malloc(pac->pac_info_len); |
| if (pac->pac_info == NULL) |
| goto parse_fail; |
| os_memcpy(pac->pac_info, pos, pac->pac_info_len); |
| pos += pac->pac_info_len; |
| eap_fast_pac_get_a_id(pac); |
| |
| count++; |
| if (prev) |
| prev->next = pac; |
| else |
| *pac_root = pac; |
| prev = pac; |
| } |
| |
| if (blob == NULL) |
| os_free(buf); |
| |
| wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)", |
| (unsigned long) count, pac_file); |
| |
| return 0; |
| |
| parse_fail: |
| wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)", |
| pac_file); |
| if (blob == NULL) |
| os_free(buf); |
| if (pac) |
| eap_fast_free_pac(pac); |
| return -1; |
| } |
| |
| |
| /** |
| * eap_fast_save_pac_bin - Save PAC entries (binary format) |
| * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() |
| * @pac_root: Root of the PAC list |
| * @pac_file: Name of the PAC file/blob |
| * Returns: 0 on success, -1 on failure |
| */ |
| int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root, |
| const char *pac_file) |
| { |
| size_t len, count = 0; |
| struct eap_fast_pac *pac; |
| u8 *buf, *pos; |
| |
| len = 6; |
| pac = pac_root; |
| while (pac) { |
| if (pac->pac_opaque_len > 65535 || |
| pac->pac_info_len > 65535) |
| return -1; |
| len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len + |
| 2 + pac->pac_info_len; |
| pac = pac->next; |
| } |
| |
| buf = os_malloc(len); |
| if (buf == NULL) |
| return -1; |
| |
| pos = buf; |
| WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC); |
| pos += 4; |
| WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION); |
| pos += 2; |
| |
| pac = pac_root; |
| while (pac) { |
| WPA_PUT_BE16(pos, pac->pac_type); |
| pos += 2; |
| os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN); |
| pos += EAP_FAST_PAC_KEY_LEN; |
| WPA_PUT_BE16(pos, pac->pac_opaque_len); |
| pos += 2; |
| os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len); |
| pos += pac->pac_opaque_len; |
| WPA_PUT_BE16(pos, pac->pac_info_len); |
| pos += 2; |
| os_memcpy(pos, pac->pac_info, pac->pac_info_len); |
| pos += pac->pac_info_len; |
| |
| pac = pac->next; |
| count++; |
| } |
| |
| if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) { |
| os_free(buf); |
| return -1; |
| } |
| |
| wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' " |
| "(bin)", (unsigned long) count, pac_file); |
| |
| return 0; |
| } |