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