eap_tnc.c 12 KB

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