$search
00001 /* 00002 * EAP peer method: EAP-TNC (Trusted Network Connect) 00003 * Copyright (c) 2007, Jouni Malinen <j@w1.fi> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License version 2 as 00007 * published by the Free Software Foundation. 00008 * 00009 * Alternatively, this software may be distributed under the terms of BSD 00010 * license. 00011 * 00012 * See README and COPYING for more details. 00013 */ 00014 00015 #include "includes.h" 00016 00017 #include "common.h" 00018 #include "base64.h" 00019 #include "eap_i.h" 00020 #include "tncc.h" 00021 00022 00023 struct eap_tnc_data { 00024 enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state; 00025 struct tncc_data *tncc; 00026 struct wpabuf *in_buf; 00027 struct wpabuf *out_buf; 00028 size_t out_used; 00029 size_t fragment_size; 00030 }; 00031 00032 00033 /* EAP-TNC Flags */ 00034 #define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80 00035 #define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40 00036 #define EAP_TNC_FLAGS_START 0x20 00037 #define EAP_TNC_VERSION_MASK 0x07 00038 00039 #define EAP_TNC_VERSION 1 00040 00041 00042 static void * eap_tnc_init(struct eap_sm *sm) 00043 { 00044 struct eap_tnc_data *data; 00045 00046 data = os_zalloc(sizeof(*data)); 00047 if (data == NULL) 00048 return NULL; 00049 data->state = WAIT_START; 00050 data->fragment_size = 1300; 00051 data->tncc = tncc_init(); 00052 if (data->tncc == NULL) { 00053 os_free(data); 00054 return NULL; 00055 } 00056 00057 return data; 00058 } 00059 00060 00061 static void eap_tnc_deinit(struct eap_sm *sm, void *priv) 00062 { 00063 struct eap_tnc_data *data = priv; 00064 00065 wpabuf_free(data->in_buf); 00066 wpabuf_free(data->out_buf); 00067 tncc_deinit(data->tncc); 00068 os_free(data); 00069 } 00070 00071 00072 static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) 00073 { 00074 struct wpabuf *msg; 00075 00076 msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id); 00077 if (msg == NULL) { 00078 wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " 00079 "for fragment ack"); 00080 return NULL; 00081 } 00082 wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */ 00083 00084 wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); 00085 00086 return msg; 00087 } 00088 00089 00090 static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, 00091 struct eap_method_ret *ret, u8 id) 00092 { 00093 struct wpabuf *resp; 00094 u8 flags; 00095 size_t send_len, plen; 00096 00097 ret->ignore = FALSE; 00098 wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Response"); 00099 ret->allowNotifications = TRUE; 00100 00101 flags = EAP_TNC_VERSION; 00102 send_len = wpabuf_len(data->out_buf) - data->out_used; 00103 if (1 + send_len > data->fragment_size) { 00104 send_len = data->fragment_size - 1; 00105 flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS; 00106 if (data->out_used == 0) { 00107 flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED; 00108 send_len -= 4; 00109 } 00110 } 00111 00112 plen = 1 + send_len; 00113 if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) 00114 plen += 4; 00115 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen, 00116 EAP_CODE_RESPONSE, id); 00117 if (resp == NULL) 00118 return NULL; 00119 00120 wpabuf_put_u8(resp, flags); /* Flags */ 00121 if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) 00122 wpabuf_put_be32(resp, wpabuf_len(data->out_buf)); 00123 00124 wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, 00125 send_len); 00126 data->out_used += send_len; 00127 00128 ret->methodState = METHOD_MAY_CONT; 00129 ret->decision = DECISION_FAIL; 00130 00131 if (data->out_used == wpabuf_len(data->out_buf)) { 00132 wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " 00133 "(message sent completely)", 00134 (unsigned long) send_len); 00135 wpabuf_free(data->out_buf); 00136 data->out_buf = NULL; 00137 data->out_used = 0; 00138 } else { 00139 wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " 00140 "(%lu more to send)", (unsigned long) send_len, 00141 (unsigned long) wpabuf_len(data->out_buf) - 00142 data->out_used); 00143 data->state = WAIT_FRAG_ACK; 00144 } 00145 00146 return resp; 00147 } 00148 00149 00150 static int eap_tnc_process_cont(struct eap_tnc_data *data, 00151 const u8 *buf, size_t len) 00152 { 00153 /* Process continuation of a pending message */ 00154 if (len > wpabuf_tailroom(data->in_buf)) { 00155 wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow"); 00156 data->state = FAIL; 00157 return -1; 00158 } 00159 00160 wpabuf_put_data(data->in_buf, buf, len); 00161 wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for " 00162 "%lu bytes more", (unsigned long) len, 00163 (unsigned long) wpabuf_tailroom(data->in_buf)); 00164 00165 return 0; 00166 } 00167 00168 00169 static struct wpabuf * eap_tnc_process_fragment(struct eap_tnc_data *data, 00170 struct eap_method_ret *ret, 00171 u8 id, u8 flags, 00172 u32 message_length, 00173 const u8 *buf, size_t len) 00174 { 00175 /* Process a fragment that is not the last one of the message */ 00176 if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) { 00177 wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a " 00178 "fragmented packet"); 00179 ret->ignore = TRUE; 00180 return NULL; 00181 } 00182 00183 if (data->in_buf == NULL) { 00184 /* First fragment of the message */ 00185 data->in_buf = wpabuf_alloc(message_length); 00186 if (data->in_buf == NULL) { 00187 wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for " 00188 "message"); 00189 ret->ignore = TRUE; 00190 return NULL; 00191 } 00192 wpabuf_put_data(data->in_buf, buf, len); 00193 wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first " 00194 "fragment, waiting for %lu bytes more", 00195 (unsigned long) len, 00196 (unsigned long) wpabuf_tailroom(data->in_buf)); 00197 } 00198 00199 return eap_tnc_build_frag_ack(id, EAP_CODE_RESPONSE); 00200 } 00201 00202 00203 static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, 00204 struct eap_method_ret *ret, 00205 const struct wpabuf *reqData) 00206 { 00207 struct eap_tnc_data *data = priv; 00208 struct wpabuf *resp; 00209 const u8 *pos, *end; 00210 u8 *rpos, *rpos1; 00211 size_t len, rlen; 00212 size_t imc_len; 00213 char *start_buf, *end_buf; 00214 size_t start_len, end_len; 00215 int tncs_done = 0; 00216 u8 flags, id; 00217 u32 message_length = 0; 00218 struct wpabuf tmpbuf; 00219 00220 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, reqData, &len); 00221 if (pos == NULL) { 00222 wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (pos=%p len=%lu)", 00223 pos, (unsigned long) len); 00224 ret->ignore = TRUE; 00225 return NULL; 00226 } 00227 00228 id = eap_get_id(reqData); 00229 00230 end = pos + len; 00231 00232 if (len == 0) 00233 flags = 0; /* fragment ack */ 00234 else 00235 flags = *pos++; 00236 00237 if (len > 0 && (flags & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) { 00238 wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d", 00239 flags & EAP_TNC_VERSION_MASK); 00240 ret->ignore = TRUE; 00241 return NULL; 00242 } 00243 00244 if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { 00245 if (end - pos < 4) { 00246 wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); 00247 ret->ignore = TRUE; 00248 return NULL; 00249 } 00250 message_length = WPA_GET_BE32(pos); 00251 pos += 4; 00252 00253 if (message_length < (u32) (end - pos)) { 00254 wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " 00255 "Length (%d; %ld remaining in this msg)", 00256 message_length, (long) (end - pos)); 00257 ret->ignore = TRUE; 00258 return NULL; 00259 } 00260 } 00261 00262 wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x " 00263 "Message Length %u", flags, message_length); 00264 00265 if (data->state == WAIT_FRAG_ACK) { 00266 if (len > 1) { 00267 wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload in " 00268 "WAIT_FRAG_ACK state"); 00269 ret->ignore = TRUE; 00270 return NULL; 00271 } 00272 wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); 00273 data->state = PROC_MSG; 00274 return eap_tnc_build_msg(data, ret, id); 00275 } 00276 00277 if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { 00278 ret->ignore = TRUE; 00279 return NULL; 00280 } 00281 00282 if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) { 00283 return eap_tnc_process_fragment(data, ret, id, flags, 00284 message_length, pos, 00285 end - pos); 00286 } 00287 00288 if (data->in_buf == NULL) { 00289 /* Wrap unfragmented messages as wpabuf without extra copy */ 00290 wpabuf_set(&tmpbuf, pos, end - pos); 00291 data->in_buf = &tmpbuf; 00292 } 00293 00294 if (data->state == WAIT_START) { 00295 if (!(flags & EAP_TNC_FLAGS_START)) { 00296 wpa_printf(MSG_DEBUG, "EAP-TNC: Server did not use " 00297 "start flag in the first message"); 00298 ret->ignore = TRUE; 00299 goto fail; 00300 } 00301 00302 tncc_init_connection(data->tncc); 00303 00304 data->state = PROC_MSG; 00305 } else { 00306 enum tncc_process_res res; 00307 00308 if (flags & EAP_TNC_FLAGS_START) { 00309 wpa_printf(MSG_DEBUG, "EAP-TNC: Server used start " 00310 "flag again"); 00311 ret->ignore = TRUE; 00312 goto fail; 00313 } 00314 00315 res = tncc_process_if_tnccs(data->tncc, 00316 wpabuf_head(data->in_buf), 00317 wpabuf_len(data->in_buf)); 00318 switch (res) { 00319 case TNCCS_PROCESS_ERROR: 00320 ret->ignore = TRUE; 00321 goto fail; 00322 case TNCCS_PROCESS_OK_NO_RECOMMENDATION: 00323 case TNCCS_RECOMMENDATION_ERROR: 00324 wpa_printf(MSG_DEBUG, "EAP-TNC: No " 00325 "TNCCS-Recommendation received"); 00326 break; 00327 case TNCCS_RECOMMENDATION_ALLOW: 00328 wpa_msg(sm->msg_ctx, MSG_INFO, 00329 "TNC: Recommendation = allow"); 00330 tncs_done = 1; 00331 break; 00332 case TNCCS_RECOMMENDATION_NONE: 00333 wpa_msg(sm->msg_ctx, MSG_INFO, 00334 "TNC: Recommendation = none"); 00335 tncs_done = 1; 00336 break; 00337 case TNCCS_RECOMMENDATION_ISOLATE: 00338 wpa_msg(sm->msg_ctx, MSG_INFO, 00339 "TNC: Recommendation = isolate"); 00340 tncs_done = 1; 00341 break; 00342 } 00343 } 00344 00345 if (data->in_buf != &tmpbuf) 00346 wpabuf_free(data->in_buf); 00347 data->in_buf = NULL; 00348 00349 ret->ignore = FALSE; 00350 ret->methodState = METHOD_MAY_CONT; 00351 ret->decision = DECISION_UNCOND_SUCC; 00352 ret->allowNotifications = TRUE; 00353 00354 if (data->out_buf) { 00355 data->state = PROC_MSG; 00356 return eap_tnc_build_msg(data, ret, id); 00357 } 00358 00359 if (tncs_done) { 00360 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, 00361 EAP_CODE_RESPONSE, eap_get_id(reqData)); 00362 if (resp == NULL) 00363 return NULL; 00364 00365 wpabuf_put_u8(resp, EAP_TNC_VERSION); 00366 wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS done - reply with an " 00367 "empty ACK message"); 00368 return resp; 00369 } 00370 00371 imc_len = tncc_total_send_len(data->tncc); 00372 00373 start_buf = tncc_if_tnccs_start(data->tncc); 00374 if (start_buf == NULL) 00375 return NULL; 00376 start_len = os_strlen(start_buf); 00377 end_buf = tncc_if_tnccs_end(); 00378 if (end_buf == NULL) { 00379 os_free(start_buf); 00380 return NULL; 00381 } 00382 end_len = os_strlen(end_buf); 00383 00384 rlen = start_len + imc_len + end_len; 00385 resp = wpabuf_alloc(rlen); 00386 if (resp == NULL) { 00387 os_free(start_buf); 00388 os_free(end_buf); 00389 return NULL; 00390 } 00391 00392 wpabuf_put_data(resp, start_buf, start_len); 00393 os_free(start_buf); 00394 00395 rpos1 = wpabuf_put(resp, 0); 00396 rpos = tncc_copy_send_buf(data->tncc, rpos1); 00397 wpabuf_put(resp, rpos - rpos1); 00398 00399 wpabuf_put_data(resp, end_buf, end_len); 00400 os_free(end_buf); 00401 00402 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Response", 00403 wpabuf_head(resp), wpabuf_len(resp)); 00404 00405 data->out_buf = resp; 00406 data->state = PROC_MSG; 00407 return eap_tnc_build_msg(data, ret, id); 00408 00409 fail: 00410 if (data->in_buf == &tmpbuf) 00411 data->in_buf = NULL; 00412 return NULL; 00413 } 00414 00415 00416 int eap_peer_tnc_register(void) 00417 { 00418 struct eap_method *eap; 00419 int ret; 00420 00421 eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 00422 EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); 00423 if (eap == NULL) 00424 return -1; 00425 00426 eap->init = eap_tnc_init; 00427 eap->deinit = eap_tnc_deinit; 00428 eap->process = eap_tnc_process; 00429 00430 ret = eap_peer_method_register(eap); 00431 if (ret) 00432 eap_peer_method_free(eap); 00433 return ret; 00434 }