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