Commit 0ca16aba33 for strongswan.org

commit 0ca16aba3300472cd3868d9a7f564c7fd409e7ac
Author: Tobias Brunner <tobias@strongswan.org>
Date:   Mon Jul 27 12:02:38 2026 +0200

    unit-tests: Add possibility to test EAP authentication

diff --git a/src/libcharon/tests/Makefile.am b/src/libcharon/tests/Makefile.am
index 1d925019cf..8a3a432895 100644
--- a/src/libcharon/tests/Makefile.am
+++ b/src/libcharon/tests/Makefile.am
@@ -35,6 +35,7 @@ exchange_tests_SOURCES = \
   utils/exchange_test_asserts.h utils/exchange_test_asserts.c \
   utils/exchange_test_helper.h utils/exchange_test_helper.c \
   utils/job_asserts.h \
+  utils/mock_eap.h utils/mock_eap.c \
   utils/mock_dh.h utils/mock_dh.c \
   utils/mock_ipsec.h utils/mock_ipsec.c \
   utils/mock_net.h utils/mock_net.c \
diff --git a/src/libcharon/tests/utils/exchange_test_helper.c b/src/libcharon/tests/utils/exchange_test_helper.c
index 42530be44b..93aa35ca2d 100644
--- a/src/libcharon/tests/utils/exchange_test_helper.c
+++ b/src/libcharon/tests/utils/exchange_test_helper.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-2018 Tobias Brunner
+ * Copyright (C) 2016-2026 Tobias Brunner
  *
  * Copyright (C) secunet Security Networks AG
  *
@@ -15,6 +15,7 @@
  */

 #include "exchange_test_helper.h"
+#include "mock_eap.h"
 #include "mock_dh.h"
 #include "mock_ipsec.h"
 #include "mock_net.h"
@@ -163,16 +164,24 @@ static child_cfg_t *create_child_cfg(bool initiator,
 	return child_cfg;
 }

-static void add_auth_cfg(peer_cfg_t *peer_cfg, bool initiator, bool local)
+static void add_auth_cfg(peer_cfg_t *peer_cfg, bool initiator, bool local,
+						 exchange_test_sa_conf_t *conf)
 {
 	auth_cfg_t *auth;
 	char *id = "init";
+	bool eap = conf ? conf->initiator.eap : FALSE;

-	auth = auth_cfg_create();
-	auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK);
-	if (initiator ^ local)
+	if (initiator != local)
 	{
 		id = "resp";
+		eap = conf ? conf->responder.eap : FALSE;
+	}
+
+	auth = auth_cfg_create();
+	auth->add(auth, AUTH_RULE_AUTH_CLASS, eap ? AUTH_CLASS_EAP : AUTH_CLASS_PSK);
+	if (eap)
+	{
+		auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_GTC);
 	}
 	auth->add(auth, AUTH_RULE_IDENTITY, identification_create_from_string(id));
 	peer_cfg->add_auth_cfg(peer_cfg, auth, local);
@@ -190,8 +199,8 @@ static peer_cfg_t *create_peer_cfg(bool initiator,

 	peer_cfg = peer_cfg_create(initiator ? "init" : "resp",
 							   create_ike_cfg(initiator, conf), &peer);
-	add_auth_cfg(peer_cfg, initiator, TRUE);
-	add_auth_cfg(peer_cfg, initiator, FALSE);
+	add_auth_cfg(peer_cfg, initiator, TRUE, conf);
+	add_auth_cfg(peer_cfg, initiator, FALSE, conf);
 	return peer_cfg;
 }

@@ -380,6 +389,10 @@ void exchange_test_helper_init(char *plugins)
 		PLUGIN_REGISTER(NONCE_GEN, create_nonce_gen),
 			PLUGIN_PROVIDE(NONCE_GEN),
 				PLUGIN_DEPENDS(RNG, RNG_WEAK),
+		PLUGIN_CALLBACK(eap_method_register, mock_eap_create_server),
+			PLUGIN_PROVIDE(EAP_SERVER, EAP_GTC),
+		PLUGIN_CALLBACK(eap_method_register, mock_eap_create_peer),
+			PLUGIN_PROVIDE(EAP_PEER, EAP_GTC),
 	};

 	INIT(backend,
diff --git a/src/libcharon/tests/utils/exchange_test_helper.h b/src/libcharon/tests/utils/exchange_test_helper.h
index b42f79d645..9746e4d238 100644
--- a/src/libcharon/tests/utils/exchange_test_helper.h
+++ b/src/libcharon/tests/utils/exchange_test_helper.h
@@ -112,6 +112,8 @@ struct exchange_test_sa_conf_t {
 		char *esp;
 		/** Support for childless IKE_SAs */
 		childless_t childless;
+		/** Use EAP authentication */
+		bool eap;
 	} initiator, responder;
 };

diff --git a/src/libcharon/tests/utils/mock_eap.c b/src/libcharon/tests/utils/mock_eap.c
new file mode 100644
index 0000000000..f39b395906
--- /dev/null
+++ b/src/libcharon/tests/utils/mock_eap.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2026 Tobias Brunner
+ *
+ * Copyright (C) secunet Security Networks 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 "mock_eap.h"
+
+#define REQUEST_MSG "password"
+#define MOCK_PW_MSG "mock password"
+
+typedef struct private_eap_method_t private_eap_method_t;
+
+/**
+ * Private data
+ */
+struct private_eap_method_t {
+
+	/**
+	 * Public interface
+	 */
+	eap_method_t public;
+
+	/**
+	 * ID of the server
+	 */
+	identification_t *server;
+
+	/**
+	 * ID of the peer
+	 */
+	identification_t *peer;
+
+	/**
+	 * EAP message identifier
+	 */
+	uint8_t identifier;
+};
+
+typedef struct eap_gtc_header_t eap_gtc_header_t;
+
+/**
+ * packed eap GTC header struct
+ */
+struct eap_gtc_header_t {
+	/** EAP code (REQUEST/RESPONSE) */
+	uint8_t code;
+	/** unique message identifier */
+	uint8_t identifier;
+	/** length of whole message */
+	uint16_t length;
+	/** EAP type */
+	uint8_t type;
+	/** type data */
+	uint8_t data[];
+} __attribute__((__packed__));
+
+METHOD(eap_method_t, initiate_peer, status_t,
+	private_eap_method_t *this, eap_payload_t **out)
+{
+	/* peer never initiates */
+	return FAILED;
+}
+
+METHOD(eap_method_t, initiate_server, status_t,
+	private_eap_method_t *this, eap_payload_t **out)
+{
+	eap_gtc_header_t *req;
+	size_t len;
+
+	len = strlen(REQUEST_MSG);
+	req = alloca(sizeof(eap_gtc_header_t) + len);
+	req->length = htons(sizeof(eap_gtc_header_t) + len);
+	req->code = EAP_REQUEST;
+	req->identifier = this->identifier;
+	req->type = EAP_GTC;
+	memcpy(req->data, REQUEST_MSG, len);
+
+	*out = eap_payload_create_data(chunk_create((void*)req,
+								   sizeof(eap_gtc_header_t) + len));
+	return NEED_MORE;
+}
+
+METHOD(eap_method_t, process_peer, status_t,
+	private_eap_method_t *this, eap_payload_t *in, eap_payload_t **out)
+{
+	eap_gtc_header_t *res;
+	size_t len;
+
+	len = strlen(MOCK_PW_MSG);
+	this->identifier = in->get_identifier(in);
+	res = alloca(sizeof(eap_gtc_header_t) + len);
+	res->length = htons(sizeof(eap_gtc_header_t) + len);
+	res->code = EAP_RESPONSE;
+	res->identifier = this->identifier;
+	res->type = EAP_GTC;
+	memcpy(res->data, MOCK_PW_MSG, len);
+
+	*out = eap_payload_create_data(chunk_create((void*)res,
+								   sizeof(eap_gtc_header_t) + len));
+	return NEED_MORE;
+}
+
+METHOD(eap_method_t, process_server, status_t,
+	private_eap_method_t *this, eap_payload_t *in, eap_payload_t **out)
+{
+	chunk_t pass;
+
+	pass = chunk_skip(in->get_data(in), 5);
+	if (this->identifier != in->get_identifier(in) || !pass.len ||
+		!chunk_equals_const(chunk_from_str(MOCK_PW_MSG), pass))
+	{
+		DBG1(DBG_IKE, "received invalid EAP-GTC message");
+		return FAILED;
+	}
+	return SUCCESS;
+}
+
+METHOD(eap_method_t, get_type, eap_type_t,
+	private_eap_method_t *this, pen_t *vendor)
+{
+	*vendor = 0;
+	return EAP_GTC;
+}
+
+METHOD(eap_method_t, get_msk, status_t,
+	private_eap_method_t *this, chunk_t *msk)
+{
+	return NOT_SUPPORTED;
+}
+
+METHOD(eap_method_t, get_identifier, uint8_t,
+	private_eap_method_t *this)
+{
+	return this->identifier;
+}
+
+METHOD(eap_method_t, set_identifier, void,
+	private_eap_method_t *this, uint8_t identifier)
+{
+	this->identifier = identifier;
+}
+
+METHOD(eap_method_t, is_mutual, bool,
+	private_eap_method_t *this)
+{
+	return FALSE;
+}
+
+METHOD(eap_method_t, destroy, void,
+	private_eap_method_t *this)
+{
+	this->peer->destroy(this->peer);
+	this->server->destroy(this->server);
+	free(this);
+}
+
+/**
+ * Generic constructor
+ */
+static private_eap_method_t *mock_eap_create_generic(identification_t *server,
+												  identification_t *peer)
+{
+	private_eap_method_t *this;
+
+	INIT(this,
+		.public = {
+			.get_type = _get_type,
+			.is_mutual = _is_mutual,
+			.get_msk = _get_msk,
+			.get_identifier = _get_identifier,
+			.set_identifier = _set_identifier,
+			.destroy = _destroy,
+		},
+		.peer = peer->clone(peer),
+		.server = server->clone(server),
+	);
+
+	return this;
+}
+
+/*
+ * Described in header
+ */
+eap_method_t *mock_eap_create_server(identification_t *server,
+									 identification_t *peer)
+{
+	private_eap_method_t *this = mock_eap_create_generic(server, peer);
+
+	this->public.initiate = _initiate_server;
+	this->public.process = _process_server;
+
+	/* generate a non-zero identifier */
+	do {
+		this->identifier = random();
+	} while (!this->identifier);
+
+	return &this->public;
+}
+
+/*
+ * Described in header
+ */
+eap_method_t *mock_eap_create_peer(identification_t *server,
+								   identification_t *peer)
+{
+	private_eap_method_t *this = mock_eap_create_generic(server, peer);
+
+	this->public.initiate = _initiate_peer;
+	this->public.process = _process_peer;
+
+	return &this->public;
+}
diff --git a/src/libcharon/tests/utils/mock_eap.h b/src/libcharon/tests/utils/mock_eap.h
new file mode 100644
index 0000000000..2707949363
--- /dev/null
+++ b/src/libcharon/tests/utils/mock_eap.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2026 Tobias Brunner
+ *
+ * Copyright (C) secunet Security Networks 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.
+ */
+
+/**
+ * Provides an EAP method implementation that does no real work.
+ *
+ * @defgroup mock_eap mock_eap
+ * @{ @ingroup test_utils_c
+ */
+
+#ifndef MOCK_EAP_H_
+#define MOCK_EAP_H_
+
+#include <sa/eap/eap_method.h>
+
+/**
+ * Creates an EAP method acting as server.
+ *
+ * @param server	ID of the EAP server
+ * @param peer		ID of the EAP client
+ * @return			created object
+ */
+eap_method_t *mock_eap_create_server(identification_t *server,
+									 identification_t *peer);
+
+/**
+ * Creates an EAP method acting as client.
+ *
+ * @param server	ID of the EAP server
+ * @param peer		ID of the EAP client
+ * @return			created object
+ */
+eap_method_t *mock_eap_create_peer(identification_t *server,
+								   identification_t *peer);
+
+#endif /** MOCK_EAP_H_ @}*/