eap_wsc.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. /*
  2. * EAP-WSC peer for Wi-Fi Protected Setup
  3. * Copyright (c) 2007-2009, 2012, 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 "uuid.h"
  11. #include "eap_i.h"
  12. #include "eap_common/eap_wsc_common.h"
  13. #include "wps/wps.h"
  14. #include "wps/wps_defs.h"
  15. struct eap_wsc_data {
  16. enum { WAIT_START, MESG, WAIT_FRAG_ACK, FAIL } state;
  17. int registrar;
  18. struct wpabuf *in_buf;
  19. struct wpabuf *out_buf;
  20. enum wsc_op_code in_op_code, out_op_code;
  21. size_t out_used;
  22. size_t fragment_size;
  23. struct wps_data *wps;
  24. struct wps_context *wps_ctx;
  25. };
  26. static const char * eap_wsc_state_txt(int state)
  27. {
  28. switch (state) {
  29. case WAIT_START:
  30. return "WAIT_START";
  31. case MESG:
  32. return "MESG";
  33. case WAIT_FRAG_ACK:
  34. return "WAIT_FRAG_ACK";
  35. case FAIL:
  36. return "FAIL";
  37. default:
  38. return "?";
  39. }
  40. }
  41. static void eap_wsc_state(struct eap_wsc_data *data, int state)
  42. {
  43. wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
  44. eap_wsc_state_txt(data->state),
  45. eap_wsc_state_txt(state));
  46. data->state = state;
  47. }
  48. static int eap_wsc_new_ap_settings(struct wps_credential *cred,
  49. const char *params)
  50. {
  51. const char *pos, *end;
  52. size_t len;
  53. os_memset(cred, 0, sizeof(*cred));
  54. pos = os_strstr(params, "new_ssid=");
  55. if (pos == NULL)
  56. return 0;
  57. pos += 9;
  58. end = os_strchr(pos, ' ');
  59. if (end == NULL)
  60. len = os_strlen(pos);
  61. else
  62. len = end - pos;
  63. if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
  64. hexstr2bin(pos, cred->ssid, len / 2)) {
  65. wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid");
  66. return -1;
  67. }
  68. cred->ssid_len = len / 2;
  69. pos = os_strstr(params, "new_auth=");
  70. if (pos == NULL) {
  71. wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth");
  72. return -1;
  73. }
  74. if (os_strncmp(pos + 9, "OPEN", 4) == 0)
  75. cred->auth_type = WPS_AUTH_OPEN;
  76. else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
  77. cred->auth_type = WPS_AUTH_WPAPSK;
  78. else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
  79. cred->auth_type = WPS_AUTH_WPA2PSK;
  80. else {
  81. wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth");
  82. return -1;
  83. }
  84. pos = os_strstr(params, "new_encr=");
  85. if (pos == NULL) {
  86. wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr");
  87. return -1;
  88. }
  89. if (os_strncmp(pos + 9, "NONE", 4) == 0)
  90. cred->encr_type = WPS_ENCR_NONE;
  91. #ifdef CONFIG_TESTING_OPTIONS
  92. else if (os_strncmp(pos + 9, "WEP", 3) == 0)
  93. cred->encr_type = WPS_ENCR_WEP;
  94. #endif /* CONFIG_TESTING_OPTIONS */
  95. else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
  96. cred->encr_type = WPS_ENCR_TKIP;
  97. else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
  98. cred->encr_type = WPS_ENCR_AES;
  99. else {
  100. wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr");
  101. return -1;
  102. }
  103. pos = os_strstr(params, "new_key=");
  104. if (pos == NULL)
  105. return 0;
  106. pos += 8;
  107. end = os_strchr(pos, ' ');
  108. if (end == NULL)
  109. len = os_strlen(pos);
  110. else
  111. len = end - pos;
  112. if ((len & 1) || len > 2 * sizeof(cred->key) ||
  113. hexstr2bin(pos, cred->key, len / 2)) {
  114. wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key");
  115. return -1;
  116. }
  117. cred->key_len = len / 2;
  118. return 1;
  119. }
  120. static void * eap_wsc_init(struct eap_sm *sm)
  121. {
  122. struct eap_wsc_data *data;
  123. const u8 *identity;
  124. size_t identity_len;
  125. int registrar;
  126. struct wps_config cfg;
  127. const char *pos, *end;
  128. const char *phase1;
  129. struct wps_context *wps;
  130. struct wps_credential new_ap_settings;
  131. int res;
  132. int nfc = 0;
  133. u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
  134. wps = sm->wps;
  135. if (wps == NULL) {
  136. wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
  137. return NULL;
  138. }
  139. identity = eap_get_config_identity(sm, &identity_len);
  140. if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
  141. os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
  142. registrar = 1; /* Supplicant is Registrar */
  143. else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
  144. os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
  145. registrar = 0; /* Supplicant is Enrollee */
  146. else {
  147. wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
  148. identity, identity_len);
  149. return NULL;
  150. }
  151. data = os_zalloc(sizeof(*data));
  152. if (data == NULL)
  153. return NULL;
  154. data->state = registrar ? MESG : WAIT_START;
  155. data->registrar = registrar;
  156. data->wps_ctx = wps;
  157. os_memset(&cfg, 0, sizeof(cfg));
  158. cfg.wps = wps;
  159. cfg.registrar = registrar;
  160. phase1 = eap_get_config_phase1(sm);
  161. if (phase1 == NULL) {
  162. wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
  163. "set");
  164. os_free(data);
  165. return NULL;
  166. }
  167. pos = os_strstr(phase1, "pin=");
  168. if (pos) {
  169. pos += 4;
  170. cfg.pin = (const u8 *) pos;
  171. while (*pos != '\0' && *pos != ' ')
  172. pos++;
  173. cfg.pin_len = pos - (const char *) cfg.pin;
  174. if (cfg.pin_len == 6 &&
  175. os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) {
  176. cfg.pin = NULL;
  177. cfg.pin_len = 0;
  178. nfc = 1;
  179. }
  180. } else {
  181. pos = os_strstr(phase1, "pbc=1");
  182. if (pos)
  183. cfg.pbc = 1;
  184. }
  185. pos = os_strstr(phase1, "dev_pw_id=");
  186. if (pos) {
  187. u16 id = atoi(pos + 10);
  188. if (id == DEV_PW_NFC_CONNECTION_HANDOVER)
  189. nfc = 1;
  190. if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER)
  191. cfg.dev_pw_id = id;
  192. }
  193. if (cfg.pin == NULL && !cfg.pbc && !nfc) {
  194. wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
  195. "configuration data");
  196. os_free(data);
  197. return NULL;
  198. }
  199. pos = os_strstr(phase1, " pkhash=");
  200. if (pos) {
  201. size_t len;
  202. pos += 8;
  203. end = os_strchr(pos, ' ');
  204. if (end)
  205. len = end - pos;
  206. else
  207. len = os_strlen(pos);
  208. if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
  209. hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
  210. wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
  211. os_free(data);
  212. return NULL;
  213. }
  214. cfg.peer_pubkey_hash = pkhash;
  215. }
  216. res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
  217. if (res < 0) {
  218. os_free(data);
  219. wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP "
  220. "settings");
  221. return NULL;
  222. }
  223. if (res == 1) {
  224. wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
  225. "WPS");
  226. cfg.new_ap_settings = &new_ap_settings;
  227. }
  228. data->wps = wps_init(&cfg);
  229. if (data->wps == NULL) {
  230. os_free(data);
  231. wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed");
  232. return NULL;
  233. }
  234. res = eap_get_config_fragment_size(sm);
  235. if (res > 0)
  236. data->fragment_size = res;
  237. else
  238. data->fragment_size = WSC_FRAGMENT_SIZE;
  239. wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
  240. (unsigned int) data->fragment_size);
  241. if (registrar && cfg.pin) {
  242. wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
  243. cfg.pin, cfg.pin_len, 0);
  244. }
  245. /* Use reduced client timeout for WPS to avoid long wait */
  246. if (sm->ClientTimeout > 30)
  247. sm->ClientTimeout = 30;
  248. return data;
  249. }
  250. static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
  251. {
  252. struct eap_wsc_data *data = priv;
  253. wpabuf_free(data->in_buf);
  254. wpabuf_free(data->out_buf);
  255. wps_deinit(data->wps);
  256. os_free(data->wps_ctx->network_key);
  257. data->wps_ctx->network_key = NULL;
  258. os_free(data);
  259. }
  260. static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
  261. struct eap_method_ret *ret, u8 id)
  262. {
  263. struct wpabuf *resp;
  264. u8 flags;
  265. size_t send_len, plen;
  266. ret->ignore = FALSE;
  267. wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
  268. ret->allowNotifications = TRUE;
  269. flags = 0;
  270. send_len = wpabuf_len(data->out_buf) - data->out_used;
  271. if (2 + send_len > data->fragment_size) {
  272. send_len = data->fragment_size - 2;
  273. flags |= WSC_FLAGS_MF;
  274. if (data->out_used == 0) {
  275. flags |= WSC_FLAGS_LF;
  276. send_len -= 2;
  277. }
  278. }
  279. plen = 2 + send_len;
  280. if (flags & WSC_FLAGS_LF)
  281. plen += 2;
  282. resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
  283. EAP_CODE_RESPONSE, id);
  284. if (resp == NULL)
  285. return NULL;
  286. wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
  287. wpabuf_put_u8(resp, flags); /* Flags */
  288. if (flags & WSC_FLAGS_LF)
  289. wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
  290. wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
  291. send_len);
  292. data->out_used += send_len;
  293. ret->methodState = METHOD_MAY_CONT;
  294. ret->decision = DECISION_FAIL;
  295. if (data->out_used == wpabuf_len(data->out_buf)) {
  296. wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
  297. "(message sent completely)",
  298. (unsigned long) send_len);
  299. wpabuf_free(data->out_buf);
  300. data->out_buf = NULL;
  301. data->out_used = 0;
  302. if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
  303. data->out_op_code == WSC_NACK ||
  304. data->out_op_code == WSC_Done) {
  305. eap_wsc_state(data, FAIL);
  306. ret->methodState = METHOD_DONE;
  307. } else
  308. eap_wsc_state(data, MESG);
  309. } else {
  310. wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
  311. "(%lu more to send)", (unsigned long) send_len,
  312. (unsigned long) wpabuf_len(data->out_buf) -
  313. data->out_used);
  314. eap_wsc_state(data, WAIT_FRAG_ACK);
  315. }
  316. return resp;
  317. }
  318. static int eap_wsc_process_cont(struct eap_wsc_data *data,
  319. const u8 *buf, size_t len, u8 op_code)
  320. {
  321. /* Process continuation of a pending message */
  322. if (op_code != data->in_op_code) {
  323. wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
  324. "fragment (expected %d)",
  325. op_code, data->in_op_code);
  326. return -1;
  327. }
  328. if (len > wpabuf_tailroom(data->in_buf)) {
  329. wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
  330. eap_wsc_state(data, FAIL);
  331. return -1;
  332. }
  333. wpabuf_put_data(data->in_buf, buf, len);
  334. wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
  335. "for %lu bytes more", (unsigned long) len,
  336. (unsigned long) wpabuf_tailroom(data->in_buf));
  337. return 0;
  338. }
  339. static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
  340. struct eap_method_ret *ret,
  341. u8 id, u8 flags, u8 op_code,
  342. u16 message_length,
  343. const u8 *buf, size_t len)
  344. {
  345. /* Process a fragment that is not the last one of the message */
  346. if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
  347. wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
  348. "fragmented packet");
  349. ret->ignore = TRUE;
  350. return NULL;
  351. }
  352. if (data->in_buf == NULL) {
  353. /* First fragment of the message */
  354. data->in_buf = wpabuf_alloc(message_length);
  355. if (data->in_buf == NULL) {
  356. wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
  357. "message");
  358. ret->ignore = TRUE;
  359. return NULL;
  360. }
  361. data->in_op_code = op_code;
  362. wpabuf_put_data(data->in_buf, buf, len);
  363. wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
  364. "fragment, waiting for %lu bytes more",
  365. (unsigned long) len,
  366. (unsigned long) wpabuf_tailroom(data->in_buf));
  367. }
  368. return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
  369. }
  370. static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
  371. struct eap_method_ret *ret,
  372. const struct wpabuf *reqData)
  373. {
  374. struct eap_wsc_data *data = priv;
  375. const u8 *start, *pos, *end;
  376. size_t len;
  377. u8 op_code, flags, id;
  378. u16 message_length = 0;
  379. enum wps_process_res res;
  380. struct wpabuf tmpbuf;
  381. struct wpabuf *r;
  382. pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
  383. &len);
  384. if (pos == NULL || len < 2) {
  385. ret->ignore = TRUE;
  386. return NULL;
  387. }
  388. id = eap_get_id(reqData);
  389. start = pos;
  390. end = start + len;
  391. op_code = *pos++;
  392. flags = *pos++;
  393. if (flags & WSC_FLAGS_LF) {
  394. if (end - pos < 2) {
  395. wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
  396. ret->ignore = TRUE;
  397. return NULL;
  398. }
  399. message_length = WPA_GET_BE16(pos);
  400. pos += 2;
  401. if (message_length < end - pos || message_length > 50000) {
  402. wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
  403. "Length");
  404. ret->ignore = TRUE;
  405. return NULL;
  406. }
  407. }
  408. wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
  409. "Flags 0x%x Message Length %d",
  410. op_code, flags, message_length);
  411. if (data->state == WAIT_FRAG_ACK) {
  412. if (op_code != WSC_FRAG_ACK) {
  413. wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
  414. "in WAIT_FRAG_ACK state", op_code);
  415. ret->ignore = TRUE;
  416. return NULL;
  417. }
  418. wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
  419. eap_wsc_state(data, MESG);
  420. return eap_wsc_build_msg(data, ret, id);
  421. }
  422. if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
  423. op_code != WSC_Done && op_code != WSC_Start) {
  424. wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
  425. op_code);
  426. ret->ignore = TRUE;
  427. return NULL;
  428. }
  429. if (data->state == WAIT_START) {
  430. if (op_code != WSC_Start) {
  431. wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
  432. "in WAIT_START state", op_code);
  433. ret->ignore = TRUE;
  434. return NULL;
  435. }
  436. wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
  437. eap_wsc_state(data, MESG);
  438. /* Start message has empty payload, skip processing */
  439. goto send_msg;
  440. } else if (op_code == WSC_Start) {
  441. wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
  442. op_code);
  443. ret->ignore = TRUE;
  444. return NULL;
  445. }
  446. if (data->in_buf &&
  447. eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
  448. ret->ignore = TRUE;
  449. return NULL;
  450. }
  451. if (flags & WSC_FLAGS_MF) {
  452. return eap_wsc_process_fragment(data, ret, id, flags, op_code,
  453. message_length, pos,
  454. end - pos);
  455. }
  456. if (data->in_buf == NULL) {
  457. /* Wrap unfragmented messages as wpabuf without extra copy */
  458. wpabuf_set(&tmpbuf, pos, end - pos);
  459. data->in_buf = &tmpbuf;
  460. }
  461. res = wps_process_msg(data->wps, op_code, data->in_buf);
  462. switch (res) {
  463. case WPS_DONE:
  464. wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
  465. "successfully - wait for EAP failure");
  466. eap_wsc_state(data, FAIL);
  467. break;
  468. case WPS_CONTINUE:
  469. eap_wsc_state(data, MESG);
  470. break;
  471. case WPS_FAILURE:
  472. case WPS_PENDING:
  473. wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
  474. eap_wsc_state(data, FAIL);
  475. break;
  476. }
  477. if (data->in_buf != &tmpbuf)
  478. wpabuf_free(data->in_buf);
  479. data->in_buf = NULL;
  480. send_msg:
  481. if (data->out_buf == NULL) {
  482. data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
  483. if (data->out_buf == NULL) {
  484. wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
  485. "message from WPS");
  486. eap_wsc_state(data, FAIL);
  487. ret->methodState = METHOD_DONE;
  488. ret->decision = DECISION_FAIL;
  489. return NULL;
  490. }
  491. data->out_used = 0;
  492. }
  493. eap_wsc_state(data, MESG);
  494. r = eap_wsc_build_msg(data, ret, id);
  495. if (data->state == FAIL && ret->methodState == METHOD_DONE) {
  496. /* Use reduced client timeout for WPS to avoid long wait */
  497. if (sm->ClientTimeout > 2)
  498. sm->ClientTimeout = 2;
  499. }
  500. return r;
  501. }
  502. int eap_peer_wsc_register(void)
  503. {
  504. struct eap_method *eap;
  505. eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
  506. EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
  507. "WSC");
  508. if (eap == NULL)
  509. return -1;
  510. eap->init = eap_wsc_init;
  511. eap->deinit = eap_wsc_deinit;
  512. eap->process = eap_wsc_process;
  513. return eap_peer_method_register(eap);
  514. }