=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/sshkey.c,v retrieving revision 1.102 retrieving revision 1.103 diff -u -r1.102 -r1.103 --- src/usr.bin/ssh/sshkey.c 2020/03/06 18:23:17 1.102 +++ src/usr.bin/ssh/sshkey.c 2020/04/08 00:01:52 1.103 @@ -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 @@ } 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 @@ 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 @@ 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 @@ 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,7 +4238,42 @@ 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 */ /* success */ @@ -4203,18 +4287,8 @@ 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;