From c0f5b2294796451001fd328c44f0d00f1114eddf Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Wed, 8 Apr 2020 00:01:52 +0000 Subject: [PATCH] upstream: 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@ OpenBSD-Commit-ID: 7de31d80fb9062aa01901ddf040c286b64ff904e --- sshkey.c | 154 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 114 insertions(+), 40 deletions(-) diff --git a/sshkey.c b/sshkey.c index 6eba16ecf..0fc0f97ca 100644 --- a/sshkey.c +++ b/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. @@ -4060,30 +4060,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; } @@ -4133,13 +4124,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; @@ -4161,11 +4193,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); @@ -4228,13 +4255,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) @@ -4244,6 +4293,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 */ @@ -4258,18 +4342,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;