blob: 92c6728ba654f1ddc8bf814c6711bb14f91c0464 [file] [log] [blame]
djm@openbsd.org93f02102019-01-20 22:51:37 +00001/* $OpenBSD: ssh-pkcs11-helper.c,v 1.15 2019/01/20 22:51:37 djm 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 Miller7ea845e2010-02-12 09:21:02 +110020#include <sys/types.h>
Damien Miller8ad0fbd2010-02-12 09:49:06 +110021#ifdef HAVE_SYS_TIME_H
22# include <sys/time.h>
23#endif
24
25#include "openbsd-compat/sys-queue.h"
Damien Miller7ea845e2010-02-12 09:21:02 +110026
27#include <stdarg.h>
28#include <string.h>
29#include <unistd.h>
30#include <errno.h>
31
32#include "xmalloc.h"
markus@openbsd.orgb0d34132018-01-08 15:18:46 +000033#include "sshbuf.h"
Damien Miller7ea845e2010-02-12 09:21:02 +110034#include "log.h"
35#include "misc.h"
markus@openbsd.orgb0d34132018-01-08 15:18:46 +000036#include "sshkey.h"
Damien Miller7ea845e2010-02-12 09:21:02 +110037#include "authfd.h"
38#include "ssh-pkcs11.h"
markus@openbsd.orgb0d34132018-01-08 15:18:46 +000039#include "ssherr.h"
Damien Miller7ea845e2010-02-12 09:21:02 +110040
Damien Miller47f9a412010-03-14 08:37:49 +110041#ifdef ENABLE_PKCS11
42
Damien Miller7ea845e2010-02-12 09:21:02 +110043/* borrows code from sftp-server and ssh-agent */
44
45struct pkcs11_keyinfo {
markus@openbsd.org54d90ac2017-05-30 08:52:19 +000046 struct sshkey *key;
Damien Miller7ea845e2010-02-12 09:21:02 +110047 char *providername;
48 TAILQ_ENTRY(pkcs11_keyinfo) next;
49};
50
51TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist;
52
53#define MAX_MSG_LENGTH 10240 /*XXX*/
54
Damien Miller7ea845e2010-02-12 09:21:02 +110055/* input and output queue */
markus@openbsd.orgb0d34132018-01-08 15:18:46 +000056struct sshbuf *iqueue;
57struct sshbuf *oqueue;
Damien Miller7ea845e2010-02-12 09:21:02 +110058
59static void
markus@openbsd.org54d90ac2017-05-30 08:52:19 +000060add_key(struct sshkey *k, char *name)
Damien Miller7ea845e2010-02-12 09:21:02 +110061{
62 struct pkcs11_keyinfo *ki;
63
64 ki = xcalloc(1, sizeof(*ki));
65 ki->providername = xstrdup(name);
66 ki->key = k;
67 TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next);
68}
69
70static void
71del_keys_by_name(char *name)
72{
73 struct pkcs11_keyinfo *ki, *nxt;
74
75 for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) {
76 nxt = TAILQ_NEXT(ki, next);
77 if (!strcmp(ki->providername, name)) {
78 TAILQ_REMOVE(&pkcs11_keylist, ki, next);
Darren Tuckera627d422013-06-02 07:31:17 +100079 free(ki->providername);
markus@openbsd.orgb0d34132018-01-08 15:18:46 +000080 sshkey_free(ki->key);
Damien Miller7ea845e2010-02-12 09:21:02 +110081 free(ki);
82 }
83 }
84}
85
86/* lookup matching 'private' key */
markus@openbsd.org54d90ac2017-05-30 08:52:19 +000087static struct sshkey *
88lookup_key(struct sshkey *k)
Damien Miller7ea845e2010-02-12 09:21:02 +110089{
90 struct pkcs11_keyinfo *ki;
91
92 TAILQ_FOREACH(ki, &pkcs11_keylist, next) {
93 debug("check %p %s", ki, ki->providername);
markus@openbsd.orgb0d34132018-01-08 15:18:46 +000094 if (sshkey_equal(k, ki->key))
Damien Miller7ea845e2010-02-12 09:21:02 +110095 return (ki->key);
96 }
97 return (NULL);
98}
99
100static void
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000101send_msg(struct sshbuf *m)
Damien Miller7ea845e2010-02-12 09:21:02 +1100102{
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000103 int r;
Damien Miller7ea845e2010-02-12 09:21:02 +1100104
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000105 if ((r = sshbuf_put_stringb(oqueue, m)) != 0)
106 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100107}
108
109static void
110process_add(void)
111{
112 char *name, *pin;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000113 struct sshkey **keys = NULL;
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000114 int r, i, nkeys;
Damien Miller7ea845e2010-02-12 09:21:02 +1100115 u_char *blob;
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000116 size_t blen;
117 struct sshbuf *msg;
Damien Miller7ea845e2010-02-12 09:21:02 +1100118
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000119 if ((msg = sshbuf_new()) == NULL)
120 fatal("%s: sshbuf_new failed", __func__);
121 if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 ||
122 (r = sshbuf_get_cstring(iqueue, &pin, NULL)) != 0)
123 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100124 if ((nkeys = pkcs11_add_provider(name, pin, &keys)) > 0) {
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000125 if ((r = sshbuf_put_u8(msg,
126 SSH2_AGENT_IDENTITIES_ANSWER)) != 0 ||
127 (r = sshbuf_put_u32(msg, nkeys)) != 0)
128 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100129 for (i = 0; i < nkeys; i++) {
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000130 if ((r = sshkey_to_blob(keys[i], &blob, &blen)) != 0) {
131 debug("%s: sshkey_to_blob: %s",
132 __func__, ssh_err(r));
Damien Millerf1e44ea2013-12-05 10:23:21 +1100133 continue;
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000134 }
135 if ((r = sshbuf_put_string(msg, blob, blen)) != 0 ||
136 (r = sshbuf_put_cstring(msg, name)) != 0)
137 fatal("%s: buffer error: %s",
138 __func__, ssh_err(r));
Darren Tuckera627d422013-06-02 07:31:17 +1000139 free(blob);
Damien Miller7ea845e2010-02-12 09:21:02 +1100140 add_key(keys[i], name);
141 }
Damien Miller7ea845e2010-02-12 09:21:02 +1100142 } else {
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000143 if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0)
144 fatal("%s: buffer error: %s", __func__, ssh_err(r));
djm@openbsd.org93f02102019-01-20 22:51:37 +0000145 if ((r = sshbuf_put_u32(msg, -nkeys)) != 0)
146 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100147 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000148 free(keys);
Darren Tuckera627d422013-06-02 07:31:17 +1000149 free(pin);
150 free(name);
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000151 send_msg(msg);
152 sshbuf_free(msg);
Damien Miller7ea845e2010-02-12 09:21:02 +1100153}
154
155static void
156process_del(void)
157{
158 char *name, *pin;
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000159 struct sshbuf *msg;
160 int r;
Damien Miller7ea845e2010-02-12 09:21:02 +1100161
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000162 if ((msg = sshbuf_new()) == NULL)
163 fatal("%s: sshbuf_new failed", __func__);
164 if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 ||
165 (r = sshbuf_get_cstring(iqueue, &pin, NULL)) != 0)
166 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100167 del_keys_by_name(name);
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000168 if ((r = sshbuf_put_u8(msg, pkcs11_del_provider(name) == 0 ?
169 SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0)
170 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Darren Tuckera627d422013-06-02 07:31:17 +1000171 free(pin);
172 free(name);
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000173 send_msg(msg);
174 sshbuf_free(msg);
Damien Miller7ea845e2010-02-12 09:21:02 +1100175}
176
177static void
178process_sign(void)
179{
180 u_char *blob, *data, *signature = NULL;
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000181 size_t blen, dlen, slen = 0;
182 int r, ok = -1;
markus@openbsd.org54d90ac2017-05-30 08:52:19 +0000183 struct sshkey *key, *found;
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000184 struct sshbuf *msg;
Damien Miller7ea845e2010-02-12 09:21:02 +1100185
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000186 /* XXX support SHA2 signature flags */
187 if ((r = sshbuf_get_string(iqueue, &blob, &blen)) != 0 ||
188 (r = sshbuf_get_string(iqueue, &data, &dlen)) != 0 ||
189 (r = sshbuf_get_u32(iqueue, NULL)) != 0)
190 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100191
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000192 if ((r = sshkey_from_blob(blob, blen, &key)) != 0)
193 error("%s: sshkey_from_blob: %s", __func__, ssh_err(r));
194 else {
Damien Miller7ea845e2010-02-12 09:21:02 +1100195 if ((found = lookup_key(key)) != NULL) {
Damien Miller86687062014-07-02 15:28:02 +1000196#ifdef WITH_OPENSSL
djm@openbsd.org93f02102019-01-20 22:51:37 +0000197 u_int xslen;
Damien Miller86687062014-07-02 15:28:02 +1000198 int ret;
199
djm@openbsd.org93f02102019-01-20 22:51:37 +0000200 if (key->type == KEY_RSA) {
201 slen = RSA_size(key->rsa);
202 signature = xmalloc(slen);
203 ret = RSA_private_encrypt(dlen, data, signature,
204 found->rsa, RSA_PKCS1_PADDING);
205 if (ret != -1) {
206 slen = ret;
207 ok = 0;
208 }
209 } else if (key->type == KEY_ECDSA) {
210 xslen = ECDSA_size(key->ecdsa);
211 signature = xmalloc(xslen);
212 /* "The parameter type is ignored." */
213 ret = ECDSA_sign(-1, data, dlen, signature,
214 &xslen, found->ecdsa);
215 if (ret != 0)
216 ok = 0;
217 else
218 error("%s: ECDSA_sign"
219 " returns %d", __func__, ret);
220 slen = xslen;
221 } else
222 error("%s: don't know how to sign with key "
223 "type %d", __func__, (int)key->type);
Damien Miller86687062014-07-02 15:28:02 +1000224#endif /* WITH_OPENSSL */
Damien Miller7ea845e2010-02-12 09:21:02 +1100225 }
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000226 sshkey_free(key);
Damien Miller7ea845e2010-02-12 09:21:02 +1100227 }
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000228 if ((msg = sshbuf_new()) == NULL)
229 fatal("%s: sshbuf_new failed", __func__);
Damien Miller7ea845e2010-02-12 09:21:02 +1100230 if (ok == 0) {
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000231 if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 ||
232 (r = sshbuf_put_string(msg, signature, slen)) != 0)
233 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100234 } else {
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000235 if ((r = sshbuf_put_u8(msg, SSH2_AGENT_FAILURE)) != 0)
236 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100237 }
Darren Tuckera627d422013-06-02 07:31:17 +1000238 free(data);
239 free(blob);
240 free(signature);
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000241 send_msg(msg);
242 sshbuf_free(msg);
Damien Miller7ea845e2010-02-12 09:21:02 +1100243}
244
245static void
246process(void)
247{
248 u_int msg_len;
249 u_int buf_len;
250 u_int consumed;
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000251 u_char type;
252 const u_char *cp;
253 int r;
Damien Miller7ea845e2010-02-12 09:21:02 +1100254
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000255 buf_len = sshbuf_len(iqueue);
Damien Miller7ea845e2010-02-12 09:21:02 +1100256 if (buf_len < 5)
257 return; /* Incomplete message. */
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000258 cp = sshbuf_ptr(iqueue);
Damien Miller7ea845e2010-02-12 09:21:02 +1100259 msg_len = get_u32(cp);
260 if (msg_len > MAX_MSG_LENGTH) {
261 error("bad message len %d", msg_len);
262 cleanup_exit(11);
263 }
264 if (buf_len < msg_len + 4)
265 return;
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000266 if ((r = sshbuf_consume(iqueue, 4)) != 0 ||
267 (r = sshbuf_get_u8(iqueue, &type)) != 0)
268 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100269 buf_len -= 4;
Damien Miller7ea845e2010-02-12 09:21:02 +1100270 switch (type) {
271 case SSH_AGENTC_ADD_SMARTCARD_KEY:
272 debug("process_add");
273 process_add();
274 break;
275 case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
276 debug("process_del");
277 process_del();
278 break;
279 case SSH2_AGENTC_SIGN_REQUEST:
280 debug("process_sign");
281 process_sign();
282 break;
283 default:
284 error("Unknown message %d", type);
285 break;
286 }
287 /* discard the remaining bytes from the current packet */
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000288 if (buf_len < sshbuf_len(iqueue)) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100289 error("iqueue grew unexpectedly");
290 cleanup_exit(255);
291 }
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000292 consumed = buf_len - sshbuf_len(iqueue);
Damien Miller7ea845e2010-02-12 09:21:02 +1100293 if (msg_len < consumed) {
294 error("msg_len %d < consumed %d", msg_len, consumed);
295 cleanup_exit(255);
296 }
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000297 if (msg_len > consumed) {
298 if ((r = sshbuf_consume(iqueue, msg_len - consumed)) != 0)
299 fatal("%s: buffer error: %s", __func__, ssh_err(r));
300 }
Damien Miller7ea845e2010-02-12 09:21:02 +1100301}
302
303void
304cleanup_exit(int i)
305{
306 /* XXX */
307 _exit(i);
308}
309
310int
311main(int argc, char **argv)
312{
313 fd_set *rset, *wset;
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000314 int r, in, out, max, log_stderr = 0;
Damien Miller7ea845e2010-02-12 09:21:02 +1100315 ssize_t len, olen, set_size;
316 SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
317 LogLevel log_level = SYSLOG_LEVEL_ERROR;
318 char buf[4*4096];
319
Damien Miller7ea845e2010-02-12 09:21:02 +1100320 extern char *__progname;
321
dtucker@openbsd.orgffb1e7e2016-02-15 09:47:49 +0000322 ssh_malloc_init(); /* must be called before any mallocs */
Tim Ricebff24b82010-02-28 14:51:56 -0800323 TAILQ_INIT(&pkcs11_keylist);
324 pkcs11_init(0);
325
Damien Miller09a24db2010-02-28 03:28:05 +1100326 seed_rng();
Damien Millerdfa41562010-02-12 10:06:28 +1100327 __progname = ssh_get_progname(argv[0]);
328
Damien Miller7ea845e2010-02-12 09:21:02 +1100329 log_init(__progname, log_level, log_facility, log_stderr);
330
331 in = STDIN_FILENO;
332 out = STDOUT_FILENO;
333
334 max = 0;
335 if (in > max)
336 max = in;
337 if (out > max)
338 max = out;
339
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000340 if ((iqueue = sshbuf_new()) == NULL)
341 fatal("%s: sshbuf_new failed", __func__);
342 if ((oqueue = sshbuf_new()) == NULL)
343 fatal("%s: sshbuf_new failed", __func__);
Damien Miller7ea845e2010-02-12 09:21:02 +1100344
345 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
deraadt@openbsd.orgce445b02015-08-20 22:32:42 +0000346 rset = xmalloc(set_size);
347 wset = xmalloc(set_size);
Damien Miller7ea845e2010-02-12 09:21:02 +1100348
349 for (;;) {
350 memset(rset, 0, set_size);
351 memset(wset, 0, set_size);
352
353 /*
354 * Ensure that we can read a full buffer and handle
355 * the worst-case length packet it can generate,
356 * otherwise apply backpressure by stopping reads.
357 */
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000358 if ((r = sshbuf_check_reserve(iqueue, sizeof(buf))) == 0 &&
359 (r = sshbuf_check_reserve(oqueue, MAX_MSG_LENGTH)) == 0)
Damien Miller7ea845e2010-02-12 09:21:02 +1100360 FD_SET(in, rset);
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000361 else if (r != SSH_ERR_NO_BUFFER_SPACE)
362 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100363
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000364 olen = sshbuf_len(oqueue);
Damien Miller7ea845e2010-02-12 09:21:02 +1100365 if (olen > 0)
366 FD_SET(out, wset);
367
368 if (select(max+1, rset, wset, NULL, NULL) < 0) {
369 if (errno == EINTR)
370 continue;
371 error("select: %s", strerror(errno));
372 cleanup_exit(2);
373 }
374
375 /* copy stdin to iqueue */
376 if (FD_ISSET(in, rset)) {
377 len = read(in, buf, sizeof buf);
378 if (len == 0) {
379 debug("read eof");
380 cleanup_exit(0);
381 } else if (len < 0) {
382 error("read: %s", strerror(errno));
383 cleanup_exit(1);
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000384 } else if ((r = sshbuf_put(iqueue, buf, len)) != 0) {
385 fatal("%s: buffer error: %s",
386 __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100387 }
388 }
389 /* send oqueue to stdout */
390 if (FD_ISSET(out, wset)) {
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000391 len = write(out, sshbuf_ptr(oqueue), olen);
Damien Miller7ea845e2010-02-12 09:21:02 +1100392 if (len < 0) {
393 error("write: %s", strerror(errno));
394 cleanup_exit(1);
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000395 } else if ((r = sshbuf_consume(oqueue, len)) != 0) {
396 fatal("%s: buffer error: %s",
397 __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100398 }
399 }
400
401 /*
402 * Process requests from client if we can fit the results
403 * into the output buffer, otherwise stop processing input
404 * and let the output queue drain.
405 */
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000406 if ((r = sshbuf_check_reserve(oqueue, MAX_MSG_LENGTH)) == 0)
Damien Miller7ea845e2010-02-12 09:21:02 +1100407 process();
markus@openbsd.orgb0d34132018-01-08 15:18:46 +0000408 else if (r != SSH_ERR_NO_BUFFER_SPACE)
409 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7ea845e2010-02-12 09:21:02 +1100410 }
411}
Damien Millerdfa41562010-02-12 10:06:28 +1100412#else /* ENABLE_PKCS11 */
413int
414main(int argc, char **argv)
415{
416 extern char *__progname;
417
418 __progname = ssh_get_progname(argv[0]);
419 log_init(__progname, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTH, 0);
420 fatal("PKCS#11 support disabled at compile time");
421}
422#endif /* ENABLE_PKCS11 */