Implemented a hook that recreates a valid incoming IKE_AUTH response, even if AUTH_FAILED
authorMartin Willi <martin@revosec.ch>
Fri, 12 Nov 2010 14:40:29 +0000 (15:40 +0100)
committerMartin Willi <martin@revosec.ch>
Wed, 5 Jan 2011 15:45:46 +0000 (16:45 +0100)
src/conftest/Makefile.am
src/conftest/hooks/pretend_auth.c [new file with mode: 0644]

index 58758ac..3d669ae 100644 (file)
@@ -6,7 +6,7 @@ conftest_SOURCES = conftest.c conftest.h config.c config.h actions.c actions.h \
        hooks/hook.h hooks/ike_auth_fill.c hooks/unsort_message.c \
        hooks/add_notify.c hooks/unencrypted_notify.c hooks/ignore_message.c \
        hooks/add_payload.c hooks/set_critical.c hooks/force_cookie.c \
-       hooks/set_ike_version.c
+       hooks/set_ike_version.c hooks/pretend_auth.c
 
 INCLUDES = \
        -I$(top_srcdir)/src/libstrongswan \
diff --git a/src/conftest/hooks/pretend_auth.c b/src/conftest/hooks/pretend_auth.c
new file mode 100644 (file)
index 0000000..451c414
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "hook.h"
+
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/cert_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ts_payload.h>
+
+typedef struct private_pretend_auth_t private_pretend_auth_t;
+
+/**
+ * Private data of an pretend_auth_t object.
+ */
+struct private_pretend_auth_t {
+
+       /**
+        * Implements the hook_t interface.
+        */
+       hook_t hook;
+
+       /**
+        * remote peer identity
+        */
+       identification_t *id;
+
+       /**
+        * IKE_SA_INIT data for signature
+        */
+       chunk_t ike_init;
+
+       /**
+        * Nonce for signature
+        */
+       chunk_t nonce;
+
+       /**
+        * Selected CHILD_SA proposal
+        */
+       proposal_t *proposal;
+
+       /**
+        * List of initiators Traffic Selectors
+        */
+       linked_list_t *tsi;
+
+       /**
+        * List of responders Traffic Selectors
+        */
+       linked_list_t *tsr;
+};
+
+/**
+ * Process IKE_SA_INIT request message, outgoing
+ */
+static void process_init_request(private_pretend_auth_t *this,
+                                                                ike_sa_t *ike_sa, message_t *message)
+{
+       nonce_payload_t *nonce;
+
+       nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
+       if (nonce)
+       {
+               free(this->nonce.ptr);
+               this->nonce = nonce->get_nonce(nonce);
+       }
+}
+
+/**
+ * Process IKE_AUTH request message, outgoing
+ */
+static void process_auth_request(private_pretend_auth_t *this,
+                                                                ike_sa_t *ike_sa, message_t *message)
+{
+       id_payload_t *id;
+       sa_payload_t *sa;
+       ts_payload_t *tsi, *tsr;
+       linked_list_t *proposals;
+
+       id = (id_payload_t*)message->get_payload(message, ID_RESPONDER);
+       if (id)
+       {
+               this->id->destroy(this->id);
+               this->id = id->get_identification(id);
+       }
+       sa = (sa_payload_t*)message->get_payload(message, SECURITY_ASSOCIATION);
+       if (sa)
+       {
+               proposals = sa->get_proposals(sa);
+               proposals->remove_first(proposals, (void**)&this->proposal);
+               if (this->proposal)
+               {
+                       this->proposal->set_spi(this->proposal, htonl(0x12345678));
+               }
+               proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
+       }
+       tsi = (ts_payload_t*)message->get_payload(message,
+                                                                                         TRAFFIC_SELECTOR_INITIATOR);
+       if (tsi)
+       {
+               this->tsi = tsi->get_traffic_selectors(tsi);
+       }
+       tsr = (ts_payload_t*)message->get_payload(message,
+                                                                                         TRAFFIC_SELECTOR_RESPONDER);
+       if (tsr)
+       {
+               this->tsr = tsr->get_traffic_selectors(tsr);
+       }
+
+}
+
+/**
+ * Process IKE_SA_INIT response message, incoming
+ */
+static void process_init_response(private_pretend_auth_t *this,
+                                                                 ike_sa_t *ike_sa, message_t *message)
+{
+       this->ike_init = message->get_packet_data(message);
+}
+
+/**
+ * Build CERT payloads
+ */
+static void build_certs(private_pretend_auth_t *this,
+                                               ike_sa_t *ike_sa, message_t *message, auth_cfg_t *auth)
+{
+       enumerator_t *enumerator;
+       cert_payload_t *payload;
+       certificate_t *cert;
+       auth_rule_t type;
+
+       /* get subject cert first, then issuing certificates */
+       cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
+       if (cert)
+       {
+               payload = cert_payload_create_from_cert(cert);
+               if (payload)
+               {
+                       DBG1(DBG_IKE, "pretending end entity cert \"%Y\"",
+                                cert->get_subject(cert));
+                       message->add_payload(message, (payload_t*)payload);
+               }
+       }
+       enumerator = auth->create_enumerator(auth);
+       while (enumerator->enumerate(enumerator, &type, &cert))
+       {
+               if (type == AUTH_RULE_IM_CERT)
+               {
+                       payload = cert_payload_create_from_cert(cert);
+                       if (payload)
+                       {
+                               DBG1(DBG_IKE, "pretending issuer cert \"%Y\"",
+                                        cert->get_subject(cert));
+                               message->add_payload(message, (payload_t*)payload);
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+}
+
+/**
+ * Build faked AUTH payload
+ */
+static bool build_auth(private_pretend_auth_t *this,
+                                          ike_sa_t *ike_sa, message_t *message)
+{
+       chunk_t octets, auth_data;
+       private_key_t *private;
+       auth_cfg_t *auth;
+       auth_payload_t *auth_payload;
+       auth_method_t auth_method;
+       signature_scheme_t scheme;
+       keymat_t *keymat;
+
+       auth = auth_cfg_create();
+       private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, this->id, auth);
+       build_certs(this, ike_sa, message, auth);
+       auth->destroy(auth);
+       if (private == NULL)
+       {
+               DBG1(DBG_CFG, "no private key found for '%Y' to pretend AUTH", this->id);
+               return FALSE;
+       }
+
+       switch (private->get_type(private))
+       {
+               case KEY_RSA:
+                       scheme = SIGN_RSA_EMSA_PKCS1_SHA1;
+                       auth_method = AUTH_RSA;
+                       break;
+               case KEY_ECDSA:
+                       /* we try to deduct the signature scheme from the keysize */
+                       switch (private->get_keysize(private))
+                       {
+                               case 256:
+                                       scheme = SIGN_ECDSA_256;
+                                       auth_method = AUTH_ECDSA_256;
+                                       break;
+                               case 384:
+                                       scheme = SIGN_ECDSA_384;
+                                       auth_method = AUTH_ECDSA_384;
+                                       break;
+                               case 521:
+                                       scheme = SIGN_ECDSA_521;
+                                       auth_method = AUTH_ECDSA_521;
+                                       break;
+                               default:
+                                       DBG1(DBG_CFG, "%d bit ECDSA private key size not supported",
+                                                       private->get_keysize(private));
+                                       return FALSE;
+                       }
+                       break;
+               default:
+                       DBG1(DBG_CFG, "private key of type %N not supported",
+                                       key_type_names, private->get_type(private));
+                       return FALSE;
+       }
+       keymat = ike_sa->get_keymat(ike_sa);
+       octets = keymat->get_auth_octets(keymat, TRUE, this->ike_init,
+                                                                        this->nonce, this->id);
+       if (!private->sign(private, scheme, octets, &auth_data))
+       {
+               chunk_free(&octets);
+               private->destroy(private);
+               return FALSE;
+       }
+       auth_payload = auth_payload_create();
+       auth_payload->set_auth_method(auth_payload, auth_method);
+       auth_payload->set_data(auth_payload, auth_data);
+       chunk_free(&auth_data);
+       chunk_free(&octets);
+       private->destroy(private);
+       message->add_payload(message, (payload_t*)auth_payload);
+       DBG1(DBG_CFG, "pretending AUTH payload for '%Y' with %N",
+                this->id, auth_method_names, auth_method);
+       return TRUE;
+}
+
+/**
+ * Process IKE_AUTH response message, incoming
+ */
+static void process_auth_response(private_pretend_auth_t *this,
+                                                                 ike_sa_t *ike_sa, message_t *message)
+{
+       enumerator_t *enumerator;
+       payload_t *payload;
+
+       /* check for, and remove AUTHENTICATION_FAILED notify */
+       enumerator = message->create_payload_enumerator(message);
+       while (enumerator->enumerate(enumerator, &payload))
+       {
+               notify_payload_t *notify = (notify_payload_t*)payload;
+
+               if (payload->get_type(payload) != NOTIFY ||
+                       notify->get_notify_type(notify) != AUTHENTICATION_FAILED)
+               {
+                       DBG1(DBG_CFG, "no %N notify found, disabling AUTH pretending",
+                                notify_type_names, AUTHENTICATION_FAILED);
+                       enumerator->destroy(enumerator);
+                       return;
+               }
+               message->remove_payload_at(message, enumerator);
+               payload->destroy(payload);
+       }
+       enumerator->destroy(enumerator);
+
+       if (!build_auth(this, ike_sa, message))
+       {
+               message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
+               return;
+       }
+       message->add_payload(message, (payload_t*)
+                               id_payload_create_from_identification(ID_RESPONDER, this->id));
+       if (this->proposal)
+       {
+               message->add_payload(message, (payload_t*)
+                                       sa_payload_create_from_proposal(this->proposal));
+       }
+       if (this->tsi)
+       {
+               message->add_payload(message, (payload_t*)
+                                       ts_payload_create_from_traffic_selectors(TRUE, this->tsi));
+       }
+       if (this->tsr)
+       {
+               message->add_payload(message, (payload_t*)
+                                       ts_payload_create_from_traffic_selectors(FALSE, this->tsr));
+       }
+}
+
+METHOD(listener_t, message, bool,
+       private_pretend_auth_t *this, ike_sa_t *ike_sa, message_t *message,
+       bool incoming)
+{
+       if (incoming)
+       {
+               if (!message->get_request(message))
+               {
+                       if (message->get_exchange_type(message) == IKE_SA_INIT)
+                       {
+                               process_init_response(this, ike_sa, message);
+                       }
+                       if (message->get_exchange_type(message) == IKE_AUTH &&
+                               message->get_message_id(message) == 1)
+                       {
+                               process_auth_response(this, ike_sa, message);
+                       }
+               }
+       }
+       else
+       {
+               if (message->get_request(message))
+               {
+                       if (message->get_exchange_type(message) == IKE_SA_INIT)
+                       {
+                               process_init_request(this, ike_sa, message);
+                       }
+                       if (message->get_exchange_type(message) == IKE_AUTH &&
+                               message->get_message_id(message) == 1)
+                       {
+                               process_auth_request(this, ike_sa, message);
+                       }
+               }
+       }
+       return TRUE;
+}
+
+METHOD(hook_t, destroy, void,
+       private_pretend_auth_t *this)
+{
+       if (this->tsi)
+       {
+               this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+       }
+       if (this->tsr)
+       {
+               this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+       }
+       DESTROY_IF(this->proposal);
+       this->id->destroy(this->id);
+       free(this->ike_init.ptr);
+       free(this->nonce.ptr);
+       free(this);
+}
+
+/**
+ * Create the IKE_AUTH fill hook
+ */
+hook_t *pretend_auth_hook_create(char *name)
+{
+       private_pretend_auth_t *this;
+
+       INIT(this,
+               .hook = {
+                       .listener = {
+                               .message = _message,
+                       },
+                       .destroy = _destroy,
+               },
+               .id = identification_create_from_string(
+                               conftest->test->get_str(conftest->test,
+                                                                               "hooks.%s.peer", "%any", name)),
+       );
+
+       return &this->hook;
+}