merged EAP framework from branch into trunk

includes a lot of other modifications
This commit is contained in:
Martin Willi
2007-02-12 15:56:47 +00:00
parent 6fda18d99d
commit f27f6296e6
64 changed files with 8538 additions and 481 deletions
@@ -26,6 +26,7 @@
#include <sa/authenticators/rsa_authenticator.h>
#include <sa/authenticators/psk_authenticator.h>
#include <sa/authenticators/eap_authenticator.h>
ENUM_BEGIN(auth_method_names, AUTH_RSA, AUTH_DSS,
@@ -47,6 +48,8 @@ authenticator_t *authenticator_create(ike_sa_t *ike_sa, auth_method_t auth_metho
return (authenticator_t*)rsa_authenticator_create(ike_sa);
case AUTH_PSK:
return (authenticator_t*)psk_authenticator_create(ike_sa);
case AUTH_EAP:
return (authenticator_t*)eap_authenticator_create(ike_sa);
default:
return NULL;
}
File diff suppressed because it is too large Load Diff
+133
View File
@@ -0,0 +1,133 @@
/**
* @file eap_aka.h
*
* @brief Interface of eap_aka_t.
*
*/
/*
* Copyright (C) 2006 Martin Willi
* 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.
*/
#ifndef EAP_AKA_H_
#define EAP_AKA_H_
typedef struct eap_aka_t eap_aka_t;
typedef enum aka_subtype_t aka_subtype_t;
typedef enum aka_attribute_t aka_attribute_t;
#include <sa/authenticators/eap/eap_method.h>
/**
* Subtypes of AKA messages
*/
enum aka_subtype_t {
AKA_CHALLENGE = 1,
AKA_AUTHENTICATION_REJECT = 2,
AKA_SYNCHRONIZATION_FAILURE = 4,
AKA_IDENTITY = 5,
AKA_NOTIFICATION = 12,
AKA_REAUTHENTICATION = 13,
AKA_CLIENT_ERROR = 14,
};
/**
* enum names for aka_subtype_t
*/
extern enum_name_t *aka_subtype_names;
/**
* Attribute types in AKA messages
*/
enum aka_attribute_t {
/** defines the end of attribute list */
AT_END = -1,
AT_RAND = 1,
AT_AUTN = 2,
AT_RES = 3,
AT_AUTS = 4,
AT_PADDING = 6,
AT_NONCE_MT = 7,
AT_PERMANENT_ID_REQ = 10,
AT_MAC = 11,
AT_NOTIFICATION = 12,
AT_ANY_ID_REQ = 13,
AT_IDENTITY = 14,
AT_VERSION_LIST = 15,
AT_SELECTED_VERSION = 16,
AT_FULLAUTH_ID_REQ = 17,
AT_COUNTER = 19,
AT_COUNTER_TOO_SMALL = 20,
AT_NONCE_S = 21,
AT_CLIENT_ERROR_CODE = 22,
AT_IV = 129,
AT_ENCR_DATA = 130,
AT_NEXT_PSEUDONYM = 132,
AT_NEXT_REAUTH_ID = 133,
AT_CHECKCODE = 134,
AT_RESULT_IND = 135,
};
/**
* enum names for aka_attribute_t
*/
extern enum_name_t *aka_attribute_names;
/**
* @brief Implementation of the eap_method_t interface using EAP-AKA.
*
* EAP-AKA uses 3rd generation mobile phone standard authentication
* mechanism for authentication. It is a mutual authentication
* mechanism which establishs a shared key and therefore supports EAP_ONLY
* authentication. This implementation follows the standard of the
* 3GPP2 (S.S0055) and not the one of 3GGP.
* The shared key used for authentication is from ipsec.secrets. The
* peers ID is used to query it.
* The AKA mechanism uses sequence numbers to detect replay attacks. The
* peer stores the sequence number normally in a USIM and accepts
* incremental sequence numbers (incremental for lifetime of the USIM). To
* prevent a complex sequence number management, this implementation uses
* a sequence number derived from time. It is initialized to the startup
* time of the daemon. As long as the (UTC) time of the system is not
* turned back while the daemon is not running, this method is secure.
*
* @b Constructors:
* - eap_aka_create()
* - eap_client_create() using eap_method EAP_AKA
*
* @ingroup eap
*/
struct eap_aka_t {
/**
* Implemented eap_method_t interface.
*/
eap_method_t eap_method_interface;
};
/**
* @brief Creates the EAP method EAP-AKA.
*
* @param server ID of the EAP server
* @param peer ID of the EAP client
* @return eap_aka_t object
*
* @ingroup eap
*/
eap_aka_t *eap_create(eap_role_t role,
identification_t *server, identification_t *peer);
#endif /* EAP_AKA_H_ */
@@ -0,0 +1,243 @@
/**
* @file eap_method.c
*
* @brief Generic constructor for eap_methods.
*
*/
/*
* Copyright (C) 2006 Martin Willi
* 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 <string.h>
#include <sys/stat.h>
#include <dirent.h>
#include <error.h>
#include <dlfcn.h>
#include "eap_method.h"
#include <daemon.h>
#include <library.h>
#include <utils/linked_list.h>
#include <utils/identification.h>
ENUM_BEGIN(eap_type_names, EAP_IDENTITY, EAP_TOKEN_CARD,
"EAP_IDENTITY",
"EAP_NOTIFICATION",
"EAP_NAK",
"EAP_MD5",
"EAP_ONE_TIME_PASSWORD",
"EAP_TOKEN_CARD");
ENUM_NEXT(eap_type_names, EAP_AKA, EAP_AKA, EAP_TOKEN_CARD,
"EAP_AKA");
ENUM_END(eap_type_names, EAP_AKA);
ENUM(eap_code_names, EAP_REQUEST, EAP_FAILURE,
"EAP_REQUEST",
"EAP_RESPONSE",
"EAP_SUCCESS",
"EAP_FAILURE",
);
ENUM(eap_role_names, EAP_SERVER, EAP_PEER,
"EAP_SERVER",
"EAP_PEER",
);
typedef struct module_entry_t module_entry_t;
/**
* Representation of a loaded module: EAP type, library handle, constructor
*/
struct module_entry_t {
eap_type_t type;
void *handle;
eap_constructor_t constructor;
};
/** List of module_entry_t's */
static linked_list_t *modules = NULL;
/**
* unload modules at daemon shutdown
*/
void eap_method_unload()
{
if (modules)
{
module_entry_t *entry;
while (modules->remove_last(modules, (void**)&entry) == SUCCESS)
{
DBG2(DBG_CFG, "unloaded module for %s", eap_type_names, entry->type);
dlclose(entry->handle);
free(entry);
}
modules->destroy(modules);
modules = NULL;
}
}
/**
* Load EAP modules at daemon startup
*/
void eap_method_load(char *directory)
{
struct dirent* entry;
struct stat stb;
DIR* dir;
eap_method_unload();
modules = linked_list_create();
if (stat(directory, &stb) == -1 || !(stb.st_mode & S_IFDIR))
{
DBG1(DBG_CFG, "error opening EAP modules directory %s", directory);
return;
}
if (stb.st_uid != 0)
{
DBG1(DBG_CFG, "EAP modules directory %s not owned by root, skipped", directory);
return;
}
if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP)
{
DBG1(DBG_CFG, "EAP modules directory %s writable by others, skipped", directory);
return;
}
dir = opendir(directory);
if (dir == NULL)
{
DBG1(DBG_CFG, "error opening EAP modules directory %s", directory);
return;
}
DBG1(DBG_CFG, "loading EAP modules from '%s'", directory);
while ((entry = readdir(dir)) != NULL)
{
char file[256];
module_entry_t module, *loaded_module;
eap_method_t *method;
identification_t *id;
char *ending;
snprintf(file, sizeof(file), "%s/%s", directory, entry->d_name);
if (stat(file, &stb) == -1 || !(stb.st_mode & S_IFREG))
{
DBG2(DBG_CFG, " skipping %s, doesn't look like a file",
entry->d_name);
continue;
}
ending = entry->d_name + strlen(entry->d_name) - 3;
if (ending <= entry->d_name || !streq(ending, ".so"))
{
/* skip anything which does not look like a library */
DBG2(DBG_CFG, " skipping %s, doesn't look like a library",
entry->d_name);
continue;
}
if (stb.st_uid != 0)
{
DBG1(DBG_CFG, " skipping %s, file is not owned by root", entry->d_name);
return;
}
if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP)
{
DBG1(DBG_CFG, " skipping %s, file is writeable by others", entry->d_name);
continue;
}
/* try to load the library */
module.handle = dlopen(file, RTLD_LAZY);
if (module.handle == NULL)
{
DBG1(DBG_CFG, " opening EAP module %s failed: %s", entry->d_name,
dlerror());
continue;
}
module.constructor = dlsym(module.handle, "eap_create");
if (module.constructor == NULL)
{
DBG1(DBG_CFG, " EAP module %s has no eap_create() function, skipped",
entry->d_name);
dlclose(module.handle);
continue;
}
/* get the type implemented in the method, create an instance for it */
id = identification_create_from_string("[email protected]");
method = module.constructor(EAP_SERVER, id, id);
if (method == NULL)
{
method = module.constructor(EAP_PEER, id, id);
}
id->destroy(id);
if (method == NULL)
{
DBG1(DBG_CFG, " unable to create instance of EAP method %s, skipped",
entry->d_name);
dlclose(module.handle);
continue;
}
module.type = method->get_type(method);
method->destroy(method);
DBG1(DBG_CFG, " loaded EAP method %N successfully from %s",
eap_type_names, module.type, entry->d_name);
loaded_module = malloc_thing(module_entry_t);
memcpy(loaded_module, &module, sizeof(module));
modules->insert_last(modules, loaded_module);
}
closedir(dir);
}
/*
* Described in header.
*/
eap_method_t *eap_method_create(eap_type_t type, eap_role_t role,
identification_t *server,
identification_t *peer)
{
eap_method_t *method = NULL;
iterator_t *iterator;
module_entry_t *entry;
iterator = modules->create_iterator(modules, TRUE);
while (iterator->iterate(iterator, (void**)&entry))
{
if (entry->type == type)
{
method = entry->constructor(role, server, peer);
if (method)
{
break;
}
}
}
iterator->destroy(iterator);
if (method == NULL)
{
DBG1(DBG_CFG, "no EAP module found for %N %N",
eap_type_names, type, eap_role_names, role);
}
return method;
}
@@ -0,0 +1,241 @@
/**
* @file eap_method.h
*
* @brief Interface eap_method_t.
*
*/
/*
* Copyright (C) 2006 Martin Willi
* 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.
*/
#ifndef EAP_METHOD_H_
#define EAP_METHOD_H_
typedef struct eap_method_t eap_method_t;
typedef enum eap_role_t eap_role_t;
typedef enum eap_type_t eap_type_t;
typedef enum eap_code_t eap_code_t;
#include <library.h>
#include <utils/identification.h>
#include <encoding/payloads/eap_payload.h>
/**
* Role of an eap_method, SERVER or PEER (client)
*
* @ingroup eap
*/
enum eap_role_t {
EAP_SERVER,
EAP_PEER,
};
/**
* enum names for eap_role_t.
*
* @ingroup eap
*/
extern enum_name_t *eap_role_names;
/**
* EAP types, defines the EAP method implementation
*
* @ingroup eap
*/
enum eap_type_t {
EAP_IDENTITY = 1,
EAP_NOTIFICATION = 2,
EAP_NAK = 3,
EAP_MD5 = 4,
EAP_ONE_TIME_PASSWORD = 5,
EAP_TOKEN_CARD = 6,
EAP_AKA = 23,
};
/**
* enum names for eap_type_t.
*
* @ingroup eap
*/
extern enum_name_t *eap_type_names;
/**
* EAP code, type of an EAP message
*
* @ingroup eap
*/
enum eap_code_t {
EAP_REQUEST = 1,
EAP_RESPONSE = 2,
EAP_SUCCESS = 3,
EAP_FAILURE = 4,
};
/**
* enum names for eap_code_t.
*
* @ingroup eap
*/
extern enum_name_t *eap_code_names;
/**
* @brief Interface of an EAP method for server and client side.
*
* An EAP method initiates an EAP exchange and processes requests and
* responses. An EAP method may need multiple exchanges before succeeding, and
* the eap_authentication may use multiple EAP methods to authenticate a peer.
* To accomplish these requirements, all EAP methods have their own
* implementation while the eap_authenticatior uses one or more of these
* EAP methods. Sending of EAP(SUCCESS/FAILURE) message is not the job
* of the method, the eap_authenticator does this.
* An EAP method may establish a MSK, this is used the complete the
* authentication. Even if a mutual EAP method is used, the traditional
* AUTH payloads are required. Only these include the nonces and messages from
* ike_sa_init and therefore prevent man in the middle attacks.
*
* @b Constructors:
* - eap_method_create()
*
* @ingroup eap
*/
struct eap_method_t {
/**
* @brief Initiate the EAP exchange.
*
* initiate() is only useable for server implementations, as clients only
* reply to server requests.
* A eap_payload is created in "out" if result is NEED_MORE.
*
* @param this calling object
* @param out eap_payload to send to the client
* @return
* - NEED_MORE, if an other exchange is required
* - FAILED, if unable to create eap request payload
*/
status_t (*initiate) (eap_method_t *this, eap_payload_t **out);
/**
* @brief Process a received EAP message.
*
* A eap_payload is created in "out" if result is NEED_MORE.
*
* @param this calling object
* @param in eap_payload response received
* @param out created eap_payload to send
* @return
* - NEED_MORE, if an other exchange is required
* - FAILED, if EAP method failed
* - SUCCESS, if EAP method succeeded
*/
status_t (*process) (eap_method_t *this, eap_payload_t *in,
eap_payload_t **out);
/**
* @brief Get the EAP type implemented in this method.
*
* @param this calling object
* @return type of the EAP method
*/
eap_type_t (*get_type) (eap_method_t *this);
/**
* @brief Check if this EAP method authenticates the server.
*
* Some EAP methods provide mutual authentication and
* allow authentication using only EAP, if the peer supports it.
*
* @param this calling object
* @return TRUE if methods provides mutual authentication
*/
bool (*is_mutual) (eap_method_t *this);
/**
* @brief Get the MSK established by this EAP method.
*
* Not all EAP methods establish a shared secret.
*
* @param this calling object
* @param msk chunk receiving internal stored MSK
* @return
* - SUCCESS, or
* - FAILED, if MSK not established (yet)
*/
status_t (*get_msk) (eap_method_t *this, chunk_t *msk);
/**
* @brief Destroys a eap_method_t object.
*
* @param this calling object
*/
void (*destroy) (eap_method_t *this);
};
/**
* @brief Creates an EAP method for a specific type and role.
*
* @param eap_type EAP type to use
* @param role role of the eap_method, server or peer
* @param server ID of acting server
* @param peer ID of involved peer (client)
* @return eap_method_t object
*
* @ingroup eap
*/
eap_method_t *eap_method_create(eap_type_t eap_type, eap_role_t role,
identification_t *server, identification_t *peer);
/**
* @brief (Re-)Load all EAP modules in the EAP modules directory.
*
* For security reasons, the directory and all it's modules must be owned
* by root and must not be writeable by someone else.
*
* @param dir directory of the EAP modules
*
* @ingroup eap
*/
void eap_method_load(char *directory);
/**
* @brief Unload all loaded EAP modules
*
* @ingroup eap
*/
void eap_method_unload();
/**
* @brief Constructor definition for a pluggable EAP module.
*
* Each EAP module must define a constructor function which will return
* an initialized object with the methods defined in eap_method_t. The
* constructor must be named eap_create() and it's signature must be equal
* to that of eap_constructor_t.
* A module may implement only a single role. If it does not support the role
* requested, NULL should be returned. Multiple modules are allowed of the
* same EAP type to support seperate implementations of peer/server.
*
* @param role role the module will play, peer or server
* @param server ID of the server to use for credential lookup
* @param peer ID of the peer to use for credential lookup
* @return implementation of the eap_method_t interface
*
* @ingroup eap
*/
typedef eap_method_t *(*eap_constructor_t)(eap_role_t role,
identification_t *server,
identification_t *peer);
#endif /* EAP_METHOD_H_ */
@@ -0,0 +1,348 @@
/**
* @file eap_authenticator.c
*
* @brief Implementation of eap_authenticator_t.
*
*/
/*
* Copyright (C) 2006 Martin Willi
* 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 <string.h>
#include "eap_authenticator.h"
#include <daemon.h>
#include <config/policies/policy.h>
#include <sa/authenticators/eap/eap_method.h>
typedef struct private_eap_authenticator_t private_eap_authenticator_t;
/**
* Private data of an eap_authenticator_t object.
*/
struct private_eap_authenticator_t {
/**
* Public authenticator_t interface.
*/
eap_authenticator_t public;
/**
* Assigned IKE_SA
*/
ike_sa_t *ike_sa;
/**
* Role of this authenticator, PEER or SERVER
*/
eap_role_t role;
/**
* Current EAP method processing
*/
eap_method_t *method;
/**
* MSK used to build and verify auth payload
*/
chunk_t msk;
};
extern chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce,
chunk_t secret, identification_t *id,
prf_t *prf);
/**
* Implementation of authenticator_t.verify.
*/
static status_t verify(private_eap_authenticator_t *this, chunk_t ike_sa_init,
chunk_t my_nonce, auth_payload_t *auth_payload)
{
chunk_t auth_data, recv_auth_data;
identification_t *other_id = this->ike_sa->get_other_id(this->ike_sa);
prf_t *prf = this->ike_sa->get_auth_verify(this->ike_sa);
auth_data = build_shared_key_signature(ike_sa_init, my_nonce, this->msk,
other_id, prf);
recv_auth_data = auth_payload->get_data(auth_payload);
if (!chunk_equals(auth_data, recv_auth_data))
{
DBG1(DBG_IKE, "verification of AUTH payload created from EAP MSK failed");
chunk_free(&auth_data);
return FAILED;
}
chunk_free(&auth_data);
DBG1(DBG_IKE, "authentication of '%D' with %N successful",
other_id, auth_method_names, AUTH_EAP);
return SUCCESS;
}
/**
* Implementation of authenticator_t.build.
*/
static status_t build(private_eap_authenticator_t *this, chunk_t ike_sa_init,
chunk_t other_nonce, auth_payload_t **auth_payload)
{
chunk_t auth_data;
identification_t *my_id = this->ike_sa->get_my_id(this->ike_sa);
prf_t *prf = this->ike_sa->get_auth_build(this->ike_sa);
DBG1(DBG_IKE, "authentication of '%D' (myself) with %N",
my_id, auth_method_names, AUTH_EAP);
auth_data = build_shared_key_signature(ike_sa_init, other_nonce,
this->msk, my_id, prf);
*auth_payload = auth_payload_create();
(*auth_payload)->set_auth_method(*auth_payload, AUTH_PSK);
(*auth_payload)->set_data(*auth_payload, auth_data);
chunk_free(&auth_data);
return SUCCESS;
}
/**
* Implementation of eap_authenticator_t.initiate
*/
static status_t initiate(private_eap_authenticator_t *this, eap_type_t type,
eap_payload_t **out)
{
/* if initiate() is called, role is always server */
this->role = EAP_SERVER;
if (type == 0)
{
DBG1(DBG_IKE,
"client requested EAP authentication, but configuration forbids it");
*out = eap_payload_create_code(EAP_FAILURE);
return FAILED;
}
DBG1(DBG_IKE, "requesting %N authentication", eap_type_names, type);
this->method = eap_method_create(type, this->role,
this->ike_sa->get_my_id(this->ike_sa),
this->ike_sa->get_other_id(this->ike_sa));
if (this->method == NULL)
{
DBG1(DBG_IKE, "configured EAP server method %N not supported, sending %N",
eap_type_names, type, eap_code_names, EAP_FAILURE);
*out = eap_payload_create_code(EAP_FAILURE);
return FAILED;
}
if (this->method->initiate(this->method, out) != NEED_MORE)
{
DBG1(DBG_IKE, "failed to initiate %N, sending %N",
eap_type_names, type, eap_code_names, EAP_FAILURE);
*out = eap_payload_create_code(EAP_FAILURE);
return FAILED;
}
return NEED_MORE;
}
/**
* Processing method for a peer
*/
static status_t process_peer(private_eap_authenticator_t *this,
eap_payload_t *in, eap_payload_t **out)
{
eap_type_t type = in->get_type(in);
/* create an eap_method for the first call */
if (this->method == NULL)
{
DBG1(DBG_IKE, "EAP server requested %N authentication",
eap_type_names, type);
this->method = eap_method_create(type, EAP_PEER,
this->ike_sa->get_other_id(this->ike_sa),
this->ike_sa->get_my_id(this->ike_sa));
if (this->method == NULL)
{
DBG1(DBG_IKE, "EAP server requested unsupported "
"EAP method %N, sending EAP_NAK", eap_type_names, type);
*out = eap_payload_create_nak();
return NEED_MORE;
}
}
switch (this->method->process(this->method, in, out))
{
case NEED_MORE:
return NEED_MORE;
case SUCCESS:
if (this->method->get_msk(this->method, &this->msk) == SUCCESS)
{
DBG1(DBG_IKE, "EAP method %N succeded, MSK established",
eap_type_names, this->method->get_type(this->method));
this->msk = chunk_clone(this->msk);
return SUCCESS;
}
DBG1(DBG_IKE, "EAP method %N succeded, but no MSK established",
eap_type_names, this->method->get_type(this->method));
return FAILED;
case FAILED:
default:
DBG1(DBG_IKE, "EAP method %N failed",
eap_type_names, this->method->get_type(this->method));
return FAILED;
}
}
/**
* Processing method for a server
*/
static status_t process_server(private_eap_authenticator_t *this,
eap_payload_t *in, eap_payload_t **out)
{
switch (this->method->process(this->method, in, out))
{
case NEED_MORE:
return NEED_MORE;
case SUCCESS:
if (this->method->get_msk(this->method, &this->msk) == SUCCESS)
{
DBG1(DBG_IKE, "EAP method %N succeded, MSK established",
eap_type_names, this->method->get_type(this->method));
this->msk = chunk_clone(this->msk);
*out = eap_payload_create_code(EAP_SUCCESS);
return SUCCESS;
}
DBG1(DBG_IKE, "EAP method %N succeded, but no MSK established",
eap_type_names, this->method->get_type(this->method));
*out = eap_payload_create_code(EAP_FAILURE);
return FAILED;
case FAILED:
default:
DBG1(DBG_IKE, "EAP method %N failed for peer %D",
eap_type_names, this->method->get_type(this->method),
this->ike_sa->get_other_id(this->ike_sa));
*out = eap_payload_create_code(EAP_FAILURE);
return FAILED;
}
}
/**
* Implementation of eap_authenticator_t.process
*/
static status_t process(private_eap_authenticator_t *this, eap_payload_t *in,
eap_payload_t **out)
{
eap_code_t code = in->get_code(in);
switch (this->role)
{
case EAP_SERVER:
{
switch (code)
{
case EAP_RESPONSE:
{
return process_server(this, in, out);
}
default:
{
DBG1(DBG_IKE, "received %N, sending %N",
eap_code_names, code, eap_code_names, EAP_FAILURE);
*out = eap_payload_create_code(EAP_FAILURE);
return FAILED;
}
}
}
case EAP_PEER:
{
switch (code)
{
case EAP_REQUEST:
{
return process_peer(this, in, out);
}
case EAP_SUCCESS:
{
if (this->method->get_msk(this->method, &this->msk) == SUCCESS)
{
DBG1(DBG_IKE, "EAP method %N succeded, MSK established",
eap_type_names, this->method->get_type(this->method));
this->msk = chunk_clone(this->msk);
return SUCCESS;
}
DBG1(DBG_IKE, "EAP method %N succeded, but no MSK established",
eap_type_names, this->method->get_type(this->method));
return FAILED;
}
case EAP_FAILURE:
default:
{
DBG1(DBG_IKE, "received %N, EAP authentication failed",
eap_code_names, code);
return FAILED;
}
}
}
default:
{
return FAILED;
}
}
}
/**
* Implementation of authenticator_t.is_mutual.
*/
static bool is_mutual(private_eap_authenticator_t *this)
{
if (this->method)
{
return this->method->is_mutual(this->method);
}
return FALSE;
}
/**
* Implementation of authenticator_t.destroy.
*/
static void destroy(private_eap_authenticator_t *this)
{
DESTROY_IF(this->method);
chunk_free(&this->msk);
free(this);
}
/*
* Described in header.
*/
eap_authenticator_t *eap_authenticator_create(ike_sa_t *ike_sa)
{
private_eap_authenticator_t *this = malloc_thing(private_eap_authenticator_t);
/* public functions */
this->public.authenticator_interface.verify = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t*))verify;
this->public.authenticator_interface.build = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t**))build;
this->public.authenticator_interface.destroy = (void(*)(authenticator_t*))destroy;
this->public.is_mutual = (bool(*)(eap_authenticator_t*))is_mutual;
this->public.initiate = (status_t(*)(eap_authenticator_t*,eap_type_t,eap_payload_t**))initiate;
this->public.process = (status_t(*)(eap_authenticator_t*,eap_payload_t*,eap_payload_t**))process;
/* private data */
this->ike_sa = ike_sa;
this->role = EAP_PEER;
this->method = NULL;
this->msk = chunk_empty;
return &this->public;
}
@@ -0,0 +1,156 @@
/**
* @file eap_authenticator.h
*
* @brief Interface of eap_authenticator_t.
*
*/
/*
* Copyright (C) 2006 Martin Willi
* 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.
*/
#ifndef EAP_AUTHENTICATOR_H_
#define EAP_AUTHENTICATOR_H_
typedef struct eap_authenticator_t eap_authenticator_t;
#include <sa/authenticators/authenticator.h>
#include <encoding/payloads/eap_payload.h>
/**
* @brief Implementation of the authenticator_t interface using AUTH_EAP.
*
* Authentication using EAP involves the most complex authenticator. It stays
* alive over multiple ike_auth transactions and handles multiple EAP
* messages.
* EAP authentication must be clearly distinguished between using
* mutual EAP methods and using methods not providing server authentication.
* If no mutual authentication is used, the server must prove it's identity
* by traditional AUTH methods (RSA, psk). Only when the EAP method is mutual,
* the client should accept an EAP-only authentication.
* RFC4306 does always use traditional authentiction, EAP only authentication
* is described in the internet draft draft-eronen-ipsec-ikev2-eap-auth-05.txt.
*
* @verbatim
ike_sa_init
------------------------->
<-------------------------
followed by multiple ike_auth:
+--------+ +--------+
| EAP | ID, SA, TS, N(EAP_ONLY) | EAP |
| client | ---------------------------> | server |
| | ID, [AUTH,] EAP | | AUTH payload is
| | <--------------------------- | | only included if
| | EAP | | authentication
| | ---------------------------> | | is not mutual.
| | EAP | |
| | <--------------------------- | |
| | EAP | |
| | ---------------------------> | |
| | EAP(SUCCESS) | |
| | <--------------------------- | |
| | AUTH | | If EAP establishes
| | ---------------------------> | | a session key, AUTH
| | AUTH, SA, TS | | payloads use this
| | <--------------------------- | | key, not SK_pi/pr
+--------+ +--------+
@endverbatim
* @b Constructors:
* - eap_authenticator_create()
* - authenticator_create() using auth_method AUTH_EAP
*
* @ingroup authenticators
*/
struct eap_authenticator_t {
/**
* Implemented authenticator_t interface.
*/
authenticator_t authenticator_interface;
/**
* @brief Check if the EAP method was/is mutual and secure.
*
* RFC4306 proposes to authenticate the EAP responder (server) by standard
* IKEv2 methods (RSA, psk). Not all, but some EAP methods
* provide mutual authentication, which would result in a redundant
* authentication. If the client supports EAP_ONLY_AUTHENTICATION, and
* the the server provides mutual authentication, authentication using
* RSA/PSK may be omitted. If the server did not include a traditional
* AUTH payload, the client must verify that the server initiated mutual
* EAP authentication before it can trust the server.
*
* @param this calling object
* @return TRUE, if no AUTH payload required, FALSE otherwise
*/
bool (*is_mutual) (eap_authenticator_t* this);
/**
* @brief Initiate the EAP exchange.
*
* The server initiates EAP exchanges, so the client never calls
* this method. If initiate() returns NEED_MORE, the EAP authentication
* process started. In any case, a payload is created in "out".
*
* @param this calling object
* @param type EAP method to use to authenticate client
* @param out created initiaal EAP message to send
* @return
* - FAILED, if initiation failed
* - NEED_MORE, if more EAP exchanges reqired
*/
status_t (*initiate) (eap_authenticator_t* this, eap_type_t type,
eap_payload_t **out);
/**
* @brief Process an EAP message.
*
* After receiving an EAP message "in", the peer/server processes
* the payload and creates a reply/subsequent request.
* The server side always returns NEED_MORE if another EAP message
* is excepted from the client, SUCCESS if EAP exchange completed and
* "out" is EAP_SUCCES, or FAILED if the EAP exchange failed with
* a EAP_FAILURE payload in "out". Anyway, a payload in "out" is always
* created.
* The peer (client) side only creates a "out" payload if result is
* NEED_MORE, a SUCCESS/FAILED is returned whenever a
* EAP_SUCCESS/EAP_FAILURE message is received in "in".
* If a SUCCESS is returned (on any side), the EAP authentication was
* successful and the AUTH payload can be exchanged.
*
* @param this calling object
* @param in received EAP message
* @param out created EAP message to send
* @return
* - FAILED, if authentication/EAP exchange failed
* - SUCCESS, if authentication completed
* - NEED_MORE, if more EAP exchanges reqired
*/
status_t (*process) (eap_authenticator_t* this,
eap_payload_t *in, eap_payload_t **out);
};
/**
* @brief Creates an authenticator for AUTH_EAP.
*
* @param ike_sa associated ike_sa
* @return eap_authenticator_t object
*
* @ingroup authenticators
*/
eap_authenticator_t *eap_authenticator_create(ike_sa_t *ike_sa);
#endif /* EAP_AUTHENTICATOR_H_ */
@@ -1,7 +1,7 @@
/**
* @file authenticator.c
* @file psk_authenticator.c
*
* @brief Implementation of authenticator_t.
* @brief Implementation of psk_authenticator_t.
*
*/
@@ -54,24 +54,35 @@ struct private_psk_authenticator_t {
};
/**
* Function implemented in rsa_authenticator.c
* Builds the octets to be signed as described in section 2.15 of RFC 4306
*/
extern chunk_t build_tbs_octets(private_psk_authenticator_t *this, chunk_t ike_sa_init,
chunk_t nonce, identification_t *id, prf_t *prf);
chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce,
identification_t *id, prf_t *prf)
{
u_int8_t id_header_buf[] = {0x00, 0x00, 0x00, 0x00};
chunk_t id_header = chunk_from_buf(id_header_buf);
chunk_t id_with_header, id_prfd, id_encoding;
id_header_buf[0] = id->get_type(id);
id_encoding = id->get_encoding(id);
id_with_header = chunk_cat("cc", id_header, id_encoding);
prf->allocate_bytes(prf, id_with_header, &id_prfd);
chunk_free(&id_with_header);
return chunk_cat("ccm", ike_sa_init, nonce, id_prfd);
}
/**
* Creates the AUTH data using auth method SHARED_KEY_MESSAGE_INTEGRITY_CODE.
*/
static chunk_t build_shared_key_signature(private_psk_authenticator_t *this,
chunk_t ike_sa_init,
chunk_t nonce,
chunk_t secret,
identification_t *id,
prf_t *prf)
chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce,
chunk_t secret, identification_t *id,
prf_t *prf)
{
chunk_t key_pad, key, auth_data, octets;
octets = build_tbs_octets(this, ike_sa_init, nonce, id, prf);
octets = build_tbs_octets(ike_sa_init, nonce, id, prf);
/* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), <msg octets>) */
key_pad.ptr = IKEV2_KEY_PAD;
key_pad.len = IKEV2_KEY_PAD_LENGTH;
@@ -94,7 +105,7 @@ static chunk_t build_shared_key_signature(private_psk_authenticator_t *this,
* Implementation of authenticator_t.verify.
*/
static status_t verify(private_psk_authenticator_t *this, chunk_t ike_sa_init,
chunk_t my_nonce, auth_payload_t *auth_payload)
chunk_t my_nonce, auth_payload_t *auth_payload)
{
status_t status;
chunk_t auth_data, recv_auth_data, shared_key;
@@ -110,7 +121,7 @@ static status_t verify(private_psk_authenticator_t *this, chunk_t ike_sa_init,
return status;
}
auth_data = build_shared_key_signature(this, ike_sa_init, my_nonce,
auth_data = build_shared_key_signature(ike_sa_init, my_nonce,
shared_key, other_id,
this->ike_sa->get_auth_verify(this->ike_sa));
chunk_free(&shared_key);
@@ -153,7 +164,7 @@ static status_t build(private_psk_authenticator_t *this, chunk_t ike_sa_init,
return status;
}
auth_data = build_shared_key_signature(this, ike_sa_init,
auth_data = build_shared_key_signature(ike_sa_init,
other_nonce, shared_key, my_id,
this->ike_sa->get_auth_build(this->ike_sa));
DBG2(DBG_IKE, "successfully created shared key MAC");
@@ -1,7 +1,7 @@
/**
* @file authenticator.c
* @file rsa_authenticator.c
*
* @brief Implementation of authenticator_t.
* @brief Implementation of rsa_authenticator_t.
*
*/
@@ -48,24 +48,10 @@ struct private_rsa_authenticator_t {
};
/**
* Builds the octets to be signed as described in section 2.15 of RFC 4306
* Function implemented in psk_authenticator.c
*/
chunk_t build_tbs_octets(private_rsa_authenticator_t *this, chunk_t ike_sa_init,
chunk_t nonce, identification_t *id, prf_t *prf)
{
u_int8_t id_header_buf[] = {0x00, 0x00, 0x00, 0x00};
chunk_t id_header = chunk_from_buf(id_header_buf);
chunk_t id_with_header, id_prfd, id_encoding;
id_header_buf[0] = id->get_type(id);
id_encoding = id->get_encoding(id);
id_with_header = chunk_cat("cc", id_header, id_encoding);
prf->allocate_bytes(prf, id_with_header, &id_prfd);
chunk_free(&id_with_header);
return chunk_cat("ccm", ike_sa_init, nonce, id_prfd);
}
extern chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce,
identification_t *id, prf_t *prf);
/**
* Implementation of authenticator_t.verify.
@@ -92,7 +78,7 @@ static status_t verify(private_rsa_authenticator_t *this, chunk_t ike_sa_init,
DBG1(DBG_IKE, "no RSA public key found for '%D'", other_id);
return NOT_FOUND;
}
octets = build_tbs_octets(this, ike_sa_init, my_nonce, other_id,
octets = build_tbs_octets(ike_sa_init, my_nonce, other_id,
this->ike_sa->get_auth_verify(this->ike_sa));
status = public_key->verify_emsa_pkcs1_signature(public_key, octets, auth_data);
chunk_free(&octets);
@@ -145,7 +131,7 @@ static status_t build(private_rsa_authenticator_t *this, chunk_t ike_sa_init,
}
DBG2(DBG_IKE, "matching RSA private key found");
octets = build_tbs_octets(this, ike_sa_init, other_nonce, my_id,
octets = build_tbs_octets(ike_sa_init, other_nonce, my_id,
this->ike_sa->get_auth_build(this->ike_sa));
status = my_key->build_emsa_pkcs1_signature(my_key, HASH_SHA1, octets, &auth_data);
chunk_free(&octets);