blob: bcc18c6bc46631f2e20b9b9e55d63fab2e9e11d4 [file] [log] [blame]
markus@openbsd.org49f47e62018-07-09 21:59:10 +00001/* $OpenBSD: ssh-pkcs11-client.c,v 1.10 2018/07/09 21:59:10 markus Exp $ */
Damien Miller7ea845e2010-02-12 09:21:02 +11002/*
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
Damien Miller8ad0fbd2010-02-12 09:49:06 +110018#include "includes.h"
19
Damien Millerdfa41562010-02-12 10:06:28 +110020#ifdef ENABLE_PKCS11
21
Damien Miller7ea845e2010-02-12 09:21:02 +110022#include <sys/types.h>
Damien Miller8ad0fbd2010-02-12 09:49:06 +110023#ifdef HAVE_SYS_TIME_H
24# include <sys/time.h>
25#endif
Damien Miller7ea845e2010-02-12 09:21:02 +110026#include <sys/socket.h>
27
28#include <stdarg.h>
29#include <string.h>
30#include <unistd.h>
31#include <errno.h>
32
Damien Miller86687062014-07-02 15:28:02 +100033#include <openssl/rsa.h>
34
Damien Miller7ea845e2010-02-12 09:21:02 +110035#include "pathnames.h"
36#include "xmalloc.h"
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000037#include "sshbuf.h"
Damien Miller7ea845e2010-02-12 09:21:02 +110038#include "log.h"
39#include "misc.h"
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000040#include "sshkey.h"
Damien Miller7ea845e2010-02-12 09:21:02 +110041#include "authfd.h"
42#include "atomicio.h"
43#include "ssh-pkcs11.h"
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000044#include "ssherr.h"
Damien Miller7ea845e2010-02-12 09:21:02 +110045
46/* borrows code from sftp-server and ssh-agent */
47
48int fd = -1;
49pid_t pid = -1;
50
51static void
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000052send_msg(struct sshbuf *m)
Damien Miller7ea845e2010-02-12 09:21:02 +110053{
54 u_char buf[4];
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000055 size_t mlen = sshbuf_len(m);
56 int r;
Damien Miller7ea845e2010-02-12 09:21:02 +110057
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000058 POKE_U32(buf, mlen);
Damien Miller7ea845e2010-02-12 09:21:02 +110059 if (atomicio(vwrite, fd, buf, 4) != 4 ||
markus@openbsd.org49f47e62018-07-09 21:59:10 +000060 atomicio(vwrite, fd, sshbuf_mutable_ptr(m),
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000061 sshbuf_len(m)) != sshbuf_len(m))
Damien Miller7ea845e2010-02-12 09:21:02 +110062 error("write to helper failed");
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000063 if ((r = sshbuf_consume(m, mlen)) != 0)
64 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +110065}
66
67static int
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000068recv_msg(struct sshbuf *m)
Damien Miller7ea845e2010-02-12 09:21:02 +110069{
70 u_int l, len;
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000071 u_char c, buf[1024];
72 int r;
Damien Miller7ea845e2010-02-12 09:21:02 +110073
74 if ((len = atomicio(read, fd, buf, 4)) != 4) {
75 error("read from helper failed: %u", len);
76 return (0); /* XXX */
77 }
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000078 len = PEEK_U32(buf);
Damien Miller7ea845e2010-02-12 09:21:02 +110079 if (len > 256 * 1024)
80 fatal("response too long: %u", len);
81 /* read len bytes into m */
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000082 sshbuf_reset(m);
Damien Miller7ea845e2010-02-12 09:21:02 +110083 while (len > 0) {
84 l = len;
85 if (l > sizeof(buf))
86 l = sizeof(buf);
87 if (atomicio(read, fd, buf, l) != l) {
88 error("response from helper failed.");
89 return (0); /* XXX */
90 }
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000091 if ((r = sshbuf_put(m, buf, l)) != 0)
92 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +110093 len -= l;
94 }
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +000095 if ((r = sshbuf_get_u8(m, &c)) != 0)
96 fatal("%s: buffer error: %s", __func__, ssh_err(r));
97 return c;
Damien Miller7ea845e2010-02-12 09:21:02 +110098}
99
100int
101pkcs11_init(int interactive)
102{
103 return (0);
104}
105
106void
107pkcs11_terminate(void)
108{
tb@openbsd.org34843802018-02-05 05:37:46 +0000109 if (fd >= 0)
110 close(fd);
Damien Miller7ea845e2010-02-12 09:21:02 +1100111}
112
113static int
114pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
115 int padding)
116{
markus@openbsd.org54d90ac2017-05-30 08:52:19 +0000117 struct sshkey key; /* XXX */
Damien Miller7ea845e2010-02-12 09:21:02 +1100118 u_char *blob, *signature = NULL;
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000119 size_t blen, slen = 0;
120 int r, ret = -1;
121 struct sshbuf *msg;
Damien Miller7ea845e2010-02-12 09:21:02 +1100122
123 if (padding != RSA_PKCS1_PADDING)
124 return (-1);
125 key.type = KEY_RSA;
126 key.rsa = rsa;
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000127 if ((r = sshkey_to_blob(&key, &blob, &blen)) != 0) {
128 error("%s: sshkey_to_blob: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100129 return -1;
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000130 }
131 if ((msg = sshbuf_new()) == NULL)
132 fatal("%s: sshbuf_new failed", __func__);
133 if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
134 (r = sshbuf_put_string(msg, blob, blen)) != 0 ||
135 (r = sshbuf_put_string(msg, from, flen)) != 0 ||
136 (r = sshbuf_put_u32(msg, 0)) != 0)
137 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Darren Tuckera627d422013-06-02 07:31:17 +1000138 free(blob);
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000139 send_msg(msg);
140 sshbuf_reset(msg);
Damien Miller7ea845e2010-02-12 09:21:02 +1100141
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000142 if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) {
143 if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0)
144 fatal("%s: buffer error: %s", __func__, ssh_err(r));
145 if (slen <= (size_t)RSA_size(rsa)) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100146 memcpy(to, signature, slen);
147 ret = slen;
148 }
Darren Tuckera627d422013-06-02 07:31:17 +1000149 free(signature);
Damien Miller7ea845e2010-02-12 09:21:02 +1100150 }
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000151 sshbuf_free(msg);
Damien Miller7ea845e2010-02-12 09:21:02 +1100152 return (ret);
153}
154
155/* redirect the private key encrypt operation to the ssh-pkcs11-helper */
156static int
157wrap_key(RSA *rsa)
158{
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000159 static RSA_METHOD *helper_rsa;
Damien Miller7ea845e2010-02-12 09:21:02 +1100160
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000161 if ((helper_rsa = RSA_meth_dup(RSA_get_default_method())) == NULL)
162 fatal("%s: RSA_meth_dup failed", __func__);
163 if (!RSA_meth_set1_name(helper_rsa, "ssh-pkcs11-helper") ||
164 !RSA_meth_set_priv_enc(helper_rsa, pkcs11_rsa_private_encrypt))
165 fatal("%s: failed to prepare method", __func__);
166 RSA_set_method(rsa, helper_rsa);
Damien Miller7ea845e2010-02-12 09:21:02 +1100167 return (0);
168}
169
170static int
171pkcs11_start_helper(void)
172{
173 int pair[2];
174
175 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
176 error("socketpair: %s", strerror(errno));
177 return (-1);
178 }
179 if ((pid = fork()) == -1) {
180 error("fork: %s", strerror(errno));
181 return (-1);
182 } else if (pid == 0) {
183 if ((dup2(pair[1], STDIN_FILENO) == -1) ||
184 (dup2(pair[1], STDOUT_FILENO) == -1)) {
185 fprintf(stderr, "dup2: %s\n", strerror(errno));
186 _exit(1);
187 }
188 close(pair[0]);
189 close(pair[1]);
190 execlp(_PATH_SSH_PKCS11_HELPER, _PATH_SSH_PKCS11_HELPER,
mmcc@openbsd.org94141b72015-12-11 00:20:04 +0000191 (char *)NULL);
Damien Miller7ea845e2010-02-12 09:21:02 +1100192 fprintf(stderr, "exec: %s: %s\n", _PATH_SSH_PKCS11_HELPER,
193 strerror(errno));
194 _exit(1);
195 }
196 close(pair[1]);
197 fd = pair[0];
198 return (0);
199}
200
201int
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000202pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp)
Damien Miller7ea845e2010-02-12 09:21:02 +1100203{
markus@openbsd.org54d90ac2017-05-30 08:52:19 +0000204 struct sshkey *k;
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000205 int r;
Damien Miller7ea845e2010-02-12 09:21:02 +1100206 u_char *blob;
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000207 size_t blen;
208 u_int nkeys, i;
209 struct sshbuf *msg;
Damien Miller7ea845e2010-02-12 09:21:02 +1100210
211 if (fd < 0 && pkcs11_start_helper() < 0)
212 return (-1);
213
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000214 if ((msg = sshbuf_new()) == NULL)
215 fatal("%s: sshbuf_new failed", __func__);
216 if ((r = sshbuf_put_u8(msg, SSH_AGENTC_ADD_SMARTCARD_KEY)) != 0 ||
217 (r = sshbuf_put_cstring(msg, name)) != 0 ||
218 (r = sshbuf_put_cstring(msg, pin)) != 0)
219 fatal("%s: buffer error: %s", __func__, ssh_err(r));
220 send_msg(msg);
221 sshbuf_reset(msg);
Damien Miller7ea845e2010-02-12 09:21:02 +1100222
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000223 if (recv_msg(msg) == SSH2_AGENT_IDENTITIES_ANSWER) {
224 if ((r = sshbuf_get_u32(msg, &nkeys)) != 0)
225 fatal("%s: buffer error: %s", __func__, ssh_err(r));
226 *keysp = xcalloc(nkeys, sizeof(struct sshkey *));
Damien Miller7ea845e2010-02-12 09:21:02 +1100227 for (i = 0; i < nkeys; i++) {
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000228 /* XXX clean up properly instead of fatal() */
229 if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 ||
230 (r = sshbuf_skip_string(msg)) != 0)
231 fatal("%s: buffer error: %s",
232 __func__, ssh_err(r));
233 if ((r = sshkey_from_blob(blob, blen, &k)) != 0)
234 fatal("%s: bad key: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100235 wrap_key(k->rsa);
236 (*keysp)[i] = k;
Darren Tuckera627d422013-06-02 07:31:17 +1000237 free(blob);
Damien Miller7ea845e2010-02-12 09:21:02 +1100238 }
239 } else {
240 nkeys = -1;
241 }
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000242 sshbuf_free(msg);
Damien Miller7ea845e2010-02-12 09:21:02 +1100243 return (nkeys);
244}
245
246int
247pkcs11_del_provider(char *name)
248{
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000249 int r, ret = -1;
250 struct sshbuf *msg;
Damien Miller7ea845e2010-02-12 09:21:02 +1100251
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000252 if ((msg = sshbuf_new()) == NULL)
253 fatal("%s: sshbuf_new failed", __func__);
254 if ((r = sshbuf_put_u8(msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY)) != 0 ||
255 (r = sshbuf_put_cstring(msg, name)) != 0 ||
256 (r = sshbuf_put_cstring(msg, "")) != 0)
257 fatal("%s: buffer error: %s", __func__, ssh_err(r));
258 send_msg(msg);
259 sshbuf_reset(msg);
Damien Miller7ea845e2010-02-12 09:21:02 +1100260
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000261 if (recv_msg(msg) == SSH_AGENT_SUCCESS)
Damien Miller7ea845e2010-02-12 09:21:02 +1100262 ret = 0;
markus@openbsd.orgff55f4a2018-07-09 20:39:28 +0000263 sshbuf_free(msg);
Damien Miller7ea845e2010-02-12 09:21:02 +1100264 return (ret);
265}
Damien Millerdfa41562010-02-12 10:06:28 +1100266
267#endif /* ENABLE_PKCS11 */