summaryrefslogtreecommitdiff
path: root/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c')
-rw-r--r--src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c314
1 files changed, 274 insertions, 40 deletions
diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
index 6fb14bc06..151b49718 100644
--- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
+++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Tobias Brunner
+ * Copyright (C) 2008-2015 Tobias Brunner
* Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -20,6 +20,9 @@
#include <daemon.h>
#include <encoding/payloads/auth_payload.h>
#include <sa/ikev2/keymat_v2.h>
+#include <asn1/asn1.h>
+#include <asn1/oid.h>
+#include <collections/array.h>
typedef struct private_pubkey_authenticator_t private_pubkey_authenticator_t;
@@ -52,83 +55,303 @@ struct private_pubkey_authenticator_t {
* Reserved bytes of ID payload
*/
char reserved[3];
+
+ /**
+ * Whether to store signature schemes on remote auth configs.
+ */
+ bool store_signature_scheme;
};
-METHOD(authenticator_t, build, status_t,
- private_pubkey_authenticator_t *this, message_t *message)
+/**
+ * Parse authentication data used for Signature Authentication as per RFC 7427
+ */
+static bool parse_signature_auth_data(chunk_t *auth_data, key_type_t *key_type,
+ signature_scheme_t *scheme)
{
- chunk_t octets = chunk_empty, auth_data;
- status_t status = FAILED;
- private_key_t *private;
- identification_t *id;
- auth_cfg_t *auth;
- auth_payload_t *auth_payload;
- auth_method_t auth_method;
+ u_int8_t len;
+ int oid;
+
+ if (!auth_data->len)
+ {
+ return FALSE;
+ }
+ len = auth_data->ptr[0];
+ *auth_data = chunk_skip(*auth_data, 1);
+ /* we currently don't support schemes that require parameters */
+ oid = asn1_parse_algorithmIdentifier(*auth_data, 1, NULL);
+ *scheme = signature_scheme_from_oid(oid);
+ if (*scheme == SIGN_UNKNOWN)
+ {
+ return FALSE;
+ }
+ *key_type = key_type_from_signature_scheme(*scheme);
+ *auth_data = chunk_skip(*auth_data, len);
+ return TRUE;
+}
+
+/**
+ * Build authentication data used for Signature Authentication as per RFC 7427
+ */
+static bool build_signature_auth_data(chunk_t *auth_data,
+ signature_scheme_t scheme)
+{
+ chunk_t data;
+ u_int8_t len;
+ int oid;
+
+ oid = signature_scheme_to_oid(scheme);
+ if (oid == OID_UNKNOWN)
+ {
+ return FALSE;
+ }
+ data = asn1_algorithmIdentifier(oid);
+ len = data.len;
+ *auth_data = chunk_cat("cmm", chunk_from_thing(len), data, *auth_data);
+ return TRUE;
+}
+
+/**
+ * Selects possible signature schemes based on our configuration, the other
+ * peer's capabilities and the private key
+ */
+static array_t *select_signature_schemes(keymat_v2_t *keymat,
+ auth_cfg_t *auth, private_key_t *private)
+{
+ enumerator_t *enumerator;
signature_scheme_t scheme;
+ uintptr_t config;
+ auth_rule_t rule;
+ key_type_t key_type;
+ bool have_config = FALSE;
+ array_t *selected;
+
+ selected = array_create(sizeof(signature_scheme_t), 0);
+ key_type = private->get_type(private);
+ enumerator = auth->create_enumerator(auth);
+ while (enumerator->enumerate(enumerator, &rule, &config))
+ {
+ if (rule != AUTH_RULE_SIGNATURE_SCHEME)
+ {
+ continue;
+ }
+ have_config = TRUE;
+ if (key_type == key_type_from_signature_scheme(config) &&
+ keymat->hash_algorithm_supported(keymat,
+ hasher_from_signature_scheme(config)))
+ {
+ scheme = config;
+ array_insert(selected, ARRAY_TAIL, &scheme);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (!have_config)
+ {
+ /* if no specific configuration, find schemes appropriate for the key
+ * and supported by the other peer */
+ enumerator = signature_schemes_for_key(key_type,
+ private->get_keysize(private));
+ while (enumerator->enumerate(enumerator, &scheme))
+ {
+ if (keymat->hash_algorithm_supported(keymat,
+ hasher_from_signature_scheme(scheme)))
+ {
+ array_insert(selected, ARRAY_TAIL, &scheme);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* for RSA we tried at least SHA-512, also try other schemes down to
+ * what we'd use with classic authentication */
+ if (key_type == KEY_RSA)
+ {
+ signature_scheme_t schemes[] = {
+ SIGN_RSA_EMSA_PKCS1_SHA384,
+ SIGN_RSA_EMSA_PKCS1_SHA256,
+ SIGN_RSA_EMSA_PKCS1_SHA1,
+ }, contained;
+ bool found;
+ int i, j;
+
+ for (i = 0; i < countof(schemes); i++)
+ {
+ scheme = schemes[i];
+ found = FALSE;
+ for (j = 0; j < array_count(selected); j++)
+ {
+ array_get(selected, j, &contained);
+ if (scheme == contained)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found && keymat->hash_algorithm_supported(keymat,
+ hasher_from_signature_scheme(scheme)))
+ {
+ array_insert(selected, ARRAY_TAIL, &scheme);
+ }
+ }
+ }
+ }
+ return selected;
+}
+
+/**
+ * Create a signature using RFC 7427 signature authentication
+ */
+static status_t sign_signature_auth(private_pubkey_authenticator_t *this,
+ auth_cfg_t *auth, private_key_t *private,
+ identification_t *id, chunk_t *auth_data)
+{
+ enumerator_t *enumerator;
keymat_v2_t *keymat;
+ signature_scheme_t scheme = SIGN_UNKNOWN, *schemep;
+ array_t *schemes;
+ chunk_t octets = chunk_empty;
+ status_t status = FAILED;
- id = this->ike_sa->get_my_id(this->ike_sa);
- auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
- private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, auth);
- if (private == NULL)
+ keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
+ schemes = select_signature_schemes(keymat, auth, private);
+ if (!array_count(schemes))
{
- DBG1(DBG_IKE, "no private key found for '%Y'", id);
- return NOT_FOUND;
+ DBG1(DBG_IKE, "no common hash algorithm found to create signature "
+ "with %N key", key_type_names, private->get_type(private));
+ array_destroy(schemes);
+ return FAILED;
}
+ if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init,
+ this->nonce, id, this->reserved, &octets))
+ {
+ enumerator = array_create_enumerator(schemes);
+ while (enumerator->enumerate(enumerator, &schemep))
+ {
+ scheme = *schemep;
+ if (private->sign(private, scheme, octets, auth_data) &&
+ build_signature_auth_data(auth_data, scheme))
+ {
+ status = SUCCESS;
+ break;
+ }
+ else
+ {
+ DBG2(DBG_IKE, "unable to create %N signature for %N key",
+ signature_scheme_names, scheme, key_type_names,
+ private->get_type(private));
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N %s", id,
+ signature_scheme_names, scheme,
+ status == SUCCESS ? "successful" : "failed");
+ array_destroy(schemes);
+ chunk_free(&octets);
+ return status;
+}
+
+/**
+ * Create a classic IKEv2 signature
+ */
+static status_t sign_classic(private_pubkey_authenticator_t *this,
+ auth_cfg_t *auth, private_key_t *private,
+ identification_t *id, auth_method_t *auth_method,
+ chunk_t *auth_data)
+{
+ signature_scheme_t scheme;
+ keymat_v2_t *keymat;
+ chunk_t octets = chunk_empty;
+ status_t status = FAILED;
+
switch (private->get_type(private))
{
case KEY_RSA:
- /* we currently use always SHA1 for signatures,
- * TODO: support other hashes depending on configuration/auth */
scheme = SIGN_RSA_EMSA_PKCS1_SHA1;
- auth_method = AUTH_RSA;
+ *auth_method = AUTH_RSA;
break;
case KEY_ECDSA:
- /* we try to deduct the signature scheme from the keysize */
+ /* deduct the signature scheme from the keysize */
switch (private->get_keysize(private))
{
case 256:
scheme = SIGN_ECDSA_256;
- auth_method = AUTH_ECDSA_256;
+ *auth_method = AUTH_ECDSA_256;
break;
case 384:
scheme = SIGN_ECDSA_384;
- auth_method = AUTH_ECDSA_384;
+ *auth_method = AUTH_ECDSA_384;
break;
case 521:
scheme = SIGN_ECDSA_521;
- auth_method = AUTH_ECDSA_521;
+ *auth_method = AUTH_ECDSA_521;
break;
default:
DBG1(DBG_IKE, "%d bit ECDSA private key size not supported",
- private->get_keysize(private));
- return status;
+ private->get_keysize(private));
+ return FAILED;
}
break;
default:
DBG1(DBG_IKE, "private key of type %N not supported",
- key_type_names, private->get_type(private));
- return status;
+ key_type_names, private->get_type(private));
+ return FAILED;
}
+
keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init,
this->nonce, id, this->reserved, &octets) &&
- private->sign(private, scheme, octets, &auth_data))
+ private->sign(private, scheme, octets, auth_data))
{
- auth_payload = auth_payload_create();
- auth_payload->set_auth_method(auth_payload, auth_method);
- auth_payload->set_data(auth_payload, auth_data);
- chunk_free(&auth_data);
- message->add_payload(message, (payload_t*)auth_payload);
status = SUCCESS;
}
DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N %s", id,
- auth_method_names, auth_method,
- (status == SUCCESS)? "successful":"failed");
+ auth_method_names, *auth_method,
+ status == SUCCESS ? "successful" : "failed");
chunk_free(&octets);
+ return status;
+}
+
+METHOD(authenticator_t, build, status_t,
+ private_pubkey_authenticator_t *this, message_t *message)
+{
+ private_key_t *private;
+ identification_t *id;
+ auth_cfg_t *auth;
+ chunk_t auth_data;
+ status_t status;
+ auth_payload_t *auth_payload;
+ auth_method_t auth_method;
+
+ id = this->ike_sa->get_my_id(this->ike_sa);
+ auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+ private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, auth);
+ if (!private)
+ {
+ DBG1(DBG_IKE, "no private key found for '%Y'", id);
+ return NOT_FOUND;
+ }
+
+ if (this->ike_sa->supports_extension(this->ike_sa, EXT_SIGNATURE_AUTH))
+ {
+ auth_method = AUTH_DS;
+ status = sign_signature_auth(this, auth, private, id, &auth_data);
+ }
+ else
+ {
+ status = sign_classic(this, auth, private, id, &auth_method,
+ &auth_data);
+ }
private->destroy(private);
+ if (status == SUCCESS)
+ {
+ auth_payload = auth_payload_create();
+ auth_payload->set_auth_method(auth_payload, auth_method);
+ auth_payload->set_data(auth_payload, auth_data);
+ chunk_free(&auth_data);
+ message->add_payload(message, (payload_t*)auth_payload);
+ }
return status;
}
@@ -153,11 +376,10 @@ METHOD(authenticator_t, process, status_t,
return FAILED;
}
auth_method = auth_payload->get_auth_method(auth_payload);
+ auth_data = auth_payload->get_data(auth_payload);
switch (auth_method)
{
case AUTH_RSA:
- /* We currently accept SHA1 signatures only
- * TODO: allow other hash algorithms and note it in "auth" */
key_type = KEY_RSA;
scheme = SIGN_RSA_EMSA_PKCS1_SHA1;
break;
@@ -170,10 +392,15 @@ METHOD(authenticator_t, process, status_t,
case AUTH_ECDSA_521:
scheme = SIGN_ECDSA_521;
break;
+ case AUTH_DS:
+ if (parse_signature_auth_data(&auth_data, &key_type, &scheme))
+ {
+ break;
+ }
+ /* fall-through */
default:
return INVALID_ARG;
}
- auth_data = auth_payload->get_data(auth_payload);
id = this->ike_sa->get_other_id(this->ike_sa);
keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
if (!keymat->get_auth_octets(keymat, TRUE, this->ike_sa_init,
@@ -188,11 +415,16 @@ METHOD(authenticator_t, process, status_t,
{
if (public->verify(public, scheme, octets, auth_data))
{
- DBG1(DBG_IKE, "authentication of '%Y' with %N successful",
- id, auth_method_names, auth_method);
+ DBG1(DBG_IKE, "authentication of '%Y' with %N successful", id,
+ auth_method == AUTH_DS ? signature_scheme_names : auth_method_names,
+ auth_method == AUTH_DS ? scheme : auth_method);
status = SUCCESS;
auth->merge(auth, current_auth, FALSE);
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
+ if (this->store_signature_scheme)
+ {
+ auth->add(auth, AUTH_RULE_SIGNATURE_SCHEME, (uintptr_t)scheme);
+ }
break;
}
else
@@ -265,6 +497,8 @@ pubkey_authenticator_t *pubkey_authenticator_create_verifier(ike_sa_t *ike_sa,
.ike_sa = ike_sa,
.ike_sa_init = received_init,
.nonce = sent_nonce,
+ .store_signature_scheme = lib->settings->get_bool(lib->settings,
+ "%s.signature_authentication_constraints", TRUE, lib->ns),
);
memcpy(this->reserved, reserved, sizeof(this->reserved));