ssh-pkcs11-client.c revision 1.20 1 1.14 pgoyette /* $NetBSD: ssh-pkcs11-client.c,v 1.20 2024/09/24 21:32:19 christos Exp $ */
2 1.20 christos /* $OpenBSD: ssh-pkcs11-client.c,v 1.20 2024/08/15 00:51:51 djm Exp $ */
3 1.17 christos
4 1.1 adam /*
5 1.1 adam * Copyright (c) 2010 Markus Friedl. All rights reserved.
6 1.15 christos * Copyright (c) 2014 Pedro Martelletto. All rights reserved.
7 1.1 adam *
8 1.1 adam * Permission to use, copy, modify, and distribute this software for any
9 1.1 adam * purpose with or without fee is hereby granted, provided that the above
10 1.1 adam * copyright notice and this permission notice appear in all copies.
11 1.1 adam *
12 1.1 adam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 1.1 adam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 1.1 adam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 1.1 adam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 1.1 adam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 1.1 adam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 1.1 adam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 1.1 adam */
20 1.2 adam #include "includes.h"
21 1.14 pgoyette __RCSID("$NetBSD: ssh-pkcs11-client.c,v 1.20 2024/09/24 21:32:19 christos Exp $");
22 1.1 adam
23 1.1 adam #include <sys/types.h>
24 1.1 adam #include <sys/time.h>
25 1.1 adam #include <sys/socket.h>
26 1.1 adam
27 1.1 adam #include <stdarg.h>
28 1.1 adam #include <string.h>
29 1.1 adam #include <unistd.h>
30 1.1 adam #include <errno.h>
31 1.18 christos #include <limits.h>
32 1.1 adam
33 1.15 christos #include <openssl/ecdsa.h>
34 1.5 christos #include <openssl/rsa.h>
35 1.5 christos
36 1.1 adam #include "pathnames.h"
37 1.1 adam #include "xmalloc.h"
38 1.13 christos #include "sshbuf.h"
39 1.1 adam #include "log.h"
40 1.1 adam #include "misc.h"
41 1.13 christos #include "sshkey.h"
42 1.1 adam #include "authfd.h"
43 1.1 adam #include "atomicio.h"
44 1.1 adam #include "ssh-pkcs11.h"
45 1.13 christos #include "ssherr.h"
46 1.1 adam
47 1.1 adam /* borrows code from sftp-server and ssh-agent */
48 1.1 adam
49 1.18 christos /*
50 1.18 christos * Maintain a list of ssh-pkcs11-helper subprocesses. These may be looked up
51 1.18 christos * by provider path or their unique EC/RSA METHOD pointers.
52 1.18 christos */
53 1.18 christos struct helper {
54 1.18 christos char *path;
55 1.18 christos pid_t pid;
56 1.18 christos int fd;
57 1.18 christos RSA_METHOD *rsa_meth;
58 1.18 christos EC_KEY_METHOD *ec_meth;
59 1.18 christos int (*rsa_finish)(RSA *rsa);
60 1.18 christos void (*ec_finish)(EC_KEY *key);
61 1.18 christos size_t nrsa, nec; /* number of active keys of each type */
62 1.18 christos };
63 1.18 christos static struct helper **helpers;
64 1.18 christos static size_t nhelpers;
65 1.18 christos
66 1.18 christos static struct helper *
67 1.18 christos helper_by_provider(const char *path)
68 1.18 christos {
69 1.18 christos size_t i;
70 1.18 christos
71 1.18 christos for (i = 0; i < nhelpers; i++) {
72 1.18 christos if (helpers[i] == NULL || helpers[i]->path == NULL ||
73 1.18 christos helpers[i]->fd == -1)
74 1.18 christos continue;
75 1.18 christos if (strcmp(helpers[i]->path, path) == 0)
76 1.18 christos return helpers[i];
77 1.18 christos }
78 1.18 christos return NULL;
79 1.18 christos }
80 1.18 christos
81 1.18 christos static struct helper *
82 1.18 christos helper_by_rsa(const RSA *rsa)
83 1.18 christos {
84 1.18 christos size_t i;
85 1.18 christos const RSA_METHOD *meth;
86 1.18 christos
87 1.18 christos if ((meth = RSA_get_method(rsa)) == NULL)
88 1.18 christos return NULL;
89 1.18 christos for (i = 0; i < nhelpers; i++) {
90 1.18 christos if (helpers[i] != NULL && helpers[i]->rsa_meth == meth)
91 1.18 christos return helpers[i];
92 1.18 christos }
93 1.18 christos return NULL;
94 1.18 christos
95 1.18 christos }
96 1.18 christos
97 1.18 christos static struct helper *
98 1.18 christos helper_by_ec(const EC_KEY *ec)
99 1.18 christos {
100 1.18 christos size_t i;
101 1.18 christos const EC_KEY_METHOD *meth;
102 1.18 christos
103 1.18 christos if ((meth = EC_KEY_get_method(ec)) == NULL)
104 1.18 christos return NULL;
105 1.18 christos for (i = 0; i < nhelpers; i++) {
106 1.18 christos if (helpers[i] != NULL && helpers[i]->ec_meth == meth)
107 1.18 christos return helpers[i];
108 1.18 christos }
109 1.18 christos return NULL;
110 1.18 christos
111 1.18 christos }
112 1.18 christos
113 1.18 christos static void
114 1.18 christos helper_free(struct helper *helper)
115 1.18 christos {
116 1.18 christos size_t i;
117 1.18 christos int found = 0;
118 1.18 christos
119 1.18 christos if (helper == NULL)
120 1.18 christos return;
121 1.18 christos if (helper->path == NULL || helper->ec_meth == NULL ||
122 1.18 christos helper->rsa_meth == NULL)
123 1.18 christos fatal_f("inconsistent helper");
124 1.18 christos debug3_f("free helper for provider %s", helper->path);
125 1.18 christos for (i = 0; i < nhelpers; i++) {
126 1.18 christos if (helpers[i] == helper) {
127 1.18 christos if (found)
128 1.18 christos fatal_f("helper recorded more than once");
129 1.18 christos found = 1;
130 1.18 christos }
131 1.18 christos else if (found)
132 1.18 christos helpers[i - 1] = helpers[i];
133 1.18 christos }
134 1.18 christos if (found) {
135 1.18 christos helpers = xrecallocarray(helpers, nhelpers,
136 1.18 christos nhelpers - 1, sizeof(*helpers));
137 1.18 christos nhelpers--;
138 1.18 christos }
139 1.18 christos free(helper->path);
140 1.18 christos EC_KEY_METHOD_free(helper->ec_meth);
141 1.18 christos RSA_meth_free(helper->rsa_meth);
142 1.18 christos free(helper);
143 1.18 christos }
144 1.18 christos
145 1.18 christos static void
146 1.18 christos helper_terminate(struct helper *helper)
147 1.18 christos {
148 1.18 christos if (helper == NULL) {
149 1.18 christos return;
150 1.18 christos } else if (helper->fd == -1) {
151 1.18 christos debug3_f("already terminated");
152 1.18 christos } else {
153 1.18 christos debug3_f("terminating helper for %s; "
154 1.18 christos "remaining %zu RSA %zu ECDSA",
155 1.18 christos helper->path, helper->nrsa, helper->nec);
156 1.18 christos close(helper->fd);
157 1.18 christos /* XXX waitpid() */
158 1.18 christos helper->fd = -1;
159 1.18 christos helper->pid = -1;
160 1.18 christos }
161 1.18 christos /*
162 1.18 christos * Don't delete the helper entry until there are no remaining keys
163 1.18 christos * that reference it. Otherwise, any signing operation would call
164 1.18 christos * a free'd METHOD pointer and that would be bad.
165 1.18 christos */
166 1.18 christos if (helper->nrsa == 0 && helper->nec == 0)
167 1.18 christos helper_free(helper);
168 1.18 christos }
169 1.1 adam
170 1.1 adam static void
171 1.18 christos send_msg(int fd, struct sshbuf *m)
172 1.1 adam {
173 1.1 adam u_char buf[4];
174 1.13 christos size_t mlen = sshbuf_len(m);
175 1.13 christos int r;
176 1.1 adam
177 1.18 christos if (fd == -1)
178 1.18 christos return;
179 1.13 christos POKE_U32(buf, mlen);
180 1.1 adam if (atomicio(vwrite, fd, buf, 4) != 4 ||
181 1.13 christos atomicio(vwrite, fd, sshbuf_mutable_ptr(m),
182 1.13 christos sshbuf_len(m)) != sshbuf_len(m))
183 1.1 adam error("write to helper failed");
184 1.13 christos if ((r = sshbuf_consume(m, mlen)) != 0)
185 1.17 christos fatal_fr(r, "consume");
186 1.1 adam }
187 1.1 adam
188 1.1 adam static int
189 1.18 christos recv_msg(int fd, struct sshbuf *m)
190 1.1 adam {
191 1.1 adam u_int l, len;
192 1.13 christos u_char c, buf[1024];
193 1.13 christos int r;
194 1.1 adam
195 1.18 christos sshbuf_reset(m);
196 1.18 christos if (fd == -1)
197 1.18 christos return 0; /* XXX */
198 1.1 adam if ((len = atomicio(read, fd, buf, 4)) != 4) {
199 1.1 adam error("read from helper failed: %u", len);
200 1.1 adam return (0); /* XXX */
201 1.1 adam }
202 1.13 christos len = PEEK_U32(buf);
203 1.1 adam if (len > 256 * 1024)
204 1.1 adam fatal("response too long: %u", len);
205 1.1 adam /* read len bytes into m */
206 1.1 adam while (len > 0) {
207 1.1 adam l = len;
208 1.1 adam if (l > sizeof(buf))
209 1.1 adam l = sizeof(buf);
210 1.1 adam if (atomicio(read, fd, buf, l) != l) {
211 1.1 adam error("response from helper failed.");
212 1.1 adam return (0); /* XXX */
213 1.1 adam }
214 1.13 christos if ((r = sshbuf_put(m, buf, l)) != 0)
215 1.17 christos fatal_fr(r, "sshbuf_put");
216 1.1 adam len -= l;
217 1.1 adam }
218 1.13 christos if ((r = sshbuf_get_u8(m, &c)) != 0)
219 1.17 christos fatal_fr(r, "parse type");
220 1.13 christos return c;
221 1.1 adam }
222 1.1 adam
223 1.1 adam int
224 1.1 adam pkcs11_init(int interactive)
225 1.1 adam {
226 1.18 christos return 0;
227 1.1 adam }
228 1.1 adam
229 1.1 adam void
230 1.1 adam pkcs11_terminate(void)
231 1.1 adam {
232 1.18 christos size_t i;
233 1.18 christos
234 1.18 christos debug3_f("terminating %zu helpers", nhelpers);
235 1.18 christos for (i = 0; i < nhelpers; i++)
236 1.18 christos helper_terminate(helpers[i]);
237 1.1 adam }
238 1.1 adam
239 1.1 adam static int
240 1.15 christos rsa_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, int padding)
241 1.1 adam {
242 1.15 christos struct sshkey *key = NULL;
243 1.15 christos struct sshbuf *msg = NULL;
244 1.15 christos u_char *blob = NULL, *signature = NULL;
245 1.13 christos size_t blen, slen = 0;
246 1.13 christos int r, ret = -1;
247 1.18 christos struct helper *helper;
248 1.1 adam
249 1.18 christos if ((helper = helper_by_rsa(rsa)) == NULL || helper->fd == -1)
250 1.18 christos fatal_f("no helper for PKCS11 key");
251 1.18 christos debug3_f("signing with PKCS11 provider %s", helper->path);
252 1.1 adam if (padding != RSA_PKCS1_PADDING)
253 1.15 christos goto fail;
254 1.20 christos if ((key = sshkey_new(KEY_UNSPEC)) == NULL) {
255 1.17 christos error_f("sshkey_new failed");
256 1.15 christos goto fail;
257 1.15 christos }
258 1.20 christos if ((key->pkey = EVP_PKEY_new()) == NULL ||
259 1.20 christos EVP_PKEY_set1_RSA(key->pkey, rsa) != 1) {
260 1.20 christos error_f("pkey setup failed");
261 1.20 christos goto fail;
262 1.20 christos }
263 1.20 christos
264 1.15 christos key->type = KEY_RSA;
265 1.15 christos if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) {
266 1.17 christos error_fr(r, "encode key");
267 1.15 christos goto fail;
268 1.13 christos }
269 1.13 christos if ((msg = sshbuf_new()) == NULL)
270 1.17 christos fatal_f("sshbuf_new failed");
271 1.13 christos if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
272 1.13 christos (r = sshbuf_put_string(msg, blob, blen)) != 0 ||
273 1.13 christos (r = sshbuf_put_string(msg, from, flen)) != 0 ||
274 1.13 christos (r = sshbuf_put_u32(msg, 0)) != 0)
275 1.17 christos fatal_fr(r, "compose");
276 1.18 christos send_msg(helper->fd, msg);
277 1.13 christos sshbuf_reset(msg);
278 1.1 adam
279 1.18 christos if (recv_msg(helper->fd, msg) == SSH2_AGENT_SIGN_RESPONSE) {
280 1.13 christos if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0)
281 1.17 christos fatal_fr(r, "parse");
282 1.13 christos if (slen <= (size_t)RSA_size(rsa)) {
283 1.1 adam memcpy(to, signature, slen);
284 1.1 adam ret = slen;
285 1.1 adam }
286 1.4 christos free(signature);
287 1.1 adam }
288 1.15 christos fail:
289 1.15 christos free(blob);
290 1.15 christos sshkey_free(key);
291 1.13 christos sshbuf_free(msg);
292 1.1 adam return (ret);
293 1.1 adam }
294 1.1 adam
295 1.18 christos static int
296 1.18 christos rsa_finish(RSA *rsa)
297 1.18 christos {
298 1.18 christos struct helper *helper;
299 1.18 christos
300 1.18 christos if ((helper = helper_by_rsa(rsa)) == NULL)
301 1.18 christos fatal_f("no helper for PKCS11 key");
302 1.18 christos debug3_f("free PKCS11 RSA key for provider %s", helper->path);
303 1.18 christos if (helper->rsa_finish != NULL)
304 1.18 christos helper->rsa_finish(rsa);
305 1.18 christos if (helper->nrsa == 0)
306 1.18 christos fatal_f("RSA refcount error");
307 1.18 christos helper->nrsa--;
308 1.18 christos debug3_f("provider %s remaining keys: %zu RSA %zu ECDSA",
309 1.18 christos helper->path, helper->nrsa, helper->nec);
310 1.18 christos if (helper->nrsa == 0 && helper->nec == 0)
311 1.18 christos helper_terminate(helper);
312 1.18 christos return 1;
313 1.18 christos }
314 1.18 christos
315 1.15 christos static ECDSA_SIG *
316 1.15 christos ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
317 1.15 christos const BIGNUM *rp, EC_KEY *ec)
318 1.15 christos {
319 1.15 christos struct sshkey *key = NULL;
320 1.15 christos struct sshbuf *msg = NULL;
321 1.15 christos ECDSA_SIG *ret = NULL;
322 1.15 christos const u_char *cp;
323 1.15 christos u_char *blob = NULL, *signature = NULL;
324 1.15 christos size_t blen, slen = 0;
325 1.15 christos int r, nid;
326 1.18 christos struct helper *helper;
327 1.15 christos
328 1.18 christos if ((helper = helper_by_ec(ec)) == NULL || helper->fd == -1)
329 1.18 christos fatal_f("no helper for PKCS11 key");
330 1.18 christos debug3_f("signing with PKCS11 provider %s", helper->path);
331 1.20 christos
332 1.20 christos if ((key = sshkey_new(KEY_UNSPEC)) == NULL) {
333 1.20 christos error_f("sshkey_new failed");
334 1.15 christos goto fail;
335 1.15 christos }
336 1.20 christos if ((key->pkey = EVP_PKEY_new()) == NULL ||
337 1.20 christos EVP_PKEY_set1_EC_KEY(key->pkey, ec) != 1) {
338 1.20 christos error("pkey setup failed");
339 1.20 christos goto fail;
340 1.20 christos }
341 1.20 christos if ((nid = sshkey_ecdsa_pkey_to_nid(key->pkey)) < 0) {
342 1.20 christos error("couldn't get curve nid");
343 1.15 christos goto fail;
344 1.15 christos }
345 1.15 christos key->ecdsa_nid = nid;
346 1.15 christos key->type = KEY_ECDSA;
347 1.15 christos
348 1.15 christos if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) {
349 1.17 christos error_fr(r, "encode key");
350 1.15 christos goto fail;
351 1.15 christos }
352 1.15 christos if ((msg = sshbuf_new()) == NULL)
353 1.17 christos fatal_f("sshbuf_new failed");
354 1.15 christos if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
355 1.15 christos (r = sshbuf_put_string(msg, blob, blen)) != 0 ||
356 1.15 christos (r = sshbuf_put_string(msg, dgst, dgst_len)) != 0 ||
357 1.15 christos (r = sshbuf_put_u32(msg, 0)) != 0)
358 1.17 christos fatal_fr(r, "compose");
359 1.18 christos send_msg(helper->fd, msg);
360 1.15 christos sshbuf_reset(msg);
361 1.15 christos
362 1.18 christos if (recv_msg(helper->fd, msg) == SSH2_AGENT_SIGN_RESPONSE) {
363 1.15 christos if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0)
364 1.17 christos fatal_fr(r, "parse");
365 1.15 christos cp = signature;
366 1.15 christos ret = d2i_ECDSA_SIG(NULL, &cp, slen);
367 1.15 christos free(signature);
368 1.15 christos }
369 1.15 christos
370 1.15 christos fail:
371 1.15 christos free(blob);
372 1.15 christos sshkey_free(key);
373 1.15 christos sshbuf_free(msg);
374 1.15 christos return (ret);
375 1.15 christos }
376 1.15 christos
377 1.18 christos static void
378 1.18 christos ecdsa_do_finish(EC_KEY *ec)
379 1.18 christos {
380 1.18 christos struct helper *helper;
381 1.18 christos
382 1.18 christos if ((helper = helper_by_ec(ec)) == NULL)
383 1.18 christos fatal_f("no helper for PKCS11 key");
384 1.18 christos debug3_f("free PKCS11 ECDSA key for provider %s", helper->path);
385 1.18 christos if (helper->ec_finish != NULL)
386 1.18 christos helper->ec_finish(ec);
387 1.18 christos if (helper->nec == 0)
388 1.18 christos fatal_f("ECDSA refcount error");
389 1.18 christos helper->nec--;
390 1.18 christos debug3_f("provider %s remaining keys: %zu RSA %zu ECDSA",
391 1.18 christos helper->path, helper->nrsa, helper->nec);
392 1.18 christos if (helper->nrsa == 0 && helper->nec == 0)
393 1.18 christos helper_terminate(helper);
394 1.18 christos }
395 1.15 christos
396 1.15 christos /* redirect private key crypto operations to the ssh-pkcs11-helper */
397 1.15 christos static void
398 1.18 christos wrap_key(struct helper *helper, struct sshkey *k)
399 1.15 christos {
400 1.20 christos RSA *rsa = NULL;
401 1.20 christos EC_KEY *ecdsa = NULL;
402 1.20 christos
403 1.18 christos debug3_f("wrap %s for provider %s", sshkey_type(k), helper->path);
404 1.18 christos if (k->type == KEY_RSA) {
405 1.20 christos if ((rsa = EVP_PKEY_get1_RSA(k->pkey)) == NULL)
406 1.20 christos fatal_f("no RSA key");
407 1.20 christos if (RSA_set_method(rsa, helper->rsa_meth) != 1)
408 1.20 christos fatal_f("RSA_set_method failed");
409 1.18 christos if (helper->nrsa++ >= INT_MAX)
410 1.18 christos fatal_f("RSA refcount error");
411 1.20 christos if (EVP_PKEY_set1_RSA(k->pkey, rsa) != 1)
412 1.20 christos fatal_f("EVP_PKEY_set1_RSA failed");
413 1.20 christos RSA_free(rsa);
414 1.18 christos } else if (k->type == KEY_ECDSA) {
415 1.20 christos if ((ecdsa = EVP_PKEY_get1_EC_KEY(k->pkey)) == NULL)
416 1.20 christos fatal_f("no ECDSA key");
417 1.20 christos if (EC_KEY_set_method(ecdsa, helper->ec_meth) != 1)
418 1.20 christos fatal_f("EC_KEY_set_method failed");
419 1.18 christos if (helper->nec++ >= INT_MAX)
420 1.18 christos fatal_f("EC refcount error");
421 1.20 christos if (EVP_PKEY_set1_EC_KEY(k->pkey, ecdsa) != 1)
422 1.20 christos fatal_f("EVP_PKEY_set1_EC_KEY failed");
423 1.20 christos EC_KEY_free(ecdsa);
424 1.18 christos } else
425 1.17 christos fatal_f("unknown key type");
426 1.18 christos k->flags |= SSHKEY_FLAG_EXT;
427 1.18 christos debug3_f("provider %s remaining keys: %zu RSA %zu ECDSA",
428 1.18 christos helper->path, helper->nrsa, helper->nec);
429 1.15 christos }
430 1.15 christos
431 1.19 christos /*
432 1.19 christos * Make a private PKCS#11-backed certificate by grafting a previously-loaded
433 1.19 christos * PKCS#11 private key and a public certificate key.
434 1.19 christos */
435 1.19 christos int
436 1.19 christos pkcs11_make_cert(const struct sshkey *priv,
437 1.19 christos const struct sshkey *certpub, struct sshkey **certprivp)
438 1.19 christos {
439 1.19 christos struct helper *helper = NULL;
440 1.19 christos struct sshkey *ret;
441 1.19 christos int r;
442 1.20 christos RSA *rsa_priv = NULL, *rsa_cert = NULL;
443 1.20 christos EC_KEY *ec_priv = NULL, *ec_cert = NULL;
444 1.19 christos
445 1.19 christos debug3_f("private key type %s cert type %s", sshkey_type(priv),
446 1.19 christos sshkey_type(certpub));
447 1.19 christos *certprivp = NULL;
448 1.19 christos if (!sshkey_is_cert(certpub) || sshkey_is_cert(priv) ||
449 1.19 christos !sshkey_equal_public(priv, certpub)) {
450 1.19 christos error_f("private key %s doesn't match cert %s",
451 1.19 christos sshkey_type(priv), sshkey_type(certpub));
452 1.19 christos return SSH_ERR_INVALID_ARGUMENT;
453 1.19 christos }
454 1.19 christos *certprivp = NULL;
455 1.19 christos if (priv->type == KEY_RSA) {
456 1.20 christos if ((rsa_priv = EVP_PKEY_get1_RSA(priv->pkey)) == NULL)
457 1.20 christos fatal_f("no RSA pkey");
458 1.20 christos if ((helper = helper_by_rsa(rsa_priv)) == NULL ||
459 1.19 christos helper->fd == -1)
460 1.19 christos fatal_f("no helper for PKCS11 RSA key");
461 1.19 christos if ((r = sshkey_from_private(priv, &ret)) != 0)
462 1.19 christos fatal_fr(r, "copy key");
463 1.20 christos if ((rsa_cert = EVP_PKEY_get1_RSA(ret->pkey)) == NULL)
464 1.20 christos fatal_f("no RSA cert pkey");
465 1.20 christos if (RSA_set_method(rsa_cert, helper->rsa_meth) != 1)
466 1.20 christos fatal_f("RSA_set_method failed");
467 1.19 christos if (helper->nrsa++ >= INT_MAX)
468 1.19 christos fatal_f("RSA refcount error");
469 1.20 christos if (EVP_PKEY_set1_RSA(ret->pkey, rsa_cert) != 1)
470 1.20 christos fatal_f("EVP_PKEY_set1_RSA failed");
471 1.20 christos RSA_free(rsa_priv);
472 1.20 christos RSA_free(rsa_cert);
473 1.19 christos } else if (priv->type == KEY_ECDSA) {
474 1.20 christos if ((ec_priv = EVP_PKEY_get1_EC_KEY(priv->pkey)) == NULL)
475 1.20 christos fatal_f("no EC pkey");
476 1.20 christos if ((helper = helper_by_ec(ec_priv)) == NULL ||
477 1.19 christos helper->fd == -1)
478 1.19 christos fatal_f("no helper for PKCS11 EC key");
479 1.19 christos if ((r = sshkey_from_private(priv, &ret)) != 0)
480 1.19 christos fatal_fr(r, "copy key");
481 1.20 christos if ((ec_cert = EVP_PKEY_get1_EC_KEY(ret->pkey)) == NULL)
482 1.20 christos fatal_f("no EC cert pkey");
483 1.20 christos if (EC_KEY_set_method(ec_cert, helper->ec_meth) != 1)
484 1.20 christos fatal_f("EC_KEY_set_method failed");
485 1.19 christos if (helper->nec++ >= INT_MAX)
486 1.19 christos fatal_f("EC refcount error");
487 1.20 christos if (EVP_PKEY_set1_EC_KEY(ret->pkey, ec_cert) != 1)
488 1.20 christos fatal_f("EVP_PKEY_set1_EC_KEY failed");
489 1.20 christos EC_KEY_free(ec_priv);
490 1.20 christos EC_KEY_free(ec_cert);
491 1.19 christos } else
492 1.19 christos fatal_f("unknown key type %s", sshkey_type(priv));
493 1.19 christos
494 1.19 christos ret->flags |= SSHKEY_FLAG_EXT;
495 1.19 christos if ((r = sshkey_to_certified(ret)) != 0 ||
496 1.19 christos (r = sshkey_cert_copy(certpub, ret)) != 0)
497 1.19 christos fatal_fr(r, "graft certificate");
498 1.19 christos debug3_f("provider %s remaining keys: %zu RSA %zu ECDSA",
499 1.19 christos helper->path, helper->nrsa, helper->nec);
500 1.19 christos /* success */
501 1.19 christos *certprivp = ret;
502 1.19 christos return 0;
503 1.19 christos }
504 1.19 christos
505 1.1 adam static int
506 1.18 christos pkcs11_start_helper_methods(struct helper *helper)
507 1.1 adam {
508 1.18 christos int (*ec_init)(EC_KEY *key);
509 1.18 christos int (*ec_copy)(EC_KEY *dest, const EC_KEY *src);
510 1.18 christos int (*ec_set_group)(EC_KEY *key, const EC_GROUP *grp);
511 1.18 christos int (*ec_set_private)(EC_KEY *key, const BIGNUM *priv_key);
512 1.18 christos int (*ec_set_public)(EC_KEY *key, const EC_POINT *pub_key);
513 1.18 christos int (*ec_sign)(int, const unsigned char *, int, unsigned char *,
514 1.18 christos unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL;
515 1.18 christos RSA_METHOD *rsa_meth;
516 1.18 christos EC_KEY_METHOD *ec_meth;
517 1.15 christos
518 1.18 christos if ((ec_meth = EC_KEY_METHOD_new(EC_KEY_OpenSSL())) == NULL)
519 1.18 christos return -1;
520 1.18 christos EC_KEY_METHOD_get_sign(ec_meth, &ec_sign, NULL, NULL);
521 1.18 christos EC_KEY_METHOD_set_sign(ec_meth, ec_sign, NULL, ecdsa_do_sign);
522 1.18 christos EC_KEY_METHOD_get_init(ec_meth, &ec_init, &helper->ec_finish,
523 1.18 christos &ec_copy, &ec_set_group, &ec_set_private, &ec_set_public);
524 1.18 christos EC_KEY_METHOD_set_init(ec_meth, ec_init, ecdsa_do_finish,
525 1.18 christos ec_copy, ec_set_group, ec_set_private, ec_set_public);
526 1.1 adam
527 1.18 christos if ((rsa_meth = RSA_meth_dup(RSA_get_default_method())) == NULL)
528 1.17 christos fatal_f("RSA_meth_dup failed");
529 1.18 christos helper->rsa_finish = RSA_meth_get_finish(rsa_meth);
530 1.18 christos if (!RSA_meth_set1_name(rsa_meth, "ssh-pkcs11-helper") ||
531 1.18 christos !RSA_meth_set_priv_enc(rsa_meth, rsa_encrypt) ||
532 1.18 christos !RSA_meth_set_finish(rsa_meth, rsa_finish))
533 1.17 christos fatal_f("failed to prepare method");
534 1.15 christos
535 1.18 christos helper->ec_meth = ec_meth;
536 1.18 christos helper->rsa_meth = rsa_meth;
537 1.18 christos return 0;
538 1.1 adam }
539 1.1 adam
540 1.18 christos static struct helper *
541 1.18 christos pkcs11_start_helper(const char *path)
542 1.1 adam {
543 1.1 adam int pair[2];
544 1.18 christos const char *prog, *verbosity = NULL;
545 1.18 christos struct helper *helper;
546 1.18 christos pid_t pid;
547 1.18 christos
548 1.18 christos if (nhelpers >= INT_MAX)
549 1.18 christos fatal_f("too many helpers");
550 1.18 christos debug3_f("start helper for %s", path);
551 1.18 christos if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
552 1.18 christos error_f("socketpair: %s", strerror(errno));
553 1.18 christos return NULL;
554 1.15 christos }
555 1.18 christos helper = xcalloc(1, sizeof(*helper));
556 1.18 christos if (pkcs11_start_helper_methods(helper) == -1) {
557 1.18 christos error_f("pkcs11_start_helper_methods failed");
558 1.18 christos goto fail;
559 1.1 adam }
560 1.1 adam if ((pid = fork()) == -1) {
561 1.18 christos error_f("fork: %s", strerror(errno));
562 1.18 christos fail:
563 1.18 christos close(pair[0]);
564 1.18 christos close(pair[1]);
565 1.18 christos RSA_meth_free(helper->rsa_meth);
566 1.18 christos EC_KEY_METHOD_free(helper->ec_meth);
567 1.18 christos free(helper);
568 1.18 christos return NULL;
569 1.1 adam } else if (pid == 0) {
570 1.1 adam if ((dup2(pair[1], STDIN_FILENO) == -1) ||
571 1.1 adam (dup2(pair[1], STDOUT_FILENO) == -1)) {
572 1.1 adam fprintf(stderr, "dup2: %s\n", strerror(errno));
573 1.1 adam _exit(1);
574 1.1 adam }
575 1.1 adam close(pair[0]);
576 1.1 adam close(pair[1]);
577 1.18 christos prog = getenv("SSH_PKCS11_HELPER");
578 1.18 christos if (prog == NULL || strlen(prog) == 0)
579 1.18 christos prog = _PATH_SSH_PKCS11_HELPER;
580 1.18 christos if (log_level_get() >= SYSLOG_LEVEL_DEBUG1)
581 1.18 christos verbosity = "-vvv";
582 1.18 christos debug_f("starting %s %s", prog,
583 1.15 christos verbosity == NULL ? "" : verbosity);
584 1.18 christos execlp(prog, prog, verbosity, (char *)NULL);
585 1.18 christos fprintf(stderr, "exec: %s: %s\n", prog, strerror(errno));
586 1.1 adam _exit(1);
587 1.1 adam }
588 1.1 adam close(pair[1]);
589 1.18 christos helper->fd = pair[0];
590 1.18 christos helper->path = xstrdup(path);
591 1.18 christos helper->pid = pid;
592 1.18 christos debug3_f("helper %zu for \"%s\" on fd %d pid %ld", nhelpers,
593 1.18 christos helper->path, helper->fd, (long)helper->pid);
594 1.18 christos helpers = xrecallocarray(helpers, nhelpers,
595 1.18 christos nhelpers + 1, sizeof(*helpers));
596 1.18 christos helpers[nhelpers++] = helper;
597 1.18 christos return helper;
598 1.1 adam }
599 1.1 adam
600 1.1 adam int
601 1.16 christos pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp,
602 1.16 christos char ***labelsp)
603 1.1 adam {
604 1.10 christos struct sshkey *k;
605 1.15 christos int r, type;
606 1.1 adam u_char *blob;
607 1.16 christos char *label;
608 1.13 christos size_t blen;
609 1.13 christos u_int nkeys, i;
610 1.13 christos struct sshbuf *msg;
611 1.18 christos struct helper *helper;
612 1.1 adam
613 1.18 christos if ((helper = helper_by_provider(name)) == NULL &&
614 1.18 christos (helper = pkcs11_start_helper(name)) == NULL)
615 1.18 christos return -1;
616 1.1 adam
617 1.13 christos if ((msg = sshbuf_new()) == NULL)
618 1.17 christos fatal_f("sshbuf_new failed");
619 1.13 christos if ((r = sshbuf_put_u8(msg, SSH_AGENTC_ADD_SMARTCARD_KEY)) != 0 ||
620 1.13 christos (r = sshbuf_put_cstring(msg, name)) != 0 ||
621 1.13 christos (r = sshbuf_put_cstring(msg, pin)) != 0)
622 1.17 christos fatal_fr(r, "compose");
623 1.18 christos send_msg(helper->fd, msg);
624 1.13 christos sshbuf_reset(msg);
625 1.13 christos
626 1.18 christos type = recv_msg(helper->fd, msg);
627 1.15 christos if (type == SSH2_AGENT_IDENTITIES_ANSWER) {
628 1.13 christos if ((r = sshbuf_get_u32(msg, &nkeys)) != 0)
629 1.17 christos fatal_fr(r, "parse nkeys");
630 1.13 christos *keysp = xcalloc(nkeys, sizeof(struct sshkey *));
631 1.16 christos if (labelsp)
632 1.16 christos *labelsp = xcalloc(nkeys, sizeof(char *));
633 1.1 adam for (i = 0; i < nkeys; i++) {
634 1.13 christos /* XXX clean up properly instead of fatal() */
635 1.13 christos if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 ||
636 1.16 christos (r = sshbuf_get_cstring(msg, &label, NULL)) != 0)
637 1.17 christos fatal_fr(r, "parse key");
638 1.13 christos if ((r = sshkey_from_blob(blob, blen, &k)) != 0)
639 1.17 christos fatal_fr(r, "decode key");
640 1.18 christos wrap_key(helper, k);
641 1.1 adam (*keysp)[i] = k;
642 1.16 christos if (labelsp)
643 1.16 christos (*labelsp)[i] = label;
644 1.16 christos else
645 1.16 christos free(label);
646 1.4 christos free(blob);
647 1.1 adam }
648 1.15 christos } else if (type == SSH2_AGENT_FAILURE) {
649 1.15 christos if ((r = sshbuf_get_u32(msg, &nkeys)) != 0)
650 1.15 christos nkeys = -1;
651 1.1 adam } else {
652 1.1 adam nkeys = -1;
653 1.1 adam }
654 1.13 christos sshbuf_free(msg);
655 1.1 adam return (nkeys);
656 1.1 adam }
657 1.1 adam
658 1.1 adam int
659 1.1 adam pkcs11_del_provider(char *name)
660 1.1 adam {
661 1.18 christos struct helper *helper;
662 1.1 adam
663 1.18 christos /*
664 1.18 christos * ssh-agent deletes keys before calling this, so the helper entry
665 1.18 christos * should be gone before we get here.
666 1.18 christos */
667 1.18 christos debug3_f("delete %s", name);
668 1.18 christos if ((helper = helper_by_provider(name)) != NULL)
669 1.18 christos helper_terminate(helper);
670 1.18 christos return 0;
671 1.1 adam }
672