139beb93cSSam Leffler /*
239beb93cSSam Leffler * EAP peer method: EAP-FAST PAC file processing
339beb93cSSam Leffler * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
739beb93cSSam Leffler */
839beb93cSSam Leffler
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler
1139beb93cSSam Leffler #include "common.h"
1239beb93cSSam Leffler #include "eap_config.h"
1339beb93cSSam Leffler #include "eap_i.h"
1439beb93cSSam Leffler #include "eap_fast_pac.h"
1539beb93cSSam Leffler
1639beb93cSSam Leffler /* TODO: encrypt PAC-Key in the PAC file */
1739beb93cSSam Leffler
1839beb93cSSam Leffler
1939beb93cSSam Leffler /* Text data format */
2039beb93cSSam Leffler static const char *pac_file_hdr =
2139beb93cSSam Leffler "wpa_supplicant EAP-FAST PAC file - version 1";
2239beb93cSSam Leffler
2339beb93cSSam Leffler /*
2439beb93cSSam Leffler * Binary data format
2539beb93cSSam Leffler * 4-octet magic value: 6A E4 92 0C
2639beb93cSSam Leffler * 2-octet version (big endian)
2739beb93cSSam Leffler * <version specific data>
2839beb93cSSam Leffler *
2939beb93cSSam Leffler * version=0:
3039beb93cSSam Leffler * Sequence of PAC entries:
3139beb93cSSam Leffler * 2-octet PAC-Type (big endian)
3239beb93cSSam Leffler * 32-octet PAC-Key
3339beb93cSSam Leffler * 2-octet PAC-Opaque length (big endian)
3439beb93cSSam Leffler * <variable len> PAC-Opaque data (length bytes)
3539beb93cSSam Leffler * 2-octet PAC-Info length (big endian)
3639beb93cSSam Leffler * <variable len> PAC-Info data (length bytes)
3739beb93cSSam Leffler */
3839beb93cSSam Leffler
3939beb93cSSam Leffler #define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c
4039beb93cSSam Leffler #define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0
4139beb93cSSam Leffler
4239beb93cSSam Leffler
4339beb93cSSam Leffler /**
4439beb93cSSam Leffler * eap_fast_free_pac - Free PAC data
4539beb93cSSam Leffler * @pac: Pointer to the PAC entry
4639beb93cSSam Leffler *
4739beb93cSSam Leffler * Note that the PAC entry must not be in a list since this function does not
4839beb93cSSam Leffler * remove the list links.
4939beb93cSSam Leffler */
eap_fast_free_pac(struct eap_fast_pac * pac)5039beb93cSSam Leffler void eap_fast_free_pac(struct eap_fast_pac *pac)
5139beb93cSSam Leffler {
5239beb93cSSam Leffler os_free(pac->pac_opaque);
5339beb93cSSam Leffler os_free(pac->pac_info);
5439beb93cSSam Leffler os_free(pac->a_id);
5539beb93cSSam Leffler os_free(pac->i_id);
5639beb93cSSam Leffler os_free(pac->a_id_info);
5739beb93cSSam Leffler os_free(pac);
5839beb93cSSam Leffler }
5939beb93cSSam Leffler
6039beb93cSSam Leffler
6139beb93cSSam Leffler /**
6239beb93cSSam Leffler * eap_fast_get_pac - Get a PAC entry based on A-ID
6339beb93cSSam Leffler * @pac_root: Pointer to root of the PAC list
6439beb93cSSam Leffler * @a_id: A-ID to search for
6539beb93cSSam Leffler * @a_id_len: Length of A-ID
6639beb93cSSam Leffler * @pac_type: PAC-Type to search for
6739beb93cSSam Leffler * Returns: Pointer to the PAC entry, or %NULL if A-ID not found
6839beb93cSSam Leffler */
eap_fast_get_pac(struct eap_fast_pac * pac_root,const u8 * a_id,size_t a_id_len,u16 pac_type)6939beb93cSSam Leffler struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
7039beb93cSSam Leffler const u8 *a_id, size_t a_id_len,
7139beb93cSSam Leffler u16 pac_type)
7239beb93cSSam Leffler {
7339beb93cSSam Leffler struct eap_fast_pac *pac = pac_root;
7439beb93cSSam Leffler
7539beb93cSSam Leffler while (pac) {
7639beb93cSSam Leffler if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
7739beb93cSSam Leffler os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
7839beb93cSSam Leffler return pac;
7939beb93cSSam Leffler }
8039beb93cSSam Leffler pac = pac->next;
8139beb93cSSam Leffler }
8239beb93cSSam Leffler return NULL;
8339beb93cSSam Leffler }
8439beb93cSSam Leffler
8539beb93cSSam Leffler
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)8639beb93cSSam Leffler static void eap_fast_remove_pac(struct eap_fast_pac **pac_root,
8739beb93cSSam Leffler struct eap_fast_pac **pac_current,
8839beb93cSSam Leffler const u8 *a_id, size_t a_id_len, u16 pac_type)
8939beb93cSSam Leffler {
9039beb93cSSam Leffler struct eap_fast_pac *pac, *prev;
9139beb93cSSam Leffler
9239beb93cSSam Leffler pac = *pac_root;
9339beb93cSSam Leffler prev = NULL;
9439beb93cSSam Leffler
9539beb93cSSam Leffler while (pac) {
9639beb93cSSam Leffler if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
9739beb93cSSam Leffler os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
9839beb93cSSam Leffler if (prev == NULL)
9939beb93cSSam Leffler *pac_root = pac->next;
10039beb93cSSam Leffler else
10139beb93cSSam Leffler prev->next = pac->next;
10239beb93cSSam Leffler if (*pac_current == pac)
10339beb93cSSam Leffler *pac_current = NULL;
10439beb93cSSam Leffler eap_fast_free_pac(pac);
10539beb93cSSam Leffler break;
10639beb93cSSam Leffler }
10739beb93cSSam Leffler prev = pac;
10839beb93cSSam Leffler pac = pac->next;
10939beb93cSSam Leffler }
11039beb93cSSam Leffler }
11139beb93cSSam Leffler
11239beb93cSSam Leffler
eap_fast_copy_buf(u8 ** dst,size_t * dst_len,const u8 * src,size_t src_len)11339beb93cSSam Leffler static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
11439beb93cSSam Leffler const u8 *src, size_t src_len)
11539beb93cSSam Leffler {
11639beb93cSSam Leffler if (src) {
117*85732ac8SCy Schubert *dst = os_memdup(src, src_len);
11839beb93cSSam Leffler if (*dst == NULL)
11939beb93cSSam Leffler return -1;
12039beb93cSSam Leffler *dst_len = src_len;
12139beb93cSSam Leffler }
12239beb93cSSam Leffler return 0;
12339beb93cSSam Leffler }
12439beb93cSSam Leffler
12539beb93cSSam Leffler
12639beb93cSSam Leffler /**
12739beb93cSSam Leffler * eap_fast_add_pac - Add a copy of a PAC entry to a list
12839beb93cSSam Leffler * @pac_root: Pointer to PAC list root pointer
12939beb93cSSam Leffler * @pac_current: Pointer to the current PAC pointer
13039beb93cSSam Leffler * @entry: New entry to clone and add to the list
13139beb93cSSam Leffler * Returns: 0 on success, -1 on failure
13239beb93cSSam Leffler *
13339beb93cSSam Leffler * This function makes a clone of the given PAC entry and adds this copied
13439beb93cSSam Leffler * entry to the list (pac_root). If an old entry for the same A-ID is found,
13539beb93cSSam Leffler * it will be removed from the PAC list and in this case, pac_current entry
13639beb93cSSam Leffler * is set to %NULL if it was the removed entry.
13739beb93cSSam Leffler */
eap_fast_add_pac(struct eap_fast_pac ** pac_root,struct eap_fast_pac ** pac_current,struct eap_fast_pac * entry)13839beb93cSSam Leffler int eap_fast_add_pac(struct eap_fast_pac **pac_root,
13939beb93cSSam Leffler struct eap_fast_pac **pac_current,
14039beb93cSSam Leffler struct eap_fast_pac *entry)
14139beb93cSSam Leffler {
14239beb93cSSam Leffler struct eap_fast_pac *pac;
14339beb93cSSam Leffler
14439beb93cSSam Leffler if (entry == NULL || entry->a_id == NULL)
14539beb93cSSam Leffler return -1;
14639beb93cSSam Leffler
14739beb93cSSam Leffler /* Remove a possible old entry for the matching A-ID. */
14839beb93cSSam Leffler eap_fast_remove_pac(pac_root, pac_current,
14939beb93cSSam Leffler entry->a_id, entry->a_id_len, entry->pac_type);
15039beb93cSSam Leffler
15139beb93cSSam Leffler /* Allocate a new entry and add it to the list of PACs. */
15239beb93cSSam Leffler pac = os_zalloc(sizeof(*pac));
15339beb93cSSam Leffler if (pac == NULL)
15439beb93cSSam Leffler return -1;
15539beb93cSSam Leffler
15639beb93cSSam Leffler pac->pac_type = entry->pac_type;
15739beb93cSSam Leffler os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
15839beb93cSSam Leffler if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
15939beb93cSSam Leffler entry->pac_opaque, entry->pac_opaque_len) < 0 ||
16039beb93cSSam Leffler eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
16139beb93cSSam Leffler entry->pac_info, entry->pac_info_len) < 0 ||
16239beb93cSSam Leffler eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
16339beb93cSSam Leffler entry->a_id, entry->a_id_len) < 0 ||
16439beb93cSSam Leffler eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
16539beb93cSSam Leffler entry->i_id, entry->i_id_len) < 0 ||
16639beb93cSSam Leffler eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
16739beb93cSSam Leffler entry->a_id_info, entry->a_id_info_len) < 0) {
16839beb93cSSam Leffler eap_fast_free_pac(pac);
16939beb93cSSam Leffler return -1;
17039beb93cSSam Leffler }
17139beb93cSSam Leffler
17239beb93cSSam Leffler pac->next = *pac_root;
17339beb93cSSam Leffler *pac_root = pac;
17439beb93cSSam Leffler
17539beb93cSSam Leffler return 0;
17639beb93cSSam Leffler }
17739beb93cSSam Leffler
17839beb93cSSam Leffler
17939beb93cSSam Leffler struct eap_fast_read_ctx {
18039beb93cSSam Leffler FILE *f;
18139beb93cSSam Leffler const char *pos;
18239beb93cSSam Leffler const char *end;
18339beb93cSSam Leffler int line;
18439beb93cSSam Leffler char *buf;
18539beb93cSSam Leffler size_t buf_len;
18639beb93cSSam Leffler };
18739beb93cSSam Leffler
eap_fast_read_line(struct eap_fast_read_ctx * rc,char ** value)18839beb93cSSam Leffler static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
18939beb93cSSam Leffler {
19039beb93cSSam Leffler char *pos;
19139beb93cSSam Leffler
19239beb93cSSam Leffler rc->line++;
19339beb93cSSam Leffler if (rc->f) {
19439beb93cSSam Leffler if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
19539beb93cSSam Leffler return -1;
19639beb93cSSam Leffler } else {
19739beb93cSSam Leffler const char *l_end;
19839beb93cSSam Leffler size_t len;
19939beb93cSSam Leffler if (rc->pos >= rc->end)
20039beb93cSSam Leffler return -1;
20139beb93cSSam Leffler l_end = rc->pos;
20239beb93cSSam Leffler while (l_end < rc->end && *l_end != '\n')
20339beb93cSSam Leffler l_end++;
20439beb93cSSam Leffler len = l_end - rc->pos;
20539beb93cSSam Leffler if (len >= rc->buf_len)
20639beb93cSSam Leffler len = rc->buf_len - 1;
20739beb93cSSam Leffler os_memcpy(rc->buf, rc->pos, len);
20839beb93cSSam Leffler rc->buf[len] = '\0';
20939beb93cSSam Leffler rc->pos = l_end + 1;
21039beb93cSSam Leffler }
21139beb93cSSam Leffler
21239beb93cSSam Leffler rc->buf[rc->buf_len - 1] = '\0';
21339beb93cSSam Leffler pos = rc->buf;
21439beb93cSSam Leffler while (*pos != '\0') {
21539beb93cSSam Leffler if (*pos == '\n' || *pos == '\r') {
21639beb93cSSam Leffler *pos = '\0';
21739beb93cSSam Leffler break;
21839beb93cSSam Leffler }
21939beb93cSSam Leffler pos++;
22039beb93cSSam Leffler }
22139beb93cSSam Leffler
22239beb93cSSam Leffler pos = os_strchr(rc->buf, '=');
22339beb93cSSam Leffler if (pos)
22439beb93cSSam Leffler *pos++ = '\0';
22539beb93cSSam Leffler *value = pos;
22639beb93cSSam Leffler
22739beb93cSSam Leffler return 0;
22839beb93cSSam Leffler }
22939beb93cSSam Leffler
23039beb93cSSam Leffler
eap_fast_parse_hex(const char * value,size_t * len)23139beb93cSSam Leffler static u8 * eap_fast_parse_hex(const char *value, size_t *len)
23239beb93cSSam Leffler {
23339beb93cSSam Leffler int hlen;
23439beb93cSSam Leffler u8 *buf;
23539beb93cSSam Leffler
23639beb93cSSam Leffler if (value == NULL)
23739beb93cSSam Leffler return NULL;
23839beb93cSSam Leffler hlen = os_strlen(value);
23939beb93cSSam Leffler if (hlen & 1)
24039beb93cSSam Leffler return NULL;
24139beb93cSSam Leffler *len = hlen / 2;
24239beb93cSSam Leffler buf = os_malloc(*len);
24339beb93cSSam Leffler if (buf == NULL)
24439beb93cSSam Leffler return NULL;
24539beb93cSSam Leffler if (hexstr2bin(value, buf, *len)) {
24639beb93cSSam Leffler os_free(buf);
24739beb93cSSam Leffler return NULL;
24839beb93cSSam Leffler }
24939beb93cSSam Leffler return buf;
25039beb93cSSam Leffler }
25139beb93cSSam Leffler
25239beb93cSSam Leffler
eap_fast_init_pac_data(struct eap_sm * sm,const char * pac_file,struct eap_fast_read_ctx * rc)25339beb93cSSam Leffler static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
25439beb93cSSam Leffler struct eap_fast_read_ctx *rc)
25539beb93cSSam Leffler {
25639beb93cSSam Leffler os_memset(rc, 0, sizeof(*rc));
25739beb93cSSam Leffler
25839beb93cSSam Leffler rc->buf_len = 2048;
25939beb93cSSam Leffler rc->buf = os_malloc(rc->buf_len);
26039beb93cSSam Leffler if (rc->buf == NULL)
26139beb93cSSam Leffler return -1;
26239beb93cSSam Leffler
26339beb93cSSam Leffler if (os_strncmp(pac_file, "blob://", 7) == 0) {
26439beb93cSSam Leffler const struct wpa_config_blob *blob;
26539beb93cSSam Leffler blob = eap_get_config_blob(sm, pac_file + 7);
26639beb93cSSam Leffler if (blob == NULL) {
26739beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
26839beb93cSSam Leffler "assume no PAC entries have been "
26939beb93cSSam Leffler "provisioned", pac_file + 7);
27039beb93cSSam Leffler os_free(rc->buf);
27139beb93cSSam Leffler return -1;
27239beb93cSSam Leffler }
27339beb93cSSam Leffler rc->pos = (char *) blob->data;
27439beb93cSSam Leffler rc->end = (char *) blob->data + blob->len;
27539beb93cSSam Leffler } else {
27639beb93cSSam Leffler rc->f = fopen(pac_file, "rb");
27739beb93cSSam Leffler if (rc->f == NULL) {
27839beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
27939beb93cSSam Leffler "assume no PAC entries have been "
28039beb93cSSam Leffler "provisioned", pac_file);
28139beb93cSSam Leffler os_free(rc->buf);
28239beb93cSSam Leffler return -1;
28339beb93cSSam Leffler }
28439beb93cSSam Leffler }
28539beb93cSSam Leffler
28639beb93cSSam Leffler return 0;
28739beb93cSSam Leffler }
28839beb93cSSam Leffler
28939beb93cSSam Leffler
eap_fast_deinit_pac_data(struct eap_fast_read_ctx * rc)29039beb93cSSam Leffler static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
29139beb93cSSam Leffler {
29239beb93cSSam Leffler os_free(rc->buf);
29339beb93cSSam Leffler if (rc->f)
29439beb93cSSam Leffler fclose(rc->f);
29539beb93cSSam Leffler }
29639beb93cSSam Leffler
29739beb93cSSam Leffler
eap_fast_parse_start(struct eap_fast_pac ** pac)29839beb93cSSam Leffler static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
29939beb93cSSam Leffler {
30039beb93cSSam Leffler if (*pac)
30139beb93cSSam Leffler return "START line without END";
30239beb93cSSam Leffler
30339beb93cSSam Leffler *pac = os_zalloc(sizeof(struct eap_fast_pac));
30439beb93cSSam Leffler if (*pac == NULL)
30539beb93cSSam Leffler return "No memory for PAC entry";
30639beb93cSSam Leffler (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
30739beb93cSSam Leffler return NULL;
30839beb93cSSam Leffler }
30939beb93cSSam Leffler
31039beb93cSSam Leffler
eap_fast_parse_end(struct eap_fast_pac ** pac_root,struct eap_fast_pac ** pac)31139beb93cSSam Leffler static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
31239beb93cSSam Leffler struct eap_fast_pac **pac)
31339beb93cSSam Leffler {
31439beb93cSSam Leffler if (*pac == NULL)
31539beb93cSSam Leffler return "END line without START";
31639beb93cSSam Leffler if (*pac_root) {
31739beb93cSSam Leffler struct eap_fast_pac *end = *pac_root;
31839beb93cSSam Leffler while (end->next)
31939beb93cSSam Leffler end = end->next;
32039beb93cSSam Leffler end->next = *pac;
32139beb93cSSam Leffler } else
32239beb93cSSam Leffler *pac_root = *pac;
32339beb93cSSam Leffler
32439beb93cSSam Leffler *pac = NULL;
32539beb93cSSam Leffler return NULL;
32639beb93cSSam Leffler }
32739beb93cSSam Leffler
32839beb93cSSam Leffler
eap_fast_parse_pac_type(struct eap_fast_pac * pac,char * pos)32939beb93cSSam Leffler static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
33039beb93cSSam Leffler char *pos)
33139beb93cSSam Leffler {
3325b9c547cSRui Paulo if (!pos)
3335b9c547cSRui Paulo return "Cannot parse pac type";
33439beb93cSSam Leffler pac->pac_type = atoi(pos);
33539beb93cSSam Leffler if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
33639beb93cSSam Leffler pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
33739beb93cSSam Leffler pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION)
33839beb93cSSam Leffler return "Unrecognized PAC-Type";
33939beb93cSSam Leffler
34039beb93cSSam Leffler return NULL;
34139beb93cSSam Leffler }
34239beb93cSSam Leffler
34339beb93cSSam Leffler
eap_fast_parse_pac_key(struct eap_fast_pac * pac,char * pos)34439beb93cSSam Leffler static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos)
34539beb93cSSam Leffler {
34639beb93cSSam Leffler u8 *key;
34739beb93cSSam Leffler size_t key_len;
34839beb93cSSam Leffler
34939beb93cSSam Leffler key = eap_fast_parse_hex(pos, &key_len);
35039beb93cSSam Leffler if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
35139beb93cSSam Leffler os_free(key);
35239beb93cSSam Leffler return "Invalid PAC-Key";
35339beb93cSSam Leffler }
35439beb93cSSam Leffler
35539beb93cSSam Leffler os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
35639beb93cSSam Leffler os_free(key);
35739beb93cSSam Leffler
35839beb93cSSam Leffler return NULL;
35939beb93cSSam Leffler }
36039beb93cSSam Leffler
36139beb93cSSam Leffler
eap_fast_parse_pac_opaque(struct eap_fast_pac * pac,char * pos)36239beb93cSSam Leffler static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
36339beb93cSSam Leffler char *pos)
36439beb93cSSam Leffler {
36539beb93cSSam Leffler os_free(pac->pac_opaque);
36639beb93cSSam Leffler pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
36739beb93cSSam Leffler if (pac->pac_opaque == NULL)
36839beb93cSSam Leffler return "Invalid PAC-Opaque";
36939beb93cSSam Leffler return NULL;
37039beb93cSSam Leffler }
37139beb93cSSam Leffler
37239beb93cSSam Leffler
eap_fast_parse_a_id(struct eap_fast_pac * pac,char * pos)37339beb93cSSam Leffler static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
37439beb93cSSam Leffler {
37539beb93cSSam Leffler os_free(pac->a_id);
37639beb93cSSam Leffler pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
37739beb93cSSam Leffler if (pac->a_id == NULL)
37839beb93cSSam Leffler return "Invalid A-ID";
37939beb93cSSam Leffler return NULL;
38039beb93cSSam Leffler }
38139beb93cSSam Leffler
38239beb93cSSam Leffler
eap_fast_parse_i_id(struct eap_fast_pac * pac,char * pos)38339beb93cSSam Leffler static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
38439beb93cSSam Leffler {
38539beb93cSSam Leffler os_free(pac->i_id);
38639beb93cSSam Leffler pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
38739beb93cSSam Leffler if (pac->i_id == NULL)
38839beb93cSSam Leffler return "Invalid I-ID";
38939beb93cSSam Leffler return NULL;
39039beb93cSSam Leffler }
39139beb93cSSam Leffler
39239beb93cSSam Leffler
eap_fast_parse_a_id_info(struct eap_fast_pac * pac,char * pos)39339beb93cSSam Leffler static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
39439beb93cSSam Leffler char *pos)
39539beb93cSSam Leffler {
39639beb93cSSam Leffler os_free(pac->a_id_info);
39739beb93cSSam Leffler pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
39839beb93cSSam Leffler if (pac->a_id_info == NULL)
39939beb93cSSam Leffler return "Invalid A-ID-Info";
40039beb93cSSam Leffler return NULL;
40139beb93cSSam Leffler }
40239beb93cSSam Leffler
40339beb93cSSam Leffler
40439beb93cSSam Leffler /**
40539beb93cSSam Leffler * eap_fast_load_pac - Load PAC entries (text format)
40639beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
40739beb93cSSam Leffler * @pac_root: Pointer to root of the PAC list (to be filled)
40839beb93cSSam Leffler * @pac_file: Name of the PAC file/blob to load
40939beb93cSSam Leffler * Returns: 0 on success, -1 on failure
41039beb93cSSam Leffler */
eap_fast_load_pac(struct eap_sm * sm,struct eap_fast_pac ** pac_root,const char * pac_file)41139beb93cSSam Leffler int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
41239beb93cSSam Leffler const char *pac_file)
41339beb93cSSam Leffler {
41439beb93cSSam Leffler struct eap_fast_read_ctx rc;
41539beb93cSSam Leffler struct eap_fast_pac *pac = NULL;
41639beb93cSSam Leffler int count = 0;
41739beb93cSSam Leffler char *pos;
41839beb93cSSam Leffler const char *err = NULL;
41939beb93cSSam Leffler
42039beb93cSSam Leffler if (pac_file == NULL)
42139beb93cSSam Leffler return -1;
42239beb93cSSam Leffler
42339beb93cSSam Leffler if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
42439beb93cSSam Leffler return 0;
42539beb93cSSam Leffler
426f05cddf9SRui Paulo if (eap_fast_read_line(&rc, &pos) < 0) {
427f05cddf9SRui Paulo /* empty file - assume it is fine to overwrite */
428f05cddf9SRui Paulo eap_fast_deinit_pac_data(&rc);
429f05cddf9SRui Paulo return 0;
430f05cddf9SRui Paulo }
431f05cddf9SRui Paulo if (os_strcmp(pac_file_hdr, rc.buf) != 0)
43239beb93cSSam Leffler err = "Unrecognized header line";
43339beb93cSSam Leffler
43439beb93cSSam Leffler while (!err && eap_fast_read_line(&rc, &pos) == 0) {
43539beb93cSSam Leffler if (os_strcmp(rc.buf, "START") == 0)
43639beb93cSSam Leffler err = eap_fast_parse_start(&pac);
43739beb93cSSam Leffler else if (os_strcmp(rc.buf, "END") == 0) {
43839beb93cSSam Leffler err = eap_fast_parse_end(pac_root, &pac);
43939beb93cSSam Leffler count++;
44039beb93cSSam Leffler } else if (!pac)
44139beb93cSSam Leffler err = "Unexpected line outside START/END block";
44239beb93cSSam Leffler else if (os_strcmp(rc.buf, "PAC-Type") == 0)
44339beb93cSSam Leffler err = eap_fast_parse_pac_type(pac, pos);
44439beb93cSSam Leffler else if (os_strcmp(rc.buf, "PAC-Key") == 0)
44539beb93cSSam Leffler err = eap_fast_parse_pac_key(pac, pos);
44639beb93cSSam Leffler else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
44739beb93cSSam Leffler err = eap_fast_parse_pac_opaque(pac, pos);
44839beb93cSSam Leffler else if (os_strcmp(rc.buf, "A-ID") == 0)
44939beb93cSSam Leffler err = eap_fast_parse_a_id(pac, pos);
45039beb93cSSam Leffler else if (os_strcmp(rc.buf, "I-ID") == 0)
45139beb93cSSam Leffler err = eap_fast_parse_i_id(pac, pos);
45239beb93cSSam Leffler else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
45339beb93cSSam Leffler err = eap_fast_parse_a_id_info(pac, pos);
45439beb93cSSam Leffler }
45539beb93cSSam Leffler
45639beb93cSSam Leffler if (pac) {
457780fb4a2SCy Schubert if (!err)
45839beb93cSSam Leffler err = "PAC block not terminated with END";
45939beb93cSSam Leffler eap_fast_free_pac(pac);
46039beb93cSSam Leffler }
46139beb93cSSam Leffler
46239beb93cSSam Leffler eap_fast_deinit_pac_data(&rc);
46339beb93cSSam Leffler
46439beb93cSSam Leffler if (err) {
46539beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'",
46639beb93cSSam Leffler err, pac_file, rc.line);
46739beb93cSSam Leffler return -1;
46839beb93cSSam Leffler }
46939beb93cSSam Leffler
47039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
47139beb93cSSam Leffler count, pac_file);
47239beb93cSSam Leffler
47339beb93cSSam Leffler return 0;
47439beb93cSSam Leffler }
47539beb93cSSam Leffler
47639beb93cSSam Leffler
eap_fast_write(char ** buf,char ** pos,size_t * buf_len,const char * field,const u8 * data,size_t len,int txt)47739beb93cSSam Leffler static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
47839beb93cSSam Leffler const char *field, const u8 *data,
47939beb93cSSam Leffler size_t len, int txt)
48039beb93cSSam Leffler {
48139beb93cSSam Leffler size_t i, need;
48239beb93cSSam Leffler int ret;
483e28a4053SRui Paulo char *end;
48439beb93cSSam Leffler
485e28a4053SRui Paulo if (data == NULL || buf == NULL || *buf == NULL ||
486e28a4053SRui Paulo pos == NULL || *pos == NULL || *pos < *buf)
48739beb93cSSam Leffler return;
48839beb93cSSam Leffler
48939beb93cSSam Leffler need = os_strlen(field) + len * 2 + 30;
49039beb93cSSam Leffler if (txt)
49139beb93cSSam Leffler need += os_strlen(field) + len + 20;
49239beb93cSSam Leffler
49339beb93cSSam Leffler if (*pos - *buf + need > *buf_len) {
49439beb93cSSam Leffler char *nbuf = os_realloc(*buf, *buf_len + need);
49539beb93cSSam Leffler if (nbuf == NULL) {
49639beb93cSSam Leffler os_free(*buf);
49739beb93cSSam Leffler *buf = NULL;
49839beb93cSSam Leffler return;
49939beb93cSSam Leffler }
500f05cddf9SRui Paulo *pos = nbuf + (*pos - *buf);
50139beb93cSSam Leffler *buf = nbuf;
50239beb93cSSam Leffler *buf_len += need;
50339beb93cSSam Leffler }
504e28a4053SRui Paulo end = *buf + *buf_len;
50539beb93cSSam Leffler
506e28a4053SRui Paulo ret = os_snprintf(*pos, end - *pos, "%s=", field);
5075b9c547cSRui Paulo if (os_snprintf_error(end - *pos, ret))
50839beb93cSSam Leffler return;
50939beb93cSSam Leffler *pos += ret;
510e28a4053SRui Paulo *pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
511e28a4053SRui Paulo ret = os_snprintf(*pos, end - *pos, "\n");
5125b9c547cSRui Paulo if (os_snprintf_error(end - *pos, ret))
51339beb93cSSam Leffler return;
51439beb93cSSam Leffler *pos += ret;
51539beb93cSSam Leffler
51639beb93cSSam Leffler if (txt) {
517e28a4053SRui Paulo ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
5185b9c547cSRui Paulo if (os_snprintf_error(end - *pos, ret))
51939beb93cSSam Leffler return;
52039beb93cSSam Leffler *pos += ret;
52139beb93cSSam Leffler for (i = 0; i < len; i++) {
522e28a4053SRui Paulo ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
5235b9c547cSRui Paulo if (os_snprintf_error(end - *pos, ret))
52439beb93cSSam Leffler return;
52539beb93cSSam Leffler *pos += ret;
52639beb93cSSam Leffler }
527e28a4053SRui Paulo ret = os_snprintf(*pos, end - *pos, "\n");
5285b9c547cSRui Paulo if (os_snprintf_error(end - *pos, ret))
52939beb93cSSam Leffler return;
53039beb93cSSam Leffler *pos += ret;
53139beb93cSSam Leffler }
53239beb93cSSam Leffler }
53339beb93cSSam Leffler
53439beb93cSSam Leffler
eap_fast_write_pac(struct eap_sm * sm,const char * pac_file,char * buf,size_t len)53539beb93cSSam Leffler static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
53639beb93cSSam Leffler char *buf, size_t len)
53739beb93cSSam Leffler {
53839beb93cSSam Leffler if (os_strncmp(pac_file, "blob://", 7) == 0) {
53939beb93cSSam Leffler struct wpa_config_blob *blob;
54039beb93cSSam Leffler blob = os_zalloc(sizeof(*blob));
54139beb93cSSam Leffler if (blob == NULL)
54239beb93cSSam Leffler return -1;
54339beb93cSSam Leffler blob->data = (u8 *) buf;
54439beb93cSSam Leffler blob->len = len;
54539beb93cSSam Leffler buf = NULL;
54639beb93cSSam Leffler blob->name = os_strdup(pac_file + 7);
54739beb93cSSam Leffler if (blob->name == NULL) {
54839beb93cSSam Leffler os_free(blob);
54939beb93cSSam Leffler return -1;
55039beb93cSSam Leffler }
55139beb93cSSam Leffler eap_set_config_blob(sm, blob);
55239beb93cSSam Leffler } else {
55339beb93cSSam Leffler FILE *f;
55439beb93cSSam Leffler f = fopen(pac_file, "wb");
55539beb93cSSam Leffler if (f == NULL) {
55639beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC "
55739beb93cSSam Leffler "file '%s' for writing", pac_file);
55839beb93cSSam Leffler return -1;
55939beb93cSSam Leffler }
56039beb93cSSam Leffler if (fwrite(buf, 1, len, f) != len) {
56139beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all "
56239beb93cSSam Leffler "PACs into '%s'", pac_file);
56339beb93cSSam Leffler fclose(f);
56439beb93cSSam Leffler return -1;
56539beb93cSSam Leffler }
56639beb93cSSam Leffler os_free(buf);
56739beb93cSSam Leffler fclose(f);
56839beb93cSSam Leffler }
56939beb93cSSam Leffler
57039beb93cSSam Leffler return 0;
57139beb93cSSam Leffler }
57239beb93cSSam Leffler
57339beb93cSSam Leffler
eap_fast_add_pac_data(struct eap_fast_pac * pac,char ** buf,char ** pos,size_t * buf_len)57439beb93cSSam Leffler static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
57539beb93cSSam Leffler char **pos, size_t *buf_len)
57639beb93cSSam Leffler {
57739beb93cSSam Leffler int ret;
57839beb93cSSam Leffler
57939beb93cSSam Leffler ret = os_snprintf(*pos, *buf + *buf_len - *pos,
58039beb93cSSam Leffler "START\nPAC-Type=%d\n", pac->pac_type);
5815b9c547cSRui Paulo if (os_snprintf_error(*buf + *buf_len - *pos, ret))
58239beb93cSSam Leffler return -1;
58339beb93cSSam Leffler
58439beb93cSSam Leffler *pos += ret;
58539beb93cSSam Leffler eap_fast_write(buf, pos, buf_len, "PAC-Key",
58639beb93cSSam Leffler pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
58739beb93cSSam Leffler eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
58839beb93cSSam Leffler pac->pac_opaque, pac->pac_opaque_len, 0);
58939beb93cSSam Leffler eap_fast_write(buf, pos, buf_len, "PAC-Info",
59039beb93cSSam Leffler pac->pac_info, pac->pac_info_len, 0);
59139beb93cSSam Leffler eap_fast_write(buf, pos, buf_len, "A-ID",
59239beb93cSSam Leffler pac->a_id, pac->a_id_len, 0);
59339beb93cSSam Leffler eap_fast_write(buf, pos, buf_len, "I-ID",
59439beb93cSSam Leffler pac->i_id, pac->i_id_len, 1);
59539beb93cSSam Leffler eap_fast_write(buf, pos, buf_len, "A-ID-Info",
59639beb93cSSam Leffler pac->a_id_info, pac->a_id_info_len, 1);
59739beb93cSSam Leffler if (*buf == NULL) {
59839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
59939beb93cSSam Leffler "data");
60039beb93cSSam Leffler return -1;
60139beb93cSSam Leffler }
60239beb93cSSam Leffler ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
6035b9c547cSRui Paulo if (os_snprintf_error(*buf + *buf_len - *pos, ret))
60439beb93cSSam Leffler return -1;
60539beb93cSSam Leffler *pos += ret;
60639beb93cSSam Leffler
60739beb93cSSam Leffler return 0;
60839beb93cSSam Leffler }
60939beb93cSSam Leffler
61039beb93cSSam Leffler
61139beb93cSSam Leffler /**
61239beb93cSSam Leffler * eap_fast_save_pac - Save PAC entries (text format)
61339beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
61439beb93cSSam Leffler * @pac_root: Root of the PAC list
61539beb93cSSam Leffler * @pac_file: Name of the PAC file/blob
61639beb93cSSam Leffler * Returns: 0 on success, -1 on failure
61739beb93cSSam Leffler */
eap_fast_save_pac(struct eap_sm * sm,struct eap_fast_pac * pac_root,const char * pac_file)61839beb93cSSam Leffler int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
61939beb93cSSam Leffler const char *pac_file)
62039beb93cSSam Leffler {
62139beb93cSSam Leffler struct eap_fast_pac *pac;
62239beb93cSSam Leffler int ret, count = 0;
62339beb93cSSam Leffler char *buf, *pos;
62439beb93cSSam Leffler size_t buf_len;
62539beb93cSSam Leffler
62639beb93cSSam Leffler if (pac_file == NULL)
62739beb93cSSam Leffler return -1;
62839beb93cSSam Leffler
62939beb93cSSam Leffler buf_len = 1024;
63039beb93cSSam Leffler pos = buf = os_malloc(buf_len);
63139beb93cSSam Leffler if (buf == NULL)
63239beb93cSSam Leffler return -1;
63339beb93cSSam Leffler
63439beb93cSSam Leffler ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
6355b9c547cSRui Paulo if (os_snprintf_error(buf + buf_len - pos, ret)) {
63639beb93cSSam Leffler os_free(buf);
63739beb93cSSam Leffler return -1;
63839beb93cSSam Leffler }
63939beb93cSSam Leffler pos += ret;
64039beb93cSSam Leffler
64139beb93cSSam Leffler pac = pac_root;
64239beb93cSSam Leffler while (pac) {
64339beb93cSSam Leffler if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
64439beb93cSSam Leffler os_free(buf);
64539beb93cSSam Leffler return -1;
64639beb93cSSam Leffler }
64739beb93cSSam Leffler count++;
64839beb93cSSam Leffler pac = pac->next;
64939beb93cSSam Leffler }
65039beb93cSSam Leffler
65139beb93cSSam Leffler if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
65239beb93cSSam Leffler os_free(buf);
65339beb93cSSam Leffler return -1;
65439beb93cSSam Leffler }
65539beb93cSSam Leffler
65639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
65739beb93cSSam Leffler count, pac_file);
65839beb93cSSam Leffler
65939beb93cSSam Leffler return 0;
66039beb93cSSam Leffler }
66139beb93cSSam Leffler
66239beb93cSSam Leffler
66339beb93cSSam Leffler /**
66439beb93cSSam Leffler * eap_fast_pac_list_truncate - Truncate a PAC list to the given length
66539beb93cSSam Leffler * @pac_root: Root of the PAC list
66639beb93cSSam Leffler * @max_len: Maximum length of the list (>= 1)
66739beb93cSSam Leffler * Returns: Number of PAC entries removed
66839beb93cSSam Leffler */
eap_fast_pac_list_truncate(struct eap_fast_pac * pac_root,size_t max_len)66939beb93cSSam Leffler size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
67039beb93cSSam Leffler size_t max_len)
67139beb93cSSam Leffler {
67239beb93cSSam Leffler struct eap_fast_pac *pac, *prev;
67339beb93cSSam Leffler size_t count;
67439beb93cSSam Leffler
67539beb93cSSam Leffler pac = pac_root;
67639beb93cSSam Leffler prev = NULL;
67739beb93cSSam Leffler count = 0;
67839beb93cSSam Leffler
67939beb93cSSam Leffler while (pac) {
68039beb93cSSam Leffler count++;
68139beb93cSSam Leffler if (count > max_len)
68239beb93cSSam Leffler break;
68339beb93cSSam Leffler prev = pac;
68439beb93cSSam Leffler pac = pac->next;
68539beb93cSSam Leffler }
68639beb93cSSam Leffler
68739beb93cSSam Leffler if (count <= max_len || prev == NULL)
68839beb93cSSam Leffler return 0;
68939beb93cSSam Leffler
69039beb93cSSam Leffler count = 0;
69139beb93cSSam Leffler prev->next = NULL;
69239beb93cSSam Leffler
69339beb93cSSam Leffler while (pac) {
69439beb93cSSam Leffler prev = pac;
69539beb93cSSam Leffler pac = pac->next;
69639beb93cSSam Leffler eap_fast_free_pac(prev);
69739beb93cSSam Leffler count++;
69839beb93cSSam Leffler }
69939beb93cSSam Leffler
70039beb93cSSam Leffler return count;
70139beb93cSSam Leffler }
70239beb93cSSam Leffler
70339beb93cSSam Leffler
eap_fast_pac_get_a_id(struct eap_fast_pac * pac)70439beb93cSSam Leffler static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
70539beb93cSSam Leffler {
70639beb93cSSam Leffler u8 *pos, *end;
70739beb93cSSam Leffler u16 type, len;
70839beb93cSSam Leffler
70939beb93cSSam Leffler pos = pac->pac_info;
71039beb93cSSam Leffler end = pos + pac->pac_info_len;
71139beb93cSSam Leffler
712780fb4a2SCy Schubert while (end - pos > 4) {
71339beb93cSSam Leffler type = WPA_GET_BE16(pos);
71439beb93cSSam Leffler pos += 2;
71539beb93cSSam Leffler len = WPA_GET_BE16(pos);
71639beb93cSSam Leffler pos += 2;
7175b9c547cSRui Paulo if (len > (unsigned int) (end - pos))
71839beb93cSSam Leffler break;
71939beb93cSSam Leffler
72039beb93cSSam Leffler if (type == PAC_TYPE_A_ID) {
72139beb93cSSam Leffler os_free(pac->a_id);
722*85732ac8SCy Schubert pac->a_id = os_memdup(pos, len);
72339beb93cSSam Leffler if (pac->a_id == NULL)
72439beb93cSSam Leffler break;
72539beb93cSSam Leffler pac->a_id_len = len;
72639beb93cSSam Leffler }
72739beb93cSSam Leffler
72839beb93cSSam Leffler if (type == PAC_TYPE_A_ID_INFO) {
72939beb93cSSam Leffler os_free(pac->a_id_info);
730*85732ac8SCy Schubert pac->a_id_info = os_memdup(pos, len);
73139beb93cSSam Leffler if (pac->a_id_info == NULL)
73239beb93cSSam Leffler break;
73339beb93cSSam Leffler pac->a_id_info_len = len;
73439beb93cSSam Leffler }
73539beb93cSSam Leffler
73639beb93cSSam Leffler pos += len;
73739beb93cSSam Leffler }
73839beb93cSSam Leffler }
73939beb93cSSam Leffler
74039beb93cSSam Leffler
74139beb93cSSam Leffler /**
74239beb93cSSam Leffler * eap_fast_load_pac_bin - Load PAC entries (binary format)
74339beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
74439beb93cSSam Leffler * @pac_root: Pointer to root of the PAC list (to be filled)
74539beb93cSSam Leffler * @pac_file: Name of the PAC file/blob to load
74639beb93cSSam Leffler * Returns: 0 on success, -1 on failure
74739beb93cSSam Leffler */
eap_fast_load_pac_bin(struct eap_sm * sm,struct eap_fast_pac ** pac_root,const char * pac_file)74839beb93cSSam Leffler int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
74939beb93cSSam Leffler const char *pac_file)
75039beb93cSSam Leffler {
75139beb93cSSam Leffler const struct wpa_config_blob *blob = NULL;
75239beb93cSSam Leffler u8 *buf, *end, *pos;
75339beb93cSSam Leffler size_t len, count = 0;
75439beb93cSSam Leffler struct eap_fast_pac *pac, *prev;
75539beb93cSSam Leffler
75639beb93cSSam Leffler *pac_root = NULL;
75739beb93cSSam Leffler
75839beb93cSSam Leffler if (pac_file == NULL)
75939beb93cSSam Leffler return -1;
76039beb93cSSam Leffler
76139beb93cSSam Leffler if (os_strncmp(pac_file, "blob://", 7) == 0) {
76239beb93cSSam Leffler blob = eap_get_config_blob(sm, pac_file + 7);
76339beb93cSSam Leffler if (blob == NULL) {
76439beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
76539beb93cSSam Leffler "assume no PAC entries have been "
76639beb93cSSam Leffler "provisioned", pac_file + 7);
76739beb93cSSam Leffler return 0;
76839beb93cSSam Leffler }
76939beb93cSSam Leffler buf = blob->data;
77039beb93cSSam Leffler len = blob->len;
77139beb93cSSam Leffler } else {
77239beb93cSSam Leffler buf = (u8 *) os_readfile(pac_file, &len);
77339beb93cSSam Leffler if (buf == NULL) {
77439beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
77539beb93cSSam Leffler "assume no PAC entries have been "
77639beb93cSSam Leffler "provisioned", pac_file);
77739beb93cSSam Leffler return 0;
77839beb93cSSam Leffler }
77939beb93cSSam Leffler }
78039beb93cSSam Leffler
78139beb93cSSam Leffler if (len == 0) {
78239beb93cSSam Leffler if (blob == NULL)
78339beb93cSSam Leffler os_free(buf);
78439beb93cSSam Leffler return 0;
78539beb93cSSam Leffler }
78639beb93cSSam Leffler
78739beb93cSSam Leffler if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC ||
78839beb93cSSam Leffler WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) {
78939beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)",
79039beb93cSSam Leffler pac_file);
79139beb93cSSam Leffler if (blob == NULL)
79239beb93cSSam Leffler os_free(buf);
79339beb93cSSam Leffler return -1;
79439beb93cSSam Leffler }
79539beb93cSSam Leffler
79639beb93cSSam Leffler pac = prev = NULL;
79739beb93cSSam Leffler pos = buf + 6;
79839beb93cSSam Leffler end = buf + len;
79939beb93cSSam Leffler while (pos < end) {
8005b9c547cSRui Paulo u16 val;
8015b9c547cSRui Paulo
802780fb4a2SCy Schubert if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) {
803780fb4a2SCy Schubert pac = NULL;
80439beb93cSSam Leffler goto parse_fail;
805780fb4a2SCy Schubert }
80639beb93cSSam Leffler
80739beb93cSSam Leffler pac = os_zalloc(sizeof(*pac));
80839beb93cSSam Leffler if (pac == NULL)
80939beb93cSSam Leffler goto parse_fail;
81039beb93cSSam Leffler
81139beb93cSSam Leffler pac->pac_type = WPA_GET_BE16(pos);
81239beb93cSSam Leffler pos += 2;
81339beb93cSSam Leffler os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
81439beb93cSSam Leffler pos += EAP_FAST_PAC_KEY_LEN;
8155b9c547cSRui Paulo val = WPA_GET_BE16(pos);
81639beb93cSSam Leffler pos += 2;
8175b9c547cSRui Paulo if (val > end - pos)
81839beb93cSSam Leffler goto parse_fail;
8195b9c547cSRui Paulo pac->pac_opaque_len = val;
820*85732ac8SCy Schubert pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len);
82139beb93cSSam Leffler if (pac->pac_opaque == NULL)
82239beb93cSSam Leffler goto parse_fail;
82339beb93cSSam Leffler pos += pac->pac_opaque_len;
8245b9c547cSRui Paulo if (2 > end - pos)
82539beb93cSSam Leffler goto parse_fail;
8265b9c547cSRui Paulo val = WPA_GET_BE16(pos);
8275b9c547cSRui Paulo pos += 2;
8285b9c547cSRui Paulo if (val > end - pos)
8295b9c547cSRui Paulo goto parse_fail;
8305b9c547cSRui Paulo pac->pac_info_len = val;
831*85732ac8SCy Schubert pac->pac_info = os_memdup(pos, pac->pac_info_len);
83239beb93cSSam Leffler if (pac->pac_info == NULL)
83339beb93cSSam Leffler goto parse_fail;
83439beb93cSSam Leffler pos += pac->pac_info_len;
83539beb93cSSam Leffler eap_fast_pac_get_a_id(pac);
83639beb93cSSam Leffler
83739beb93cSSam Leffler count++;
83839beb93cSSam Leffler if (prev)
83939beb93cSSam Leffler prev->next = pac;
84039beb93cSSam Leffler else
84139beb93cSSam Leffler *pac_root = pac;
84239beb93cSSam Leffler prev = pac;
84339beb93cSSam Leffler }
84439beb93cSSam Leffler
84539beb93cSSam Leffler if (blob == NULL)
84639beb93cSSam Leffler os_free(buf);
84739beb93cSSam Leffler
84839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)",
84939beb93cSSam Leffler (unsigned long) count, pac_file);
85039beb93cSSam Leffler
85139beb93cSSam Leffler return 0;
85239beb93cSSam Leffler
85339beb93cSSam Leffler parse_fail:
85439beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)",
85539beb93cSSam Leffler pac_file);
85639beb93cSSam Leffler if (blob == NULL)
85739beb93cSSam Leffler os_free(buf);
85839beb93cSSam Leffler if (pac)
85939beb93cSSam Leffler eap_fast_free_pac(pac);
86039beb93cSSam Leffler return -1;
86139beb93cSSam Leffler }
86239beb93cSSam Leffler
86339beb93cSSam Leffler
86439beb93cSSam Leffler /**
86539beb93cSSam Leffler * eap_fast_save_pac_bin - Save PAC entries (binary format)
86639beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
86739beb93cSSam Leffler * @pac_root: Root of the PAC list
86839beb93cSSam Leffler * @pac_file: Name of the PAC file/blob
86939beb93cSSam Leffler * Returns: 0 on success, -1 on failure
87039beb93cSSam Leffler */
eap_fast_save_pac_bin(struct eap_sm * sm,struct eap_fast_pac * pac_root,const char * pac_file)87139beb93cSSam Leffler int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
87239beb93cSSam Leffler const char *pac_file)
87339beb93cSSam Leffler {
87439beb93cSSam Leffler size_t len, count = 0;
87539beb93cSSam Leffler struct eap_fast_pac *pac;
87639beb93cSSam Leffler u8 *buf, *pos;
87739beb93cSSam Leffler
87839beb93cSSam Leffler len = 6;
87939beb93cSSam Leffler pac = pac_root;
88039beb93cSSam Leffler while (pac) {
88139beb93cSSam Leffler if (pac->pac_opaque_len > 65535 ||
88239beb93cSSam Leffler pac->pac_info_len > 65535)
88339beb93cSSam Leffler return -1;
88439beb93cSSam Leffler len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
88539beb93cSSam Leffler 2 + pac->pac_info_len;
88639beb93cSSam Leffler pac = pac->next;
88739beb93cSSam Leffler }
88839beb93cSSam Leffler
88939beb93cSSam Leffler buf = os_malloc(len);
89039beb93cSSam Leffler if (buf == NULL)
89139beb93cSSam Leffler return -1;
89239beb93cSSam Leffler
89339beb93cSSam Leffler pos = buf;
89439beb93cSSam Leffler WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
89539beb93cSSam Leffler pos += 4;
89639beb93cSSam Leffler WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
89739beb93cSSam Leffler pos += 2;
89839beb93cSSam Leffler
89939beb93cSSam Leffler pac = pac_root;
90039beb93cSSam Leffler while (pac) {
90139beb93cSSam Leffler WPA_PUT_BE16(pos, pac->pac_type);
90239beb93cSSam Leffler pos += 2;
90339beb93cSSam Leffler os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
90439beb93cSSam Leffler pos += EAP_FAST_PAC_KEY_LEN;
90539beb93cSSam Leffler WPA_PUT_BE16(pos, pac->pac_opaque_len);
90639beb93cSSam Leffler pos += 2;
90739beb93cSSam Leffler os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
90839beb93cSSam Leffler pos += pac->pac_opaque_len;
90939beb93cSSam Leffler WPA_PUT_BE16(pos, pac->pac_info_len);
91039beb93cSSam Leffler pos += 2;
91139beb93cSSam Leffler os_memcpy(pos, pac->pac_info, pac->pac_info_len);
91239beb93cSSam Leffler pos += pac->pac_info_len;
91339beb93cSSam Leffler
91439beb93cSSam Leffler pac = pac->next;
91539beb93cSSam Leffler count++;
91639beb93cSSam Leffler }
91739beb93cSSam Leffler
91839beb93cSSam Leffler if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
91939beb93cSSam Leffler os_free(buf);
92039beb93cSSam Leffler return -1;
92139beb93cSSam Leffler }
92239beb93cSSam Leffler
92339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' "
92439beb93cSSam Leffler "(bin)", (unsigned long) count, pac_file);
92539beb93cSSam Leffler
92639beb93cSSam Leffler return 0;
92739beb93cSSam Leffler }
928