/* * Copyright (C) 2011 Martin Willi * Copyright (C) 2011 revosec AG * * 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 . * * 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 "xauth_eap.h" #include #include #include typedef struct private_xauth_eap_t private_xauth_eap_t; /** * Private data of an xauth_eap_t object. */ struct private_xauth_eap_t { /** * Public interface. */ xauth_eap_t public; /** * ID of the server */ identification_t *server; /** * ID of the peer */ identification_t *peer; /** * Callback credential set */ callback_cred_t *cred; /** * XAuth password */ chunk_t pass; }; /** * Callback credential set function */ static shared_key_t* shared_cb(private_xauth_eap_t *this, shared_key_type_t type, identification_t *me, identification_t *other, id_match_t *match_me, id_match_t *match_other) { shared_key_t *shared; if (!this->pass.len) { return NULL; } if (type != SHARED_EAP && type != SHARED_ANY) { return NULL; } if (me) { if (!this->peer->equals(this->peer, me)) { return NULL; } if (match_me) { *match_me = ID_MATCH_PERFECT; } } else if (match_me) { *match_me = ID_MATCH_ANY; } if (other) { if (!this->server->equals(this->server, other)) { return NULL; } if (match_other) { *match_other = ID_MATCH_PERFECT; } } else if (match_other) { *match_other = ID_MATCH_ANY; } shared = shared_key_create(SHARED_EAP, chunk_clone(this->pass)); this->pass = chunk_empty; return shared; } /** * Do EAP exchanges to verify secret */ static bool verify_eap(private_xauth_eap_t *this, eap_method_t *backend) { eap_payload_t *request, *response; eap_method_t *frontend; eap_type_t type; u_int32_t vendor; status_t status; if (backend->initiate(backend, &request) != NEED_MORE) { return FALSE; } type = request->get_type(request, &vendor); frontend = charon->eap->create_instance(charon->eap, type, vendor, EAP_PEER, this->server, this->peer); if (!frontend) { DBG1(DBG_IKE, "XAuth-EAP backend requested %N, but not supported", eap_type_names, type); request->destroy(request); return FALSE; } while (TRUE) { /* credential set is active in frontend only, but not in backend */ lib->credmgr->add_local_set(lib->credmgr, &this->cred->set, TRUE); status = frontend->process(frontend, request, &response); lib->credmgr->remove_local_set(lib->credmgr, &this->cred->set); request->destroy(request); if (status != NEED_MORE) { /* clients should never return SUCCESS */ frontend->destroy(frontend); return FALSE; } status = backend->process(backend, response, &request); response->destroy(response); switch (status) { case SUCCESS: frontend->destroy(frontend); return TRUE; case NEED_MORE: break; default: frontend->destroy(frontend); return FALSE; } } } METHOD(xauth_method_t, initiate, status_t, private_xauth_eap_t *this, cp_payload_t **out) { cp_payload_t *cp; cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST); cp->add_attribute(cp, configuration_attribute_create_chunk( CONFIGURATION_ATTRIBUTE_V1, XAUTH_USER_NAME, chunk_empty)); cp->add_attribute(cp, configuration_attribute_create_chunk( CONFIGURATION_ATTRIBUTE_V1, XAUTH_USER_PASSWORD, chunk_empty)); *out = cp; return NEED_MORE; } METHOD(xauth_method_t, process, status_t, private_xauth_eap_t *this, cp_payload_t *in, cp_payload_t **out) { configuration_attribute_t *attr; enumerator_t *enumerator; identification_t *id; chunk_t user = chunk_empty; eap_method_t *backend; eap_type_t type; char *name; bool ok; enumerator = in->create_attribute_enumerator(in); while (enumerator->enumerate(enumerator, &attr)) { switch (attr->get_type(attr)) { case XAUTH_USER_NAME: user = attr->get_chunk(attr); break; case XAUTH_USER_PASSWORD: this->pass = attr->get_chunk(attr); break; default: break; } } enumerator->destroy(enumerator); if (!user.ptr || !this->pass.ptr) { DBG1(DBG_IKE, "peer did not respond to our XAuth request"); return FAILED; } if (user.len) { id = identification_create_from_data(user); if (!id) { DBG1(DBG_IKE, "failed to parse provided XAuth username"); return FAILED; } this->peer->destroy(this->peer); this->peer = id; } if (this->pass.len && this->pass.ptr[this->pass.len - 1] == 0) { /* fix null-terminated passwords (Android etc.) */ this->pass.len -= 1; } name = lib->settings->get_str(lib->settings, "%s.plugins.xauth-eap.backend", "radius", charon->name); type = eap_type_from_string(name); if (!type) { DBG1(DBG_CFG, "Unknown XAuth-EAP method: %s", name); return FAILED; } backend = charon->eap->create_instance(charon->eap, type, 0, EAP_SERVER, this->server, this->peer); if (!backend) { DBG1(DBG_CFG, "XAuth-EAP method backend not supported: %s", name); return FAILED; } ok = verify_eap(this, backend); backend->destroy(backend); if (ok) { return SUCCESS; } return FAILED; } METHOD(xauth_method_t, get_identity, identification_t*, private_xauth_eap_t *this) { return this->peer; } METHOD(xauth_method_t, destroy, void, private_xauth_eap_t *this) { this->cred->destroy(this->cred); this->server->destroy(this->server); this->peer->destroy(this->peer); free(this); } /* * Described in header. */ xauth_eap_t *xauth_eap_create_server(identification_t *server, identification_t *peer) { private_xauth_eap_t *this; INIT(this, .public = { .xauth_method = { .initiate = _initiate, .process = _process, .get_identity = _get_identity, .destroy = _destroy, }, }, .server = server->clone(server), .peer = peer->clone(peer), ); this->cred = callback_cred_create_shared((void*)shared_cb, this); return &this->public; }