eap_fast_pac.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. /*
  2. * EAP peer method: EAP-FAST PAC file processing
  3. * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. *
  9. * Alternatively, this software may be distributed under the terms of BSD
  10. * license.
  11. *
  12. * See README and COPYING for more details.
  13. */
  14. #include "includes.h"
  15. #include "common.h"
  16. #include "eap_config.h"
  17. #include "eap_i.h"
  18. #include "eap_fast_pac.h"
  19. /* TODO: encrypt PAC-Key in the PAC file */
  20. /* Text data format */
  21. static const char *pac_file_hdr =
  22. "wpa_supplicant EAP-FAST PAC file - version 1";
  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. #define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c
  39. #define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0
  40. /**
  41. * eap_fast_free_pac - Free PAC data
  42. * @pac: Pointer to the PAC entry
  43. *
  44. * Note that the PAC entry must not be in a list since this function does not
  45. * remove the list links.
  46. */
  47. void eap_fast_free_pac(struct eap_fast_pac *pac)
  48. {
  49. os_free(pac->pac_opaque);
  50. os_free(pac->pac_info);
  51. os_free(pac->a_id);
  52. os_free(pac->i_id);
  53. os_free(pac->a_id_info);
  54. os_free(pac);
  55. }
  56. /**
  57. * eap_fast_get_pac - Get a PAC entry based on A-ID
  58. * @pac_root: Pointer to root of the PAC list
  59. * @a_id: A-ID to search for
  60. * @a_id_len: Length of A-ID
  61. * @pac_type: PAC-Type to search for
  62. * Returns: Pointer to the PAC entry, or %NULL if A-ID not found
  63. */
  64. struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
  65. const u8 *a_id, size_t a_id_len,
  66. u16 pac_type)
  67. {
  68. struct eap_fast_pac *pac = pac_root;
  69. while (pac) {
  70. if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
  71. os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
  72. return pac;
  73. }
  74. pac = pac->next;
  75. }
  76. return NULL;
  77. }
  78. static void eap_fast_remove_pac(struct eap_fast_pac **pac_root,
  79. struct eap_fast_pac **pac_current,
  80. const u8 *a_id, size_t a_id_len, u16 pac_type)
  81. {
  82. struct eap_fast_pac *pac, *prev;
  83. pac = *pac_root;
  84. prev = NULL;
  85. while (pac) {
  86. if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
  87. os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
  88. if (prev == NULL)
  89. *pac_root = pac->next;
  90. else
  91. prev->next = pac->next;
  92. if (*pac_current == pac)
  93. *pac_current = NULL;
  94. eap_fast_free_pac(pac);
  95. break;
  96. }
  97. prev = pac;
  98. pac = pac->next;
  99. }
  100. }
  101. static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
  102. const u8 *src, size_t src_len)
  103. {
  104. if (src) {
  105. *dst = os_malloc(src_len);
  106. if (*dst == NULL)
  107. return -1;
  108. os_memcpy(*dst, src, src_len);
  109. *dst_len = src_len;
  110. }
  111. return 0;
  112. }
  113. /**
  114. * eap_fast_add_pac - Add a copy of a PAC entry to a list
  115. * @pac_root: Pointer to PAC list root pointer
  116. * @pac_current: Pointer to the current PAC pointer
  117. * @entry: New entry to clone and add to the list
  118. * Returns: 0 on success, -1 on failure
  119. *
  120. * This function makes a clone of the given PAC entry and adds this copied
  121. * entry to the list (pac_root). If an old entry for the same A-ID is found,
  122. * it will be removed from the PAC list and in this case, pac_current entry
  123. * is set to %NULL if it was the removed entry.
  124. */
  125. int eap_fast_add_pac(struct eap_fast_pac **pac_root,
  126. struct eap_fast_pac **pac_current,
  127. struct eap_fast_pac *entry)
  128. {
  129. struct eap_fast_pac *pac;
  130. if (entry == NULL || entry->a_id == NULL)
  131. return -1;
  132. /* Remove a possible old entry for the matching A-ID. */
  133. eap_fast_remove_pac(pac_root, pac_current,
  134. entry->a_id, entry->a_id_len, entry->pac_type);
  135. /* Allocate a new entry and add it to the list of PACs. */
  136. pac = os_zalloc(sizeof(*pac));
  137. if (pac == NULL)
  138. return -1;
  139. pac->pac_type = entry->pac_type;
  140. os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
  141. if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
  142. entry->pac_opaque, entry->pac_opaque_len) < 0 ||
  143. eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
  144. entry->pac_info, entry->pac_info_len) < 0 ||
  145. eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
  146. entry->a_id, entry->a_id_len) < 0 ||
  147. eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
  148. entry->i_id, entry->i_id_len) < 0 ||
  149. eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
  150. entry->a_id_info, entry->a_id_info_len) < 0) {
  151. eap_fast_free_pac(pac);
  152. return -1;
  153. }
  154. pac->next = *pac_root;
  155. *pac_root = pac;
  156. return 0;
  157. }
  158. struct eap_fast_read_ctx {
  159. FILE *f;
  160. const char *pos;
  161. const char *end;
  162. int line;
  163. char *buf;
  164. size_t buf_len;
  165. };
  166. static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
  167. {
  168. char *pos;
  169. rc->line++;
  170. if (rc->f) {
  171. if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
  172. return -1;
  173. } else {
  174. const char *l_end;
  175. size_t len;
  176. if (rc->pos >= rc->end)
  177. return -1;
  178. l_end = rc->pos;
  179. while (l_end < rc->end && *l_end != '\n')
  180. l_end++;
  181. len = l_end - rc->pos;
  182. if (len >= rc->buf_len)
  183. len = rc->buf_len - 1;
  184. os_memcpy(rc->buf, rc->pos, len);
  185. rc->buf[len] = '\0';
  186. rc->pos = l_end + 1;
  187. }
  188. rc->buf[rc->buf_len - 1] = '\0';
  189. pos = rc->buf;
  190. while (*pos != '\0') {
  191. if (*pos == '\n' || *pos == '\r') {
  192. *pos = '\0';
  193. break;
  194. }
  195. pos++;
  196. }
  197. pos = os_strchr(rc->buf, '=');
  198. if (pos)
  199. *pos++ = '\0';
  200. *value = pos;
  201. return 0;
  202. }
  203. static u8 * eap_fast_parse_hex(const char *value, size_t *len)
  204. {
  205. int hlen;
  206. u8 *buf;
  207. if (value == NULL)
  208. return NULL;
  209. hlen = os_strlen(value);
  210. if (hlen & 1)
  211. return NULL;
  212. *len = hlen / 2;
  213. buf = os_malloc(*len);
  214. if (buf == NULL)
  215. return NULL;
  216. if (hexstr2bin(value, buf, *len)) {
  217. os_free(buf);
  218. return NULL;
  219. }
  220. return buf;
  221. }
  222. static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
  223. struct eap_fast_read_ctx *rc)
  224. {
  225. os_memset(rc, 0, sizeof(*rc));
  226. rc->buf_len = 2048;
  227. rc->buf = os_malloc(rc->buf_len);
  228. if (rc->buf == NULL)
  229. return -1;
  230. if (os_strncmp(pac_file, "blob://", 7) == 0) {
  231. const struct wpa_config_blob *blob;
  232. blob = eap_get_config_blob(sm, pac_file + 7);
  233. if (blob == NULL) {
  234. wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
  235. "assume no PAC entries have been "
  236. "provisioned", pac_file + 7);
  237. os_free(rc->buf);
  238. return -1;
  239. }
  240. rc->pos = (char *) blob->data;
  241. rc->end = (char *) blob->data + blob->len;
  242. } else {
  243. rc->f = fopen(pac_file, "rb");
  244. if (rc->f == NULL) {
  245. wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
  246. "assume no PAC entries have been "
  247. "provisioned", pac_file);
  248. os_free(rc->buf);
  249. return -1;
  250. }
  251. }
  252. return 0;
  253. }
  254. static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
  255. {
  256. os_free(rc->buf);
  257. if (rc->f)
  258. fclose(rc->f);
  259. }
  260. static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
  261. {
  262. if (*pac)
  263. return "START line without END";
  264. *pac = os_zalloc(sizeof(struct eap_fast_pac));
  265. if (*pac == NULL)
  266. return "No memory for PAC entry";
  267. (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
  268. return NULL;
  269. }
  270. static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
  271. struct eap_fast_pac **pac)
  272. {
  273. if (*pac == NULL)
  274. return "END line without START";
  275. if (*pac_root) {
  276. struct eap_fast_pac *end = *pac_root;
  277. while (end->next)
  278. end = end->next;
  279. end->next = *pac;
  280. } else
  281. *pac_root = *pac;
  282. *pac = NULL;
  283. return NULL;
  284. }
  285. static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
  286. char *pos)
  287. {
  288. pac->pac_type = atoi(pos);
  289. if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
  290. pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
  291. pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION)
  292. return "Unrecognized PAC-Type";
  293. return NULL;
  294. }
  295. static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos)
  296. {
  297. u8 *key;
  298. size_t key_len;
  299. key = eap_fast_parse_hex(pos, &key_len);
  300. if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
  301. os_free(key);
  302. return "Invalid PAC-Key";
  303. }
  304. os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
  305. os_free(key);
  306. return NULL;
  307. }
  308. static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
  309. char *pos)
  310. {
  311. os_free(pac->pac_opaque);
  312. pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
  313. if (pac->pac_opaque == NULL)
  314. return "Invalid PAC-Opaque";
  315. return NULL;
  316. }
  317. static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
  318. {
  319. os_free(pac->a_id);
  320. pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
  321. if (pac->a_id == NULL)
  322. return "Invalid A-ID";
  323. return NULL;
  324. }
  325. static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
  326. {
  327. os_free(pac->i_id);
  328. pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
  329. if (pac->i_id == NULL)
  330. return "Invalid I-ID";
  331. return NULL;
  332. }
  333. static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
  334. char *pos)
  335. {
  336. os_free(pac->a_id_info);
  337. pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
  338. if (pac->a_id_info == NULL)
  339. return "Invalid A-ID-Info";
  340. return NULL;
  341. }
  342. /**
  343. * eap_fast_load_pac - Load PAC entries (text format)
  344. * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
  345. * @pac_root: Pointer to root of the PAC list (to be filled)
  346. * @pac_file: Name of the PAC file/blob to load
  347. * Returns: 0 on success, -1 on failure
  348. */
  349. int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
  350. const char *pac_file)
  351. {
  352. struct eap_fast_read_ctx rc;
  353. struct eap_fast_pac *pac = NULL;
  354. int count = 0;
  355. char *pos;
  356. const char *err = NULL;
  357. if (pac_file == NULL)
  358. return -1;
  359. if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
  360. return 0;
  361. if (eap_fast_read_line(&rc, &pos) < 0 ||
  362. os_strcmp(pac_file_hdr, rc.buf) != 0)
  363. err = "Unrecognized header line";
  364. while (!err && eap_fast_read_line(&rc, &pos) == 0) {
  365. if (os_strcmp(rc.buf, "START") == 0)
  366. err = eap_fast_parse_start(&pac);
  367. else if (os_strcmp(rc.buf, "END") == 0) {
  368. err = eap_fast_parse_end(pac_root, &pac);
  369. count++;
  370. } else if (!pac)
  371. err = "Unexpected line outside START/END block";
  372. else if (os_strcmp(rc.buf, "PAC-Type") == 0)
  373. err = eap_fast_parse_pac_type(pac, pos);
  374. else if (os_strcmp(rc.buf, "PAC-Key") == 0)
  375. err = eap_fast_parse_pac_key(pac, pos);
  376. else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
  377. err = eap_fast_parse_pac_opaque(pac, pos);
  378. else if (os_strcmp(rc.buf, "A-ID") == 0)
  379. err = eap_fast_parse_a_id(pac, pos);
  380. else if (os_strcmp(rc.buf, "I-ID") == 0)
  381. err = eap_fast_parse_i_id(pac, pos);
  382. else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
  383. err = eap_fast_parse_a_id_info(pac, pos);
  384. }
  385. if (pac) {
  386. err = "PAC block not terminated with END";
  387. eap_fast_free_pac(pac);
  388. }
  389. eap_fast_deinit_pac_data(&rc);
  390. if (err) {
  391. wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'",
  392. err, pac_file, rc.line);
  393. return -1;
  394. }
  395. wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
  396. count, pac_file);
  397. return 0;
  398. }
  399. static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
  400. const char *field, const u8 *data,
  401. size_t len, int txt)
  402. {
  403. size_t i, need;
  404. int ret;
  405. if (data == NULL || *buf == NULL)
  406. return;
  407. need = os_strlen(field) + len * 2 + 30;
  408. if (txt)
  409. need += os_strlen(field) + len + 20;
  410. if (*pos - *buf + need > *buf_len) {
  411. char *nbuf = os_realloc(*buf, *buf_len + need);
  412. if (nbuf == NULL) {
  413. os_free(*buf);
  414. *buf = NULL;
  415. return;
  416. }
  417. *buf = nbuf;
  418. *buf_len += need;
  419. }
  420. ret = os_snprintf(*pos, *buf + *buf_len - *pos, "%s=", field);
  421. if (ret < 0 || ret >= *buf + *buf_len - *pos)
  422. return;
  423. *pos += ret;
  424. *pos += wpa_snprintf_hex(*pos, *buf + *buf_len - *pos, data, len);
  425. ret = os_snprintf(*pos, *buf + *buf_len - *pos, "\n");
  426. if (ret < 0 || ret >= *buf + *buf_len - *pos)
  427. return;
  428. *pos += ret;
  429. if (txt) {
  430. ret = os_snprintf(*pos, *buf + *buf_len - *pos,
  431. "%s-txt=", field);
  432. if (ret < 0 || ret >= *buf + *buf_len - *pos)
  433. return;
  434. *pos += ret;
  435. for (i = 0; i < len; i++) {
  436. ret = os_snprintf(*pos, *buf + *buf_len - *pos,
  437. "%c", data[i]);
  438. if (ret < 0 || ret >= *buf + *buf_len - *pos)
  439. return;
  440. *pos += ret;
  441. }
  442. ret = os_snprintf(*pos, *buf + *buf_len - *pos, "\n");
  443. if (ret < 0 || ret >= *buf + *buf_len - *pos)
  444. return;
  445. *pos += ret;
  446. }
  447. }
  448. static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
  449. char *buf, size_t len)
  450. {
  451. if (os_strncmp(pac_file, "blob://", 7) == 0) {
  452. struct wpa_config_blob *blob;
  453. blob = os_zalloc(sizeof(*blob));
  454. if (blob == NULL)
  455. return -1;
  456. blob->data = (u8 *) buf;
  457. blob->len = len;
  458. buf = NULL;
  459. blob->name = os_strdup(pac_file + 7);
  460. if (blob->name == NULL) {
  461. os_free(blob);
  462. return -1;
  463. }
  464. eap_set_config_blob(sm, blob);
  465. } else {
  466. FILE *f;
  467. f = fopen(pac_file, "wb");
  468. if (f == NULL) {
  469. wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC "
  470. "file '%s' for writing", pac_file);
  471. return -1;
  472. }
  473. if (fwrite(buf, 1, len, f) != len) {
  474. wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all "
  475. "PACs into '%s'", pac_file);
  476. fclose(f);
  477. return -1;
  478. }
  479. os_free(buf);
  480. fclose(f);
  481. }
  482. return 0;
  483. }
  484. static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
  485. char **pos, size_t *buf_len)
  486. {
  487. int ret;
  488. ret = os_snprintf(*pos, *buf + *buf_len - *pos,
  489. "START\nPAC-Type=%d\n", pac->pac_type);
  490. if (ret < 0 || ret >= *buf + *buf_len - *pos)
  491. return -1;
  492. *pos += ret;
  493. eap_fast_write(buf, pos, buf_len, "PAC-Key",
  494. pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
  495. eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
  496. pac->pac_opaque, pac->pac_opaque_len, 0);
  497. eap_fast_write(buf, pos, buf_len, "PAC-Info",
  498. pac->pac_info, pac->pac_info_len, 0);
  499. eap_fast_write(buf, pos, buf_len, "A-ID",
  500. pac->a_id, pac->a_id_len, 0);
  501. eap_fast_write(buf, pos, buf_len, "I-ID",
  502. pac->i_id, pac->i_id_len, 1);
  503. eap_fast_write(buf, pos, buf_len, "A-ID-Info",
  504. pac->a_id_info, pac->a_id_info_len, 1);
  505. if (*buf == NULL) {
  506. wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
  507. "data");
  508. return -1;
  509. }
  510. ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
  511. if (ret < 0 || ret >= *buf + *buf_len - *pos)
  512. return -1;
  513. *pos += ret;
  514. return 0;
  515. }
  516. /**
  517. * eap_fast_save_pac - Save PAC entries (text format)
  518. * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
  519. * @pac_root: Root of the PAC list
  520. * @pac_file: Name of the PAC file/blob
  521. * Returns: 0 on success, -1 on failure
  522. */
  523. int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
  524. const char *pac_file)
  525. {
  526. struct eap_fast_pac *pac;
  527. int ret, count = 0;
  528. char *buf, *pos;
  529. size_t buf_len;
  530. if (pac_file == NULL)
  531. return -1;
  532. buf_len = 1024;
  533. pos = buf = os_malloc(buf_len);
  534. if (buf == NULL)
  535. return -1;
  536. ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
  537. if (ret < 0 || ret >= buf + buf_len - pos) {
  538. os_free(buf);
  539. return -1;
  540. }
  541. pos += ret;
  542. pac = pac_root;
  543. while (pac) {
  544. if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
  545. os_free(buf);
  546. return -1;
  547. }
  548. count++;
  549. pac = pac->next;
  550. }
  551. if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
  552. os_free(buf);
  553. return -1;
  554. }
  555. wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
  556. count, pac_file);
  557. return 0;
  558. }
  559. /**
  560. * eap_fast_pac_list_truncate - Truncate a PAC list to the given length
  561. * @pac_root: Root of the PAC list
  562. * @max_len: Maximum length of the list (>= 1)
  563. * Returns: Number of PAC entries removed
  564. */
  565. size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
  566. size_t max_len)
  567. {
  568. struct eap_fast_pac *pac, *prev;
  569. size_t count;
  570. pac = pac_root;
  571. prev = NULL;
  572. count = 0;
  573. while (pac) {
  574. count++;
  575. if (count > max_len)
  576. break;
  577. prev = pac;
  578. pac = pac->next;
  579. }
  580. if (count <= max_len || prev == NULL)
  581. return 0;
  582. count = 0;
  583. prev->next = NULL;
  584. while (pac) {
  585. prev = pac;
  586. pac = pac->next;
  587. eap_fast_free_pac(prev);
  588. count++;
  589. }
  590. return count;
  591. }
  592. static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
  593. {
  594. u8 *pos, *end;
  595. u16 type, len;
  596. pos = pac->pac_info;
  597. end = pos + pac->pac_info_len;
  598. while (pos + 4 < end) {
  599. type = WPA_GET_BE16(pos);
  600. pos += 2;
  601. len = WPA_GET_BE16(pos);
  602. pos += 2;
  603. if (pos + len > end)
  604. break;
  605. if (type == PAC_TYPE_A_ID) {
  606. os_free(pac->a_id);
  607. pac->a_id = os_malloc(len);
  608. if (pac->a_id == NULL)
  609. break;
  610. os_memcpy(pac->a_id, pos, len);
  611. pac->a_id_len = len;
  612. }
  613. if (type == PAC_TYPE_A_ID_INFO) {
  614. os_free(pac->a_id_info);
  615. pac->a_id_info = os_malloc(len);
  616. if (pac->a_id_info == NULL)
  617. break;
  618. os_memcpy(pac->a_id_info, pos, len);
  619. pac->a_id_info_len = len;
  620. }
  621. pos += len;
  622. }
  623. }
  624. /**
  625. * eap_fast_load_pac_bin - Load PAC entries (binary format)
  626. * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
  627. * @pac_root: Pointer to root of the PAC list (to be filled)
  628. * @pac_file: Name of the PAC file/blob to load
  629. * Returns: 0 on success, -1 on failure
  630. */
  631. int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
  632. const char *pac_file)
  633. {
  634. const struct wpa_config_blob *blob = NULL;
  635. u8 *buf, *end, *pos;
  636. size_t len, count = 0;
  637. struct eap_fast_pac *pac, *prev;
  638. *pac_root = NULL;
  639. if (pac_file == NULL)
  640. return -1;
  641. if (os_strncmp(pac_file, "blob://", 7) == 0) {
  642. blob = eap_get_config_blob(sm, pac_file + 7);
  643. if (blob == NULL) {
  644. wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
  645. "assume no PAC entries have been "
  646. "provisioned", pac_file + 7);
  647. return 0;
  648. }
  649. buf = blob->data;
  650. len = blob->len;
  651. } else {
  652. buf = (u8 *) os_readfile(pac_file, &len);
  653. if (buf == NULL) {
  654. wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
  655. "assume no PAC entries have been "
  656. "provisioned", pac_file);
  657. return 0;
  658. }
  659. }
  660. if (len == 0) {
  661. if (blob == NULL)
  662. os_free(buf);
  663. return 0;
  664. }
  665. if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC ||
  666. WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) {
  667. wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)",
  668. pac_file);
  669. if (blob == NULL)
  670. os_free(buf);
  671. return -1;
  672. }
  673. pac = prev = NULL;
  674. pos = buf + 6;
  675. end = buf + len;
  676. while (pos < end) {
  677. if (end - pos < 2 + 32 + 2 + 2)
  678. goto parse_fail;
  679. pac = os_zalloc(sizeof(*pac));
  680. if (pac == NULL)
  681. goto parse_fail;
  682. pac->pac_type = WPA_GET_BE16(pos);
  683. pos += 2;
  684. os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
  685. pos += EAP_FAST_PAC_KEY_LEN;
  686. pac->pac_opaque_len = WPA_GET_BE16(pos);
  687. pos += 2;
  688. if (pos + pac->pac_opaque_len + 2 > end)
  689. goto parse_fail;
  690. pac->pac_opaque = os_malloc(pac->pac_opaque_len);
  691. if (pac->pac_opaque == NULL)
  692. goto parse_fail;
  693. os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
  694. pos += pac->pac_opaque_len;
  695. pac->pac_info_len = WPA_GET_BE16(pos);
  696. pos += 2;
  697. if (pos + pac->pac_info_len > end)
  698. goto parse_fail;
  699. pac->pac_info = os_malloc(pac->pac_info_len);
  700. if (pac->pac_info == NULL)
  701. goto parse_fail;
  702. os_memcpy(pac->pac_info, pos, pac->pac_info_len);
  703. pos += pac->pac_info_len;
  704. eap_fast_pac_get_a_id(pac);
  705. count++;
  706. if (prev)
  707. prev->next = pac;
  708. else
  709. *pac_root = pac;
  710. prev = pac;
  711. }
  712. if (blob == NULL)
  713. os_free(buf);
  714. wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)",
  715. (unsigned long) count, pac_file);
  716. return 0;
  717. parse_fail:
  718. wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)",
  719. pac_file);
  720. if (blob == NULL)
  721. os_free(buf);
  722. if (pac)
  723. eap_fast_free_pac(pac);
  724. return -1;
  725. }
  726. /**
  727. * eap_fast_save_pac_bin - Save PAC entries (binary format)
  728. * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
  729. * @pac_root: Root of the PAC list
  730. * @pac_file: Name of the PAC file/blob
  731. * Returns: 0 on success, -1 on failure
  732. */
  733. int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
  734. const char *pac_file)
  735. {
  736. size_t len, count = 0;
  737. struct eap_fast_pac *pac;
  738. u8 *buf, *pos;
  739. len = 6;
  740. pac = pac_root;
  741. while (pac) {
  742. if (pac->pac_opaque_len > 65535 ||
  743. pac->pac_info_len > 65535)
  744. return -1;
  745. len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
  746. 2 + pac->pac_info_len;
  747. pac = pac->next;
  748. }
  749. buf = os_malloc(len);
  750. if (buf == NULL)
  751. return -1;
  752. pos = buf;
  753. WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
  754. pos += 4;
  755. WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
  756. pos += 2;
  757. pac = pac_root;
  758. while (pac) {
  759. WPA_PUT_BE16(pos, pac->pac_type);
  760. pos += 2;
  761. os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
  762. pos += EAP_FAST_PAC_KEY_LEN;
  763. WPA_PUT_BE16(pos, pac->pac_opaque_len);
  764. pos += 2;
  765. os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
  766. pos += pac->pac_opaque_len;
  767. WPA_PUT_BE16(pos, pac->pac_info_len);
  768. pos += 2;
  769. os_memcpy(pos, pac->pac_info, pac->pac_info_len);
  770. pos += pac->pac_info_len;
  771. pac = pac->next;
  772. count++;
  773. }
  774. if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
  775. os_free(buf);
  776. return -1;
  777. }
  778. wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' "
  779. "(bin)", (unsigned long) count, pac_file);
  780. return 0;
  781. }