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