xref: /openbsd-src/usr.bin/ssh/ssh-pkcs11-client.c (revision 5411e7691687441b2e28282ca3a1c531c6b949c4)
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