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