1*5411e769Sdjm /* $OpenBSD: ssh-pkcs11-client.c,v 1.20 2024/08/15 00:51:51 djm Exp $ */ 241503fafSmarkus /* 341503fafSmarkus * Copyright (c) 2010 Markus Friedl. All rights reserved. 421f43f82Sdjm * Copyright (c) 2014 Pedro Martelletto. All rights reserved. 541503fafSmarkus * 641503fafSmarkus * Permission to use, copy, modify, and distribute this software for any 741503fafSmarkus * purpose with or without fee is hereby granted, provided that the above 841503fafSmarkus * copyright notice and this permission notice appear in all copies. 941503fafSmarkus * 1041503fafSmarkus * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1141503fafSmarkus * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1241503fafSmarkus * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1341503fafSmarkus * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1441503fafSmarkus * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1541503fafSmarkus * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1641503fafSmarkus * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1741503fafSmarkus */ 1841503fafSmarkus 1941503fafSmarkus #include <sys/types.h> 2041503fafSmarkus #include <sys/time.h> 2141503fafSmarkus #include <sys/socket.h> 2241503fafSmarkus 2341503fafSmarkus #include <stdarg.h> 2441503fafSmarkus #include <string.h> 2541503fafSmarkus #include <unistd.h> 2641503fafSmarkus #include <errno.h> 27c3ac409dSdjm #include <limits.h> 2841503fafSmarkus 2921f43f82Sdjm #include <openssl/ecdsa.h> 30ea2d8289Sdjm #include <openssl/rsa.h> 31ea2d8289Sdjm 3241503fafSmarkus #include "pathnames.h" 3341503fafSmarkus #include "xmalloc.h" 34d2107d3eSmarkus #include "sshbuf.h" 3541503fafSmarkus #include "log.h" 3641503fafSmarkus #include "misc.h" 37d2107d3eSmarkus #include "sshkey.h" 3841503fafSmarkus #include "authfd.h" 3941503fafSmarkus #include "atomicio.h" 4041503fafSmarkus #include "ssh-pkcs11.h" 41d2107d3eSmarkus #include "ssherr.h" 4241503fafSmarkus 4341503fafSmarkus /* borrows code from sftp-server and ssh-agent */ 4441503fafSmarkus 45c3ac409dSdjm /* 46c3ac409dSdjm * Maintain a list of ssh-pkcs11-helper subprocesses. These may be looked up 47c3ac409dSdjm * by provider path or their unique EC/RSA METHOD pointers. 48c3ac409dSdjm */ 49c3ac409dSdjm struct helper { 50c3ac409dSdjm char *path; 51c3ac409dSdjm pid_t pid; 52c3ac409dSdjm int fd; 53c3ac409dSdjm RSA_METHOD *rsa_meth; 54c3ac409dSdjm EC_KEY_METHOD *ec_meth; 55c3ac409dSdjm int (*rsa_finish)(RSA *rsa); 56c3ac409dSdjm void (*ec_finish)(EC_KEY *key); 57c3ac409dSdjm size_t nrsa, nec; /* number of active keys of each type */ 58c3ac409dSdjm }; 59c3ac409dSdjm static struct helper **helpers; 60c3ac409dSdjm static size_t nhelpers; 61c3ac409dSdjm 62c3ac409dSdjm static struct helper * 63c3ac409dSdjm helper_by_provider(const char *path) 64c3ac409dSdjm { 65c3ac409dSdjm size_t i; 66c3ac409dSdjm 67c3ac409dSdjm for (i = 0; i < nhelpers; i++) { 68c3ac409dSdjm if (helpers[i] == NULL || helpers[i]->path == NULL || 69c3ac409dSdjm helpers[i]->fd == -1) 70c3ac409dSdjm continue; 71c3ac409dSdjm if (strcmp(helpers[i]->path, path) == 0) 72c3ac409dSdjm return helpers[i]; 73c3ac409dSdjm } 74c3ac409dSdjm return NULL; 75c3ac409dSdjm } 76c3ac409dSdjm 77c3ac409dSdjm static struct helper * 78c3ac409dSdjm helper_by_rsa(const RSA *rsa) 79c3ac409dSdjm { 80c3ac409dSdjm size_t i; 81c3ac409dSdjm const RSA_METHOD *meth; 82c3ac409dSdjm 83c3ac409dSdjm if ((meth = RSA_get_method(rsa)) == NULL) 84c3ac409dSdjm return NULL; 85c3ac409dSdjm for (i = 0; i < nhelpers; i++) { 86c3ac409dSdjm if (helpers[i] != NULL && helpers[i]->rsa_meth == meth) 87c3ac409dSdjm return helpers[i]; 88c3ac409dSdjm } 89c3ac409dSdjm return NULL; 90c3ac409dSdjm 91c3ac409dSdjm } 92c3ac409dSdjm 93c3ac409dSdjm static struct helper * 94c3ac409dSdjm helper_by_ec(const EC_KEY *ec) 95c3ac409dSdjm { 96c3ac409dSdjm size_t i; 97c3ac409dSdjm const EC_KEY_METHOD *meth; 98c3ac409dSdjm 99c3ac409dSdjm if ((meth = EC_KEY_get_method(ec)) == NULL) 100c3ac409dSdjm return NULL; 101c3ac409dSdjm for (i = 0; i < nhelpers; i++) { 102c3ac409dSdjm if (helpers[i] != NULL && helpers[i]->ec_meth == meth) 103c3ac409dSdjm return helpers[i]; 104c3ac409dSdjm } 105c3ac409dSdjm return NULL; 106c3ac409dSdjm 107c3ac409dSdjm } 10841503fafSmarkus 10941503fafSmarkus static void 110c3ac409dSdjm helper_free(struct helper *helper) 111c3ac409dSdjm { 112c3ac409dSdjm size_t i; 113c3ac409dSdjm int found = 0; 114c3ac409dSdjm 115c3ac409dSdjm if (helper == NULL) 116c3ac409dSdjm return; 117c3ac409dSdjm if (helper->path == NULL || helper->ec_meth == NULL || 118c3ac409dSdjm helper->rsa_meth == NULL) 119c3ac409dSdjm fatal_f("inconsistent helper"); 120c3ac409dSdjm debug3_f("free helper for provider %s", helper->path); 121c3ac409dSdjm for (i = 0; i < nhelpers; i++) { 122c3ac409dSdjm if (helpers[i] == helper) { 123c3ac409dSdjm if (found) 124c3ac409dSdjm fatal_f("helper recorded more than once"); 125c3ac409dSdjm found = 1; 126c3ac409dSdjm } 127c3ac409dSdjm else if (found) 128c3ac409dSdjm helpers[i - 1] = helpers[i]; 129c3ac409dSdjm } 130c3ac409dSdjm if (found) { 131c3ac409dSdjm helpers = xrecallocarray(helpers, nhelpers, 132c3ac409dSdjm nhelpers - 1, sizeof(*helpers)); 133c3ac409dSdjm nhelpers--; 134c3ac409dSdjm } 135c3ac409dSdjm free(helper->path); 136c3ac409dSdjm EC_KEY_METHOD_free(helper->ec_meth); 137c3ac409dSdjm RSA_meth_free(helper->rsa_meth); 138c3ac409dSdjm free(helper); 139c3ac409dSdjm } 140c3ac409dSdjm 141c3ac409dSdjm static void 142c3ac409dSdjm helper_terminate(struct helper *helper) 143c3ac409dSdjm { 144c3ac409dSdjm if (helper == NULL) { 145c3ac409dSdjm return; 146c3ac409dSdjm } else if (helper->fd == -1) { 147c3ac409dSdjm debug3_f("already terminated"); 148c3ac409dSdjm } else { 149c3ac409dSdjm debug3_f("terminating helper for %s; " 150c3ac409dSdjm "remaining %zu RSA %zu ECDSA", 151c3ac409dSdjm helper->path, helper->nrsa, helper->nec); 152c3ac409dSdjm close(helper->fd); 153c3ac409dSdjm /* XXX waitpid() */ 154c3ac409dSdjm helper->fd = -1; 155c3ac409dSdjm helper->pid = -1; 156c3ac409dSdjm } 157c3ac409dSdjm /* 158c3ac409dSdjm * Don't delete the helper entry until there are no remaining keys 159c3ac409dSdjm * that reference it. Otherwise, any signing operation would call 160c3ac409dSdjm * a free'd METHOD pointer and that would be bad. 161c3ac409dSdjm */ 162c3ac409dSdjm if (helper->nrsa == 0 && helper->nec == 0) 163c3ac409dSdjm helper_free(helper); 164c3ac409dSdjm } 165c3ac409dSdjm 166c3ac409dSdjm static void 167c3ac409dSdjm send_msg(int fd, struct sshbuf *m) 16841503fafSmarkus { 16941503fafSmarkus u_char buf[4]; 170d2107d3eSmarkus size_t mlen = sshbuf_len(m); 171d2107d3eSmarkus int r; 17241503fafSmarkus 173c3ac409dSdjm if (fd == -1) 174c3ac409dSdjm return; 175d2107d3eSmarkus POKE_U32(buf, mlen); 17641503fafSmarkus if (atomicio(vwrite, fd, buf, 4) != 4 || 17725fe41faSmarkus atomicio(vwrite, fd, sshbuf_mutable_ptr(m), 178d2107d3eSmarkus sshbuf_len(m)) != sshbuf_len(m)) 17941503fafSmarkus error("write to helper failed"); 180d2107d3eSmarkus if ((r = sshbuf_consume(m, mlen)) != 0) 18148e6b99dSdjm fatal_fr(r, "consume"); 18241503fafSmarkus } 18341503fafSmarkus 18441503fafSmarkus static int 185c3ac409dSdjm recv_msg(int fd, struct sshbuf *m) 18641503fafSmarkus { 18741503fafSmarkus u_int l, len; 188d2107d3eSmarkus u_char c, buf[1024]; 189d2107d3eSmarkus int r; 19041503fafSmarkus 191c3ac409dSdjm sshbuf_reset(m); 192c3ac409dSdjm if (fd == -1) 193c3ac409dSdjm return 0; /* XXX */ 19441503fafSmarkus if ((len = atomicio(read, fd, buf, 4)) != 4) { 19541503fafSmarkus error("read from helper failed: %u", len); 19641503fafSmarkus return (0); /* XXX */ 19741503fafSmarkus } 198d2107d3eSmarkus len = PEEK_U32(buf); 19941503fafSmarkus if (len > 256 * 1024) 20041503fafSmarkus fatal("response too long: %u", len); 20141503fafSmarkus /* read len bytes into m */ 20241503fafSmarkus while (len > 0) { 20341503fafSmarkus l = len; 20441503fafSmarkus if (l > sizeof(buf)) 20541503fafSmarkus l = sizeof(buf); 20641503fafSmarkus if (atomicio(read, fd, buf, l) != l) { 20741503fafSmarkus error("response from helper failed."); 20841503fafSmarkus return (0); /* XXX */ 20941503fafSmarkus } 210d2107d3eSmarkus if ((r = sshbuf_put(m, buf, l)) != 0) 21148e6b99dSdjm fatal_fr(r, "sshbuf_put"); 21241503fafSmarkus len -= l; 21341503fafSmarkus } 214d2107d3eSmarkus if ((r = sshbuf_get_u8(m, &c)) != 0) 21548e6b99dSdjm fatal_fr(r, "parse type"); 216d2107d3eSmarkus return c; 21741503fafSmarkus } 21841503fafSmarkus 21941503fafSmarkus int 22041503fafSmarkus pkcs11_init(int interactive) 22141503fafSmarkus { 222c3ac409dSdjm return 0; 22341503fafSmarkus } 22441503fafSmarkus 22541503fafSmarkus void 22641503fafSmarkus pkcs11_terminate(void) 22741503fafSmarkus { 228c3ac409dSdjm size_t i; 229c3ac409dSdjm 230c3ac409dSdjm debug3_f("terminating %zu helpers", nhelpers); 231c3ac409dSdjm for (i = 0; i < nhelpers; i++) 232c3ac409dSdjm helper_terminate(helpers[i]); 23341503fafSmarkus } 23441503fafSmarkus 23541503fafSmarkus static int 23621f43f82Sdjm rsa_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, int padding) 23741503fafSmarkus { 2384aabd04fSdjm struct sshkey *key = NULL; 2394aabd04fSdjm struct sshbuf *msg = NULL; 2404aabd04fSdjm u_char *blob = NULL, *signature = NULL; 241d2107d3eSmarkus size_t blen, slen = 0; 242d2107d3eSmarkus int r, ret = -1; 243c3ac409dSdjm struct helper *helper; 24441503fafSmarkus 245c3ac409dSdjm if ((helper = helper_by_rsa(rsa)) == NULL || helper->fd == -1) 246c3ac409dSdjm fatal_f("no helper for PKCS11 key"); 247c3ac409dSdjm debug3_f("signing with PKCS11 provider %s", helper->path); 24841503fafSmarkus if (padding != RSA_PKCS1_PADDING) 2494aabd04fSdjm goto fail; 250*5411e769Sdjm if ((key = sshkey_new(KEY_UNSPEC)) == NULL) { 25148e6b99dSdjm error_f("sshkey_new failed"); 2524aabd04fSdjm goto fail; 2534aabd04fSdjm } 254*5411e769Sdjm if ((key->pkey = EVP_PKEY_new()) == NULL || 255*5411e769Sdjm EVP_PKEY_set1_RSA(key->pkey, rsa) != 1) { 256*5411e769Sdjm error_f("pkey setup failed"); 257*5411e769Sdjm goto fail; 258*5411e769Sdjm } 259*5411e769Sdjm 2604aabd04fSdjm key->type = KEY_RSA; 2614aabd04fSdjm if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) { 26248e6b99dSdjm error_fr(r, "encode key"); 2634aabd04fSdjm goto fail; 264d2107d3eSmarkus } 265d2107d3eSmarkus if ((msg = sshbuf_new()) == NULL) 26648e6b99dSdjm fatal_f("sshbuf_new failed"); 267d2107d3eSmarkus if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || 268d2107d3eSmarkus (r = sshbuf_put_string(msg, blob, blen)) != 0 || 269d2107d3eSmarkus (r = sshbuf_put_string(msg, from, flen)) != 0 || 270d2107d3eSmarkus (r = sshbuf_put_u32(msg, 0)) != 0) 27148e6b99dSdjm fatal_fr(r, "compose"); 272c3ac409dSdjm send_msg(helper->fd, msg); 273d2107d3eSmarkus sshbuf_reset(msg); 27441503fafSmarkus 275c3ac409dSdjm if (recv_msg(helper->fd, msg) == SSH2_AGENT_SIGN_RESPONSE) { 276d2107d3eSmarkus if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0) 27748e6b99dSdjm fatal_fr(r, "parse"); 278d2107d3eSmarkus if (slen <= (size_t)RSA_size(rsa)) { 27941503fafSmarkus memcpy(to, signature, slen); 28041503fafSmarkus ret = slen; 28141503fafSmarkus } 2820d40fefdSdjm free(signature); 28341503fafSmarkus } 2844aabd04fSdjm fail: 2854aabd04fSdjm free(blob); 2864aabd04fSdjm sshkey_free(key); 287d2107d3eSmarkus sshbuf_free(msg); 28841503fafSmarkus return (ret); 28941503fafSmarkus } 29041503fafSmarkus 291c3ac409dSdjm static int 292c3ac409dSdjm rsa_finish(RSA *rsa) 293c3ac409dSdjm { 294c3ac409dSdjm struct helper *helper; 295c3ac409dSdjm 296c3ac409dSdjm if ((helper = helper_by_rsa(rsa)) == NULL) 297c3ac409dSdjm fatal_f("no helper for PKCS11 key"); 298c3ac409dSdjm debug3_f("free PKCS11 RSA key for provider %s", helper->path); 299c3ac409dSdjm if (helper->rsa_finish != NULL) 300c3ac409dSdjm helper->rsa_finish(rsa); 301c3ac409dSdjm if (helper->nrsa == 0) 302c3ac409dSdjm fatal_f("RSA refcount error"); 303c3ac409dSdjm helper->nrsa--; 304c3ac409dSdjm debug3_f("provider %s remaining keys: %zu RSA %zu ECDSA", 305c3ac409dSdjm helper->path, helper->nrsa, helper->nec); 306c3ac409dSdjm if (helper->nrsa == 0 && helper->nec == 0) 307c3ac409dSdjm helper_terminate(helper); 308c3ac409dSdjm return 1; 309c3ac409dSdjm } 310c3ac409dSdjm 31121f43f82Sdjm static ECDSA_SIG * 31221f43f82Sdjm ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, 31321f43f82Sdjm const BIGNUM *rp, EC_KEY *ec) 31441503fafSmarkus { 3154aabd04fSdjm struct sshkey *key = NULL; 3164aabd04fSdjm struct sshbuf *msg = NULL; 31721f43f82Sdjm ECDSA_SIG *ret = NULL; 3184aabd04fSdjm const u_char *cp; 3194aabd04fSdjm u_char *blob = NULL, *signature = NULL; 3204aabd04fSdjm size_t blen, slen = 0; 3214aabd04fSdjm int r, nid; 322c3ac409dSdjm struct helper *helper; 32321f43f82Sdjm 324c3ac409dSdjm if ((helper = helper_by_ec(ec)) == NULL || helper->fd == -1) 325c3ac409dSdjm fatal_f("no helper for PKCS11 key"); 326c3ac409dSdjm debug3_f("signing with PKCS11 provider %s", helper->path); 3274aabd04fSdjm 328*5411e769Sdjm if ((key = sshkey_new(KEY_UNSPEC)) == NULL) { 32948e6b99dSdjm error_f("sshkey_new failed"); 3304aabd04fSdjm goto fail; 3314aabd04fSdjm } 332*5411e769Sdjm if ((key->pkey = EVP_PKEY_new()) == NULL || 333*5411e769Sdjm EVP_PKEY_set1_EC_KEY(key->pkey, ec) != 1) { 334*5411e769Sdjm error("pkey setup failed"); 335*5411e769Sdjm goto fail; 336*5411e769Sdjm } 337*5411e769Sdjm if ((nid = sshkey_ecdsa_pkey_to_nid(key->pkey)) < 0) { 338*5411e769Sdjm error("couldn't get curve nid"); 339*5411e769Sdjm goto fail; 340*5411e769Sdjm } 3414aabd04fSdjm key->ecdsa_nid = nid; 3424aabd04fSdjm key->type = KEY_ECDSA; 3434aabd04fSdjm 3444aabd04fSdjm if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) { 34548e6b99dSdjm error_fr(r, "encode key"); 3464aabd04fSdjm goto fail; 34721f43f82Sdjm } 34821f43f82Sdjm if ((msg = sshbuf_new()) == NULL) 34948e6b99dSdjm fatal_f("sshbuf_new failed"); 35021f43f82Sdjm if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || 35121f43f82Sdjm (r = sshbuf_put_string(msg, blob, blen)) != 0 || 35221f43f82Sdjm (r = sshbuf_put_string(msg, dgst, dgst_len)) != 0 || 35321f43f82Sdjm (r = sshbuf_put_u32(msg, 0)) != 0) 35448e6b99dSdjm fatal_fr(r, "compose"); 355c3ac409dSdjm send_msg(helper->fd, msg); 35621f43f82Sdjm sshbuf_reset(msg); 35721f43f82Sdjm 358c3ac409dSdjm if (recv_msg(helper->fd, msg) == SSH2_AGENT_SIGN_RESPONSE) { 35921f43f82Sdjm if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0) 36048e6b99dSdjm fatal_fr(r, "parse"); 36121f43f82Sdjm cp = signature; 36221f43f82Sdjm ret = d2i_ECDSA_SIG(NULL, &cp, slen); 36321f43f82Sdjm free(signature); 36421f43f82Sdjm } 36521f43f82Sdjm 3664aabd04fSdjm fail: 3674aabd04fSdjm free(blob); 3684aabd04fSdjm sshkey_free(key); 36921f43f82Sdjm sshbuf_free(msg); 37021f43f82Sdjm return (ret); 37121f43f82Sdjm } 37221f43f82Sdjm 373c3ac409dSdjm static void 374c3ac409dSdjm ecdsa_do_finish(EC_KEY *ec) 375c3ac409dSdjm { 376c3ac409dSdjm struct helper *helper; 377c3ac409dSdjm 378c3ac409dSdjm if ((helper = helper_by_ec(ec)) == NULL) 379c3ac409dSdjm fatal_f("no helper for PKCS11 key"); 380c3ac409dSdjm debug3_f("free PKCS11 ECDSA key for provider %s", helper->path); 381c3ac409dSdjm if (helper->ec_finish != NULL) 382c3ac409dSdjm helper->ec_finish(ec); 383c3ac409dSdjm if (helper->nec == 0) 384c3ac409dSdjm fatal_f("ECDSA refcount error"); 385c3ac409dSdjm helper->nec--; 386c3ac409dSdjm debug3_f("provider %s remaining keys: %zu RSA %zu ECDSA", 387c3ac409dSdjm helper->path, helper->nrsa, helper->nec); 388c3ac409dSdjm if (helper->nrsa == 0 && helper->nec == 0) 389c3ac409dSdjm helper_terminate(helper); 390c3ac409dSdjm } 39121f43f82Sdjm 39221f43f82Sdjm /* redirect private key crypto operations to the ssh-pkcs11-helper */ 39321f43f82Sdjm static void 394c3ac409dSdjm wrap_key(struct helper *helper, struct sshkey *k) 39521f43f82Sdjm { 396*5411e769Sdjm RSA *rsa = NULL; 397*5411e769Sdjm EC_KEY *ecdsa = NULL; 398*5411e769Sdjm 399c3ac409dSdjm debug3_f("wrap %s for provider %s", sshkey_type(k), helper->path); 400c3ac409dSdjm if (k->type == KEY_RSA) { 401*5411e769Sdjm if ((rsa = EVP_PKEY_get1_RSA(k->pkey)) == NULL) 402*5411e769Sdjm fatal_f("no RSA key"); 403*5411e769Sdjm if (RSA_set_method(rsa, helper->rsa_meth) != 1) 404*5411e769Sdjm fatal_f("RSA_set_method failed"); 405c3ac409dSdjm if (helper->nrsa++ >= INT_MAX) 406c3ac409dSdjm fatal_f("RSA refcount error"); 407*5411e769Sdjm if (EVP_PKEY_set1_RSA(k->pkey, rsa) != 1) 408*5411e769Sdjm fatal_f("EVP_PKEY_set1_RSA failed"); 409*5411e769Sdjm RSA_free(rsa); 410c3ac409dSdjm } else if (k->type == KEY_ECDSA) { 411*5411e769Sdjm if ((ecdsa = EVP_PKEY_get1_EC_KEY(k->pkey)) == NULL) 412*5411e769Sdjm fatal_f("no ECDSA key"); 413*5411e769Sdjm if (EC_KEY_set_method(ecdsa, helper->ec_meth) != 1) 414*5411e769Sdjm fatal_f("EC_KEY_set_method failed"); 415c3ac409dSdjm if (helper->nec++ >= INT_MAX) 416c3ac409dSdjm fatal_f("EC refcount error"); 417*5411e769Sdjm if (EVP_PKEY_set1_EC_KEY(k->pkey, ecdsa) != 1) 418*5411e769Sdjm fatal_f("EVP_PKEY_set1_EC_KEY failed"); 419*5411e769Sdjm EC_KEY_free(ecdsa); 420c3ac409dSdjm } else 42148e6b99dSdjm fatal_f("unknown key type"); 422c3ac409dSdjm k->flags |= SSHKEY_FLAG_EXT; 423c3ac409dSdjm debug3_f("provider %s remaining keys: %zu RSA %zu ECDSA", 424c3ac409dSdjm helper->path, helper->nrsa, helper->nec); 42521f43f82Sdjm } 42621f43f82Sdjm 427eead3eb2Sdjm /* 428eead3eb2Sdjm * Make a private PKCS#11-backed certificate by grafting a previously-loaded 429eead3eb2Sdjm * PKCS#11 private key and a public certificate key. 430eead3eb2Sdjm */ 431eead3eb2Sdjm int 432eead3eb2Sdjm pkcs11_make_cert(const struct sshkey *priv, 433eead3eb2Sdjm const struct sshkey *certpub, struct sshkey **certprivp) 434eead3eb2Sdjm { 435eead3eb2Sdjm struct helper *helper = NULL; 436eead3eb2Sdjm struct sshkey *ret; 437eead3eb2Sdjm int r; 438*5411e769Sdjm RSA *rsa_priv = NULL, *rsa_cert = NULL; 439*5411e769Sdjm EC_KEY *ec_priv = NULL, *ec_cert = NULL; 440eead3eb2Sdjm 441eead3eb2Sdjm debug3_f("private key type %s cert type %s", sshkey_type(priv), 442eead3eb2Sdjm sshkey_type(certpub)); 443eead3eb2Sdjm *certprivp = NULL; 444eead3eb2Sdjm if (!sshkey_is_cert(certpub) || sshkey_is_cert(priv) || 445eead3eb2Sdjm !sshkey_equal_public(priv, certpub)) { 446eead3eb2Sdjm error_f("private key %s doesn't match cert %s", 447eead3eb2Sdjm sshkey_type(priv), sshkey_type(certpub)); 448eead3eb2Sdjm return SSH_ERR_INVALID_ARGUMENT; 449eead3eb2Sdjm } 450eead3eb2Sdjm *certprivp = NULL; 451eead3eb2Sdjm if (priv->type == KEY_RSA) { 452*5411e769Sdjm if ((rsa_priv = EVP_PKEY_get1_RSA(priv->pkey)) == NULL) 453*5411e769Sdjm fatal_f("no RSA pkey"); 454*5411e769Sdjm if ((helper = helper_by_rsa(rsa_priv)) == NULL || 455eead3eb2Sdjm helper->fd == -1) 456eead3eb2Sdjm fatal_f("no helper for PKCS11 RSA key"); 457eead3eb2Sdjm if ((r = sshkey_from_private(priv, &ret)) != 0) 458eead3eb2Sdjm fatal_fr(r, "copy key"); 459*5411e769Sdjm if ((rsa_cert = EVP_PKEY_get1_RSA(ret->pkey)) == NULL) 460*5411e769Sdjm fatal_f("no RSA cert pkey"); 461*5411e769Sdjm if (RSA_set_method(rsa_cert, helper->rsa_meth) != 1) 462*5411e769Sdjm fatal_f("RSA_set_method failed"); 463eead3eb2Sdjm if (helper->nrsa++ >= INT_MAX) 464eead3eb2Sdjm fatal_f("RSA refcount error"); 465*5411e769Sdjm if (EVP_PKEY_set1_RSA(ret->pkey, rsa_cert) != 1) 466*5411e769Sdjm fatal_f("EVP_PKEY_set1_RSA failed"); 467*5411e769Sdjm RSA_free(rsa_priv); 468*5411e769Sdjm RSA_free(rsa_cert); 469eead3eb2Sdjm } else if (priv->type == KEY_ECDSA) { 470*5411e769Sdjm if ((ec_priv = EVP_PKEY_get1_EC_KEY(priv->pkey)) == NULL) 471*5411e769Sdjm fatal_f("no EC pkey"); 472*5411e769Sdjm if ((helper = helper_by_ec(ec_priv)) == NULL || 473eead3eb2Sdjm helper->fd == -1) 474eead3eb2Sdjm fatal_f("no helper for PKCS11 EC key"); 475eead3eb2Sdjm if ((r = sshkey_from_private(priv, &ret)) != 0) 476eead3eb2Sdjm fatal_fr(r, "copy key"); 477*5411e769Sdjm if ((ec_cert = EVP_PKEY_get1_EC_KEY(ret->pkey)) == NULL) 478*5411e769Sdjm fatal_f("no EC cert pkey"); 479*5411e769Sdjm if (EC_KEY_set_method(ec_cert, helper->ec_meth) != 1) 480*5411e769Sdjm fatal_f("EC_KEY_set_method failed"); 481eead3eb2Sdjm if (helper->nec++ >= INT_MAX) 482eead3eb2Sdjm fatal_f("EC refcount error"); 483*5411e769Sdjm if (EVP_PKEY_set1_EC_KEY(ret->pkey, ec_cert) != 1) 484*5411e769Sdjm fatal_f("EVP_PKEY_set1_EC_KEY failed"); 485*5411e769Sdjm EC_KEY_free(ec_priv); 486*5411e769Sdjm EC_KEY_free(ec_cert); 487eead3eb2Sdjm } else 488eead3eb2Sdjm fatal_f("unknown key type %s", sshkey_type(priv)); 489eead3eb2Sdjm 490eead3eb2Sdjm ret->flags |= SSHKEY_FLAG_EXT; 491eead3eb2Sdjm if ((r = sshkey_to_certified(ret)) != 0 || 492eead3eb2Sdjm (r = sshkey_cert_copy(certpub, ret)) != 0) 493eead3eb2Sdjm fatal_fr(r, "graft certificate"); 494eead3eb2Sdjm debug3_f("provider %s remaining keys: %zu RSA %zu ECDSA", 495eead3eb2Sdjm helper->path, helper->nrsa, helper->nec); 496eead3eb2Sdjm /* success */ 497eead3eb2Sdjm *certprivp = ret; 498eead3eb2Sdjm return 0; 499eead3eb2Sdjm } 500eead3eb2Sdjm 50121f43f82Sdjm static int 502c3ac409dSdjm pkcs11_start_helper_methods(struct helper *helper) 50321f43f82Sdjm { 504c3ac409dSdjm int (*ec_init)(EC_KEY *key); 505c3ac409dSdjm int (*ec_copy)(EC_KEY *dest, const EC_KEY *src); 506c3ac409dSdjm int (*ec_set_group)(EC_KEY *key, const EC_GROUP *grp); 507c3ac409dSdjm int (*ec_set_private)(EC_KEY *key, const BIGNUM *priv_key); 508c3ac409dSdjm int (*ec_set_public)(EC_KEY *key, const EC_POINT *pub_key); 509c3ac409dSdjm int (*ec_sign)(int, const unsigned char *, int, unsigned char *, 51021f43f82Sdjm unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL; 511c3ac409dSdjm RSA_METHOD *rsa_meth; 512c3ac409dSdjm EC_KEY_METHOD *ec_meth; 51341503fafSmarkus 514c3ac409dSdjm if ((ec_meth = EC_KEY_METHOD_new(EC_KEY_OpenSSL())) == NULL) 515c3ac409dSdjm return -1; 516c3ac409dSdjm EC_KEY_METHOD_get_sign(ec_meth, &ec_sign, NULL, NULL); 517c3ac409dSdjm EC_KEY_METHOD_set_sign(ec_meth, ec_sign, NULL, ecdsa_do_sign); 518c3ac409dSdjm EC_KEY_METHOD_get_init(ec_meth, &ec_init, &helper->ec_finish, 519c3ac409dSdjm &ec_copy, &ec_set_group, &ec_set_private, &ec_set_public); 520c3ac409dSdjm EC_KEY_METHOD_set_init(ec_meth, ec_init, ecdsa_do_finish, 521c3ac409dSdjm ec_copy, ec_set_group, ec_set_private, ec_set_public); 522c3ac409dSdjm 523c3ac409dSdjm if ((rsa_meth = RSA_meth_dup(RSA_get_default_method())) == NULL) 52448e6b99dSdjm fatal_f("RSA_meth_dup failed"); 525c3ac409dSdjm helper->rsa_finish = RSA_meth_get_finish(rsa_meth); 526c3ac409dSdjm if (!RSA_meth_set1_name(rsa_meth, "ssh-pkcs11-helper") || 527c3ac409dSdjm !RSA_meth_set_priv_enc(rsa_meth, rsa_encrypt) || 528c3ac409dSdjm !RSA_meth_set_finish(rsa_meth, rsa_finish)) 52948e6b99dSdjm fatal_f("failed to prepare method"); 53021f43f82Sdjm 531c3ac409dSdjm helper->ec_meth = ec_meth; 532c3ac409dSdjm helper->rsa_meth = rsa_meth; 533c3ac409dSdjm return 0; 53441503fafSmarkus } 53541503fafSmarkus 536c3ac409dSdjm static struct helper * 537c3ac409dSdjm pkcs11_start_helper(const char *path) 53841503fafSmarkus { 53941503fafSmarkus int pair[2]; 540c3ac409dSdjm char *prog, *verbosity = NULL; 541c3ac409dSdjm struct helper *helper; 542c3ac409dSdjm pid_t pid; 5439f07e697Sdjm 544c3ac409dSdjm if (nhelpers >= INT_MAX) 545c3ac409dSdjm fatal_f("too many helpers"); 546c3ac409dSdjm debug3_f("start helper for %s", path); 54741503fafSmarkus if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { 548c3ac409dSdjm error_f("socketpair: %s", strerror(errno)); 549c3ac409dSdjm return NULL; 550c3ac409dSdjm } 551c3ac409dSdjm helper = xcalloc(1, sizeof(*helper)); 552c3ac409dSdjm if (pkcs11_start_helper_methods(helper) == -1) { 553c3ac409dSdjm error_f("pkcs11_start_helper_methods failed"); 554c3ac409dSdjm goto fail; 55541503fafSmarkus } 55641503fafSmarkus if ((pid = fork()) == -1) { 557c3ac409dSdjm error_f("fork: %s", strerror(errno)); 558c3ac409dSdjm fail: 559c3ac409dSdjm close(pair[0]); 560c3ac409dSdjm close(pair[1]); 561c3ac409dSdjm RSA_meth_free(helper->rsa_meth); 562c3ac409dSdjm EC_KEY_METHOD_free(helper->ec_meth); 563c3ac409dSdjm free(helper); 564c3ac409dSdjm return NULL; 56541503fafSmarkus } else if (pid == 0) { 56641503fafSmarkus if ((dup2(pair[1], STDIN_FILENO) == -1) || 56741503fafSmarkus (dup2(pair[1], STDOUT_FILENO) == -1)) { 56841503fafSmarkus fprintf(stderr, "dup2: %s\n", strerror(errno)); 56941503fafSmarkus _exit(1); 57041503fafSmarkus } 57141503fafSmarkus close(pair[0]); 57241503fafSmarkus close(pair[1]); 573c3ac409dSdjm prog = getenv("SSH_PKCS11_HELPER"); 574c3ac409dSdjm if (prog == NULL || strlen(prog) == 0) 575c3ac409dSdjm prog = _PATH_SSH_PKCS11_HELPER; 576c3ac409dSdjm if (log_level_get() >= SYSLOG_LEVEL_DEBUG1) 577c3ac409dSdjm verbosity = "-vvv"; 578c3ac409dSdjm debug_f("starting %s %s", prog, 5799f07e697Sdjm verbosity == NULL ? "" : verbosity); 580c3ac409dSdjm execlp(prog, prog, verbosity, (char *)NULL); 581c3ac409dSdjm fprintf(stderr, "exec: %s: %s\n", prog, strerror(errno)); 58241503fafSmarkus _exit(1); 58341503fafSmarkus } 58441503fafSmarkus close(pair[1]); 585c3ac409dSdjm helper->fd = pair[0]; 586c3ac409dSdjm helper->path = xstrdup(path); 587c3ac409dSdjm helper->pid = pid; 588c3ac409dSdjm debug3_f("helper %zu for \"%s\" on fd %d pid %ld", nhelpers, 589c3ac409dSdjm helper->path, helper->fd, (long)helper->pid); 590c3ac409dSdjm helpers = xrecallocarray(helpers, nhelpers, 591c3ac409dSdjm nhelpers + 1, sizeof(*helpers)); 592c3ac409dSdjm helpers[nhelpers++] = helper; 593c3ac409dSdjm return helper; 59441503fafSmarkus } 59541503fafSmarkus 59641503fafSmarkus int 59744e54ccbSdjm pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp, 59844e54ccbSdjm char ***labelsp) 59941503fafSmarkus { 60040a7db51Smarkus struct sshkey *k; 60121f43f82Sdjm int r, type; 60241503fafSmarkus u_char *blob; 60344e54ccbSdjm char *label; 604d2107d3eSmarkus size_t blen; 605d2107d3eSmarkus u_int nkeys, i; 606d2107d3eSmarkus struct sshbuf *msg; 607c3ac409dSdjm struct helper *helper; 60841503fafSmarkus 609c3ac409dSdjm if ((helper = helper_by_provider(name)) == NULL && 610c3ac409dSdjm (helper = pkcs11_start_helper(name)) == NULL) 611c3ac409dSdjm return -1; 61241503fafSmarkus 613d2107d3eSmarkus if ((msg = sshbuf_new()) == NULL) 61448e6b99dSdjm fatal_f("sshbuf_new failed"); 615d2107d3eSmarkus if ((r = sshbuf_put_u8(msg, SSH_AGENTC_ADD_SMARTCARD_KEY)) != 0 || 616d2107d3eSmarkus (r = sshbuf_put_cstring(msg, name)) != 0 || 617d2107d3eSmarkus (r = sshbuf_put_cstring(msg, pin)) != 0) 61848e6b99dSdjm fatal_fr(r, "compose"); 619c3ac409dSdjm send_msg(helper->fd, msg); 620d2107d3eSmarkus sshbuf_reset(msg); 62141503fafSmarkus 622c3ac409dSdjm type = recv_msg(helper->fd, msg); 62321f43f82Sdjm if (type == SSH2_AGENT_IDENTITIES_ANSWER) { 624d2107d3eSmarkus if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) 62548e6b99dSdjm fatal_fr(r, "parse nkeys"); 626d2107d3eSmarkus *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); 62744e54ccbSdjm if (labelsp) 62844e54ccbSdjm *labelsp = xcalloc(nkeys, sizeof(char *)); 62941503fafSmarkus for (i = 0; i < nkeys; i++) { 630d2107d3eSmarkus /* XXX clean up properly instead of fatal() */ 631d2107d3eSmarkus if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 || 63244e54ccbSdjm (r = sshbuf_get_cstring(msg, &label, NULL)) != 0) 63348e6b99dSdjm fatal_fr(r, "parse key"); 634d2107d3eSmarkus if ((r = sshkey_from_blob(blob, blen, &k)) != 0) 63548e6b99dSdjm fatal_fr(r, "decode key"); 636c3ac409dSdjm wrap_key(helper, k); 63741503fafSmarkus (*keysp)[i] = k; 63844e54ccbSdjm if (labelsp) 63944e54ccbSdjm (*labelsp)[i] = label; 64044e54ccbSdjm else 64144e54ccbSdjm free(label); 6420d40fefdSdjm free(blob); 64341503fafSmarkus } 64421f43f82Sdjm } else if (type == SSH2_AGENT_FAILURE) { 64521f43f82Sdjm if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) 64621f43f82Sdjm nkeys = -1; 64741503fafSmarkus } else { 64841503fafSmarkus nkeys = -1; 64941503fafSmarkus } 650d2107d3eSmarkus sshbuf_free(msg); 65141503fafSmarkus return (nkeys); 65241503fafSmarkus } 65341503fafSmarkus 65441503fafSmarkus int 65541503fafSmarkus pkcs11_del_provider(char *name) 65641503fafSmarkus { 657c3ac409dSdjm struct helper *helper; 65841503fafSmarkus 659c3ac409dSdjm /* 660c3ac409dSdjm * ssh-agent deletes keys before calling this, so the helper entry 661c3ac409dSdjm * should be gone before we get here. 662c3ac409dSdjm */ 663c3ac409dSdjm debug3_f("delete %s", name); 664c3ac409dSdjm if ((helper = helper_by_provider(name)) != NULL) 665c3ac409dSdjm helper_terminate(helper); 666c3ac409dSdjm return 0; 66741503fafSmarkus } 668