eap_fast_pac.c 21 KB

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