summaryrefslogtreecommitdiff
path: root/ssh-agent.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2019-10-31 21:22:01 +0000
committerDamien Miller <djm@mindrot.org>2019-11-01 09:46:09 +1100
commit07da39f71d36fb547749a5b16aa8892e621a7e4a (patch)
treedd75cbd723102d887bc11f781cc0a23eee6b2f2f /ssh-agent.c
parenteebec620c9519c4839d781c4d5b6082152998f82 (diff)
upstream: ssh-agent support for U2F/FIDO keys
feedback & ok markus@ OpenBSD-Commit-ID: bb544a44bc32e45d2ec8bf652db2046f38360acb
Diffstat (limited to 'ssh-agent.c')
-rw-r--r--ssh-agent.c218
1 files changed, 199 insertions, 19 deletions
diff --git a/ssh-agent.c b/ssh-agent.c
index e500591a9..6bf9536fb 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-agent.c,v 1.237 2019/06/28 13:35:04 deraadt Exp $ */ 1/* $OpenBSD: ssh-agent.c,v 1.238 2019/10/31 21:22:01 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -41,6 +41,7 @@
41#include <sys/resource.h> 41#include <sys/resource.h>
42#include <sys/stat.h> 42#include <sys/stat.h>
43#include <sys/socket.h> 43#include <sys/socket.h>
44#include <sys/wait.h>
44#ifdef HAVE_SYS_TIME_H 45#ifdef HAVE_SYS_TIME_H
45# include <sys/time.h> 46# include <sys/time.h>
46#endif 47#endif
@@ -85,13 +86,13 @@
85#include "digest.h" 86#include "digest.h"
86#include "ssherr.h" 87#include "ssherr.h"
87#include "match.h" 88#include "match.h"
88 89#include "msg.h"
89#ifdef ENABLE_PKCS11 90#include "pathnames.h"
90#include "ssh-pkcs11.h" 91#include "ssh-pkcs11.h"
91#endif 92#include "ssh-sk.h"
92 93
93#ifndef DEFAULT_PKCS11_WHITELIST 94#ifndef DEFAULT_PROVIDER_WHITELIST
94# define DEFAULT_PKCS11_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" 95# define DEFAULT_PROVIDER_WHITELIST "/usr/lib*/*,/usr/local/lib*/*"
95#endif 96#endif
96 97
97/* Maximum accepted message length */ 98/* Maximum accepted message length */
@@ -123,6 +124,7 @@ typedef struct identity {
123 char *provider; 124 char *provider;
124 time_t death; 125 time_t death;
125 u_int confirm; 126 u_int confirm;
127 char *sk_provider;
126} Identity; 128} Identity;
127 129
128struct idtable { 130struct idtable {
@@ -146,8 +148,8 @@ pid_t cleanup_pid = 0;
146char socket_name[PATH_MAX]; 148char socket_name[PATH_MAX];
147char socket_dir[PATH_MAX]; 149char socket_dir[PATH_MAX];
148 150
149/* PKCS#11 path whitelist */ 151/* PKCS#11/Security key path whitelist */
150static char *pkcs11_whitelist; 152static char *provider_whitelist;
151 153
152/* locking */ 154/* locking */
153#define LOCK_SIZE 32 155#define LOCK_SIZE 32
@@ -189,6 +191,7 @@ free_identity(Identity *id)
189 sshkey_free(id->key); 191 sshkey_free(id->key);
190 free(id->provider); 192 free(id->provider);
191 free(id->comment); 193 free(id->comment);
194 free(id->sk_provider);
192 free(id); 195 free(id);
193} 196}
194 197
@@ -278,6 +281,121 @@ agent_decode_alg(struct sshkey *key, u_int flags)
278 return NULL; 281 return NULL;
279} 282}
280 283
284static int
285provider_sign(const char *provider, struct sshkey *key,
286 u_char **sigp, size_t *lenp,
287 const u_char *data, size_t datalen,
288 const char *alg, u_int compat)
289{
290 int status, pair[2], r = SSH_ERR_INTERNAL_ERROR;
291 pid_t pid;
292 char *helper, *verbosity = NULL;
293 struct sshbuf *kbuf, *req, *resp;
294 u_char version;
295
296 debug3("%s: start for provider %s", __func__, provider);
297
298 *sigp = NULL;
299 *lenp = 0;
300
301 helper = getenv("SSH_SK_HELPER");
302 if (helper == NULL || strlen(helper) == 0)
303 helper = _PATH_SSH_SK_HELPER;
304 if (log_level_get() >= SYSLOG_LEVEL_DEBUG1)
305 verbosity = "-vvv";
306
307 /* Start helper */
308 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
309 error("socketpair: %s", strerror(errno));
310 return SSH_ERR_SYSTEM_ERROR;
311 }
312 if ((pid = fork()) == -1) {
313 error("fork: %s", strerror(errno));
314 close(pair[0]);
315 close(pair[1]);
316 return SSH_ERR_SYSTEM_ERROR;
317 }
318 if (pid == 0) {
319 if ((dup2(pair[1], STDIN_FILENO) == -1) ||
320 (dup2(pair[1], STDOUT_FILENO) == -1))
321 fatal("%s: dup2: %s", __func__, ssh_err(r));
322 close(pair[0]);
323 close(pair[1]);
324 closefrom(STDERR_FILENO + 1);
325 debug("%s: starting %s %s", __func__, helper,
326 verbosity == NULL ? "" : verbosity);
327 execlp(helper, helper, verbosity, (char *)NULL);
328 fatal("%s: execlp: %s", __func__, strerror(errno));
329 }
330 close(pair[1]);
331
332 if ((kbuf = sshbuf_new()) == NULL ||
333 (req = sshbuf_new()) == NULL ||
334 (resp = sshbuf_new()) == NULL)
335 fatal("%s: sshbuf_new failed", __func__);
336
337 if ((r = sshkey_private_serialize(key, kbuf)) != 0 ||
338 (r = sshbuf_put_stringb(req, kbuf)) != 0 ||
339 (r = sshbuf_put_cstring(req, provider)) != 0 ||
340 (r = sshbuf_put_string(req, data, datalen)) != 0 ||
341 (r = sshbuf_put_u32(req, compat)) != 0)
342 fatal("%s: compose: %s", __func__, ssh_err(r));
343 if ((r = ssh_msg_send(pair[0], SSH_SK_HELPER_VERSION, req)) != 0) {
344 error("%s: send: %s", __func__, ssh_err(r));
345 goto out;
346 }
347 if ((r = ssh_msg_recv(pair[0], resp)) != 0) {
348 error("%s: receive: %s", __func__, ssh_err(r));
349 goto out;
350 }
351 if ((r = sshbuf_get_u8(resp, &version)) != 0) {
352 error("%s: parse version: %s", __func__, ssh_err(r));
353 goto out;
354 }
355 if (version != SSH_SK_HELPER_VERSION) {
356 error("%s: unsupported version: got %u, expected %u",
357 __func__, version, SSH_SK_HELPER_VERSION);
358 r = SSH_ERR_INVALID_FORMAT;
359 goto out;
360 }
361 if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) {
362 error("%s: parse signature: %s", __func__, ssh_err(r));
363 r = SSH_ERR_INVALID_FORMAT;
364 goto out;
365 }
366 if (sshbuf_len(resp) != 0) {
367 error("%s: trailing data in response", __func__);
368 r = SSH_ERR_INVALID_FORMAT;
369 goto out;
370 }
371 /* success */
372 r = 0;
373 out:
374 while (waitpid(pid, &status, 0) == -1) {
375 if (errno != EINTR)
376 fatal("%s: waitpid: %s", __func__, ssh_err(r));
377 }
378 if (!WIFEXITED(status)) {
379 error("%s: helper %s exited abnormally", __func__, helper);
380 if (r == 0)
381 r = SSH_ERR_SYSTEM_ERROR;
382 } else if (WEXITSTATUS(status) != 0) {
383 error("%s: helper %s exited with non-zero exit status",
384 __func__, helper);
385 if (r == 0)
386 r = SSH_ERR_SYSTEM_ERROR;
387 }
388 if (r != 0) {
389 freezero(*sigp, *lenp);
390 *sigp = NULL;
391 *lenp = 0;
392 }
393 sshbuf_free(kbuf);
394 sshbuf_free(req);
395 sshbuf_free(resp);
396 return r;
397}
398
281/* ssh2 only */ 399/* ssh2 only */
282static void 400static void
283process_sign_request2(SocketEntry *e) 401process_sign_request2(SocketEntry *e)
@@ -308,10 +426,19 @@ process_sign_request2(SocketEntry *e)
308 verbose("%s: user refused key", __func__); 426 verbose("%s: user refused key", __func__);
309 goto send; 427 goto send;
310 } 428 }
311 if ((r = sshkey_sign(id->key, &signature, &slen, 429 if (id->sk_provider != NULL) {
312 data, dlen, agent_decode_alg(key, flags), compat)) != 0) { 430 if ((r = provider_sign(id->sk_provider, id->key, &signature,
313 error("%s: sshkey_sign: %s", __func__, ssh_err(r)); 431 &slen, data, dlen, agent_decode_alg(key, flags),
314 goto send; 432 compat)) != 0) {
433 error("%s: sshkey_sign: %s", __func__, ssh_err(r));
434 goto send;
435 }
436 } else {
437 if ((r = sshkey_sign(id->key, &signature, &slen,
438 data, dlen, agent_decode_alg(key, flags), compat)) != 0) {
439 error("%s: sshkey_sign: %s", __func__, ssh_err(r));
440 goto send;
441 }
315 } 442 }
316 /* Success */ 443 /* Success */
317 ok = 0; 444 ok = 0;
@@ -411,7 +538,7 @@ process_add_identity(SocketEntry *e)
411 Identity *id; 538 Identity *id;
412 int success = 0, confirm = 0; 539 int success = 0, confirm = 0;
413 u_int seconds, maxsign; 540 u_int seconds, maxsign;
414 char *comment = NULL; 541 char *fp, *comment = NULL, *ext_name = NULL, *sk_provider = NULL;
415 time_t death = 0; 542 time_t death = 0;
416 struct sshkey *k = NULL; 543 struct sshkey *k = NULL;
417 u_char ctype; 544 u_char ctype;
@@ -456,15 +583,58 @@ process_add_identity(SocketEntry *e)
456 goto err; 583 goto err;
457 } 584 }
458 break; 585 break;
586 case SSH_AGENT_CONSTRAIN_EXTENSION:
587 if ((r = sshbuf_get_cstring(e->request,
588 &ext_name, NULL)) != 0) {
589 error("%s: cannot parse extension: %s",
590 __func__, ssh_err(r));
591 goto err;
592 }
593 debug("%s: constraint ext %s", __func__, ext_name);
594 if (strcmp(ext_name, "sk-provider@openssh.com") == 0) {
595 if (sk_provider != NULL) {
596 error("%s already set", ext_name);
597 goto err;
598 }
599 if ((r = sshbuf_get_cstring(e->request,
600 &sk_provider, NULL)) != 0) {
601 error("%s: cannot parse %s: %s",
602 __func__, ext_name, ssh_err(r));
603 goto err;
604 }
605 } else {
606 error("%s: unsupported constraint \"%s\"",
607 __func__, ext_name);
608 goto err;
609 }
610 free(ext_name);
611 break;
459 default: 612 default:
460 error("%s: Unknown constraint %d", __func__, ctype); 613 error("%s: Unknown constraint %d", __func__, ctype);
461 err: 614 err:
615 free(sk_provider);
616 free(ext_name);
462 sshbuf_reset(e->request); 617 sshbuf_reset(e->request);
463 free(comment); 618 free(comment);
464 sshkey_free(k); 619 sshkey_free(k);
465 goto send; 620 goto send;
466 } 621 }
467 } 622 }
623 if (sk_provider != NULL) {
624 if (sshkey_type_plain(k->type) != KEY_ECDSA_SK) {
625 error("Cannot add provider: %s is not a security key",
626 sshkey_type(k));
627 free(sk_provider);
628 goto send;
629 }
630 if (match_pattern_list(sk_provider,
631 provider_whitelist, 0) != 1) {
632 error("Refusing add key: provider %s not whitelisted",
633 sk_provider);
634 free(sk_provider);
635 goto send;
636 }
637 }
468 638
469 success = 1; 639 success = 1;
470 if (lifetime && !death) 640 if (lifetime && !death)
@@ -478,11 +648,21 @@ process_add_identity(SocketEntry *e)
478 /* key state might have been updated */ 648 /* key state might have been updated */
479 sshkey_free(id->key); 649 sshkey_free(id->key);
480 free(id->comment); 650 free(id->comment);
651 free(id->sk_provider);
481 } 652 }
482 id->key = k; 653 id->key = k;
483 id->comment = comment; 654 id->comment = comment;
484 id->death = death; 655 id->death = death;
485 id->confirm = confirm; 656 id->confirm = confirm;
657 id->sk_provider = sk_provider;
658
659 if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT,
660 SSH_FP_DEFAULT)) == NULL)
661 fatal("%s: sshkey_fingerprint failed", __func__);
662 debug("%s: add %s %s \"%.100s\" (life: %u) (confirm: %u) "
663 "(provider: %s)", __func__, sshkey_ssh_name(k), fp, comment,
664 seconds, confirm, sk_provider == NULL ? "none" : sk_provider);
665 free(fp);
486send: 666send:
487 send_status(e, success); 667 send_status(e, success);
488} 668}
@@ -600,7 +780,7 @@ process_add_smartcard_key(SocketEntry *e)
600 provider, strerror(errno)); 780 provider, strerror(errno));
601 goto send; 781 goto send;
602 } 782 }
603 if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { 783 if (match_pattern_list(canonical_provider, provider_whitelist, 0) != 1) {
604 verbose("refusing PKCS#11 add of \"%.100s\": " 784 verbose("refusing PKCS#11 add of \"%.100s\": "
605 "provider not whitelisted", canonical_provider); 785 "provider not whitelisted", canonical_provider);
606 goto send; 786 goto send;
@@ -1079,7 +1259,7 @@ usage(void)
1079{ 1259{
1080 fprintf(stderr, 1260 fprintf(stderr,
1081 "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n" 1261 "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n"
1082 " [-P pkcs11_whitelist] [-t life] [command [arg ...]]\n" 1262 " [-P provider_whitelist] [-t life] [command [arg ...]]\n"
1083 " ssh-agent [-c | -s] -k\n"); 1263 " ssh-agent [-c | -s] -k\n");
1084 exit(1); 1264 exit(1);
1085} 1265}
@@ -1137,9 +1317,9 @@ main(int ac, char **av)
1137 k_flag++; 1317 k_flag++;
1138 break; 1318 break;
1139 case 'P': 1319 case 'P':
1140 if (pkcs11_whitelist != NULL) 1320 if (provider_whitelist != NULL)
1141 fatal("-P option already specified"); 1321 fatal("-P option already specified");
1142 pkcs11_whitelist = xstrdup(optarg); 1322 provider_whitelist = xstrdup(optarg);
1143 break; 1323 break;
1144 case 's': 1324 case 's':
1145 if (c_flag) 1325 if (c_flag)
@@ -1175,8 +1355,8 @@ main(int ac, char **av)
1175 if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag)) 1355 if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag))
1176 usage(); 1356 usage();
1177 1357
1178 if (pkcs11_whitelist == NULL) 1358 if (provider_whitelist == NULL)
1179 pkcs11_whitelist = xstrdup(DEFAULT_PKCS11_WHITELIST); 1359 provider_whitelist = xstrdup(DEFAULT_PROVIDER_WHITELIST);
1180 1360
1181 if (ac == 0 && !c_flag && !s_flag) { 1361 if (ac == 0 && !c_flag && !s_flag) {
1182 shell = getenv("SHELL"); 1362 shell = getenv("SHELL");