diff options
Diffstat (limited to 'ssh-agent.c')
-rw-r--r-- | ssh-agent.c | 152 |
1 files changed, 123 insertions, 29 deletions
diff --git a/ssh-agent.c b/ssh-agent.c index 9c6680a25..7eb6f0dc5 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.255 2020/02/06 22:30:54 naddy 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,14 @@ | |||
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 "ssherr.h" |
91 | #include "pathnames.h" | ||
90 | #include "ssh-pkcs11.h" | 92 | #include "ssh-pkcs11.h" |
91 | #endif | 93 | #include "sk-api.h" |
92 | 94 | ||
93 | #ifndef DEFAULT_PKCS11_WHITELIST | 95 | #ifndef DEFAULT_PROVIDER_WHITELIST |
94 | # define DEFAULT_PKCS11_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" | 96 | # define DEFAULT_PROVIDER_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" |
95 | #endif | 97 | #endif |
96 | 98 | ||
97 | /* Maximum accepted message length */ | 99 | /* Maximum accepted message length */ |
@@ -123,6 +125,7 @@ typedef struct identity { | |||
123 | char *provider; | 125 | char *provider; |
124 | time_t death; | 126 | time_t death; |
125 | u_int confirm; | 127 | u_int confirm; |
128 | char *sk_provider; | ||
126 | } Identity; | 129 | } Identity; |
127 | 130 | ||
128 | struct idtable { | 131 | struct idtable { |
@@ -146,8 +149,8 @@ pid_t cleanup_pid = 0; | |||
146 | char socket_name[PATH_MAX]; | 149 | char socket_name[PATH_MAX]; |
147 | char socket_dir[PATH_MAX]; | 150 | char socket_dir[PATH_MAX]; |
148 | 151 | ||
149 | /* PKCS#11 path whitelist */ | 152 | /* PKCS#11/Security key path whitelist */ |
150 | static char *pkcs11_whitelist; | 153 | static char *provider_whitelist; |
151 | 154 | ||
152 | /* locking */ | 155 | /* locking */ |
153 | #define LOCK_SIZE 32 | 156 | #define LOCK_SIZE 32 |
@@ -189,6 +192,7 @@ free_identity(Identity *id) | |||
189 | sshkey_free(id->key); | 192 | sshkey_free(id->key); |
190 | free(id->provider); | 193 | free(id->provider); |
191 | free(id->comment); | 194 | free(id->comment); |
195 | free(id->sk_provider); | ||
192 | free(id); | 196 | free(id); |
193 | } | 197 | } |
194 | 198 | ||
@@ -287,9 +291,11 @@ process_sign_request2(SocketEntry *e) | |||
287 | size_t dlen, slen = 0; | 291 | size_t dlen, slen = 0; |
288 | u_int compat = 0, flags; | 292 | u_int compat = 0, flags; |
289 | int r, ok = -1; | 293 | int r, ok = -1; |
294 | char *fp = NULL; | ||
290 | struct sshbuf *msg; | 295 | struct sshbuf *msg; |
291 | struct sshkey *key = NULL; | 296 | struct sshkey *key = NULL; |
292 | struct identity *id; | 297 | struct identity *id; |
298 | struct notifier_ctx *notifier = NULL; | ||
293 | 299 | ||
294 | if ((msg = sshbuf_new()) == NULL) | 300 | if ((msg = sshbuf_new()) == NULL) |
295 | fatal("%s: sshbuf_new failed", __func__); | 301 | fatal("%s: sshbuf_new failed", __func__); |
@@ -308,15 +314,27 @@ process_sign_request2(SocketEntry *e) | |||
308 | verbose("%s: user refused key", __func__); | 314 | verbose("%s: user refused key", __func__); |
309 | goto send; | 315 | goto send; |
310 | } | 316 | } |
317 | if (sshkey_is_sk(id->key) && | ||
318 | (id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { | ||
319 | if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, | ||
320 | SSH_FP_DEFAULT)) == NULL) | ||
321 | fatal("%s: fingerprint failed", __func__); | ||
322 | notifier = notify_start(0, | ||
323 | "Confirm user presence for key %s %s", | ||
324 | sshkey_type(id->key), fp); | ||
325 | } | ||
311 | if ((r = sshkey_sign(id->key, &signature, &slen, | 326 | if ((r = sshkey_sign(id->key, &signature, &slen, |
312 | data, dlen, agent_decode_alg(key, flags), compat)) != 0) { | 327 | data, dlen, agent_decode_alg(key, flags), |
328 | id->sk_provider, compat)) != 0) { | ||
313 | error("%s: sshkey_sign: %s", __func__, ssh_err(r)); | 329 | error("%s: sshkey_sign: %s", __func__, ssh_err(r)); |
314 | goto send; | 330 | goto send; |
315 | } | 331 | } |
316 | /* Success */ | 332 | /* Success */ |
317 | ok = 0; | 333 | ok = 0; |
318 | send: | 334 | send: |
335 | notify_complete(notifier); | ||
319 | sshkey_free(key); | 336 | sshkey_free(key); |
337 | free(fp); | ||
320 | if (ok == 0) { | 338 | if (ok == 0) { |
321 | if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 || | 339 | if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 || |
322 | (r = sshbuf_put_string(msg, signature, slen)) != 0) | 340 | (r = sshbuf_put_string(msg, signature, slen)) != 0) |
@@ -411,7 +429,8 @@ process_add_identity(SocketEntry *e) | |||
411 | Identity *id; | 429 | Identity *id; |
412 | int success = 0, confirm = 0; | 430 | int success = 0, confirm = 0; |
413 | u_int seconds, maxsign; | 431 | u_int seconds, maxsign; |
414 | char *comment = NULL; | 432 | char *fp, *comment = NULL, *ext_name = NULL, *sk_provider = NULL; |
433 | char canonical_provider[PATH_MAX]; | ||
415 | time_t death = 0; | 434 | time_t death = 0; |
416 | struct sshkey *k = NULL; | 435 | struct sshkey *k = NULL; |
417 | u_char ctype; | 436 | u_char ctype; |
@@ -423,10 +442,6 @@ process_add_identity(SocketEntry *e) | |||
423 | error("%s: decode private key: %s", __func__, ssh_err(r)); | 442 | error("%s: decode private key: %s", __func__, ssh_err(r)); |
424 | goto err; | 443 | goto err; |
425 | } | 444 | } |
426 | if ((r = sshkey_shield_private(k)) != 0) { | ||
427 | error("%s: shield private key: %s", __func__, ssh_err(r)); | ||
428 | goto err; | ||
429 | } | ||
430 | while (sshbuf_len(e->request)) { | 445 | while (sshbuf_len(e->request)) { |
431 | if ((r = sshbuf_get_u8(e->request, &ctype)) != 0) { | 446 | if ((r = sshbuf_get_u8(e->request, &ctype)) != 0) { |
432 | error("%s: buffer error: %s", __func__, ssh_err(r)); | 447 | error("%s: buffer error: %s", __func__, ssh_err(r)); |
@@ -456,15 +471,75 @@ process_add_identity(SocketEntry *e) | |||
456 | goto err; | 471 | goto err; |
457 | } | 472 | } |
458 | break; | 473 | break; |
474 | case SSH_AGENT_CONSTRAIN_EXTENSION: | ||
475 | if ((r = sshbuf_get_cstring(e->request, | ||
476 | &ext_name, NULL)) != 0) { | ||
477 | error("%s: cannot parse extension: %s", | ||
478 | __func__, ssh_err(r)); | ||
479 | goto err; | ||
480 | } | ||
481 | debug("%s: constraint ext %s", __func__, ext_name); | ||
482 | if (strcmp(ext_name, "sk-provider@openssh.com") == 0) { | ||
483 | if (sk_provider != NULL) { | ||
484 | error("%s already set", ext_name); | ||
485 | goto err; | ||
486 | } | ||
487 | if ((r = sshbuf_get_cstring(e->request, | ||
488 | &sk_provider, NULL)) != 0) { | ||
489 | error("%s: cannot parse %s: %s", | ||
490 | __func__, ext_name, ssh_err(r)); | ||
491 | goto err; | ||
492 | } | ||
493 | } else { | ||
494 | error("%s: unsupported constraint \"%s\"", | ||
495 | __func__, ext_name); | ||
496 | goto err; | ||
497 | } | ||
498 | free(ext_name); | ||
499 | break; | ||
459 | default: | 500 | default: |
460 | error("%s: Unknown constraint %d", __func__, ctype); | 501 | error("%s: Unknown constraint %d", __func__, ctype); |
461 | err: | 502 | err: |
503 | free(sk_provider); | ||
504 | free(ext_name); | ||
462 | sshbuf_reset(e->request); | 505 | sshbuf_reset(e->request); |
463 | free(comment); | 506 | free(comment); |
464 | sshkey_free(k); | 507 | sshkey_free(k); |
465 | goto send; | 508 | goto send; |
466 | } | 509 | } |
467 | } | 510 | } |
511 | if (sk_provider != NULL) { | ||
512 | if (!sshkey_is_sk(k)) { | ||
513 | error("Cannot add provider: %s is not an " | ||
514 | "authenticator-hosted key", sshkey_type(k)); | ||
515 | free(sk_provider); | ||
516 | goto send; | ||
517 | } | ||
518 | if (strcasecmp(sk_provider, "internal") == 0) { | ||
519 | debug("%s: internal provider", __func__); | ||
520 | } else { | ||
521 | if (realpath(sk_provider, canonical_provider) == NULL) { | ||
522 | verbose("failed provider \"%.100s\": " | ||
523 | "realpath: %s", sk_provider, | ||
524 | strerror(errno)); | ||
525 | free(sk_provider); | ||
526 | goto send; | ||
527 | } | ||
528 | free(sk_provider); | ||
529 | sk_provider = xstrdup(canonical_provider); | ||
530 | if (match_pattern_list(sk_provider, | ||
531 | provider_whitelist, 0) != 1) { | ||
532 | error("Refusing add key: " | ||
533 | "provider %s not whitelisted", sk_provider); | ||
534 | free(sk_provider); | ||
535 | goto send; | ||
536 | } | ||
537 | } | ||
538 | } | ||
539 | if ((r = sshkey_shield_private(k)) != 0) { | ||
540 | error("%s: shield private key: %s", __func__, ssh_err(r)); | ||
541 | goto err; | ||
542 | } | ||
468 | 543 | ||
469 | success = 1; | 544 | success = 1; |
470 | if (lifetime && !death) | 545 | if (lifetime && !death) |
@@ -478,11 +553,21 @@ process_add_identity(SocketEntry *e) | |||
478 | /* key state might have been updated */ | 553 | /* key state might have been updated */ |
479 | sshkey_free(id->key); | 554 | sshkey_free(id->key); |
480 | free(id->comment); | 555 | free(id->comment); |
556 | free(id->sk_provider); | ||
481 | } | 557 | } |
482 | id->key = k; | 558 | id->key = k; |
483 | id->comment = comment; | 559 | id->comment = comment; |
484 | id->death = death; | 560 | id->death = death; |
485 | id->confirm = confirm; | 561 | id->confirm = confirm; |
562 | id->sk_provider = sk_provider; | ||
563 | |||
564 | if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT, | ||
565 | SSH_FP_DEFAULT)) == NULL) | ||
566 | fatal("%s: sshkey_fingerprint failed", __func__); | ||
567 | debug("%s: add %s %s \"%.100s\" (life: %u) (confirm: %u) " | ||
568 | "(provider: %s)", __func__, sshkey_ssh_name(k), fp, comment, | ||
569 | seconds, confirm, sk_provider == NULL ? "none" : sk_provider); | ||
570 | free(fp); | ||
486 | send: | 571 | send: |
487 | send_status(e, success); | 572 | send_status(e, success); |
488 | } | 573 | } |
@@ -560,6 +645,7 @@ static void | |||
560 | process_add_smartcard_key(SocketEntry *e) | 645 | process_add_smartcard_key(SocketEntry *e) |
561 | { | 646 | { |
562 | char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; | 647 | char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; |
648 | char **comments = NULL; | ||
563 | int r, i, count = 0, success = 0, confirm = 0; | 649 | int r, i, count = 0, success = 0, confirm = 0; |
564 | u_int seconds; | 650 | u_int seconds; |
565 | time_t death = 0; | 651 | time_t death = 0; |
@@ -600,7 +686,7 @@ process_add_smartcard_key(SocketEntry *e) | |||
600 | provider, strerror(errno)); | 686 | provider, strerror(errno)); |
601 | goto send; | 687 | goto send; |
602 | } | 688 | } |
603 | if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { | 689 | if (match_pattern_list(canonical_provider, provider_whitelist, 0) != 1) { |
604 | verbose("refusing PKCS#11 add of \"%.100s\": " | 690 | verbose("refusing PKCS#11 add of \"%.100s\": " |
605 | "provider not whitelisted", canonical_provider); | 691 | "provider not whitelisted", canonical_provider); |
606 | goto send; | 692 | goto send; |
@@ -609,28 +695,34 @@ process_add_smartcard_key(SocketEntry *e) | |||
609 | if (lifetime && !death) | 695 | if (lifetime && !death) |
610 | death = monotime() + lifetime; | 696 | death = monotime() + lifetime; |
611 | 697 | ||
612 | count = pkcs11_add_provider(canonical_provider, pin, &keys); | 698 | count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments); |
613 | for (i = 0; i < count; i++) { | 699 | for (i = 0; i < count; i++) { |
614 | k = keys[i]; | 700 | k = keys[i]; |
615 | if (lookup_identity(k) == NULL) { | 701 | if (lookup_identity(k) == NULL) { |
616 | id = xcalloc(1, sizeof(Identity)); | 702 | id = xcalloc(1, sizeof(Identity)); |
617 | id->key = k; | 703 | id->key = k; |
704 | keys[i] = NULL; /* transferred */ | ||
618 | id->provider = xstrdup(canonical_provider); | 705 | id->provider = xstrdup(canonical_provider); |
619 | id->comment = xstrdup(canonical_provider); /* XXX */ | 706 | if (*comments[i] != '\0') { |
707 | id->comment = comments[i]; | ||
708 | comments[i] = NULL; /* transferred */ | ||
709 | } else { | ||
710 | id->comment = xstrdup(canonical_provider); | ||
711 | } | ||
620 | id->death = death; | 712 | id->death = death; |
621 | id->confirm = confirm; | 713 | id->confirm = confirm; |
622 | TAILQ_INSERT_TAIL(&idtab->idlist, id, next); | 714 | TAILQ_INSERT_TAIL(&idtab->idlist, id, next); |
623 | idtab->nentries++; | 715 | idtab->nentries++; |
624 | success = 1; | 716 | success = 1; |
625 | } else { | ||
626 | sshkey_free(k); | ||
627 | } | 717 | } |
628 | keys[i] = NULL; | 718 | sshkey_free(keys[i]); |
719 | free(comments[i]); | ||
629 | } | 720 | } |
630 | send: | 721 | send: |
631 | free(pin); | 722 | free(pin); |
632 | free(provider); | 723 | free(provider); |
633 | free(keys); | 724 | free(keys); |
725 | free(comments); | ||
634 | send_status(e, success); | 726 | send_status(e, success); |
635 | } | 727 | } |
636 | 728 | ||
@@ -1079,7 +1171,7 @@ usage(void) | |||
1079 | { | 1171 | { |
1080 | fprintf(stderr, | 1172 | fprintf(stderr, |
1081 | "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n" | 1173 | "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n" |
1082 | " [-P pkcs11_whitelist] [-t life] [command [arg ...]]\n" | 1174 | " [-P provider_whitelist] [-t life] [command [arg ...]]\n" |
1083 | " ssh-agent [-c | -s] -k\n"); | 1175 | " ssh-agent [-c | -s] -k\n"); |
1084 | exit(1); | 1176 | exit(1); |
1085 | } | 1177 | } |
@@ -1113,8 +1205,10 @@ main(int ac, char **av) | |||
1113 | 1205 | ||
1114 | platform_disable_tracing(0); /* strict=no */ | 1206 | platform_disable_tracing(0); /* strict=no */ |
1115 | 1207 | ||
1208 | #ifdef RLIMIT_NOFILE | ||
1116 | if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) | 1209 | if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) |
1117 | fatal("%s: getrlimit: %s", __progname, strerror(errno)); | 1210 | fatal("%s: getrlimit: %s", __progname, strerror(errno)); |
1211 | #endif | ||
1118 | 1212 | ||
1119 | __progname = ssh_get_progname(av[0]); | 1213 | __progname = ssh_get_progname(av[0]); |
1120 | seed_rng(); | 1214 | seed_rng(); |
@@ -1135,9 +1229,9 @@ main(int ac, char **av) | |||
1135 | k_flag++; | 1229 | k_flag++; |
1136 | break; | 1230 | break; |
1137 | case 'P': | 1231 | case 'P': |
1138 | if (pkcs11_whitelist != NULL) | 1232 | if (provider_whitelist != NULL) |
1139 | fatal("-P option already specified"); | 1233 | fatal("-P option already specified"); |
1140 | pkcs11_whitelist = xstrdup(optarg); | 1234 | provider_whitelist = xstrdup(optarg); |
1141 | break; | 1235 | break; |
1142 | case 's': | 1236 | case 's': |
1143 | if (c_flag) | 1237 | if (c_flag) |
@@ -1173,8 +1267,8 @@ main(int ac, char **av) | |||
1173 | if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag)) | 1267 | if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag)) |
1174 | usage(); | 1268 | usage(); |
1175 | 1269 | ||
1176 | if (pkcs11_whitelist == NULL) | 1270 | if (provider_whitelist == NULL) |
1177 | pkcs11_whitelist = xstrdup(DEFAULT_PKCS11_WHITELIST); | 1271 | provider_whitelist = xstrdup(DEFAULT_PROVIDER_WHITELIST); |
1178 | 1272 | ||
1179 | if (ac == 0 && !c_flag && !s_flag) { | 1273 | if (ac == 0 && !c_flag && !s_flag) { |
1180 | shell = getenv("SHELL"); | 1274 | shell = getenv("SHELL"); |
@@ -1329,10 +1423,10 @@ skip: | |||
1329 | if (ac > 0) | 1423 | if (ac > 0) |
1330 | parent_alive_interval = 10; | 1424 | parent_alive_interval = 10; |
1331 | idtab_init(); | 1425 | idtab_init(); |
1332 | signal(SIGPIPE, SIG_IGN); | 1426 | ssh_signal(SIGPIPE, SIG_IGN); |
1333 | signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN); | 1427 | ssh_signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN); |
1334 | signal(SIGHUP, cleanup_handler); | 1428 | ssh_signal(SIGHUP, cleanup_handler); |
1335 | signal(SIGTERM, cleanup_handler); | 1429 | ssh_signal(SIGTERM, cleanup_handler); |
1336 | 1430 | ||
1337 | if (pledge("stdio rpath cpath unix id proc exec", NULL) == -1) | 1431 | if (pledge("stdio rpath cpath unix id proc exec", NULL) == -1) |
1338 | fatal("%s: pledge: %s", __progname, strerror(errno)); | 1432 | fatal("%s: pledge: %s", __progname, strerror(errno)); |