diff options
-rw-r--r-- | sshkey.c | 154 |
1 files changed, 114 insertions, 40 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshkey.c,v 1.102 2020/03/06 18:23:17 markus Exp $ */ | 1 | /* $OpenBSD: sshkey.c,v 1.103 2020/04/08 00:01:52 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. |
4 | * Copyright (c) 2008 Alexander von Gernler. All rights reserved. | 4 | * Copyright (c) 2008 Alexander von Gernler. All rights reserved. |
@@ -4060,30 +4060,21 @@ sshkey_private_to_blob2(struct sshkey *prv, struct sshbuf *blob, | |||
4060 | } | 4060 | } |
4061 | 4061 | ||
4062 | static int | 4062 | static int |
4063 | sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, | 4063 | private2_uudecode(struct sshbuf *blob, struct sshbuf **decodedp) |
4064 | struct sshkey **keyp, char **commentp) | ||
4065 | { | 4064 | { |
4066 | char *comment = NULL, *ciphername = NULL, *kdfname = NULL; | ||
4067 | const struct sshcipher *cipher = NULL; | ||
4068 | const u_char *cp; | 4065 | const u_char *cp; |
4069 | int r = SSH_ERR_INTERNAL_ERROR; | ||
4070 | size_t encoded_len; | 4066 | size_t encoded_len; |
4071 | size_t i, keylen = 0, ivlen = 0, authlen = 0, slen = 0; | 4067 | int r; |
4068 | u_char last; | ||
4072 | struct sshbuf *encoded = NULL, *decoded = NULL; | 4069 | struct sshbuf *encoded = NULL, *decoded = NULL; |
4073 | struct sshbuf *kdf = NULL, *decrypted = NULL; | ||
4074 | struct sshcipher_ctx *ciphercontext = NULL; | ||
4075 | struct sshkey *k = NULL; | ||
4076 | u_char *key = NULL, *salt = NULL, *dp, pad, last; | ||
4077 | u_int blocksize, rounds, nkeys, encrypted_len, check1, check2; | ||
4078 | 4070 | ||
4079 | if (keyp != NULL) | 4071 | if (blob == NULL || decodedp == NULL) |
4080 | *keyp = NULL; | 4072 | return SSH_ERR_INVALID_ARGUMENT; |
4081 | if (commentp != NULL) | 4073 | |
4082 | *commentp = NULL; | 4074 | *decodedp = NULL; |
4083 | 4075 | ||
4084 | if ((encoded = sshbuf_new()) == NULL || | 4076 | if ((encoded = sshbuf_new()) == NULL || |
4085 | (decoded = sshbuf_new()) == NULL || | 4077 | (decoded = sshbuf_new()) == NULL) { |
4086 | (decrypted = sshbuf_new()) == NULL) { | ||
4087 | r = SSH_ERR_ALLOC_FAIL; | 4078 | r = SSH_ERR_ALLOC_FAIL; |
4088 | goto out; | 4079 | goto out; |
4089 | } | 4080 | } |
@@ -4133,13 +4124,54 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, | |||
4133 | r = SSH_ERR_INVALID_FORMAT; | 4124 | r = SSH_ERR_INVALID_FORMAT; |
4134 | goto out; | 4125 | goto out; |
4135 | } | 4126 | } |
4127 | /* success */ | ||
4128 | *decodedp = decoded; | ||
4129 | decoded = NULL; | ||
4130 | r = 0; | ||
4131 | out: | ||
4132 | sshbuf_free(encoded); | ||
4133 | sshbuf_free(decoded); | ||
4134 | return r; | ||
4135 | } | ||
4136 | |||
4137 | static int | ||
4138 | private2_decrypt(struct sshbuf *decoded, struct sshbuf **decryptedp, | ||
4139 | const char *passphrase) | ||
4140 | { | ||
4141 | char *ciphername = NULL, *kdfname = NULL; | ||
4142 | const struct sshcipher *cipher = NULL; | ||
4143 | int r = SSH_ERR_INTERNAL_ERROR; | ||
4144 | size_t keylen = 0, ivlen = 0, authlen = 0, slen = 0; | ||
4145 | struct sshbuf *kdf = NULL, *decrypted = NULL; | ||
4146 | struct sshcipher_ctx *ciphercontext = NULL; | ||
4147 | u_char *key = NULL, *salt = NULL, *dp; | ||
4148 | u_int blocksize, rounds, nkeys, encrypted_len, check1, check2; | ||
4149 | |||
4150 | if (decoded == NULL || decryptedp == NULL) | ||
4151 | return SSH_ERR_INVALID_ARGUMENT; | ||
4152 | |||
4153 | *decryptedp = NULL; | ||
4154 | |||
4155 | if ((decrypted = sshbuf_new()) == NULL) { | ||
4156 | r = SSH_ERR_ALLOC_FAIL; | ||
4157 | goto out; | ||
4158 | } | ||
4159 | |||
4136 | /* parse public portion of key */ | 4160 | /* parse public portion of key */ |
4137 | if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 || | 4161 | if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 || |
4138 | (r = sshbuf_get_cstring(decoded, &ciphername, NULL)) != 0 || | 4162 | (r = sshbuf_get_cstring(decoded, &ciphername, NULL)) != 0 || |
4139 | (r = sshbuf_get_cstring(decoded, &kdfname, NULL)) != 0 || | 4163 | (r = sshbuf_get_cstring(decoded, &kdfname, NULL)) != 0 || |
4140 | (r = sshbuf_froms(decoded, &kdf)) != 0 || | 4164 | (r = sshbuf_froms(decoded, &kdf)) != 0 || |
4141 | (r = sshbuf_get_u32(decoded, &nkeys)) != 0 || | 4165 | (r = sshbuf_get_u32(decoded, &nkeys)) != 0) |
4142 | (r = sshbuf_skip_string(decoded)) != 0 || /* pubkey */ | 4166 | goto out; |
4167 | |||
4168 | if (nkeys != 1) { | ||
4169 | /* XXX only one key supported at present */ | ||
4170 | r = SSH_ERR_INVALID_FORMAT; | ||
4171 | goto out; | ||
4172 | } | ||
4173 | |||
4174 | if ((r = sshbuf_skip_string(decoded)) != 0 || /* pubkey */ | ||
4143 | (r = sshbuf_get_u32(decoded, &encrypted_len)) != 0) | 4175 | (r = sshbuf_get_u32(decoded, &encrypted_len)) != 0) |
4144 | goto out; | 4176 | goto out; |
4145 | 4177 | ||
@@ -4161,11 +4193,6 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, | |||
4161 | r = SSH_ERR_KEY_WRONG_PASSPHRASE; | 4193 | r = SSH_ERR_KEY_WRONG_PASSPHRASE; |
4162 | goto out; | 4194 | goto out; |
4163 | } | 4195 | } |
4164 | if (nkeys != 1) { | ||
4165 | /* XXX only one key supported */ | ||
4166 | r = SSH_ERR_INVALID_FORMAT; | ||
4167 | goto out; | ||
4168 | } | ||
4169 | 4196 | ||
4170 | /* check size of encrypted key blob */ | 4197 | /* check size of encrypted key blob */ |
4171 | blocksize = cipher_blocksize(cipher); | 4198 | blocksize = cipher_blocksize(cipher); |
@@ -4228,13 +4255,35 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, | |||
4228 | r = SSH_ERR_KEY_WRONG_PASSPHRASE; | 4255 | r = SSH_ERR_KEY_WRONG_PASSPHRASE; |
4229 | goto out; | 4256 | goto out; |
4230 | } | 4257 | } |
4258 | /* success */ | ||
4259 | *decryptedp = decrypted; | ||
4260 | decrypted = NULL; | ||
4261 | r = 0; | ||
4262 | out: | ||
4263 | cipher_free(ciphercontext); | ||
4264 | free(ciphername); | ||
4265 | free(kdfname); | ||
4266 | if (salt != NULL) { | ||
4267 | explicit_bzero(salt, slen); | ||
4268 | free(salt); | ||
4269 | } | ||
4270 | if (key != NULL) { | ||
4271 | explicit_bzero(key, keylen + ivlen); | ||
4272 | free(key); | ||
4273 | } | ||
4274 | sshbuf_free(kdf); | ||
4275 | sshbuf_free(decrypted); | ||
4276 | return r; | ||
4277 | } | ||
4231 | 4278 | ||
4232 | /* Load the private key and comment */ | 4279 | /* Check deterministic padding after private key */ |
4233 | if ((r = sshkey_private_deserialize(decrypted, &k)) != 0 || | 4280 | static int |
4234 | (r = sshbuf_get_cstring(decrypted, &comment, NULL)) != 0) | 4281 | private2_check_padding(struct sshbuf *decrypted) |
4235 | goto out; | 4282 | { |
4283 | u_char pad; | ||
4284 | size_t i; | ||
4285 | int r = SSH_ERR_INTERNAL_ERROR; | ||
4236 | 4286 | ||
4237 | /* Check deterministic padding */ | ||
4238 | i = 0; | 4287 | i = 0; |
4239 | while (sshbuf_len(decrypted)) { | 4288 | while (sshbuf_len(decrypted)) { |
4240 | if ((r = sshbuf_get_u8(decrypted, &pad)) != 0) | 4289 | if ((r = sshbuf_get_u8(decrypted, &pad)) != 0) |
@@ -4244,6 +4293,41 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, | |||
4244 | goto out; | 4293 | goto out; |
4245 | } | 4294 | } |
4246 | } | 4295 | } |
4296 | /* success */ | ||
4297 | r = 0; | ||
4298 | out: | ||
4299 | explicit_bzero(&pad, sizeof(pad)); | ||
4300 | explicit_bzero(&i, sizeof(i)); | ||
4301 | return r; | ||
4302 | } | ||
4303 | |||
4304 | static int | ||
4305 | sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, | ||
4306 | struct sshkey **keyp, char **commentp) | ||
4307 | { | ||
4308 | char *comment = NULL; | ||
4309 | int r = SSH_ERR_INTERNAL_ERROR; | ||
4310 | struct sshbuf *decoded = NULL, *decrypted = NULL; | ||
4311 | struct sshkey *k = NULL; | ||
4312 | |||
4313 | if (keyp != NULL) | ||
4314 | *keyp = NULL; | ||
4315 | if (commentp != NULL) | ||
4316 | *commentp = NULL; | ||
4317 | |||
4318 | /* Undo base64 encoding and decrypt the private section */ | ||
4319 | if ((r = private2_uudecode(blob, &decoded)) != 0 || | ||
4320 | (r = private2_decrypt(decoded, &decrypted, passphrase)) != 0) | ||
4321 | goto out; | ||
4322 | |||
4323 | /* Load the private key and comment */ | ||
4324 | if ((r = sshkey_private_deserialize(decrypted, &k)) != 0 || | ||
4325 | (r = sshbuf_get_cstring(decrypted, &comment, NULL)) != 0) | ||
4326 | goto out; | ||
4327 | |||
4328 | /* Check deterministic padding after private section */ | ||
4329 | if ((r = private2_check_padding(decrypted)) != 0) | ||
4330 | goto out; | ||
4247 | 4331 | ||
4248 | /* XXX decode pubkey and check against private */ | 4332 | /* XXX decode pubkey and check against private */ |
4249 | 4333 | ||
@@ -4258,18 +4342,8 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, | |||
4258 | comment = NULL; | 4342 | comment = NULL; |
4259 | } | 4343 | } |
4260 | out: | 4344 | out: |
4261 | pad = 0; | ||
4262 | cipher_free(ciphercontext); | ||
4263 | free(ciphername); | ||
4264 | free(kdfname); | ||
4265 | free(comment); | 4345 | free(comment); |
4266 | if (salt != NULL) | ||
4267 | freezero(salt, slen); | ||
4268 | if (key != NULL) | ||
4269 | freezero(key, keylen + ivlen); | ||
4270 | sshbuf_free(encoded); | ||
4271 | sshbuf_free(decoded); | 4346 | sshbuf_free(decoded); |
4272 | sshbuf_free(kdf); | ||
4273 | sshbuf_free(decrypted); | 4347 | sshbuf_free(decrypted); |
4274 | sshkey_free(k); | 4348 | sshkey_free(k); |
4275 | return r; | 4349 | return r; |