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 23 : void eap_fast_free_pac(struct eap_fast_pac *pac)
51 : {
52 23 : os_free(pac->pac_opaque);
53 23 : os_free(pac->pac_info);
54 23 : os_free(pac->a_id);
55 23 : os_free(pac->i_id);
56 23 : os_free(pac->a_id_info);
57 23 : os_free(pac);
58 23 : }
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 37 : 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 37 : struct eap_fast_pac *pac = pac_root;
74 :
75 74 : while (pac) {
76 22 : if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
77 11 : os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
78 11 : return pac;
79 : }
80 0 : pac = pac->next;
81 : }
82 26 : return NULL;
83 : }
84 :
85 :
86 12 : 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 12 : pac = *pac_root;
93 12 : prev = NULL;
94 :
95 24 : 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 12 : }
111 :
112 :
113 60 : static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
114 : const u8 *src, size_t src_len)
115 : {
116 60 : if (src) {
117 60 : *dst = os_malloc(src_len);
118 60 : if (*dst == NULL)
119 0 : return -1;
120 60 : os_memcpy(*dst, src, src_len);
121 60 : *dst_len = src_len;
122 : }
123 60 : 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 12 : 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 12 : if (entry == NULL || entry->a_id == NULL)
146 0 : return -1;
147 :
148 : /* Remove a possible old entry for the matching A-ID. */
149 24 : eap_fast_remove_pac(pac_root, pac_current,
150 24 : 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 12 : pac = os_zalloc(sizeof(*pac));
154 12 : if (pac == NULL)
155 0 : return -1;
156 :
157 12 : pac->pac_type = entry->pac_type;
158 12 : os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
159 24 : if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
160 24 : entry->pac_opaque, entry->pac_opaque_len) < 0 ||
161 24 : eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
162 24 : entry->pac_info, entry->pac_info_len) < 0 ||
163 24 : eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
164 24 : entry->a_id, entry->a_id_len) < 0 ||
165 24 : eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
166 24 : entry->i_id, entry->i_id_len) < 0 ||
167 24 : eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
168 12 : entry->a_id_info, entry->a_id_info_len) < 0) {
169 0 : eap_fast_free_pac(pac);
170 0 : return -1;
171 : }
172 :
173 12 : pac->next = *pac_root;
174 12 : *pac_root = pac;
175 :
176 12 : 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 91 : static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
190 : {
191 : char *pos;
192 :
193 91 : rc->line++;
194 91 : 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 65 : if (rc->pos >= rc->end)
201 5 : return -1;
202 60 : l_end = rc->pos;
203 2604 : while (l_end < rc->end && *l_end != '\n')
204 2484 : l_end++;
205 60 : len = l_end - rc->pos;
206 60 : if (len >= rc->buf_len)
207 0 : len = rc->buf_len - 1;
208 60 : os_memcpy(rc->buf, rc->pos, len);
209 60 : rc->buf[len] = '\0';
210 60 : rc->pos = l_end + 1;
211 : }
212 :
213 84 : rc->buf[rc->buf_len - 1] = '\0';
214 84 : pos = rc->buf;
215 3652 : while (*pos != '\0') {
216 3508 : if (*pos == '\n' || *pos == '\r') {
217 24 : *pos = '\0';
218 24 : break;
219 : }
220 3484 : pos++;
221 : }
222 :
223 84 : pos = os_strchr(rc->buf, '=');
224 84 : if (pos)
225 63 : *pos++ = '\0';
226 84 : *value = pos;
227 :
228 84 : return 0;
229 : }
230 :
231 :
232 35 : static u8 * eap_fast_parse_hex(const char *value, size_t *len)
233 : {
234 : int hlen;
235 : u8 *buf;
236 :
237 35 : if (value == NULL)
238 0 : return NULL;
239 35 : hlen = os_strlen(value);
240 35 : if (hlen & 1)
241 0 : return NULL;
242 35 : *len = hlen / 2;
243 35 : buf = os_malloc(*len);
244 35 : if (buf == NULL)
245 0 : return NULL;
246 35 : if (hexstr2bin(value, buf, *len)) {
247 0 : os_free(buf);
248 0 : return NULL;
249 : }
250 35 : return buf;
251 : }
252 :
253 :
254 19 : static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
255 : struct eap_fast_read_ctx *rc)
256 : {
257 19 : os_memset(rc, 0, sizeof(*rc));
258 :
259 19 : rc->buf_len = 2048;
260 19 : rc->buf = os_malloc(rc->buf_len);
261 19 : if (rc->buf == NULL)
262 0 : return -1;
263 :
264 19 : if (os_strncmp(pac_file, "blob://", 7) == 0) {
265 : const struct wpa_config_blob *blob;
266 16 : blob = eap_get_config_blob(sm, pac_file + 7);
267 16 : if (blob == NULL) {
268 11 : wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
269 : "assume no PAC entries have been "
270 : "provisioned", pac_file + 7);
271 11 : os_free(rc->buf);
272 11 : return -1;
273 : }
274 5 : rc->pos = (char *) blob->data;
275 5 : 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 7 : return 0;
288 : }
289 :
290 :
291 7 : static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
292 : {
293 7 : os_free(rc->buf);
294 7 : if (rc->f)
295 2 : fclose(rc->f);
296 7 : }
297 :
298 :
299 7 : static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
300 : {
301 7 : if (*pac)
302 0 : return "START line without END";
303 :
304 7 : *pac = os_zalloc(sizeof(struct eap_fast_pac));
305 7 : if (*pac == NULL)
306 0 : return "No memory for PAC entry";
307 7 : (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
308 7 : return NULL;
309 : }
310 :
311 :
312 7 : static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
313 : struct eap_fast_pac **pac)
314 : {
315 7 : if (*pac == NULL)
316 0 : return "END line without START";
317 7 : 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 7 : *pac_root = *pac;
324 :
325 7 : *pac = NULL;
326 7 : return NULL;
327 : }
328 :
329 :
330 7 : static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
331 : char *pos)
332 : {
333 7 : if (!pos)
334 0 : return "Cannot parse pac type";
335 7 : pac->pac_type = atoi(pos);
336 7 : 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 7 : return NULL;
342 : }
343 :
344 :
345 7 : 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 7 : key = eap_fast_parse_hex(pos, &key_len);
351 7 : if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
352 0 : os_free(key);
353 0 : return "Invalid PAC-Key";
354 : }
355 :
356 7 : os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
357 7 : os_free(key);
358 :
359 7 : return NULL;
360 : }
361 :
362 :
363 7 : static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
364 : char *pos)
365 : {
366 7 : os_free(pac->pac_opaque);
367 7 : pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
368 7 : if (pac->pac_opaque == NULL)
369 0 : return "Invalid PAC-Opaque";
370 7 : return NULL;
371 : }
372 :
373 :
374 7 : static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
375 : {
376 7 : os_free(pac->a_id);
377 7 : pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
378 7 : if (pac->a_id == NULL)
379 0 : return "Invalid A-ID";
380 7 : return NULL;
381 : }
382 :
383 :
384 7 : static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
385 : {
386 7 : os_free(pac->i_id);
387 7 : pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
388 7 : if (pac->i_id == NULL)
389 0 : return "Invalid I-ID";
390 7 : return NULL;
391 : }
392 :
393 :
394 7 : static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
395 : char *pos)
396 : {
397 7 : os_free(pac->a_id_info);
398 7 : pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
399 7 : if (pac->a_id_info == NULL)
400 0 : return "Invalid A-ID-Info";
401 7 : 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 19 : 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 19 : struct eap_fast_pac *pac = NULL;
417 19 : int count = 0;
418 : char *pos;
419 19 : const char *err = NULL;
420 :
421 19 : if (pac_file == NULL)
422 0 : return -1;
423 :
424 19 : if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
425 12 : return 0;
426 :
427 7 : 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 7 : if (os_strcmp(pac_file_hdr, rc.buf) != 0)
433 0 : err = "Unrecognized header line";
434 :
435 91 : while (!err && eap_fast_read_line(&rc, &pos) == 0) {
436 77 : if (os_strcmp(rc.buf, "START") == 0)
437 7 : err = eap_fast_parse_start(&pac);
438 70 : else if (os_strcmp(rc.buf, "END") == 0) {
439 7 : err = eap_fast_parse_end(pac_root, &pac);
440 7 : count++;
441 63 : } else if (!pac)
442 0 : err = "Unexpected line outside START/END block";
443 63 : else if (os_strcmp(rc.buf, "PAC-Type") == 0)
444 7 : err = eap_fast_parse_pac_type(pac, pos);
445 56 : else if (os_strcmp(rc.buf, "PAC-Key") == 0)
446 7 : err = eap_fast_parse_pac_key(pac, pos);
447 49 : else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
448 7 : err = eap_fast_parse_pac_opaque(pac, pos);
449 42 : else if (os_strcmp(rc.buf, "A-ID") == 0)
450 7 : err = eap_fast_parse_a_id(pac, pos);
451 35 : else if (os_strcmp(rc.buf, "I-ID") == 0)
452 7 : err = eap_fast_parse_i_id(pac, pos);
453 28 : else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
454 7 : err = eap_fast_parse_a_id_info(pac, pos);
455 : }
456 :
457 7 : if (pac) {
458 0 : err = "PAC block not terminated with END";
459 0 : eap_fast_free_pac(pac);
460 : }
461 :
462 7 : eap_fast_deinit_pac_data(&rc);
463 :
464 7 : 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 7 : 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 60 : 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 60 : if (data == NULL || buf == NULL || *buf == NULL ||
486 60 : pos == NULL || *pos == NULL || *pos < *buf)
487 0 : return;
488 :
489 60 : need = os_strlen(field) + len * 2 + 30;
490 60 : if (txt)
491 20 : need += os_strlen(field) + len + 20;
492 :
493 60 : 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 60 : end = *buf + *buf_len;
505 :
506 60 : ret = os_snprintf(*pos, end - *pos, "%s=", field);
507 60 : if (os_snprintf_error(end - *pos, ret))
508 0 : return;
509 60 : *pos += ret;
510 60 : *pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
511 60 : ret = os_snprintf(*pos, end - *pos, "\n");
512 60 : if (os_snprintf_error(end - *pos, ret))
513 0 : return;
514 60 : *pos += ret;
515 :
516 60 : if (txt) {
517 20 : ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
518 20 : if (os_snprintf_error(end - *pos, ret))
519 0 : return;
520 20 : *pos += ret;
521 212 : for (i = 0; i < len; i++) {
522 192 : ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
523 192 : if (os_snprintf_error(end - *pos, ret))
524 0 : return;
525 192 : *pos += ret;
526 : }
527 20 : ret = os_snprintf(*pos, end - *pos, "\n");
528 20 : if (os_snprintf_error(end - *pos, ret))
529 0 : return;
530 20 : *pos += ret;
531 : }
532 : }
533 :
534 :
535 12 : static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
536 : char *buf, size_t len)
537 : {
538 12 : if (os_strncmp(pac_file, "blob://", 7) == 0) {
539 : struct wpa_config_blob *blob;
540 10 : blob = os_zalloc(sizeof(*blob));
541 10 : if (blob == NULL)
542 0 : return -1;
543 10 : blob->data = (u8 *) buf;
544 10 : blob->len = len;
545 10 : buf = NULL;
546 10 : blob->name = os_strdup(pac_file + 7);
547 10 : if (blob->name == NULL) {
548 0 : os_free(blob);
549 0 : return -1;
550 : }
551 10 : 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 12 : return 0;
571 : }
572 :
573 :
574 10 : 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 10 : ret = os_snprintf(*pos, *buf + *buf_len - *pos,
580 10 : "START\nPAC-Type=%d\n", pac->pac_type);
581 10 : if (os_snprintf_error(*buf + *buf_len - *pos, ret))
582 0 : return -1;
583 :
584 10 : *pos += ret;
585 10 : eap_fast_write(buf, pos, buf_len, "PAC-Key",
586 10 : pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
587 20 : eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
588 10 : pac->pac_opaque, pac->pac_opaque_len, 0);
589 20 : eap_fast_write(buf, pos, buf_len, "PAC-Info",
590 10 : pac->pac_info, pac->pac_info_len, 0);
591 20 : eap_fast_write(buf, pos, buf_len, "A-ID",
592 10 : pac->a_id, pac->a_id_len, 0);
593 20 : eap_fast_write(buf, pos, buf_len, "I-ID",
594 10 : pac->i_id, pac->i_id_len, 1);
595 20 : eap_fast_write(buf, pos, buf_len, "A-ID-Info",
596 10 : pac->a_id_info, pac->a_id_info_len, 1);
597 10 : if (*buf == NULL) {
598 0 : wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
599 : "data");
600 0 : return -1;
601 : }
602 10 : ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
603 10 : if (os_snprintf_error(*buf + *buf_len - *pos, ret))
604 0 : return -1;
605 10 : *pos += ret;
606 :
607 10 : 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 10 : 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 10 : int ret, count = 0;
623 : char *buf, *pos;
624 : size_t buf_len;
625 :
626 10 : if (pac_file == NULL)
627 0 : return -1;
628 :
629 10 : buf_len = 1024;
630 10 : pos = buf = os_malloc(buf_len);
631 10 : if (buf == NULL)
632 0 : return -1;
633 :
634 10 : ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
635 10 : if (os_snprintf_error(buf + buf_len - pos, ret)) {
636 0 : os_free(buf);
637 0 : return -1;
638 : }
639 10 : pos += ret;
640 :
641 10 : pac = pac_root;
642 30 : while (pac) {
643 10 : if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
644 0 : os_free(buf);
645 0 : return -1;
646 : }
647 10 : count++;
648 10 : pac = pac->next;
649 : }
650 :
651 10 : if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
652 0 : os_free(buf);
653 0 : return -1;
654 : }
655 :
656 10 : wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
657 : count, pac_file);
658 :
659 10 : 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 37 : 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 37 : pac = pac_root;
676 37 : prev = NULL;
677 37 : count = 0;
678 :
679 97 : while (pac) {
680 23 : count++;
681 23 : if (count > max_len)
682 0 : break;
683 23 : prev = pac;
684 23 : pac = pac->next;
685 : }
686 :
687 37 : if (count <= max_len || prev == NULL)
688 37 : 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 (len > (unsigned int) (end - pos))
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 : u16 val;
803 :
804 4 : if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2)
805 0 : goto parse_fail;
806 :
807 4 : pac = os_zalloc(sizeof(*pac));
808 4 : if (pac == NULL)
809 0 : goto parse_fail;
810 :
811 4 : pac->pac_type = WPA_GET_BE16(pos);
812 4 : pos += 2;
813 4 : os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
814 4 : pos += EAP_FAST_PAC_KEY_LEN;
815 4 : val = WPA_GET_BE16(pos);
816 4 : pos += 2;
817 4 : if (val > end - pos)
818 0 : goto parse_fail;
819 4 : pac->pac_opaque_len = val;
820 4 : pac->pac_opaque = os_malloc(pac->pac_opaque_len);
821 4 : if (pac->pac_opaque == NULL)
822 0 : goto parse_fail;
823 4 : os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
824 4 : pos += pac->pac_opaque_len;
825 4 : if (2 > end - pos)
826 0 : goto parse_fail;
827 4 : val = WPA_GET_BE16(pos);
828 4 : pos += 2;
829 4 : if (val > end - pos)
830 0 : goto parse_fail;
831 4 : pac->pac_info_len = val;
832 4 : pac->pac_info = os_malloc(pac->pac_info_len);
833 4 : if (pac->pac_info == NULL)
834 0 : goto parse_fail;
835 4 : os_memcpy(pac->pac_info, pos, pac->pac_info_len);
836 4 : pos += pac->pac_info_len;
837 4 : eap_fast_pac_get_a_id(pac);
838 :
839 4 : count++;
840 4 : if (prev)
841 0 : prev->next = pac;
842 : else
843 4 : *pac_root = pac;
844 4 : prev = pac;
845 : }
846 :
847 4 : if (blob == NULL)
848 2 : os_free(buf);
849 :
850 4 : wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)",
851 : (unsigned long) count, pac_file);
852 :
853 4 : return 0;
854 :
855 : parse_fail:
856 0 : wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)",
857 : pac_file);
858 0 : if (blob == NULL)
859 0 : os_free(buf);
860 0 : if (pac)
861 0 : eap_fast_free_pac(pac);
862 0 : return -1;
863 : }
864 :
865 :
866 : /**
867 : * eap_fast_save_pac_bin - Save PAC entries (binary format)
868 : * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
869 : * @pac_root: Root of the PAC list
870 : * @pac_file: Name of the PAC file/blob
871 : * Returns: 0 on success, -1 on failure
872 : */
873 2 : int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
874 : const char *pac_file)
875 : {
876 2 : size_t len, count = 0;
877 : struct eap_fast_pac *pac;
878 : u8 *buf, *pos;
879 :
880 2 : len = 6;
881 2 : pac = pac_root;
882 6 : while (pac) {
883 4 : if (pac->pac_opaque_len > 65535 ||
884 2 : pac->pac_info_len > 65535)
885 0 : return -1;
886 4 : len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
887 2 : 2 + pac->pac_info_len;
888 2 : pac = pac->next;
889 : }
890 :
891 2 : buf = os_malloc(len);
892 2 : if (buf == NULL)
893 0 : return -1;
894 :
895 2 : pos = buf;
896 2 : WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
897 2 : pos += 4;
898 2 : WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
899 2 : pos += 2;
900 :
901 2 : pac = pac_root;
902 6 : while (pac) {
903 2 : WPA_PUT_BE16(pos, pac->pac_type);
904 2 : pos += 2;
905 2 : os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
906 2 : pos += EAP_FAST_PAC_KEY_LEN;
907 2 : WPA_PUT_BE16(pos, pac->pac_opaque_len);
908 2 : pos += 2;
909 2 : os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
910 2 : pos += pac->pac_opaque_len;
911 2 : WPA_PUT_BE16(pos, pac->pac_info_len);
912 2 : pos += 2;
913 2 : os_memcpy(pos, pac->pac_info, pac->pac_info_len);
914 2 : pos += pac->pac_info_len;
915 :
916 2 : pac = pac->next;
917 2 : count++;
918 : }
919 :
920 2 : if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
921 0 : os_free(buf);
922 0 : return -1;
923 : }
924 :
925 2 : wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' "
926 : "(bin)", (unsigned long) count, pac_file);
927 :
928 2 : return 0;
929 : }
|