From patchwork Fri Jan 26 09:31:06 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 221 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director4.mail.ord1d.rsapps.net ([172.28.255.1]) by backend31.mail.ord1d.rsapps.net (Dovecot) with LMTP id I7UPDECQa1o8ewAAgoeIoA for ; Fri, 26 Jan 2018 15:32:00 -0500 Received: from director3.mail.ord1c.rsapps.net ([172.28.255.1]) by director4.mail.ord1d.rsapps.net (Dovecot) with LMTP id e/DgC0CQa1omaAAAHDmxtw ; Fri, 26 Jan 2018 15:32:00 -0500 Received: from smtp1.gate.ord1c ([172.28.255.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by director3.mail.ord1c.rsapps.net (Dovecot) with LMTP id uZ18CkCQa1rgfAAAdSFV8w ; Fri, 26 Jan 2018 15:32:00 -0500 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.34.181.88] Authentication-Results: smtp1.gate.ord1c.rsapps.net; iprev=pass policy.iprev="216.34.181.88"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dkim=fail (signature verification failed) header.d=hansenpartnership.com; dmarc=fail (p=none; dis=none) header.from=hansenpartnership.com X-Classification-ID: f40f13de-02d7-11e8-83c2-842b2b47c027-1-1 Received: from [216.34.181.88] ([216.34.181.88:21114] helo=lists.sourceforge.net) by smtp1.gate.ord1c.rsapps.net (envelope-from ) (ecelerity 4.2.1.56364 r(Core:4.2.1.14)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id D6/EE-25975-D309B6A5; Fri, 26 Jan 2018 15:31:57 -0500 Received: from localhost ([127.0.0.1] helo=sfs-ml-1.v29.ch3.sourceforge.com) by sfs-ml-1.v29.ch3.sourceforge.com with esmtp (Exim 4.89) (envelope-from ) id 1efAeZ-0004gN-Ti; Fri, 26 Jan 2018 20:31:15 +0000 Received: from sfi-mx-2.v28.ch3.sourceforge.com ([172.29.28.192] helo=mx.sourceforge.net) by sfs-ml-1.v29.ch3.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.89) (envelope-from ) id 1efAeZ-0004gH-8G for openvpn-devel@lists.sourceforge.net; Fri, 26 Jan 2018 20:31:15 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:Mime-Version:Content-Type :References:In-Reply-To:Date:To:From:Subject:Message-ID:Sender:Reply-To:Cc: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=E/3DYPVSVCDJqtHzNdeko3adQ9sTX2hwygq0OOUeoOk=; b=bBEfQvwwgIiCy09MJSXG8qsCb/ oJn6UbyXmqUtJuIZwHHoBVrhUCx0RoySNandl9pnaT6ejK/DoXKWvhtxIySgnUa74BvdhI+cJLEcu CUPzRpi3e6UWfZM4K6kzHjVNbFXTlEPxtb9LuJTqHMSOj1Rt77vpZHeC7EST2m6HlnyY=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:Mime-Version:Content-Type:References: In-Reply-To:Date:To:From:Subject:Message-ID:Sender:Reply-To:Cc:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=E/3DYPVSVCDJqtHzNdeko3adQ9sTX2hwygq0OOUeoOk=; b=FJ4OTFt9rcgprro7VuoiDcLClN mJ4MpcE8E5obdgASu9LB75GB25hxeVRV6UuGq8c9NRoT43EdsgnSWcwOecodCs1ArM+OKcN8Bweyd SyQcRNQefsOU9nk6ByHKL1ndawMZ2ForOvJGOGehiFP/ydbE1lzOtR/fUvcqJUhcsZuI=; Received: from bedivere.hansenpartnership.com ([66.63.167.143]) by sfi-mx-2.v28.ch3.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.89) id 1efAeW-0000Rk-RM for openvpn-devel@lists.sourceforge.net; Fri, 26 Jan 2018 20:31:15 +0000 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id ED0DA8EE3BE for ; Fri, 26 Jan 2018 12:31:06 -0800 (PST) Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id TZWwmk1kpeAh for ; Fri, 26 Jan 2018 12:31:06 -0800 (PST) Received: from [153.66.254.194] (unknown [50.35.65.221]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by bedivere.hansenpartnership.com (Postfix) with ESMTPSA id A99538EE0EF for ; Fri, 26 Jan 2018 12:31:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1516998666; bh=lIo1/bGg8UB9gZmJAdzmxWyiXKDhOSe+xEK6OYwlJTo=; h=Subject:From:To:Date:In-Reply-To:References:From; b=UvQmFYzfsUiuaJrKbKjKiBbunqatsYjIoDjXe6nG2Y2SQMFP+0QvsbywROVU9+Yk/ wUmZNrTqR5UJblOCX5aap6z4y7Cze30nCEJbnhXGQ5by2TNgiMUf032dkOL3SvD/KH dz3psrhWIlcWdrV02eG44eEN8Gf7qs59mNoqE0Cg= Message-ID: <1516998666.3034.17.camel@HansenPartnership.com> From: James Bottomley To: openvpn-devel@lists.sourceforge.net Date: Fri, 26 Jan 2018 12:31:06 -0800 In-Reply-To: <1516998568.3034.15.camel@HansenPartnership.com> References: <1516998568.3034.15.camel@HansenPartnership.com> X-Mailer: Evolution 3.20.5 Mime-Version: 1.0 X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature X-Headers-End: 1efAeW-0000Rk-RM Subject: [Openvpn-devel] [PATCH v3 2/2] Add unit tests for engine keys X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox Testing engines is problematic, so one of the prerequisites built for the tests is a simple openssl engine that reads a non-standard PEM guarded key. The test is simply can we run a client/server configuration with the usual sample key replaced by an engine key. The trivial engine prints out some operations and we check for these in the log to make sure the engine was used to load the key and that it correctly got the password. Signed-off-by: James Bottomley --- configure.ac | 2 + tests/unit_tests/Makefile.am | 6 +- tests/unit_tests/engine-key/Makefile.am | 14 ++++ tests/unit_tests/engine-key/check_engine_keys.sh | 30 +++++++ tests/unit_tests/engine-key/libtestengine.c | 102 +++++++++++++++++++++++ tests/unit_tests/engine-key/openssl.cnf | 12 +++ 6 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 tests/unit_tests/engine-key/Makefile.am create mode 100755 tests/unit_tests/engine-key/check_engine_keys.sh create mode 100644 tests/unit_tests/engine-key/libtestengine.c create mode 100644 tests/unit_tests/engine-key/openssl.cnf diff --git a/configure.ac b/configure.ac index 2c1937e5..ea4e5bc8 100644 --- a/configure.ac +++ b/configure.ac @@ -1360,6 +1360,7 @@ AM_CONDITIONAL([GIT_CHECKOUT], [test "${GIT_CHECKOUT}" = "yes"]) AM_CONDITIONAL([ENABLE_PLUGIN_AUTH_PAM], [test "${enable_plugin_auth_pam}" = "yes"]) AM_CONDITIONAL([ENABLE_PLUGIN_DOWN_ROOT], [test "${enable_plugin_down_root}" = "yes"]) AM_CONDITIONAL([HAVE_LD_WRAP_SUPPORT], [test "${have_ld_wrap_support}" = "yes"]) +AM_CONDITIONAL([OPENSSL_ENGINE], [test "${have_openssl_engine}" = "yes"]) sampledir="\$(docdir)/sample" AC_SUBST([plugindir]) @@ -1424,6 +1425,7 @@ AC_CONFIG_FILES([ tests/unit_tests/openvpn/Makefile tests/unit_tests/plugins/Makefile tests/unit_tests/plugins/auth-pam/Makefile + tests/unit_tests/engine-key/Makefile vendor/Makefile sample/Makefile ]) diff --git a/tests/unit_tests/Makefile.am b/tests/unit_tests/Makefile.am index 31d37b89..648bd7a8 100644 --- a/tests/unit_tests/Makefile.am +++ b/tests/unit_tests/Makefile.am @@ -1,5 +1,9 @@ AUTOMAKE_OPTIONS = foreign +SUBDIRS = if CMOCKA_INITIALIZED -SUBDIRS = example_test openvpn plugins +SUBDIRS += example_test openvpn plugins +endif +if OPENSSL_ENGINE +SUBDIRS += engine-key endif diff --git a/tests/unit_tests/engine-key/Makefile.am b/tests/unit_tests/engine-key/Makefile.am new file mode 100644 index 00000000..f83bf817 --- /dev/null +++ b/tests/unit_tests/engine-key/Makefile.am @@ -0,0 +1,14 @@ +AUTOMAKE_OPTIONS = foreign + +check_LTLIBRARIES = libtestengine.la + +TESTS_ENVIRONMENT = srcdir="$(abs_srcdir)"; \ + top_builddir="$(top_builddir)"; \ + top_srcdir="$(top_srcdir)"; \ + export srcdir top_builddir top_srcdir; + +TESTS = check_engine_keys.sh + +libtestengine_la_SOURCES = libtestengine.c +libtestengine_la_LDFLAGS = -rpath /lib -avoid-version + diff --git a/tests/unit_tests/engine-key/check_engine_keys.sh b/tests/unit_tests/engine-key/check_engine_keys.sh new file mode 100755 index 00000000..c1ae59e6 --- /dev/null +++ b/tests/unit_tests/engine-key/check_engine_keys.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +OPENSSL_CONF="${srcdir}/openssl.cnf" +export OPENSSL_CONF + +password='AT3S4PASSWD' + +key="${srcdir}/client.key" +pwdfile="${srcdir}/passwd" + +# create an engine key for us +sed 's/PRIVATE KEY/TEST ENGINE KEY/' < ${top_srcdir}/sample/sample-keys/client.key > ${key} +echo "$password" > $pwdfile + +# note here we've induced a mismatch in the client key and the server +# cert which openvpn should report and die. Check that it does. Note +# also that this mismatch depends on openssl not openvpn, so it is +# somewhat fragile +${top_builddir}/src/openvpn/openvpn --cd ${top_srcdir}/sample --config sample-config-files/loopback-server --engine testengine --key ${key} --askpass $pwdfile > log.txt 2>&1 + +# first off check we died because of a key mismatch. If this doesn't +# pass, suspect openssl of returning different messages and update the +# test accordingly +grep -q 'X509_check_private_key:key values mismatch' log.txt || { echo "Key mismatch not detected"; exit 1; } + +# now look for the engine prints (these are under our control) +grep -q 'ENGINE: engine_init called' log.txt || { echo "Engine initialization not detected"; exit 1; } +grep -q 'ENGINE: engine_load_key called' log.txt || { echo "Key was not loaded from engine"; exit 1; } +grep -q "ENGINE: engine_load_key got password ${password}" log.txt || { echo "Key password was not retrieved by the engine"; exit 1; } +exit 0 diff --git a/tests/unit_tests/engine-key/libtestengine.c b/tests/unit_tests/engine-key/libtestengine.c new file mode 100644 index 00000000..4880223d --- /dev/null +++ b/tests/unit_tests/engine-key/libtestengine.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include + +static char *engine_id = "testengine"; +static char *engine_name = "Engine for testing openvpn engine key support"; + +static int is_initialized = 0; + +static int engine_init(ENGINE *e) +{ + is_initialized = 1; + fprintf(stderr, "ENGINE: engine_init called\n"); + return 1; +} + +static int engine_finish(ENGINE *e) +{ + fprintf(stderr, "ENGINE: engine_finsh called\n"); + is_initialized = 0; + return 1; +} + +static EVP_PKEY *engine_load_key(ENGINE *e, const char *key_id, + UI_METHOD *ui_method, void *cb_data) +{ + BIO *b; + EVP_PKEY *pkey; + PKCS8_PRIV_KEY_INFO *p8inf; + UI *ui; + char auth[256]; + int len; + + fprintf(stderr, "ENGINE: engine_load_key called\n"); + + if (!is_initialized) { + fprintf(stderr, "Load Key called without correct initialization\n"); + return NULL; + } + b = BIO_new_file(key_id, "r"); + if (!b) { + fprintf(stderr, "File %s does not exist or cannot be read\n", key_id); + return 0; + } + /* Basically read an EVP_PKEY private key file with different + * PEM guards --- we are a test engine */ + p8inf = PEM_ASN1_read_bio((d2i_of_void *)d2i_PKCS8_PRIV_KEY_INFO, + "TEST ENGINE KEY", b, + NULL, NULL, NULL); + BIO_free(b); + if (!p8inf) { + fprintf(stderr, "Failed to read engine private key\n"); + return NULL; + } + pkey = EVP_PKCS82PKEY(p8inf); + + /* now we have a private key, pretend it had a password + * this verifies the password makes it through openvpn OK */ + ui = UI_new(); + + if (ui_method) + UI_set_method(ui, ui_method); + + UI_add_user_data(ui, cb_data); + + if (UI_add_input_string(ui, "enter test engine key", + UI_INPUT_FLAG_DEFAULT_PWD, + auth, 0, sizeof(auth)) == 0) { + fprintf(stderr, "UI_add_input_string failed\n"); + goto out; + } + + if (UI_process(ui)) { + fprintf(stderr, "UI_process failed\n"); + goto out; + } + + fprintf(stderr, "ENGINE: engine_load_key got password %s\n", auth); + + out: + UI_free(ui); + + return pkey; +} + + +static int engine_bind_fn(ENGINE *e, const char *id) +{ + if (id && strcmp(id, engine_id) != 0) + return 0; + if (!ENGINE_set_id(e, engine_id) || + !ENGINE_set_name(e, engine_name) || + !ENGINE_set_init_function(e, engine_init) || + !ENGINE_set_finish_function(e, engine_finish) || + !ENGINE_set_load_privkey_function(e, engine_load_key)) + return 0; + return 1; +} + +IMPLEMENT_DYNAMIC_CHECK_FN() +IMPLEMENT_DYNAMIC_BIND_FN(engine_bind_fn) diff --git a/tests/unit_tests/engine-key/openssl.cnf b/tests/unit_tests/engine-key/openssl.cnf new file mode 100644 index 00000000..53200c46 --- /dev/null +++ b/tests/unit_tests/engine-key/openssl.cnf @@ -0,0 +1,12 @@ +HOME = . +openssl_conf = openssl_init + +[req] +[openssl_init] +engines = engines_section + +[engines_section] +testengine = testengine_section + +[testengine_section] +dynamic_path = $ENV::srcdir/.libs/libtestengine.so