[PATCH] sshd: Add pkcs11 support for HostKey.

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

 



Add optional PKCS#11 HostKey support in sshd. Parse sshd_config for
PKCS#11 URIs in the HostKey field, using p11-kit.

Co-authored-by : Mathieu Othacehe <othacehe@xxxxxxx>
---
 Makefile.in            |   4 +-
 configure.ac           |  50 ++++++++++++-
 monitor.c              |  11 ++-
 regress/Makefile       |   3 +-
 regress/sshd-pkcs11.sh |  25 +++++++
 servconf.c             |  10 +++
 ssh-pkcs11.c           | 161 +++++++++++++++++++++++++++++++++++------
 ssh-pkcs11.h           |   2 +
 sshd-pkcs11.c          | 103 ++++++++++++++++++++++++++
 sshd-pkcs11.h          |  26 +++++++
 sshd-session.c         |  48 +++++++++++-
 sshd.c                 |  34 +++++++++
 sshd_config.5          |   6 ++
 13 files changed, 455 insertions(+), 28 deletions(-)
 create mode 100644 regress/sshd-pkcs11.sh
 create mode 100644 sshd-pkcs11.c
 create mode 100644 sshd-pkcs11.h

diff --git a/Makefile.in b/Makefile.in
index 4243006b0..0d786ecc4 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -123,7 +123,7 @@ SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
 SSHDOBJS=sshd.o \
 	platform-listen.o \
 	servconf.o sshpty.o srclimit.o groupaccess.o auth2-methods.o \
-	dns.o fatal.o compat.o utf8.o authfd.o canohost.o \
+	dns.o fatal.o compat.o utf8.o authfd.o canohost.o sshd-pkcs11.o \
 	$(SKOBJS)
 
 SSHD_SESSION_OBJS=sshd-session.o auth-rhosts.o auth-passwd.o \
@@ -139,7 +139,7 @@ SSHD_SESSION_OBJS=sshd-session.o auth-rhosts.o auth-passwd.o \
 	sftp-server.o sftp-common.o \
 	sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
 	sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \
-	sandbox-solaris.o uidswap.o $(SKOBJS)
+	sandbox-solaris.o sshd-pkcs11.o uidswap.o $(SKOBJS)
 
 SFTP_CLIENT_OBJS=sftp-common.o sftp-client.o sftp-glob.o
 
diff --git a/configure.ac b/configure.ac
index 591d5a388..8d78d350c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3262,6 +3262,49 @@ else
 fi
 AC_MSG_RESULT([$enable_sk])
 
+# Now check for p11-kit support.
+if test "x$enable_pkcs11" = "xyes" ; then
+	use_pkgconfig_for_p11kit=
+	if test "x$PKGCONFIG" != "xno"; then
+		AC_MSG_CHECKING([if $PKGCONFIG knows about p11kit])
+		if "$PKGCONFIG" p11-kit-1; then
+			AC_MSG_RESULT([yes])
+			use_pkgconfig_for_p11kit=yes
+		else
+			AC_MSG_RESULT([no])
+		fi
+	fi
+	if test "x$use_pkgconfig_for_p11kit" = "xyes"; then
+		LIBP11KIT=`$PKGCONFIG --libs p11-kit-1`
+		CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags p11-kit-1`"
+	else
+		LIBP11KIT="-lp11-kit"
+	fi
+	OTHERLIBS=`echo $LIBP11KIT | sed 's/-lp11-kit//'`
+	p11kit_error=
+	AC_CHECK_LIB([p11-kit], [p11_kit_uri_parse],
+		[ ],
+		[ p11kit_error="missing/unusable p11kit" ],
+		[ $OTHERLIBS ]
+	)
+	AC_CHECK_HEADER([p11-kit/p11-kit.h], [],
+		[ p11kit_error="missing p11-kit.h from p11-kit" ])
+	AC_CHECK_HEADER([p11-kit/uri.h], [],
+		[ p11kit_error="missing p11-kit/uri.h from p11-kit" ])
+	AC_MSG_CHECKING([for usable p11-kit installation])
+	if test ! -z "$p11kit_error" ; then
+		AC_MSG_RESULT([$p11kit_error])
+		AC_MSG_WARN([No usable p11-kit library/headers found])
+		LIBP11KIT=""
+        else
+		AC_MSG_RESULT([yes])
+		AC_SUBST([LIBP11KIT])
+	        AC_DEFINE([ENABLE_PKCS11_WITH_P11KIT], [1],
+                  [pkcs11 with p11kit support for URI parsing])
+		SSHDLIBS="$LIBP11KIT $SSHDLIBS"
+	fi
+fi
+
 # Now check for built-in security key support.
 if test "x$enable_sk" = "xyes" -a "x$enable_sk_internal" != "xno" ; then
 	use_pkgconfig_for_libfido2=
@@ -5734,11 +5777,14 @@ echo "         Libraries: ${LIBS}"
 if test ! -z "${CHANNELLIBS}"; then
 echo "     +for channels: ${CHANNELLIBS}"
 fi
+if test ! -z "${LIBP11KIT}"; then
+echo "        +for P11KIT: ${LIBP11KIT}"
+fi
 if test ! -z "${LIBFIDO2}"; then
-echo "        +for FIDO2: ${LIBFIDO2}"
+echo "         +for FIDO2: ${LIBFIDO2}"
 fi
 if test ! -z "${SSHDLIBS}"; then
-echo "         +for sshd: ${SSHDLIBS}"
+echo "          +for sshd: ${SSHDLIBS}"
 fi
 
 echo ""
diff --git a/monitor.c b/monitor.c
index 5966b4f96..20331bb70 100644
--- a/monitor.c
+++ b/monitor.c
@@ -683,7 +683,16 @@ mm_answer_sign(struct ssh *ssh, int sock, struct sshbuf *m)
 		if ((r = ssh_agent_sign(auth_sock, key, &signature, &siglen,
 		    p, datlen, alg, compat)) != 0)
 			fatal_fr(r, "agent sign");
-	} else
+	}
+#ifdef ENABLE_PKCS11_WITH_P11KIT
+	else if ((key = get_hostkey_public_by_index(keyid, ssh)) != NULL &&
+		    auth_sock <= 0) {
+			if ((r = sshkey_sign(key, &signature, &siglen, p,
+			    datlen, alg, 0, NULL, compat)) != 0)
+				fatal_fr(r, "pkcs11 sign");
+		}
+#endif
+	else
 		fatal_f("no hostkey from index %d", keyid);
 
 	debug3_f("%s %s signature len=%zu", alg,
diff --git a/regress/Makefile b/regress/Makefile
index 7f7349706..eac1c9a14 100644
--- a/regress/Makefile
+++ b/regress/Makefile
@@ -111,7 +111,8 @@ LTESTS= 	connect \
 		agent-pkcs11-restrict \
 		agent-pkcs11-cert \
 		penalty \
-		penalty-expire
+		penalty-expire \
+		sshd-pkcs11
 
 INTEROP_TESTS=	putty-transfer putty-ciphers putty-kex conch-ciphers
 INTEROP_TESTS+=	dropbear-ciphers dropbear-kex
diff --git a/regress/sshd-pkcs11.sh b/regress/sshd-pkcs11.sh
new file mode 100644
index 000000000..8dbd37a61
--- /dev/null
+++ b/regress/sshd-pkcs11.sh
@@ -0,0 +1,25 @@
+tid="sshd pkcs11 HostKey"
+
+p11_setup || skip "No PKCS#11 library found"
+
+trace "start sshd"
+
+SSHD_SESSION=$OBJ/../sshd-session
+
+# Using @ delimiter because / is used inside $SSHD_SESSION
+sed -i "s@SshdSessionPath.*@SshdSessionPath         ${SSHD_SESSION}@" sshd_config
+
+# Accept new hostkeys to force server signing with all his keys
+sed -i 'd' known_hosts
+sed -i "s/StrictHostKeyChecking.*/StrictHostKeyChecking   accept-new/" ssh_config
+
+echo "HostKey pkcs11:object=01;?module-path=$TEST_SSH_PKCS11&pin-value=$TEST_SSH_PIN" >> sshd_config
+echo "HostKey pkcs11:object=02;?module-path=$TEST_SSH_PKCS11&pin-value=$TEST_SSH_PIN" >> sshd_config
+
+start_sshd
+
+trace "direct connect"
+${SSH} -F $OBJ/ssh_config somehost true
+if [ $? -ne 0 ]; then
+	fail "ssh direct connect failed"
+fi
diff --git a/servconf.c b/servconf.c
index 89b8413e8..70aa72ed8 100644
--- a/servconf.c
+++ b/servconf.c
@@ -46,6 +46,9 @@
 # include "openbsd-compat/glob.h"
 #endif
 
+#ifdef ENABLE_PKCS11_WITH_P11KIT
+#include "sshd-pkcs11.h"
+#endif
 #include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "ssh.h"
@@ -802,6 +805,13 @@ derelativise_path(const char *path)
 
 	if (strcasecmp(path, "none") == 0)
 		return xstrdup("none");
+#ifdef ENABLE_PKCS11_WITH_P11KIT
+	if (is_pkcs11_uri(path))
+		return xstrdup(path);
+#else
+	if (strcasecmp(path, "pkcs11") > 0)
+		debug("PKCS#11 URI not supported.");
+#endif
 	expanded = tilde_expand_filename(path, getuid());
 	if (path_absolute(expanded))
 		return expanded;
diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c
index fadf9c9c6..665c32733 100644
--- a/ssh-pkcs11.c
+++ b/ssh-pkcs11.c
@@ -63,6 +63,8 @@ struct pkcs11_provider {
 	CK_ULONG		nslots;
 	CK_SLOT_ID		*slotlist;
 	struct pkcs11_slotinfo	*slotinfo;
+	CK_ATTRIBUTE_PTR        filters_attr;
+	size_t                  filters_size;
 	int			valid;
 	int			refcount;
 	TAILQ_ENTRY(pkcs11_provider) next;
@@ -444,6 +446,7 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
 	tlen = RSA_size(rsa);
 
 	/* XXX handle CKR_BUFFER_TOO_SMALL */
+	debug("Signing RSA, using keyid : %x", *k11->keyid);
 	rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen);
 	if (rv == CKR_OK)
 		rval = tlen;
@@ -541,6 +544,7 @@ ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
 	sig = xmalloc(siglen);
 
 	/* XXX handle CKR_BUFFER_TOO_SMALL */
+	debug("Signing ECDSA using keyid : %x", *k11->keyid);
 	rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, sig, &siglen);
 	if (rv != CKR_OK) {
 		error("C_Sign failed: %lu", rv);
@@ -1126,27 +1130,56 @@ pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
 {
 	struct sshkey		*key = NULL;
 	CK_OBJECT_CLASS		 key_class;
-	CK_ATTRIBUTE		 key_attr[1];
+	CK_ATTRIBUTE_PTR         cur_attr;
+	CK_ATTRIBUTE             key_attr[1];
+	CK_ATTRIBUTE_PTR	 key_attrs;
+	size_t                   key_attrs_count;
 	CK_SESSION_HANDLE	 session;
 	CK_FUNCTION_LIST	*f = NULL;
 	CK_RV			 rv;
 	CK_OBJECT_HANDLE	 obj;
 	CK_ULONG		 n = 0;
-	int			 ret = -1;
+	int			 i, ret = -1;
 	char			*label;
+	int                     filter_with_class = 0;
+
+	key_attrs = xcalloc(p->filters_size + 1, sizeof(*key_attrs));
 
-	memset(&key_attr, 0, sizeof(key_attr));
 	memset(&obj, 0, sizeof(obj));
+	memset(&key_attr, 0, sizeof(key_attr));
+
+	key_attrs_count = p->filters_size;
+	memcpy(key_attrs, p->filters_attr,
+	    p->filters_size * sizeof(*key_attrs));
 
 	key_class = CKO_CERTIFICATE;
-	key_attr[0].type = CKA_CLASS;
-	key_attr[0].pValue = &key_class;
-	key_attr[0].ulValueLen = sizeof(key_class);
+	for (i = 0; i < (int)p->filters_size; i++) {
+		/*
+		 * If the pkcs11 URI already has a class, change it to
+		 * CKO_CERTIFICATE.
+		 */
+		cur_attr = &key_attrs[i];
+		if (cur_attr->type == CKA_CLASS) {
+			cur_attr->pValue = &key_class;
+			cur_attr->ulValueLen = sizeof(key_class);
+			filter_with_class = 1;
+		}
+	}
+
+	if (!filter_with_class) {
+		key_attr[0].type = CKA_CLASS;
+		key_attr[0].pValue = &key_class;
+		key_attr[0].ulValueLen = sizeof(key_class);
+
+		key_attrs_count += 1;
+		memcpy(key_attrs + p->filters_size, key_attr,
+		    sizeof(*key_attrs));
+	}
 
 	session = p->slotinfo[slotidx].session;
 	f = p->function_list;
 
-	rv = f->C_FindObjectsInit(session, key_attr, 1);
+	rv = f->C_FindObjectsInit(session, key_attrs, key_attrs_count);
 	if (rv != CKR_OK) {
 		error("C_FindObjectsInit failed: %lu", rv);
 		goto fail;
@@ -1231,26 +1264,55 @@ pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
 {
 	struct sshkey		*key = NULL;
 	CK_OBJECT_CLASS		 key_class;
+	CK_ATTRIBUTE_PTR         cur_attr;
+	CK_ATTRIBUTE_PTR	 key_attrs;
+	size_t                   key_attrs_count;
 	CK_ATTRIBUTE		 key_attr[2];
 	CK_SESSION_HANDLE	 session;
 	CK_FUNCTION_LIST	*f = NULL;
 	CK_RV			 rv;
 	CK_OBJECT_HANDLE	 obj;
 	CK_ULONG		 n = 0;
-	int			 ret = -1;
+	int			 i, ret = -1;
+	int                     filter_with_class = 0;
+
+	key_attrs = xcalloc(p->filters_size + 1, sizeof(*key_attrs));
 
-	memset(&key_attr, 0, sizeof(key_attr));
 	memset(&obj, 0, sizeof(obj));
+	memset(&key_attr, 0, sizeof(key_attr));
+
+	key_attrs_count = p->filters_size;
+	memcpy(key_attrs, p->filters_attr,
+	    p->filters_size * sizeof(*key_attrs));
 
 	key_class = CKO_PUBLIC_KEY;
-	key_attr[0].type = CKA_CLASS;
-	key_attr[0].pValue = &key_class;
-	key_attr[0].ulValueLen = sizeof(key_class);
+	for (i = 0; i < (int)p->filters_size; i++) {
+		/*
+		 * If the pkcs11 URI already has a class, change it to
+		 * CKO_PUBLIC_KEY.
+		 */
+		cur_attr = &key_attrs[i];
+		if (cur_attr->type == CKA_CLASS) {
+			cur_attr->pValue = &key_class;
+			cur_attr->ulValueLen = sizeof(key_class);
+			filter_with_class = 1;
+		}
+	}
+
+	if (!filter_with_class) {
+		key_attr[0].type = CKA_CLASS;
+		key_attr[0].pValue = &key_class;
+		key_attr[0].ulValueLen = sizeof(key_class);
+
+		key_attrs_count += 1;
+		memcpy(key_attrs + p->filters_size, key_attr,
+		    sizeof(*key_attrs));
+	}
 
 	session = p->slotinfo[slotidx].session;
 	f = p->function_list;
 
-	rv = f->C_FindObjectsInit(session, key_attr, 1);
+	rv = f->C_FindObjectsInit(session, key_attrs, key_attrs_count);
 	if (rv != CKR_OK) {
 		error("C_FindObjectsInit failed: %lu", rv);
 		goto fail;
@@ -1533,13 +1595,14 @@ pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
 #endif /* WITH_PKCS11_KEYGEN */
 
 /*
- * register a new provider, fails if provider already exists. if
- * keyp is provided, fetch keys.
+ * register a new provider. fails if the provider already exists, unless
+ * allow_reuse is set to true. if keyp is provided, fetch keys.
  */
 static int
 pkcs11_register_provider(char *provider_id, char *pin,
     struct sshkey ***keyp, char ***labelsp,
-    struct pkcs11_provider **providerp, CK_ULONG user)
+    struct pkcs11_provider **providerp, CK_ULONG user,
+    int allow_reuse)
 {
 	int nkeys, need_finalize = 0;
 	int ret = -1;
@@ -1560,9 +1623,16 @@ pkcs11_register_provider(char *provider_id, char *pin,
 	if (labelsp != NULL)
 		*labelsp = NULL;
 
-	if (pkcs11_provider_lookup(provider_id) != NULL) {
+	p = pkcs11_provider_lookup(provider_id);
+	if (p != NULL) {
 		debug_f("provider already registered: %s", provider_id);
-		goto fail;
+
+		if (allow_reuse) {
+			*providerp = p;
+			return 0;
+		} else {
+			return -1;
+		}
 	}
 	if (lib_contains_symbol(provider_id, "C_GetFunctionList") != 0) {
 		error("provider %s is not a PKCS11 library", provider_id);
@@ -1651,6 +1721,15 @@ pkcs11_register_provider(char *provider_id, char *pin,
 		if ((ret = pkcs11_open_session(p, i, pin, user)) != 0 ||
 		    keyp == NULL)
 			continue;
+
+		/*
+		 * in case we want to use the same provider multiple times, do
+		 * not fetch the keys at that stage. they will be fetched later
+		 * on using pkcs11_get_key_provider with different filters.
+		 */
+		if (allow_reuse)
+			continue;
+
 		pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys);
 		pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys);
 		if (nkeys == 0 && !p->slotinfo[i].logged_in &&
@@ -1693,6 +1772,46 @@ fail:
 	return (ret);
 }
 
+static int
+pkcs11_get_key_provider(char *provider_id, struct pkcs11_provider *p,
+    struct sshkey ***keyp, char ***labelsp)
+{
+	int nkeys = 0;
+
+	for (CK_ULONG i = 0; i < p->nslots; i++) {
+		if (p->slotinfo[i].session <= 0)
+			continue;
+		pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys);
+	}
+
+	return (nkeys);
+}
+
+/*
+ * create or re-use a provider. fetch the provider keys using the given filters.
+ */
+int
+pkcs11_add_provider_with_filters(char *provider_id, char *pin,
+    struct sshkey ***keyp, char ***labelsp,
+    void *filters, size_t filters_size)
+{
+
+	struct pkcs11_provider *p = NULL;
+	int nkeys;
+
+	if (pkcs11_register_provider(provider_id, pin, keyp, labelsp, &p,
+	    CKU_USER, 1) != 0) {
+		error("create provider failed");
+	}
+	p->filters_attr = filters;
+	p->filters_size = filters_size;
+	nkeys = pkcs11_get_key_provider(provider_id, p, keyp, labelsp);
+	if (nkeys == 0)
+		debug_f("provider %s returned no keys", provider_id);
+
+	return (nkeys);
+}
+
 /*
  * register a new provider and get number of keys hold by the token,
  * fails if provider already exists
@@ -1705,7 +1824,7 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp,
 	int nkeys;
 
 	nkeys = pkcs11_register_provider(provider_id, pin, keyp, labelsp,
-	    &p, CKU_USER);
+	    &p, CKU_USER, 0);
 
 	/* no keys found or some other error, de-register provider */
 	if (nkeys <= 0 && p != NULL) {
@@ -1737,7 +1856,7 @@ pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label,
 	if ((p = pkcs11_provider_lookup(provider_id)) != NULL)
 		debug_f("provider \"%s\" available", provider_id);
 	else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, NULL,
-	    &p, CKU_SO)) < 0) {
+	    &p, CKU_SO, 0)) < 0) {
 		debug_f("could not register provider %s", provider_id);
 		goto out;
 	} else
@@ -1808,7 +1927,7 @@ pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx,
 	if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
 		debug_f("using provider \"%s\"", provider_id);
 	} else if (pkcs11_register_provider(provider_id, pin, NULL, NULL, &p,
-	    CKU_SO) < 0) {
+	    CKU_SO, 0) < 0) {
 		debug_f("could not register provider %s",
 		    provider_id);
 		goto out;
diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h
index 526022319..37688bad3 100644
--- a/ssh-pkcs11.h
+++ b/ssh-pkcs11.h
@@ -25,6 +25,8 @@
 int	pkcs11_init(int);
 void	pkcs11_terminate(void);
 int	pkcs11_add_provider(char *, char *, struct sshkey ***, char ***);
+int     pkcs11_add_provider_with_filters(char *, char *, struct sshkey ***,
+    char ***, void *, size_t);
 int	pkcs11_del_provider(char *);
 #ifdef WITH_PKCS11_KEYGEN
 struct sshkey *
diff --git a/sshd-pkcs11.c b/sshd-pkcs11.c
new file mode 100644
index 000000000..e9e8944e6
--- /dev/null
+++ b/sshd-pkcs11.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2024 Mathieu Othacehe <othacehe@xxxxxxx>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#ifdef ENABLE_PKCS11_WITH_P11KIT
+
+#define CRYPTOKI_COMPAT
+#include "pkcs11.h"
+
+#include <p11-kit/uri.h>
+
+#include "log.h"
+#include "sshkey.h"
+#include "ssh-pkcs11.h"
+#include "sshd-pkcs11.h"
+
+static int pkcs11_init_done = 0;
+
+int
+is_pkcs11_uri(const char *hostkey)
+{
+	P11KitUri *p11_uri;
+	int ret;
+
+	p11_uri = p11_kit_uri_new();
+	if (!p11_uri) {
+		error("failed to allocate PKCS11 URI");
+		return (0);
+	}
+
+	ret = p11_kit_uri_parse(hostkey, P11_KIT_URI_FOR_OBJECT, p11_uri);
+	return ret == P11_KIT_URI_OK;
+}
+
+/*
+ * parse the given PKCS#11 URI and return the key that is pointed out. fail if
+ * more than one key is found.
+ */
+int
+pkcs11_add_provider_from_uri(const char *uri, struct sshkey **keyp)
+{
+	P11KitUri *p11_uri;
+	struct sshkey **keys = NULL;
+	const char *pin, *provider;
+	CK_ATTRIBUTE_PTR filters_attr;
+	size_t filters_size;
+	int nkeys = 0, ret;
+
+	p11_uri = p11_kit_uri_new();
+	if (!p11_uri) {
+		error("failed to allocate PKCS11 URI");
+		return (0);
+	}
+
+	ret = p11_kit_uri_parse(uri, P11_KIT_URI_FOR_OBJECT, p11_uri);
+	if (ret != P11_KIT_URI_OK) {
+		error("failed to parse PKCS11 URI: %s", uri);
+		goto end;
+	}
+
+	pin = p11_kit_uri_get_pin_value(p11_uri);
+	provider = p11_kit_uri_get_module_path(p11_uri);
+	filters_attr = p11_kit_uri_get_attributes(p11_uri, &filters_size);
+
+	if (!pkcs11_init_done) {
+		ret = pkcs11_init(0);
+		if (ret)
+			goto end;
+
+		pkcs11_init_done = 1;
+	}
+
+	nkeys = pkcs11_add_provider_with_filters((char *)provider,
+	    (char *)pin,
+	    &keys, NULL,
+	    filters_attr, filters_size);
+
+ end:
+	p11_kit_uri_free(p11_uri);
+
+	if (nkeys != 1)
+		return (-1);
+
+	if (keyp)
+		*keyp = keys[0];
+
+	return (0);
+}
+#endif /* ENABLE_PKCS11_WITH_P11KIT */
diff --git a/sshd-pkcs11.h b/sshd-pkcs11.h
new file mode 100644
index 000000000..a44705fba
--- /dev/null
+++ b/sshd-pkcs11.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2024 Mathieu Othacehe <othacehe@xxxxxxx>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#ifndef SSHD_PKCS11_H
+#define SSHD_PKCS11_H
+
+#include "sshkey.h"
+
+int is_pkcs11_uri(const char *);
+int pkcs11_add_provider_from_uri(const char *, struct sshkey **);
+
+#endif
diff --git a/sshd-session.c b/sshd-session.c
index 4b79b9ba6..e1593b0a9 100644
--- a/sshd-session.c
+++ b/sshd-session.c
@@ -109,6 +109,9 @@
 #include "sk-api.h"
 #include "srclimit.h"
 #include "dh.h"
+#ifdef ENABLE_PKCS11_WITH_P11KIT
+#include "sshd-pkcs11.h"
+#endif
 
 /* Re-exec fds */
 #define REEXEC_DEVCRYPTO_RESERVED_FD	(STDERR_FILENO + 1)
@@ -146,6 +149,9 @@ static int saved_argc;
 int auth_sock = -1;
 static int have_agent = 0;
 
+/* pkcs11 support */
+static int have_pkcs11 = 0;
+
 /*
  * Any really sensitive data in the application is contained in this
  * structure. The idea is that this structure could be locked into memory so
@@ -676,6 +682,37 @@ usage(void)
 	exit(1);
 }
 
+#ifdef ENABLE_PKCS11_WITH_P11KIT
+static int parse_pkcs11_uri(u_int num_key, const u_char *hostkey)
+{
+	struct sshkey *key;
+	int ret;
+
+	if (!is_pkcs11_uri(hostkey))
+		return 0;
+
+	ret = pkcs11_add_provider_from_uri(hostkey, &key);
+	if (ret) {
+		fatal_f("unable to load host key \"%s\"", hostkey);
+		return -1;
+	}
+
+	/*
+	 * the public key read by the pkcs11 provider should be the same as the
+	 * one that was packed by sshd.
+	 */
+	if (!sshkey_equal(sensitive_data.host_pubkeys[num_key], key)) {
+		fatal_f("mismatch with parsed public key");
+		return -1;
+	}
+
+	have_pkcs11 = 1;
+	sensitive_data.host_pubkeys[num_key] = key;
+
+	return 0;
+}
+#endif
+
 static void
 parse_hostkeys(struct sshbuf *hostkeys)
 {
@@ -727,6 +764,14 @@ parse_hostkeys(struct sshbuf *hostkeys)
 		sensitive_data.host_certificates[num_keys] = k;
 		if (k)
 			debug2_f("cert %u: %s", num_keys, sshkey_ssh_name(k));
+		/* hostkey file */
+		k = NULL;
+		if ((r = sshbuf_get_string_direct(hostkeys, &cp, &len)) != 0)
+			fatal_fr(r, "extract hostkey file");
+#ifdef ENABLE_PKCS11_WITH_P11KIT
+		if (len != 0 && !sensitive_data.host_keys[num_keys])
+			parse_pkcs11_uri(num_keys, cp);
+#endif
 		num_keys++;
 	}
 	sensitive_data.num_hostkeys = num_keys;
@@ -1149,7 +1194,8 @@ main(int ac, char **av)
 
 	for (i = 0; i < options.num_host_key_files; i++) {
 		if (sensitive_data.host_keys[i] != NULL ||
-		    (have_agent && sensitive_data.host_pubkeys[i] != NULL)) {
+		    ((have_agent || have_pkcs11) &&
+		    sensitive_data.host_pubkeys[i] != NULL)) {
 			have_key = 1;
 			break;
 		}
diff --git a/sshd.c b/sshd.c
index df76dc78c..eb474fe3b 100644
--- a/sshd.c
+++ b/sshd.c
@@ -69,6 +69,9 @@
 #include <prot.h>
 #endif
 
+#ifdef ENABLE_PKCS11_WITH_P11KIT
+#include "sshd-pkcs11.h"
+#endif
 #include "xmalloc.h"
 #include "ssh.h"
 #include "sshpty.h"
@@ -674,6 +677,15 @@ pack_hostkeys(void)
 			if ((r = sshbuf_put_string(hostkeys, NULL, 0)) != 0)
 				fatal_fr(r, "compose host cert empty");
 		}
+		/* hostkey file */
+		if (options.host_key_files[i] != NULL) {
+			if ((r = sshbuf_put_cstring(hostkeys,
+			    options.host_key_files[i])) != 0)
+				fatal_fr(r, "compose hostkey file");
+		} else {
+			if ((r = sshbuf_put_string(hostkeys, NULL, 0)) != 0)
+				fatal_fr(r, "compose hostkey empty file");
+		}
 	}
 
 	sshbuf_free(keybuf);
@@ -712,6 +724,7 @@ send_rexec_state(int fd, struct sshbuf *conf)
 	 *		string private_key
 	 *		string public_key
 	 *		string certificate
+	 *		string host_key_file
 	 *	}
 	 *	string	included_files[] {
 	 *		string	selector
@@ -1479,6 +1492,27 @@ main(int ac, char **av)
 
 		if (options.host_key_files[i] == NULL)
 			continue;
+
+#ifdef ENABLE_PKCS11_WITH_P11KIT
+		if (!is_pkcs11_uri(options.host_key_files[i]))
+			goto no_pkcs11;
+
+		r = pkcs11_add_provider_from_uri(options.host_key_files[i],
+		    &key);
+		if (r) {
+			error("Unable to load host key \"%s\"",
+			    options.host_key_files[i]);
+			sensitive_data.host_keys[i] = NULL;
+			sensitive_data.host_pubkeys[i] = NULL;
+			continue;
+		}
+
+		sensitive_data.host_keys[i] = NULL;
+		sensitive_data.host_pubkeys[i] = key;
+		sensitive_data.have_ssh2_key = 1;
+		continue;
+ no_pkcs11:
+#endif
 		if ((r = sshkey_load_private(options.host_key_files[i], "",
 		    &key, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR)
 			do_log2_r(r, ll, "Unable to load host key \"%s\"",
diff --git a/sshd_config.5 b/sshd_config.5
index dbed44f2a..bb4ed54fa 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -836,6 +836,12 @@ and that the
 option restricts which of the keys are actually used by
 .Xr sshd 8 .
 .Pp
+The HostKey can contain a PKCS#11 URI, defined by the RFC 7512,
+pointing to a private host key. This URI must contain a path to the
+PKCS#11 module and the CKA_ID or CKA_LABEL of the host key.  The user
+pin code may also be supplied. For example,
+.Pa pkcs11:object=ssh_host_rsa_key?module-path=/usr/lib/my-pkcs11.so&pin-value=1234
+.Pp
 It is possible to have multiple host key files.
 It is also possible to specify public host key files instead.
 In this case operations on the private key will be delegated
-- 
2.34.1


--=-=-=
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@xxxxxxxxxxx
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev

--=-=-=--



[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux