diff options
author | djm@openbsd.org <djm@openbsd.org> | 2020-04-08 02:01:52 +0200 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2020-04-08 02:14:21 +0200 |
commit | c0f5b2294796451001fd328c44f0d00f1114eddf (patch) | |
tree | c5ca6f4e3ea8fbba8e68cce80758281c3f2b487b /sshkey.c | |
parent | Include openssl-compat.h before checking ifdefs. (diff) | |
download | openssh-c0f5b2294796451001fd328c44f0d00f1114eddf.tar.xz openssh-c0f5b2294796451001fd328c44f0d00f1114eddf.zip |
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
Diffstat (limited to 'sshkey.c')
-rw-r--r-- | sshkey.c | 154 |
1 files changed, 114 insertions, 40 deletions
@@ -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; |