eap_server_tnc.c 13 KB

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