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 18 : void eap_fast_free_pac(struct eap_fast_pac *pac)
51 : {
52 18 : os_free(pac->pac_opaque);
53 18 : os_free(pac->pac_info);
54 18 : os_free(pac->a_id);
55 18 : os_free(pac->i_id);
56 18 : os_free(pac->a_id_info);
57 18 : os_free(pac);
58 18 : }
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 27 : 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 27 : struct eap_fast_pac *pac = pac_root;
74 :
75 54 : while (pac) {
76 18 : if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
77 9 : os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
78 9 : return pac;
79 : }
80 0 : pac = pac->next;
81 : }
82 18 : return NULL;
83 : }
84 :
85 :
86 9 : 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 9 : pac = *pac_root;
93 9 : prev = NULL;
94 :
95 18 : 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 9 : }
111 :
112 :
113 45 : static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
114 : const u8 *src, size_t src_len)
115 : {
116 45 : if (src) {
117 45 : *dst = os_malloc(src_len);
118 45 : if (*dst == NULL)
119 0 : return -1;
120 45 : os_memcpy(*dst, src, src_len);
121 45 : *dst_len = src_len;
122 : }
123 45 : 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 9 : 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 9 : if (entry == NULL || entry->a_id == NULL)
146 0 : return -1;
147 :
148 : /* Remove a possible old entry for the matching A-ID. */
149 18 : eap_fast_remove_pac(pac_root, pac_current,
150 18 : 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 9 : pac = os_zalloc(sizeof(*pac));
154 9 : if (pac == NULL)
155 0 : return -1;
156 :
157 9 : pac->pac_type = entry->pac_type;
158 9 : os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
159 18 : if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
160 18 : entry->pac_opaque, entry->pac_opaque_len) < 0 ||
161 18 : eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
162 18 : entry->pac_info, entry->pac_info_len) < 0 ||
163 18 : eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
164 18 : entry->a_id, entry->a_id_len) < 0 ||
165 18 : eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
166 18 : entry->i_id, entry->i_id_len) < 0 ||
167 18 : eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
168 9 : entry->a_id_info, entry->a_id_info_len) < 0) {
169 0 : eap_fast_free_pac(pac);
170 0 : return -1;
171 : }
172 :
173 9 : pac->next = *pac_root;
174 9 : *pac_root = pac;
175 :
176 9 : 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 65 : static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
190 : {
191 : char *pos;
192 :
193 65 : rc->line++;
194 65 : if (rc->f) {
195 26 : if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
196 2 : 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 1572 : 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 60 : rc->buf[rc->buf_len - 1] = '\0';
214 60 : pos = rc->buf;
215 2620 : while (*pos != '\0') {
216 2524 : if (*pos == '\n' || *pos == '\r') {
217 24 : *pos = '\0';
218 24 : break;
219 : }
220 2500 : pos++;
221 : }
222 :
223 60 : pos = os_strchr(rc->buf, '=');
224 60 : if (pos)
225 45 : *pos++ = '\0';
226 60 : *value = pos;
227 :
228 60 : return 0;
229 : }
230 :
231 :
232 25 : static u8 * eap_fast_parse_hex(const char *value, size_t *len)
233 : {
234 : int hlen;
235 : u8 *buf;
236 :
237 25 : if (value == NULL)
238 0 : return NULL;
239 25 : hlen = os_strlen(value);
240 25 : if (hlen & 1)
241 0 : return NULL;
242 25 : *len = hlen / 2;
243 25 : buf = os_malloc(*len);
244 25 : if (buf == NULL)
245 0 : return NULL;
246 25 : if (hexstr2bin(value, buf, *len)) {
247 0 : os_free(buf);
248 0 : return NULL;
249 : }
250 25 : return buf;
251 : }
252 :
253 :
254 13 : static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
255 : struct eap_fast_read_ctx *rc)
256 : {
257 13 : os_memset(rc, 0, sizeof(*rc));
258 :
259 13 : rc->buf_len = 2048;
260 13 : rc->buf = os_malloc(rc->buf_len);
261 13 : if (rc->buf == NULL)
262 0 : return -1;
263 :
264 13 : if (os_strncmp(pac_file, "blob://", 7) == 0) {
265 : const struct wpa_config_blob *blob;
266 10 : blob = eap_get_config_blob(sm, pac_file + 7);
267 10 : if (blob == NULL) {
268 7 : wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
269 : "assume no PAC entries have been "
270 : "provisioned", pac_file + 7);
271 7 : os_free(rc->buf);
272 7 : return -1;
273 : }
274 3 : rc->pos = (char *) blob->data;
275 3 : rc->end = (char *) blob->data + blob->len;
276 : } else {
277 3 : rc->f = fopen(pac_file, "rb");
278 3 : if (rc->f == NULL) {
279 1 : wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
280 : "assume no PAC entries have been "
281 : "provisioned", pac_file);
282 1 : os_free(rc->buf);
283 1 : return -1;
284 : }
285 : }
286 :
287 5 : return 0;
288 : }
289 :
290 :
291 5 : static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
292 : {
293 5 : os_free(rc->buf);
294 5 : if (rc->f)
295 2 : fclose(rc->f);
296 5 : }
297 :
298 :
299 5 : static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
300 : {
301 5 : if (*pac)
302 0 : return "START line without END";
303 :
304 5 : *pac = os_zalloc(sizeof(struct eap_fast_pac));
305 5 : if (*pac == NULL)
306 0 : return "No memory for PAC entry";
307 5 : (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
308 5 : return NULL;
309 : }
310 :
311 :
312 5 : static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
313 : struct eap_fast_pac **pac)
314 : {
315 5 : if (*pac == NULL)
316 0 : return "END line without START";
317 5 : 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 5 : *pac_root = *pac;
324 :
325 5 : *pac = NULL;
326 5 : return NULL;
327 : }
328 :
329 :
330 5 : static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
331 : char *pos)
332 : {
333 5 : if (!pos)
334 0 : return "Cannot parse pac type";
335 5 : pac->pac_type = atoi(pos);
336 5 : 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 5 : return NULL;
342 : }
343 :
344 :
345 5 : 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 5 : key = eap_fast_parse_hex(pos, &key_len);
351 5 : if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
352 0 : os_free(key);
353 0 : return "Invalid PAC-Key";
354 : }
355 :
356 5 : os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
357 5 : os_free(key);
358 :
359 5 : return NULL;
360 : }
361 :
362 :
363 5 : static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
364 : char *pos)
365 : {
366 5 : os_free(pac->pac_opaque);
367 5 : pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
368 5 : if (pac->pac_opaque == NULL)
369 0 : return "Invalid PAC-Opaque";
370 5 : return NULL;
371 : }
372 :
373 :
374 5 : static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
375 : {
376 5 : os_free(pac->a_id);
377 5 : pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
378 5 : if (pac->a_id == NULL)
379 0 : return "Invalid A-ID";
380 5 : return NULL;
381 : }
382 :
383 :
384 5 : static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
385 : {
386 5 : os_free(pac->i_id);
387 5 : pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
388 5 : if (pac->i_id == NULL)
389 0 : return "Invalid I-ID";
390 5 : return NULL;
391 : }
392 :
393 :
394 5 : static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
395 : char *pos)
396 : {
397 5 : os_free(pac->a_id_info);
398 5 : pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
399 5 : if (pac->a_id_info == NULL)
400 0 : return "Invalid A-ID-Info";
401 5 : 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 13 : 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 13 : struct eap_fast_pac *pac = NULL;
417 13 : int count = 0;
418 : char *pos;
419 13 : const char *err = NULL;
420 :
421 13 : if (pac_file == NULL)
422 0 : return -1;
423 :
424 13 : if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
425 8 : return 0;
426 :
427 5 : 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 5 : if (os_strcmp(pac_file_hdr, rc.buf) != 0)
433 0 : err = "Unrecognized header line";
434 :
435 65 : while (!err && eap_fast_read_line(&rc, &pos) == 0) {
436 55 : if (os_strcmp(rc.buf, "START") == 0)
437 5 : err = eap_fast_parse_start(&pac);
438 50 : else if (os_strcmp(rc.buf, "END") == 0) {
439 5 : err = eap_fast_parse_end(pac_root, &pac);
440 5 : count++;
441 45 : } else if (!pac)
442 0 : err = "Unexpected line outside START/END block";
443 45 : else if (os_strcmp(rc.buf, "PAC-Type") == 0)
444 5 : err = eap_fast_parse_pac_type(pac, pos);
445 40 : else if (os_strcmp(rc.buf, "PAC-Key") == 0)
446 5 : err = eap_fast_parse_pac_key(pac, pos);
447 35 : else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
448 5 : err = eap_fast_parse_pac_opaque(pac, pos);
449 30 : else if (os_strcmp(rc.buf, "A-ID") == 0)
450 5 : err = eap_fast_parse_a_id(pac, pos);
451 25 : else if (os_strcmp(rc.buf, "I-ID") == 0)
452 5 : err = eap_fast_parse_i_id(pac, pos);
453 20 : else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
454 5 : err = eap_fast_parse_a_id_info(pac, pos);
455 : }
456 :
457 5 : if (pac) {
458 0 : err = "PAC block not terminated with END";
459 0 : eap_fast_free_pac(pac);
460 : }
461 :
462 5 : eap_fast_deinit_pac_data(&rc);
463 :
464 5 : 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 5 : wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
471 : count, pac_file);
472 :
473 5 : return 0;
474 : }
475 :
476 :
477 42 : 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 42 : if (data == NULL || buf == NULL || *buf == NULL ||
486 42 : pos == NULL || *pos == NULL || *pos < *buf)
487 0 : return;
488 :
489 42 : need = os_strlen(field) + len * 2 + 30;
490 42 : if (txt)
491 14 : need += os_strlen(field) + len + 20;
492 :
493 42 : 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 42 : end = *buf + *buf_len;
505 :
506 42 : ret = os_snprintf(*pos, end - *pos, "%s=", field);
507 42 : if (ret < 0 || ret >= end - *pos)
508 0 : return;
509 42 : *pos += ret;
510 42 : *pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
511 42 : ret = os_snprintf(*pos, end - *pos, "\n");
512 42 : if (ret < 0 || ret >= end - *pos)
513 0 : return;
514 42 : *pos += ret;
515 :
516 42 : if (txt) {
517 14 : ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
518 14 : if (ret < 0 || ret >= end - *pos)
519 0 : return;
520 14 : *pos += ret;
521 137 : for (i = 0; i < len; i++) {
522 123 : ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
523 123 : if (ret < 0 || ret >= end - *pos)
524 0 : return;
525 123 : *pos += ret;
526 : }
527 14 : ret = os_snprintf(*pos, end - *pos, "\n");
528 14 : if (ret < 0 || ret >= end - *pos)
529 0 : return;
530 14 : *pos += ret;
531 : }
532 : }
533 :
534 :
535 9 : static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
536 : char *buf, size_t len)
537 : {
538 9 : if (os_strncmp(pac_file, "blob://", 7) == 0) {
539 : struct wpa_config_blob *blob;
540 7 : blob = os_zalloc(sizeof(*blob));
541 7 : if (blob == NULL)
542 0 : return -1;
543 7 : blob->data = (u8 *) buf;
544 7 : blob->len = len;
545 7 : buf = NULL;
546 7 : blob->name = os_strdup(pac_file + 7);
547 7 : if (blob->name == NULL) {
548 0 : os_free(blob);
549 0 : return -1;
550 : }
551 7 : eap_set_config_blob(sm, blob);
552 : } else {
553 : FILE *f;
554 2 : f = fopen(pac_file, "wb");
555 2 : 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 2 : 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 2 : os_free(buf);
567 2 : fclose(f);
568 : }
569 :
570 9 : return 0;
571 : }
572 :
573 :
574 7 : 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 7 : ret = os_snprintf(*pos, *buf + *buf_len - *pos,
580 7 : "START\nPAC-Type=%d\n", pac->pac_type);
581 7 : if (ret < 0 || ret >= *buf + *buf_len - *pos)
582 0 : return -1;
583 :
584 7 : *pos += ret;
585 7 : eap_fast_write(buf, pos, buf_len, "PAC-Key",
586 7 : pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
587 14 : eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
588 7 : pac->pac_opaque, pac->pac_opaque_len, 0);
589 14 : eap_fast_write(buf, pos, buf_len, "PAC-Info",
590 7 : pac->pac_info, pac->pac_info_len, 0);
591 14 : eap_fast_write(buf, pos, buf_len, "A-ID",
592 7 : pac->a_id, pac->a_id_len, 0);
593 14 : eap_fast_write(buf, pos, buf_len, "I-ID",
594 7 : pac->i_id, pac->i_id_len, 1);
595 14 : eap_fast_write(buf, pos, buf_len, "A-ID-Info",
596 7 : pac->a_id_info, pac->a_id_info_len, 1);
597 7 : if (*buf == NULL) {
598 0 : wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
599 : "data");
600 0 : return -1;
601 : }
602 7 : ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
603 7 : if (ret < 0 || ret >= *buf + *buf_len - *pos)
604 0 : return -1;
605 7 : *pos += ret;
606 :
607 7 : 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 7 : 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 7 : int ret, count = 0;
623 : char *buf, *pos;
624 : size_t buf_len;
625 :
626 7 : if (pac_file == NULL)
627 0 : return -1;
628 :
629 7 : buf_len = 1024;
630 7 : pos = buf = os_malloc(buf_len);
631 7 : if (buf == NULL)
632 0 : return -1;
633 :
634 7 : ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
635 7 : if (ret < 0 || ret >= buf + buf_len - pos) {
636 0 : os_free(buf);
637 0 : return -1;
638 : }
639 7 : pos += ret;
640 :
641 7 : pac = pac_root;
642 21 : while (pac) {
643 7 : if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
644 0 : os_free(buf);
645 0 : return -1;
646 : }
647 7 : count++;
648 7 : pac = pac->next;
649 : }
650 :
651 7 : if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
652 0 : os_free(buf);
653 0 : return -1;
654 : }
655 :
656 7 : wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
657 : count, pac_file);
658 :
659 7 : 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 28 : 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 28 : pac = pac_root;
676 28 : prev = NULL;
677 28 : count = 0;
678 :
679 74 : while (pac) {
680 18 : count++;
681 18 : if (count > max_len)
682 0 : break;
683 18 : prev = pac;
684 18 : pac = pac->next;
685 : }
686 :
687 28 : if (count <= max_len || prev == NULL)
688 28 : 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 0 : return count;
701 : }
702 :
703 :
704 4 : static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
705 : {
706 : u8 *pos, *end;
707 : u16 type, len;
708 :
709 4 : pos = pac->pac_info;
710 4 : end = pos + pac->pac_info_len;
711 :
712 28 : while (pos + 4 < end) {
713 20 : type = WPA_GET_BE16(pos);
714 20 : pos += 2;
715 20 : len = WPA_GET_BE16(pos);
716 20 : pos += 2;
717 20 : if (pos + len > end)
718 0 : break;
719 :
720 20 : if (type == PAC_TYPE_A_ID) {
721 4 : os_free(pac->a_id);
722 4 : pac->a_id = os_malloc(len);
723 4 : if (pac->a_id == NULL)
724 0 : break;
725 4 : os_memcpy(pac->a_id, pos, len);
726 4 : pac->a_id_len = len;
727 : }
728 :
729 20 : if (type == PAC_TYPE_A_ID_INFO) {
730 4 : os_free(pac->a_id_info);
731 4 : pac->a_id_info = os_malloc(len);
732 4 : if (pac->a_id_info == NULL)
733 0 : break;
734 4 : os_memcpy(pac->a_id_info, pos, len);
735 4 : pac->a_id_info_len = len;
736 : }
737 :
738 20 : pos += len;
739 : }
740 4 : }
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 6 : int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
751 : const char *pac_file)
752 : {
753 6 : const struct wpa_config_blob *blob = NULL;
754 : u8 *buf, *end, *pos;
755 6 : size_t len, count = 0;
756 : struct eap_fast_pac *pac, *prev;
757 :
758 6 : *pac_root = NULL;
759 :
760 6 : if (pac_file == NULL)
761 0 : return -1;
762 :
763 6 : if (os_strncmp(pac_file, "blob://", 7) == 0) {
764 3 : blob = eap_get_config_blob(sm, pac_file + 7);
765 3 : if (blob == NULL) {
766 1 : wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
767 : "assume no PAC entries have been "
768 : "provisioned", pac_file + 7);
769 1 : return 0;
770 : }
771 2 : buf = blob->data;
772 2 : len = blob->len;
773 : } else {
774 3 : buf = (u8 *) os_readfile(pac_file, &len);
775 3 : if (buf == NULL) {
776 1 : wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
777 : "assume no PAC entries have been "
778 : "provisioned", pac_file);
779 1 : return 0;
780 : }
781 : }
782 :
783 4 : if (len == 0) {
784 0 : if (blob == NULL)
785 0 : os_free(buf);
786 0 : return 0;
787 : }
788 :
789 8 : if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC ||
790 4 : 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 4 : pac = prev = NULL;
799 4 : pos = buf + 6;
800 4 : end = buf + len;
801 12 : while (pos < end) {
802 4 : if (end - pos < 2 + 32 + 2 + 2)
803 0 : goto parse_fail;
804 :
805 4 : pac = os_zalloc(sizeof(*pac));
806 4 : if (pac == NULL)
807 0 : goto parse_fail;
808 :
809 4 : pac->pac_type = WPA_GET_BE16(pos);
810 4 : pos += 2;
811 4 : os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
812 4 : pos += EAP_FAST_PAC_KEY_LEN;
813 4 : pac->pac_opaque_len = WPA_GET_BE16(pos);
814 4 : pos += 2;
815 4 : if (pos + pac->pac_opaque_len + 2 > end)
816 0 : goto parse_fail;
817 4 : pac->pac_opaque = os_malloc(pac->pac_opaque_len);
818 4 : if (pac->pac_opaque == NULL)
819 0 : goto parse_fail;
820 4 : os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
821 4 : pos += pac->pac_opaque_len;
822 4 : pac->pac_info_len = WPA_GET_BE16(pos);
823 4 : pos += 2;
824 4 : if (pos + pac->pac_info_len > end)
825 0 : goto parse_fail;
826 4 : pac->pac_info = os_malloc(pac->pac_info_len);
827 4 : if (pac->pac_info == NULL)
828 0 : goto parse_fail;
829 4 : os_memcpy(pac->pac_info, pos, pac->pac_info_len);
830 4 : pos += pac->pac_info_len;
831 4 : eap_fast_pac_get_a_id(pac);
832 :
833 4 : count++;
834 4 : if (prev)
835 0 : prev->next = pac;
836 : else
837 4 : *pac_root = pac;
838 4 : prev = pac;
839 : }
840 :
841 4 : if (blob == NULL)
842 2 : os_free(buf);
843 :
844 4 : wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)",
845 : (unsigned long) count, pac_file);
846 :
847 4 : 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 2 : int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
868 : const char *pac_file)
869 : {
870 2 : size_t len, count = 0;
871 : struct eap_fast_pac *pac;
872 : u8 *buf, *pos;
873 :
874 2 : len = 6;
875 2 : pac = pac_root;
876 6 : while (pac) {
877 4 : if (pac->pac_opaque_len > 65535 ||
878 2 : pac->pac_info_len > 65535)
879 0 : return -1;
880 4 : len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
881 2 : 2 + pac->pac_info_len;
882 2 : pac = pac->next;
883 : }
884 :
885 2 : buf = os_malloc(len);
886 2 : if (buf == NULL)
887 0 : return -1;
888 :
889 2 : pos = buf;
890 2 : WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
891 2 : pos += 4;
892 2 : WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
893 2 : pos += 2;
894 :
895 2 : pac = pac_root;
896 6 : while (pac) {
897 2 : WPA_PUT_BE16(pos, pac->pac_type);
898 2 : pos += 2;
899 2 : os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
900 2 : pos += EAP_FAST_PAC_KEY_LEN;
901 2 : WPA_PUT_BE16(pos, pac->pac_opaque_len);
902 2 : pos += 2;
903 2 : os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
904 2 : pos += pac->pac_opaque_len;
905 2 : WPA_PUT_BE16(pos, pac->pac_info_len);
906 2 : pos += 2;
907 2 : os_memcpy(pos, pac->pac_info, pac->pac_info_len);
908 2 : pos += pac->pac_info_len;
909 :
910 2 : pac = pac->next;
911 2 : count++;
912 : }
913 :
914 2 : if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
915 0 : os_free(buf);
916 0 : return -1;
917 : }
918 :
919 2 : wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' "
920 : "(bin)", (unsigned long) count, pac_file);
921 :
922 2 : return 0;
923 : }
|