[PATCH 2/2] Keep number of rounds when changing passphrase or comment in private keep file

Loïc loic at venez.fr
Fri Apr 10 00:08:45 AEST 2020


---
 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



More information about the openssh-unix-dev mailing list