summaryrefslogtreecommitdiff
path: root/ssh-keygen.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2020-01-02 22:40:09 +0000
committerDamien Miller <djm@mindrot.org>2020-01-03 09:43:24 +1100
commit9039971887cccd95b209c479296f772a3a93e8e7 (patch)
tree935303f0db7914aa9011132be61e05f58a3e6c98 /ssh-keygen.c
parent878ba4350d57e905d6bb1865d8ff31bdfe5deab4 (diff)
upstream: ability to download FIDO2 resident keys from a token via
"ssh-keygen -K". This will save public/private keys into the current directory. This is handy if you move a token between hosts. feedback & ok markus@ OpenBSD-Commit-ID: d57c1f9802f7850f00a117a1d36682a6c6d10da6
Diffstat (limited to 'ssh-keygen.c')
-rw-r--r--ssh-keygen.c224
1 files changed, 163 insertions, 61 deletions
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,