Blob Blame History Raw
From fa5147c86941512921282b84819b896a0d4f29bb Mon Sep 17 00:00:00 2001
From: Daiki Ueno <dueno@redhat.com>
Date: Wed, 19 Jun 2019 17:21:16 +0200
Subject: [PATCH] pkcs11: ignore login error when traversing tokens

If a token is a general access device, it is expected that login
attempt to that token returns error:
https://github.com/p11-glue/p11-kit/blob/master/trust/module.c#L852

On the other hand, _pkcs11_traverse_tokens treats the error as fatal
and stops iteration.  This behavior prevents object search without
token specifier if such tokens are registered in the system.

Reported by Stanislav Zidek in
https://bugzilla.redhat.com/show_bug.cgi?id=1705478

Signed-off-by: Daiki Ueno <dueno@redhat.com>
---
 .gitignore                  |   1 +
 lib/pkcs11.c                |   8 +-
 tests/Makefile.am           |   2 +-
 tests/p11-kit-load.sh       |  23 ++++++
 tests/pkcs11/list-objects.c | 150 ++++++++++++++++++++++++++++++++++++
 5 files changed, 182 insertions(+), 2 deletions(-)
 create mode 100644 tests/pkcs11/list-objects.c

diff --git a/lib/pkcs11.c b/lib/pkcs11.c
index de5309b29..2ef0e3e02 100644
--- a/lib/pkcs11.c
+++ b/lib/pkcs11.c
@@ -1617,7 +1617,13 @@ _pkcs11_traverse_tokens(find_func_t find_func, void *input,
 					 info, flags);
 			if (ret < 0) {
 				gnutls_assert();
-				return ret;
+				pkcs11_close_session(&sinfo);
+
+				/* treat the error as fatal only if
+				 * the token requires login */
+				if (l_tinfo.flags & CKF_LOGIN_REQUIRED)
+					return ret;
+				continue;
 			}
 
 			ret =
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a67f1549c..7fe954f63 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -496,7 +496,7 @@ dist_check_SCRIPTS += p11-kit-trust.sh testpkcs11.sh certtool-pkcs11.sh
 if HAVE_PKCS11_TRUST_STORE
 if P11KIT_0_23_11_API
 dist_check_SCRIPTS += p11-kit-load.sh
-indirect_tests += pkcs11/list-tokens
+indirect_tests += pkcs11/list-tokens pkcs11/list-objects
 endif
 endif
 
diff --git a/tests/p11-kit-load.sh b/tests/p11-kit-load.sh
index 3201a2c5f..419900f6a 100755
--- a/tests/p11-kit-load.sh
+++ b/tests/p11-kit-load.sh
@@ -22,6 +22,7 @@
 srcdir="${srcdir:-.}"
 builddir="${builddir:-.}"
 CERTTOOL="${CERTTOOL:-../src/certtool${EXEEXT}}"
+P11TOOL="${P11TOOL:-../src/p11tool${EXEEXT}}"
 DIFF="${DIFF:-diff}"
 PKGCONFIG="${PKG_CONFIG:-$(which pkg-config)}"
 TMP_SOFTHSM_DIR="./softhsm-load.$$.tmp"
@@ -90,6 +91,12 @@ if test $? != 0; then
 	exit 1
 fi
 
+GNUTLS_PIN="${PIN}" ${P11TOOL} --login --label GnuTLS-Test-RSA --generate-privkey rsa --provider "${SOFTHSM_MODULE}" pkcs11: --outfile /dev/null
+if test $? != 0; then
+	echo "failed to generate privkey"
+	exit 1
+fi
+
 FILTERTOKEN="sed s/token=.*//g"
 
 # Check whether both are listed
@@ -175,6 +182,22 @@ if test "$nr" != 2;then
 	exit 1
 fi
 
+# Check whether public key and privkey are listed.
+nr=$(GNUTLS_PIN="${PIN}" ${builddir}/pkcs11/list-objects -o ${P11DIR} -t all pkcs11:token=GnuTLS-Test|sort -u|wc -l)
+if test "$nr" != 2;then
+	echo "Error in test 8: did not find all objects"
+	${builddir}/pkcs11/list-objects -o ${P11DIR} -t all pkcs11:token=GnuTLS-Test
+	exit 1
+fi
+
+# Check whether all privkeys are listed even if trust module is registered.
+nr=$(GNUTLS_PIN="${PIN}" ${builddir}/pkcs11/list-objects -o ${P11DIR} -t privkey pkcs11:|sort -u|wc -l)
+if test "$nr" != 1;then
+	echo "Error in test 9: did not find privkey objects"
+	${builddir}/pkcs11/list-objects -o ${P11DIR} -t privkey pkcs11:
+	exit 1
+fi
+
 rm -f ${P11DIR}/*
 rm -rf ${TMP_SOFTHSM_DIR}
 
diff --git a/tests/pkcs11/list-objects.c b/tests/pkcs11/list-objects.c
new file mode 100644
index 000000000..ab30cd568
--- /dev/null
+++ b/tests/pkcs11/list-objects.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2016-2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
+#include <getopt.h>
+#define P11_KIT_FUTURE_UNSTABLE_API
+#include <p11-kit/p11-kit.h>
+#include "cert-common.h"
+
+/* lists the registered PKCS#11 modules by p11-kit.
+ */
+
+static void tls_log_func(int level, const char *str)
+{
+	fprintf(stderr, "|<%d>| %s", level, str);
+}
+
+static const char *opt_pin;
+
+static
+int pin_func(void* userdata, int attempt, const char* url, const char *label,
+	     unsigned flags, char *pin, size_t pin_max)
+{
+	if (attempt == 0) {
+		strcpy(pin, opt_pin);
+		return 0;
+	}
+	return -1;
+}
+
+int main(int argc, char **argv)
+{
+	int ret;
+	unsigned i;
+	int opt;
+	char *url, *mod;
+	unsigned flags;
+	unsigned obj_flags = 0;
+	int attrs = GNUTLS_PKCS11_OBJ_ATTR_ALL;
+	gnutls_pkcs11_obj_t *crt_list;
+	unsigned int crt_list_size = 0;
+	const char *envvar;
+
+	ret = gnutls_global_init();
+	if (ret != 0) {
+		fprintf(stderr, "error at %d: %s\n", __LINE__, gnutls_strerror(ret));
+		exit(1);
+	}
+
+	gnutls_global_set_log_function(tls_log_func);
+
+	while((opt = getopt(argc, argv, "o:t:")) != -1) {
+		switch(opt) {
+			case 'o':
+				mod = strdup(optarg);
+				p11_kit_override_system_files(NULL, NULL, mod, mod, NULL);
+				break;
+			case 't':
+				/* specify the object type to list */
+				if (strcmp(optarg, "all") == 0)
+					attrs = GNUTLS_PKCS11_OBJ_ATTR_ALL;
+				else if (strcmp(optarg, "privkey") == 0)
+					attrs = GNUTLS_PKCS11_OBJ_ATTR_PRIVKEY;
+				else {
+					fprintf(stderr, "Unknown object type %s\n", optarg);
+					exit(1);
+				}
+				break;
+			default:
+				fprintf(stderr, "Unknown option %c\n", (char)opt);
+				exit(1);
+		}
+	}
+
+	if (optind == argc) {
+		fprintf(stderr, "specify URL\n");
+		exit(1);
+	}
+	url = argv[optind];
+
+	envvar = getenv("GNUTLS_PIN");
+	if (envvar && *envvar != '\0') {
+		opt_pin = envvar;
+		obj_flags |= GNUTLS_PKCS11_OBJ_FLAG_LOGIN;
+		gnutls_pkcs11_set_pin_function(pin_func, NULL);
+	}
+
+	ret = gnutls_pkcs11_token_get_flags(url, &flags);
+	if (ret < 0) {
+		flags = 0;
+	}
+
+	ret =
+	    gnutls_pkcs11_obj_list_import_url2(&crt_list, &crt_list_size,
+					       url, attrs, obj_flags);
+	if (ret != 0) {
+		fprintf(stderr, "error at %d: %s\n", __LINE__, gnutls_strerror(ret));
+		exit(1);
+	}
+
+	for (i = 0; i < crt_list_size; i++) {
+		char *output;
+
+		ret =
+		    gnutls_pkcs11_obj_export_url(crt_list[i], 0,
+						 &output);
+		if (ret != 0) {
+			fprintf(stderr, "error at %d: %s\n", __LINE__, gnutls_strerror(ret));
+			exit(1);
+		}
+
+		fprintf(stdout, "%s\n", output);
+		gnutls_free(output);
+		gnutls_pkcs11_obj_deinit(crt_list[i]);
+	}
+	gnutls_free(crt_list);
+
+	gnutls_global_deinit();
+}
-- 
2.21.0