summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ssh-keygen.111
-rw-r--r--ssh-keygen.c224
2 files changed, 172 insertions, 63 deletions
diff --git a/ssh-keygen.1 b/ssh-keygen.1
index f0d70adec..569a46b19 100644
--- a/ssh-keygen.1
+++ b/ssh-keygen.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: ssh-keygen.1,v 1.186 2019/12/30 16:10:00 jmc Exp $ 1.\" $OpenBSD: ssh-keygen.1,v 1.187 2020/01/02 22:40:09 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
@@ -35,7 +35,7 @@
35.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37.\" 37.\"
38.Dd $Mdocdate: December 30 2019 $ 38.Dd $Mdocdate: January 2 2020 $
39.Dt SSH-KEYGEN 1 39.Dt SSH-KEYGEN 1
40.Os 40.Os
41.Sh NAME 41.Sh NAME
@@ -92,6 +92,9 @@
92.Fl H 92.Fl H
93.Op Fl f Ar known_hosts_file 93.Op Fl f Ar known_hosts_file
94.Nm ssh-keygen 94.Nm ssh-keygen
95.Fl K
96.Op Fl w Ar provider
97.Nm ssh-keygen
95.Fl R Ar hostname 98.Fl R Ar hostname
96.Op Fl f Ar known_hosts_file 99.Op Fl f Ar known_hosts_file
97.Nm ssh-keygen 100.Nm ssh-keygen
@@ -363,6 +366,10 @@ commercial SSH implementations.
363The default import format is 366The default import format is
364.Dq RFC4716 . 367.Dq RFC4716 .
365.It Fl k 368.It Fl k
369Download resident keys from a FIDO authenticator.
370Public and private key files will be written to the current directory for
371each downloaded key.
372.It Fl k
366Generate a KRL file. 373Generate a KRL file.
367In this mode, 374In this mode,
368.Nm 375.Nm
diff --git a/ssh-keygen.c b/ssh-keygen.c
index 3640a3c37..7731339f7 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-keygen.c,v 1.380 2019/12/30 09:49:52 djm Exp $ */ 1/* $OpenBSD: ssh-keygen.c,v 1.381 2020/01/02 22:40:09 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -2871,6 +2871,137 @@ do_moduli_screen(const char *out_file, char **opts, size_t nopts)
2871#endif /* WITH_OPENSSL */ 2871#endif /* WITH_OPENSSL */
2872} 2872}
2873 2873
2874static char *
2875private_key_passphrase(void)
2876{
2877 char *passphrase1, *passphrase2;
2878
2879 /* Ask for a passphrase (twice). */
2880 if (identity_passphrase)
2881 passphrase1 = xstrdup(identity_passphrase);
2882 else if (identity_new_passphrase)
2883 passphrase1 = xstrdup(identity_new_passphrase);
2884 else {
2885passphrase_again:
2886 passphrase1 =
2887 read_passphrase("Enter passphrase (empty for no "
2888 "passphrase): ", RP_ALLOW_STDIN);
2889 passphrase2 = read_passphrase("Enter same passphrase again: ",
2890 RP_ALLOW_STDIN);
2891 if (strcmp(passphrase1, passphrase2) != 0) {
2892 /*
2893 * The passphrases do not match. Clear them and
2894 * retry.
2895 */
2896 freezero(passphrase1, strlen(passphrase1));
2897 freezero(passphrase2, strlen(passphrase2));
2898 printf("Passphrases do not match. Try again.\n");
2899 goto passphrase_again;
2900 }
2901 /* Clear the other copy of the passphrase. */
2902 freezero(passphrase2, strlen(passphrase2));
2903 }
2904 return passphrase1;
2905}
2906
2907static const char *
2908skip_ssh_url_preamble(const char *s)
2909{
2910 if (strncmp(s, "ssh://", 6) == 0)
2911 return s + 6;
2912 else if (strncmp(s, "ssh:", 4) == 0)
2913 return s + 4;
2914 return s;
2915}
2916
2917static int
2918do_download_sk(const char *skprovider)
2919{
2920 struct sshkey **keys;
2921 size_t nkeys, i;
2922 int r, ok = -1;
2923 char *fp, *pin, *pass = NULL, *path, *pubpath;
2924 const char *ext;
2925
2926 if (skprovider == NULL)
2927 fatal("Cannot download keys without provider");
2928
2929 pin = read_passphrase("Enter PIN for security key: ", RP_ALLOW_STDIN);
2930 if ((r = sshsk_load_resident(skprovider, pin, &keys, &nkeys)) != 0) {
2931 freezero(pin, strlen(pin));
2932 error("Unable to load resident keys: %s", ssh_err(r));
2933 return -1;
2934 }
2935 if (nkeys == 0)
2936 logit("No keys to download");
2937 freezero(pin, strlen(pin));
2938
2939 for (i = 0; i < nkeys; i++) {
2940 if (keys[i]->type != KEY_ECDSA_SK &&
2941 keys[i]->type != KEY_ED25519_SK) {
2942 error("Unsupported key type %s (%d)",
2943 sshkey_type(keys[i]), keys[i]->type);
2944 continue;
2945 }
2946 if ((fp = sshkey_fingerprint(keys[i],
2947 fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
2948 fatal("%s: sshkey_fingerprint failed", __func__);
2949 debug("%s: key %zu: %s %s %s (flags 0x%02x)", __func__, i,
2950 sshkey_type(keys[i]), fp, keys[i]->sk_application,
2951 keys[i]->sk_flags);
2952 ext = skip_ssh_url_preamble(keys[i]->sk_application);
2953 xasprintf(&path, "id_%s_rk%s%s",
2954 keys[i]->type == KEY_ECDSA_SK ? "ecdsa_sk" : "ed25519_sk",
2955 *ext == '\0' ? "" : "_", ext);
2956
2957 /* If the file already exists, ask the user to confirm. */
2958 if (!confirm_overwrite(path)) {
2959 free(path);
2960 break;
2961 }
2962
2963 /* Save the key with the application string as the comment */
2964 if (pass == NULL)
2965 pass = private_key_passphrase();
2966 if ((r = sshkey_save_private(keys[i], path, pass,
2967 keys[i]->sk_application, private_key_format,
2968 openssh_format_cipher, rounds)) != 0) {
2969 error("Saving key \"%s\" failed: %s",
2970 path, ssh_err(r));
2971 free(path);
2972 break;
2973 }
2974 if (!quiet) {
2975 printf("Saved %s key%s%s to %s\n",
2976 sshkey_type(keys[i]),
2977 *ext != '\0' ? " " : "",
2978 *ext != '\0' ? keys[i]->sk_application : "",
2979 path);
2980 }
2981
2982 /* Save public key too */
2983 xasprintf(&pubpath, "%s.pub", path);
2984 free(path);
2985 if ((r = sshkey_save_public(keys[i], pubpath,
2986 keys[i]->sk_application)) != 0) {
2987 free(pubpath);
2988 error("Saving public key \"%s\" failed: %s",
2989 pubpath, ssh_err(r));
2990 break;
2991 }
2992 free(pubpath);
2993 }
2994
2995 if (i >= nkeys)
2996 ok = 0; /* success */
2997 if (pass != NULL)
2998 freezero(pass, strlen(pass));
2999 for (i = 0; i < nkeys; i++)
3000 sshkey_free(keys[i]);
3001 free(keys);
3002 return ok ? 0 : -1;
3003}
3004
2874static void 3005static void
2875usage(void) 3006usage(void)
2876{ 3007{
@@ -2891,6 +3022,8 @@ usage(void)
2891 " ssh-keygen -D pkcs11\n"); 3022 " ssh-keygen -D pkcs11\n");
2892#endif 3023#endif
2893 fprintf(stderr, 3024 fprintf(stderr,
3025 " ssh-keygen -K path [-w sk_provider]\n");
3026 fprintf(stderr,
2894 " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n" 3027 " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n"
2895 " ssh-keygen -H [-f known_hosts_file]\n" 3028 " ssh-keygen -H [-f known_hosts_file]\n"
2896 " ssh-keygen -R hostname [-f known_hosts_file]\n" 3029 " ssh-keygen -R hostname [-f known_hosts_file]\n"
@@ -2920,24 +3053,23 @@ usage(void)
2920int 3053int
2921main(int argc, char **argv) 3054main(int argc, char **argv)
2922{ 3055{
2923 char dotsshdir[PATH_MAX], comment[1024], *passphrase1, *passphrase2; 3056 char dotsshdir[PATH_MAX], comment[1024], *passphrase;
2924 char *rr_hostname = NULL, *ep, *fp, *ra; 3057 char *rr_hostname = NULL, *ep, *fp, *ra;
2925 struct sshkey *private, *public; 3058 struct sshkey *private, *public;
2926 struct passwd *pw; 3059 struct passwd *pw;
2927 struct stat st; 3060 struct stat st;
2928 int r, opt, type, fd; 3061 int r, opt, type;
2929 int change_passphrase = 0, change_comment = 0, show_cert = 0; 3062 int change_passphrase = 0, change_comment = 0, show_cert = 0;
2930 int find_host = 0, delete_host = 0, hash_hosts = 0; 3063 int find_host = 0, delete_host = 0, hash_hosts = 0;
2931 int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0; 3064 int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0;
2932 int prefer_agent = 0, convert_to = 0, convert_from = 0; 3065 int prefer_agent = 0, convert_to = 0, convert_from = 0;
2933 int print_public = 0, print_generic = 0, cert_serial_autoinc = 0; 3066 int print_public = 0, print_generic = 0, cert_serial_autoinc = 0;
2934 int do_gen_candidates = 0, do_screen_candidates = 0; 3067 int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0;
2935 unsigned long long cert_serial = 0; 3068 unsigned long long cert_serial = 0;
2936 char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; 3069 char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL;
2937 size_t i, nopts = 0; 3070 size_t i, nopts = 0;
2938 u_int32_t bits = 0; 3071 u_int32_t bits = 0;
2939 uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; 3072 uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD;
2940 FILE *f;
2941 const char *errstr; 3073 const char *errstr;
2942 int log_level = SYSLOG_LEVEL_INFO; 3074 int log_level = SYSLOG_LEVEL_INFO;
2943 char *sign_op = NULL; 3075 char *sign_op = NULL;
@@ -2965,8 +3097,8 @@ main(int argc, char **argv)
2965 3097
2966 sk_provider = getenv("SSH_SK_PROVIDER"); 3098 sk_provider = getenv("SSH_SK_PROVIDER");
2967 3099
2968 /* Remaining characters: dGjJKSTWx */ 3100 /* Remaining characters: dGjJSTWx */
2969 while ((opt = getopt(argc, argv, "ABHLQUXceghiklopquvy" 3101 while ((opt = getopt(argc, argv, "ABHKLQUXceghiklopquvy"
2970 "C:D:E:F:I:M:N:O:P:R:V:Y:Z:" 3102 "C:D:E:F:I:M:N:O:P:R:V:Y:Z:"
2971 "a:b:f:g:m:n:r:s:t:w:z:")) != -1) { 3103 "a:b:f:g:m:n:r:s:t:w:z:")) != -1) {
2972 switch (opt) { 3104 switch (opt) {
@@ -3046,6 +3178,9 @@ main(int argc, char **argv)
3046 case 'g': 3178 case 'g':
3047 print_generic = 1; 3179 print_generic = 1;
3048 break; 3180 break;
3181 case 'K':
3182 download_sk = 1;
3183 break;
3049 case 'P': 3184 case 'P':
3050 identity_passphrase = optarg; 3185 identity_passphrase = optarg;
3051 break; 3186 break;
@@ -3261,6 +3396,8 @@ main(int argc, char **argv)
3261 } 3396 }
3262 if (pkcs11provider != NULL) 3397 if (pkcs11provider != NULL)
3263 do_download(pw); 3398 do_download(pw);
3399 if (download_sk)
3400 return do_download_sk(sk_provider);
3264 if (print_fingerprint || print_bubblebabble) 3401 if (print_fingerprint || print_bubblebabble)
3265 do_fingerprint(pw); 3402 do_fingerprint(pw);
3266 if (change_passphrase) 3403 if (change_passphrase)
@@ -3356,7 +3493,7 @@ main(int argc, char **argv)
3356 printf("You may need to touch your security key " 3493 printf("You may need to touch your security key "
3357 "to authorize key generation.\n"); 3494 "to authorize key generation.\n");
3358 } 3495 }
3359 passphrase1 = NULL; 3496 passphrase = NULL;
3360 for (i = 0 ; i < 3; i++) { 3497 for (i = 0 ; i < 3; i++) {
3361 if (!quiet) { 3498 if (!quiet) {
3362 printf("You may need to touch your security " 3499 printf("You may need to touch your security "
@@ -3365,21 +3502,21 @@ main(int argc, char **argv)
3365 fflush(stdout); 3502 fflush(stdout);
3366 r = sshsk_enroll(type, sk_provider, 3503 r = sshsk_enroll(type, sk_provider,
3367 cert_key_id == NULL ? "ssh:" : cert_key_id, 3504 cert_key_id == NULL ? "ssh:" : cert_key_id,
3368 sk_flags, passphrase1, NULL, &private, NULL); 3505 sk_flags, passphrase, NULL, &private, NULL);
3369 if (r == 0) 3506 if (r == 0)
3370 break; 3507 break;
3371 if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) 3508 if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
3372 exit(1); /* error message already printed */ 3509 exit(1); /* error message already printed */
3373 if (passphrase1 != NULL) 3510 if (passphrase != NULL)
3374 freezero(passphrase1, strlen(passphrase1)); 3511 freezero(passphrase, strlen(passphrase));
3375 passphrase1 = read_passphrase("Enter PIN for security " 3512 passphrase = read_passphrase("Enter PIN for security "
3376 "key: ", RP_ALLOW_STDIN); 3513 "key: ", RP_ALLOW_STDIN);
3377 } 3514 }
3378 if (passphrase1 != NULL) 3515 if (passphrase != NULL)
3379 freezero(passphrase1, strlen(passphrase1)); 3516 freezero(passphrase, strlen(passphrase));
3380 if (i > 3) 3517 if (i > 3)
3381 fatal("Too many incorrect PINs"); 3518 fatal("Too many incorrect PINs");
3382 break; 3519 break;
3383 default: 3520 default:
3384 if ((r = sshkey_generate(type, bits, &private)) != 0) 3521 if ((r = sshkey_generate(type, bits, &private)) != 0)
3385 fatal("sshkey_generate failed"); 3522 fatal("sshkey_generate failed");
@@ -3409,35 +3546,9 @@ main(int argc, char **argv)
3409 /* If the file already exists, ask the user to confirm. */ 3546 /* If the file already exists, ask the user to confirm. */
3410 if (!confirm_overwrite(identity_file)) 3547 if (!confirm_overwrite(identity_file))
3411 exit(1); 3548 exit(1);
3412 /* Ask for a passphrase (twice). */
3413 if (identity_passphrase)
3414 passphrase1 = xstrdup(identity_passphrase);
3415 else if (identity_new_passphrase)
3416 passphrase1 = xstrdup(identity_new_passphrase);
3417 else {
3418passphrase_again:
3419 passphrase1 =
3420 read_passphrase("Enter passphrase (empty for no "
3421 "passphrase): ", RP_ALLOW_STDIN);
3422 passphrase2 = read_passphrase("Enter same passphrase again: ",
3423 RP_ALLOW_STDIN);
3424 if (strcmp(passphrase1, passphrase2) != 0) {
3425 /*
3426 * The passphrases do not match. Clear them and
3427 * retry.
3428 */
3429 explicit_bzero(passphrase1, strlen(passphrase1));
3430 explicit_bzero(passphrase2, strlen(passphrase2));
3431 free(passphrase1);
3432 free(passphrase2);
3433 printf("Passphrases do not match. Try again.\n");
3434 goto passphrase_again;
3435 }
3436 /* Clear the other copy of the passphrase. */
3437 explicit_bzero(passphrase2, strlen(passphrase2));
3438 free(passphrase2);
3439 }
3440 3549
3550 /* Determine the passphrase for the private key */
3551 passphrase = private_key_passphrase();
3441 if (identity_comment) { 3552 if (identity_comment) {
3442 strlcpy(comment, identity_comment, sizeof(comment)); 3553 strlcpy(comment, identity_comment, sizeof(comment));
3443 } else { 3554 } else {
@@ -3446,35 +3557,26 @@ passphrase_again:
3446 } 3557 }
3447 3558
3448 /* Save the key with the given passphrase and comment. */ 3559 /* Save the key with the given passphrase and comment. */
3449 if ((r = sshkey_save_private(private, identity_file, passphrase1, 3560 if ((r = sshkey_save_private(private, identity_file, passphrase,
3450 comment, private_key_format, openssh_format_cipher, rounds)) != 0) { 3561 comment, private_key_format, openssh_format_cipher, rounds)) != 0) {
3451 error("Saving key \"%s\" failed: %s", 3562 error("Saving key \"%s\" failed: %s",
3452 identity_file, ssh_err(r)); 3563 identity_file, ssh_err(r));
3453 explicit_bzero(passphrase1, strlen(passphrase1)); 3564 freezero(passphrase, strlen(passphrase));
3454 free(passphrase1);
3455 exit(1); 3565 exit(1);
3456 } 3566 }
3457 /* Clear the passphrase. */ 3567 freezero(passphrase, strlen(passphrase));
3458 explicit_bzero(passphrase1, strlen(passphrase1));
3459 free(passphrase1);
3460
3461 /* Clear the private key and the random number generator. */
3462 sshkey_free(private); 3568 sshkey_free(private);
3463 3569
3464 if (!quiet) 3570 if (!quiet) {
3465 printf("Your identification has been saved in %s.\n", identity_file); 3571 printf("Your identification has been saved in %s.\n",
3572 identity_file);
3573 }
3466 3574
3467 strlcat(identity_file, ".pub", sizeof(identity_file)); 3575 strlcat(identity_file, ".pub", sizeof(identity_file));
3468 if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) 3576 if ((r = sshkey_save_public(public, identity_file, comment)) != 0) {
3469 fatal("Unable to save public key to %s: %s", 3577 fatal("Unable to save public key to %s: %s",
3470 identity_file, strerror(errno)); 3578 identity_file, strerror(errno));
3471 if ((f = fdopen(fd, "w")) == NULL) 3579 }
3472 fatal("fdopen %s failed: %s", identity_file, strerror(errno));
3473 if ((r = sshkey_write(public, f)) != 0)
3474 error("write key failed: %s", ssh_err(r));
3475 fprintf(f, " %s\n", comment);
3476 if (ferror(f) || fclose(f) != 0)
3477 fatal("write public failed: %s", strerror(errno));
3478 3580
3479 if (!quiet) { 3581 if (!quiet) {
3480 fp = sshkey_fingerprint(public, fingerprint_hash, 3582 fp = sshkey_fingerprint(public, fingerprint_hash,