diff options
Diffstat (limited to 'src/libcharon/plugins/eap_peap/eap_peap_peer.c')
-rw-r--r-- | src/libcharon/plugins/eap_peap/eap_peap_peer.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/libcharon/plugins/eap_peap/eap_peap_peer.c b/src/libcharon/plugins/eap_peap/eap_peap_peer.c new file mode 100644 index 000000000..a4a92c78e --- /dev/null +++ b/src/libcharon/plugins/eap_peap/eap_peap_peer.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2011 Andreas Steffen + * Copyright (C) 2011 HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "eap_peap_peer.h" +#include "eap_peap_avp.h" + +#include <debug.h> +#include <daemon.h> + +typedef struct private_eap_peap_peer_t private_eap_peap_peer_t; + +/** + * Private data of an eap_peap_peer_t object. + */ +struct private_eap_peap_peer_t { + + /** + * Public eap_peap_peer_t interface. + */ + eap_peap_peer_t public; + + /** + * Server identity + */ + identification_t *server; + + /** + * Peer identity + */ + identification_t *peer; + + /** + * Current EAP-PEAP state + */ + bool start_phase2; + + /** + * Outer phase 1 EAP method + */ + eap_method_t *ph1_method; + + /** + * Current phase 2 EAP method + */ + eap_method_t *ph2_method; + + /** + * Pending outbound EAP message + */ + eap_payload_t *out; + + /** + * AVP handler + */ + eap_peap_avp_t *avp; +}; + +METHOD(tls_application_t, process, status_t, + private_eap_peap_peer_t *this, tls_reader_t *reader) +{ + chunk_t data = chunk_empty; + status_t status; + payload_t *payload; + eap_payload_t *in; + eap_code_t code; + eap_type_t type, received_type; + u_int32_t vendor, received_vendor; + + status = this->avp->process(this->avp, reader, &data, + this->ph1_method->get_identifier(this->ph1_method)); + switch (status) + { + case SUCCESS: + break; + case NEED_MORE: + return NEED_MORE; + case FAILED: + default: + return FAILED; + } + + in = eap_payload_create_data(data); + DBG3(DBG_IKE, "%B", &data); + chunk_free(&data); + payload = (payload_t*)in; + + if (payload->verify(payload) != SUCCESS) + { + in->destroy(in); + return FAILED; + } + + code = in->get_code(in); + if (code == EAP_REQUEST || code == EAP_RESPONSE) + { + received_type = in->get_type(in, &received_vendor); + DBG1(DBG_IKE, "received tunneled EAP-PEAP AVP [EAP/%N/%N]", + eap_code_short_names, code, + eap_type_short_names, received_type); + if (code != EAP_REQUEST) + { + DBG1(DBG_IKE, "%N expected", eap_code_names, EAP_REQUEST); + in->destroy(in); + return FAILED; + } + } + else + { + DBG1(DBG_IKE, "received tunneled EAP-PEAP AVP [EAP/%N]", + eap_code_short_names, code); + this->out = eap_payload_create_code(code, in->get_identifier(in)); + in->destroy(in); + return NEED_MORE; + } + + if (this->ph2_method == NULL) + { + if (received_vendor) + { + DBG1(DBG_IKE, "server requested vendor specific EAP method %d-%d " + "(id: %u)", received_type, received_vendor, + in->get_identifier(in)); + } + else + { + DBG1(DBG_IKE, "server requested %N authentication (id: %u)", + eap_type_names, received_type, in->get_identifier(in)); + } + this->ph2_method = charon->eap->create_instance(charon->eap, + received_type, received_vendor, + EAP_PEER, this->server, this->peer); + if (!this->ph2_method) + { + DBG1(DBG_IKE, "EAP method not supported"); + this->out = eap_payload_create_nak(in->get_identifier(in)); + in->destroy(in); + return NEED_MORE; + } + this->start_phase2 = FALSE; + } + + type = this->ph2_method->get_type(this->ph2_method, &vendor); + + if (type != received_type || vendor != received_vendor) + { + DBG1(DBG_IKE, "received invalid EAP request"); + in->destroy(in); + return FAILED; + } + + status = this->ph2_method->process(this->ph2_method, in, &this->out); + in->destroy(in); + + switch (status) + { + case SUCCESS: + this->ph2_method->destroy(this->ph2_method); + this->ph2_method = NULL; + return NEED_MORE; + case NEED_MORE: + if (type != EAP_TNC) + { + this->ph2_method->destroy(this->ph2_method); + this->ph2_method = NULL; + } + return NEED_MORE; + case FAILED: + default: + if (vendor) + { + DBG1(DBG_IKE, "vendor specific EAP method %d-%d failed", + type, vendor); + } + else + { + DBG1(DBG_IKE, "%N method failed", eap_type_names, type); + } + return FAILED; + } +} + +METHOD(tls_application_t, build, status_t, + private_eap_peap_peer_t *this, tls_writer_t *writer) +{ + chunk_t data; + eap_code_t code; + eap_type_t type; + u_int32_t vendor; + + if (this->ph2_method == NULL && this->start_phase2) + { + /* generate an EAP Identity response */ + this->ph2_method = charon->eap->create_instance(charon->eap, EAP_IDENTITY, + 0, EAP_PEER, this->server, this->peer); + if (this->ph2_method == NULL) + { + DBG1(DBG_IKE, "EAP_IDENTITY method not available"); + return FAILED; + } + + /* synchronize EAP message identifiers of inner protocol with outer */ + this->ph2_method->set_identifier(this->ph2_method, + this->ph1_method->get_identifier(this->ph1_method)); + + this->ph2_method->process(this->ph2_method, NULL, &this->out); + this->ph2_method->destroy(this->ph2_method); + this->ph2_method = NULL; + this->start_phase2 = FALSE; + } + + if (this->out) + { + code = this->out->get_code(this->out); + type = this->out->get_type(this->out, &vendor); + if (code == EAP_REQUEST || code == EAP_RESPONSE) + { + DBG1(DBG_IKE, "sending tunneled EAP-PEAP AVP [EAP/%N/%N]", + eap_code_short_names, code, eap_type_short_names, type); + } + else + { + DBG1(DBG_IKE, "sending tunneled EAP-PEAP AVP [EAP/%N]", + eap_code_short_names, code); + } + + /* get the raw EAP message data */ + data = this->out->get_data(this->out); + DBG3(DBG_IKE, "%B", &data); + this->avp->build(this->avp, writer, data); + + this->out->destroy(this->out); + this->out = NULL; + } + return INVALID_STATE; +} + +METHOD(tls_application_t, destroy, void, + private_eap_peap_peer_t *this) +{ + this->server->destroy(this->server); + this->peer->destroy(this->peer); + DESTROY_IF(this->ph2_method); + DESTROY_IF(this->out); + this->avp->destroy(this->avp); + free(this); +} + +/** + * See header + */ +eap_peap_peer_t *eap_peap_peer_create(identification_t *server, + identification_t *peer, + eap_method_t *eap_method) +{ + private_eap_peap_peer_t *this; + + INIT(this, + .public = { + .application = { + .process = _process, + .build = _build, + .destroy = _destroy, + }, + }, + .server = server->clone(server), + .peer = peer->clone(peer), + .ph1_method = eap_method, + .start_phase2 = TRUE, + .avp = eap_peap_avp_create(FALSE), + ); + + return &this->public; +} |