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