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 --=-=-=--