Separated libcharon/sa directory with ikev1 and ikev2 subfolders

This commit is contained in:
Martin Willi
2012-03-20 17:31:26 +01:00
parent 2e3c9f8799
commit 15a682f4c2
127 changed files with 206 additions and 212 deletions
@@ -0,0 +1,196 @@
/*
* 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 <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 "informational.h"
#include <daemon.h>
#include <sa/ikev1/tasks/isakmp_delete.h>
#include <sa/ikev1/tasks/quick_delete.h>
#include <encoding/payloads/delete_payload.h>
typedef struct private_informational_t private_informational_t;
/**
* Private members of a informational_t task.
*/
struct private_informational_t {
/**
* Public methods and task_t interface.
*/
informational_t public;
/**
* Assigned IKE_SA.
*/
ike_sa_t *ike_sa;
/**
* Notify payload to send
*/
notify_payload_t *notify;
/**
* Delete subtask
*/
task_t *del;
};
METHOD(task_t, build_i, status_t,
private_informational_t *this, message_t *message)
{
message->add_payload(message, &this->notify->payload_interface);
this->notify = NULL;
return SUCCESS;
}
METHOD(task_t, process_r, status_t,
private_informational_t *this, message_t *message)
{
enumerator_t *enumerator;
delete_payload_t *delete;
notify_payload_t *notify;
notify_type_t type;
payload_t *payload;
status_t status = SUCCESS;
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
switch (payload->get_type(payload))
{
case NOTIFY_V1:
notify = (notify_payload_t*)payload;
type = notify->get_notify_type(notify);
if (type == INITIAL_CONTACT_IKEV1)
{
this->ike_sa->set_condition(this->ike_sa,
COND_INIT_CONTACT_SEEN, TRUE);
}
else if (type < 16384)
{
DBG1(DBG_IKE, "received %N error notify",
notify_type_names, notify->get_notify_type(notify));
if (this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING)
{ /* only critical during main mode */
status = FAILED;
}
break;
}
else
{
DBG1(DBG_IKE, "received %N notify",
notify_type_names, notify->get_notify_type(notify));
}
continue;
case DELETE_V1:
if (!this->del)
{
delete = (delete_payload_t*)payload;
if (delete->get_protocol_id(delete) == PROTO_IKE)
{
this->del = (task_t*)isakmp_delete_create(this->ike_sa,
FALSE);
}
else
{
this->del = (task_t*)quick_delete_create(this->ike_sa,
PROTO_NONE, 0, FALSE);
}
}
break;
default:
continue;
}
break;
}
enumerator->destroy(enumerator);
if (this->del && status == SUCCESS)
{
return this->del->process(this->del, message);
}
return status;
}
METHOD(task_t, build_r, status_t,
private_informational_t *this, message_t *message)
{
if (this->del)
{
return this->del->build(this->del, message);
}
return FAILED;
}
METHOD(task_t, process_i, status_t,
private_informational_t *this, message_t *message)
{
return FAILED;
}
METHOD(task_t, get_type, task_type_t,
private_informational_t *this)
{
return TASK_INFORMATIONAL;
}
METHOD(task_t, migrate, void,
private_informational_t *this, ike_sa_t *ike_sa)
{
this->ike_sa = ike_sa;
}
METHOD(task_t, destroy, void,
private_informational_t *this)
{
DESTROY_IF(this->notify);
DESTROY_IF(this->del);
free(this);
}
/*
* Described in header.
*/
informational_t *informational_create(ike_sa_t *ike_sa, notify_payload_t *notify)
{
private_informational_t *this;
INIT(this,
.public = {
.task = {
.get_type = _get_type,
.migrate = _migrate,
.destroy = _destroy,
},
},
.ike_sa = ike_sa,
.notify = notify,
);
if (notify)
{
this->public.task.build = _build_i;
this->public.task.process = _process_i;
}
else
{
this->public.task.build = _build_r;
this->public.task.process = _process_r;
}
return &this->public;
}
@@ -0,0 +1,51 @@
/*
* 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 <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.
*/
/**
* @defgroup informational informational
* @{ @ingroup tasks
*/
#ifndef INFORMATIONAL_H_
#define informational_H_
typedef struct informational_t informational_t;
#include <library.h>
#include <sa/ike_sa.h>
#include <sa/task.h>
#include <encoding/payloads/notify_payload.h>
/**
* IKEv1 informational exchange, negotiates errors.
*/
struct informational_t {
/**
* Implements the task_t interface
*/
task_t task;
};
/**
* Create a new informational task.
*
* @param ike_sa IKE_SA this task works for
* @param notify notify to send as initiator, NULL if responder
* @return task to handle by the task_manager
*/
informational_t *informational_create(ike_sa_t *ike_sa, notify_payload_t *notify);
#endif /** INFORMATIONAL_H_ @}*/
@@ -0,0 +1,348 @@
/*
* 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 <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 "isakmp_cert_post.h"
#include <daemon.h>
#include <sa/ike_sa.h>
#include <encoding/payloads/cert_payload.h>
#include <encoding/payloads/certreq_payload.h>
#include <encoding/payloads/auth_payload.h>
#include <encoding/payloads/sa_payload.h>
#include <credentials/certificates/x509.h>
typedef struct private_isakmp_cert_post_t private_isakmp_cert_post_t;
/**
* Private members of a isakmp_cert_post_t task.
*/
struct private_isakmp_cert_post_t {
/**
* Public methods and task_t interface.
*/
isakmp_cert_post_t public;
/**
* Assigned IKE_SA.
*/
ike_sa_t *ike_sa;
/**
* Are we the initiator?
*/
bool initiator;
/**
* States of ike cert pre
*/
enum {
CR_SA,
CR_KE,
CR_AUTH,
} state;
};
/**
* Check if we actually use certificates for authentication
*/
static bool use_certs(private_isakmp_cert_post_t *this, message_t *message)
{
enumerator_t *enumerator;
payload_t *payload;
bool use = FALSE;
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
if (payload->get_type(payload) == SECURITY_ASSOCIATION_V1)
{
sa_payload_t *sa_payload = (sa_payload_t*)payload;
switch (sa_payload->get_auth_method(sa_payload))
{
case AUTH_RSA:
case AUTH_XAUTH_INIT_RSA:
case AUTH_XAUTH_RESP_RSA:
case AUTH_HYBRID_INIT_RSA:
case AUTH_HYBRID_RESP_RSA:
use = TRUE;
break;
default:
break;
}
break;
}
}
enumerator->destroy(enumerator);
return use;
}
/**
* Add certificates to message
*/
static void build_certs(private_isakmp_cert_post_t *this, message_t *message)
{
peer_cfg_t *peer_cfg;
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
if (!peer_cfg)
{
return;
}
switch (peer_cfg->get_cert_policy(peer_cfg))
{
case CERT_NEVER_SEND:
break;
case CERT_SEND_IF_ASKED:
if (!this->ike_sa->has_condition(this->ike_sa, COND_CERTREQ_SEEN))
{
break;
}
/* FALL */
case CERT_ALWAYS_SEND:
{
cert_payload_t *payload;
enumerator_t *enumerator;
certificate_t *cert;
auth_rule_t type;
auth_cfg_t *auth;
auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
if (!cert)
{
break;
}
payload = cert_payload_create_from_cert(CERTIFICATE_V1, cert);
if (!payload)
{
break;
}
DBG1(DBG_IKE, "sending 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(CERTIFICATE_V1, cert);
if (payload)
{
DBG1(DBG_IKE, "sending issuer cert \"%Y\"",
cert->get_subject(cert));
message->add_payload(message, (payload_t*)payload);
}
}
}
enumerator->destroy(enumerator);
}
}
}
METHOD(task_t, build_i, status_t,
private_isakmp_cert_post_t *this, message_t *message)
{
switch (message->get_exchange_type(message))
{
case ID_PROT:
if (this->state == CR_AUTH)
{
build_certs(this, message);
return SUCCESS;
}
return NEED_MORE;
case AGGRESSIVE:
if (this->state == CR_AUTH)
{
build_certs(this, message);
return SUCCESS;
}
return NEED_MORE;
default:
return FAILED;
}
}
METHOD(task_t, process_r, status_t,
private_isakmp_cert_post_t *this, message_t *message)
{
switch (message->get_exchange_type(message))
{
case ID_PROT:
{
switch (this->state)
{
case CR_SA:
if (!use_certs(this, message))
{
return SUCCESS;
}
return NEED_MORE;
case CR_KE:
return NEED_MORE;
case CR_AUTH:
return NEED_MORE;
}
}
case AGGRESSIVE:
{
switch (this->state)
{
case CR_SA:
if (!use_certs(this, message))
{
return SUCCESS;
}
return NEED_MORE;
case CR_AUTH:
return SUCCESS;
default:
return FAILED;
}
}
default:
return FAILED;
}
}
METHOD(task_t, build_r, status_t,
private_isakmp_cert_post_t *this, message_t *message)
{
switch (message->get_exchange_type(message))
{
case ID_PROT:
switch (this->state)
{
case CR_SA:
this->state = CR_KE;
return NEED_MORE;
case CR_KE:
this->state = CR_AUTH;
return NEED_MORE;
case CR_AUTH:
build_certs(this, message);
return SUCCESS;
}
case AGGRESSIVE:
switch (this->state)
{
case CR_SA:
build_certs(this, message);
this->state = CR_AUTH;
return NEED_MORE;
case CR_AUTH:
return SUCCESS;
default:
return FAILED;
}
default:
return FAILED;
}
}
METHOD(task_t, process_i, status_t,
private_isakmp_cert_post_t *this, message_t *message)
{
switch (message->get_exchange_type(message))
{
case ID_PROT:
{
switch (this->state)
{
case CR_SA:
if (!use_certs(this, message))
{
return SUCCESS;
}
this->state = CR_KE;
return NEED_MORE;
case CR_KE:
this->state = CR_AUTH;
return NEED_MORE;
case CR_AUTH:
return SUCCESS;
default:
return FAILED;
}
break;
}
case AGGRESSIVE:
{
if (!use_certs(this, message))
{
return SUCCESS;
}
return SUCCESS;
}
default:
return FAILED;
}
}
METHOD(task_t, get_type, task_type_t,
private_isakmp_cert_post_t *this)
{
return TASK_ISAKMP_CERT_POST;
}
METHOD(task_t, migrate, void,
private_isakmp_cert_post_t *this, ike_sa_t *ike_sa)
{
this->ike_sa = ike_sa;
}
METHOD(task_t, destroy, void,
private_isakmp_cert_post_t *this)
{
free(this);
}
/*
* Described in header.
*/
isakmp_cert_post_t *isakmp_cert_post_create(ike_sa_t *ike_sa, bool initiator)
{
private_isakmp_cert_post_t *this;
INIT(this,
.public = {
.task = {
.get_type = _get_type,
.migrate = _migrate,
.destroy = _destroy,
},
},
.ike_sa = ike_sa,
.initiator = initiator,
.state = CR_SA,
);
if (initiator)
{
this->public.task.process = _process_i;
this->public.task.build = _build_i;
}
else
{
this->public.task.process = _process_r;
this->public.task.build = _build_r;
}
return &this->public;
}
@@ -0,0 +1,53 @@
/*
* 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 <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.
*/
/**
* @defgroup isakmp_cert_post isakmp_cert_post
* @{ @ingroup tasks
*/
#ifndef ISAKMP_CERT_POST_H_
#define ISAKMP_CERT_POST_H_
typedef struct isakmp_cert_post_t isakmp_cert_post_t;
#include <library.h>
#include <sa/ike_sa.h>
#include <sa/task.h>
/**
* ISAKMP_CERT_POST, IKEv1 certificate processing after authentication.
*/
struct isakmp_cert_post_t {
/**
* Implements the task_t interface
*/
task_t task;
};
/**
* Create a new isakmp_cert_post task.
*
* The initiator parameter means the original initiator, not the initiator
* of the certificate request.
*
* @param ike_sa IKE_SA this task works for
* @param initiator TRUE if task is the original initiator
* @return isakmp_cert_post task to handle by the task_manager
*/
isakmp_cert_post_t *isakmp_cert_post_create(ike_sa_t *ike_sa, bool initiator);
#endif /** ISAKMP_CERT_POST_H_ @}*/
@@ -0,0 +1,533 @@
/*
* 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 <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 "isakmp_cert_pre.h"
#include <daemon.h>
#include <sa/ike_sa.h>
#include <encoding/payloads/cert_payload.h>
#include <encoding/payloads/sa_payload.h>
#include <encoding/payloads/certreq_payload.h>
#include <credentials/certificates/x509.h>
typedef struct private_isakmp_cert_pre_t private_isakmp_cert_pre_t;
/**
* Private members of a isakmp_cert_pre_t task.
*/
struct private_isakmp_cert_pre_t {
/**
* Public methods and task_t interface.
*/
isakmp_cert_pre_t public;
/**
* Assigned IKE_SA.
*/
ike_sa_t *ike_sa;
/**
* Are we the initiator?
*/
bool initiator;
/**
* Send certificate requests?
*/
bool send_req;
/** next message we expect */
enum {
CR_SA,
CR_KE,
CR_AUTH,
} state;
};
/**
* Find the CA certificate for a given certreq payload
*/
static certificate_t* find_certificate(private_isakmp_cert_pre_t *this,
certreq_payload_t *certreq)
{
identification_t *id;
certificate_t *cert;
if (certreq->get_cert_type(certreq) != CERT_X509)
{
DBG1(DBG_IKE, "%N CERTREQ not supported - ignored",
certificate_type_names, certreq->get_cert_type(certreq));
return NULL;
}
id = certreq->get_dn(certreq);
if (!id)
{
DBG1(DBG_IKE, "ignoring certificate request without data",
certificate_type_names, certreq->get_cert_type(certreq));
return NULL;
}
cert = lib->credmgr->get_cert(lib->credmgr, CERT_X509, KEY_ANY, id, TRUE);
if (cert)
{
DBG1(DBG_IKE, "received cert request for '%Y'",
cert->get_subject(cert));
}
else
{
DBG1(DBG_IKE, "received cert request for unknown ca '%Y'", id);
}
id->destroy(id);
return cert;
}
/**
* read certificate requests
*/
static void process_certreqs(private_isakmp_cert_pre_t *this, message_t *message)
{
enumerator_t *enumerator;
payload_t *payload;
auth_cfg_t *auth;
auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
switch (payload->get_type(payload))
{
case CERTIFICATE_REQUEST_V1:
{
certificate_t *cert;
this->ike_sa->set_condition(this->ike_sa,
COND_CERTREQ_SEEN, TRUE);
cert = find_certificate(this, (certreq_payload_t*)payload);
if (cert)
{
auth->add(auth, AUTH_RULE_CA_CERT, cert);
}
break;
}
default:
break;
}
}
enumerator->destroy(enumerator);
}
/**
* Import receuved certificates
*/
static void process_certs(private_isakmp_cert_pre_t *this, message_t *message)
{
enumerator_t *enumerator;
payload_t *payload;
auth_cfg_t *auth;
bool first = TRUE;
auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
if (payload->get_type(payload) == CERTIFICATE_V1)
{
cert_payload_t *cert_payload;
cert_encoding_t encoding;
certificate_t *cert;
cert_payload = (cert_payload_t*)payload;
encoding = cert_payload->get_cert_encoding(cert_payload);
switch (encoding)
{
case ENC_X509_SIGNATURE:
{
cert = cert_payload->get_cert(cert_payload);
if (cert)
{
if (first)
{ /* the first is an end entity certificate */
DBG1(DBG_IKE, "received end entity cert \"%Y\"",
cert->get_subject(cert));
auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert);
first = FALSE;
}
else
{
DBG1(DBG_IKE, "received issuer cert \"%Y\"",
cert->get_subject(cert));
auth->add(auth, AUTH_HELPER_IM_CERT, cert);
}
}
break;
}
case ENC_CRL:
cert = cert_payload->get_cert(cert_payload);
if (cert)
{
DBG1(DBG_IKE, "received CRL \"%Y\"",
cert->get_subject(cert));
auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert);
}
break;
case ENC_PKCS7_WRAPPED_X509:
case ENC_PGP:
case ENC_DNS_SIGNED_KEY:
case ENC_KERBEROS_TOKEN:
case ENC_ARL:
case ENC_SPKI:
case ENC_X509_ATTRIBUTE:
case ENC_RAW_RSA_KEY:
case ENC_X509_HASH_AND_URL_BUNDLE:
case ENC_OCSP_CONTENT:
default:
DBG1(DBG_ENC, "certificate encoding %N not supported",
cert_encoding_names, encoding);
}
}
}
enumerator->destroy(enumerator);
}
/**
* Add the subject of a CA certificate a message
*/
static void add_certreq(private_isakmp_cert_pre_t *this, message_t *message,
certificate_t *cert)
{
if (cert->get_type(cert) == CERT_X509)
{
x509_t *x509 = (x509_t*)cert;
if (x509->get_flags(x509) & X509_CA)
{
DBG1(DBG_IKE, "sending cert request for \"%Y\"",
cert->get_subject(cert));
message->add_payload(message, (payload_t*)
certreq_payload_create_dn(cert->get_subject(cert)));
}
}
}
/**
* Add auth_cfg's CA certificates to the certificate request
*/
static void add_certreqs(private_isakmp_cert_pre_t *this,
auth_cfg_t *auth, message_t *message)
{
enumerator_t *enumerator;
auth_rule_t type;
void *value;
enumerator = auth->create_enumerator(auth);
while (enumerator->enumerate(enumerator, &type, &value))
{
switch (type)
{
case AUTH_RULE_CA_CERT:
add_certreq(this, message, (certificate_t*)value);
break;
default:
break;
}
}
enumerator->destroy(enumerator);
}
/**
* Build certificate requests
*/
static void build_certreqs(private_isakmp_cert_pre_t *this, message_t *message)
{
enumerator_t *enumerator;
ike_cfg_t *ike_cfg;
peer_cfg_t *peer_cfg;
certificate_t *cert;
auth_cfg_t *auth;
ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
if (!ike_cfg->send_certreq(ike_cfg))
{
return;
}
/* check if we require a specific CA for that peer */
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
if (peer_cfg)
{
enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, FALSE);
if (enumerator->enumerate(enumerator, &auth))
{
add_certreqs(this, auth, message);
}
enumerator->destroy(enumerator);
}
if (!message->get_payload(message, CERTIFICATE_REQUEST_V1))
{
/* otherwise add all trusted CA certificates */
enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
CERT_ANY, KEY_ANY, NULL, TRUE);
while (enumerator->enumerate(enumerator, &cert))
{
add_certreq(this, message, cert);
}
enumerator->destroy(enumerator);
}
}
/**
* Check if we actually use certificates for authentication
*/
static bool use_certs(private_isakmp_cert_pre_t *this, message_t *message)
{
enumerator_t *enumerator;
payload_t *payload;
bool use = FALSE;
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
if (payload->get_type(payload) == SECURITY_ASSOCIATION_V1)
{
sa_payload_t *sa_payload = (sa_payload_t*)payload;
switch (sa_payload->get_auth_method(sa_payload))
{
case AUTH_HYBRID_INIT_RSA:
case AUTH_HYBRID_RESP_RSA:
if (!this->initiator)
{
this->send_req = FALSE;
}
/* FALL */
case AUTH_RSA:
case AUTH_XAUTH_INIT_RSA:
case AUTH_XAUTH_RESP_RSA:
use = TRUE;
break;
default:
break;
}
break;
}
}
enumerator->destroy(enumerator);
return use;
}
METHOD(task_t, build_i, status_t,
private_isakmp_cert_pre_t *this, message_t *message)
{
switch (message->get_exchange_type(message))
{
case ID_PROT:
if (this->state == CR_AUTH)
{
build_certreqs(this, message);
}
return NEED_MORE;
case AGGRESSIVE:
if (this->state == CR_SA)
{
build_certreqs(this, message);
}
return NEED_MORE;
default:
return FAILED;
}
}
METHOD(task_t, process_r, status_t,
private_isakmp_cert_pre_t *this, message_t *message)
{
switch (message->get_exchange_type(message))
{
case ID_PROT:
{
switch (this->state)
{
case CR_SA:
if (!use_certs(this, message))
{
return SUCCESS;
}
return NEED_MORE;
case CR_KE:
process_certreqs(this, message);
return NEED_MORE;
case CR_AUTH:
process_certreqs(this, message);
process_certs(this, message);
return SUCCESS;
}
}
case AGGRESSIVE:
{
switch (this->state)
{
case CR_SA:
if (!use_certs(this, message))
{
return SUCCESS;
}
process_certreqs(this, message);
return NEED_MORE;
case CR_AUTH:
process_certs(this, message);
return SUCCESS;
default:
return FAILED;
}
}
default:
return FAILED;
}
}
METHOD(task_t, build_r, status_t,
private_isakmp_cert_pre_t *this, message_t *message)
{
switch (message->get_exchange_type(message))
{
case ID_PROT:
switch (this->state)
{
case CR_SA:
this->state = CR_KE;
return NEED_MORE;
case CR_KE:
if (this->send_req)
{
build_certreqs(this, message);
}
this->state = CR_AUTH;
return NEED_MORE;
case CR_AUTH:
return NEED_MORE;
}
case AGGRESSIVE:
switch (this->state)
{
case CR_SA:
this->state = CR_AUTH;
return NEED_MORE;
case CR_AUTH:
return SUCCESS;
default:
return FAILED;
}
default:
return FAILED;
}
}
METHOD(task_t, process_i, status_t,
private_isakmp_cert_pre_t *this, message_t *message)
{
switch (message->get_exchange_type(message))
{
case ID_PROT:
{
switch (this->state)
{
case CR_SA:
if (!use_certs(this, message))
{
return SUCCESS;
}
this->state = CR_KE;
return NEED_MORE;
case CR_KE:
process_certreqs(this, message);
this->state = CR_AUTH;
return NEED_MORE;
case CR_AUTH:
process_certs(this, message);
return SUCCESS;
default:
return FAILED;
}
break;
}
case AGGRESSIVE:
{
if (!use_certs(this, message))
{
return SUCCESS;
}
process_certreqs(this, message);
process_certs(this, message);
return SUCCESS;
}
default:
return FAILED;
}
}
METHOD(task_t, get_type, task_type_t,
private_isakmp_cert_pre_t *this)
{
return TASK_ISAKMP_CERT_PRE;
}
METHOD(task_t, migrate, void,
private_isakmp_cert_pre_t *this, ike_sa_t *ike_sa)
{
this->ike_sa = ike_sa;
}
METHOD(task_t, destroy, void,
private_isakmp_cert_pre_t *this)
{
free(this);
}
/*
* Described in header.
*/
isakmp_cert_pre_t *isakmp_cert_pre_create(ike_sa_t *ike_sa, bool initiator)
{
private_isakmp_cert_pre_t *this;
INIT(this,
.public = {
.task = {
.get_type = _get_type,
.migrate = _migrate,
.destroy = _destroy,
},
},
.ike_sa = ike_sa,
.initiator = initiator,
.state = CR_SA,
.send_req = TRUE,
);
if (initiator)
{
this->public.task.build = _build_i;
this->public.task.process = _process_i;
}
else
{
this->public.task.build = _build_r;
this->public.task.process = _process_r;
}
return &this->public;
}
@@ -0,0 +1,53 @@
/*
* 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 <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.
*/
/**
* @defgroup isakmp_cert_pre isakmp_cert_pre
* @{ @ingroup tasks
*/
#ifndef ISAKMP_CERT_PRE_H_
#define ISAKMP_CERT_PRE_H_
typedef struct isakmp_cert_pre_t isakmp_cert_pre_t;
#include <library.h>
#include <sa/ike_sa.h>
#include <sa/task.h>
/**
* ISAKMP_CERT_PRE task, IKEv1 certificate processing before authentication.
*/
struct isakmp_cert_pre_t {
/**
* Implements the task_t interface
*/
task_t task;
};
/**
* Create a new ISAKMP_CERT_PRE task.
*
* The initiator parameter means the original initiator, not the initiator
* of the certificate request.
*
* @param ike_sa IKE_SA this task works for
* @param initiator TRUE if task is the original initiator
* @return isakmp_cert_pre task to handle by the task_manager
*/
isakmp_cert_pre_t *isakmp_cert_pre_create(ike_sa_t *ike_sa, bool initiator);
#endif /** ISAKMP_CERT_PRE_H_ @}*/
@@ -0,0 +1,147 @@
/*
* 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 <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 "isakmp_delete.h"
#include <daemon.h>
#include <encoding/payloads/delete_payload.h>
typedef struct private_isakmp_delete_t private_isakmp_delete_t;
/**
* Private members of a isakmp_delete_t task.
*/
struct private_isakmp_delete_t {
/**
* Public methods and task_t interface.
*/
isakmp_delete_t public;
/**
* Assigned IKE_SA.
*/
ike_sa_t *ike_sa;
};
METHOD(task_t, build_i, status_t,
private_isakmp_delete_t *this, message_t *message)
{
delete_payload_t *delete_payload;
ike_sa_id_t *id;
DBG0(DBG_IKE, "deleting IKE_SA %s[%d] between %H[%Y]...%H[%Y]",
this->ike_sa->get_name(this->ike_sa),
this->ike_sa->get_unique_id(this->ike_sa),
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_my_id(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa),
this->ike_sa->get_other_id(this->ike_sa));
delete_payload = delete_payload_create(DELETE_V1, PROTO_IKE);
id = this->ike_sa->get_id(this->ike_sa);
delete_payload->set_ike_spi(delete_payload, id->get_initiator_spi(id),
id->get_responder_spi(id));
message->add_payload(message, (payload_t*)delete_payload);
DBG1(DBG_IKE, "sending DELETE for IKE_SA %s[%d]",
this->ike_sa->get_name(this->ike_sa),
this->ike_sa->get_unique_id(this->ike_sa));
this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
return SUCCESS;
}
METHOD(task_t, process_i, status_t,
private_isakmp_delete_t *this, message_t *message)
{
return FAILED;
}
METHOD(task_t, process_r, status_t,
private_isakmp_delete_t *this, message_t *message)
{
DBG1(DBG_IKE, "received DELETE for IKE_SA %s[%d]",
this->ike_sa->get_name(this->ike_sa),
this->ike_sa->get_unique_id(this->ike_sa));
DBG0(DBG_IKE, "deleting IKE_SA %s[%d] between %H[%Y]...%H[%Y]",
this->ike_sa->get_name(this->ike_sa),
this->ike_sa->get_unique_id(this->ike_sa),
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_my_id(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa),
this->ike_sa->get_other_id(this->ike_sa));
this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
return DESTROY_ME;
}
METHOD(task_t, build_r, status_t,
private_isakmp_delete_t *this, message_t *message)
{
return FAILED;
}
METHOD(task_t, get_type, task_type_t,
private_isakmp_delete_t *this)
{
return TASK_ISAKMP_DELETE;
}
METHOD(task_t, migrate, void,
private_isakmp_delete_t *this, ike_sa_t *ike_sa)
{
this->ike_sa = ike_sa;
}
METHOD(task_t, destroy, void,
private_isakmp_delete_t *this)
{
free(this);
}
/*
* Described in header.
*/
isakmp_delete_t *isakmp_delete_create(ike_sa_t *ike_sa, bool initiator)
{
private_isakmp_delete_t *this;
INIT(this,
.public = {
.task = {
.get_type = _get_type,
.migrate = _migrate,
.destroy = _destroy,
},
},
.ike_sa = ike_sa,
);
if (initiator)
{
this->public.task.build = _build_i;
this->public.task.process = _process_i;
}
else
{
this->public.task.build = _build_r;
this->public.task.process = _process_r;
}
return &this->public;
}
@@ -0,0 +1,50 @@
/*
* 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 <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.
*/
/**
* @defgroup isakmp_delete isakmp_delete
* @{ @ingroup tasks
*/
#ifndef ISAKMP_DELETE_H_
#define ISAKMP_DELETE_H_
typedef struct isakmp_delete_t isakmp_delete_t;
#include <library.h>
#include <sa/ike_sa.h>
#include <sa/task.h>
/**
* Task of type ISAKMP_DELETE, delete an IKEv1 IKE_SA.
*/
struct isakmp_delete_t {
/**
* Implements the task_t interface
*/
task_t task;
};
/**
* Create a new isakmp_delete task.
*
* @param ike_sa IKE_SA this task works for
* @param initiator TRUE if we initiate the delete
* @return isakmp_delete task to handle by the task_manager
*/
isakmp_delete_t *isakmp_delete_create(ike_sa_t *ike_sa, bool initiator);
#endif /** ISAKMP_DELETE_H_ @}*/
+440
View File
@@ -0,0 +1,440 @@
/*
* Copyright (C) 2006-2011 Tobias Brunner,
* Copyright (C) 2006-2007 Martin Willi
* Copyright (C) 2006 Daniel Roethlisberger
* 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 "isakmp_natd.h"
#include <string.h>
#include <hydra.h>
#include <daemon.h>
#include <sa/ikev1/keymat_v1.h>
#include <config/peer_cfg.h>
#include <crypto/hashers/hasher.h>
#include <encoding/payloads/hash_payload.h>
typedef struct private_isakmp_natd_t private_isakmp_natd_t;
/**
* Private members of a ike_natt_t task.
*/
struct private_isakmp_natd_t {
/**
* Public interface.
*/
isakmp_natd_t public;
/**
* Assigned IKE_SA.
*/
ike_sa_t *ike_sa;
/**
* Are we the initiator?
*/
bool initiator;
/**
* Keymat derivation (from SA)
*/
keymat_v1_t *keymat;
/**
* Did we process any NAT detection payloads for a source address?
*/
bool src_seen;
/**
* Did we process any NAT detection payloads for a destination address?
*/
bool dst_seen;
/**
* Have we found a matching source address NAT hash?
*/
bool src_matched;
/**
* Have we found a matching destination address NAT hash?
*/
bool dst_matched;
};
/**
* Build NAT detection hash for a host.
*/
static chunk_t generate_natd_hash(private_isakmp_natd_t *this,
ike_sa_id_t *ike_sa_id, host_t *host)
{
hasher_t *hasher;
chunk_t natd_chunk, natd_hash;
u_int64_t spi_i, spi_r;
u_int16_t port;
hasher = this->keymat->get_hasher(this->keymat);
if (!hasher)
{
DBG1(DBG_IKE, "no hasher available to build NAT-D payload");
return chunk_empty;
}
spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
port = htons(host->get_port(host));
/* natd_hash = HASH(CKY-I | CKY-R | IP | Port) */
natd_chunk = chunk_cata("cccc", chunk_from_thing(spi_i),
chunk_from_thing(spi_r), host->get_address(host),
chunk_from_thing(port));
hasher->allocate_hash(hasher, natd_chunk, &natd_hash);
DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk);
DBG3(DBG_IKE, "natd_hash %B", &natd_hash);
return natd_hash;
}
/**
* Build a faked NAT-D payload to enforce UDP encapsulation.
*/
static chunk_t generate_natd_hash_faked(private_isakmp_natd_t *this)
{
hasher_t *hasher;
chunk_t chunk;
rng_t *rng;
hasher = this->keymat->get_hasher(this->keymat);
if (!hasher)
{
DBG1(DBG_IKE, "no hasher available to build NAT-D payload");
return chunk_empty;
}
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (!rng)
{
DBG1(DBG_IKE, "unable to get random bytes for NAT-D fake");
return chunk_empty;
}
rng->allocate_bytes(rng, hasher->get_hash_size(hasher), &chunk);
rng->destroy(rng);
return chunk;
}
/**
* Build a NAT-D payload.
*/
static hash_payload_t *build_natd_payload(private_isakmp_natd_t *this, bool src,
host_t *host)
{
hash_payload_t *payload;
ike_cfg_t *config;
chunk_t hash;
config = this->ike_sa->get_ike_cfg(this->ike_sa);
if (src && config->force_encap(config))
{
hash = generate_natd_hash_faked(this);
}
else
{
ike_sa_id_t *ike_sa_id = this->ike_sa->get_id(this->ike_sa);
hash = generate_natd_hash(this, ike_sa_id, host);
}
payload = hash_payload_create(NAT_D_V1);
payload->set_hash(payload, hash);
chunk_free(&hash);
return payload;
}
/**
* Add NAT-D payloads to the message.
*/
static void add_natd_payloads(private_isakmp_natd_t *this, message_t *message)
{
hash_payload_t *payload;
host_t *host;
/* destination has to be added first */
host = message->get_destination(message);
payload = build_natd_payload(this, FALSE, host);
message->add_payload(message, (payload_t*)payload);
/* source is added second, compared with IKEv2 we always know the source,
* as these payloads are added in the second Phase 1 exchange or the
* response to the first */
host = message->get_source(message);
payload = build_natd_payload(this, TRUE, host);
message->add_payload(message, (payload_t*)payload);
}
/**
* Read NAT-D payloads from message and evaluate them.
*/
static void process_payloads(private_isakmp_natd_t *this, message_t *message)
{
enumerator_t *enumerator;
payload_t *payload;
hash_payload_t *hash_payload;
chunk_t hash, src_hash, dst_hash;
ike_sa_id_t *ike_sa_id;
host_t *me, *other;
ike_cfg_t *config;
/* precompute hashes for incoming NAT-D comparison */
ike_sa_id = message->get_ike_sa_id(message);
me = message->get_destination(message);
other = message->get_source(message);
dst_hash = generate_natd_hash(this, ike_sa_id, me);
src_hash = generate_natd_hash(this, ike_sa_id, other);
DBG3(DBG_IKE, "precalculated src_hash %B", &src_hash);
DBG3(DBG_IKE, "precalculated dst_hash %B", &dst_hash);
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
if (payload->get_type(payload) != NAT_D_V1)
{
continue;
}
hash_payload = (hash_payload_t*)payload;
if (!this->dst_seen)
{ /* the first NAT-D payload contains the destination hash */
this->dst_seen = TRUE;
hash = hash_payload->get_hash(hash_payload);
DBG3(DBG_IKE, "received dst_hash %B", &hash);
if (chunk_equals(hash, dst_hash))
{
this->dst_matched = TRUE;
}
continue;
}
/* the other NAT-D payloads contain source hashes */
this->src_seen = TRUE;
if (!this->src_matched)
{
hash = hash_payload->get_hash(hash_payload);
DBG3(DBG_IKE, "received src_hash %B", &hash);
if (chunk_equals(hash, src_hash))
{
this->src_matched = TRUE;
}
}
}
enumerator->destroy(enumerator);
chunk_free(&src_hash);
chunk_free(&dst_hash);
if (this->src_seen && this->dst_seen)
{
this->ike_sa->set_condition(this->ike_sa, COND_NAT_HERE,
!this->dst_matched);
this->ike_sa->set_condition(this->ike_sa, COND_NAT_THERE,
!this->src_matched);
config = this->ike_sa->get_ike_cfg(this->ike_sa);
if (this->dst_matched && this->src_matched &&
config->force_encap(config))
{
this->ike_sa->set_condition(this->ike_sa, COND_NAT_FAKE, TRUE);
}
}
}
METHOD(task_t, build_i, status_t,
private_isakmp_natd_t *this, message_t *message)
{
status_t result = NEED_MORE;
switch (message->get_exchange_type(message))
{
case AGGRESSIVE:
{ /* add NAT-D payloads to the second request, already processed
* those by the responder contained in the first response */
result = SUCCESS;
/* fall */
}
case ID_PROT:
{ /* add NAT-D payloads to the second request, need to process
* those by the responder contained in the second response */
if (message->get_payload(message, SECURITY_ASSOCIATION_V1))
{ /* wait for the second exchange */
return NEED_MORE;
}
add_natd_payloads(this, message);
return result;
}
default:
break;
}
return SUCCESS;
}
METHOD(task_t, process_i, status_t,
private_isakmp_natd_t *this, message_t *message)
{
status_t result = NEED_MORE;
if (!this->ike_sa->supports_extension(this->ike_sa, EXT_NATT))
{ /* we didn't receive VIDs inidcating support for NAT-T */
return SUCCESS;
}
switch (message->get_exchange_type(message))
{
case ID_PROT:
{ /* process NAT-D payloads in the second response, added them in the
* second request already, so we're done afterwards */
if (message->get_payload(message, SECURITY_ASSOCIATION_V1))
{ /* wait for the second exchange */
return NEED_MORE;
}
result = SUCCESS;
/* fall */
}
case AGGRESSIVE:
{ /* process NAT-D payloads in the first response, add them in the
* following second request */
process_payloads(this, message);
if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY))
{
this->ike_sa->float_ports(this->ike_sa);
}
return result;
}
default:
break;
}
return SUCCESS;
}
METHOD(task_t, process_r, status_t,
private_isakmp_natd_t *this, message_t *message)
{
status_t result = NEED_MORE;
if (!this->ike_sa->supports_extension(this->ike_sa, EXT_NATT))
{ /* we didn't receive VIDs indicating NAT-T support */
return SUCCESS;
}
switch (message->get_exchange_type(message))
{
case AGGRESSIVE:
{ /* proccess NAT-D payloads in the second request, already added ours
* in the first response */
result = SUCCESS;
/* fall */
}
case ID_PROT:
{ /* process NAT-D payloads in the second request, need to add ours
* to the second response */
if (message->get_payload(message, SECURITY_ASSOCIATION_V1))
{ /* wait for the second exchange */
return NEED_MORE;
}
process_payloads(this, message);
return result;
}
default:
break;
}
return SUCCESS;
}
METHOD(task_t, build_r, status_t,
private_isakmp_natd_t *this, message_t *message)
{
switch (message->get_exchange_type(message))
{
case ID_PROT:
{ /* add NAT-D payloads to second response, already processed those
* contained in the second request */
if (message->get_payload(message, SECURITY_ASSOCIATION_V1))
{ /* wait for the second exchange */
return NEED_MORE;
}
add_natd_payloads(this, message);
return SUCCESS;
}
case AGGRESSIVE:
{ /* add NAT-D payloads to the first response, process those contained
* in the following second request */
add_natd_payloads(this, message);
return NEED_MORE;
}
default:
break;
}
return SUCCESS;
}
METHOD(task_t, get_type, task_type_t,
private_isakmp_natd_t *this)
{
return TASK_ISAKMP_NATD;
}
METHOD(task_t, migrate, void,
private_isakmp_natd_t *this, ike_sa_t *ike_sa)
{
this->ike_sa = ike_sa;
this->src_seen = FALSE;
this->dst_seen = FALSE;
this->src_matched = FALSE;
this->dst_matched = FALSE;
}
METHOD(task_t, destroy, void,
private_isakmp_natd_t *this)
{
free(this);
}
/*
* Described in header.
*/
isakmp_natd_t *isakmp_natd_create(ike_sa_t *ike_sa, bool initiator)
{
private_isakmp_natd_t *this;
INIT(this,
.public = {
.task = {
.get_type = _get_type,
.migrate = _migrate,
.destroy = _destroy,
},
},
.ike_sa = ike_sa,
.keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa),
.initiator = initiator,
);
if (initiator)
{
this->public.task.build = _build_i;
this->public.task.process = _process_i;
}
else
{
this->public.task.build = _build_r;
this->public.task.process = _process_r;
}
return &this->public;
}
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2011 Tobias Brunner
* 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.
*/
/**
* @defgroup isakmp_natd isakmp_natd
* @{ @ingroup tasks
*/
#ifndef ISAKMP_NATD_H_
#define ISAKMP_NATD_H_
typedef struct isakmp_natd_t isakmp_natd_t;
#include <library.h>
#include <sa/ike_sa.h>
#include <sa/task.h>
/**
* Task of type ISAKMP_NATD, detects NAT situation in IKEv1 Phase 1.
*/
struct isakmp_natd_t {
/**
* Implements the task_t interface
*/
task_t task;
};
/**
* Create a new ISAKMP_NATD task.
*
* @param ike_sa IKE_SA this task works for
* @param initiator TRUE if task is the original initiator
* @return isakmp_natd task to handle by the task_manager
*/
isakmp_natd_t *isakmp_natd_create(ike_sa_t *ike_sa, bool initiator);
#endif /** ISAKMP_NATD_H_ @}*/
@@ -0,0 +1,204 @@
/*
* Copyright (C) 2009 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 "isakmp_vendor.h"
#include <daemon.h>
#include <encoding/payloads/vendor_id_payload.h>
typedef struct private_isakmp_vendor_t private_isakmp_vendor_t;
/**
* Private data of an isakmp_vendor_t object.
*/
struct private_isakmp_vendor_t {
/**
* Public isakmp_vendor_t interface.
*/
isakmp_vendor_t public;
/**
* Associated IKE_SA
*/
ike_sa_t *ike_sa;
/**
* Are we the inititator of this task
*/
bool initiator;
};
/**
* IKEv1 Vendor ID database
*/
static struct {
/* Description */
char *desc;
/* extension flag negotiated with vendor ID, if any */
ike_extension_t extension;
/* send yourself? */
bool send;
/* length of vendor ID string */
int len;
/* vendor ID string */
char *id;
} vendor_ids[] = {
/* strongSwan MD5("strongSwan") */
{ "strongSwan", EXT_STRONGSWAN, FALSE, 16,
"\x88\x2f\xe5\x6d\x6f\xd2\x0d\xbc\x22\x51\x61\x3b\x2e\xbe\x5b\xeb"},
/* XAuth, MD5("draft-ietf-ipsra-isakmp-xauth-06.txt") */
{ "XAuth", EXT_XAUTH, TRUE, 8,
"\x09\x00\x26\x89\xdf\xd6\xb7\x12"},
/* NAT-Traversal, MD5("RFC 3947") */
{ "NAT-T (RFC 3947)", EXT_NATT, TRUE, 16,
"\x4a\x13\x1c\x81\x07\x03\x58\x45\x5c\x57\x28\xf2\x0e\x95\x45\x2f"},
/* draft-ietf-ipsec-dpd-00 */
{ "DPD", 0, FALSE, 16,
"\xaf\xca\xd7\x13\x68\xa1\xf1\xc9\x6b\x86\x96\xfc\x77\x57\x01\x00"},
{ "draft-stenberg-ipsec-nat-traversal-01", 0, FALSE, 16,
"\x27\xba\xb5\xdc\x01\xea\x07\x60\xea\x4e\x31\x90\xac\x27\xc0\xd0"},
{ "draft-stenberg-ipsec-nat-traversal-02", 0, FALSE, 16,
"\x61\x05\xc4\x22\xe7\x68\x47\xe4\x3f\x96\x84\x80\x12\x92\xae\xcd"},
{ "draft-ietf-ipsec-nat-t-ike-00", 0, FALSE, 16,
"\x44\x85\x15\x2d\x18\xb6\xbb\xcd\x0b\xe8\xa8\x46\x95\x79\xdd\xcc"},
{ "draft-ietf-ipsec-nat-t-ike-02", 0, FALSE, 16,
"\xcd\x60\x46\x43\x35\xdf\x21\xf8\x7c\xfd\xb2\xfc\x68\xb6\xa4\x48"},
{ "draft-ietf-ipsec-nat-t-ike-02", 0, FALSE, 16,
"\x90\xcb\x80\x91\x3e\xbb\x69\x6e\x08\x63\x81\xb5\xec\x42\x7b\x1f"},
{ "draft-ietf-ipsec-nat-t-ike-03", 0, FALSE, 16,
"\x7d\x94\x19\xa6\x53\x10\xca\x6f\x2c\x17\x9d\x92\x15\x52\x9d\x56"},
{ "Cisco Unity", 0, FALSE, 16,
"\x12\xf5\xf2\x8c\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00"},
};
METHOD(task_t, build, status_t,
private_isakmp_vendor_t *this, message_t *message)
{
vendor_id_payload_t *vid_payload;
bool strongswan;
int i;
strongswan = lib->settings->get_bool(lib->settings,
"charon.send_vendor_id", FALSE);
for (i = 0; i < countof(vendor_ids); i++)
{
if (vendor_ids[i].send ||
(vendor_ids[i].extension == EXT_STRONGSWAN && strongswan))
{
vid_payload = vendor_id_payload_create_data(VENDOR_ID_V1,
chunk_clone(chunk_create(vendor_ids[i].id, vendor_ids[i].len)));
message->add_payload(message, &vid_payload->payload_interface);
}
}
return this->initiator ? NEED_MORE : SUCCESS;
}
METHOD(task_t, process, status_t,
private_isakmp_vendor_t *this, message_t *message)
{
enumerator_t *enumerator;
payload_t *payload;
int i;
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
if (payload->get_type(payload) == VENDOR_ID_V1)
{
vendor_id_payload_t *vid;
bool found = FALSE;
chunk_t data;
vid = (vendor_id_payload_t*)payload;
data = vid->get_data(vid);
for (i = 0; i < countof(vendor_ids); i++)
{
if (chunk_equals(data, chunk_create(vendor_ids[i].id,
vendor_ids[i].len)))
{
DBG1(DBG_IKE, "received %s vendor id", vendor_ids[i].desc);
if (vendor_ids[i].extension)
{
this->ike_sa->enable_extension(this->ike_sa,
vendor_ids[i].extension);
}
found = TRUE;
}
}
if (!found)
{
DBG1(DBG_ENC, "received unknown vendor id: %#B", &data);
}
}
}
enumerator->destroy(enumerator);
return this->initiator ? SUCCESS : NEED_MORE;
}
METHOD(task_t, migrate, void,
private_isakmp_vendor_t *this, ike_sa_t *ike_sa)
{
this->ike_sa = ike_sa;
}
METHOD(task_t, get_type, task_type_t,
private_isakmp_vendor_t *this)
{
return TASK_ISAKMP_VENDOR;
}
METHOD(task_t, destroy, void,
private_isakmp_vendor_t *this)
{
free(this);
}
/**
* See header
*/
isakmp_vendor_t *isakmp_vendor_create(ike_sa_t *ike_sa, bool initiator)
{
private_isakmp_vendor_t *this;
INIT(this,
.public = {
.task = {
.build = _build,
.process = _process,
.migrate = _migrate,
.get_type = _get_type,
.destroy = _destroy,
},
},
.initiator = initiator,
.ike_sa = ike_sa,
);
return &this->public;
}
@@ -0,0 +1,49 @@
/*
* 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 <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.
*/
/**
* @defgroup isakmp_vendor isakmp_vendor
* @{ @ingroup tasks
*/
#ifndef ISAKMP_VENDOR_H_
#define ISAKMP_VENDOR_H_
typedef struct isakmp_vendor_t isakmp_vendor_t;
#include <library.h>
#include <sa/ike_sa.h>
#include <sa/task.h>
/**
* Vendor ID processing task for IKEv1.
*/
struct isakmp_vendor_t {
/**
* Implements task interface.
*/
task_t task;
};
/**
* Create a isakmp_vendor instance.
*
* @param ike_sa IKE_SA this task works for
* @param initiator TRUE if task is the original initiator
*/
isakmp_vendor_t *isakmp_vendor_create(ike_sa_t *ike_sa, bool initiator);
#endif /** ISAKMP_VENDOR_H_ @}*/
File diff suppressed because it is too large Load Diff
+50
View File
@@ -0,0 +1,50 @@
/*
* 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 <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.
*/
/**
* @defgroup main_mode main_mode
* @{ @ingroup tasks
*/
#ifndef MAIN_MODE_H_
#define MAIN_MODE_H_
typedef struct main_mode_t main_mode_t;
#include <library.h>
#include <sa/ike_sa.h>
#include <sa/task.h>
/**
* IKEv1 main mode, establishes a mainmode including authentication.
*/
struct main_mode_t {
/**
* Implements the task_t interface
*/
task_t task;
};
/**
* Create a new main_mode task.
*
* @param ike_sa IKE_SA this task works for
* @param initiator TRUE if task initiated locally
* @return task to handle by the task_manager
*/
main_mode_t *main_mode_create(ike_sa_t *ike_sa, bool initiator);
#endif /** MAIN_MODE_H_ @}*/
+417
View File
@@ -0,0 +1,417 @@
/*
* 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 <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 "mode_config.h"
#include <daemon.h>
#include <hydra.h>
#include <encoding/payloads/cp_payload.h>
typedef struct private_mode_config_t private_mode_config_t;
/**
* Private members of a mode_config_t task.
*/
struct private_mode_config_t {
/**
* Public methods and task_t interface.
*/
mode_config_t public;
/**
* Assigned IKE_SA.
*/
ike_sa_t *ike_sa;
/**
* Are we the initiator?
*/
bool initiator;
/**
* virtual ip
*/
host_t *virtual_ip;
/**
* list of attributes requested and its handler, entry_t
*/
linked_list_t *requested;
};
/**
* Entry for a requested attribute and the requesting handler
*/
typedef struct {
/** attribute requested */
configuration_attribute_type_t type;
/** handler requesting this attribute */
attribute_handler_t *handler;
} entry_t;
/**
* build INTERNAL_IPV4/6_ADDRESS attribute from virtual ip
*/
static configuration_attribute_t *build_vip(host_t *vip)
{
configuration_attribute_type_t type;
chunk_t chunk, prefix;
if (vip->get_family(vip) == AF_INET)
{
type = INTERNAL_IP4_ADDRESS;
if (vip->is_anyaddr(vip))
{
chunk = chunk_empty;
}
else
{
chunk = vip->get_address(vip);
}
}
else
{
type = INTERNAL_IP6_ADDRESS;
if (vip->is_anyaddr(vip))
{
chunk = chunk_empty;
}
else
{
prefix = chunk_alloca(1);
*prefix.ptr = 64;
chunk = vip->get_address(vip);
chunk = chunk_cata("cc", chunk, prefix);
}
}
return configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
type, chunk);
}
/**
* Handle a received attribute as initiator
*/
static void handle_attribute(private_mode_config_t *this,
configuration_attribute_t *ca)
{
attribute_handler_t *handler = NULL;
enumerator_t *enumerator;
entry_t *entry;
/* find the handler which requested this attribute */
enumerator = this->requested->create_enumerator(this->requested);
while (enumerator->enumerate(enumerator, &entry))
{
if (entry->type == ca->get_type(ca))
{
handler = entry->handler;
this->requested->remove_at(this->requested, enumerator);
free(entry);
break;
}
}
enumerator->destroy(enumerator);
/* and pass it to the handle function */
handler = hydra->attributes->handle(hydra->attributes,
this->ike_sa->get_other_id(this->ike_sa), handler,
ca->get_type(ca), ca->get_chunk(ca));
if (handler)
{
this->ike_sa->add_configuration_attribute(this->ike_sa,
handler, ca->get_type(ca), ca->get_chunk(ca));
}
}
/**
* process a single configuration attribute
*/
static void process_attribute(private_mode_config_t *this,
configuration_attribute_t *ca)
{
host_t *ip;
chunk_t addr;
int family = AF_INET6;
switch (ca->get_type(ca))
{
case INTERNAL_IP4_ADDRESS:
family = AF_INET;
/* fall */
case INTERNAL_IP6_ADDRESS:
{
addr = ca->get_chunk(ca);
if (addr.len == 0)
{
ip = host_create_any(family);
}
else
{
/* skip prefix byte in IPv6 payload*/
if (family == AF_INET6)
{
addr.len--;
}
ip = host_create_from_chunk(family, addr, 0);
}
if (ip)
{
DESTROY_IF(this->virtual_ip);
this->virtual_ip = ip;
}
break;
}
default:
{
if (this->initiator)
{
handle_attribute(this, ca);
}
}
}
}
/**
* Scan for configuration payloads and attributes
*/
static void process_payloads(private_mode_config_t *this, message_t *message)
{
enumerator_t *enumerator, *attributes;
payload_t *payload;
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
if (payload->get_type(payload) == CONFIGURATION_V1)
{
cp_payload_t *cp = (cp_payload_t*)payload;
configuration_attribute_t *ca;
switch (cp->get_type(cp))
{
case CFG_REQUEST:
case CFG_REPLY:
attributes = cp->create_attribute_enumerator(cp);
while (attributes->enumerate(attributes, &ca))
{
DBG2(DBG_IKE, "processing %N attribute",
configuration_attribute_type_names, ca->get_type(ca));
process_attribute(this, ca);
}
attributes->destroy(attributes);
break;
default:
DBG1(DBG_IKE, "ignoring %N config payload",
config_type_names, cp->get_type(cp));
break;
}
}
}
enumerator->destroy(enumerator);
}
METHOD(task_t, build_i, status_t,
private_mode_config_t *this, message_t *message)
{
cp_payload_t *cp = NULL;
enumerator_t *enumerator;
attribute_handler_t *handler;
peer_cfg_t *config;
configuration_attribute_type_t type;
chunk_t data;
host_t *vip;
/* reuse virtual IP if we already have one */
vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
if (!vip)
{
config = this->ike_sa->get_peer_cfg(this->ike_sa);
vip = config->get_virtual_ip(config);
}
if (vip)
{
cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST);
cp->add_attribute(cp, build_vip(vip));
}
enumerator = hydra->attributes->create_initiator_enumerator(hydra->attributes,
this->ike_sa->get_other_id(this->ike_sa), vip);
while (enumerator->enumerate(enumerator, &handler, &type, &data))
{
configuration_attribute_t *ca;
entry_t *entry;
DBG2(DBG_IKE, "building %N attribute",
configuration_attribute_type_names, type);
ca = configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
type, data);
if (!cp)
{
cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST);
}
cp->add_attribute(cp, ca);
INIT(entry,
.type = type,
.handler = handler,
);
this->requested->insert_last(this->requested, entry);
}
enumerator->destroy(enumerator);
if (cp)
{
message->add_payload(message, (payload_t*)cp);
}
return NEED_MORE;
}
METHOD(task_t, process_r, status_t,
private_mode_config_t *this, message_t *message)
{
process_payloads(this, message);
return NEED_MORE;
}
METHOD(task_t, build_r, status_t,
private_mode_config_t *this, message_t *message)
{
enumerator_t *enumerator;
configuration_attribute_type_t type;
chunk_t value;
host_t *vip = NULL;
cp_payload_t *cp = NULL;
peer_cfg_t *config;
identification_t *id;
id = this->ike_sa->get_other_eap_id(this->ike_sa);
config = this->ike_sa->get_peer_cfg(this->ike_sa);
if (this->virtual_ip)
{
DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip);
if (config->get_pool(config))
{
vip = hydra->attributes->acquire_address(hydra->attributes,
config->get_pool(config), id, this->virtual_ip);
}
cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY);
if (vip)
{
DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", vip, id);
this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, vip);
cp->add_attribute(cp, build_vip(vip));
}
else
{
DBG1(DBG_IKE, "no virtual IP found, sending empty config payload");
}
}
/* query registered providers for additional attributes to include */
enumerator = hydra->attributes->create_responder_enumerator(
hydra->attributes, config->get_pool(config), id, vip);
while (enumerator->enumerate(enumerator, &type, &value))
{
if (!cp)
{
cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY);
}
DBG2(DBG_IKE, "building %N attribute",
configuration_attribute_type_names, type);
cp->add_attribute(cp,
configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
type, value));
}
enumerator->destroy(enumerator);
if (cp)
{
message->add_payload(message, (payload_t*)cp);
}
DESTROY_IF(vip);
return SUCCESS;
}
METHOD(task_t, process_i, status_t,
private_mode_config_t *this, message_t *message)
{
process_payloads(this, message);
if (this->virtual_ip)
{
this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip);
}
return SUCCESS;
}
METHOD(task_t, get_type, task_type_t,
private_mode_config_t *this)
{
return TASK_MODE_CONFIG;
}
METHOD(task_t, migrate, void,
private_mode_config_t *this, ike_sa_t *ike_sa)
{
DESTROY_IF(this->virtual_ip);
this->ike_sa = ike_sa;
this->virtual_ip = NULL;
this->requested->destroy_function(this->requested, free);
this->requested = linked_list_create();
}
METHOD(task_t, destroy, void,
private_mode_config_t *this)
{
DESTROY_IF(this->virtual_ip);
this->requested->destroy_function(this->requested, free);
free(this);
}
/*
* Described in header.
*/
mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator)
{
private_mode_config_t *this;
INIT(this,
.public = {
.task = {
.get_type = _get_type,
.migrate = _migrate,
.destroy = _destroy,
},
},
.initiator = initiator,
.ike_sa = ike_sa,
.requested = linked_list_create(),
);
if (initiator)
{
this->public.task.build = _build_i;
this->public.task.process = _process_i;
}
else
{
this->public.task.build = _build_r;
this->public.task.process = _process_r;
}
return &this->public;
}
@@ -0,0 +1,50 @@
/*
* 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 <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.
*/
/**
* @defgroup mode_config mode_config
* @{ @ingroup tasks
*/
#ifndef MODE_CONFIG_H_
#define MODE_CONFIG_H_
typedef struct mode_config_t mode_config_t;
#include <library.h>
#include <sa/ike_sa.h>
#include <sa/task.h>
/**
* Task of type TASK_MODE_COFNIG, IKEv1 configuration attribute exchange.
*/
struct mode_config_t {
/**
* Implements the task_t interface
*/
task_t task;
};
/**
* Create a new mode_config task.
*
* @param ike_sa IKE_SA this task works for
* @param initiator TRUE for initiator
* @return mode_config task to handle by the task_manager
*/
mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator);
#endif /** MODE_CONFIG_H_ @}*/
+222
View File
@@ -0,0 +1,222 @@
/*
* 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 <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 "quick_delete.h"
#include <daemon.h>
#include <encoding/payloads/delete_payload.h>
typedef struct private_quick_delete_t private_quick_delete_t;
/**
* Private members of a quick_delete_t task.
*/
struct private_quick_delete_t {
/**
* Public methods and task_t interface.
*/
quick_delete_t public;
/**
* Assigned IKE_SA.
*/
ike_sa_t *ike_sa;
/**
* Are we the initiator?
*/
bool initiator;
/**
* Protocol of CHILD_SA to delete
*/
protocol_id_t protocol;
/**
* Inbound SPI of CHILD_SA to delete
*/
u_int32_t spi;
/**
* Send delete even if SA does not exist
*/
bool force;
};
/**
* Delete the specified CHILD_SA, if found
*/
static bool delete_child(private_quick_delete_t *this,
protocol_id_t protocol, u_int32_t spi)
{
u_int64_t bytes_in, bytes_out;
child_sa_t *child_sa;
child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, TRUE);
if (!child_sa)
{ /* fallback and check for outbound SA */
child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, FALSE);
if (!child_sa)
{
return FALSE;
}
this->spi = spi = child_sa->get_spi(child_sa, TRUE);
}
child_sa->set_state(child_sa, CHILD_DELETING);
child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in);
child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out);
DBG0(DBG_IKE, "closing CHILD_SA %s{%d} "
"with SPIs %.8x_i (%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R",
child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in,
ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out,
child_sa->get_traffic_selectors(child_sa, TRUE),
child_sa->get_traffic_selectors(child_sa, FALSE));
charon->bus->child_updown(charon->bus, child_sa, FALSE);
this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
/* TODO-IKEv1: handle close action? */
return TRUE;
}
METHOD(task_t, build_i, status_t,
private_quick_delete_t *this, message_t *message)
{
if (delete_child(this, this->protocol, this->spi) || this->force)
{
delete_payload_t *delete_payload;
DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x",
protocol_id_names, this->protocol, ntohl(this->spi));
delete_payload = delete_payload_create(DELETE_V1, PROTO_ESP);
delete_payload->add_spi(delete_payload, this->spi);
message->add_payload(message, &delete_payload->payload_interface);
return SUCCESS;
}
return FAILED;
}
METHOD(task_t, process_i, status_t,
private_quick_delete_t *this, message_t *message)
{
return FAILED;
}
METHOD(task_t, process_r, status_t,
private_quick_delete_t *this, message_t *message)
{
enumerator_t *payloads, *spis;
payload_t *payload;
delete_payload_t *delete_payload;
protocol_id_t protocol;
u_int32_t spi;
payloads = message->create_payload_enumerator(message);
while (payloads->enumerate(payloads, &payload))
{
if (payload->get_type(payload) == DELETE_V1)
{
delete_payload = (delete_payload_t*)payload;
protocol = delete_payload->get_protocol_id(delete_payload);
if (protocol != PROTO_ESP && protocol != PROTO_AH)
{
continue;
}
spis = delete_payload->create_spi_enumerator(delete_payload);
while (spis->enumerate(spis, &spi))
{
DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x",
protocol_id_names, protocol, ntohl(spi));
if (!delete_child(this, protocol, spi))
{
DBG1(DBG_IKE, "CHILD_SA not found, ignored");
continue;
}
}
spis->destroy(spis);
}
}
payloads->destroy(payloads);
return SUCCESS;
}
METHOD(task_t, build_r, status_t,
private_quick_delete_t *this, message_t *message)
{
return FAILED;
}
METHOD(task_t, get_type, task_type_t,
private_quick_delete_t *this)
{
return TASK_QUICK_DELETE;
}
METHOD(task_t, migrate, void,
private_quick_delete_t *this, ike_sa_t *ike_sa)
{
this->ike_sa = ike_sa;
}
METHOD(task_t, destroy, void,
private_quick_delete_t *this)
{
free(this);
}
/*
* Described in header.
*/
quick_delete_t *quick_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
u_int32_t spi, bool force)
{
private_quick_delete_t *this;
INIT(this,
.public = {
.task = {
.get_type = _get_type,
.migrate = _migrate,
.destroy = _destroy,
},
},
.ike_sa = ike_sa,
.protocol = protocol,
.spi = spi,
.force = force,
);
if (protocol != PROTO_NONE)
{
this->public.task.build = _build_i;
this->public.task.process = _process_i;
}
else
{
this->public.task.build = _build_r;
this->public.task.process = _process_r;
}
return &this->public;
}
@@ -0,0 +1,54 @@
/*
* 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 <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.
*/
/**
* @defgroup quick_delete quick_delete
* @{ @ingroup tasks
*/
#ifndef QUICK_DELETE_H_
#define QUICK_DELETE_H_
typedef struct quick_delete_t quick_delete_t;
#include <library.h>
#include <sa/ike_sa.h>
#include <sa/task.h>
#include <sa/child_sa.h>
/**
* Task of type QUICK_DELETE, delete an IKEv1 quick mode SA.
*/
struct quick_delete_t {
/**
* Implements the task_t interface
*/
task_t task;
};
/**
* Create a new quick_delete task.
*
* @param ike_sa IKE_SA this task works for
* @param protocol protocol of CHILD_SA to delete, PROTO_NONE as responder
* @param spi inbound SPI of CHILD_SA to delete
* @param force send delete even if SA does not exist
* @return quick_delete task to handle by the task_manager
*/
quick_delete_t *quick_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
u_int32_t spi, bool force);
#endif /** QUICK_DELETE_H_ @}*/
+947
View File
@@ -0,0 +1,947 @@
/*
* 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 <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 "quick_mode.h"
#include <string.h>
#include <daemon.h>
#include <sa/ikev1/keymat_v1.h>
#include <encoding/payloads/sa_payload.h>
#include <encoding/payloads/nonce_payload.h>
#include <encoding/payloads/ke_payload.h>
#include <encoding/payloads/id_payload.h>
#include <encoding/payloads/payload.h>
#include <sa/ikev1/tasks/informational.h>
#include <sa/ikev1/tasks/quick_delete.h>
typedef struct private_quick_mode_t private_quick_mode_t;
/**
* Private members of a quick_mode_t task.
*/
struct private_quick_mode_t {
/**
* Public methods and task_t interface.
*/
quick_mode_t public;
/**
* Assigned IKE_SA.
*/
ike_sa_t *ike_sa;
/**
* TRUE if we are initiating quick mode
*/
bool initiator;
/**
* Traffic selector of initiator
*/
traffic_selector_t *tsi;
/**
* Traffic selector of responder
*/
traffic_selector_t *tsr;
/**
* Initiators nonce
*/
chunk_t nonce_i;
/**
* Responder nonce
*/
chunk_t nonce_r;
/**
* Initiators ESP SPI
*/
u_int32_t spi_i;
/**
* Responder ESP SPI
*/
u_int32_t spi_r;
/**
* selected CHILD_SA proposal
*/
proposal_t *proposal;
/**
* Config of CHILD_SA to establish
*/
child_cfg_t *config;
/**
* CHILD_SA we are about to establish
*/
child_sa_t *child_sa;
/**
* IKEv1 keymat
*/
keymat_v1_t *keymat;
/**
* DH exchange, when PFS is in use
*/
diffie_hellman_t *dh;
/**
* Negotiated lifetime of new SA
*/
u_int32_t lifetime;
/**
* Negotaited lifebytes of new SA
*/
u_int64_t lifebytes;
/**
* Notify type in case of error
*/
notify_type_t notify_type;
/** states of quick mode */
enum {
QM_INIT,
QM_NEGOTIATED,
} state;
};
/**
* Install negotiated CHILD_SA
*/
static bool install(private_quick_mode_t *this)
{
status_t status, status_i, status_o;
chunk_t encr_i, encr_r, integ_i, integ_r;
linked_list_t *tsi, *tsr;
this->child_sa->set_proposal(this->child_sa, this->proposal);
this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
this->child_sa->set_mode(this->child_sa, MODE_TUNNEL);
this->child_sa->set_protocol(this->child_sa,
this->proposal->get_protocol(this->proposal));
status_i = status_o = FAILED;
encr_i = encr_r = integ_i = integ_r = chunk_empty;
tsi = linked_list_create();
tsr = linked_list_create();
tsi->insert_last(tsi, this->tsi);
tsr->insert_last(tsr, this->tsr);
if (this->keymat->derive_child_keys(this->keymat, this->proposal, this->dh,
this->spi_i, this->spi_r, this->nonce_i, this->nonce_r,
&encr_i, &integ_i, &encr_r, &integ_r))
{
if (this->initiator)
{
status_i = this->child_sa->install(this->child_sa, encr_r, integ_r,
this->spi_i, 0, TRUE, FALSE, tsi, tsr);
status_o = this->child_sa->install(this->child_sa, encr_i, integ_i,
this->spi_r, 0, FALSE, FALSE, tsi, tsr);
}
else
{
status_i = this->child_sa->install(this->child_sa, encr_i, integ_i,
this->spi_r, 0, TRUE, FALSE, tsr, tsi);
status_o = this->child_sa->install(this->child_sa, encr_r, integ_r,
this->spi_i, 0, FALSE, FALSE, tsr, tsi);
}
}
chunk_clear(&integ_i);
chunk_clear(&integ_r);
chunk_clear(&encr_i);
chunk_clear(&encr_r);
if (status_i != SUCCESS || status_o != SUCCESS)
{
DBG1(DBG_IKE, "unable to install %s%s%sIPsec SA (SAD) in kernel",
(status_i != SUCCESS) ? "inbound " : "",
(status_i != SUCCESS && status_o != SUCCESS) ? "and ": "",
(status_o != SUCCESS) ? "outbound " : "");
tsi->destroy(tsi);
tsr->destroy(tsr);
return FALSE;
}
if (this->initiator)
{
status = this->child_sa->add_policies(this->child_sa, tsi, tsr);
}
else
{
status = this->child_sa->add_policies(this->child_sa, tsr, tsi);
}
tsi->destroy(tsi);
tsr->destroy(tsr);
if (status != SUCCESS)
{
DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
return FALSE;
}
charon->bus->child_keys(charon->bus, this->child_sa, this->initiator,
this->dh, this->nonce_i, this->nonce_r);
/* add to IKE_SA, and remove from task */
this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
"with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
this->child_sa->get_name(this->child_sa),
this->child_sa->get_reqid(this->child_sa),
ntohl(this->child_sa->get_spi(this->child_sa, TRUE)),
ntohl(this->child_sa->get_spi(this->child_sa, FALSE)),
this->child_sa->get_traffic_selectors(this->child_sa, TRUE),
this->child_sa->get_traffic_selectors(this->child_sa, FALSE));
charon->bus->child_updown(charon->bus, this->child_sa, TRUE);
this->child_sa = NULL;
return TRUE;
}
/**
* Generate and add NONCE
*/
static bool add_nonce(private_quick_mode_t *this, chunk_t *nonce,
message_t *message)
{
nonce_payload_t *nonce_payload;
rng_t *rng;
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (!rng)
{
DBG1(DBG_IKE, "no RNG found to create nonce");
return FALSE;
}
rng->allocate_bytes(rng, NONCE_SIZE, nonce);
rng->destroy(rng);
nonce_payload = nonce_payload_create(NONCE_V1);
nonce_payload->set_nonce(nonce_payload, *nonce);
message->add_payload(message, &nonce_payload->payload_interface);
return TRUE;
}
/**
* Extract nonce from NONCE payload
*/
static bool get_nonce(private_quick_mode_t *this, chunk_t *nonce,
message_t *message)
{
nonce_payload_t *nonce_payload;
nonce_payload = (nonce_payload_t*)message->get_payload(message, NONCE_V1);
if (!nonce_payload)
{
DBG1(DBG_IKE, "NONCE payload missing in message");
return FALSE;
}
*nonce = nonce_payload->get_nonce(nonce_payload);
return TRUE;
}
/**
* Add KE payload to message
*/
static void add_ke(private_quick_mode_t *this, message_t *message)
{
ke_payload_t *ke_payload;
ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1, this->dh);
message->add_payload(message, &ke_payload->payload_interface);
}
/**
* Get DH value from a KE payload
*/
static bool get_ke(private_quick_mode_t *this, message_t *message)
{
ke_payload_t *ke_payload;
ke_payload = (ke_payload_t*)message->get_payload(message, KEY_EXCHANGE_V1);
if (!ke_payload)
{
DBG1(DBG_IKE, "KE payload missing");
return FALSE;
}
this->dh->set_other_public_value(this->dh,
ke_payload->get_key_exchange_data(ke_payload));
return TRUE;
}
/**
* Select a traffic selector from configuration
*/
static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local,
linked_list_t *supplied)
{
traffic_selector_t *ts;
linked_list_t *list;
host_t *host;
host = this->ike_sa->get_virtual_ip(this->ike_sa, local);
if (!host)
{
if (local)
{
host = this->ike_sa->get_my_host(this->ike_sa);
}
else
{
host = this->ike_sa->get_other_host(this->ike_sa);
}
}
list = this->config->get_traffic_selectors(this->config, local,
supplied, host);
if (list->get_first(list, (void**)&ts) == SUCCESS)
{
if (list->get_count(list) > 1)
{
DBG1(DBG_IKE, "configuration has more than one %s traffic selector,"
" using first only", local ? "local" : "remote");
}
ts = ts->clone(ts);
}
else
{
DBG1(DBG_IKE, "%s traffic selector missing in configuration",
local ? "local" : "local");
ts = NULL;
}
list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
return ts;
}
/**
* Add selected traffic selectors to message
*/
static void add_ts(private_quick_mode_t *this, message_t *message)
{
id_payload_t *id_payload;
host_t *hsi, *hsr;
if (this->initiator)
{
hsi = this->ike_sa->get_my_host(this->ike_sa);
hsr = this->ike_sa->get_other_host(this->ike_sa);
}
else
{
hsr = this->ike_sa->get_my_host(this->ike_sa);
hsi = this->ike_sa->get_other_host(this->ike_sa);
}
/* add ID payload only if negotiating non host2host tunnels */
if (!this->tsi->is_host(this->tsi, hsi) ||
!this->tsr->is_host(this->tsr, hsr) ||
this->tsi->get_protocol(this->tsi) ||
this->tsr->get_protocol(this->tsr) ||
this->tsi->get_from_port(this->tsi) ||
this->tsr->get_from_port(this->tsr) ||
this->tsi->get_to_port(this->tsi) != 65535 ||
this->tsr->get_to_port(this->tsr) != 65535)
{
id_payload = id_payload_create_from_ts(this->tsi);
message->add_payload(message, &id_payload->payload_interface);
id_payload = id_payload_create_from_ts(this->tsr);
message->add_payload(message, &id_payload->payload_interface);
}
}
/**
* Get traffic selectors from received message
*/
static bool get_ts(private_quick_mode_t *this, message_t *message)
{
traffic_selector_t *tsi = NULL, *tsr = NULL;
enumerator_t *enumerator;
id_payload_t *id_payload;
payload_t *payload;
host_t *hsi, *hsr;
bool first = TRUE;
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
if (payload->get_type(payload) == ID_V1)
{
id_payload = (id_payload_t*)payload;
if (first)
{
tsi = id_payload->get_ts(id_payload);
first = FALSE;
}
else
{
tsr = id_payload->get_ts(id_payload);
break;
}
}
}
enumerator->destroy(enumerator);
/* create host2host selectors if ID payloads missing */
if (this->initiator)
{
hsi = this->ike_sa->get_my_host(this->ike_sa);
hsr = this->ike_sa->get_other_host(this->ike_sa);
}
else
{
hsr = this->ike_sa->get_my_host(this->ike_sa);
hsi = this->ike_sa->get_other_host(this->ike_sa);
}
if (!tsi)
{
tsi = traffic_selector_create_from_subnet(hsi->clone(hsi),
hsi->get_family(hsi) == AF_INET ? 32 : 128, 0, 0);
}
if (!tsr)
{
tsr = traffic_selector_create_from_subnet(hsr->clone(hsr),
hsr->get_family(hsr) == AF_INET ? 32 : 128, 0, 0);
}
if (this->initiator)
{
/* check if peer selection valid */
if (!tsr->is_contained_in(tsr, this->tsr) ||
!tsi->is_contained_in(tsi, this->tsi))
{
DBG1(DBG_IKE, "peer selected invalid traffic selectors: ",
"%R for %R, %R for %R", tsi, this->tsi, tsr, this->tsr);
tsi->destroy(tsi);
tsr->destroy(tsr);
return FALSE;
}
this->tsi->destroy(this->tsi);
this->tsr->destroy(this->tsr);
this->tsi = tsi;
this->tsr = tsr;
}
else
{
this->tsi = tsi;
this->tsr = tsr;
}
return TRUE;
}
/**
* Add NAT-OA payloads
*/
static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message)
{
identification_t *id;
id_payload_t *nat_oa;
host_t *src, *dst;
src = message->get_source(message);
dst = message->get_destination(message);
src = this->initiator ? src : dst;
dst = this->initiator ? dst : src;
/* first NAT-OA is the initiator's address */
id = identification_create_from_sockaddr(src->get_sockaddr(src));
nat_oa = id_payload_create_from_identification(NAT_OA_V1, id);
message->add_payload(message, (payload_t*)nat_oa);
id->destroy(id);
/* second NAT-OA is that of the responder */
id = identification_create_from_sockaddr(dst->get_sockaddr(dst));
nat_oa = id_payload_create_from_identification(NAT_OA_V1, id);
message->add_payload(message, (payload_t*)nat_oa);
id->destroy(id);
}
/**
* Look up lifetimes
*/
static void get_lifetimes(private_quick_mode_t *this)
{
lifetime_cfg_t *lft;
lft = this->config->get_lifetime(this->config);
if (lft->time.life)
{
this->lifetime = lft->time.life;
}
else if (lft->bytes.life)
{
this->lifebytes = lft->bytes.life;
}
free(lft);
}
/**
* Check and apply lifetimes
*/
static void apply_lifetimes(private_quick_mode_t *this, sa_payload_t *sa_payload)
{
u_int32_t lifetime;
u_int64_t lifebytes;
lifetime = sa_payload->get_lifetime(sa_payload);
lifebytes = sa_payload->get_lifebytes(sa_payload);
if (this->lifetime != lifetime)
{
DBG1(DBG_IKE, "received %us lifetime, configured %us, using lower",
lifetime, this->lifetime);
this->lifetime = min(this->lifetime, lifetime);
}
if (this->lifebytes != lifebytes)
{
DBG1(DBG_IKE, "received %llu lifebytes, configured %llu, using lower",
lifebytes, this->lifebytes);
this->lifebytes = min(this->lifebytes, lifebytes);
}
}
/**
* Set the task ready to build notify error message
*/
static status_t send_notify(private_quick_mode_t *this, notify_type_t type)
{
notify_payload_t *notify;
notify = notify_payload_create_from_protocol_and_type(NOTIFY_V1,
PROTO_ESP, type);
notify->set_spi(notify, this->spi_i);
this->ike_sa->queue_task(this->ike_sa,
(task_t*)informational_create(this->ike_sa, notify));
/* cancel all active/passive tasks in favour of informational */
return ALREADY_DONE;
}
METHOD(task_t, build_i, status_t,
private_quick_mode_t *this, message_t *message)
{
switch (this->state)
{
case QM_INIT:
{
enumerator_t *enumerator;
sa_payload_t *sa_payload;
linked_list_t *list;
proposal_t *proposal;
ipsec_mode_t mode;
diffie_hellman_group_t group;
bool udp = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
this->child_sa = child_sa_create(
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa),
this->config, 0, udp);
list = this->config->get_proposals(this->config, FALSE);
this->spi_i = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
if (!this->spi_i)
{
DBG1(DBG_IKE, "allocating SPI from kernel failed");
return FAILED;
}
enumerator = list->create_enumerator(list);
while (enumerator->enumerate(enumerator, &proposal))
{
proposal->set_spi(proposal, this->spi_i);
}
enumerator->destroy(enumerator);
mode = this->config->get_mode(this->config);
if (udp && mode == MODE_TRANSPORT)
{
/* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
add_nat_oa_payloads(this, message);
}
get_lifetimes(this);
sa_payload = sa_payload_create_from_proposals_v1(list,
this->lifetime, this->lifebytes, AUTH_NONE,
mode, udp);
list->destroy_offset(list, offsetof(proposal_t, destroy));
message->add_payload(message, &sa_payload->payload_interface);
if (!add_nonce(this, &this->nonce_i, message))
{
return FAILED;
}
group = this->config->get_dh_group(this->config);
if (group != MODP_NONE)
{
this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
group);
if (!this->dh)
{
DBG1(DBG_IKE, "configured DH group %N not supported",
diffie_hellman_group_names, group);
return FAILED;
}
add_ke(this, message);
}
this->tsi = select_ts(this, TRUE, NULL);
this->tsr = select_ts(this, FALSE, NULL);
if (!this->tsi || !this->tsr)
{
return FAILED;
}
add_ts(this, message);
return NEED_MORE;
}
case QM_NEGOTIATED:
{
return SUCCESS;
}
default:
return FAILED;
}
}
/**
* Check for notify errors, return TRUE if error found
*/
static bool has_notify_errors(private_quick_mode_t *this, message_t *message)
{
enumerator_t *enumerator;
payload_t *payload;
bool err = FALSE;
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
if (payload->get_type(payload) == NOTIFY_V1)
{
notify_payload_t *notify;
notify_type_t type;
notify = (notify_payload_t*)payload;
type = notify->get_notify_type(notify);
if (type < 16384)
{
DBG1(DBG_IKE, "received %N error notify",
notify_type_names, type);
err = TRUE;
}
else
{
DBG1(DBG_IKE, "received %N notify", notify_type_names, type);
}
}
}
enumerator->destroy(enumerator);
return err;
}
METHOD(task_t, process_r, status_t,
private_quick_mode_t *this, message_t *message)
{
switch (this->state)
{
case QM_INIT:
{
sa_payload_t *sa_payload;
linked_list_t *tsi, *tsr, *list;
peer_cfg_t *peer_cfg;
host_t *me, *other;
u_int16_t group;
bool udp = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
if (!get_ts(this, message))
{
return FAILED;
}
me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
if (!me)
{
me = this->ike_sa->get_my_host(this->ike_sa);
}
other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
if (!other)
{
other = this->ike_sa->get_other_host(this->ike_sa);
}
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
tsi = linked_list_create();
tsr = linked_list_create();
tsi->insert_last(tsi, this->tsi);
tsr->insert_last(tsr, this->tsr);
this->tsi = this->tsr = NULL;
this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi,
me, other);
this->tsi = select_ts(this, FALSE, tsi);
this->tsr = select_ts(this, TRUE, tsr);
tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
if (!this->config)
{
DBG1(DBG_IKE, "no child config found");
return send_notify(this, NO_PROPOSAL_CHOSEN);
}
sa_payload = (sa_payload_t*)message->get_payload(message,
SECURITY_ASSOCIATION_V1);
if (!sa_payload)
{
DBG1(DBG_IKE, "sa payload missing");
return send_notify(this, INVALID_PAYLOAD_TYPE);
}
list = sa_payload->get_proposals(sa_payload);
this->proposal = this->config->select_proposal(this->config,
list, FALSE, FALSE);
list->destroy_offset(list, offsetof(proposal_t, destroy));
get_lifetimes(this);
apply_lifetimes(this, sa_payload);
if (!this->proposal)
{
DBG1(DBG_IKE, "no matching proposal found, sending %N",
notify_type_names, NO_PROPOSAL_CHOSEN);
return send_notify(this, NO_PROPOSAL_CHOSEN);
}
this->spi_i = this->proposal->get_spi(this->proposal);
if (!get_nonce(this, &this->nonce_i, message))
{
return send_notify(this, INVALID_PAYLOAD_TYPE);
}
if (this->proposal->get_algorithm(this->proposal,
DIFFIE_HELLMAN_GROUP, &group, NULL))
{
this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
group);
if (!this->dh)
{
DBG1(DBG_IKE, "negotiated DH group %N not supported",
diffie_hellman_group_names, group);
return send_notify(this, INVALID_KEY_INFORMATION);
}
if (!get_ke(this, message))
{
return send_notify(this, INVALID_PAYLOAD_TYPE);
}
}
this->child_sa = child_sa_create(
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa),
this->config, 0, udp);
return NEED_MORE;
}
case QM_NEGOTIATED:
{
if (has_notify_errors(this, message))
{
return SUCCESS;
}
if (!install(this))
{
this->ike_sa->queue_task(this->ike_sa,
(task_t*)quick_delete_create(this->ike_sa,
this->proposal->get_protocol(this->proposal),
this->spi_i, TRUE));
return ALREADY_DONE;
}
return SUCCESS;
}
default:
return FAILED;
}
}
METHOD(task_t, build_r, status_t,
private_quick_mode_t *this, message_t *message)
{
switch (this->state)
{
case QM_INIT:
{
sa_payload_t *sa_payload;
ipsec_mode_t mode;
bool udp = this->child_sa->has_encap(this->child_sa);
this->spi_r = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
if (!this->spi_r)
{
DBG1(DBG_IKE, "allocating SPI from kernel failed");
return send_notify(this, NO_PROPOSAL_CHOSEN);
}
this->proposal->set_spi(this->proposal, this->spi_r);
mode = this->config->get_mode(this->config);
if (udp && mode == MODE_TRANSPORT)
{
/* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
add_nat_oa_payloads(this, message);
}
sa_payload = sa_payload_create_from_proposal_v1(this->proposal,
this->lifetime, this->lifebytes, AUTH_NONE,
mode, udp);
message->add_payload(message, &sa_payload->payload_interface);
if (!add_nonce(this, &this->nonce_r, message))
{
return FAILED;
}
if (this->dh)
{
add_ke(this, message);
}
add_ts(this, message);
this->state = QM_NEGOTIATED;
return NEED_MORE;
}
default:
return FAILED;
}
}
METHOD(task_t, process_i, status_t,
private_quick_mode_t *this, message_t *message)
{
switch (this->state)
{
case QM_INIT:
{
sa_payload_t *sa_payload;
linked_list_t *list;
sa_payload = (sa_payload_t*)message->get_payload(message,
SECURITY_ASSOCIATION_V1);
if (!sa_payload)
{
DBG1(DBG_IKE, "sa payload missing");
return send_notify(this, NO_PROPOSAL_CHOSEN);
}
list = sa_payload->get_proposals(sa_payload);
this->proposal = this->config->select_proposal(this->config,
list, FALSE, FALSE);
list->destroy_offset(list, offsetof(proposal_t, destroy));
if (!this->proposal)
{
DBG1(DBG_IKE, "no matching proposal found");
return send_notify(this, NO_PROPOSAL_CHOSEN);
}
this->spi_r = this->proposal->get_spi(this->proposal);
apply_lifetimes(this, sa_payload);
if (!get_nonce(this, &this->nonce_r, message))
{
return send_notify(this, INVALID_PAYLOAD_TYPE);
}
if (this->dh && !get_ke(this, message))
{
return send_notify(this, INVALID_KEY_INFORMATION);
}
if (!get_ts(this, message))
{
return send_notify(this, INVALID_PAYLOAD_TYPE);
}
if (!install(this))
{
return send_notify(this, NO_PROPOSAL_CHOSEN);
}
this->state = QM_NEGOTIATED;
return NEED_MORE;
}
default:
return FAILED;
}
}
METHOD(task_t, get_type, task_type_t,
private_quick_mode_t *this)
{
return TASK_QUICK_MODE;
}
METHOD(task_t, migrate, void,
private_quick_mode_t *this, ike_sa_t *ike_sa)
{
this->ike_sa = ike_sa;
}
METHOD(task_t, destroy, void,
private_quick_mode_t *this)
{
chunk_free(&this->nonce_i);
chunk_free(&this->nonce_r);
DESTROY_IF(this->tsi);
DESTROY_IF(this->tsr);
DESTROY_IF(this->proposal);
DESTROY_IF(this->child_sa);
DESTROY_IF(this->config);
DESTROY_IF(this->dh);
free(this);
}
/*
* Described in header.
*/
quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
traffic_selector_t *tsi, traffic_selector_t *tsr)
{
private_quick_mode_t *this;
INIT(this,
.public = {
.task = {
.get_type = _get_type,
.migrate = _migrate,
.destroy = _destroy,
},
},
.ike_sa = ike_sa,
.initiator = config != NULL,
.config = config,
.keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa),
.state = QM_INIT,
);
if (config)
{
this->public.task.build = _build_i;
this->public.task.process = _process_i;
}
else
{
this->public.task.build = _build_r;
this->public.task.process = _process_r;
}
return &this->public;
}
+53
View File
@@ -0,0 +1,53 @@
/*
* 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 <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.
*/
/**
* @defgroup quick_mode quick_mode
* @{ @ingroup tasks
*/
#ifndef QUICK_MODE_H_
#define QUICK_MODE_H_
typedef struct quick_mode_t quick_mode_t;
#include <library.h>
#include <sa/ike_sa.h>
#include <sa/task.h>
/**
* IKEv1 quick mode, establishes a CHILD_SA in IKEv1.
*/
struct quick_mode_t {
/**
* Implements the task_t interface
*/
task_t task;
};
/**
* Create a new quick_mode task.
*
* @param ike_sa IKE_SA this task works for
* @param config child_cfg if task initiator, NULL if responder
* @param tsi source of triggering packet, or NULL
* @param tsr destination of triggering packet, or NULL
* @return task to handle by the task_manager
*/
quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
traffic_selector_t *tsi, traffic_selector_t *tsr);
#endif /** QUICK_MODE_H_ @}*/
+399
View File
@@ -0,0 +1,399 @@
/*
* 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 <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 "xauth.h"
#include <daemon.h>
#include <hydra.h>
#include <encoding/payloads/cp_payload.h>
typedef struct private_xauth_t private_xauth_t;
/**
* Status types exchanged
*/
typedef enum {
XAUTH_FAILED = 0,
XAUTH_OK = 1,
} xauth_status_t;
/**
* Private members of a xauth_t task.
*/
struct private_xauth_t {
/**
* Public methods and task_t interface.
*/
xauth_t public;
/**
* Assigned IKE_SA.
*/
ike_sa_t *ike_sa;
/**
* Are we the XAUTH initiator?
*/
bool initiator;
/**
* XAuth backend to use
*/
xauth_method_t *xauth;
/**
* XAuth username
*/
identification_t *user;
/**
* Generated configuration payload
*/
cp_payload_t *cp;
/**
* status of Xauth exchange
*/
xauth_status_t status;
};
/**
* Load XAuth backend
*/
static xauth_method_t *load_method(private_xauth_t* this)
{
identification_t *server, *peer;
enumerator_t *enumerator;
xauth_method_t *xauth;
xauth_role_t role;
peer_cfg_t *peer_cfg;
auth_cfg_t *auth;
char *name;
if (this->initiator)
{
server = this->ike_sa->get_my_id(this->ike_sa);
peer = this->ike_sa->get_other_id(this->ike_sa);
role = XAUTH_SERVER;
}
else
{
peer = this->ike_sa->get_my_id(this->ike_sa);
server = this->ike_sa->get_other_id(this->ike_sa);
role = XAUTH_PEER;
}
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, !this->initiator);
if (!enumerator->enumerate(enumerator, &auth) ||
(uintptr_t)auth->get(auth, AUTH_RULE_AUTH_CLASS) != AUTH_CLASS_XAUTH)
{
if (!enumerator->enumerate(enumerator, &auth) ||
(uintptr_t)auth->get(auth, AUTH_RULE_AUTH_CLASS) != AUTH_CLASS_XAUTH)
{
DBG1(DBG_CFG, "no XAuth authentication round found");
enumerator->destroy(enumerator);
return NULL;
}
}
name = auth->get(auth, AUTH_RULE_XAUTH_BACKEND);
this->user = auth->get(auth, AUTH_RULE_XAUTH_IDENTITY);
if (!this->initiator && this->user)
{ /* use XAUTH username, if configured */
peer = this->user;
}
enumerator->destroy(enumerator);
xauth = charon->xauth->create_instance(charon->xauth, name, role,
server, peer);
if (!xauth)
{
if (name)
{
DBG1(DBG_CFG, "no XAuth method found named '%s'");
}
else
{
DBG1(DBG_CFG, "no XAuth method found");
}
}
return xauth;
}
/**
* Set IKE_SA to established state
*/
static void establish(private_xauth_t *this)
{
DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
this->ike_sa->get_name(this->ike_sa),
this->ike_sa->get_unique_id(this->ike_sa),
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_my_id(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa),
this->ike_sa->get_other_id(this->ike_sa));
this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
}
METHOD(task_t, build_i_status, status_t,
private_xauth_t *this, message_t *message)
{
cp_payload_t *cp;
cp = cp_payload_create_type(CONFIGURATION_V1, CFG_SET);
cp->add_attribute(cp,
configuration_attribute_create_value(XAUTH_STATUS, this->status));
message->add_payload(message, (payload_t *)cp);
return NEED_MORE;
}
METHOD(task_t, build_i, status_t,
private_xauth_t *this, message_t *message)
{
if (!this->xauth)
{
cp_payload_t *cp;
this->xauth = load_method(this);
if (!this->xauth)
{
return FAILED;
}
if (this->xauth->initiate(this->xauth, &cp) != NEED_MORE)
{
return FAILED;
}
message->add_payload(message, (payload_t *)cp);
return NEED_MORE;
}
if (this->cp)
{ /* send previously generated payload */
message->add_payload(message, (payload_t *)this->cp);
this->cp = NULL;
return NEED_MORE;
}
return FAILED;
}
METHOD(task_t, build_r_ack, status_t,
private_xauth_t *this, message_t *message)
{
cp_payload_t *cp;
cp = cp_payload_create_type(CONFIGURATION_V1, CFG_ACK);
cp->add_attribute(cp,
configuration_attribute_create_chunk(
CONFIGURATION_ATTRIBUTE_V1, XAUTH_STATUS, chunk_empty));
message->add_payload(message, (payload_t *)cp);
if (this->status == XAUTH_OK)
{
establish(this);
return SUCCESS;
}
return FAILED;
}
METHOD(task_t, process_r, status_t,
private_xauth_t *this, message_t *message)
{
cp_payload_t *cp;
if (!this->xauth)
{
this->xauth = load_method(this);
if (!this->xauth)
{ /* send empty reply */
return NEED_MORE;
}
}
cp = (cp_payload_t*)message->get_payload(message, CONFIGURATION_V1);
if (!cp)
{
DBG1(DBG_IKE, "configuration payload missing in XAuth request");
return FAILED;
}
if (cp->get_type(cp) == CFG_REQUEST)
{
switch (this->xauth->process(this->xauth, cp, &this->cp))
{
case NEED_MORE:
return NEED_MORE;
case SUCCESS:
case FAILED:
default:
break;
}
this->cp = NULL;
return NEED_MORE;
}
if (cp->get_type(cp) == CFG_SET)
{
configuration_attribute_t *attribute;
enumerator_t *enumerator;
enumerator = cp->create_attribute_enumerator(cp);
while (enumerator->enumerate(enumerator, &attribute))
{
if (attribute->get_type(attribute) == XAUTH_STATUS)
{
this->status = attribute->get_value(attribute);
}
}
enumerator->destroy(enumerator);
if (this->status == XAUTH_OK)
{
DBG1(DBG_IKE, "XAuth authentication of '%Y' (myself) successful",
this->xauth->get_identity(this->xauth));
establish(this);
}
else
{
DBG1(DBG_IKE, "XAuth authentication of '%Y' (myself) failed",
this->xauth->get_identity(this->xauth));
}
}
this->public.task.build = _build_r_ack;
return NEED_MORE;
}
METHOD(task_t, build_r, status_t,
private_xauth_t *this, message_t *message)
{
if (!this->cp)
{ /* send empty reply if building data failed */
this->cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY);
}
message->add_payload(message, (payload_t *)this->cp);
this->cp = NULL;
return NEED_MORE;
}
METHOD(task_t, process_i_status, status_t,
private_xauth_t *this, message_t *message)
{
cp_payload_t *cp;
cp = (cp_payload_t*)message->get_payload(message, CONFIGURATION_V1);
if (!cp || cp->get_type(cp) != CFG_ACK)
{
DBG1(DBG_IKE, "received invalid XAUTH status response");
return FAILED;
}
if (this->status != XAUTH_OK)
{
DBG1(DBG_IKE, "destroying IKE_SA after failed XAuth authentication");
return FAILED;
}
establish(this);
return SUCCESS;
}
METHOD(task_t, process_i, status_t,
private_xauth_t *this, message_t *message)
{
identification_t *id;
cp_payload_t *cp;
cp = (cp_payload_t*)message->get_payload(message, CONFIGURATION_V1);
if (!cp)
{
DBG1(DBG_IKE, "configuration payload missing in XAuth response");
return FAILED;
}
switch (this->xauth->process(this->xauth, cp, &this->cp))
{
case NEED_MORE:
return NEED_MORE;
case SUCCESS:
id = this->xauth->get_identity(this->xauth);
if (this->user && !id->matches(id, this->user))
{
DBG1(DBG_IKE, "XAuth username '%Y' does not match to "
"configured username '%Y'", id, this->user);
break;
}
DBG1(DBG_IKE, "XAuth authentication of '%Y' successful", id);
this->status = XAUTH_OK;
break;
case FAILED:
DBG1(DBG_IKE, "XAuth authentication of '%Y' failed",
this->xauth->get_identity(this->xauth));
break;
default:
return FAILED;
}
this->public.task.build = _build_i_status;
this->public.task.process = _process_i_status;
return NEED_MORE;
}
METHOD(task_t, get_type, task_type_t,
private_xauth_t *this)
{
return TASK_XAUTH;
}
METHOD(task_t, migrate, void,
private_xauth_t *this, ike_sa_t *ike_sa)
{
this->ike_sa = ike_sa;
}
METHOD(task_t, destroy, void,
private_xauth_t *this)
{
DESTROY_IF(this->xauth);
DESTROY_IF(this->cp);
free(this);
}
/*
* Described in header.
*/
xauth_t *xauth_create(ike_sa_t *ike_sa, bool initiator)
{
private_xauth_t *this;
INIT(this,
.public = {
.task = {
.get_type = _get_type,
.migrate = _migrate,
.destroy = _destroy,
},
},
.initiator = initiator,
.ike_sa = ike_sa,
.status = XAUTH_FAILED,
);
if (initiator)
{
this->public.task.build = _build_i;
this->public.task.process = _process_i;
}
else
{
this->public.task.build = _build_r;
this->public.task.process = _process_r;
}
return &this->public;
}
+50
View File
@@ -0,0 +1,50 @@
/*
* 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 <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.
*/
/**
* @defgroup xauth xauth
* @{ @ingroup tasks
*/
#ifndef XAUTH_H_
#define XAUTH_H_
typedef struct xauth_t xauth_t;
#include <library.h>
#include <sa/ike_sa.h>
#include <sa/task.h>
/**
* Task of type TASK_XAUTH, additional authentication after main/aggressive mode.
*/
struct xauth_t {
/**
* Implements the task_t interface
*/
task_t task;
};
/**
* Create a new xauth task.
*
* @param ike_sa IKE_SA this task works for
* @param initiator TRUE for initiator
* @return xauth task to handle by the task_manager
*/
xauth_t *xauth_create(ike_sa_t *ike_sa, bool initiator);
#endif /** XAUTH_H_ @}*/