Loïc
2020-Apr-09 14:08 UTC
[PATCH 2/2] Keep number of rounds when changing passphrase or comment in private keep file
--- authfile.c | 16 ++++++++------- authfile.h | 7 ++++--- ssh-keygen.c | 24 +++++++++++++++------- ssh-keysign.c | 2 +- sshconnect2.c | 2 +- sshd.c | 2 +- sshkey.c | 55 ++++++++++++++++++++++++++++++++++++++++++++------- sshkey.h | 10 +++++++++- 8 files changed, 90 insertions(+), 28 deletions(-) diff --git a/authfile.c b/authfile.c index 6867c326d8f0..fbdc7103e8e9 100644 --- a/authfile.c +++ b/authfile.c @@ -116,7 +116,7 @@ sshkey_perm_ok(int fd, const char *filename) int sshkey_load_private_type(int type, const char *filename, const char *passphrase, - struct sshkey **keyp, char **commentp) + struct sshkey **keyp, char **commentp, struct sshkey_vault **vault_infop) { int fd, r; @@ -124,6 +124,8 @@ sshkey_load_private_type(int type, const char *filename, const char *passphrase, *keyp = NULL; if (commentp != NULL) *commentp = NULL; + if (vault_infop != NULL) + *vault_infop = NULL; if ((fd = open(filename, O_RDONLY)) == -1) return SSH_ERR_SYSTEM_ERROR; @@ -132,7 +134,7 @@ sshkey_load_private_type(int type, const char *filename, const char *passphrase, if (r != 0) goto out; - r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp); + r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp, vault_infop); if (r == 0 && keyp && *keyp) r = sshkey_set_filename(*keyp, filename); out: @@ -142,7 +144,7 @@ sshkey_load_private_type(int type, const char *filename, const char *passphrase, int sshkey_load_private_type_fd(int fd, int type, const char *passphrase, - struct sshkey **keyp, char **commentp) + struct sshkey **keyp, char **commentp, struct sshkey_vault **vault_infop) { struct sshbuf *buffer = NULL; int r; @@ -151,7 +153,7 @@ sshkey_load_private_type_fd(int fd, int type, const char *passphrase, *keyp = NULL; if ((r = sshbuf_load_fd(fd, &buffer)) != 0 || (r = sshkey_parse_private_fileblob_type(buffer, type, - passphrase, keyp, commentp)) != 0) + passphrase, keyp, commentp, vault_infop)) != 0) goto out; /* success */ @@ -164,9 +166,9 @@ sshkey_load_private_type_fd(int fd, int type, const char *passphrase, /* This is identical to sshkey_load_private_type() with KEY_UNSPEC as type */ int sshkey_load_private(const char *filename, const char *passphrase, - struct sshkey **keyp, char **commentp) + struct sshkey **keyp, char **commentp, struct sshkey_vault **vault_infop) { - return sshkey_load_private_type(KEY_UNSPEC, filename, passphrase, keyp, commentp);; + return sshkey_load_private_type(KEY_UNSPEC, filename, passphrase, keyp, commentp, vault_infop);; } /* Load a pubkey from the unencrypted envelope of a new-format private key */ @@ -334,7 +336,7 @@ sshkey_load_private_cert(int type, const char *filename, const char *passphrase, } if ((r = sshkey_load_private_type(type, filename, - passphrase, &key, NULL)) != 0 || + passphrase, &key, NULL, NULL)) != 0 || (r = sshkey_load_cert(filename, &cert)) != 0) goto out; diff --git a/authfile.h b/authfile.h index 1db067a813a1..85f78ac78edf 100644 --- a/authfile.h +++ b/authfile.h @@ -29,6 +29,7 @@ struct sshbuf; struct sshkey; +struct sshkey_vault; /* XXX document these */ /* XXX some of these could probably be merged/retired */ @@ -37,13 +38,13 @@ int sshkey_save_private(struct sshkey *, const char *, const char *, const char *, int, const char *, int); int sshkey_load_cert(const char *, struct sshkey **); int sshkey_load_public(const char *, struct sshkey **, char **); -int sshkey_load_private(const char *, const char *, struct sshkey **, char **); +int sshkey_load_private(const char *, const char *, struct sshkey **, char **, struct sshkey_vault **); int sshkey_load_private_cert(int, const char *, const char *, struct sshkey **); int sshkey_load_private_type(int, const char *, const char *, - struct sshkey **, char **); + struct sshkey **, char **, struct sshkey_vault **); int sshkey_load_private_type_fd(int fd, int type, const char *passphrase, - struct sshkey **keyp, char **commentp); + struct sshkey **keyp, char **commentp, struct sshkey_vault **vault_infop); int sshkey_perm_ok(int, const char *); int sshkey_in_file(struct sshkey *, const char *, int, int); int sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file); diff --git a/ssh-keygen.c b/ssh-keygen.c index 802fd25c286f..134b01cc53c7 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -318,7 +318,7 @@ load_identity(const char *filename, char **commentp) if (commentp != NULL) *commentp = NULL; - if ((r = sshkey_load_private(filename, "", &prv, commentp)) == 0) + if ((r = sshkey_load_private(filename, "", &prv, commentp, NULL)) == 0) return prv; if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) fatal("Load key \"%s\": %s", filename, ssh_err(r)); @@ -326,7 +326,7 @@ load_identity(const char *filename, char **commentp) pass = xstrdup(identity_passphrase); else pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); - r = sshkey_load_private(filename, pass, &prv, commentp); + r = sshkey_load_private(filename, pass, &prv, commentp, NULL); freezero(pass, strlen(pass)); if (r != 0) fatal("Load key \"%s\": %s", filename, ssh_err(r)); @@ -918,7 +918,7 @@ fingerprint_private(const char *path) if ((r = sshkey_load_public(path, &public, &comment)) != 0) { debug("load public \"%s\": %s", path, ssh_err(r)); if ((r = sshkey_load_private(path, NULL, - &public, &comment)) != 0) { + &public, &comment, NULL)) != 0) { debug("load private \"%s\": %s", path, ssh_err(r)); fatal("%s is not a key file.", path); } @@ -1406,6 +1406,7 @@ do_change_passphrase(struct passwd *pw) char *old_passphrase, *passphrase1, *passphrase2; struct stat st; struct sshkey *private; + struct sshkey_vault *vault_info; int r; if (!have_identity) @@ -1413,7 +1414,7 @@ do_change_passphrase(struct passwd *pw) if (stat(identity_file, &st) == -1) fatal("%s: %s", identity_file, strerror(errno)); /* Try to load the file with empty passphrase. */ - r = sshkey_load_private(identity_file, "", &private, &comment); + r = sshkey_load_private(identity_file, "", &private, &comment, &vault_info); if (r == SSH_ERR_KEY_WRONG_PASSPHRASE) { if (identity_passphrase) old_passphrase = xstrdup(identity_passphrase); @@ -1422,7 +1423,7 @@ do_change_passphrase(struct passwd *pw) read_passphrase("Enter old passphrase: ", RP_ALLOW_STDIN); r = sshkey_load_private(identity_file, old_passphrase, - &private, &comment); + &private, &comment, &vault_info); freezero(old_passphrase, strlen(old_passphrase)); if (r != 0) goto badkey; @@ -1457,6 +1458,10 @@ do_change_passphrase(struct passwd *pw) freezero(passphrase2, strlen(passphrase2)); } + if (vault_info != NULL && strcmp(vault_info->kdfname, "bcrypt") == 0 && rounds == 0) { + rounds = vault_info->rounds; + printf("Keeping existing rounds %d\n", rounds); + } /* Save the file using the new passphrase. */ if ((r = sshkey_save_private(private, identity_file, passphrase1, comment, private_key_format, openssh_format_cipher, rounds)) != 0) { @@ -1513,6 +1518,7 @@ do_change_comment(struct passwd *pw, const char *identity_comment) char new_comment[1024], *comment, *passphrase; struct sshkey *private; struct sshkey *public; + struct sshkey *vault_info; struct stat st; FILE *f; int r, fd; @@ -1522,7 +1528,7 @@ do_change_comment(struct passwd *pw, const char *identity_comment) if (stat(identity_file, &st) == -1) fatal("%s: %s", identity_file, strerror(errno)); if ((r = sshkey_load_private(identity_file, "", - &private, &comment)) == 0) + &private, &comment, &vault_info)) == 0) passphrase = xstrdup(""); else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) fatal("Cannot load private key \"%s\": %s.", @@ -1537,7 +1543,7 @@ do_change_comment(struct passwd *pw, const char *identity_comment) RP_ALLOW_STDIN); /* Try to load using the passphrase. */ if ((r = sshkey_load_private(identity_file, passphrase, - &private, &comment)) != 0) { + &private, &comment, &vault_info)) != 0) { freezero(passphrase, strlen(passphrase)); fatal("Cannot load private key \"%s\": %s.", identity_file, ssh_err(r)); @@ -1577,6 +1583,10 @@ do_change_comment(struct passwd *pw, const char *identity_comment) exit(0); } + if (vault_info != NULL && strcmp(vault_info->kdfname, "bcrypt") == 0 && rounds == 0) { + rounds = vault_info->rounds; + printf("Keeping existing rounds %d\n", rounds); + } /* Save the file using the new passphrase. */ if ((r = sshkey_save_private(private, identity_file, passphrase, new_comment, private_key_format, openssh_format_cipher, diff --git a/ssh-keysign.c b/ssh-keysign.c index 3e3ea3e1481d..c9c20483b9a5 100644 --- a/ssh-keysign.c +++ b/ssh-keysign.c @@ -225,7 +225,7 @@ main(int argc, char **argv) if (key_fd[i] == -1) continue; r = sshkey_load_private_type_fd(key_fd[i], KEY_UNSPEC, - NULL, &key, NULL); + NULL, &key, NULL, NULL); close(key_fd[i]); if (r != 0) debug("parse key %d: %s", i, ssh_err(r)); diff --git a/sshconnect2.c b/sshconnect2.c index af00fb30c39b..f062e5316ae6 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1472,7 +1472,7 @@ load_identity_file(Identity *id) } } switch ((r = sshkey_load_private_type(KEY_UNSPEC, id->filename, - passphrase, &private, &comment))) { + passphrase, &private, &comment, NULL))) { case 0: break; case SSH_ERR_KEY_WRONG_PASSPHRASE: diff --git a/sshd.c b/sshd.c index 6f8f11a3bdac..42c19089a225 100644 --- a/sshd.c +++ b/sshd.c @@ -1789,7 +1789,7 @@ main(int ac, char **av) if (options.host_key_files[i] == NULL) continue; if ((r = sshkey_load_private(options.host_key_files[i], "", - &key, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR) + &key, NULL, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR) do_log2(ll, "Unable to load host key \"%s\": %s", options.host_key_files[i], ssh_err(r)); if (sshkey_is_sk(key) && diff --git a/sshkey.c b/sshkey.c index d6cc6365f0da..c57db193886d 100644 --- a/sshkey.c +++ b/sshkey.c @@ -679,6 +679,26 @@ sshkey_free(struct sshkey *k) freezero(k, sizeof(*k)); } +struct sshkey_vault * +sshkey_vault_new() +{ + struct sshkey_vault *k = calloc(1, sizeof(*k)); + if (k == NULL) + return NULL; + k->rounds = -1; + return k; +} + +void +sshkey_vault_free(struct sshkey_vault *k) +{ + if (k == NULL) + return; + free(k->kdfname); + free(k); + return; +} + static int cert_compare(struct sshkey_cert *a, struct sshkey_cert *b) { @@ -4136,7 +4156,7 @@ private2_uudecode(struct sshbuf *blob, struct sshbuf **decodedp) static int private2_decrypt(struct sshbuf *decoded, const char *passphrase, - struct sshbuf **decryptedp, struct sshkey **pubkeyp) + struct sshbuf **decryptedp, struct sshkey **pubkeyp, struct sshkey_vault **vault_infop) { char *ciphername = NULL, *kdfname = NULL; const struct sshcipher *cipher = NULL; @@ -4145,11 +4165,20 @@ private2_decrypt(struct sshbuf *decoded, const char *passphrase, struct sshbuf *kdf = NULL, *decrypted = NULL; struct sshcipher_ctx *ciphercontext = NULL; struct sshkey *pubkey = NULL; + struct sshkey_vault *vault_info = NULL; u_char *key = NULL, *salt = NULL, *dp; u_int blocksize, rounds, nkeys, encrypted_len, check1, check2; if (decoded == NULL || decryptedp == NULL || pubkeyp == NULL) return SSH_ERR_INVALID_ARGUMENT; + + if (vault_infop != NULL) { + *vault_infop = NULL; + } + if ((vault_info = calloc(1, sizeof(*vault_info))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } *decryptedp = NULL; *pubkeyp = NULL; @@ -4185,6 +4214,10 @@ private2_decrypt(struct sshbuf *decoded, const char *passphrase, r = SSH_ERR_KEY_UNKNOWN_CIPHER; goto out; } + if ((vault_info->kdfname = strdup(kdfname)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } if (strcmp(kdfname, "none") == 0 && strcmp(ciphername, "none") != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; @@ -4215,6 +4248,7 @@ private2_decrypt(struct sshbuf *decoded, const char *passphrase, if ((r = sshbuf_get_string(kdf, &salt, &slen)) != 0 || (r = sshbuf_get_u32(kdf, &rounds)) != 0) goto out; + vault_info->rounds = rounds; if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen, key, keylen + ivlen, rounds) < 0) { r = SSH_ERR_INVALID_FORMAT; @@ -4262,6 +4296,10 @@ private2_decrypt(struct sshbuf *decoded, const char *passphrase, decrypted = NULL; *pubkeyp = pubkey; pubkey = NULL; + if (vault_infop != NULL) { + *vault_infop = vault_info; + vault_info = NULL; + } r = 0; out: cipher_free(ciphercontext); @@ -4278,6 +4316,7 @@ private2_decrypt(struct sshbuf *decoded, const char *passphrase, } sshbuf_free(kdf); sshbuf_free(decrypted); + sshkey_vault_free(vault_info); return r; } @@ -4308,7 +4347,7 @@ private2_check_padding(struct sshbuf *decrypted) static int sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, - struct sshkey **keyp, char **commentp) + struct sshkey **keyp, char **commentp, struct sshkey_vault **vault_infop) { char *comment = NULL; int r = SSH_ERR_INTERNAL_ERROR; @@ -4323,7 +4362,7 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, /* Undo base64 encoding and decrypt the private section */ if ((r = private2_uudecode(blob, &decoded)) != 0 || (r = private2_decrypt(decoded, passphrase, - &decrypted, &pubkey)) != 0) + &decrypted, &pubkey, vault_infop)) != 0) goto out; if (type != KEY_UNSPEC && @@ -4731,7 +4770,7 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type, int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, - const char *passphrase, struct sshkey **keyp, char **commentp) + const char *passphrase, struct sshkey **keyp, char **commentp, struct sshkey_vault **vault_infop) { int r = SSH_ERR_INTERNAL_ERROR; @@ -4739,16 +4778,18 @@ sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, *keyp = NULL; if (commentp != NULL) *commentp = NULL; + if (vault_infop != NULL) + *vault_infop = NULL; switch (type) { case KEY_ED25519: case KEY_XMSS: /* No fallback for new-format-only keys */ return sshkey_parse_private2(blob, type, passphrase, - keyp, commentp); + keyp, commentp, vault_infop); default: r = sshkey_parse_private2(blob, type, passphrase, keyp, - commentp); + commentp, vault_infop); /* Only fallback to PEM parser if a format error occurred. */ if (r != SSH_ERR_INVALID_FORMAT) return r; @@ -4771,7 +4812,7 @@ sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase, *commentp = NULL; return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC, - passphrase, keyp, commentp); + passphrase, keyp, commentp, NULL); } void diff --git a/sshkey.h b/sshkey.h index 9c1d4f6372f6..70f855ac7e51 100644 --- a/sshkey.h +++ b/sshkey.h @@ -162,6 +162,14 @@ struct sshkey_sig_details { uint8_t sk_flags; /* U2F signature flags; see ssh-sk.h */ }; +/* Key storage parameters in private key file */ +struct sshkey_vault { + char *kdfname; + int rounds; +}; +struct sshkey_vault *sshkey_vault_new(); +void sshkey_vault_free(struct sshkey_vault *); + struct sshkey *sshkey_new(int); void sshkey_free(struct sshkey *); int sshkey_equal_public(const struct sshkey *, @@ -258,7 +266,7 @@ int sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, int sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase, struct sshkey **keyp, char **commentp); int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, - const char *passphrase, struct sshkey **keyp, char **commentp); + const char *passphrase, struct sshkey **keyp, char **commentp, struct sshkey_vault **vault_infop); int sshkey_parse_pubkey_from_private_fileblob_type(struct sshbuf *blob, int type, struct sshkey **pubkeyp); -- 2.17.1