strongswan/src/charon-tkm/src/tkm/tkm_listener.c

371 lines
8.9 KiB
C

/*
* Copyright (C) 2012 Reto Buerki
* Copyright (C) 2012 Adrian-Ken Rueegsegger
* 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 <stdarg.h>
#include <daemon.h>
#include <encoding/payloads/auth_payload.h>
#include <utils/chunk.h>
#include <tkm/types.h>
#include <tkm/constants.h>
#include <tkm/client.h>
#include "tkm.h"
#include "tkm_listener.h"
#include "tkm_keymat.h"
#include "tkm_utils.h"
typedef struct private_tkm_listener_t private_tkm_listener_t;
/**
* Private data of a tkm_listener_t object.
*/
struct private_tkm_listener_t {
/**
* Public tkm_listener_t interface.
*/
tkm_listener_t public;
};
/**
* Return id of remote identity.
*
* TODO: Replace this with the lookup for the remote identitiy id.
*
* Currently the reqid of the first child SA in peer config of IKE SA is
* returned. Might choose wrong reqid if IKE SA has multiple child configs
* with different reqids.
*
* @param peer_cfg Remote peer config
* @return remote identity id if found, 0 otherwise
*/
static ri_id_type get_remote_identity_id(peer_cfg_t *peer)
{
ri_id_type remote_id = 0;
child_cfg_t *child;
enumerator_t* children;
children = peer->create_child_cfg_enumerator(peer);
/* pick the reqid of the first child, no need to enumerate all children. */
children->enumerate(children, &child);
remote_id = child->get_reqid(child);
children->destroy(children);
return remote_id;
}
/**
* Build a TKM certificate chain context with given cc id.
*
* @param ike_sa IKE SA containing auth config to build certificate chain from
* @param cc_id Certificate chain ID
* @return TRUE if certificate chain was built successfully,
* FALSE otherwise
*/
static bool build_cert_chain(const ike_sa_t * const ike_sa, cc_id_type cc_id)
{
auth_cfg_t *auth;
certificate_t *cert;
enumerator_t *rounds;
DBG1(DBG_IKE, "building certificate chain context %llu for IKE SA %s",
cc_id, ike_sa->get_name((ike_sa_t *)ike_sa));
rounds = ike_sa->create_auth_cfg_enumerator((ike_sa_t *)ike_sa, FALSE);
while (rounds->enumerate(rounds, &auth))
{
cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
if (cert)
{
chunk_t enc_user_cert;
ri_id_type ri_id;
certificate_type user_cert;
auth_rule_t rule;
enumerator_t *enumerator;
/* set user certificate */
if (!cert->get_encoding(cert, CERT_ASN1_DER, &enc_user_cert))
{
DBG1(DBG_IKE, "unable to extract encoded user certificate");
rounds->destroy(rounds);
return FALSE;
}
ri_id = get_remote_identity_id(ike_sa->get_peer_cfg((ike_sa_t *)ike_sa));
chunk_to_sequence(&enc_user_cert, &user_cert, sizeof(certificate_type));
chunk_free(&enc_user_cert);
if (ike_cc_set_user_certificate(cc_id, ri_id, 1, user_cert) != TKM_OK)
{
DBG1(DBG_IKE, "error setting user certificate of cert chain"
" (cc_id: %llu)", cc_id);
rounds->destroy(rounds);
return FALSE;
}
/* process intermediate CA certificates */
enumerator = auth->create_enumerator(auth);
while (enumerator->enumerate(enumerator, &rule, &cert))
{
if (rule == AUTH_RULE_IM_CERT)
{
chunk_t enc_im_cert;
certificate_type im_cert;
if (!cert->get_encoding(cert, CERT_ASN1_DER, &enc_im_cert))
{
DBG1(DBG_IKE, "unable to extract encoded intermediate CA"
" certificate");
rounds->destroy(rounds);
enumerator->destroy(enumerator);
return FALSE;
}
chunk_to_sequence(&enc_im_cert, &im_cert,
sizeof(certificate_type));
chunk_free(&enc_im_cert);
if (ike_cc_add_certificate(cc_id, 1, im_cert) != TKM_OK)
{
DBG1(DBG_IKE, "error adding intermediate certificate to"
" cert chain (cc_id: %llu)", cc_id);
rounds->destroy(rounds);
enumerator->destroy(enumerator);
return FALSE;
}
}
}
enumerator->destroy(enumerator);
/* finally add CA certificate */
cert = auth->get(auth, AUTH_RULE_CA_CERT);
if (cert)
{
const ca_id_type ca_id = 1;
certificate_type ca_cert;
chunk_t enc_ca_cert;
if (!cert->get_encoding(cert, CERT_ASN1_DER, &enc_ca_cert))
{
DBG1(DBG_IKE, "unable to extract encoded CA certificate");
rounds->destroy(rounds);
return FALSE;
}
chunk_to_sequence(&enc_ca_cert, &ca_cert,
sizeof(certificate_type));
chunk_free(&enc_ca_cert);
if (ike_cc_add_certificate(cc_id, 1, ca_cert) != TKM_OK)
{
DBG1(DBG_IKE, "error adding CA certificate to cert chain "
"(cc_id: %llu)", cc_id);
rounds->destroy(rounds);
return FALSE;
}
if (ike_cc_check_ca(cc_id, ca_id) != TKM_OK)
{
DBG1(DBG_IKE, "certificate chain (cc_id: %llu) not based on"
" trusted CA (ca_id: %llu)", cc_id, ca_id);
rounds->destroy(rounds);
return FALSE;
}
rounds->destroy(rounds);
return TRUE;
}
else
{
DBG1(DBG_IKE, "no CA certificate");
}
}
else
{
DBG1(DBG_IKE, "no subject certificate for remote peer");
}
}
rounds->destroy(rounds);
return FALSE;
}
METHOD(listener_t, alert, bool,
private_tkm_listener_t *this, ike_sa_t *ike_sa,
alert_t alert, va_list args)
{
if (alert == ALERT_KEEP_ON_CHILD_SA_FAILURE)
{
tkm_keymat_t *keymat;
isa_id_type isa_id;
int is_first;
is_first = va_arg(args, int);
if (!is_first)
{
return TRUE;
}
keymat = (tkm_keymat_t*)ike_sa->get_keymat(ike_sa);
isa_id = keymat->get_isa_id(keymat);
DBG1(DBG_IKE, "TKM alert listener called for ISA context %llu", isa_id);
if (ike_isa_skip_create_first(isa_id) != TKM_OK)
{
DBG1(DBG_IKE, "Skip of first child SA creation failed for ISA "
"context %llu", isa_id);
}
}
return TRUE;
}
METHOD(listener_t, authorize, bool,
private_tkm_listener_t *this, ike_sa_t *ike_sa,
bool final, bool *success)
{
tkm_keymat_t *keymat;
isa_id_type isa_id;
cc_id_type cc_id;
chunk_t *auth, *other_init_msg;
signature_type signature;
init_message_type init_msg;
if (!final)
{
return TRUE;
}
*success = FALSE;
keymat = (tkm_keymat_t*)ike_sa->get_keymat(ike_sa);
isa_id = keymat->get_isa_id(keymat);
DBG1(DBG_IKE, "TKM authorize listener called for ISA context %llu", isa_id);
cc_id = tkm->idmgr->acquire_id(tkm->idmgr, TKM_CTX_CC);
if (!cc_id)
{
DBG1(DBG_IKE, "unable to acquire CC context id");
return TRUE;
}
if (!build_cert_chain(ike_sa, cc_id))
{
DBG1(DBG_IKE, "unable to build certificate chain");
goto cc_reset;
}
auth = keymat->get_auth_payload(keymat);
if (!auth->ptr)
{
DBG1(DBG_IKE, "no AUTHENTICATION data available");
goto cc_reset;
}
other_init_msg = keymat->get_peer_init_msg(keymat);
if (!other_init_msg->ptr)
{
DBG1(DBG_IKE, "no peer init message available");
goto cc_reset;
}
chunk_to_sequence(auth, &signature, sizeof(signature_type));
chunk_to_sequence(other_init_msg, &init_msg, sizeof(init_message_type));
if (ike_isa_auth(isa_id, cc_id, init_msg, signature) != TKM_OK)
{
DBG1(DBG_IKE, "TKM based authentication failed"
" for ISA context %llu", isa_id);
goto cc_reset;
}
else
{
DBG1(DBG_IKE, "TKM based authentication successful"
" for ISA context %llu", isa_id);
*success = TRUE;
}
cc_reset:
if (ike_cc_reset(cc_id) != TKM_OK)
{
DBG1(DBG_IKE, "unable to reset CC context %llu", cc_id);
}
tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_CC, cc_id);
return TRUE; /* stay registered */
}
METHOD(listener_t, message, bool,
private_tkm_listener_t *this, ike_sa_t *ike_sa,
message_t *message, bool incoming, bool plain)
{
tkm_keymat_t *keymat;
isa_id_type isa_id;
auth_payload_t *auth_payload;
if (!incoming || !plain || message->get_exchange_type(message) != IKE_AUTH)
{
return TRUE;
}
keymat = (tkm_keymat_t*)ike_sa->get_keymat(ike_sa);
isa_id = keymat->get_isa_id(keymat);
DBG1(DBG_IKE, "saving AUTHENTICATION payload for authorize hook"
" (ISA context %llu)", isa_id);
auth_payload = (auth_payload_t*)message->get_payload(message,
PLV2_AUTH);
if (auth_payload)
{
chunk_t auth_data;
auth_data = auth_payload->get_data(auth_payload);
keymat->set_auth_payload(keymat, &auth_data);
}
else
{
DBG1(DBG_IKE, "unable to extract AUTHENTICATION payload, authorize will"
" fail");
}
return TRUE;
}
METHOD(tkm_listener_t, destroy, void,
private_tkm_listener_t *this)
{
free(this);
}
/**
* See header
*/
tkm_listener_t *tkm_listener_create()
{
private_tkm_listener_t *this;
INIT(this,
.public = {
.listener = {
.authorize = _authorize,
.message = _message,
.alert = _alert,
},
.destroy = _destroy,
},
);
return &this->public;
}