summaryrefslogtreecommitdiffstats
path: root/usr.bin/ssh/sshkey.c
diff options
context:
space:
mode:
authordjm <djm@openbsd.org>2020-04-08 00:01:52 +0000
committerdjm <djm@openbsd.org>2020-04-08 00:01:52 +0000
commita8b002979ef949acff340a1ce1645a99a9058771 (patch)
treeefe5e7f6e3e244234983e77e830557eda725d4bd /usr.bin/ssh/sshkey.c
parentSeparate the place to put the <a href> permalink (now marked (diff)
downloadwireguard-openbsd-a8b002979ef949acff340a1ce1645a99a9058771.tar.xz
wireguard-openbsd-a8b002979ef949acff340a1ce1645a99a9058771.zip
refactor private key parsing a little
Split out the base64 decoding and private section decryption steps in to separate functions. This will make the decryption step easier to fuzz as well as making it easier to write a "load public key from new-format private key" function. ok markus@
Diffstat (limited to 'usr.bin/ssh/sshkey.c')
-rw-r--r--usr.bin/ssh/sshkey.c154
1 files changed, 114 insertions, 40 deletions
diff --git a/usr.bin/ssh/sshkey.c b/usr.bin/ssh/sshkey.c
index a92205e2c00..3580470751e 100644
--- a/usr.bin/ssh/sshkey.c
+++ b/usr.bin/ssh/sshkey.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshkey.c,v 1.102 2020/03/06 18:23:17 markus Exp $ */
+/* $OpenBSD: sshkey.c,v 1.103 2020/04/08 00:01:52 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Alexander von Gernler. All rights reserved.
@@ -4005,30 +4005,21 @@ sshkey_private_to_blob2(struct sshkey *prv, struct sshbuf *blob,
}
static int
-sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase,
- struct sshkey **keyp, char **commentp)
+private2_uudecode(struct sshbuf *blob, struct sshbuf **decodedp)
{
- char *comment = NULL, *ciphername = NULL, *kdfname = NULL;
- const struct sshcipher *cipher = NULL;
const u_char *cp;
- int r = SSH_ERR_INTERNAL_ERROR;
size_t encoded_len;
- size_t i, keylen = 0, ivlen = 0, authlen = 0, slen = 0;
+ int r;
+ u_char last;
struct sshbuf *encoded = NULL, *decoded = NULL;
- struct sshbuf *kdf = NULL, *decrypted = NULL;
- struct sshcipher_ctx *ciphercontext = NULL;
- struct sshkey *k = NULL;
- u_char *key = NULL, *salt = NULL, *dp, pad, last;
- u_int blocksize, rounds, nkeys, encrypted_len, check1, check2;
- if (keyp != NULL)
- *keyp = NULL;
- if (commentp != NULL)
- *commentp = NULL;
+ if (blob == NULL || decodedp == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+
+ *decodedp = NULL;
if ((encoded = sshbuf_new()) == NULL ||
- (decoded = sshbuf_new()) == NULL ||
- (decrypted = sshbuf_new()) == NULL) {
+ (decoded = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
@@ -4078,13 +4069,54 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase,
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
+ /* success */
+ *decodedp = decoded;
+ decoded = NULL;
+ r = 0;
+ out:
+ sshbuf_free(encoded);
+ sshbuf_free(decoded);
+ return r;
+}
+
+static int
+private2_decrypt(struct sshbuf *decoded, struct sshbuf **decryptedp,
+ const char *passphrase)
+{
+ char *ciphername = NULL, *kdfname = NULL;
+ const struct sshcipher *cipher = NULL;
+ int r = SSH_ERR_INTERNAL_ERROR;
+ size_t keylen = 0, ivlen = 0, authlen = 0, slen = 0;
+ struct sshbuf *kdf = NULL, *decrypted = NULL;
+ struct sshcipher_ctx *ciphercontext = NULL;
+ u_char *key = NULL, *salt = NULL, *dp;
+ u_int blocksize, rounds, nkeys, encrypted_len, check1, check2;
+
+ if (decoded == NULL || decryptedp == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+
+ *decryptedp = NULL;
+
+ if ((decrypted = sshbuf_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+
/* parse public portion of key */
if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 ||
(r = sshbuf_get_cstring(decoded, &ciphername, NULL)) != 0 ||
(r = sshbuf_get_cstring(decoded, &kdfname, NULL)) != 0 ||
(r = sshbuf_froms(decoded, &kdf)) != 0 ||
- (r = sshbuf_get_u32(decoded, &nkeys)) != 0 ||
- (r = sshbuf_skip_string(decoded)) != 0 || /* pubkey */
+ (r = sshbuf_get_u32(decoded, &nkeys)) != 0)
+ goto out;
+
+ if (nkeys != 1) {
+ /* XXX only one key supported at present */
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+
+ if ((r = sshbuf_skip_string(decoded)) != 0 || /* pubkey */
(r = sshbuf_get_u32(decoded, &encrypted_len)) != 0)
goto out;
@@ -4106,11 +4138,6 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase,
r = SSH_ERR_KEY_WRONG_PASSPHRASE;
goto out;
}
- if (nkeys != 1) {
- /* XXX only one key supported */
- r = SSH_ERR_INVALID_FORMAT;
- goto out;
- }
/* check size of encrypted key blob */
blocksize = cipher_blocksize(cipher);
@@ -4173,13 +4200,35 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase,
r = SSH_ERR_KEY_WRONG_PASSPHRASE;
goto out;
}
+ /* success */
+ *decryptedp = decrypted;
+ decrypted = NULL;
+ r = 0;
+ out:
+ cipher_free(ciphercontext);
+ free(ciphername);
+ free(kdfname);
+ if (salt != NULL) {
+ explicit_bzero(salt, slen);
+ free(salt);
+ }
+ if (key != NULL) {
+ explicit_bzero(key, keylen + ivlen);
+ free(key);
+ }
+ sshbuf_free(kdf);
+ sshbuf_free(decrypted);
+ return r;
+}
- /* Load the private key and comment */
- if ((r = sshkey_private_deserialize(decrypted, &k)) != 0 ||
- (r = sshbuf_get_cstring(decrypted, &comment, NULL)) != 0)
- goto out;
+/* Check deterministic padding after private key */
+static int
+private2_check_padding(struct sshbuf *decrypted)
+{
+ u_char pad;
+ size_t i;
+ int r = SSH_ERR_INTERNAL_ERROR;
- /* Check deterministic padding */
i = 0;
while (sshbuf_len(decrypted)) {
if ((r = sshbuf_get_u8(decrypted, &pad)) != 0)
@@ -4189,6 +4238,41 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase,
goto out;
}
}
+ /* success */
+ r = 0;
+ out:
+ explicit_bzero(&pad, sizeof(pad));
+ explicit_bzero(&i, sizeof(i));
+ return r;
+}
+
+static int
+sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase,
+ struct sshkey **keyp, char **commentp)
+{
+ char *comment = NULL;
+ int r = SSH_ERR_INTERNAL_ERROR;
+ struct sshbuf *decoded = NULL, *decrypted = NULL;
+ struct sshkey *k = NULL;
+
+ if (keyp != NULL)
+ *keyp = NULL;
+ if (commentp != NULL)
+ *commentp = NULL;
+
+ /* Undo base64 encoding and decrypt the private section */
+ if ((r = private2_uudecode(blob, &decoded)) != 0 ||
+ (r = private2_decrypt(decoded, &decrypted, passphrase)) != 0)
+ goto out;
+
+ /* Load the private key and comment */
+ if ((r = sshkey_private_deserialize(decrypted, &k)) != 0 ||
+ (r = sshbuf_get_cstring(decrypted, &comment, NULL)) != 0)
+ goto out;
+
+ /* Check deterministic padding after private section */
+ if ((r = private2_check_padding(decrypted)) != 0)
+ goto out;
/* XXX decode pubkey and check against private */
@@ -4203,18 +4287,8 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase,
comment = NULL;
}
out:
- pad = 0;
- cipher_free(ciphercontext);
- free(ciphername);
- free(kdfname);
free(comment);
- if (salt != NULL)
- freezero(salt, slen);
- if (key != NULL)
- freezero(key, keylen + ivlen);
- sshbuf_free(encoded);
sshbuf_free(decoded);
- sshbuf_free(kdf);
sshbuf_free(decrypted);
sshkey_free(k);
return r;