xref: /freebsd/crypto/openssh/ssh-pkcs11-client.c (revision 190cef3d52236565eb22e18b33e9e865ec634aa3)
1*190cef3dSDag-Erling Smørgrav /* $OpenBSD: ssh-pkcs11-client.c,v 1.10 2018/07/09 21:59:10 markus Exp $ */
2b15c8340SDag-Erling Smørgrav /*
3b15c8340SDag-Erling Smørgrav  * Copyright (c) 2010 Markus Friedl.  All rights reserved.
4b15c8340SDag-Erling Smørgrav  *
5b15c8340SDag-Erling Smørgrav  * Permission to use, copy, modify, and distribute this software for any
6b15c8340SDag-Erling Smørgrav  * purpose with or without fee is hereby granted, provided that the above
7b15c8340SDag-Erling Smørgrav  * copyright notice and this permission notice appear in all copies.
8b15c8340SDag-Erling Smørgrav  *
9b15c8340SDag-Erling Smørgrav  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b15c8340SDag-Erling Smørgrav  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b15c8340SDag-Erling Smørgrav  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b15c8340SDag-Erling Smørgrav  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b15c8340SDag-Erling Smørgrav  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b15c8340SDag-Erling Smørgrav  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b15c8340SDag-Erling Smørgrav  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b15c8340SDag-Erling Smørgrav  */
17b15c8340SDag-Erling Smørgrav 
18b15c8340SDag-Erling Smørgrav #include "includes.h"
19b15c8340SDag-Erling Smørgrav 
20b15c8340SDag-Erling Smørgrav #ifdef ENABLE_PKCS11
21b15c8340SDag-Erling Smørgrav 
22b15c8340SDag-Erling Smørgrav #include <sys/types.h>
23b15c8340SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H
24b15c8340SDag-Erling Smørgrav # include <sys/time.h>
25b15c8340SDag-Erling Smørgrav #endif
26b15c8340SDag-Erling Smørgrav #include <sys/socket.h>
27b15c8340SDag-Erling Smørgrav 
28b15c8340SDag-Erling Smørgrav #include <stdarg.h>
29b15c8340SDag-Erling Smørgrav #include <string.h>
30b15c8340SDag-Erling Smørgrav #include <unistd.h>
31b15c8340SDag-Erling Smørgrav #include <errno.h>
32b15c8340SDag-Erling Smørgrav 
33a0ee8cc6SDag-Erling Smørgrav #include <openssl/rsa.h>
34a0ee8cc6SDag-Erling Smørgrav 
35b15c8340SDag-Erling Smørgrav #include "pathnames.h"
36b15c8340SDag-Erling Smørgrav #include "xmalloc.h"
37*190cef3dSDag-Erling Smørgrav #include "sshbuf.h"
38b15c8340SDag-Erling Smørgrav #include "log.h"
39b15c8340SDag-Erling Smørgrav #include "misc.h"
40*190cef3dSDag-Erling Smørgrav #include "sshkey.h"
41b15c8340SDag-Erling Smørgrav #include "authfd.h"
42b15c8340SDag-Erling Smørgrav #include "atomicio.h"
43b15c8340SDag-Erling Smørgrav #include "ssh-pkcs11.h"
44*190cef3dSDag-Erling Smørgrav #include "ssherr.h"
45b15c8340SDag-Erling Smørgrav 
46b15c8340SDag-Erling Smørgrav /* borrows code from sftp-server and ssh-agent */
47b15c8340SDag-Erling Smørgrav 
48b15c8340SDag-Erling Smørgrav int fd = -1;
49b15c8340SDag-Erling Smørgrav pid_t pid = -1;
50b15c8340SDag-Erling Smørgrav 
51b15c8340SDag-Erling Smørgrav static void
52*190cef3dSDag-Erling Smørgrav send_msg(struct sshbuf *m)
53b15c8340SDag-Erling Smørgrav {
54b15c8340SDag-Erling Smørgrav 	u_char buf[4];
55*190cef3dSDag-Erling Smørgrav 	size_t mlen = sshbuf_len(m);
56*190cef3dSDag-Erling Smørgrav 	int r;
57b15c8340SDag-Erling Smørgrav 
58*190cef3dSDag-Erling Smørgrav 	POKE_U32(buf, mlen);
59b15c8340SDag-Erling Smørgrav 	if (atomicio(vwrite, fd, buf, 4) != 4 ||
60*190cef3dSDag-Erling Smørgrav 	    atomicio(vwrite, fd, sshbuf_mutable_ptr(m),
61*190cef3dSDag-Erling Smørgrav 	    sshbuf_len(m)) != sshbuf_len(m))
62b15c8340SDag-Erling Smørgrav 		error("write to helper failed");
63*190cef3dSDag-Erling Smørgrav 	if ((r = sshbuf_consume(m, mlen)) != 0)
64*190cef3dSDag-Erling Smørgrav 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
65b15c8340SDag-Erling Smørgrav }
66b15c8340SDag-Erling Smørgrav 
67b15c8340SDag-Erling Smørgrav static int
68*190cef3dSDag-Erling Smørgrav recv_msg(struct sshbuf *m)
69b15c8340SDag-Erling Smørgrav {
70b15c8340SDag-Erling Smørgrav 	u_int l, len;
71*190cef3dSDag-Erling Smørgrav 	u_char c, buf[1024];
72*190cef3dSDag-Erling Smørgrav 	int r;
73b15c8340SDag-Erling Smørgrav 
74b15c8340SDag-Erling Smørgrav 	if ((len = atomicio(read, fd, buf, 4)) != 4) {
75b15c8340SDag-Erling Smørgrav 		error("read from helper failed: %u", len);
76b15c8340SDag-Erling Smørgrav 		return (0); /* XXX */
77b15c8340SDag-Erling Smørgrav 	}
78*190cef3dSDag-Erling Smørgrav 	len = PEEK_U32(buf);
79b15c8340SDag-Erling Smørgrav 	if (len > 256 * 1024)
80b15c8340SDag-Erling Smørgrav 		fatal("response too long: %u", len);
81b15c8340SDag-Erling Smørgrav 	/* read len bytes into m */
82*190cef3dSDag-Erling Smørgrav 	sshbuf_reset(m);
83b15c8340SDag-Erling Smørgrav 	while (len > 0) {
84b15c8340SDag-Erling Smørgrav 		l = len;
85b15c8340SDag-Erling Smørgrav 		if (l > sizeof(buf))
86b15c8340SDag-Erling Smørgrav 			l = sizeof(buf);
87b15c8340SDag-Erling Smørgrav 		if (atomicio(read, fd, buf, l) != l) {
88b15c8340SDag-Erling Smørgrav 			error("response from helper failed.");
89b15c8340SDag-Erling Smørgrav 			return (0); /* XXX */
90b15c8340SDag-Erling Smørgrav 		}
91*190cef3dSDag-Erling Smørgrav 		if ((r = sshbuf_put(m, buf, l)) != 0)
92*190cef3dSDag-Erling Smørgrav 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
93b15c8340SDag-Erling Smørgrav 		len -= l;
94b15c8340SDag-Erling Smørgrav 	}
95*190cef3dSDag-Erling Smørgrav 	if ((r = sshbuf_get_u8(m, &c)) != 0)
96*190cef3dSDag-Erling Smørgrav 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
97*190cef3dSDag-Erling Smørgrav 	return c;
98b15c8340SDag-Erling Smørgrav }
99b15c8340SDag-Erling Smørgrav 
100b15c8340SDag-Erling Smørgrav int
101b15c8340SDag-Erling Smørgrav pkcs11_init(int interactive)
102b15c8340SDag-Erling Smørgrav {
103b15c8340SDag-Erling Smørgrav 	return (0);
104b15c8340SDag-Erling Smørgrav }
105b15c8340SDag-Erling Smørgrav 
106b15c8340SDag-Erling Smørgrav void
107b15c8340SDag-Erling Smørgrav pkcs11_terminate(void)
108b15c8340SDag-Erling Smørgrav {
10947dd1d1bSDag-Erling Smørgrav 	if (fd >= 0)
110b15c8340SDag-Erling Smørgrav 		close(fd);
111b15c8340SDag-Erling Smørgrav }
112b15c8340SDag-Erling Smørgrav 
113b15c8340SDag-Erling Smørgrav static int
114b15c8340SDag-Erling Smørgrav pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
115b15c8340SDag-Erling Smørgrav     int padding)
116b15c8340SDag-Erling Smørgrav {
1174f52dfbbSDag-Erling Smørgrav 	struct sshkey key;	/* XXX */
118b15c8340SDag-Erling Smørgrav 	u_char *blob, *signature = NULL;
119*190cef3dSDag-Erling Smørgrav 	size_t blen, slen = 0;
120*190cef3dSDag-Erling Smørgrav 	int r, ret = -1;
121*190cef3dSDag-Erling Smørgrav 	struct sshbuf *msg;
122b15c8340SDag-Erling Smørgrav 
123b15c8340SDag-Erling Smørgrav 	if (padding != RSA_PKCS1_PADDING)
124b15c8340SDag-Erling Smørgrav 		return (-1);
125b15c8340SDag-Erling Smørgrav 	key.type = KEY_RSA;
126b15c8340SDag-Erling Smørgrav 	key.rsa = rsa;
127*190cef3dSDag-Erling Smørgrav 	if ((r = sshkey_to_blob(&key, &blob, &blen)) != 0) {
128*190cef3dSDag-Erling Smørgrav 		error("%s: sshkey_to_blob: %s", __func__, ssh_err(r));
129b15c8340SDag-Erling Smørgrav 		return -1;
130*190cef3dSDag-Erling Smørgrav 	}
131*190cef3dSDag-Erling Smørgrav 	if ((msg = sshbuf_new()) == NULL)
132*190cef3dSDag-Erling Smørgrav 		fatal("%s: sshbuf_new failed", __func__);
133*190cef3dSDag-Erling Smørgrav 	if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
134*190cef3dSDag-Erling Smørgrav 	    (r = sshbuf_put_string(msg, blob, blen)) != 0 ||
135*190cef3dSDag-Erling Smørgrav 	    (r = sshbuf_put_string(msg, from, flen)) != 0 ||
136*190cef3dSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(msg, 0)) != 0)
137*190cef3dSDag-Erling Smørgrav 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
138e4a9863fSDag-Erling Smørgrav 	free(blob);
139*190cef3dSDag-Erling Smørgrav 	send_msg(msg);
140*190cef3dSDag-Erling Smørgrav 	sshbuf_reset(msg);
141b15c8340SDag-Erling Smørgrav 
142*190cef3dSDag-Erling Smørgrav 	if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) {
143*190cef3dSDag-Erling Smørgrav 		if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0)
144*190cef3dSDag-Erling Smørgrav 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
145*190cef3dSDag-Erling Smørgrav 		if (slen <= (size_t)RSA_size(rsa)) {
146b15c8340SDag-Erling Smørgrav 			memcpy(to, signature, slen);
147b15c8340SDag-Erling Smørgrav 			ret = slen;
148b15c8340SDag-Erling Smørgrav 		}
149e4a9863fSDag-Erling Smørgrav 		free(signature);
150b15c8340SDag-Erling Smørgrav 	}
151*190cef3dSDag-Erling Smørgrav 	sshbuf_free(msg);
152b15c8340SDag-Erling Smørgrav 	return (ret);
153b15c8340SDag-Erling Smørgrav }
154b15c8340SDag-Erling Smørgrav 
155b15c8340SDag-Erling Smørgrav /* redirect the private key encrypt operation to the ssh-pkcs11-helper */
156b15c8340SDag-Erling Smørgrav static int
157b15c8340SDag-Erling Smørgrav wrap_key(RSA *rsa)
158b15c8340SDag-Erling Smørgrav {
159b15c8340SDag-Erling Smørgrav 	static RSA_METHOD helper_rsa;
160b15c8340SDag-Erling Smørgrav 
161b15c8340SDag-Erling Smørgrav 	memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa));
162b15c8340SDag-Erling Smørgrav 	helper_rsa.name = "ssh-pkcs11-helper";
163b15c8340SDag-Erling Smørgrav 	helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt;
164b15c8340SDag-Erling Smørgrav 	RSA_set_method(rsa, &helper_rsa);
165b15c8340SDag-Erling Smørgrav 	return (0);
166b15c8340SDag-Erling Smørgrav }
167b15c8340SDag-Erling Smørgrav 
168b15c8340SDag-Erling Smørgrav static int
169b15c8340SDag-Erling Smørgrav pkcs11_start_helper(void)
170b15c8340SDag-Erling Smørgrav {
171b15c8340SDag-Erling Smørgrav 	int pair[2];
172b15c8340SDag-Erling Smørgrav 
173b15c8340SDag-Erling Smørgrav 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
174b15c8340SDag-Erling Smørgrav 		error("socketpair: %s", strerror(errno));
175b15c8340SDag-Erling Smørgrav 		return (-1);
176b15c8340SDag-Erling Smørgrav 	}
177b15c8340SDag-Erling Smørgrav 	if ((pid = fork()) == -1) {
178b15c8340SDag-Erling Smørgrav 		error("fork: %s", strerror(errno));
179b15c8340SDag-Erling Smørgrav 		return (-1);
180b15c8340SDag-Erling Smørgrav 	} else if (pid == 0) {
181b15c8340SDag-Erling Smørgrav 		if ((dup2(pair[1], STDIN_FILENO) == -1) ||
182b15c8340SDag-Erling Smørgrav 		    (dup2(pair[1], STDOUT_FILENO) == -1)) {
183b15c8340SDag-Erling Smørgrav 			fprintf(stderr, "dup2: %s\n", strerror(errno));
184b15c8340SDag-Erling Smørgrav 			_exit(1);
185b15c8340SDag-Erling Smørgrav 		}
186b15c8340SDag-Erling Smørgrav 		close(pair[0]);
187b15c8340SDag-Erling Smørgrav 		close(pair[1]);
188b15c8340SDag-Erling Smørgrav 		execlp(_PATH_SSH_PKCS11_HELPER, _PATH_SSH_PKCS11_HELPER,
189acc1a9efSDag-Erling Smørgrav 		    (char *)NULL);
190b15c8340SDag-Erling Smørgrav 		fprintf(stderr, "exec: %s: %s\n", _PATH_SSH_PKCS11_HELPER,
191b15c8340SDag-Erling Smørgrav 		    strerror(errno));
192b15c8340SDag-Erling Smørgrav 		_exit(1);
193b15c8340SDag-Erling Smørgrav 	}
194b15c8340SDag-Erling Smørgrav 	close(pair[1]);
195b15c8340SDag-Erling Smørgrav 	fd = pair[0];
196b15c8340SDag-Erling Smørgrav 	return (0);
197b15c8340SDag-Erling Smørgrav }
198b15c8340SDag-Erling Smørgrav 
199b15c8340SDag-Erling Smørgrav int
200*190cef3dSDag-Erling Smørgrav pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp)
201b15c8340SDag-Erling Smørgrav {
2024f52dfbbSDag-Erling Smørgrav 	struct sshkey *k;
203*190cef3dSDag-Erling Smørgrav 	int r;
204b15c8340SDag-Erling Smørgrav 	u_char *blob;
205*190cef3dSDag-Erling Smørgrav 	size_t blen;
206*190cef3dSDag-Erling Smørgrav 	u_int nkeys, i;
207*190cef3dSDag-Erling Smørgrav 	struct sshbuf *msg;
208b15c8340SDag-Erling Smørgrav 
209b15c8340SDag-Erling Smørgrav 	if (fd < 0 && pkcs11_start_helper() < 0)
210b15c8340SDag-Erling Smørgrav 		return (-1);
211b15c8340SDag-Erling Smørgrav 
212*190cef3dSDag-Erling Smørgrav 	if ((msg = sshbuf_new()) == NULL)
213*190cef3dSDag-Erling Smørgrav 		fatal("%s: sshbuf_new failed", __func__);
214*190cef3dSDag-Erling Smørgrav 	if ((r = sshbuf_put_u8(msg, SSH_AGENTC_ADD_SMARTCARD_KEY)) != 0 ||
215*190cef3dSDag-Erling Smørgrav 	    (r = sshbuf_put_cstring(msg, name)) != 0 ||
216*190cef3dSDag-Erling Smørgrav 	    (r = sshbuf_put_cstring(msg, pin)) != 0)
217*190cef3dSDag-Erling Smørgrav 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
218*190cef3dSDag-Erling Smørgrav 	send_msg(msg);
219*190cef3dSDag-Erling Smørgrav 	sshbuf_reset(msg);
220b15c8340SDag-Erling Smørgrav 
221*190cef3dSDag-Erling Smørgrav 	if (recv_msg(msg) == SSH2_AGENT_IDENTITIES_ANSWER) {
222*190cef3dSDag-Erling Smørgrav 		if ((r = sshbuf_get_u32(msg, &nkeys)) != 0)
223*190cef3dSDag-Erling Smørgrav 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
224*190cef3dSDag-Erling Smørgrav 		*keysp = xcalloc(nkeys, sizeof(struct sshkey *));
225b15c8340SDag-Erling Smørgrav 		for (i = 0; i < nkeys; i++) {
226*190cef3dSDag-Erling Smørgrav 			/* XXX clean up properly instead of fatal() */
227*190cef3dSDag-Erling Smørgrav 			if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 ||
228*190cef3dSDag-Erling Smørgrav 			    (r = sshbuf_skip_string(msg)) != 0)
229*190cef3dSDag-Erling Smørgrav 				fatal("%s: buffer error: %s",
230*190cef3dSDag-Erling Smørgrav 				    __func__, ssh_err(r));
231*190cef3dSDag-Erling Smørgrav 			if ((r = sshkey_from_blob(blob, blen, &k)) != 0)
232*190cef3dSDag-Erling Smørgrav 				fatal("%s: bad key: %s", __func__, ssh_err(r));
233b15c8340SDag-Erling Smørgrav 			wrap_key(k->rsa);
234b15c8340SDag-Erling Smørgrav 			(*keysp)[i] = k;
235e4a9863fSDag-Erling Smørgrav 			free(blob);
236b15c8340SDag-Erling Smørgrav 		}
237b15c8340SDag-Erling Smørgrav 	} else {
238b15c8340SDag-Erling Smørgrav 		nkeys = -1;
239b15c8340SDag-Erling Smørgrav 	}
240*190cef3dSDag-Erling Smørgrav 	sshbuf_free(msg);
241b15c8340SDag-Erling Smørgrav 	return (nkeys);
242b15c8340SDag-Erling Smørgrav }
243b15c8340SDag-Erling Smørgrav 
244b15c8340SDag-Erling Smørgrav int
245b15c8340SDag-Erling Smørgrav pkcs11_del_provider(char *name)
246b15c8340SDag-Erling Smørgrav {
247*190cef3dSDag-Erling Smørgrav 	int r, ret = -1;
248*190cef3dSDag-Erling Smørgrav 	struct sshbuf *msg;
249b15c8340SDag-Erling Smørgrav 
250*190cef3dSDag-Erling Smørgrav 	if ((msg = sshbuf_new()) == NULL)
251*190cef3dSDag-Erling Smørgrav 		fatal("%s: sshbuf_new failed", __func__);
252*190cef3dSDag-Erling Smørgrav 	if ((r = sshbuf_put_u8(msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY)) != 0 ||
253*190cef3dSDag-Erling Smørgrav 	    (r = sshbuf_put_cstring(msg, name)) != 0 ||
254*190cef3dSDag-Erling Smørgrav 	    (r = sshbuf_put_cstring(msg, "")) != 0)
255*190cef3dSDag-Erling Smørgrav 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
256*190cef3dSDag-Erling Smørgrav 	send_msg(msg);
257*190cef3dSDag-Erling Smørgrav 	sshbuf_reset(msg);
258b15c8340SDag-Erling Smørgrav 
259*190cef3dSDag-Erling Smørgrav 	if (recv_msg(msg) == SSH_AGENT_SUCCESS)
260b15c8340SDag-Erling Smørgrav 		ret = 0;
261*190cef3dSDag-Erling Smørgrav 	sshbuf_free(msg);
262b15c8340SDag-Erling Smørgrav 	return (ret);
263b15c8340SDag-Erling Smørgrav }
264b15c8340SDag-Erling Smørgrav 
265b15c8340SDag-Erling Smørgrav #endif /* ENABLE_PKCS11 */
266