ssh-pkcs11-client.c revision 1.15 1 1.14 pgoyette /* $NetBSD: ssh-pkcs11-client.c,v 1.15 2019/04/20 17:16:40 christos Exp $ */
2 1.15 christos /* $OpenBSD: ssh-pkcs11-client.c,v 1.15 2019/01/21 12:53:35 djm Exp $ */
3 1.1 adam /*
4 1.1 adam * Copyright (c) 2010 Markus Friedl. All rights reserved.
5 1.15 christos * Copyright (c) 2014 Pedro Martelletto. All rights reserved.
6 1.1 adam *
7 1.1 adam * Permission to use, copy, modify, and distribute this software for any
8 1.1 adam * purpose with or without fee is hereby granted, provided that the above
9 1.1 adam * copyright notice and this permission notice appear in all copies.
10 1.1 adam *
11 1.1 adam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 1.1 adam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 1.1 adam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 1.1 adam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 1.1 adam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 1.1 adam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 1.1 adam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 1.1 adam */
19 1.2 adam #include "includes.h"
20 1.14 pgoyette __RCSID("$NetBSD: ssh-pkcs11-client.c,v 1.15 2019/04/20 17:16:40 christos Exp $");
21 1.1 adam
22 1.1 adam #include <sys/types.h>
23 1.1 adam #include <sys/time.h>
24 1.1 adam #include <sys/socket.h>
25 1.1 adam
26 1.1 adam #include <stdarg.h>
27 1.1 adam #include <string.h>
28 1.1 adam #include <unistd.h>
29 1.1 adam #include <errno.h>
30 1.1 adam
31 1.15 christos #include <openssl/ecdsa.h>
32 1.5 christos #include <openssl/rsa.h>
33 1.5 christos
34 1.1 adam #include "pathnames.h"
35 1.1 adam #include "xmalloc.h"
36 1.13 christos #include "sshbuf.h"
37 1.1 adam #include "log.h"
38 1.1 adam #include "misc.h"
39 1.13 christos #include "sshkey.h"
40 1.1 adam #include "authfd.h"
41 1.1 adam #include "atomicio.h"
42 1.1 adam #include "ssh-pkcs11.h"
43 1.13 christos #include "ssherr.h"
44 1.1 adam
45 1.1 adam /* borrows code from sftp-server and ssh-agent */
46 1.1 adam
47 1.15 christos static int fd = -1;
48 1.15 christos static pid_t pid = -1;
49 1.1 adam
50 1.1 adam static void
51 1.13 christos send_msg(struct sshbuf *m)
52 1.1 adam {
53 1.1 adam u_char buf[4];
54 1.13 christos size_t mlen = sshbuf_len(m);
55 1.13 christos int r;
56 1.1 adam
57 1.13 christos POKE_U32(buf, mlen);
58 1.1 adam if (atomicio(vwrite, fd, buf, 4) != 4 ||
59 1.13 christos atomicio(vwrite, fd, sshbuf_mutable_ptr(m),
60 1.13 christos sshbuf_len(m)) != sshbuf_len(m))
61 1.1 adam error("write to helper failed");
62 1.13 christos if ((r = sshbuf_consume(m, mlen)) != 0)
63 1.13 christos fatal("%s: buffer error: %s", __func__, ssh_err(r));
64 1.1 adam }
65 1.1 adam
66 1.1 adam static int
67 1.13 christos recv_msg(struct sshbuf *m)
68 1.1 adam {
69 1.1 adam u_int l, len;
70 1.13 christos u_char c, buf[1024];
71 1.13 christos int r;
72 1.1 adam
73 1.1 adam if ((len = atomicio(read, fd, buf, 4)) != 4) {
74 1.1 adam error("read from helper failed: %u", len);
75 1.1 adam return (0); /* XXX */
76 1.1 adam }
77 1.13 christos len = PEEK_U32(buf);
78 1.1 adam if (len > 256 * 1024)
79 1.1 adam fatal("response too long: %u", len);
80 1.1 adam /* read len bytes into m */
81 1.13 christos sshbuf_reset(m);
82 1.1 adam while (len > 0) {
83 1.1 adam l = len;
84 1.1 adam if (l > sizeof(buf))
85 1.1 adam l = sizeof(buf);
86 1.1 adam if (atomicio(read, fd, buf, l) != l) {
87 1.1 adam error("response from helper failed.");
88 1.1 adam return (0); /* XXX */
89 1.1 adam }
90 1.13 christos if ((r = sshbuf_put(m, buf, l)) != 0)
91 1.13 christos fatal("%s: buffer error: %s", __func__, ssh_err(r));
92 1.1 adam len -= l;
93 1.1 adam }
94 1.13 christos if ((r = sshbuf_get_u8(m, &c)) != 0)
95 1.13 christos fatal("%s: buffer error: %s", __func__, ssh_err(r));
96 1.13 christos return c;
97 1.1 adam }
98 1.1 adam
99 1.1 adam int
100 1.1 adam pkcs11_init(int interactive)
101 1.1 adam {
102 1.1 adam return (0);
103 1.1 adam }
104 1.1 adam
105 1.1 adam void
106 1.1 adam pkcs11_terminate(void)
107 1.1 adam {
108 1.12 christos if (fd >= 0)
109 1.12 christos close(fd);
110 1.1 adam }
111 1.1 adam
112 1.1 adam static int
113 1.15 christos rsa_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, int padding)
114 1.1 adam {
115 1.15 christos struct sshkey *key = NULL;
116 1.15 christos struct sshbuf *msg = NULL;
117 1.15 christos u_char *blob = NULL, *signature = NULL;
118 1.13 christos size_t blen, slen = 0;
119 1.13 christos int r, ret = -1;
120 1.1 adam
121 1.1 adam if (padding != RSA_PKCS1_PADDING)
122 1.15 christos goto fail;
123 1.15 christos key = sshkey_new(KEY_UNSPEC);
124 1.15 christos if (key == NULL) {
125 1.15 christos error("%s: sshkey_new failed", __func__);
126 1.15 christos goto fail;
127 1.15 christos }
128 1.15 christos key->type = KEY_RSA;
129 1.15 christos RSA_up_ref(rsa);
130 1.15 christos key->rsa = rsa;
131 1.15 christos if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) {
132 1.13 christos error("%s: sshkey_to_blob: %s", __func__, ssh_err(r));
133 1.15 christos goto fail;
134 1.13 christos }
135 1.13 christos if ((msg = sshbuf_new()) == NULL)
136 1.13 christos fatal("%s: sshbuf_new failed", __func__);
137 1.13 christos if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
138 1.13 christos (r = sshbuf_put_string(msg, blob, blen)) != 0 ||
139 1.13 christos (r = sshbuf_put_string(msg, from, flen)) != 0 ||
140 1.13 christos (r = sshbuf_put_u32(msg, 0)) != 0)
141 1.13 christos fatal("%s: buffer error: %s", __func__, ssh_err(r));
142 1.13 christos send_msg(msg);
143 1.13 christos sshbuf_reset(msg);
144 1.1 adam
145 1.13 christos if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) {
146 1.13 christos if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0)
147 1.13 christos fatal("%s: buffer error: %s", __func__, ssh_err(r));
148 1.13 christos if (slen <= (size_t)RSA_size(rsa)) {
149 1.1 adam memcpy(to, signature, slen);
150 1.1 adam ret = slen;
151 1.1 adam }
152 1.4 christos free(signature);
153 1.1 adam }
154 1.15 christos fail:
155 1.15 christos free(blob);
156 1.15 christos sshkey_free(key);
157 1.13 christos sshbuf_free(msg);
158 1.1 adam return (ret);
159 1.1 adam }
160 1.1 adam
161 1.15 christos static ECDSA_SIG *
162 1.15 christos ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
163 1.15 christos const BIGNUM *rp, EC_KEY *ec)
164 1.15 christos {
165 1.15 christos struct sshkey *key = NULL;
166 1.15 christos struct sshbuf *msg = NULL;
167 1.15 christos ECDSA_SIG *ret = NULL;
168 1.15 christos const u_char *cp;
169 1.15 christos u_char *blob = NULL, *signature = NULL;
170 1.15 christos size_t blen, slen = 0;
171 1.15 christos int r, nid;
172 1.15 christos
173 1.15 christos nid = sshkey_ecdsa_key_to_nid(ec);
174 1.15 christos if (nid < 0) {
175 1.15 christos error("%s: couldn't get curve nid", __func__);
176 1.15 christos goto fail;
177 1.15 christos }
178 1.15 christos
179 1.15 christos key = sshkey_new(KEY_UNSPEC);
180 1.15 christos if (key == NULL) {
181 1.15 christos error("%s: sshkey_new failed", __func__);
182 1.15 christos goto fail;
183 1.15 christos }
184 1.15 christos key->ecdsa = ec;
185 1.15 christos key->ecdsa_nid = nid;
186 1.15 christos key->type = KEY_ECDSA;
187 1.15 christos EC_KEY_up_ref(ec);
188 1.15 christos
189 1.15 christos if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) {
190 1.15 christos error("%s: sshkey_to_blob: %s", __func__, ssh_err(r));
191 1.15 christos goto fail;
192 1.15 christos }
193 1.15 christos if ((msg = sshbuf_new()) == NULL)
194 1.15 christos fatal("%s: sshbuf_new failed", __func__);
195 1.15 christos if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
196 1.15 christos (r = sshbuf_put_string(msg, blob, blen)) != 0 ||
197 1.15 christos (r = sshbuf_put_string(msg, dgst, dgst_len)) != 0 ||
198 1.15 christos (r = sshbuf_put_u32(msg, 0)) != 0)
199 1.15 christos fatal("%s: buffer error: %s", __func__, ssh_err(r));
200 1.15 christos send_msg(msg);
201 1.15 christos sshbuf_reset(msg);
202 1.15 christos
203 1.15 christos if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) {
204 1.15 christos if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0)
205 1.15 christos fatal("%s: buffer error: %s", __func__, ssh_err(r));
206 1.15 christos cp = signature;
207 1.15 christos ret = d2i_ECDSA_SIG(NULL, &cp, slen);
208 1.15 christos free(signature);
209 1.15 christos }
210 1.15 christos
211 1.15 christos fail:
212 1.15 christos free(blob);
213 1.15 christos sshkey_free(key);
214 1.15 christos sshbuf_free(msg);
215 1.15 christos return (ret);
216 1.15 christos }
217 1.15 christos
218 1.15 christos static RSA_METHOD *helper_rsa;
219 1.15 christos static EC_KEY_METHOD *helper_ecdsa;
220 1.15 christos
221 1.15 christos /* redirect private key crypto operations to the ssh-pkcs11-helper */
222 1.15 christos static void
223 1.15 christos wrap_key(struct sshkey *k)
224 1.15 christos {
225 1.15 christos if (k->type == KEY_RSA)
226 1.15 christos RSA_set_method(k->rsa, helper_rsa);
227 1.15 christos else if (k->type == KEY_ECDSA)
228 1.15 christos EC_KEY_set_method(k->ecdsa, helper_ecdsa);
229 1.15 christos else
230 1.15 christos fatal("%s: unknown key type", __func__);
231 1.15 christos }
232 1.15 christos
233 1.1 adam static int
234 1.15 christos pkcs11_start_helper_methods(void)
235 1.1 adam {
236 1.15 christos if (helper_ecdsa != NULL)
237 1.15 christos return (0);
238 1.15 christos
239 1.15 christos int (*orig_sign)(int, const unsigned char *, int, unsigned char *,
240 1.15 christos unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL;
241 1.15 christos if (helper_ecdsa != NULL)
242 1.15 christos return (0);
243 1.15 christos helper_ecdsa = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
244 1.15 christos if (helper_ecdsa == NULL)
245 1.15 christos return (-1);
246 1.15 christos EC_KEY_METHOD_get_sign(helper_ecdsa, &orig_sign, NULL, NULL);
247 1.15 christos EC_KEY_METHOD_set_sign(helper_ecdsa, orig_sign, NULL, ecdsa_do_sign);
248 1.1 adam
249 1.11 christos if ((helper_rsa = RSA_meth_dup(RSA_get_default_method())) == NULL)
250 1.15 christos fatal("%s: RSA_meth_dup failed", __func__);
251 1.15 christos if (!RSA_meth_set1_name(helper_rsa, "ssh-pkcs11-helper") ||
252 1.15 christos !RSA_meth_set_priv_enc(helper_rsa, rsa_encrypt))
253 1.15 christos fatal("%s: failed to prepare method", __func__);
254 1.15 christos
255 1.1 adam return (0);
256 1.1 adam }
257 1.1 adam
258 1.1 adam static int
259 1.1 adam pkcs11_start_helper(void)
260 1.1 adam {
261 1.1 adam int pair[2];
262 1.15 christos const char *helper, *verbosity = NULL;
263 1.15 christos
264 1.15 christos if (log_level_get() >= SYSLOG_LEVEL_DEBUG1)
265 1.15 christos verbosity = "-vvv";
266 1.15 christos
267 1.15 christos if (pkcs11_start_helper_methods() == -1) {
268 1.15 christos error("pkcs11_start_helper_methods failed");
269 1.15 christos return (-1);
270 1.15 christos }
271 1.1 adam
272 1.1 adam if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
273 1.1 adam error("socketpair: %s", strerror(errno));
274 1.1 adam return (-1);
275 1.1 adam }
276 1.1 adam if ((pid = fork()) == -1) {
277 1.1 adam error("fork: %s", strerror(errno));
278 1.1 adam return (-1);
279 1.1 adam } else if (pid == 0) {
280 1.1 adam if ((dup2(pair[1], STDIN_FILENO) == -1) ||
281 1.1 adam (dup2(pair[1], STDOUT_FILENO) == -1)) {
282 1.1 adam fprintf(stderr, "dup2: %s\n", strerror(errno));
283 1.1 adam _exit(1);
284 1.1 adam }
285 1.1 adam close(pair[0]);
286 1.1 adam close(pair[1]);
287 1.15 christos helper = getenv("SSH_PKCS11_HELPER");
288 1.15 christos if (helper == NULL || strlen(helper) == 0)
289 1.15 christos helper = _PATH_SSH_PKCS11_HELPER;
290 1.15 christos debug("%s: starting %s %s", __func__, helper,
291 1.15 christos verbosity == NULL ? "" : verbosity);
292 1.15 christos execlp(helper, helper, verbosity, (char *)NULL);
293 1.15 christos fprintf(stderr, "exec: %s: %s\n", helper, strerror(errno));
294 1.1 adam _exit(1);
295 1.1 adam }
296 1.1 adam close(pair[1]);
297 1.1 adam fd = pair[0];
298 1.1 adam return (0);
299 1.1 adam }
300 1.1 adam
301 1.1 adam int
302 1.13 christos pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp)
303 1.1 adam {
304 1.10 christos struct sshkey *k;
305 1.15 christos int r, type;
306 1.1 adam u_char *blob;
307 1.13 christos size_t blen;
308 1.13 christos u_int nkeys, i;
309 1.13 christos struct sshbuf *msg;
310 1.1 adam
311 1.1 adam if (fd < 0 && pkcs11_start_helper() < 0)
312 1.1 adam return (-1);
313 1.1 adam
314 1.13 christos if ((msg = sshbuf_new()) == NULL)
315 1.13 christos fatal("%s: sshbuf_new failed", __func__);
316 1.13 christos if ((r = sshbuf_put_u8(msg, SSH_AGENTC_ADD_SMARTCARD_KEY)) != 0 ||
317 1.13 christos (r = sshbuf_put_cstring(msg, name)) != 0 ||
318 1.13 christos (r = sshbuf_put_cstring(msg, pin)) != 0)
319 1.13 christos fatal("%s: buffer error: %s", __func__, ssh_err(r));
320 1.13 christos send_msg(msg);
321 1.13 christos sshbuf_reset(msg);
322 1.13 christos
323 1.15 christos type = recv_msg(msg);
324 1.15 christos if (type == SSH2_AGENT_IDENTITIES_ANSWER) {
325 1.13 christos if ((r = sshbuf_get_u32(msg, &nkeys)) != 0)
326 1.13 christos fatal("%s: buffer error: %s", __func__, ssh_err(r));
327 1.13 christos *keysp = xcalloc(nkeys, sizeof(struct sshkey *));
328 1.1 adam for (i = 0; i < nkeys; i++) {
329 1.13 christos /* XXX clean up properly instead of fatal() */
330 1.13 christos if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 ||
331 1.13 christos (r = sshbuf_skip_string(msg)) != 0)
332 1.13 christos fatal("%s: buffer error: %s",
333 1.13 christos __func__, ssh_err(r));
334 1.13 christos if ((r = sshkey_from_blob(blob, blen, &k)) != 0)
335 1.13 christos fatal("%s: bad key: %s", __func__, ssh_err(r));
336 1.15 christos wrap_key(k);
337 1.1 adam (*keysp)[i] = k;
338 1.4 christos free(blob);
339 1.1 adam }
340 1.15 christos } else if (type == SSH2_AGENT_FAILURE) {
341 1.15 christos if ((r = sshbuf_get_u32(msg, &nkeys)) != 0)
342 1.15 christos nkeys = -1;
343 1.1 adam } else {
344 1.1 adam nkeys = -1;
345 1.1 adam }
346 1.13 christos sshbuf_free(msg);
347 1.1 adam return (nkeys);
348 1.1 adam }
349 1.1 adam
350 1.1 adam int
351 1.1 adam pkcs11_del_provider(char *name)
352 1.1 adam {
353 1.13 christos int r, ret = -1;
354 1.13 christos struct sshbuf *msg;
355 1.1 adam
356 1.13 christos if ((msg = sshbuf_new()) == NULL)
357 1.13 christos fatal("%s: sshbuf_new failed", __func__);
358 1.13 christos if ((r = sshbuf_put_u8(msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY)) != 0 ||
359 1.13 christos (r = sshbuf_put_cstring(msg, name)) != 0 ||
360 1.13 christos (r = sshbuf_put_cstring(msg, "")) != 0)
361 1.13 christos fatal("%s: buffer error: %s", __func__, ssh_err(r));
362 1.13 christos send_msg(msg);
363 1.13 christos sshbuf_reset(msg);
364 1.1 adam
365 1.13 christos if (recv_msg(msg) == SSH_AGENT_SUCCESS)
366 1.1 adam ret = 0;
367 1.13 christos sshbuf_free(msg);
368 1.1 adam return (ret);
369 1.1 adam }
370