eap_server_tnc.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. /*
  2. * EAP server method: EAP-TNC (Trusted Network Connect)
  3. * Copyright (c) 2007-2010, 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 "base64.h"
  17. #include "eap_i.h"
  18. #include "tncs.h"
  19. struct eap_tnc_data {
  20. enum eap_tnc_state {
  21. START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE,
  22. FAIL
  23. } state;
  24. enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation;
  25. struct tncs_data *tncs;
  26. struct wpabuf *in_buf;
  27. struct wpabuf *out_buf;
  28. size_t out_used;
  29. size_t fragment_size;
  30. unsigned int was_done:1;
  31. unsigned int was_fail:1;
  32. };
  33. /* EAP-TNC Flags */
  34. #define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
  35. #define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
  36. #define EAP_TNC_FLAGS_START 0x20
  37. #define EAP_TNC_VERSION_MASK 0x07
  38. #define EAP_TNC_VERSION 1
  39. static const char * eap_tnc_state_txt(enum eap_tnc_state state)
  40. {
  41. switch (state) {
  42. case START:
  43. return "START";
  44. case CONTINUE:
  45. return "CONTINUE";
  46. case RECOMMENDATION:
  47. return "RECOMMENDATION";
  48. case FRAG_ACK:
  49. return "FRAG_ACK";
  50. case WAIT_FRAG_ACK:
  51. return "WAIT_FRAG_ACK";
  52. case DONE:
  53. return "DONE";
  54. case FAIL:
  55. return "FAIL";
  56. }
  57. return "??";
  58. }
  59. static void eap_tnc_set_state(struct eap_tnc_data *data,
  60. enum eap_tnc_state new_state)
  61. {
  62. wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s",
  63. eap_tnc_state_txt(data->state),
  64. eap_tnc_state_txt(new_state));
  65. data->state = new_state;
  66. }
  67. static void * eap_tnc_init(struct eap_sm *sm)
  68. {
  69. struct eap_tnc_data *data;
  70. data = os_zalloc(sizeof(*data));
  71. if (data == NULL)
  72. return NULL;
  73. eap_tnc_set_state(data, START);
  74. data->tncs = tncs_init();
  75. if (data->tncs == NULL) {
  76. os_free(data);
  77. return NULL;
  78. }
  79. data->fragment_size = sm->fragment_size > 100 ?
  80. sm->fragment_size - 98 : 1300;
  81. return data;
  82. }
  83. static void eap_tnc_reset(struct eap_sm *sm, void *priv)
  84. {
  85. struct eap_tnc_data *data = priv;
  86. wpabuf_free(data->in_buf);
  87. wpabuf_free(data->out_buf);
  88. tncs_deinit(data->tncs);
  89. os_free(data);
  90. }
  91. static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm,
  92. struct eap_tnc_data *data, u8 id)
  93. {
  94. struct wpabuf *req;
  95. req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST,
  96. id);
  97. if (req == NULL) {
  98. wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for "
  99. "request");
  100. eap_tnc_set_state(data, FAIL);
  101. return NULL;
  102. }
  103. wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION);
  104. eap_tnc_set_state(data, CONTINUE);
  105. return req;
  106. }
  107. static struct wpabuf * eap_tnc_build(struct eap_sm *sm,
  108. struct eap_tnc_data *data)
  109. {
  110. struct wpabuf *req;
  111. u8 *rpos, *rpos1;
  112. size_t rlen;
  113. char *start_buf, *end_buf;
  114. size_t start_len, end_len;
  115. size_t imv_len;
  116. imv_len = tncs_total_send_len(data->tncs);
  117. start_buf = tncs_if_tnccs_start(data->tncs);
  118. if (start_buf == NULL)
  119. return NULL;
  120. start_len = os_strlen(start_buf);
  121. end_buf = tncs_if_tnccs_end();
  122. if (end_buf == NULL) {
  123. os_free(start_buf);
  124. return NULL;
  125. }
  126. end_len = os_strlen(end_buf);
  127. rlen = start_len + imv_len + end_len;
  128. req = wpabuf_alloc(rlen);
  129. if (req == NULL) {
  130. os_free(start_buf);
  131. os_free(end_buf);
  132. return NULL;
  133. }
  134. wpabuf_put_data(req, start_buf, start_len);
  135. os_free(start_buf);
  136. rpos1 = wpabuf_put(req, 0);
  137. rpos = tncs_copy_send_buf(data->tncs, rpos1);
  138. wpabuf_put(req, rpos - rpos1);
  139. wpabuf_put_data(req, end_buf, end_len);
  140. os_free(end_buf);
  141. wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request",
  142. wpabuf_head(req), wpabuf_len(req));
  143. return req;
  144. }
  145. static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm,
  146. struct eap_tnc_data *data)
  147. {
  148. switch (data->recommendation) {
  149. case ALLOW:
  150. eap_tnc_set_state(data, DONE);
  151. break;
  152. case ISOLATE:
  153. eap_tnc_set_state(data, FAIL);
  154. /* TODO: support assignment to a different VLAN */
  155. break;
  156. case NO_ACCESS:
  157. eap_tnc_set_state(data, FAIL);
  158. break;
  159. case NO_RECOMMENDATION:
  160. eap_tnc_set_state(data, DONE);
  161. break;
  162. default:
  163. wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation");
  164. return NULL;
  165. }
  166. return eap_tnc_build(sm, data);
  167. }
  168. static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code)
  169. {
  170. struct wpabuf *msg;
  171. msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id);
  172. if (msg == NULL) {
  173. wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory "
  174. "for fragment ack");
  175. return NULL;
  176. }
  177. wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */
  178. wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack");
  179. return msg;
  180. }
  181. static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id)
  182. {
  183. struct wpabuf *req;
  184. u8 flags;
  185. size_t send_len, plen;
  186. wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request");
  187. flags = EAP_TNC_VERSION;
  188. send_len = wpabuf_len(data->out_buf) - data->out_used;
  189. if (1 + send_len > data->fragment_size) {
  190. send_len = data->fragment_size - 1;
  191. flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS;
  192. if (data->out_used == 0) {
  193. flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED;
  194. send_len -= 4;
  195. }
  196. }
  197. plen = 1 + send_len;
  198. if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
  199. plen += 4;
  200. req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen,
  201. EAP_CODE_REQUEST, id);
  202. if (req == NULL)
  203. return NULL;
  204. wpabuf_put_u8(req, flags); /* Flags */
  205. if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
  206. wpabuf_put_be32(req, wpabuf_len(data->out_buf));
  207. wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
  208. send_len);
  209. data->out_used += send_len;
  210. if (data->out_used == wpabuf_len(data->out_buf)) {
  211. wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
  212. "(message sent completely)",
  213. (unsigned long) send_len);
  214. wpabuf_free(data->out_buf);
  215. data->out_buf = NULL;
  216. data->out_used = 0;
  217. if (data->was_fail)
  218. eap_tnc_set_state(data, FAIL);
  219. else if (data->was_done)
  220. eap_tnc_set_state(data, DONE);
  221. } else {
  222. wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
  223. "(%lu more to send)", (unsigned long) send_len,
  224. (unsigned long) wpabuf_len(data->out_buf) -
  225. data->out_used);
  226. if (data->state == FAIL)
  227. data->was_fail = 1;
  228. else if (data->state == DONE)
  229. data->was_done = 1;
  230. eap_tnc_set_state(data, WAIT_FRAG_ACK);
  231. }
  232. return req;
  233. }
  234. static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id)
  235. {
  236. struct eap_tnc_data *data = priv;
  237. switch (data->state) {
  238. case START:
  239. tncs_init_connection(data->tncs);
  240. return eap_tnc_build_start(sm, data, id);
  241. case CONTINUE:
  242. if (data->out_buf == NULL) {
  243. data->out_buf = eap_tnc_build(sm, data);
  244. if (data->out_buf == NULL) {
  245. wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
  246. "generate message");
  247. return NULL;
  248. }
  249. data->out_used = 0;
  250. }
  251. return eap_tnc_build_msg(data, id);
  252. case RECOMMENDATION:
  253. if (data->out_buf == NULL) {
  254. data->out_buf = eap_tnc_build_recommendation(sm, data);
  255. if (data->out_buf == NULL) {
  256. wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
  257. "generate recommendation message");
  258. return NULL;
  259. }
  260. data->out_used = 0;
  261. }
  262. return eap_tnc_build_msg(data, id);
  263. case WAIT_FRAG_ACK:
  264. return eap_tnc_build_msg(data, id);
  265. case FRAG_ACK:
  266. return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST);
  267. case DONE:
  268. case FAIL:
  269. return NULL;
  270. }
  271. return NULL;
  272. }
  273. static Boolean eap_tnc_check(struct eap_sm *sm, void *priv,
  274. struct wpabuf *respData)
  275. {
  276. struct eap_tnc_data *data = priv;
  277. const u8 *pos;
  278. size_t len;
  279. pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData,
  280. &len);
  281. if (pos == NULL) {
  282. wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame");
  283. return TRUE;
  284. }
  285. if (len == 0 && data->state != WAIT_FRAG_ACK) {
  286. wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)");
  287. return TRUE;
  288. }
  289. if (len == 0)
  290. return FALSE; /* Fragment ACK does not include flags */
  291. if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
  292. wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
  293. *pos & EAP_TNC_VERSION_MASK);
  294. return TRUE;
  295. }
  296. if (*pos & EAP_TNC_FLAGS_START) {
  297. wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag");
  298. return TRUE;
  299. }
  300. return FALSE;
  301. }
  302. static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf)
  303. {
  304. enum tncs_process_res res;
  305. res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf),
  306. wpabuf_len(inbuf));
  307. switch (res) {
  308. case TNCCS_RECOMMENDATION_ALLOW:
  309. wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access");
  310. eap_tnc_set_state(data, RECOMMENDATION);
  311. data->recommendation = ALLOW;
  312. break;
  313. case TNCCS_RECOMMENDATION_NO_RECOMMENDATION:
  314. wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation");
  315. eap_tnc_set_state(data, RECOMMENDATION);
  316. data->recommendation = NO_RECOMMENDATION;
  317. break;
  318. case TNCCS_RECOMMENDATION_ISOLATE:
  319. wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation");
  320. eap_tnc_set_state(data, RECOMMENDATION);
  321. data->recommendation = ISOLATE;
  322. break;
  323. case TNCCS_RECOMMENDATION_NO_ACCESS:
  324. wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access");
  325. eap_tnc_set_state(data, RECOMMENDATION);
  326. data->recommendation = NO_ACCESS;
  327. break;
  328. case TNCCS_PROCESS_ERROR:
  329. wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error");
  330. eap_tnc_set_state(data, FAIL);
  331. break;
  332. default:
  333. break;
  334. }
  335. }
  336. static int eap_tnc_process_cont(struct eap_tnc_data *data,
  337. const u8 *buf, size_t len)
  338. {
  339. /* Process continuation of a pending message */
  340. if (len > wpabuf_tailroom(data->in_buf)) {
  341. wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow");
  342. eap_tnc_set_state(data, FAIL);
  343. return -1;
  344. }
  345. wpabuf_put_data(data->in_buf, buf, len);
  346. wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu "
  347. "bytes more", (unsigned long) len,
  348. (unsigned long) wpabuf_tailroom(data->in_buf));
  349. return 0;
  350. }
  351. static int eap_tnc_process_fragment(struct eap_tnc_data *data,
  352. u8 flags, u32 message_length,
  353. const u8 *buf, size_t len)
  354. {
  355. /* Process a fragment that is not the last one of the message */
  356. if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) {
  357. wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a "
  358. "fragmented packet");
  359. return -1;
  360. }
  361. if (data->in_buf == NULL) {
  362. /* First fragment of the message */
  363. data->in_buf = wpabuf_alloc(message_length);
  364. if (data->in_buf == NULL) {
  365. wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for "
  366. "message");
  367. return -1;
  368. }
  369. wpabuf_put_data(data->in_buf, buf, len);
  370. wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first "
  371. "fragment, waiting for %lu bytes more",
  372. (unsigned long) len,
  373. (unsigned long) wpabuf_tailroom(data->in_buf));
  374. }
  375. return 0;
  376. }
  377. static void eap_tnc_process(struct eap_sm *sm, void *priv,
  378. struct wpabuf *respData)
  379. {
  380. struct eap_tnc_data *data = priv;
  381. const u8 *pos, *end;
  382. size_t len;
  383. u8 flags;
  384. u32 message_length = 0;
  385. struct wpabuf tmpbuf;
  386. pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len);
  387. if (pos == NULL)
  388. return; /* Should not happen; message already verified */
  389. end = pos + len;
  390. if (len == 1 && (data->state == DONE || data->state == FAIL)) {
  391. wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last "
  392. "message");
  393. return;
  394. }
  395. if (len == 0) {
  396. /* fragment ack */
  397. flags = 0;
  398. } else
  399. flags = *pos++;
  400. if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
  401. if (end - pos < 4) {
  402. wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
  403. eap_tnc_set_state(data, FAIL);
  404. return;
  405. }
  406. message_length = WPA_GET_BE32(pos);
  407. pos += 4;
  408. if (message_length < (u32) (end - pos)) {
  409. wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
  410. "Length (%d; %ld remaining in this msg)",
  411. message_length, (long) (end - pos));
  412. eap_tnc_set_state(data, FAIL);
  413. return;
  414. }
  415. }
  416. wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x "
  417. "Message Length %u", flags, message_length);
  418. if (data->state == WAIT_FRAG_ACK) {
  419. if (len > 1) {
  420. wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload "
  421. "in WAIT_FRAG_ACK state");
  422. eap_tnc_set_state(data, FAIL);
  423. return;
  424. }
  425. wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
  426. eap_tnc_set_state(data, CONTINUE);
  427. return;
  428. }
  429. if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
  430. eap_tnc_set_state(data, FAIL);
  431. return;
  432. }
  433. if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
  434. if (eap_tnc_process_fragment(data, flags, message_length,
  435. pos, end - pos) < 0)
  436. eap_tnc_set_state(data, FAIL);
  437. else
  438. eap_tnc_set_state(data, FRAG_ACK);
  439. return;
  440. } else if (data->state == FRAG_ACK) {
  441. wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
  442. eap_tnc_set_state(data, CONTINUE);
  443. }
  444. if (data->in_buf == NULL) {
  445. /* Wrap unfragmented messages as wpabuf without extra copy */
  446. wpabuf_set(&tmpbuf, pos, end - pos);
  447. data->in_buf = &tmpbuf;
  448. }
  449. wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload",
  450. wpabuf_head(data->in_buf), wpabuf_len(data->in_buf));
  451. tncs_process(data, data->in_buf);
  452. if (data->in_buf != &tmpbuf)
  453. wpabuf_free(data->in_buf);
  454. data->in_buf = NULL;
  455. }
  456. static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv)
  457. {
  458. struct eap_tnc_data *data = priv;
  459. return data->state == DONE || data->state == FAIL;
  460. }
  461. static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv)
  462. {
  463. struct eap_tnc_data *data = priv;
  464. return data->state == DONE;
  465. }
  466. int eap_server_tnc_register(void)
  467. {
  468. struct eap_method *eap;
  469. int ret;
  470. eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
  471. EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
  472. if (eap == NULL)
  473. return -1;
  474. eap->init = eap_tnc_init;
  475. eap->reset = eap_tnc_reset;
  476. eap->buildReq = eap_tnc_buildReq;
  477. eap->check = eap_tnc_check;
  478. eap->process = eap_tnc_process;
  479. eap->isDone = eap_tnc_isDone;
  480. eap->isSuccess = eap_tnc_isSuccess;
  481. ret = eap_server_method_register(eap);
  482. if (ret)
  483. eap_server_method_free(eap);
  484. return ret;
  485. }