Implemented a hook that recreates a valid incoming IKE_AUTH response, even if AUTH_FAILED
This commit is contained in:
@@ -6,7 +6,7 @@ conftest_SOURCES = conftest.c conftest.h config.c config.h actions.c actions.h \
|
||||
hooks/hook.h hooks/ike_auth_fill.c hooks/unsort_message.c \
|
||||
hooks/add_notify.c hooks/unencrypted_notify.c hooks/ignore_message.c \
|
||||
hooks/add_payload.c hooks/set_critical.c hooks/force_cookie.c \
|
||||
hooks/set_ike_version.c
|
||||
hooks/set_ike_version.c hooks/pretend_auth.c
|
||||
|
||||
INCLUDES = \
|
||||
-I$(top_srcdir)/src/libstrongswan \
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Martin Willi
|
||||
* Copyright (C) 2010 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 <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 "hook.h"
|
||||
|
||||
#include <encoding/payloads/nonce_payload.h>
|
||||
#include <encoding/payloads/cert_payload.h>
|
||||
#include <encoding/payloads/auth_payload.h>
|
||||
#include <encoding/payloads/id_payload.h>
|
||||
#include <encoding/payloads/sa_payload.h>
|
||||
#include <encoding/payloads/ts_payload.h>
|
||||
|
||||
typedef struct private_pretend_auth_t private_pretend_auth_t;
|
||||
|
||||
/**
|
||||
* Private data of an pretend_auth_t object.
|
||||
*/
|
||||
struct private_pretend_auth_t {
|
||||
|
||||
/**
|
||||
* Implements the hook_t interface.
|
||||
*/
|
||||
hook_t hook;
|
||||
|
||||
/**
|
||||
* remote peer identity
|
||||
*/
|
||||
identification_t *id;
|
||||
|
||||
/**
|
||||
* IKE_SA_INIT data for signature
|
||||
*/
|
||||
chunk_t ike_init;
|
||||
|
||||
/**
|
||||
* Nonce for signature
|
||||
*/
|
||||
chunk_t nonce;
|
||||
|
||||
/**
|
||||
* Selected CHILD_SA proposal
|
||||
*/
|
||||
proposal_t *proposal;
|
||||
|
||||
/**
|
||||
* List of initiators Traffic Selectors
|
||||
*/
|
||||
linked_list_t *tsi;
|
||||
|
||||
/**
|
||||
* List of responders Traffic Selectors
|
||||
*/
|
||||
linked_list_t *tsr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Process IKE_SA_INIT request message, outgoing
|
||||
*/
|
||||
static void process_init_request(private_pretend_auth_t *this,
|
||||
ike_sa_t *ike_sa, message_t *message)
|
||||
{
|
||||
nonce_payload_t *nonce;
|
||||
|
||||
nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
|
||||
if (nonce)
|
||||
{
|
||||
free(this->nonce.ptr);
|
||||
this->nonce = nonce->get_nonce(nonce);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process IKE_AUTH request message, outgoing
|
||||
*/
|
||||
static void process_auth_request(private_pretend_auth_t *this,
|
||||
ike_sa_t *ike_sa, message_t *message)
|
||||
{
|
||||
id_payload_t *id;
|
||||
sa_payload_t *sa;
|
||||
ts_payload_t *tsi, *tsr;
|
||||
linked_list_t *proposals;
|
||||
|
||||
id = (id_payload_t*)message->get_payload(message, ID_RESPONDER);
|
||||
if (id)
|
||||
{
|
||||
this->id->destroy(this->id);
|
||||
this->id = id->get_identification(id);
|
||||
}
|
||||
sa = (sa_payload_t*)message->get_payload(message, SECURITY_ASSOCIATION);
|
||||
if (sa)
|
||||
{
|
||||
proposals = sa->get_proposals(sa);
|
||||
proposals->remove_first(proposals, (void**)&this->proposal);
|
||||
if (this->proposal)
|
||||
{
|
||||
this->proposal->set_spi(this->proposal, htonl(0x12345678));
|
||||
}
|
||||
proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
|
||||
}
|
||||
tsi = (ts_payload_t*)message->get_payload(message,
|
||||
TRAFFIC_SELECTOR_INITIATOR);
|
||||
if (tsi)
|
||||
{
|
||||
this->tsi = tsi->get_traffic_selectors(tsi);
|
||||
}
|
||||
tsr = (ts_payload_t*)message->get_payload(message,
|
||||
TRAFFIC_SELECTOR_RESPONDER);
|
||||
if (tsr)
|
||||
{
|
||||
this->tsr = tsr->get_traffic_selectors(tsr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Process IKE_SA_INIT response message, incoming
|
||||
*/
|
||||
static void process_init_response(private_pretend_auth_t *this,
|
||||
ike_sa_t *ike_sa, message_t *message)
|
||||
{
|
||||
this->ike_init = message->get_packet_data(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build CERT payloads
|
||||
*/
|
||||
static void build_certs(private_pretend_auth_t *this,
|
||||
ike_sa_t *ike_sa, message_t *message, auth_cfg_t *auth)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
cert_payload_t *payload;
|
||||
certificate_t *cert;
|
||||
auth_rule_t type;
|
||||
|
||||
/* get subject cert first, then issuing certificates */
|
||||
cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
|
||||
if (cert)
|
||||
{
|
||||
payload = cert_payload_create_from_cert(cert);
|
||||
if (payload)
|
||||
{
|
||||
DBG1(DBG_IKE, "pretending end entity cert \"%Y\"",
|
||||
cert->get_subject(cert));
|
||||
message->add_payload(message, (payload_t*)payload);
|
||||
}
|
||||
}
|
||||
enumerator = auth->create_enumerator(auth);
|
||||
while (enumerator->enumerate(enumerator, &type, &cert))
|
||||
{
|
||||
if (type == AUTH_RULE_IM_CERT)
|
||||
{
|
||||
payload = cert_payload_create_from_cert(cert);
|
||||
if (payload)
|
||||
{
|
||||
DBG1(DBG_IKE, "pretending issuer cert \"%Y\"",
|
||||
cert->get_subject(cert));
|
||||
message->add_payload(message, (payload_t*)payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build faked AUTH payload
|
||||
*/
|
||||
static bool build_auth(private_pretend_auth_t *this,
|
||||
ike_sa_t *ike_sa, message_t *message)
|
||||
{
|
||||
chunk_t octets, auth_data;
|
||||
private_key_t *private;
|
||||
auth_cfg_t *auth;
|
||||
auth_payload_t *auth_payload;
|
||||
auth_method_t auth_method;
|
||||
signature_scheme_t scheme;
|
||||
keymat_t *keymat;
|
||||
|
||||
auth = auth_cfg_create();
|
||||
private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, this->id, auth);
|
||||
build_certs(this, ike_sa, message, auth);
|
||||
auth->destroy(auth);
|
||||
if (private == NULL)
|
||||
{
|
||||
DBG1(DBG_CFG, "no private key found for '%Y' to pretend AUTH", this->id);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch (private->get_type(private))
|
||||
{
|
||||
case KEY_RSA:
|
||||
scheme = SIGN_RSA_EMSA_PKCS1_SHA1;
|
||||
auth_method = AUTH_RSA;
|
||||
break;
|
||||
case KEY_ECDSA:
|
||||
/* we try to deduct the signature scheme from the keysize */
|
||||
switch (private->get_keysize(private))
|
||||
{
|
||||
case 256:
|
||||
scheme = SIGN_ECDSA_256;
|
||||
auth_method = AUTH_ECDSA_256;
|
||||
break;
|
||||
case 384:
|
||||
scheme = SIGN_ECDSA_384;
|
||||
auth_method = AUTH_ECDSA_384;
|
||||
break;
|
||||
case 521:
|
||||
scheme = SIGN_ECDSA_521;
|
||||
auth_method = AUTH_ECDSA_521;
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_CFG, "%d bit ECDSA private key size not supported",
|
||||
private->get_keysize(private));
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_CFG, "private key of type %N not supported",
|
||||
key_type_names, private->get_type(private));
|
||||
return FALSE;
|
||||
}
|
||||
keymat = ike_sa->get_keymat(ike_sa);
|
||||
octets = keymat->get_auth_octets(keymat, TRUE, this->ike_init,
|
||||
this->nonce, this->id);
|
||||
if (!private->sign(private, scheme, octets, &auth_data))
|
||||
{
|
||||
chunk_free(&octets);
|
||||
private->destroy(private);
|
||||
return FALSE;
|
||||
}
|
||||
auth_payload = auth_payload_create();
|
||||
auth_payload->set_auth_method(auth_payload, auth_method);
|
||||
auth_payload->set_data(auth_payload, auth_data);
|
||||
chunk_free(&auth_data);
|
||||
chunk_free(&octets);
|
||||
private->destroy(private);
|
||||
message->add_payload(message, (payload_t*)auth_payload);
|
||||
DBG1(DBG_CFG, "pretending AUTH payload for '%Y' with %N",
|
||||
this->id, auth_method_names, auth_method);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process IKE_AUTH response message, incoming
|
||||
*/
|
||||
static void process_auth_response(private_pretend_auth_t *this,
|
||||
ike_sa_t *ike_sa, message_t *message)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
payload_t *payload;
|
||||
|
||||
/* check for, and remove AUTHENTICATION_FAILED notify */
|
||||
enumerator = message->create_payload_enumerator(message);
|
||||
while (enumerator->enumerate(enumerator, &payload))
|
||||
{
|
||||
notify_payload_t *notify = (notify_payload_t*)payload;
|
||||
|
||||
if (payload->get_type(payload) != NOTIFY ||
|
||||
notify->get_notify_type(notify) != AUTHENTICATION_FAILED)
|
||||
{
|
||||
DBG1(DBG_CFG, "no %N notify found, disabling AUTH pretending",
|
||||
notify_type_names, AUTHENTICATION_FAILED);
|
||||
enumerator->destroy(enumerator);
|
||||
return;
|
||||
}
|
||||
message->remove_payload_at(message, enumerator);
|
||||
payload->destroy(payload);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
if (!build_auth(this, ike_sa, message))
|
||||
{
|
||||
message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
|
||||
return;
|
||||
}
|
||||
message->add_payload(message, (payload_t*)
|
||||
id_payload_create_from_identification(ID_RESPONDER, this->id));
|
||||
if (this->proposal)
|
||||
{
|
||||
message->add_payload(message, (payload_t*)
|
||||
sa_payload_create_from_proposal(this->proposal));
|
||||
}
|
||||
if (this->tsi)
|
||||
{
|
||||
message->add_payload(message, (payload_t*)
|
||||
ts_payload_create_from_traffic_selectors(TRUE, this->tsi));
|
||||
}
|
||||
if (this->tsr)
|
||||
{
|
||||
message->add_payload(message, (payload_t*)
|
||||
ts_payload_create_from_traffic_selectors(FALSE, this->tsr));
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(listener_t, message, bool,
|
||||
private_pretend_auth_t *this, ike_sa_t *ike_sa, message_t *message,
|
||||
bool incoming)
|
||||
{
|
||||
if (incoming)
|
||||
{
|
||||
if (!message->get_request(message))
|
||||
{
|
||||
if (message->get_exchange_type(message) == IKE_SA_INIT)
|
||||
{
|
||||
process_init_response(this, ike_sa, message);
|
||||
}
|
||||
if (message->get_exchange_type(message) == IKE_AUTH &&
|
||||
message->get_message_id(message) == 1)
|
||||
{
|
||||
process_auth_response(this, ike_sa, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (message->get_request(message))
|
||||
{
|
||||
if (message->get_exchange_type(message) == IKE_SA_INIT)
|
||||
{
|
||||
process_init_request(this, ike_sa, message);
|
||||
}
|
||||
if (message->get_exchange_type(message) == IKE_AUTH &&
|
||||
message->get_message_id(message) == 1)
|
||||
{
|
||||
process_auth_request(this, ike_sa, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(hook_t, destroy, void,
|
||||
private_pretend_auth_t *this)
|
||||
{
|
||||
if (this->tsi)
|
||||
{
|
||||
this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
|
||||
}
|
||||
if (this->tsr)
|
||||
{
|
||||
this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
|
||||
}
|
||||
DESTROY_IF(this->proposal);
|
||||
this->id->destroy(this->id);
|
||||
free(this->ike_init.ptr);
|
||||
free(this->nonce.ptr);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the IKE_AUTH fill hook
|
||||
*/
|
||||
hook_t *pretend_auth_hook_create(char *name)
|
||||
{
|
||||
private_pretend_auth_t *this;
|
||||
|
||||
INIT(this,
|
||||
.hook = {
|
||||
.listener = {
|
||||
.message = _message,
|
||||
},
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.id = identification_create_from_string(
|
||||
conftest->test->get_str(conftest->test,
|
||||
"hooks.%s.peer", "%any", name)),
|
||||
);
|
||||
|
||||
return &this->hook;
|
||||
}
|
||||
Reference in New Issue
Block a user