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