fragattacks/src/eap_peer/eap_fast_pac.c
Jouni Malinen 364182a80f EAP-FAST: Clean up binary PAC file parser validation steps
This was too difficult for some static analyzers (CID 62876). In
addition, the pac_info_len assignment should really have explicitly
validated that there is room for the two octet length field instead of
trusting the following validation step to handle both this and the
actual pac_info_len bounds checking.

Signed-off-by: Jouni Malinen <j@w1.fi>
2014-11-23 21:03:40 +02:00

930 lines
21 KiB
C

/*
* 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)
{
if (!pos)
return "Cannot parse pac type";
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) {
u16 val;
if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 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;
val = WPA_GET_BE16(pos);
pos += 2;
if (val > end - pos)
goto parse_fail;
pac->pac_opaque_len = val;
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;
if (2 > end - pos)
goto parse_fail;
val = WPA_GET_BE16(pos);
pos += 2;
if (val > end - pos)
goto parse_fail;
pac->pac_info_len = val;
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;
}