From 633de33b192d808d87537834c316dc8b75fe1880 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Thu, 15 May 2014 13:48:26 +1000 Subject: - djm@cvs.openbsd.org 2014/04/28 03:09:18 [authfile.c bufaux.c buffer.h channels.c krl.c mux.c packet.c packet.h] [ssh-keygen.c] buffer_get_string_ptr's return should be const to remind callers that futzing with it will futz with the actual buffer contents --- krl.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'krl.c') diff --git a/krl.c b/krl.c index 3b4cded05..c7aa57e66 100644 --- a/krl.c +++ b/krl.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $OpenBSD: krl.c,v 1.14 2014/01/31 16:39:19 tedu Exp $ */ +/* $OpenBSD: krl.c,v 1.15 2014/04/28 03:09:18 djm Exp $ */ #include "includes.h" @@ -753,7 +753,8 @@ static int parse_revoked_certs(Buffer *buf, struct ssh_krl *krl) { int ret = -1, nbits; - u_char type, *blob; + u_char type; + const u_char *blob; u_int blen; Buffer subsect; u_int64_t serial, serial_lo, serial_hi; @@ -887,7 +888,8 @@ ssh_krl_from_blob(Buffer *buf, struct ssh_krl **krlp, char timestamp[64]; int ret = -1, r, sig_seen; Key *key = NULL, **ca_used = NULL; - u_char type, *blob, *rdata = NULL; + u_char type, *rdata = NULL; + const u_char *blob; u_int i, j, sig_off, sects_off, rlen, blen, format_version, nca_used; nca_used = 0; -- cgit v1.2.3 From 2cd7929250cf9e9f658d70dcd452f529ba08c942 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Wed, 2 Jul 2014 12:48:30 +1000 Subject: - djm@cvs.openbsd.org 2014/06/24 00:52:02 [krl.c] fix bug in KRL generation: multiple consecutive revoked certificate serial number ranges could be serialised to an invalid format. Readers of a broken KRL caused by this bug will fail closed, so no should-have-been-revoked key will be accepted. --- ChangeLog | 7 +++++++ krl.c | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'krl.c') diff --git a/ChangeLog b/ChangeLog index c7f73af16..8f24fc6bc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -17,6 +17,13 @@ the corresponding ssh_put_bignum functions create. This fixes the use of 16384-bit RSA keys (bug reported by Eivind Evensen). ok djm@ + - djm@cvs.openbsd.org 2014/06/24 00:52:02 + [krl.c] + fix bug in KRL generation: multiple consecutive revoked certificate + serial number ranges could be serialised to an invalid format. + + Readers of a broken KRL caused by this bug will fail closed, so no + should-have-been-revoked key will be accepted. 20140618 - (tim) [openssh/session.c] Work around to get chroot sftp working on UnixWare diff --git a/krl.c b/krl.c index c7aa57e66..557a48ebb 100644 --- a/krl.c +++ b/krl.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $OpenBSD: krl.c,v 1.15 2014/04/28 03:09:18 djm Exp $ */ +/* $OpenBSD: krl.c,v 1.16 2014/06/24 00:52:02 djm Exp $ */ #include "includes.h" @@ -575,6 +575,7 @@ revoked_certs_generate(struct revoked_certs *rc, Buffer *buf) buffer_put_char(buf, state); buffer_put_string(buf, buffer_ptr(§), buffer_len(§)); + buffer_clear(§); } /* If we are starting a new section then prepare it now */ -- cgit v1.2.3 From 8668706d0f52654fe64c0ca41a96113aeab8d2b8 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Wed, 2 Jul 2014 15:28:02 +1000 Subject: - djm@cvs.openbsd.org 2014/06/24 01:13:21 [Makefile.in auth-bsdauth.c auth-chall.c auth-options.c auth-rsa.c [auth2-none.c auth2-pubkey.c authfile.c authfile.h cipher-3des1.c [cipher-chachapoly.c cipher-chachapoly.h cipher.c cipher.h [digest-libc.c digest-openssl.c digest.h dns.c entropy.c hmac.h [hostfile.c key.c key.h krl.c monitor.c packet.c rsa.c rsa.h [ssh-add.c ssh-agent.c ssh-dss.c ssh-ecdsa.c ssh-ed25519.c [ssh-keygen.c ssh-pkcs11-client.c ssh-pkcs11-helper.c ssh-pkcs11.c [ssh-rsa.c sshbuf-misc.c sshbuf.h sshconnect.c sshconnect1.c [sshconnect2.c sshd.c sshkey.c sshkey.h [openbsd-compat/openssl-compat.c openbsd-compat/openssl-compat.h] New key API: refactor key-related functions to be more library-like, existing API is offered as a set of wrappers. with and ok markus@ Thanks also to Ben Hawkes, David Tomaschik, Ivan Fratric, Matthew Dempsky and Ron Bowes for a detailed review a few months ago. NB. This commit also removes portable OpenSSH support for OpenSSL <0.9.8e. --- ChangeLog | 20 + Makefile.in | 5 +- auth-bsdauth.c | 4 +- auth-chall.c | 5 +- auth-options.c | 14 +- auth-rsa.c | 5 +- auth2-none.c | 5 +- auth2-pubkey.c | 6 +- authfile.c | 1421 ++++----------- authfile.h | 63 +- cipher-3des1.c | 55 +- cipher-chachapoly.c | 25 +- cipher-chachapoly.h | 4 +- cipher.c | 363 ++-- cipher.h | 57 +- digest-libc.c | 27 +- digest-openssl.c | 36 +- digest.h | 7 +- dns.c | 4 +- entropy.c | 2 + hmac.h | 5 +- hostfile.c | 3 +- key.c | 2803 +++------------------------- key.h | 187 +- krl.c | 8 +- monitor.c | 5 +- openbsd-compat/openssl-compat.c | 141 +- openbsd-compat/openssl-compat.h | 118 +- packet.c | 38 +- rsa.c | 113 +- rsa.h | 6 +- ssh-add.c | 24 +- ssh-agent.c | 24 +- ssh-dss.c | 237 +-- ssh-ecdsa.c | 232 +-- ssh-ed25519.c | 183 +- ssh-keygen.c | 20 +- ssh-pkcs11-client.c | 4 +- ssh-pkcs11-helper.c | 8 +- ssh-pkcs11.c | 4 +- ssh-rsa.c | 260 +-- sshbuf-misc.c | 16 +- sshbuf.h | 7 +- sshconnect.c | 4 +- sshconnect1.c | 18 +- sshconnect2.c | 8 +- sshd.c | 16 +- sshkey.c | 3843 +++++++++++++++++++++++++++++++++++++++ sshkey.h | 222 +++ 49 files changed, 5812 insertions(+), 4873 deletions(-) create mode 100644 sshkey.c create mode 100644 sshkey.h (limited to 'krl.c') diff --git a/ChangeLog b/ChangeLog index 8f24fc6bc..e821f6de2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,6 +24,26 @@ Readers of a broken KRL caused by this bug will fail closed, so no should-have-been-revoked key will be accepted. + - djm@cvs.openbsd.org 2014/06/24 01:13:21 + [Makefile.in auth-bsdauth.c auth-chall.c auth-options.c auth-rsa.c + [auth2-none.c auth2-pubkey.c authfile.c authfile.h cipher-3des1.c + [cipher-chachapoly.c cipher-chachapoly.h cipher.c cipher.h + [digest-libc.c digest-openssl.c digest.h dns.c entropy.c hmac.h + [hostfile.c key.c key.h krl.c monitor.c packet.c rsa.c rsa.h + [ssh-add.c ssh-agent.c ssh-dss.c ssh-ecdsa.c ssh-ed25519.c + [ssh-keygen.c ssh-pkcs11-client.c ssh-pkcs11-helper.c ssh-pkcs11.c + [ssh-rsa.c sshbuf-misc.c sshbuf.h sshconnect.c sshconnect1.c + [sshconnect2.c sshd.c sshkey.c sshkey.h + [openbsd-compat/openssl-compat.c openbsd-compat/openssl-compat.h] + New key API: refactor key-related functions to be more library-like, + existing API is offered as a set of wrappers. + + with and ok markus@ + + Thanks also to Ben Hawkes, David Tomaschik, Ivan Fratric, Matthew + Dempsky and Ron Bowes for a detailed review a few months ago. + NB. This commit also removes portable OpenSSH support for OpenSSL + <0.9.8e. 20140618 - (tim) [openssh/session.c] Work around to get chroot sftp working on UnixWare diff --git a/Makefile.in b/Makefile.in index 6bca5b51e..16659c0b1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,4 +1,4 @@ -# $Id: Makefile.in,v 1.359 2014/05/21 22:23:59 djm Exp $ +# $Id: Makefile.in,v 1.360 2014/07/02 05:28:03 djm Exp $ # uncomment if you run a non bourne compatable shell. Ie. csh #SHELL = @SH@ @@ -68,7 +68,8 @@ LIBOPENSSH_OBJS=\ sshbuf.o \ sshbuf-getput-basic.o \ sshbuf-misc.o \ - sshbuf-getput-crypto.o + sshbuf-getput-crypto.o \ + sshkey.o LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ authfd.o authfile.o bufaux.o bufbn.o buffer.o \ diff --git a/auth-bsdauth.c b/auth-bsdauth.c index f4209c22a..37ff893e6 100644 --- a/auth-bsdauth.c +++ b/auth-bsdauth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-bsdauth.c,v 1.12 2014/03/12 04:50:32 djm Exp $ */ +/* $OpenBSD: auth-bsdauth.c,v 1.13 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * @@ -26,6 +26,8 @@ #include "includes.h" #include +#include +#include #include diff --git a/auth-chall.c b/auth-chall.c index 0005aa88b..cb3d522d9 100644 --- a/auth-chall.c +++ b/auth-chall.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-chall.c,v 1.13 2013/05/17 00:13:13 djm Exp $ */ +/* $OpenBSD: auth-chall.c,v 1.14 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * @@ -26,6 +26,9 @@ #include "includes.h" #include +#include +#include +#include #include diff --git a/auth-options.c b/auth-options.c index fa209eaab..9a3c270e9 100644 --- a/auth-options.c +++ b/auth-options.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.c,v 1.62 2013/12/19 00:27:57 djm Exp $ */ +/* $OpenBSD: auth-options.c,v 1.63 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -586,8 +586,8 @@ auth_cert_options(Key *k, struct passwd *pw) if (key_cert_is_legacy(k)) { /* All options are in the one field for v00 certs */ - if (parse_option_list(buffer_ptr(&k->cert->critical), - buffer_len(&k->cert->critical), pw, + if (parse_option_list(buffer_ptr(k->cert->critical), + buffer_len(k->cert->critical), pw, OPTIONS_CRITICAL|OPTIONS_EXTENSIONS, 1, &cert_no_port_forwarding_flag, &cert_no_agent_forwarding_flag, @@ -599,14 +599,14 @@ auth_cert_options(Key *k, struct passwd *pw) return -1; } else { /* Separate options and extensions for v01 certs */ - if (parse_option_list(buffer_ptr(&k->cert->critical), - buffer_len(&k->cert->critical), pw, + if (parse_option_list(buffer_ptr(k->cert->critical), + buffer_len(k->cert->critical), pw, OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL, &cert_forced_command, &cert_source_address_done) == -1) return -1; - if (parse_option_list(buffer_ptr(&k->cert->extensions), - buffer_len(&k->cert->extensions), pw, + if (parse_option_list(buffer_ptr(k->cert->extensions), + buffer_len(k->cert->extensions), pw, OPTIONS_EXTENSIONS, 1, &cert_no_port_forwarding_flag, &cert_no_agent_forwarding_flag, diff --git a/auth-rsa.c b/auth-rsa.c index 5dad6c3dc..1bddfa02f 100644 --- a/auth-rsa.c +++ b/auth-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rsa.c,v 1.86 2014/01/27 19:18:54 markus Exp $ */ +/* $OpenBSD: auth-rsa.c,v 1.87 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -144,7 +144,8 @@ auth_rsa_challenge_dialog(Key *key) challenge = PRIVSEP(auth_rsa_generate_challenge(key)); /* Encrypt the challenge with the public key. */ - rsa_public_encrypt(encrypted_challenge, challenge, key->rsa); + if (rsa_public_encrypt(encrypted_challenge, challenge, key->rsa) != 0) + fatal("%s: rsa_public_encrypt failed", __func__); /* Send the encrypted challenge to the client. */ packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); diff --git a/auth2-none.c b/auth2-none.c index c8c6c74a9..5501b9d64 100644 --- a/auth2-none.c +++ b/auth2-none.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-none.c,v 1.16 2010/06/25 08:46:17 djm Exp $ */ +/* $OpenBSD: auth2-none.c,v 1.17 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -30,9 +30,10 @@ #include #include -#include #include #include +#include +#include #include "atomicio.h" #include "xmalloc.h" diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 0fd27bb92..b2fd07a61 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.39 2013/12/30 23:52:27 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.40 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -230,7 +230,7 @@ pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...) } static int -match_principals_option(const char *principal_list, struct KeyCert *cert) +match_principals_option(const char *principal_list, struct sshkey_cert *cert) { char *result; u_int i; @@ -250,7 +250,7 @@ match_principals_option(const char *principal_list, struct KeyCert *cert) } static int -match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert) +match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) { FILE *f; char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts; diff --git a/authfile.c b/authfile.c index 7cb901133..e93d86738 100644 --- a/authfile.c +++ b/authfile.c @@ -1,18 +1,5 @@ -/* $OpenBSD: authfile.c,v 1.106 2014/04/29 18:01:49 markus Exp $ */ +/* $OpenBSD: authfile.c,v 1.107 2014/06/24 01:13:21 djm Exp $ */ /* - * Author: Tatu Ylonen - * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - * All rights reserved - * This file contains functions for reading and writing identity files, and - * for reading the passphrase from the user. - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - * - * * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,32 +30,15 @@ #include #include -#ifdef WITH_OPENSSL -#include -#include -#include -#endif - -/* compatibility with old or broken OpenSSL versions */ -#include "openbsd-compat/openssl-compat.h" - -#include "crypto_api.h" - #include #include -#include #include +#include #include #include #include -#ifdef HAVE_UTIL_H -#include -#endif - -#include "xmalloc.h" #include "cipher.h" -#include "buffer.h" #include "key.h" #include "ssh.h" #include "log.h" @@ -76,667 +46,92 @@ #include "rsa.h" #include "misc.h" #include "atomicio.h" -#include "uuencode.h" - -/* openssh private key file format */ -#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n" -#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n" -#define KDFNAME "bcrypt" -#define AUTH_MAGIC "openssh-key-v1" -#define SALT_LEN 16 -#define DEFAULT_CIPHERNAME "aes256-cbc" -#define DEFAULT_ROUNDS 16 +#include "sshbuf.h" +#include "ssherr.h" #define MAX_KEY_FILE_SIZE (1024 * 1024) -/* Version identification string for SSH v1 identity files. */ -static const char authfile_id_string[] = - "SSH PRIVATE KEY FILE FORMAT 1.1\n"; - -static int -key_private_to_blob2(Key *prv, Buffer *blob, const char *passphrase, - const char *comment, const char *ciphername, int rounds) -{ - u_char *key, *cp, salt[SALT_LEN]; - size_t keylen, ivlen, blocksize, authlen; - u_int len, check; - int i, n; - const Cipher *c; - Buffer encoded, b, kdf; - CipherContext ctx; - const char *kdfname = KDFNAME; - - if (rounds <= 0) - rounds = DEFAULT_ROUNDS; - if (passphrase == NULL || !strlen(passphrase)) { - ciphername = "none"; - kdfname = "none"; - } else if (ciphername == NULL) - ciphername = DEFAULT_CIPHERNAME; - else if (cipher_number(ciphername) != SSH_CIPHER_SSH2) - fatal("invalid cipher"); - - if ((c = cipher_by_name(ciphername)) == NULL) - fatal("unknown cipher name"); - buffer_init(&kdf); - blocksize = cipher_blocksize(c); - keylen = cipher_keylen(c); - ivlen = cipher_ivlen(c); - authlen = cipher_authlen(c); - key = xcalloc(1, keylen + ivlen); - if (strcmp(kdfname, "none") != 0) { - arc4random_buf(salt, SALT_LEN); - if (bcrypt_pbkdf(passphrase, strlen(passphrase), - salt, SALT_LEN, key, keylen + ivlen, rounds) < 0) - fatal("bcrypt_pbkdf failed"); - buffer_put_string(&kdf, salt, SALT_LEN); - buffer_put_int(&kdf, rounds); - } - cipher_init(&ctx, c, key, keylen, key + keylen , ivlen, 1); - explicit_bzero(key, keylen + ivlen); - free(key); - - buffer_init(&encoded); - buffer_append(&encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC)); - buffer_put_cstring(&encoded, ciphername); - buffer_put_cstring(&encoded, kdfname); - buffer_put_string(&encoded, buffer_ptr(&kdf), buffer_len(&kdf)); - buffer_put_int(&encoded, 1); /* number of keys */ - key_to_blob(prv, &cp, &len); /* public key */ - buffer_put_string(&encoded, cp, len); - - explicit_bzero(cp, len); - free(cp); - - buffer_free(&kdf); - - /* set up the buffer that will be encrypted */ - buffer_init(&b); - - /* Random check bytes */ - check = arc4random(); - buffer_put_int(&b, check); - buffer_put_int(&b, check); - - /* append private key and comment*/ - key_private_serialize(prv, &b); - buffer_put_cstring(&b, comment); - - /* padding */ - i = 0; - while (buffer_len(&b) % blocksize) - buffer_put_char(&b, ++i & 0xff); - - /* length */ - buffer_put_int(&encoded, buffer_len(&b)); - - /* encrypt */ - cp = buffer_append_space(&encoded, buffer_len(&b) + authlen); - if (cipher_crypt(&ctx, 0, cp, buffer_ptr(&b), buffer_len(&b), 0, - authlen) != 0) - fatal("%s: cipher_crypt failed", __func__); - buffer_free(&b); - cipher_cleanup(&ctx); - - /* uuencode */ - len = 2 * buffer_len(&encoded); - cp = xmalloc(len); - n = uuencode(buffer_ptr(&encoded), buffer_len(&encoded), - (char *)cp, len); - if (n < 0) - fatal("%s: uuencode", __func__); - - buffer_clear(blob); - buffer_append(blob, MARK_BEGIN, sizeof(MARK_BEGIN) - 1); - for (i = 0; i < n; i++) { - buffer_put_char(blob, cp[i]); - if (i % 70 == 69) - buffer_put_char(blob, '\n'); - } - if (i % 70 != 69) - buffer_put_char(blob, '\n'); - buffer_append(blob, MARK_END, sizeof(MARK_END) - 1); - free(cp); - - return buffer_len(blob); -} - -static Key * -key_parse_private2(Buffer *blob, int type, const char *passphrase, - char **commentp) -{ - u_char *key = NULL, *cp, *salt = NULL, pad, last; - char *comment = NULL, *ciphername = NULL, *kdfname = NULL; - const u_char *kdfp; - u_int keylen = 0, ivlen, blocksize, slen, klen, len, rounds, nkeys; - u_int check1, check2, m1len, m2len; - size_t authlen; - const Cipher *c; - Buffer b, encoded, copy, kdf; - CipherContext ctx; - Key *k = NULL; - int dlen, ret, i; - - buffer_init(&b); - buffer_init(&kdf); - buffer_init(&encoded); - buffer_init(©); - - /* uudecode */ - m1len = sizeof(MARK_BEGIN) - 1; - m2len = sizeof(MARK_END) - 1; - cp = buffer_ptr(blob); - len = buffer_len(blob); - if (len < m1len || memcmp(cp, MARK_BEGIN, m1len)) { - debug("%s: missing begin marker", __func__); - goto out; - } - cp += m1len; - len -= m1len; - while (len) { - if (*cp != '\n' && *cp != '\r') - buffer_put_char(&encoded, *cp); - last = *cp; - len--; - cp++; - if (last == '\n') { - if (len >= m2len && !memcmp(cp, MARK_END, m2len)) { - buffer_put_char(&encoded, '\0'); - break; - } - } - } - if (!len) { - debug("%s: no end marker", __func__); - goto out; - } - len = buffer_len(&encoded); - if ((cp = buffer_append_space(©, len)) == NULL) { - error("%s: buffer_append_space", __func__); - goto out; - } - if ((dlen = uudecode(buffer_ptr(&encoded), cp, len)) < 0) { - error("%s: uudecode failed", __func__); - goto out; - } - if ((u_int)dlen > len) { - error("%s: crazy uudecode length %d > %u", __func__, dlen, len); - goto out; - } - buffer_consume_end(©, len - dlen); - if (buffer_len(©) < sizeof(AUTH_MAGIC) || - memcmp(buffer_ptr(©), AUTH_MAGIC, sizeof(AUTH_MAGIC))) { - error("%s: bad magic", __func__); - goto out; - } - buffer_consume(©, sizeof(AUTH_MAGIC)); - - ciphername = buffer_get_cstring_ret(©, NULL); - if (ciphername == NULL || - (c = cipher_by_name(ciphername)) == NULL) { - error("%s: unknown cipher name", __func__); - goto out; - } - if ((passphrase == NULL || !strlen(passphrase)) && - strcmp(ciphername, "none") != 0) { - /* passphrase required */ - goto out; - } - kdfname = buffer_get_cstring_ret(©, NULL); - if (kdfname == NULL || - (strcmp(kdfname, "none") != 0 && strcmp(kdfname, "bcrypt") != 0)) { - error("%s: unknown kdf name", __func__); - goto out; - } - if (!strcmp(kdfname, "none") && strcmp(ciphername, "none") != 0) { - error("%s: cipher %s requires kdf", __func__, ciphername); - goto out; - } - /* kdf options */ - kdfp = buffer_get_string_ptr_ret(©, &klen); - if (kdfp == NULL) { - error("%s: kdf options not set", __func__); - goto out; - } - if (klen > 0) { - if ((cp = buffer_append_space(&kdf, klen)) == NULL) { - error("%s: kdf alloc failed", __func__); - goto out; - } - memcpy(cp, kdfp, klen); - } - /* number of keys */ - if (buffer_get_int_ret(&nkeys, ©) < 0) { - error("%s: key counter missing", __func__); - goto out; - } - if (nkeys != 1) { - error("%s: only one key supported", __func__); - goto out; - } - /* pubkey */ - if ((cp = buffer_get_string_ret(©, &len)) == NULL) { - error("%s: pubkey not found", __func__); - goto out; - } - free(cp); /* XXX check pubkey against decrypted private key */ - - /* size of encrypted key blob */ - len = buffer_get_int(©); - blocksize = cipher_blocksize(c); - authlen = cipher_authlen(c); - if (len < blocksize) { - error("%s: encrypted data too small", __func__); - goto out; - } - if (len % blocksize) { - error("%s: length not multiple of blocksize", __func__); - goto out; - } - - /* setup key */ - keylen = cipher_keylen(c); - ivlen = cipher_ivlen(c); - key = xcalloc(1, keylen + ivlen); - if (!strcmp(kdfname, "bcrypt")) { - if ((salt = buffer_get_string_ret(&kdf, &slen)) == NULL) { - error("%s: salt not set", __func__); - goto out; - } - if (buffer_get_int_ret(&rounds, &kdf) < 0) { - error("%s: rounds not set", __func__); - goto out; - } - if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen, - key, keylen + ivlen, rounds) < 0) { - error("%s: bcrypt_pbkdf failed", __func__); - goto out; - } - } - - cp = buffer_append_space(&b, len); - cipher_init(&ctx, c, key, keylen, key + keylen, ivlen, 0); - ret = cipher_crypt(&ctx, 0, cp, buffer_ptr(©), len, 0, authlen); - cipher_cleanup(&ctx); - buffer_consume(©, len); - - /* fail silently on decryption errors */ - if (ret != 0) { - debug("%s: decrypt failed", __func__); - goto out; - } - - if (buffer_len(©) != 0) { - error("%s: key blob has trailing data (len = %u)", __func__, - buffer_len(©)); - goto out; - } - - /* check bytes */ - if (buffer_get_int_ret(&check1, &b) < 0 || - buffer_get_int_ret(&check2, &b) < 0) { - error("check bytes missing"); - goto out; - } - if (check1 != check2) { - debug("%s: decrypt failed: 0x%08x != 0x%08x", __func__, - check1, check2); - goto out; - } - - k = key_private_deserialize(&b); - - /* comment */ - comment = buffer_get_cstring_ret(&b, NULL); - - i = 0; - while (buffer_len(&b)) { - if (buffer_get_char_ret(&pad, &b) == -1 || - pad != (++i & 0xff)) { - error("%s: bad padding", __func__); - key_free(k); - k = NULL; - goto out; - } - } - - if (k && commentp) { - *commentp = comment; - comment = NULL; - } - - /* XXX decode pubkey and check against private */ - out: - free(ciphername); - free(kdfname); - free(salt); - free(comment); - if (key) - explicit_bzero(key, keylen + ivlen); - free(key); - buffer_free(&encoded); - buffer_free(©); - buffer_free(&kdf); - buffer_free(&b); - return k; -} - -#ifdef WITH_SSH1 -/* - * Serialises the authentication (private) key to a blob, encrypting it with - * passphrase. The identification of the blob (lowest 64 bits of n) will - * precede the key to provide identification of the key without needing a - * passphrase. - */ -static int -key_private_rsa1_to_blob(Key *key, Buffer *blob, const char *passphrase, - const char *comment) -{ - Buffer buffer, encrypted; - u_char buf[100], *cp; - int i, cipher_num; - CipherContext ciphercontext; - const Cipher *cipher; - u_int32_t rnd; - - /* - * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting - * to another cipher; otherwise use SSH_AUTHFILE_CIPHER. - */ - cipher_num = (strcmp(passphrase, "") == 0) ? - SSH_CIPHER_NONE : SSH_AUTHFILE_CIPHER; - if ((cipher = cipher_by_number(cipher_num)) == NULL) - fatal("save_private_key_rsa: bad cipher"); - - /* This buffer is used to built the secret part of the private key. */ - buffer_init(&buffer); - - /* Put checkbytes for checking passphrase validity. */ - rnd = arc4random(); - buf[0] = rnd & 0xff; - buf[1] = (rnd >> 8) & 0xff; - buf[2] = buf[0]; - buf[3] = buf[1]; - buffer_append(&buffer, buf, 4); - - /* - * Store the private key (n and e will not be stored because they - * will be stored in plain text, and storing them also in encrypted - * format would just give known plaintext). - */ - buffer_put_bignum(&buffer, key->rsa->d); - buffer_put_bignum(&buffer, key->rsa->iqmp); - buffer_put_bignum(&buffer, key->rsa->q); /* reverse from SSL p */ - buffer_put_bignum(&buffer, key->rsa->p); /* reverse from SSL q */ - - /* Pad the part to be encrypted until its size is a multiple of 8. */ - while (buffer_len(&buffer) % 8 != 0) - buffer_put_char(&buffer, 0); - - /* This buffer will be used to contain the data in the file. */ - buffer_init(&encrypted); - - /* First store keyfile id string. */ - for (i = 0; authfile_id_string[i]; i++) - buffer_put_char(&encrypted, authfile_id_string[i]); - buffer_put_char(&encrypted, 0); - - /* Store cipher type. */ - buffer_put_char(&encrypted, cipher_num); - buffer_put_int(&encrypted, 0); /* For future extension */ - - /* Store public key. This will be in plain text. */ - buffer_put_int(&encrypted, BN_num_bits(key->rsa->n)); - buffer_put_bignum(&encrypted, key->rsa->n); - buffer_put_bignum(&encrypted, key->rsa->e); - buffer_put_cstring(&encrypted, comment); - - /* Allocate space for the private part of the key in the buffer. */ - cp = buffer_append_space(&encrypted, buffer_len(&buffer)); - - cipher_set_key_string(&ciphercontext, cipher, passphrase, - CIPHER_ENCRYPT); - if (cipher_crypt(&ciphercontext, 0, cp, - buffer_ptr(&buffer), buffer_len(&buffer), 0, 0) != 0) - fatal("%s: cipher_crypt failed", __func__); - cipher_cleanup(&ciphercontext); - explicit_bzero(&ciphercontext, sizeof(ciphercontext)); - - /* Destroy temporary data. */ - explicit_bzero(buf, sizeof(buf)); - buffer_free(&buffer); - - buffer_append(blob, buffer_ptr(&encrypted), buffer_len(&encrypted)); - buffer_free(&encrypted); - - return 1; -} -#endif - -#ifdef WITH_OPENSSL -/* convert SSH v2 key in OpenSSL PEM format */ -static int -key_private_pem_to_blob(Key *key, Buffer *blob, const char *_passphrase, - const char *comment) -{ - int success = 0; - int blen, len = strlen(_passphrase); - u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL; -#if (OPENSSL_VERSION_NUMBER < 0x00907000L) - const EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL; -#else - const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; -#endif - const u_char *bptr; - BIO *bio; - - if (len > 0 && len <= 4) { - error("passphrase too short: have %d bytes, need > 4", len); - return 0; - } - if ((bio = BIO_new(BIO_s_mem())) == NULL) { - error("%s: BIO_new failed", __func__); - return 0; - } - switch (key->type) { - case KEY_DSA: - success = PEM_write_bio_DSAPrivateKey(bio, key->dsa, - cipher, passphrase, len, NULL, NULL); - break; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: - success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa, - cipher, passphrase, len, NULL, NULL); - break; -#endif - case KEY_RSA: - success = PEM_write_bio_RSAPrivateKey(bio, key->rsa, - cipher, passphrase, len, NULL, NULL); - break; - } - if (success) { - if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) - success = 0; - else - buffer_append(blob, bptr, blen); - } - BIO_free(bio); - return success; -} -#endif - /* Save a key blob to a file */ static int -key_save_private_blob(Buffer *keybuf, const char *filename) +sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename) { - int fd; + int fd, oerrno; - if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) { - error("open %s failed: %s.", filename, strerror(errno)); - return 0; - } - if (atomicio(vwrite, fd, buffer_ptr(keybuf), - buffer_len(keybuf)) != buffer_len(keybuf)) { - error("write to key file %s failed: %s", filename, - strerror(errno)); + if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) + return SSH_ERR_SYSTEM_ERROR; + if (atomicio(vwrite, fd, (u_char *)sshbuf_ptr(keybuf), + sshbuf_len(keybuf)) != sshbuf_len(keybuf)) { + oerrno = errno; close(fd); unlink(filename); - return 0; + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; } close(fd); - return 1; -} - -/* Serialise "key" to buffer "blob" */ -static int -key_private_to_blob(Key *key, Buffer *blob, const char *passphrase, - const char *comment, int force_new_format, const char *new_format_cipher, - int new_format_rounds) -{ - switch (key->type) { -#ifdef WITH_SSH1 - case KEY_RSA1: - return key_private_rsa1_to_blob(key, blob, passphrase, comment); -#endif -#ifdef WITH_OPENSSL - case KEY_DSA: - case KEY_ECDSA: - case KEY_RSA: - if (force_new_format) { - return key_private_to_blob2(key, blob, passphrase, - comment, new_format_cipher, new_format_rounds); - } - return key_private_pem_to_blob(key, blob, passphrase, comment); -#endif - case KEY_ED25519: - return key_private_to_blob2(key, blob, passphrase, - comment, new_format_cipher, new_format_rounds); - default: - error("%s: cannot save key type %d", __func__, key->type); - return 0; - } + return 0; } int -key_save_private(Key *key, const char *filename, const char *passphrase, - const char *comment, int force_new_format, const char *new_format_cipher, - int new_format_rounds) +sshkey_save_private(struct sshkey *key, const char *filename, + const char *passphrase, const char *comment, + int force_new_format, const char *new_format_cipher, int new_format_rounds) { - Buffer keyblob; - int success = 0; + struct sshbuf *keyblob = NULL; + int r; - buffer_init(&keyblob); - if (!key_private_to_blob(key, &keyblob, passphrase, comment, - force_new_format, new_format_cipher, new_format_rounds)) + if ((keyblob = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment, + force_new_format, new_format_cipher, new_format_rounds)) != 0) goto out; - if (!key_save_private_blob(&keyblob, filename)) + if ((r = sshkey_save_private_blob(keyblob, filename)) != 0) goto out; - success = 1; + r = 0; out: - buffer_free(&keyblob); - return success; -} - -#ifdef WITH_SSH1 -/* - * Parse the public, unencrypted portion of a RSA1 key. - */ -static Key * -key_parse_public_rsa1(Buffer *blob, char **commentp) -{ - Key *pub; - Buffer copy; - - /* Check that it is at least big enough to contain the ID string. */ - if (buffer_len(blob) < sizeof(authfile_id_string)) { - debug3("Truncated RSA1 identifier"); - return NULL; - } - - /* - * Make sure it begins with the id string. Consume the id string - * from the buffer. - */ - if (memcmp(buffer_ptr(blob), authfile_id_string, - sizeof(authfile_id_string)) != 0) { - debug3("Incorrect RSA1 identifier"); - return NULL; - } - buffer_init(©); - buffer_append(©, buffer_ptr(blob), buffer_len(blob)); - buffer_consume(©, sizeof(authfile_id_string)); - - /* Skip cipher type and reserved data. */ - (void) buffer_get_char(©); /* cipher type */ - (void) buffer_get_int(©); /* reserved */ - - /* Read the public key from the buffer. */ - (void) buffer_get_int(©); - pub = key_new(KEY_RSA1); - buffer_get_bignum(©, pub->rsa->n); - buffer_get_bignum(©, pub->rsa->e); - if (commentp) - *commentp = buffer_get_string(©, NULL); - /* The encrypted private part is not parsed by this function. */ - buffer_free(©); - - return pub; + sshbuf_free(keyblob); + return r; } -#endif /* Load a key from a fd into a buffer */ int -key_load_file(int fd, const char *filename, Buffer *blob) +sshkey_load_file(int fd, const char *filename, struct sshbuf *blob) { u_char buf[1024]; size_t len; struct stat st; + int r; - if (fstat(fd, &st) < 0) { - error("%s: fstat of key file %.200s%sfailed: %.100s", __func__, - filename == NULL ? "" : filename, - filename == NULL ? "" : " ", - strerror(errno)); - return 0; - } + if (fstat(fd, &st) < 0) + return SSH_ERR_SYSTEM_ERROR; if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && - st.st_size > MAX_KEY_FILE_SIZE) { - toobig: - error("%s: key file %.200s%stoo large", __func__, - filename == NULL ? "" : filename, - filename == NULL ? "" : " "); - return 0; - } - buffer_clear(blob); + st.st_size > MAX_KEY_FILE_SIZE) + return SSH_ERR_INVALID_FORMAT; for (;;) { if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) { if (errno == EPIPE) break; - debug("%s: read from key file %.200s%sfailed: %.100s", - __func__, filename == NULL ? "" : filename, - filename == NULL ? "" : " ", strerror(errno)); - buffer_clear(blob); - explicit_bzero(buf, sizeof(buf)); - return 0; + r = SSH_ERR_SYSTEM_ERROR; + goto out; } - buffer_append(blob, buf, len); - if (buffer_len(blob) > MAX_KEY_FILE_SIZE) { - buffer_clear(blob); - explicit_bzero(buf, sizeof(buf)); - goto toobig; + if ((r = sshbuf_put(blob, buf, len)) != 0) + goto out; + if (sshbuf_len(blob) > MAX_KEY_FILE_SIZE) { + r = SSH_ERR_INVALID_FORMAT; + goto out; } } - explicit_bzero(buf, sizeof(buf)); if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && - st.st_size != buffer_len(blob)) { - debug("%s: key file %.200s%schanged size while reading", - __func__, filename == NULL ? "" : filename, - filename == NULL ? "" : " "); - buffer_clear(blob); - return 0; + st.st_size != (off_t)sshbuf_len(blob)) { + r = SSH_ERR_FILE_CHANGED; + goto out; } + r = 0; - return 1; + out: + explicit_bzero(buf, sizeof(buf)); + if (r != 0) + sshbuf_reset(blob); + return r; } #ifdef WITH_SSH1 @@ -745,249 +140,65 @@ key_load_file(int fd, const char *filename, Buffer *blob) * encountered (the file does not exist or is not readable), and the key * otherwise. */ -static Key * -key_load_public_rsa1(int fd, const char *filename, char **commentp) -{ - Buffer buffer; - Key *pub; - - buffer_init(&buffer); - if (!key_load_file(fd, filename, &buffer)) { - buffer_free(&buffer); - return NULL; - } - - pub = key_parse_public_rsa1(&buffer, commentp); - if (pub == NULL) - debug3("Could not load \"%s\" as a RSA1 public key", filename); - buffer_free(&buffer); - return pub; -} - -/* load public key from private-key file, works only for SSH v1 */ -Key * -key_load_public_type(int type, const char *filename, char **commentp) -{ - Key *pub; - int fd; - - if (type == KEY_RSA1) { - fd = open(filename, O_RDONLY); - if (fd < 0) - return NULL; - pub = key_load_public_rsa1(fd, filename, commentp); - close(fd); - return pub; - } - return NULL; -} - -static Key * -key_parse_private_rsa1(Buffer *blob, const char *passphrase, char **commentp) +static int +sshkey_load_public_rsa1(int fd, const char *filename, + struct sshkey **keyp, char **commentp) { - int check1, check2, cipher_type; - Buffer decrypted; - u_char *cp; - CipherContext ciphercontext; - const Cipher *cipher; - Key *prv = NULL; - Buffer copy; - - /* Check that it is at least big enough to contain the ID string. */ - if (buffer_len(blob) < sizeof(authfile_id_string)) { - debug3("Truncated RSA1 identifier"); - return NULL; - } - - /* - * Make sure it begins with the id string. Consume the id string - * from the buffer. - */ - if (memcmp(buffer_ptr(blob), authfile_id_string, - sizeof(authfile_id_string)) != 0) { - debug3("Incorrect RSA1 identifier"); - return NULL; - } - buffer_init(©); - buffer_append(©, buffer_ptr(blob), buffer_len(blob)); - buffer_consume(©, sizeof(authfile_id_string)); - - /* Read cipher type. */ - cipher_type = buffer_get_char(©); - (void) buffer_get_int(©); /* Reserved data. */ - - /* Read the public key from the buffer. */ - (void) buffer_get_int(©); - prv = key_new_private(KEY_RSA1); - - buffer_get_bignum(©, prv->rsa->n); - buffer_get_bignum(©, prv->rsa->e); - if (commentp) - *commentp = buffer_get_string(©, NULL); - else - (void)buffer_get_string_ptr(©, NULL); - - /* Check that it is a supported cipher. */ - cipher = cipher_by_number(cipher_type); - if (cipher == NULL) { - debug("Unsupported RSA1 cipher %d", cipher_type); - buffer_free(©); - goto fail; - } - /* Initialize space for decrypted data. */ - buffer_init(&decrypted); - cp = buffer_append_space(&decrypted, buffer_len(©)); - - /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ - cipher_set_key_string(&ciphercontext, cipher, passphrase, - CIPHER_DECRYPT); - if (cipher_crypt(&ciphercontext, 0, cp, - buffer_ptr(©), buffer_len(©), 0, 0) != 0) - fatal("%s: cipher_crypt failed", __func__); - cipher_cleanup(&ciphercontext); - explicit_bzero(&ciphercontext, sizeof(ciphercontext)); - buffer_free(©); - - check1 = buffer_get_char(&decrypted); - check2 = buffer_get_char(&decrypted); - if (check1 != buffer_get_char(&decrypted) || - check2 != buffer_get_char(&decrypted)) { - if (strcmp(passphrase, "") != 0) - debug("Bad passphrase supplied for RSA1 key"); - /* Bad passphrase. */ - buffer_free(&decrypted); - goto fail; - } - /* Read the rest of the private key. */ - buffer_get_bignum(&decrypted, prv->rsa->d); - buffer_get_bignum(&decrypted, prv->rsa->iqmp); /* u */ - /* in SSL and SSH v1 p and q are exchanged */ - buffer_get_bignum(&decrypted, prv->rsa->q); /* p */ - buffer_get_bignum(&decrypted, prv->rsa->p); /* q */ - - /* calculate p-1 and q-1 */ - rsa_generate_additional_parameters(prv->rsa); - - buffer_free(&decrypted); + struct sshbuf *b = NULL; + int r; - /* enable blinding */ - if (RSA_blinding_on(prv->rsa, NULL) != 1) { - error("%s: RSA_blinding_on failed", __func__); - goto fail; - } - return prv; - -fail: + *keyp = NULL; if (commentp != NULL) - free(*commentp); - key_free(prv); - return NULL; + *commentp = NULL; + + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_load_file(fd, filename, b)) != 0) + goto out; + if ((r = sshkey_parse_public_rsa1_fileblob(b, keyp, commentp)) != 0) + goto out; + r = 0; + out: + sshbuf_free(b); + return r; } -#endif +#endif /* WITH_SSH1 */ #ifdef WITH_OPENSSL -static Key * -key_parse_private_pem(Buffer *blob, int type, const char *passphrase, - char **commentp) +/* XXX Deprecate? */ +int +sshkey_load_private_pem(int fd, int type, const char *passphrase, + struct sshkey **keyp, char **commentp) { - EVP_PKEY *pk = NULL; - Key *prv = NULL; - char *name = ""; - BIO *bio; - - if ((bio = BIO_new_mem_buf(buffer_ptr(blob), - buffer_len(blob))) == NULL) { - error("%s: BIO_new_mem_buf failed", __func__); - return NULL; - } - - pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, (char *)passphrase); - BIO_free(bio); - if (pk == NULL) { - debug("%s: PEM_read_PrivateKey failed", __func__); - (void)ERR_get_error(); - } else if (pk->type == EVP_PKEY_RSA && - (type == KEY_UNSPEC||type==KEY_RSA)) { - prv = key_new(KEY_UNSPEC); - prv->rsa = EVP_PKEY_get1_RSA(pk); - prv->type = KEY_RSA; - name = "rsa w/o comment"; -#ifdef DEBUG_PK - RSA_print_fp(stderr, prv->rsa, 8); -#endif - if (RSA_blinding_on(prv->rsa, NULL) != 1) { - error("%s: RSA_blinding_on failed", __func__); - key_free(prv); - prv = NULL; - } - } else if (pk->type == EVP_PKEY_DSA && - (type == KEY_UNSPEC||type==KEY_DSA)) { - prv = key_new(KEY_UNSPEC); - prv->dsa = EVP_PKEY_get1_DSA(pk); - prv->type = KEY_DSA; - name = "dsa w/o comment"; -#ifdef DEBUG_PK - DSA_print_fp(stderr, prv->dsa, 8); -#endif -#ifdef OPENSSL_HAS_ECC - } else if (pk->type == EVP_PKEY_EC && - (type == KEY_UNSPEC||type==KEY_ECDSA)) { - prv = key_new(KEY_UNSPEC); - prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk); - prv->type = KEY_ECDSA; - if ((prv->ecdsa_nid = key_ecdsa_key_to_nid(prv->ecdsa)) == -1 || - key_curve_nid_to_name(prv->ecdsa_nid) == NULL || - key_ec_validate_public(EC_KEY_get0_group(prv->ecdsa), - EC_KEY_get0_public_key(prv->ecdsa)) != 0 || - key_ec_validate_private(prv->ecdsa) != 0) { - error("%s: bad ECDSA key", __func__); - key_free(prv); - prv = NULL; - } - name = "ecdsa w/o comment"; -#ifdef DEBUG_PK - if (prv != NULL && prv->ecdsa != NULL) - key_dump_ec_key(prv->ecdsa); -#endif -#endif /* OPENSSL_HAS_ECC */ - } else { - error("%s: PEM_read_PrivateKey: mismatch or " - "unknown EVP_PKEY save_type %d", __func__, pk->save_type); - } - if (pk != NULL) - EVP_PKEY_free(pk); - if (prv != NULL && commentp) - *commentp = xstrdup(name); - debug("read PEM private key done: type %s", - prv ? key_type(prv) : ""); - return prv; -} + struct sshbuf *buffer = NULL; + int r; -Key * -key_load_private_pem(int fd, int type, const char *passphrase, - char **commentp) -{ - Buffer buffer; - Key *prv; + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; - buffer_init(&buffer); - if (!key_load_file(fd, NULL, &buffer)) { - buffer_free(&buffer); - return NULL; - } - prv = key_parse_private_pem(&buffer, type, passphrase, commentp); - buffer_free(&buffer); - return prv; + if ((buffer = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_load_file(fd, NULL, buffer)) != 0) + goto out; + if ((r = sshkey_parse_private_pem_fileblob(buffer, type, passphrase, + keyp, commentp)) != 0) + goto out; + r = 0; + out: + sshbuf_free(buffer); + return r; } -#endif +#endif /* WITH_OPENSSL */ +/* XXX remove error() calls from here? */ int -key_perm_ok(int fd, const char *filename) +sshkey_perm_ok(int fd, const char *filename) { struct stat st; if (fstat(fd, &st) < 0) - return 0; + return SSH_ERR_SYSTEM_ERROR; /* * if a key owned by the user is accessed, then we check the * permissions of the file. if the key owned by a different user, @@ -1002,313 +213,311 @@ key_perm_ok(int fd, const char *filename) error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("Permissions 0%3.3o for '%s' are too open.", (u_int)st.st_mode & 0777, filename); - error("It is required that your private key files are NOT accessible by others."); + error("It is recommended that your private key files are NOT accessible by others."); error("This private key will be ignored."); - return 0; + return SSH_ERR_KEY_BAD_PERMISSIONS; } - return 1; + return 0; } -static Key * -key_parse_private_type(Buffer *blob, int type, const char *passphrase, - char **commentp) +/* XXX kill perm_ok now that we have SSH_ERR_KEY_BAD_PERMISSIONS? */ +int +sshkey_load_private_type(int type, const char *filename, const char *passphrase, + struct sshkey **keyp, char **commentp, int *perm_ok) { - Key *k; + int fd, r; + struct sshbuf *buffer = NULL; - switch (type) { -#ifdef WITH_SSH1 - case KEY_RSA1: - return key_parse_private_rsa1(blob, passphrase, commentp); -#endif -#ifdef WITH_OPENSSL - case KEY_DSA: - case KEY_ECDSA: - case KEY_RSA: - return key_parse_private_pem(blob, type, passphrase, commentp); -#endif - case KEY_ED25519: - return key_parse_private2(blob, type, passphrase, commentp); - case KEY_UNSPEC: - if ((k = key_parse_private2(blob, type, passphrase, commentp))) - return k; -#ifdef WITH_OPENSSL - return key_parse_private_pem(blob, type, passphrase, commentp); -#endif - default: - error("%s: cannot parse key type %d", __func__, type); - break; - } - return NULL; -} - -Key * -key_load_private_type(int type, const char *filename, const char *passphrase, - char **commentp, int *perm_ok) -{ - int fd; - Key *ret; - Buffer buffer; + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; - fd = open(filename, O_RDONLY); - if (fd < 0) { - debug("could not open key file '%s': %s", filename, - strerror(errno)); + if ((fd = open(filename, O_RDONLY)) < 0) { if (perm_ok != NULL) *perm_ok = 0; - return NULL; + return SSH_ERR_SYSTEM_ERROR; } - if (!key_perm_ok(fd, filename)) { + if (sshkey_perm_ok(fd, filename) != 0) { if (perm_ok != NULL) *perm_ok = 0; - error("bad permissions: ignore key: %s", filename); - close(fd); - return NULL; + r = SSH_ERR_KEY_BAD_PERMISSIONS; + goto out; } if (perm_ok != NULL) *perm_ok = 1; - buffer_init(&buffer); - if (!key_load_file(fd, filename, &buffer)) { - buffer_free(&buffer); - close(fd); - return NULL; + if ((buffer = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; } + if ((r = sshkey_load_file(fd, filename, buffer)) != 0) + goto out; + if ((r = sshkey_parse_private_fileblob_type(buffer, type, passphrase, + keyp, commentp)) != 0) + goto out; + r = 0; + out: close(fd); - ret = key_parse_private_type(&buffer, type, passphrase, commentp); - buffer_free(&buffer); - return ret; + if (buffer != NULL) + sshbuf_free(buffer); + return r; } -Key * -key_parse_private(Buffer *buffer, const char *filename, - const char *passphrase, char **commentp) +/* XXX this is almost identical to sshkey_load_private_type() */ +int +sshkey_load_private(const char *filename, const char *passphrase, + struct sshkey **keyp, char **commentp) { -#ifdef WITH_SSH1 - Key *pub, *prv; + struct sshbuf *buffer = NULL; + int r, fd; - /* it's a SSH v1 key if the public key part is readable */ - pub = key_parse_public_rsa1(buffer, commentp); - if (pub == NULL) { - prv = key_parse_private_type(buffer, KEY_UNSPEC, - passphrase, NULL); - /* use the filename as a comment for PEM */ - if (commentp && prv) - *commentp = xstrdup(filename); - } else { - key_free(pub); - /* key_parse_public_rsa1() has already loaded the comment */ - prv = key_parse_private_type(buffer, KEY_RSA1, passphrase, - NULL); - } - return prv; -#else - return key_parse_private_type(buffer, KEY_UNSPEC, - passphrase, commentp); -#endif -} - -Key * -key_load_private(const char *filename, const char *passphrase, - char **commentp) -{ - Key *prv; - Buffer buffer; - int fd; + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; - fd = open(filename, O_RDONLY); - if (fd < 0) { - debug("could not open key file '%s': %s", filename, - strerror(errno)); - return NULL; - } - if (!key_perm_ok(fd, filename)) { - error("bad permissions: ignore key: %s", filename); - close(fd); - return NULL; + if ((fd = open(filename, O_RDONLY)) < 0) + return SSH_ERR_SYSTEM_ERROR; + if (sshkey_perm_ok(fd, filename) != 0) { + r = SSH_ERR_KEY_BAD_PERMISSIONS; + goto out; } - buffer_init(&buffer); - if (!key_load_file(fd, filename, &buffer)) { - buffer_free(&buffer); - close(fd); - return NULL; + if ((buffer = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; } + if ((r = sshkey_load_file(fd, filename, buffer)) != 0 || + (r = sshkey_parse_private_fileblob(buffer, passphrase, filename, + keyp, commentp)) != 0) + goto out; + r = 0; + out: close(fd); - - prv = key_parse_private(&buffer, filename, passphrase, commentp); - buffer_free(&buffer); - return prv; + if (buffer != NULL) + sshbuf_free(buffer); + return r; } static int -key_try_load_public(Key *k, const char *filename, char **commentp) +sshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp) { FILE *f; char line[SSH_MAX_PUBKEY_BYTES]; char *cp; u_long linenum = 0; + int r; - f = fopen(filename, "r"); - if (f != NULL) { - while (read_keyfile_line(f, filename, line, sizeof(line), - &linenum) != -1) { - cp = line; - switch (*cp) { - case '#': - case '\n': - case '\0': - continue; - } - /* Abort loading if this looks like a private key */ - if (strncmp(cp, "-----BEGIN", 10) == 0) - break; - /* Skip leading whitespace. */ - for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) - ; - if (*cp) { - if (key_read(k, &cp) == 1) { - cp[strcspn(cp, "\r\n")] = '\0'; - if (commentp) { - *commentp = xstrdup(*cp ? - cp : filename); - } - fclose(f); - return 1; + if (commentp != NULL) + *commentp = NULL; + if ((f = fopen(filename, "r")) == NULL) + return SSH_ERR_SYSTEM_ERROR; + while (read_keyfile_line(f, filename, line, sizeof(line), + &linenum) != -1) { + cp = line; + switch (*cp) { + case '#': + case '\n': + case '\0': + continue; + } + /* Abort loading if this looks like a private key */ + if (strncmp(cp, "-----BEGIN", 10) == 0 || + strcmp(cp, "SSH PRIVATE KEY FILE") == 0) + break; + /* Skip leading whitespace. */ + for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) + ; + if (*cp) { + if ((r = sshkey_read(k, &cp)) == 0) { + cp[strcspn(cp, "\r\n")] = '\0'; + if (commentp) { + *commentp = strdup(*cp ? + cp : filename); + if (*commentp == NULL) + r = SSH_ERR_ALLOC_FAIL; } + fclose(f); + return r; } } - fclose(f); } - return 0; + fclose(f); + return SSH_ERR_INVALID_FORMAT; } /* load public key from ssh v1 private or any pubkey file */ -Key * -key_load_public(const char *filename, char **commentp) +int +sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp) { - Key *pub; + struct sshkey *pub = NULL; char file[MAXPATHLEN]; + int r, fd; + + if (keyp != NULL) + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; + if ((fd = open(filename, O_RDONLY)) < 0) + goto skip; #ifdef WITH_SSH1 /* try rsa1 private key */ - pub = key_load_public_type(KEY_RSA1, filename, commentp); - if (pub != NULL) - return pub; + r = sshkey_load_public_rsa1(fd, filename, keyp, commentp); + close(fd); + switch (r) { + case SSH_ERR_INTERNAL_ERROR: + case SSH_ERR_ALLOC_FAIL: + case SSH_ERR_INVALID_ARGUMENT: + case SSH_ERR_SYSTEM_ERROR: + case 0: + return r; + } +#endif /* WITH_SSH1 */ + + /* try ssh2 public key */ + if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) { + if (keyp != NULL) + *keyp = pub; + return 0; + } + sshkey_free(pub); +#ifdef WITH_SSH1 /* try rsa1 public key */ - pub = key_new(KEY_RSA1); - if (key_try_load_public(pub, filename, commentp) == 1) - return pub; - key_free(pub); -#endif + if ((pub = sshkey_new(KEY_RSA1)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) { + if (keyp != NULL) + *keyp = pub; + return 0; + } + sshkey_free(pub); +#endif /* WITH_SSH1 */ - /* try ssh2 public key */ - pub = key_new(KEY_UNSPEC); - if (key_try_load_public(pub, filename, commentp) == 1) - return pub; + skip: + /* try .pub suffix */ + if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) + return SSH_ERR_ALLOC_FAIL; + r = SSH_ERR_ALLOC_FAIL; /* in case strlcpy or strlcat fail */ if ((strlcpy(file, filename, sizeof file) < sizeof(file)) && (strlcat(file, ".pub", sizeof file) < sizeof(file)) && - (key_try_load_public(pub, file, commentp) == 1)) - return pub; - key_free(pub); - return NULL; + (r = sshkey_try_load_public(pub, file, commentp)) == 0) { + if (keyp != NULL) + *keyp = pub; + return 0; + } + sshkey_free(pub); + return r; } /* Load the certificate associated with the named private key */ -Key * -key_load_cert(const char *filename) +int +sshkey_load_cert(const char *filename, struct sshkey **keyp) { - Key *pub; - char *file; + struct sshkey *pub = NULL; + char *file = NULL; + int r = SSH_ERR_INTERNAL_ERROR; - pub = key_new(KEY_UNSPEC); - xasprintf(&file, "%s-cert.pub", filename); - if (key_try_load_public(pub, file, NULL) == 1) { - free(file); - return pub; + *keyp = NULL; + + if (asprintf(&file, "%s-cert.pub", filename) == -1) + return SSH_ERR_ALLOC_FAIL; + + if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { + goto out; } - free(file); - key_free(pub); - return NULL; + if ((r = sshkey_try_load_public(pub, file, NULL)) != 0) + goto out; + + *keyp = pub; + pub = NULL; + r = 0; + + out: + if (file != NULL) + free(file); + if (pub != NULL) + sshkey_free(pub); + return r; } /* Load private key and certificate */ -Key * -key_load_private_cert(int type, const char *filename, const char *passphrase, - int *perm_ok) +int +sshkey_load_private_cert(int type, const char *filename, const char *passphrase, + struct sshkey **keyp, int *perm_ok) { - Key *key, *pub; + struct sshkey *key = NULL, *cert = NULL; + int r; + + *keyp = NULL; switch (type) { #ifdef WITH_OPENSSL case KEY_RSA: case KEY_DSA: case KEY_ECDSA: -#endif case KEY_ED25519: +#endif /* WITH_OPENSSL */ + case KEY_UNSPEC: break; default: - error("%s: unsupported key type", __func__); - return NULL; + return SSH_ERR_KEY_TYPE_UNKNOWN; } - if ((key = key_load_private_type(type, filename, - passphrase, NULL, perm_ok)) == NULL) - return NULL; - - if ((pub = key_load_cert(filename)) == NULL) { - key_free(key); - return NULL; - } + if ((r = sshkey_load_private_type(type, filename, + passphrase, &key, NULL, perm_ok)) != 0 || + (r = sshkey_load_cert(filename, &cert)) != 0) + goto out; /* Make sure the private key matches the certificate */ - if (key_equal_public(key, pub) == 0) { - error("%s: certificate does not match private key %s", - __func__, filename); - } else if (key_to_certified(key, key_cert_is_legacy(pub)) != 0) { - error("%s: key_to_certified failed", __func__); - } else { - key_cert_copy(pub, key); - key_free(pub); - return key; + if (sshkey_equal_public(key, cert) == 0) { + r = SSH_ERR_KEY_CERT_MISMATCH; + goto out; } - key_free(key); - key_free(pub); - return NULL; + if ((r = sshkey_to_certified(key, sshkey_cert_is_legacy(cert))) != 0 || + (r = sshkey_cert_copy(cert, key)) != 0) + goto out; + r = 0; + *keyp = key; + key = NULL; + out: + if (key != NULL) + sshkey_free(key); + if (cert != NULL) + sshkey_free(cert); + return r; } /* - * Returns 1 if the specified "key" is listed in the file "filename", - * 0 if the key is not listed or -1 on error. + * Returns success if the specified "key" is listed in the file "filename", + * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error. * If strict_type is set then the key type must match exactly, * otherwise a comparison that ignores certficiate data is performed. */ int -key_in_file(Key *key, const char *filename, int strict_type) +sshkey_in_file(struct sshkey *key, const char *filename, int strict_type) { FILE *f; char line[SSH_MAX_PUBKEY_BYTES]; char *cp; u_long linenum = 0; - int ret = 0; - Key *pub; - int (*key_compare)(const Key *, const Key *) = strict_type ? - key_equal : key_equal_public; + int r = 0; + struct sshkey *pub = NULL; + int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) = + strict_type ? sshkey_equal : sshkey_equal_public; if ((f = fopen(filename, "r")) == NULL) { - if (errno == ENOENT) { - debug("%s: keyfile \"%s\" missing", __func__, filename); - return 0; - } else { - error("%s: could not open keyfile \"%s\": %s", __func__, - filename, strerror(errno)); - return -1; - } + if (errno == ENOENT) + return SSH_ERR_KEY_NOT_FOUND; + else + return SSH_ERR_SYSTEM_ERROR; } while (read_keyfile_line(f, filename, line, sizeof(line), - &linenum) != -1) { + &linenum) != -1) { cp = line; /* Skip leading whitespace. */ @@ -1323,18 +532,24 @@ key_in_file(Key *key, const char *filename, int strict_type) continue; } - pub = key_new(KEY_UNSPEC); - if (key_read(pub, &cp) != 1) { - key_free(pub); - continue; + if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; } - if (key_compare(key, pub)) { - ret = 1; - key_free(pub); - break; + if ((r = sshkey_read(pub, &cp)) != 0) + goto out; + if (sshkey_compare(key, pub)) { + r = 0; + goto out; } - key_free(pub); + sshkey_free(pub); + pub = NULL; } + r = SSH_ERR_KEY_NOT_FOUND; + out: + if (pub != NULL) + sshkey_free(pub); fclose(f); - return ret; + return r; } + diff --git a/authfile.h b/authfile.h index 8ba1c2dbe..223101202 100644 --- a/authfile.h +++ b/authfile.h @@ -1,32 +1,51 @@ -/* $OpenBSD: authfile.h,v 1.17 2013/12/06 13:34:54 markus Exp $ */ +/* $OpenBSD: authfile.h,v 1.18 2014/06/24 01:13:21 djm Exp $ */ /* - * Author: Tatu Ylonen - * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - * All rights reserved + * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved. * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef AUTHFILE_H #define AUTHFILE_H -int key_save_private(Key *, const char *, const char *, const char *, - int, const char *, int); -int key_load_file(int, const char *, Buffer *); -Key *key_load_cert(const char *); -Key *key_load_public(const char *, char **); -Key *key_load_public_type(int, const char *, char **); -Key *key_parse_private(Buffer *, const char *, const char *, char **); -Key *key_load_private(const char *, const char *, char **); -Key *key_load_private_cert(int, const char *, const char *, int *); -Key *key_load_private_type(int, const char *, const char *, char **, int *); -Key *key_load_private_pem(int, int, const char *, char **); -int key_perm_ok(int, const char *); -int key_in_file(Key *, const char *, int); +#ifdef WITH_LEAKMALLOC +#include "leakmalloc.h" +#endif + +struct sshbuf; +struct sshkey; + +int sshkey_save_private(struct sshkey *, const char *, + const char *, const char *, int, const char *, int); +int sshkey_load_file(int, const char *, struct sshbuf *); +int sshkey_load_cert(const char *, struct sshkey **); +int sshkey_load_public(const char *, struct sshkey **, char **); +int sshkey_load_private(const char *, const char *, struct sshkey **, char **); +int sshkey_load_private_cert(int, const char *, const char *, + struct sshkey **, int *); +int sshkey_load_private_type(int, const char *, const char *, + struct sshkey **, char **, int *); +int sshkey_load_private_pem(int, int, const char *, struct sshkey **, char **); +int sshkey_perm_ok(int, const char *); +int sshkey_in_file(struct sshkey *, const char *, int); #endif diff --git a/cipher-3des1.c b/cipher-3des1.c index b2823592b..5361f517d 100644 --- a/cipher-3des1.c +++ b/cipher-3des1.c @@ -29,13 +29,11 @@ #include -#include #include #include "xmalloc.h" #include "log.h" - -#include "openbsd-compat/openssl-compat.h" +#include "ssherr.h" /* * This is used by SSH1: @@ -57,7 +55,7 @@ struct ssh1_3des_ctx }; const EVP_CIPHER * evp_ssh1_3des(void); -void ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); +int ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); static int ssh1_3des_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv, @@ -67,11 +65,12 @@ ssh1_3des_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv, u_char *k1, *k2, *k3; if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) { - c = xcalloc(1, sizeof(*c)); + if ((c = calloc(1, sizeof(*c))) == NULL) + return 0; EVP_CIPHER_CTX_set_app_data(ctx, c); } if (key == NULL) - return (1); + return 1; if (enc == -1) enc = ctx->encrypt; k1 = k2 = k3 = (u_char *) key; @@ -85,44 +84,29 @@ ssh1_3des_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv, EVP_CIPHER_CTX_init(&c->k1); EVP_CIPHER_CTX_init(&c->k2); EVP_CIPHER_CTX_init(&c->k3); -#ifdef SSH_OLD_EVP - EVP_CipherInit(&c->k1, EVP_des_cbc(), k1, NULL, enc); - EVP_CipherInit(&c->k2, EVP_des_cbc(), k2, NULL, !enc); - EVP_CipherInit(&c->k3, EVP_des_cbc(), k3, NULL, enc); -#else if (EVP_CipherInit(&c->k1, EVP_des_cbc(), k1, NULL, enc) == 0 || EVP_CipherInit(&c->k2, EVP_des_cbc(), k2, NULL, !enc) == 0 || EVP_CipherInit(&c->k3, EVP_des_cbc(), k3, NULL, enc) == 0) { explicit_bzero(c, sizeof(*c)); free(c); EVP_CIPHER_CTX_set_app_data(ctx, NULL); - return (0); + return 0; } -#endif - return (1); + return 1; } static int -ssh1_3des_cbc(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src, - LIBCRYPTO_EVP_INL_TYPE len) +ssh1_3des_cbc(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src, size_t len) { struct ssh1_3des_ctx *c; - if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) { - error("ssh1_3des_cbc: no context"); - return (0); - } -#ifdef SSH_OLD_EVP - EVP_Cipher(&c->k1, dest, (u_char *)src, len); - EVP_Cipher(&c->k2, dest, dest, len); - EVP_Cipher(&c->k3, dest, dest, len); -#else + if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) + return 0; if (EVP_Cipher(&c->k1, dest, (u_char *)src, len) == 0 || EVP_Cipher(&c->k2, dest, dest, len) == 0 || EVP_Cipher(&c->k3, dest, dest, len) == 0) - return (0); -#endif - return (1); + return 0; + return 1; } static int @@ -138,29 +122,28 @@ ssh1_3des_cleanup(EVP_CIPHER_CTX *ctx) free(c); EVP_CIPHER_CTX_set_app_data(ctx, NULL); } - return (1); + return 1; } -void +int ssh1_3des_iv(EVP_CIPHER_CTX *evp, int doset, u_char *iv, int len) { struct ssh1_3des_ctx *c; if (len != 24) - fatal("%s: bad 3des iv length: %d", __func__, len); + return SSH_ERR_INVALID_ARGUMENT; if ((c = EVP_CIPHER_CTX_get_app_data(evp)) == NULL) - fatal("%s: no 3des context", __func__); + return SSH_ERR_INTERNAL_ERROR; if (doset) { - debug3("%s: Installed 3DES IV", __func__); memcpy(c->k1.iv, iv, 8); memcpy(c->k2.iv, iv + 8, 8); memcpy(c->k3.iv, iv + 16, 8); } else { - debug3("%s: Copying 3DES IV", __func__); memcpy(iv, c->k1.iv, 8); memcpy(iv + 8, c->k2.iv, 8); memcpy(iv + 16, c->k3.iv, 8); } + return 0; } const EVP_CIPHER * @@ -176,8 +159,6 @@ evp_ssh1_3des(void) ssh1_3des.init = ssh1_3des_init; ssh1_3des.cleanup = ssh1_3des_cleanup; ssh1_3des.do_cipher = ssh1_3des_cbc; -#ifndef SSH_OLD_EVP ssh1_3des.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH; -#endif - return (&ssh1_3des); + return &ssh1_3des; } diff --git a/cipher-chachapoly.c b/cipher-chachapoly.c index 251b94ec8..0caccd297 100644 --- a/cipher-chachapoly.c +++ b/cipher-chachapoly.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $OpenBSD: cipher-chachapoly.c,v 1.4 2014/01/31 16:39:19 tedu Exp $ */ +/* $OpenBSD: cipher-chachapoly.c,v 1.5 2014/06/24 01:13:21 djm Exp $ */ #include "includes.h" @@ -24,16 +24,18 @@ #include /* needed for misc.h */ #include "log.h" -#include "misc.h" +#include "sshbuf.h" +#include "ssherr.h" #include "cipher-chachapoly.h" -void chachapoly_init(struct chachapoly_ctx *ctx, +int chachapoly_init(struct chachapoly_ctx *ctx, const u_char *key, u_int keylen) { if (keylen != (32 + 32)) /* 2 x 256 bit keys */ - fatal("%s: invalid keylen %u", __func__, keylen); + return SSH_ERR_INVALID_ARGUMENT; chacha_keysetup(&ctx->main_ctx, key, 256); chacha_keysetup(&ctx->header_ctx, key + 32, 256); + return 0; } /* @@ -52,14 +54,14 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest, u_char seqbuf[8]; const u_char one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */ u_char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN]; - int r = -1; + int r = SSH_ERR_INTERNAL_ERROR; /* * Run ChaCha20 once to generate the Poly1305 key. The IV is the * packet sequence number. */ memset(poly_key, 0, sizeof(poly_key)); - put_u64(seqbuf, seqnr); + POKE_U64(seqbuf, seqnr); chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL); chacha_encrypt_bytes(&ctx->main_ctx, poly_key, poly_key, sizeof(poly_key)); @@ -71,8 +73,10 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest, const u_char *tag = src + aadlen + len; poly1305_auth(expected_tag, src, aadlen + len, poly_key); - if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) + if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) { + r = SSH_ERR_MAC_INVALID; goto out; + } } /* Crypt additional data */ if (aadlen) { @@ -88,7 +92,6 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest, poly_key); } r = 0; - out: explicit_bzero(expected_tag, sizeof(expected_tag)); explicit_bzero(seqbuf, sizeof(seqbuf)); @@ -104,11 +107,11 @@ chachapoly_get_length(struct chachapoly_ctx *ctx, u_char buf[4], seqbuf[8]; if (len < 4) - return -1; /* Insufficient length */ - put_u64(seqbuf, seqnr); + return SSH_ERR_MESSAGE_INCOMPLETE; + POKE_U64(seqbuf, seqnr); chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL); chacha_encrypt_bytes(&ctx->header_ctx, cp, buf, 4); - *plenp = get_u32(buf); + *plenp = PEEK_U32(buf); return 0; } diff --git a/cipher-chachapoly.h b/cipher-chachapoly.h index 7948dcdcd..b7072be7d 100644 --- a/cipher-chachapoly.h +++ b/cipher-chachapoly.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cipher-chachapoly.h,v 1.3 2014/05/02 03:27:54 djm Exp $ */ +/* $OpenBSD: cipher-chachapoly.h,v 1.4 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) Damien Miller 2013 @@ -28,7 +28,7 @@ struct chachapoly_ctx { struct chacha_ctx main_ctx, header_ctx; }; -void chachapoly_init(struct chachapoly_ctx *cpctx, +int chachapoly_init(struct chachapoly_ctx *cpctx, const u_char *key, u_int keylen) __attribute__((__bounded__(__buffer__, 2, 3))); int chachapoly_crypt(struct chachapoly_ctx *cpctx, u_int seqnr, diff --git a/cipher.c b/cipher.c index 5569d2455..48ef105ca 100644 --- a/cipher.c +++ b/cipher.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cipher.c,v 1.98 2014/04/29 18:01:49 markus Exp $ */ +/* $OpenBSD: cipher.c,v 1.99 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -43,23 +43,19 @@ #include #include -#include "xmalloc.h" -#include "log.h" -#include "misc.h" #include "cipher.h" -#include "buffer.h" +#include "misc.h" +#include "sshbuf.h" +#include "ssherr.h" #include "digest.h" -/* compatibility with old or broken OpenSSL versions */ -#include "openbsd-compat/openssl-compat.h" - #ifdef WITH_SSH1 extern const EVP_CIPHER *evp_ssh1_bf(void); extern const EVP_CIPHER *evp_ssh1_3des(void); -extern void ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); +extern int ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); #endif -struct Cipher { +struct sshcipher { char *name; int number; /* for ssh1 only */ u_int block_size; @@ -79,12 +75,12 @@ struct Cipher { #endif }; -static const struct Cipher ciphers[] = { +static const struct sshcipher ciphers[] = { #ifdef WITH_SSH1 { "des", SSH_CIPHER_DES, 8, 8, 0, 0, 0, 1, EVP_des_cbc }, { "3des", SSH_CIPHER_3DES, 8, 16, 0, 0, 0, 1, evp_ssh1_3des }, { "blowfish", SSH_CIPHER_BLOWFISH, 8, 32, 0, 0, 0, 1, evp_ssh1_bf }, -#endif +#endif /* WITH_SSH1 */ #ifdef WITH_OPENSSL { "none", SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null }, { "3des-cbc", SSH_CIPHER_SSH2, 8, 24, 0, 0, 0, 1, EVP_des_ede3_cbc }, @@ -103,12 +99,12 @@ static const struct Cipher ciphers[] = { { "aes128-ctr", SSH_CIPHER_SSH2, 16, 16, 0, 0, 0, 0, EVP_aes_128_ctr }, { "aes192-ctr", SSH_CIPHER_SSH2, 16, 24, 0, 0, 0, 0, EVP_aes_192_ctr }, { "aes256-ctr", SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, 0, EVP_aes_256_ctr }, -#ifdef OPENSSL_HAVE_EVPGCM +# ifdef OPENSSL_HAVE_EVPGCM { "aes128-gcm@openssh.com", SSH_CIPHER_SSH2, 16, 16, 12, 16, 0, 0, EVP_aes_128_gcm }, { "aes256-gcm@openssh.com", SSH_CIPHER_SSH2, 16, 32, 12, 16, 0, 0, EVP_aes_256_gcm }, -#endif +# endif /* OPENSSL_HAVE_EVPGCM */ #else /* WITH_OPENSSL */ { "aes128-ctr", SSH_CIPHER_SSH2, 16, 16, 0, 0, 0, CFLAG_AESCTR, NULL }, { "aes192-ctr", SSH_CIPHER_SSH2, 16, 24, 0, 0, 0, CFLAG_AESCTR, NULL }, @@ -117,18 +113,19 @@ static const struct Cipher ciphers[] = { #endif /* WITH_OPENSSL */ { "chacha20-poly1305@openssh.com", SSH_CIPHER_SSH2, 8, 64, 0, 16, 0, CFLAG_CHACHAPOLY, NULL }, + { NULL, SSH_CIPHER_INVALID, 0, 0, 0, 0, 0, 0, NULL } }; /*--*/ -/* Returns a list of supported ciphers separated by the specified char. */ +/* Returns a comma-separated list of supported ciphers. */ char * cipher_alg_list(char sep, int auth_only) { - char *ret = NULL; + char *tmp, *ret = NULL; size_t nlen, rlen = 0; - const Cipher *c; + const struct sshcipher *c; for (c = ciphers; c->name != NULL; c++) { if (c->number != SSH_CIPHER_SSH2) @@ -138,7 +135,11 @@ cipher_alg_list(char sep, int auth_only) if (ret != NULL) ret[rlen++] = sep; nlen = strlen(c->name); - ret = xrealloc(ret, 1, rlen + nlen + 2); + if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { + free(ret); + return NULL; + } + ret = tmp; memcpy(ret + rlen, c->name, nlen + 1); rlen += nlen; } @@ -146,19 +147,19 @@ cipher_alg_list(char sep, int auth_only) } u_int -cipher_blocksize(const Cipher *c) +cipher_blocksize(const struct sshcipher *c) { return (c->block_size); } u_int -cipher_keylen(const Cipher *c) +cipher_keylen(const struct sshcipher *c) { return (c->key_len); } u_int -cipher_seclen(const Cipher *c) +cipher_seclen(const struct sshcipher *c) { if (strcmp("3des-cbc", c->name) == 0) return 14; @@ -166,13 +167,13 @@ cipher_seclen(const Cipher *c) } u_int -cipher_authlen(const Cipher *c) +cipher_authlen(const struct sshcipher *c) { return (c->auth_len); } u_int -cipher_ivlen(const Cipher *c) +cipher_ivlen(const struct sshcipher *c) { /* * Default is cipher block size, except for chacha20+poly1305 that @@ -183,13 +184,13 @@ cipher_ivlen(const Cipher *c) } u_int -cipher_get_number(const Cipher *c) +cipher_get_number(const struct sshcipher *c) { return (c->number); } u_int -cipher_is_cbc(const Cipher *c) +cipher_is_cbc(const struct sshcipher *c) { return (c->flags & CFLAG_CBC) != 0; } @@ -206,20 +207,20 @@ cipher_mask_ssh1(int client) return mask; } -const Cipher * +const struct sshcipher * cipher_by_name(const char *name) { - const Cipher *c; + const struct sshcipher *c; for (c = ciphers; c->name != NULL; c++) if (strcmp(c->name, name) == 0) return c; return NULL; } -const Cipher * +const struct sshcipher * cipher_by_number(int id) { - const Cipher *c; + const struct sshcipher *c; for (c = ciphers; c->name != NULL; c++) if (c->number == id) return c; @@ -230,23 +231,22 @@ cipher_by_number(int id) int ciphers_valid(const char *names) { - const Cipher *c; + const struct sshcipher *c; char *cipher_list, *cp; char *p; if (names == NULL || strcmp(names, "") == 0) return 0; - cipher_list = cp = xstrdup(names); + if ((cipher_list = cp = strdup(names)) == NULL) + return 0; for ((p = strsep(&cp, CIPHER_SEP)); p && *p != '\0'; (p = strsep(&cp, CIPHER_SEP))) { c = cipher_by_name(p); if (c == NULL || c->number != SSH_CIPHER_SSH2) { - debug("bad cipher %s [%s]", p, names); free(cipher_list); return 0; } } - debug3("ciphers ok: [%s]", names); free(cipher_list); return 1; } @@ -259,7 +259,7 @@ ciphers_valid(const char *names) int cipher_number(const char *name) { - const Cipher *c; + const struct sshcipher *c; if (name == NULL) return -1; for (c = ciphers; c->name != NULL; c++) @@ -271,31 +271,33 @@ cipher_number(const char *name) char * cipher_name(int id) { - const Cipher *c = cipher_by_number(id); + const struct sshcipher *c = cipher_by_number(id); return (c==NULL) ? "" : c->name; } -void -cipher_init(CipherContext *cc, const Cipher *cipher, +const char * +cipher_warning_message(const struct sshcipher_ctx *cc) +{ + if (cc == NULL || cc->cipher == NULL) + return NULL; + if (cc->cipher->number == SSH_CIPHER_DES) + return "use of DES is strongly discouraged due to " + "cryptographic weaknesses"; + return NULL; +} + +int +cipher_init(struct sshcipher_ctx *cc, const struct sshcipher *cipher, const u_char *key, u_int keylen, const u_char *iv, u_int ivlen, int do_encrypt) { #ifdef WITH_OPENSSL - static int dowarn = 1; -#ifdef SSH_OLD_EVP - EVP_CIPHER *type; -#else + int ret = SSH_ERR_INTERNAL_ERROR; const EVP_CIPHER *type; int klen; -#endif u_char *junk, *discard; if (cipher->number == SSH_CIPHER_DES) { - if (dowarn) { - error("Warning: use of DES is strongly discouraged " - "due to cryptographic weaknesses"); - dowarn = 0; - } if (keylen > 8) keylen = 8; } @@ -303,71 +305,70 @@ cipher_init(CipherContext *cc, const Cipher *cipher, cc->plaintext = (cipher->number == SSH_CIPHER_NONE); cc->encrypt = do_encrypt; - if (keylen < cipher->key_len) - fatal("cipher_init: key length %d is insufficient for %s.", - keylen, cipher->name); - if (iv != NULL && ivlen < cipher_ivlen(cipher)) - fatal("cipher_init: iv length %d is insufficient for %s.", - ivlen, cipher->name); - cc->cipher = cipher; + if (keylen < cipher->key_len || + (iv != NULL && ivlen < cipher_ivlen(cipher))) + return SSH_ERR_INVALID_ARGUMENT; + cc->cipher = cipher; if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) { - chachapoly_init(&cc->cp_ctx, key, keylen); - return; + return chachapoly_init(&cc->cp_ctx, key, keylen); } #ifndef WITH_OPENSSL if ((cc->cipher->flags & CFLAG_AESCTR) != 0) { aesctr_keysetup(&cc->ac_ctx, key, 8 * keylen, 8 * ivlen); aesctr_ivsetup(&cc->ac_ctx, iv); - return; + return 0; } if ((cc->cipher->flags & CFLAG_NONE) != 0) - return; - fatal("unsupported cipher"); + return 0; + return SSH_ERR_INVALID_ARGUMENT; #else type = (*cipher->evptype)(); EVP_CIPHER_CTX_init(&cc->evp); -#ifdef SSH_OLD_EVP - if (type->key_len > 0 && type->key_len != keylen) { - debug("cipher_init: set keylen (%d -> %d)", - type->key_len, keylen); - type->key_len = keylen; - } - EVP_CipherInit(&cc->evp, type, (u_char *)key, (u_char *)iv, - (do_encrypt == CIPHER_ENCRYPT)); -#else if (EVP_CipherInit(&cc->evp, type, NULL, (u_char *)iv, - (do_encrypt == CIPHER_ENCRYPT)) == 0) - fatal("cipher_init: EVP_CipherInit failed for %s", - cipher->name); + (do_encrypt == CIPHER_ENCRYPT)) == 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto bad; + } if (cipher_authlen(cipher) && !EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_SET_IV_FIXED, - -1, (u_char *)iv)) - fatal("cipher_init: EVP_CTRL_GCM_SET_IV_FIXED failed for %s", - cipher->name); + -1, (u_char *)iv)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto bad; + } klen = EVP_CIPHER_CTX_key_length(&cc->evp); if (klen > 0 && keylen != (u_int)klen) { - debug2("cipher_init: set keylen (%d -> %d)", klen, keylen); - if (EVP_CIPHER_CTX_set_key_length(&cc->evp, keylen) == 0) - fatal("cipher_init: set keylen failed (%d -> %d)", - klen, keylen); + if (EVP_CIPHER_CTX_set_key_length(&cc->evp, keylen) == 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto bad; + } + } + if (EVP_CipherInit(&cc->evp, NULL, (u_char *)key, NULL, -1) == 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto bad; } - if (EVP_CipherInit(&cc->evp, NULL, (u_char *)key, NULL, -1) == 0) - fatal("cipher_init: EVP_CipherInit: set key failed for %s", - cipher->name); -#endif if (cipher->discard_len > 0) { - junk = xmalloc(cipher->discard_len); - discard = xmalloc(cipher->discard_len); - if (EVP_Cipher(&cc->evp, discard, junk, - cipher->discard_len) == 0) - fatal("evp_crypt: EVP_Cipher failed during discard"); + if ((junk = malloc(cipher->discard_len)) == NULL || + (discard = malloc(cipher->discard_len)) == NULL) { + if (junk != NULL) + free(junk); + ret = SSH_ERR_ALLOC_FAIL; + goto bad; + } + ret = EVP_Cipher(&cc->evp, discard, junk, cipher->discard_len); explicit_bzero(discard, cipher->discard_len); free(junk); free(discard); + if (ret != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + bad: + EVP_CIPHER_CTX_cleanup(&cc->evp); + return ret; + } } #endif + return 0; } /* @@ -379,16 +380,15 @@ cipher_init(CipherContext *cc, const Cipher *cipher, * Use 'authlen' bytes at offset 'len'+'aadlen' as the authentication tag. * This tag is written on encryption and verified on decryption. * Both 'aadlen' and 'authlen' can be set to 0. - * cipher_crypt() returns 0 on success and -1 if the decryption integrity - * check fails. */ int -cipher_crypt(CipherContext *cc, u_int seqnr, u_char *dest, const u_char *src, - u_int len, u_int aadlen, u_int authlen) +cipher_crypt(struct sshcipher_ctx *cc, u_int seqnr, u_char *dest, + const u_char *src, u_int len, u_int aadlen, u_int authlen) { - if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) - return chachapoly_crypt(&cc->cp_ctx, seqnr, dest, src, len, - aadlen, authlen, cc->encrypt); + if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) { + return chachapoly_crypt(&cc->cp_ctx, seqnr, dest, src, + len, aadlen, authlen, cc->encrypt); + } #ifndef WITH_OPENSSL if ((cc->cipher->flags & CFLAG_AESCTR) != 0) { if (aadlen) @@ -401,46 +401,43 @@ cipher_crypt(CipherContext *cc, u_int seqnr, u_char *dest, const u_char *src, memcpy(dest, src, aadlen + len); return 0; } - fatal("unsupported cipher"); + return SSH_ERR_INVALID_ARGUMENT; #else if (authlen) { u_char lastiv[1]; if (authlen != cipher_authlen(cc->cipher)) - fatal("%s: authlen mismatch %d", __func__, authlen); + return SSH_ERR_INVALID_ARGUMENT; /* increment IV */ if (!EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_IV_GEN, 1, lastiv)) - fatal("%s: EVP_CTRL_GCM_IV_GEN", __func__); + return SSH_ERR_LIBCRYPTO_ERROR; /* set tag on decyption */ if (!cc->encrypt && !EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_SET_TAG, authlen, (u_char *)src + aadlen + len)) - fatal("%s: EVP_CTRL_GCM_SET_TAG", __func__); + return SSH_ERR_LIBCRYPTO_ERROR; } if (aadlen) { if (authlen && EVP_Cipher(&cc->evp, NULL, (u_char *)src, aadlen) < 0) - fatal("%s: EVP_Cipher(aad) failed", __func__); + return SSH_ERR_LIBCRYPTO_ERROR; memcpy(dest, src, aadlen); } if (len % cc->cipher->block_size) - fatal("%s: bad plaintext length %d", __func__, len); + return SSH_ERR_INVALID_ARGUMENT; if (EVP_Cipher(&cc->evp, dest + aadlen, (u_char *)src + aadlen, len) < 0) - fatal("%s: EVP_Cipher failed", __func__); + return SSH_ERR_LIBCRYPTO_ERROR; if (authlen) { /* compute tag (on encrypt) or verify tag (on decrypt) */ - if (EVP_Cipher(&cc->evp, NULL, NULL, 0) < 0) { - if (cc->encrypt) - fatal("%s: EVP_Cipher(final) failed", __func__); - else - return -1; - } + if (EVP_Cipher(&cc->evp, NULL, NULL, 0) < 0) + return cc->encrypt ? + SSH_ERR_LIBCRYPTO_ERROR : SSH_ERR_MAC_INVALID; if (cc->encrypt && !EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_GET_TAG, authlen, dest + aadlen + len)) - fatal("%s: EVP_CTRL_GCM_GET_TAG", __func__); + return SSH_ERR_LIBCRYPTO_ERROR; } return 0; #endif @@ -448,61 +445,65 @@ cipher_crypt(CipherContext *cc, u_int seqnr, u_char *dest, const u_char *src, /* Extract the packet length, including any decryption necessary beforehand */ int -cipher_get_length(CipherContext *cc, u_int *plenp, u_int seqnr, +cipher_get_length(struct sshcipher_ctx *cc, u_int *plenp, u_int seqnr, const u_char *cp, u_int len) { if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) return chachapoly_get_length(&cc->cp_ctx, plenp, seqnr, cp, len); if (len < 4) - return -1; + return SSH_ERR_MESSAGE_INCOMPLETE; *plenp = get_u32(cp); return 0; } -void -cipher_cleanup(CipherContext *cc) +int +cipher_cleanup(struct sshcipher_ctx *cc) { + if (cc == NULL || cc->cipher == NULL) + return 0; if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) explicit_bzero(&cc->cp_ctx, sizeof(cc->cp_ctx)); else if ((cc->cipher->flags & CFLAG_AESCTR) != 0) explicit_bzero(&cc->ac_ctx, sizeof(cc->ac_ctx)); #ifdef WITH_OPENSSL else if (EVP_CIPHER_CTX_cleanup(&cc->evp) == 0) - error("cipher_cleanup: EVP_CIPHER_CTX_cleanup failed"); + return SSH_ERR_LIBCRYPTO_ERROR; #endif + return 0; } /* * Selects the cipher, and keys if by computing the MD5 checksum of the * passphrase and using the resulting 16 bytes as the key. */ - -void -cipher_set_key_string(CipherContext *cc, const Cipher *cipher, +int +cipher_set_key_string(struct sshcipher_ctx *cc, const struct sshcipher *cipher, const char *passphrase, int do_encrypt) { u_char digest[16]; + int r = SSH_ERR_INTERNAL_ERROR; - if (ssh_digest_memory(SSH_DIGEST_MD5, passphrase, strlen(passphrase), - digest, sizeof(digest)) < 0) - fatal("%s: md5 failed", __func__); - - cipher_init(cc, cipher, digest, 16, NULL, 0, do_encrypt); + if ((r = ssh_digest_memory(SSH_DIGEST_MD5, + passphrase, strlen(passphrase), + digest, sizeof(digest))) != 0) + goto out; + r = cipher_init(cc, cipher, digest, 16, NULL, 0, do_encrypt); + out: explicit_bzero(digest, sizeof(digest)); + return r; } /* - * Exports an IV from the CipherContext required to export the key + * Exports an IV from the sshcipher_ctx required to export the key * state back from the unprivileged child to the privileged parent * process. */ - int -cipher_get_keyiv_len(const CipherContext *cc) +cipher_get_keyiv_len(const struct sshcipher_ctx *cc) { - const Cipher *c = cc->cipher; + const struct sshcipher *c = cc->cipher; int ivlen = 0; if (c->number == SSH_CIPHER_3DES) @@ -512,25 +513,25 @@ cipher_get_keyiv_len(const CipherContext *cc) #ifdef WITH_OPENSSL else ivlen = EVP_CIPHER_CTX_iv_length(&cc->evp); -#endif +#endif /* WITH_OPENSSL */ return (ivlen); } -void -cipher_get_keyiv(CipherContext *cc, u_char *iv, u_int len) +int +cipher_get_keyiv(struct sshcipher_ctx *cc, u_char *iv, u_int len) { - const Cipher *c = cc->cipher; + const struct sshcipher *c = cc->cipher; #ifdef WITH_OPENSSL - int evplen; + int evplen; #endif if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) { if (len != 0) - fatal("%s: wrong iv length %d != %d", __func__, len, 0); - return; + return SSH_ERR_INVALID_ARGUMENT; + return 0; } if ((cc->cipher->flags & CFLAG_NONE) != 0) - return; + return 0; switch (c->number) { #ifdef WITH_OPENSSL @@ -538,51 +539,42 @@ cipher_get_keyiv(CipherContext *cc, u_char *iv, u_int len) case SSH_CIPHER_DES: case SSH_CIPHER_BLOWFISH: evplen = EVP_CIPHER_CTX_iv_length(&cc->evp); - if (evplen <= 0) - return; + if (evplen == 0) + return 0; + else if (evplen < 0) + return SSH_ERR_LIBCRYPTO_ERROR; if ((u_int)evplen != len) - fatal("%s: wrong iv length %d != %d", __func__, - evplen, len); -#ifdef USE_BUILTIN_RIJNDAEL - if (c->evptype == evp_rijndael) - ssh_rijndael_iv(&cc->evp, 0, iv, len); - else -#endif /* USE_BUILTIN_RIJNDAEL */ -#ifndef OPENSSL_HAVE_EVPCTR - if (c->evptype == evp_aes_128_ctr) - ssh_aes_ctr_iv(&cc->evp, 0, iv, len); - else -#endif /* OPENSSL_HAVE_EVPCTR */ + return SSH_ERR_INVALID_ARGUMENT; if (cipher_authlen(c)) { if (!EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_IV_GEN, - len, iv)) - fatal("%s: EVP_CTRL_GCM_IV_GEN", __func__); - } else + len, iv)) + return SSH_ERR_LIBCRYPTO_ERROR; + } else memcpy(iv, cc->evp.iv, len); break; -#endif /* WITH_OPENSSL */ +#endif #ifdef WITH_SSH1 case SSH_CIPHER_3DES: - ssh1_3des_iv(&cc->evp, 0, iv, 24); - break; -#endif /* WITH_SSH1 */ + return ssh1_3des_iv(&cc->evp, 0, iv, 24); +#endif default: - fatal("%s: bad cipher %d", __func__, c->number); + return SSH_ERR_INVALID_ARGUMENT; } + return 0; } -void -cipher_set_keyiv(CipherContext *cc, u_char *iv) +int +cipher_set_keyiv(struct sshcipher_ctx *cc, const u_char *iv) { - const Cipher *c = cc->cipher; + const struct sshcipher *c = cc->cipher; #ifdef WITH_OPENSSL - int evplen = 0; + int evplen = 0; #endif if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) - return; + return 0; if ((cc->cipher->flags & CFLAG_NONE) != 0) - return; + return 0; switch (c->number) { #ifdef WITH_OPENSSL @@ -590,42 +582,37 @@ cipher_set_keyiv(CipherContext *cc, u_char *iv) case SSH_CIPHER_DES: case SSH_CIPHER_BLOWFISH: evplen = EVP_CIPHER_CTX_iv_length(&cc->evp); - if (evplen == 0) - return; -#ifdef USE_BUILTIN_RIJNDAEL - if (c->evptype == evp_rijndael) - ssh_rijndael_iv(&cc->evp, 1, iv, evplen); - else -#endif /* USE_BUILTIN_RIJNDAEL */ -#ifndef OPENSSL_HAVE_EVPCTR - if (c->evptype == evp_aes_128_ctr) - ssh_aes_ctr_iv(&cc->evp, 1, iv, evplen); - else -#endif /* OPENSSL_HAVE_EVPCTR */ + if (evplen <= 0) + return SSH_ERR_LIBCRYPTO_ERROR; if (cipher_authlen(c)) { + /* XXX iv arg is const, but EVP_CIPHER_CTX_ctrl isn't */ if (!EVP_CIPHER_CTX_ctrl(&cc->evp, - EVP_CTRL_GCM_SET_IV_FIXED, -1, iv)) - fatal("%s: EVP_CTRL_GCM_SET_IV_FIXED failed", - __func__); - } else - memcpy(cc->evp.iv, iv, evplen); + EVP_CTRL_GCM_SET_IV_FIXED, -1, (void *)iv)) + return SSH_ERR_LIBCRYPTO_ERROR; + } else + memcpy(cc->evp.iv, iv, evplen); break; -#endif /* WITH_OPENSSL */ +#endif #ifdef WITH_SSH1 case SSH_CIPHER_3DES: - ssh1_3des_iv(&cc->evp, 1, iv, 24); - break; -#endif /* WITH_SSH1 */ + return ssh1_3des_iv(&cc->evp, 1, (u_char *)iv, 24); +#endif default: - fatal("%s: bad cipher %d", __func__, c->number); + return SSH_ERR_INVALID_ARGUMENT; } + return 0; } +#ifdef WITH_OPENSSL +#define EVP_X_STATE(evp) (evp).cipher_data +#define EVP_X_STATE_LEN(evp) (evp).cipher->ctx_size +#endif + int -cipher_get_keycontext(const CipherContext *cc, u_char *dat) +cipher_get_keycontext(const struct sshcipher_ctx *cc, u_char *dat) { #ifdef WITH_OPENSSL - const Cipher *c = cc->cipher; + const struct sshcipher *c = cc->cipher; int plen = 0; if (c->evptype == EVP_rc4) { @@ -636,15 +623,15 @@ cipher_get_keycontext(const CipherContext *cc, u_char *dat) } return (plen); #else - return (0); + return 0; #endif } void -cipher_set_keycontext(CipherContext *cc, u_char *dat) +cipher_set_keycontext(struct sshcipher_ctx *cc, const u_char *dat) { #ifdef WITH_OPENSSL - const Cipher *c = cc->cipher; + const struct sshcipher *c = cc->cipher; int plen; if (c->evptype == EVP_rc4) { diff --git a/cipher.h b/cipher.h index 5aa778f14..de74c1e3b 100644 --- a/cipher.h +++ b/cipher.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cipher.h,v 1.45 2014/04/29 18:01:49 markus Exp $ */ +/* $OpenBSD: cipher.h,v 1.46 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen @@ -37,6 +37,7 @@ #ifndef CIPHER_H #define CIPHER_H +#include #include #include "cipher-chachapoly.h" #include "cipher-aesctr.h" @@ -61,45 +62,47 @@ #define CIPHER_ENCRYPT 1 #define CIPHER_DECRYPT 0 -typedef struct Cipher Cipher; -typedef struct CipherContext CipherContext; - -struct Cipher; -struct CipherContext { +struct sshcipher; +struct sshcipher_ctx { int plaintext; int encrypt; EVP_CIPHER_CTX evp; struct chachapoly_ctx cp_ctx; /* XXX union with evp? */ struct aesctr_ctx ac_ctx; /* XXX union with evp? */ - const Cipher *cipher; + const struct sshcipher *cipher; }; +typedef struct sshcipher Cipher ; +typedef struct sshcipher_ctx CipherContext ; + u_int cipher_mask_ssh1(int); -const Cipher *cipher_by_name(const char *); -const Cipher *cipher_by_number(int); +const struct sshcipher *cipher_by_name(const char *); +const struct sshcipher *cipher_by_number(int); int cipher_number(const char *); char *cipher_name(int); int ciphers_valid(const char *); char *cipher_alg_list(char, int); -void cipher_init(CipherContext *, const Cipher *, const u_char *, u_int, - const u_char *, u_int, int); -int cipher_crypt(CipherContext *, u_int, u_char *, const u_char *, +int cipher_init(struct sshcipher_ctx *, const struct sshcipher *, + const u_char *, u_int, const u_char *, u_int, int); +const char* cipher_warning_message(const struct sshcipher_ctx *); +int cipher_crypt(struct sshcipher_ctx *, u_int, u_char *, const u_char *, u_int, u_int, u_int); -int cipher_get_length(CipherContext *, u_int *, u_int, +int cipher_get_length(struct sshcipher_ctx *, u_int *, u_int, const u_char *, u_int); -void cipher_cleanup(CipherContext *); -void cipher_set_key_string(CipherContext *, const Cipher *, const char *, int); -u_int cipher_blocksize(const Cipher *); -u_int cipher_keylen(const Cipher *); -u_int cipher_seclen(const Cipher *); -u_int cipher_authlen(const Cipher *); -u_int cipher_ivlen(const Cipher *); -u_int cipher_is_cbc(const Cipher *); +int cipher_cleanup(struct sshcipher_ctx *); +int cipher_set_key_string(struct sshcipher_ctx *, const struct sshcipher *, + const char *, int); +u_int cipher_blocksize(const struct sshcipher *); +u_int cipher_keylen(const struct sshcipher *); +u_int cipher_seclen(const struct sshcipher *); +u_int cipher_authlen(const struct sshcipher *); +u_int cipher_ivlen(const struct sshcipher *); +u_int cipher_is_cbc(const struct sshcipher *); -u_int cipher_get_number(const Cipher *); -void cipher_get_keyiv(CipherContext *, u_char *, u_int); -void cipher_set_keyiv(CipherContext *, u_char *); -int cipher_get_keyiv_len(const CipherContext *); -int cipher_get_keycontext(const CipherContext *, u_char *); -void cipher_set_keycontext(CipherContext *, u_char *); +u_int cipher_get_number(const struct sshcipher *); +int cipher_get_keyiv(struct sshcipher_ctx *, u_char *, u_int); +int cipher_set_keyiv(struct sshcipher_ctx *, const u_char *); +int cipher_get_keyiv_len(const struct sshcipher_ctx *); +int cipher_get_keycontext(const struct sshcipher_ctx *, u_char *); +void cipher_set_keycontext(struct sshcipher_ctx *, const u_char *); #endif /* CIPHER_H */ diff --git a/digest-libc.c b/digest-libc.c index 1804b0698..1b4423a05 100644 --- a/digest-libc.c +++ b/digest-libc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: digest-libc.c,v 1.2 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: digest-libc.c,v 1.3 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2013 Damien Miller * Copyright (c) 2014 Markus Friedl. All rights reserved. @@ -28,7 +28,8 @@ #include #include -#include "buffer.h" +#include "ssherr.h" +#include "sshbuf.h" #include "digest.h" typedef void md_init_fn(void *mdctx); @@ -164,7 +165,7 @@ ssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to) const struct ssh_digest *digest = ssh_digest_by_alg(from->alg); if (digest == NULL || from->alg != to->alg) - return -1; + return SSH_ERR_INVALID_ARGUMENT; memcpy(to->mdctx, from->mdctx, digest->ctx_len); return 0; } @@ -175,15 +176,15 @@ ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen) const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg); if (digest == NULL) - return -1; + return SSH_ERR_INVALID_ARGUMENT; digest->md_update(ctx->mdctx, m, mlen); return 0; } int -ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const Buffer *b) +ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b) { - return ssh_digest_update(ctx, buffer_ptr(b), buffer_len(b)); + return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b)); } int @@ -192,11 +193,11 @@ ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen) const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg); if (digest == NULL) - return -1; + return SSH_ERR_INVALID_ARGUMENT; if (dlen > UINT_MAX) - return -1; + return SSH_ERR_INVALID_ARGUMENT; if (dlen < digest->digest_len) /* No truncation allowed */ - return -1; + return SSH_ERR_INVALID_ARGUMENT; digest->md_final(d, ctx->mdctx); return 0; } @@ -223,16 +224,16 @@ ssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen) struct ssh_digest_ctx *ctx = ssh_digest_start(alg); if (ctx == NULL) - return -1; + return SSH_ERR_INVALID_ARGUMENT; if (ssh_digest_update(ctx, m, mlen) != 0 || ssh_digest_final(ctx, d, dlen) != 0) - return -1; + return SSH_ERR_INVALID_ARGUMENT; ssh_digest_free(ctx); return 0; } int -ssh_digest_buffer(int alg, const Buffer *b, u_char *d, size_t dlen) +ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen) { - return ssh_digest_memory(alg, buffer_ptr(b), buffer_len(b), d, dlen); + return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen); } diff --git a/digest-openssl.c b/digest-openssl.c index 863d37d03..de0380135 100644 --- a/digest-openssl.c +++ b/digest-openssl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: digest-openssl.c,v 1.2 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: digest-openssl.c,v 1.3 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2013 Damien Miller * @@ -26,8 +26,9 @@ #include "openbsd-compat/openssl-compat.h" -#include "buffer.h" +#include "sshbuf.h" #include "digest.h" +#include "ssherr.h" struct ssh_digest_ctx { int alg; @@ -98,9 +99,11 @@ ssh_digest_start(int alg) int ssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to) { + if (from->alg != to->alg) + return SSH_ERR_INVALID_ARGUMENT; /* we have bcopy-style order while openssl has memcpy-style */ if (!EVP_MD_CTX_copy_ex(&to->mdctx, &from->mdctx)) - return -1; + return SSH_ERR_LIBCRYPTO_ERROR; return 0; } @@ -108,14 +111,14 @@ int ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen) { if (EVP_DigestUpdate(&ctx->mdctx, m, mlen) != 1) - return -1; + return SSH_ERR_LIBCRYPTO_ERROR; return 0; } int -ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const Buffer *b) +ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b) { - return ssh_digest_update(ctx, buffer_ptr(b), buffer_len(b)); + return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b)); } int @@ -125,13 +128,13 @@ ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen) u_int l = dlen; if (dlen > UINT_MAX) - return -1; + return SSH_ERR_INVALID_ARGUMENT; if (dlen < digest->digest_len) /* No truncation allowed */ - return -1; + return SSH_ERR_INVALID_ARGUMENT; if (EVP_DigestFinal_ex(&ctx->mdctx, d, &l) != 1) - return -1; + return SSH_ERR_LIBCRYPTO_ERROR; if (l != digest->digest_len) /* sanity */ - return -1; + return SSH_ERR_INTERNAL_ERROR; return 0; } @@ -149,18 +152,19 @@ int ssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen) { struct ssh_digest_ctx *ctx = ssh_digest_start(alg); + int r; if (ctx == NULL) - return -1; - if (ssh_digest_update(ctx, m, mlen) != 0 || - ssh_digest_final(ctx, d, dlen) != 0) - return -1; + return SSH_ERR_INVALID_ARGUMENT; + if ((r = ssh_digest_update(ctx, m, mlen) != 0) || + (r = ssh_digest_final(ctx, d, dlen) != 0)) + return r; ssh_digest_free(ctx); return 0; } int -ssh_digest_buffer(int alg, const Buffer *b, u_char *d, size_t dlen) +ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen) { - return ssh_digest_memory(alg, buffer_ptr(b), buffer_len(b), d, dlen); + return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen); } diff --git a/digest.h b/digest.h index 04295e277..edf6c87da 100644 --- a/digest.h +++ b/digest.h @@ -1,4 +1,4 @@ -/* $OpenBSD: digest.h,v 1.4 2014/05/02 03:27:54 djm Exp $ */ +/* $OpenBSD: digest.h,v 1.5 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2013 Damien Miller * @@ -47,14 +47,15 @@ int ssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen) __attribute__((__bounded__(__buffer__, 2, 3))) __attribute__((__bounded__(__buffer__, 4, 5))); -int ssh_digest_buffer(int alg, const Buffer *b, u_char *d, size_t dlen) +int ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen) __attribute__((__bounded__(__buffer__, 3, 4))); /* Update API */ struct ssh_digest_ctx *ssh_digest_start(int alg); int ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen) __attribute__((__bounded__(__buffer__, 2, 3))); -int ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const Buffer *b); +int ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, + const struct sshbuf *b); int ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen) __attribute__((__bounded__(__buffer__, 2, 3))); void ssh_digest_free(struct ssh_digest_ctx *ctx); diff --git a/dns.c b/dns.c index c780f8ba7..c4d073cf5 100644 --- a/dns.c +++ b/dns.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dns.c,v 1.30 2014/04/20 09:24:26 logan Exp $ */ +/* $OpenBSD: dns.c,v 1.31 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include "xmalloc.h" #include "key.h" diff --git a/entropy.c b/entropy.c index e1a8e142b..1e9d52ac4 100644 --- a/entropy.c +++ b/entropy.c @@ -43,6 +43,8 @@ #include #include +#include "openbsd-compat/openssl-compat.h" + #include "ssh.h" #include "misc.h" #include "xmalloc.h" diff --git a/hmac.h b/hmac.h index 05813906e..42b33d002 100644 --- a/hmac.h +++ b/hmac.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hmac.h,v 1.8 2014/05/02 03:27:54 djm Exp $ */ +/* $OpenBSD: hmac.h,v 1.9 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2014 Markus Friedl. All rights reserved. * @@ -21,6 +21,7 @@ /* Returns the algorithm's digest length in bytes or 0 for invalid algorithm */ size_t ssh_hmac_bytes(int alg); +struct sshbuf; struct ssh_hmac_ctx; struct ssh_hmac_ctx *ssh_hmac_start(int alg); @@ -29,7 +30,7 @@ int ssh_hmac_init(struct ssh_hmac_ctx *ctx, const void *key, size_t klen) __attribute__((__bounded__(__buffer__, 2, 3))); int ssh_hmac_update(struct ssh_hmac_ctx *ctx, const void *m, size_t mlen) __attribute__((__bounded__(__buffer__, 2, 3))); -int ssh_hmac_update_buffer(struct ssh_hmac_ctx *ctx, const Buffer *b); +int ssh_hmac_update_buffer(struct ssh_hmac_ctx *ctx, const struct sshbuf *b); int ssh_hmac_final(struct ssh_hmac_ctx *ctx, u_char *d, size_t dlen) __attribute__((__bounded__(__buffer__, 2, 3))); void ssh_hmac_free(struct ssh_hmac_ctx *ctx); diff --git a/hostfile.c b/hostfile.c index 91741cab8..ee2daf45f 100644 --- a/hostfile.c +++ b/hostfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.c,v 1.56 2014/04/29 18:01:49 markus Exp $ */ +/* $OpenBSD: hostfile.c,v 1.57 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -47,6 +47,7 @@ #include #include #include +#include #include "xmalloc.h" #include "match.h" diff --git a/key.c b/key.c index e8fc5b1b8..75327d491 100644 --- a/key.c +++ b/key.c @@ -1,2694 +1,469 @@ -/* $OpenBSD: key.c,v 1.117 2014/04/29 18:01:49 markus Exp $ */ +/* $OpenBSD: key.c,v 1.119 2014/06/30 12:54:39 djm Exp $ */ /* - * read_bignum(): - * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - * - * - * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. - * Copyright (c) 2008 Alexander von Gernler. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * placed in the public domain */ #include "includes.h" #include #include - -#include "crypto_api.h" - -#include -#include - +#include #include #include -#include -#include "xmalloc.h" +#define SSH_KEY_NO_DEFINE #include "key.h" -#include "rsa.h" -#include "uuencode.h" -#include "buffer.h" -#include "log.h" -#include "misc.h" -#include "ssh2.h" -#include "digest.h" - -static int to_blob(const Key *, u_char **, u_int *, int); -static Key *key_from_blob2(const u_char *, u_int, int); - -static struct KeyCert * -cert_new(void) -{ - struct KeyCert *cert; - - cert = xcalloc(1, sizeof(*cert)); - buffer_init(&cert->certblob); - buffer_init(&cert->critical); - buffer_init(&cert->extensions); - cert->key_id = NULL; - cert->principals = NULL; - cert->signature_key = NULL; - return cert; -} - -Key * -key_new(int type) -{ - Key *k; -#ifdef WITH_OPENSSL - RSA *rsa; - DSA *dsa; -#endif - - k = xcalloc(1, sizeof(*k)); - k->type = type; - k->ecdsa = NULL; - k->ecdsa_nid = -1; - k->dsa = NULL; - k->rsa = NULL; - k->cert = NULL; - k->ed25519_sk = NULL; - k->ed25519_pk = NULL; - switch (k->type) { -#ifdef WITH_OPENSSL - case KEY_RSA1: - case KEY_RSA: - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - if ((rsa = RSA_new()) == NULL) - fatal("key_new: RSA_new failed"); - if ((rsa->n = BN_new()) == NULL) - fatal("key_new: BN_new failed"); - if ((rsa->e = BN_new()) == NULL) - fatal("key_new: BN_new failed"); - k->rsa = rsa; - break; - case KEY_DSA: - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - if ((dsa = DSA_new()) == NULL) - fatal("key_new: DSA_new failed"); - if ((dsa->p = BN_new()) == NULL) - fatal("key_new: BN_new failed"); - if ((dsa->q = BN_new()) == NULL) - fatal("key_new: BN_new failed"); - if ((dsa->g = BN_new()) == NULL) - fatal("key_new: BN_new failed"); - if ((dsa->pub_key = BN_new()) == NULL) - fatal("key_new: BN_new failed"); - k->dsa = dsa; - break; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: - case KEY_ECDSA_CERT: - /* Cannot do anything until we know the group */ - break; -#endif -#endif - case KEY_ED25519: - case KEY_ED25519_CERT: - /* no need to prealloc */ - break; - case KEY_UNSPEC: - break; - default: - fatal("key_new: bad key type %d", k->type); - break; - } - - if (key_is_cert(k)) - k->cert = cert_new(); - return k; -} +#include "compat.h" +#include "sshkey.h" +#include "ssherr.h" +#include "log.h" +#include "authfile.h" void key_add_private(Key *k) { - switch (k->type) { -#ifdef WITH_OPENSSL - case KEY_RSA1: - case KEY_RSA: - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - if ((k->rsa->d = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); - if ((k->rsa->iqmp = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); - if ((k->rsa->q = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); - if ((k->rsa->p = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); - if ((k->rsa->dmq1 = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); - if ((k->rsa->dmp1 = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); - break; - case KEY_DSA: - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - if ((k->dsa->priv_key = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); - break; - case KEY_ECDSA: - case KEY_ECDSA_CERT: - /* Cannot do anything until we know the group */ - break; -#endif - case KEY_ED25519: - case KEY_ED25519_CERT: - /* no need to prealloc */ - break; - case KEY_UNSPEC: - break; - default: - break; - } + int r; + + if ((r = sshkey_add_private(k)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); } Key * key_new_private(int type) { - Key *k = key_new(type); - - key_add_private(k); - return k; -} - -static void -cert_free(struct KeyCert *cert) -{ - u_int i; - - buffer_free(&cert->certblob); - buffer_free(&cert->critical); - buffer_free(&cert->extensions); - free(cert->key_id); - for (i = 0; i < cert->nprincipals; i++) - free(cert->principals[i]); - free(cert->principals); - if (cert->signature_key != NULL) - key_free(cert->signature_key); - free(cert); -} - -void -key_free(Key *k) -{ - if (k == NULL) - fatal("key_free: key is NULL"); - switch (k->type) { -#ifdef WITH_OPENSSL - case KEY_RSA1: - case KEY_RSA: - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - if (k->rsa != NULL) - RSA_free(k->rsa); - k->rsa = NULL; - break; - case KEY_DSA: - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - if (k->dsa != NULL) - DSA_free(k->dsa); - k->dsa = NULL; - break; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: - case KEY_ECDSA_CERT: - if (k->ecdsa != NULL) - EC_KEY_free(k->ecdsa); - k->ecdsa = NULL; - break; -#endif - case KEY_ED25519: - case KEY_ED25519_CERT: - if (k->ed25519_pk) { - explicit_bzero(k->ed25519_pk, ED25519_PK_SZ); - free(k->ed25519_pk); - k->ed25519_pk = NULL; - } - if (k->ed25519_sk) { - explicit_bzero(k->ed25519_sk, ED25519_SK_SZ); - free(k->ed25519_sk); - k->ed25519_sk = NULL; - } - break; - case KEY_UNSPEC: - break; - default: - fatal("key_free: bad key type %d", k->type); - break; - } - if (key_is_cert(k)) { - if (k->cert != NULL) - cert_free(k->cert); - k->cert = NULL; - } - - free(k); -} - -static int -cert_compare(struct KeyCert *a, struct KeyCert *b) -{ - if (a == NULL && b == NULL) - return 1; - if (a == NULL || b == NULL) - return 0; - if (buffer_len(&a->certblob) != buffer_len(&b->certblob)) - return 0; - if (timingsafe_bcmp(buffer_ptr(&a->certblob), buffer_ptr(&b->certblob), - buffer_len(&a->certblob)) != 0) - return 0; - return 1; -} - -/* - * Compare public portions of key only, allowing comparisons between - * certificates and plain keys too. - */ -int -key_equal_public(const Key *a, const Key *b) -{ -#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) - BN_CTX *bnctx; -#endif - - if (a == NULL || b == NULL || - key_type_plain(a->type) != key_type_plain(b->type)) - return 0; - - switch (a->type) { -#ifdef WITH_OPENSSL - case KEY_RSA1: - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - case KEY_RSA: - return a->rsa != NULL && b->rsa != NULL && - BN_cmp(a->rsa->e, b->rsa->e) == 0 && - BN_cmp(a->rsa->n, b->rsa->n) == 0; - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - case KEY_DSA: - return a->dsa != NULL && b->dsa != NULL && - BN_cmp(a->dsa->p, b->dsa->p) == 0 && - BN_cmp(a->dsa->q, b->dsa->q) == 0 && - BN_cmp(a->dsa->g, b->dsa->g) == 0 && - BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA_CERT: - case KEY_ECDSA: - if (a->ecdsa == NULL || b->ecdsa == NULL || - EC_KEY_get0_public_key(a->ecdsa) == NULL || - EC_KEY_get0_public_key(b->ecdsa) == NULL) - return 0; - if ((bnctx = BN_CTX_new()) == NULL) - fatal("%s: BN_CTX_new failed", __func__); - if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa), - EC_KEY_get0_group(b->ecdsa), bnctx) != 0 || - EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa), - EC_KEY_get0_public_key(a->ecdsa), - EC_KEY_get0_public_key(b->ecdsa), bnctx) != 0) { - BN_CTX_free(bnctx); - return 0; - } - BN_CTX_free(bnctx); - return 1; -#endif /* OPENSSL_HAS_ECC */ -#endif /* WITH_OPENSSL */ - case KEY_ED25519: - case KEY_ED25519_CERT: - return a->ed25519_pk != NULL && b->ed25519_pk != NULL && - memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0; - default: - fatal("key_equal: bad key type %d", a->type); - } - /* NOTREACHED */ -} + Key *ret = NULL; -int -key_equal(const Key *a, const Key *b) -{ - if (a == NULL || b == NULL || a->type != b->type) - return 0; - if (key_is_cert(a)) { - if (!cert_compare(a->cert, b->cert)) - return 0; - } - return key_equal_public(a, b); + if ((ret = sshkey_new_private(type)) == NULL) + fatal("%s: failed", __func__); + return ret; } u_char* key_fingerprint_raw(const Key *k, enum fp_type dgst_type, u_int *dgst_raw_length) { - u_char *blob = NULL; - u_char *retval = NULL; - u_int len = 0; - int hash_alg = -1; -#ifdef WITH_OPENSSL - int nlen, elen; -#endif - - *dgst_raw_length = 0; - - /* XXX switch to DIGEST_* directly? */ - switch (dgst_type) { - case SSH_FP_MD5: - hash_alg = SSH_DIGEST_MD5; - break; - case SSH_FP_SHA1: - hash_alg = SSH_DIGEST_SHA1; - break; - case SSH_FP_SHA256: - hash_alg = SSH_DIGEST_SHA256; - break; - default: - fatal("%s: bad digest type %d", __func__, dgst_type); - } - switch (k->type) { -#ifdef WITH_OPENSSL - case KEY_RSA1: - nlen = BN_num_bytes(k->rsa->n); - elen = BN_num_bytes(k->rsa->e); - len = nlen + elen; - blob = xmalloc(len); - BN_bn2bin(k->rsa->n, blob); - BN_bn2bin(k->rsa->e, blob + nlen); - break; - case KEY_DSA: - case KEY_ECDSA: - case KEY_RSA: -#endif - case KEY_ED25519: - key_to_blob(k, &blob, &len); - break; -#ifdef WITH_OPENSSL - case KEY_DSA_CERT_V00: - case KEY_RSA_CERT_V00: - case KEY_DSA_CERT: - case KEY_ECDSA_CERT: - case KEY_RSA_CERT: -#endif - case KEY_ED25519_CERT: - /* We want a fingerprint of the _key_ not of the cert */ - to_blob(k, &blob, &len, 1); - break; - case KEY_UNSPEC: - return retval; - default: - fatal("%s: bad key type %d", __func__, k->type); - break; - } - if (blob != NULL) { - retval = xmalloc(SSH_DIGEST_MAX_LENGTH); - if ((ssh_digest_memory(hash_alg, blob, len, - retval, SSH_DIGEST_MAX_LENGTH)) != 0) - fatal("%s: digest_memory failed", __func__); - explicit_bzero(blob, len); - free(blob); - *dgst_raw_length = ssh_digest_bytes(hash_alg); - } else { - fatal("%s: blob is null", __func__); - } - return retval; -} - -static char * -key_fingerprint_hex(u_char *dgst_raw, u_int dgst_raw_len) -{ - char *retval; - u_int i; - - retval = xcalloc(1, dgst_raw_len * 3 + 1); - for (i = 0; i < dgst_raw_len; i++) { - char hex[4]; - snprintf(hex, sizeof(hex), "%02x:", dgst_raw[i]); - strlcat(retval, hex, dgst_raw_len * 3 + 1); - } - - /* Remove the trailing ':' character */ - retval[(dgst_raw_len * 3) - 1] = '\0'; - return retval; -} - -static char * -key_fingerprint_bubblebabble(u_char *dgst_raw, u_int dgst_raw_len) -{ - char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' }; - char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm', - 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' }; - u_int i, j = 0, rounds, seed = 1; - char *retval; - - rounds = (dgst_raw_len / 2) + 1; - retval = xcalloc((rounds * 6), sizeof(char)); - retval[j++] = 'x'; - for (i = 0; i < rounds; i++) { - u_int idx0, idx1, idx2, idx3, idx4; - if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) { - idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) + - seed) % 6; - idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15; - idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) + - (seed / 6)) % 6; - retval[j++] = vowels[idx0]; - retval[j++] = consonants[idx1]; - retval[j++] = vowels[idx2]; - if ((i + 1) < rounds) { - idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15; - idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15; - retval[j++] = consonants[idx3]; - retval[j++] = '-'; - retval[j++] = consonants[idx4]; - seed = ((seed * 5) + - ((((u_int)(dgst_raw[2 * i])) * 7) + - ((u_int)(dgst_raw[(2 * i) + 1])))) % 36; - } - } else { - idx0 = seed % 6; - idx1 = 16; - idx2 = seed / 6; - retval[j++] = vowels[idx0]; - retval[j++] = consonants[idx1]; - retval[j++] = vowels[idx2]; - } - } - retval[j++] = 'x'; - retval[j++] = '\0'; - return retval; -} - -/* - * Draw an ASCII-Art representing the fingerprint so human brain can - * profit from its built-in pattern recognition ability. - * This technique is called "random art" and can be found in some - * scientific publications like this original paper: - * - * "Hash Visualization: a New Technique to improve Real-World Security", - * Perrig A. and Song D., 1999, International Workshop on Cryptographic - * Techniques and E-Commerce (CrypTEC '99) - * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf - * - * The subject came up in a talk by Dan Kaminsky, too. - * - * If you see the picture is different, the key is different. - * If the picture looks the same, you still know nothing. - * - * The algorithm used here is a worm crawling over a discrete plane, - * leaving a trace (augmenting the field) everywhere it goes. - * Movement is taken from dgst_raw 2bit-wise. Bumping into walls - * makes the respective movement vector be ignored for this turn. - * Graphs are not unambiguous, because circles in graphs can be - * walked in either direction. - */ - -/* - * Field sizes for the random art. Have to be odd, so the starting point - * can be in the exact middle of the picture, and FLDBASE should be >=8 . - * Else pictures would be too dense, and drawing the frame would - * fail, too, because the key type would not fit in anymore. - */ -#define FLDBASE 8 -#define FLDSIZE_Y (FLDBASE + 1) -#define FLDSIZE_X (FLDBASE * 2 + 1) -static char * -key_fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len, const Key *k) -{ - /* - * Chars to be used after each other every time the worm - * intersects with itself. Matter of taste. - */ - char *augmentation_string = " .o+=*BOX@%&#/^SE"; - char *retval, *p; - u_char field[FLDSIZE_X][FLDSIZE_Y]; - u_int i, b; - int x, y; - size_t len = strlen(augmentation_string) - 1; - - retval = xcalloc(1, (FLDSIZE_X + 3) * (FLDSIZE_Y + 2)); - - /* initialize field */ - memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); - x = FLDSIZE_X / 2; - y = FLDSIZE_Y / 2; - - /* process raw key */ - for (i = 0; i < dgst_raw_len; i++) { - int input; - /* each byte conveys four 2-bit move commands */ - input = dgst_raw[i]; - for (b = 0; b < 4; b++) { - /* evaluate 2 bit, rest is shifted later */ - x += (input & 0x1) ? 1 : -1; - y += (input & 0x2) ? 1 : -1; - - /* assure we are still in bounds */ - x = MAX(x, 0); - y = MAX(y, 0); - x = MIN(x, FLDSIZE_X - 1); - y = MIN(y, FLDSIZE_Y - 1); - - /* augment the field */ - if (field[x][y] < len - 2) - field[x][y]++; - input = input >> 2; - } - } - - /* mark starting point and end point*/ - field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1; - field[x][y] = len; - - /* fill in retval */ - snprintf(retval, FLDSIZE_X, "+--[%4s %4u]", key_type(k), key_size(k)); - p = strchr(retval, '\0'); - - /* output upper border */ - for (i = p - retval - 1; i < FLDSIZE_X; i++) - *p++ = '-'; - *p++ = '+'; - *p++ = '\n'; - - /* output content */ - for (y = 0; y < FLDSIZE_Y; y++) { - *p++ = '|'; - for (x = 0; x < FLDSIZE_X; x++) - *p++ = augmentation_string[MIN(field[x][y], len)]; - *p++ = '|'; - *p++ = '\n'; - } - - /* output lower border */ - *p++ = '+'; - for (i = 0; i < FLDSIZE_X; i++) - *p++ = '-'; - *p++ = '+'; - - return retval; -} - -char * -key_fingerprint(const Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep) -{ - char *retval = NULL; - u_char *dgst_raw; - u_int dgst_raw_len; - - dgst_raw = key_fingerprint_raw(k, dgst_type, &dgst_raw_len); - if (!dgst_raw) - fatal("key_fingerprint: null from key_fingerprint_raw()"); - switch (dgst_rep) { - case SSH_FP_HEX: - retval = key_fingerprint_hex(dgst_raw, dgst_raw_len); - break; - case SSH_FP_BUBBLEBABBLE: - retval = key_fingerprint_bubblebabble(dgst_raw, dgst_raw_len); - break; - case SSH_FP_RANDOMART: - retval = key_fingerprint_randomart(dgst_raw, dgst_raw_len, k); - break; - default: - fatal("key_fingerprint: bad digest representation %d", - dgst_rep); - break; - } - explicit_bzero(dgst_raw, dgst_raw_len); - free(dgst_raw); - return retval; -} - -#ifdef WITH_SSH1 -/* - * Reads a multiple-precision integer in decimal from the buffer, and advances - * the pointer. The integer must already be initialized. This function is - * permitted to modify the buffer. This leaves *cpp to point just beyond the - * last processed (and maybe modified) character. Note that this may modify - * the buffer containing the number. - */ -static int -read_bignum(char **cpp, BIGNUM * value) -{ - char *cp = *cpp; - int old; - - /* Skip any leading whitespace. */ - for (; *cp == ' ' || *cp == '\t'; cp++) - ; - - /* Check that it begins with a decimal digit. */ - if (*cp < '0' || *cp > '9') - return 0; - - /* Save starting position. */ - *cpp = cp; - - /* Move forward until all decimal digits skipped. */ - for (; *cp >= '0' && *cp <= '9'; cp++) - ; - - /* Save the old terminating character, and replace it by \0. */ - old = *cp; - *cp = 0; - - /* Parse the number. */ - if (BN_dec2bn(&value, *cpp) == 0) - return 0; - - /* Restore old terminating character. */ - *cp = old; - - /* Move beyond the number and return success. */ - *cpp = cp; - return 1; -} - -static int -write_bignum(FILE *f, BIGNUM *num) -{ - char *buf = BN_bn2dec(num); - if (buf == NULL) { - error("write_bignum: BN_bn2dec() failed"); - return 0; - } - fprintf(f, " %s", buf); - OPENSSL_free(buf); - return 1; + u_char *ret = NULL; + size_t dlen; + int r; + + if (dgst_raw_length != NULL) + *dgst_raw_length = 0; + if ((r = sshkey_fingerprint_raw(k, dgst_type, &ret, &dlen)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + if (dlen > INT_MAX) + fatal("%s: giant len %zu", __func__, dlen); + *dgst_raw_length = dlen; + return ret; } -#endif -/* returns 1 ok, -1 error */ int key_read(Key *ret, char **cpp) { - Key *k; - int success = -1; - char *cp, *space; - int len, n, type; - u_char *blob; -#ifdef WITH_SSH1 - u_int bits; -#endif -#ifdef OPENSSL_HAS_ECC - int curve_nid = -1; -#endif - - cp = *cpp; - - switch (ret->type) { - case KEY_RSA1: -#ifdef WITH_SSH1 - /* Get number of bits. */ - if (*cp < '0' || *cp > '9') - return -1; /* Bad bit count... */ - for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) - bits = 10 * bits + *cp - '0'; - if (bits == 0) - return -1; - *cpp = cp; - /* Get public exponent, public modulus. */ - if (!read_bignum(cpp, ret->rsa->e)) - return -1; - if (!read_bignum(cpp, ret->rsa->n)) - return -1; - /* validate the claimed number of bits */ - if ((u_int)BN_num_bits(ret->rsa->n) != bits) { - verbose("key_read: claimed key size %d does not match " - "actual %d", bits, BN_num_bits(ret->rsa->n)); - return -1; - } - success = 1; -#endif - break; - case KEY_UNSPEC: - case KEY_RSA: - case KEY_DSA: - case KEY_ECDSA: - case KEY_ED25519: - case KEY_DSA_CERT_V00: - case KEY_RSA_CERT_V00: - case KEY_DSA_CERT: - case KEY_ECDSA_CERT: - case KEY_RSA_CERT: - case KEY_ED25519_CERT: - space = strchr(cp, ' '); - if (space == NULL) { - debug3("key_read: missing whitespace"); - return -1; - } - *space = '\0'; - type = key_type_from_name(cp); -#ifdef OPENSSL_HAS_ECC - if (key_type_plain(type) == KEY_ECDSA && - (curve_nid = key_ecdsa_nid_from_name(cp)) == -1) { - debug("key_read: invalid curve"); - return -1; - } -#endif - *space = ' '; - if (type == KEY_UNSPEC) { - debug3("key_read: missing keytype"); - return -1; - } - cp = space+1; - if (*cp == '\0') { - debug3("key_read: short string"); - return -1; - } - if (ret->type == KEY_UNSPEC) { - ret->type = type; - } else if (ret->type != type) { - /* is a key, but different type */ - debug3("key_read: type mismatch"); - return -1; - } - len = 2*strlen(cp); - blob = xmalloc(len); - n = uudecode(cp, blob, len); - if (n < 0) { - error("key_read: uudecode %s failed", cp); - free(blob); - return -1; - } - k = key_from_blob(blob, (u_int)n); - free(blob); - if (k == NULL) { - error("key_read: key_from_blob %s failed", cp); - return -1; - } - if (k->type != type) { - error("key_read: type mismatch: encoding error"); - key_free(k); - return -1; - } -#ifdef OPENSSL_HAS_ECC - if (key_type_plain(type) == KEY_ECDSA && - curve_nid != k->ecdsa_nid) { - error("key_read: type mismatch: EC curve mismatch"); - key_free(k); - return -1; - } -#endif -/*XXXX*/ - if (key_is_cert(ret)) { - if (!key_is_cert(k)) { - error("key_read: loaded key is not a cert"); - key_free(k); - return -1; - } - if (ret->cert != NULL) - cert_free(ret->cert); - ret->cert = k->cert; - k->cert = NULL; - } -#ifdef WITH_OPENSSL - if (key_type_plain(ret->type) == KEY_RSA) { - if (ret->rsa != NULL) - RSA_free(ret->rsa); - ret->rsa = k->rsa; - k->rsa = NULL; -#ifdef DEBUG_PK - RSA_print_fp(stderr, ret->rsa, 8); -#endif - } - if (key_type_plain(ret->type) == KEY_DSA) { - if (ret->dsa != NULL) - DSA_free(ret->dsa); - ret->dsa = k->dsa; - k->dsa = NULL; -#ifdef DEBUG_PK - DSA_print_fp(stderr, ret->dsa, 8); -#endif - } -#ifdef OPENSSL_HAS_ECC - if (key_type_plain(ret->type) == KEY_ECDSA) { - if (ret->ecdsa != NULL) - EC_KEY_free(ret->ecdsa); - ret->ecdsa = k->ecdsa; - ret->ecdsa_nid = k->ecdsa_nid; - k->ecdsa = NULL; - k->ecdsa_nid = -1; -#ifdef DEBUG_PK - key_dump_ec_key(ret->ecdsa); -#endif - } -#endif -#endif - if (key_type_plain(ret->type) == KEY_ED25519) { - free(ret->ed25519_pk); - ret->ed25519_pk = k->ed25519_pk; - k->ed25519_pk = NULL; -#ifdef DEBUG_PK - /* XXX */ -#endif - } - success = 1; -/*XXXX*/ - key_free(k); - if (success != 1) - break; - /* advance cp: skip whitespace and data */ - while (*cp == ' ' || *cp == '\t') - cp++; - while (*cp != '\0' && *cp != ' ' && *cp != '\t') - cp++; - *cpp = cp; - break; - default: - fatal("key_read: bad key type: %d", ret->type); - break; - } - return success; + return sshkey_read(ret, cpp) == 0 ? 1 : -1; } int key_write(const Key *key, FILE *f) { - int n, success = 0; -#ifdef WITH_SSH1 - u_int bits = 0; -#endif - u_int len; - u_char *blob; - char *uu; - - if (key_is_cert(key)) { - if (key->cert == NULL) { - error("%s: no cert data", __func__); - return 0; - } - if (buffer_len(&key->cert->certblob) == 0) { - error("%s: no signed certificate blob", __func__); - return 0; - } - } - - switch (key->type) { -#ifdef WITH_SSH1 - case KEY_RSA1: - if (key->rsa == NULL) - return 0; - /* size of modulus 'n' */ - bits = BN_num_bits(key->rsa->n); - fprintf(f, "%u", bits); - if (write_bignum(f, key->rsa->e) && - write_bignum(f, key->rsa->n)) - return 1; - error("key_write: failed for RSA key"); - return 0; -#endif -#ifdef WITH_OPENSSL - case KEY_DSA: - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - if (key->dsa == NULL) - return 0; - break; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: - case KEY_ECDSA_CERT: - if (key->ecdsa == NULL) - return 0; - break; -#endif - case KEY_RSA: - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - if (key->rsa == NULL) - return 0; - break; -#endif - case KEY_ED25519: - case KEY_ED25519_CERT: - if (key->ed25519_pk == NULL) - return 0; - break; - default: - return 0; - } - - key_to_blob(key, &blob, &len); - uu = xmalloc(2*len); - n = uuencode(blob, len, uu, 2*len); - if (n > 0) { - fprintf(f, "%s %s", key_ssh_name(key), uu); - success = 1; - } - free(blob); - free(uu); - - return success; -} - -const char * -key_cert_type(const Key *k) -{ - switch (k->cert->type) { - case SSH2_CERT_TYPE_USER: - return "user"; - case SSH2_CERT_TYPE_HOST: - return "host"; - default: - return "unknown"; - } -} - -struct keytype { - char *name; - char *shortname; - int type; - int nid; - int cert; -}; -static const struct keytype keytypes[] = { -#ifdef WITH_OPENSSL -#ifdef WITH_SSH1 - { NULL, "RSA1", KEY_RSA1, 0, 0 }, -#endif - { "ssh-rsa", "RSA", KEY_RSA, 0, 0 }, - { "ssh-dss", "DSA", KEY_DSA, 0, 0 }, -#ifdef OPENSSL_HAS_ECC - { "ecdsa-sha2-nistp256", "ECDSA", KEY_ECDSA, NID_X9_62_prime256v1, 0 }, - { "ecdsa-sha2-nistp384", "ECDSA", KEY_ECDSA, NID_secp384r1, 0 }, -# ifdef OPENSSL_HAS_NISTP521 - { "ecdsa-sha2-nistp521", "ECDSA", KEY_ECDSA, NID_secp521r1, 0 }, -# endif -#endif /* OPENSSL_HAS_ECC */ - { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", KEY_RSA_CERT, 0, 1 }, - { "ssh-dss-cert-v01@openssh.com", "DSA-CERT", KEY_DSA_CERT, 0, 1 }, -#ifdef OPENSSL_HAS_ECC - { "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-CERT", - KEY_ECDSA_CERT, NID_X9_62_prime256v1, 1 }, - { "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT", - KEY_ECDSA_CERT, NID_secp384r1, 1 }, -# ifdef OPENSSL_HAS_NISTP521 - { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT", - KEY_ECDSA_CERT, NID_secp521r1, 1 }, -# endif -#endif /* OPENSSL_HAS_ECC */ - { "ssh-rsa-cert-v00@openssh.com", "RSA-CERT-V00", - KEY_RSA_CERT_V00, 0, 1 }, - { "ssh-dss-cert-v00@openssh.com", "DSA-CERT-V00", - KEY_DSA_CERT_V00, 0, 1 }, -#endif - { "ssh-ed25519", "ED25519", KEY_ED25519, 0, 0 }, - { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", - KEY_ED25519_CERT, 0, 1 }, - { NULL, NULL, -1, -1, 0 } -}; - -const char * -key_type(const Key *k) -{ - const struct keytype *kt; - - for (kt = keytypes; kt->type != -1; kt++) { - if (kt->type == k->type) - return kt->shortname; - } - return "unknown"; -} - -static const char * -key_ssh_name_from_type_nid(int type, int nid) -{ - const struct keytype *kt; - - for (kt = keytypes; kt->type != -1; kt++) { - if (kt->type == type && (kt->nid == 0 || kt->nid == nid)) - return kt->name; - } - return "ssh-unknown"; -} - -const char * -key_ssh_name(const Key *k) -{ - return key_ssh_name_from_type_nid(k->type, k->ecdsa_nid); -} - -const char * -key_ssh_name_plain(const Key *k) -{ - return key_ssh_name_from_type_nid(key_type_plain(k->type), - k->ecdsa_nid); -} - -int -key_type_from_name(char *name) -{ - const struct keytype *kt; - - for (kt = keytypes; kt->type != -1; kt++) { - /* Only allow shortname matches for plain key types */ - if ((kt->name != NULL && strcmp(name, kt->name) == 0) || - (!kt->cert && strcasecmp(kt->shortname, name) == 0)) - return kt->type; - } - debug2("key_type_from_name: unknown key type '%s'", name); - return KEY_UNSPEC; -} - -int -key_ecdsa_nid_from_name(const char *name) -{ - const struct keytype *kt; - - for (kt = keytypes; kt->type != -1; kt++) { - if (kt->type != KEY_ECDSA && kt->type != KEY_ECDSA_CERT) - continue; - if (kt->name != NULL && strcmp(name, kt->name) == 0) - return kt->nid; - } - debug2("%s: unknown/non-ECDSA key type '%s'", __func__, name); - return -1; -} - -char * -key_alg_list(int certs_only, int plain_only) -{ - char *ret = NULL; - size_t nlen, rlen = 0; - const struct keytype *kt; - - for (kt = keytypes; kt->type != -1; kt++) { - if (kt->name == NULL) - continue; - if ((certs_only && !kt->cert) || (plain_only && kt->cert)) - continue; - if (ret != NULL) - ret[rlen++] = '\n'; - nlen = strlen(kt->name); - ret = xrealloc(ret, 1, rlen + nlen + 2); - memcpy(ret + rlen, kt->name, nlen + 1); - rlen += nlen; - } - return ret; -} - -int -key_type_is_cert(int type) -{ - const struct keytype *kt; - - for (kt = keytypes; kt->type != -1; kt++) { - if (kt->type == type) - return kt->cert; - } - return 0; -} - -static int -key_type_is_valid_ca(int type) -{ - switch (type) { - case KEY_RSA: - case KEY_DSA: - case KEY_ECDSA: - case KEY_ED25519: - return 1; - default: - return 0; - } -} - -u_int -key_size(const Key *k) -{ - switch (k->type) { -#ifdef WITH_OPENSSL - case KEY_RSA1: - case KEY_RSA: - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - return BN_num_bits(k->rsa->n); - case KEY_DSA: - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - return BN_num_bits(k->dsa->p); -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: - case KEY_ECDSA_CERT: - return key_curve_nid_to_bits(k->ecdsa_nid); -#endif -#endif - case KEY_ED25519: - return 256; /* XXX */ - } - return 0; -} - -#ifdef WITH_OPENSSL -static RSA * -rsa_generate_private_key(u_int bits) -{ - RSA *private = RSA_new(); - BIGNUM *f4 = BN_new(); - - if (private == NULL) - fatal("%s: RSA_new failed", __func__); - if (f4 == NULL) - fatal("%s: BN_new failed", __func__); - if (!BN_set_word(f4, RSA_F4)) - fatal("%s: BN_new failed", __func__); - if (!RSA_generate_key_ex(private, bits, f4, NULL)) - fatal("%s: key generation failed.", __func__); - BN_free(f4); - return private; -} - -static DSA* -dsa_generate_private_key(u_int bits) -{ - DSA *private = DSA_new(); - - if (private == NULL) - fatal("%s: DSA_new failed", __func__); - if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL, - NULL, NULL)) - fatal("%s: DSA_generate_parameters failed", __func__); - if (!DSA_generate_key(private)) - fatal("%s: DSA_generate_key failed.", __func__); - return private; -} - -int -key_ecdsa_bits_to_nid(int bits) -{ - switch (bits) { -#ifdef OPENSSL_HAS_ECC - case 256: - return NID_X9_62_prime256v1; - case 384: - return NID_secp384r1; -# ifdef OPENSSL_HAS_NISTP521 - case 521: - return NID_secp521r1; -# endif -#endif - default: - return -1; - } -} - -#ifdef OPENSSL_HAS_ECC -int -key_ecdsa_key_to_nid(EC_KEY *k) -{ - EC_GROUP *eg; - int nids[] = { - NID_X9_62_prime256v1, - NID_secp384r1, -# ifdef OPENSSL_HAS_NISTP521 - NID_secp521r1, -# endif - -1 - }; - int nid; - u_int i; - BN_CTX *bnctx; - const EC_GROUP *g = EC_KEY_get0_group(k); - - /* - * The group may be stored in a ASN.1 encoded private key in one of two - * ways: as a "named group", which is reconstituted by ASN.1 object ID - * or explicit group parameters encoded into the key blob. Only the - * "named group" case sets the group NID for us, but we can figure - * it out for the other case by comparing against all the groups that - * are supported. - */ - if ((nid = EC_GROUP_get_curve_name(g)) > 0) - return nid; - if ((bnctx = BN_CTX_new()) == NULL) - fatal("%s: BN_CTX_new() failed", __func__); - for (i = 0; nids[i] != -1; i++) { - if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL) - fatal("%s: EC_GROUP_new_by_curve_name failed", - __func__); - if (EC_GROUP_cmp(g, eg, bnctx) == 0) - break; - EC_GROUP_free(eg); - } - BN_CTX_free(bnctx); - debug3("%s: nid = %d", __func__, nids[i]); - if (nids[i] != -1) { - /* Use the group with the NID attached */ - EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE); - if (EC_KEY_set_group(k, eg) != 1) - fatal("%s: EC_KEY_set_group", __func__); - } - return nids[i]; + return sshkey_write(key, f) == 0 ? 1 : 0; } -static EC_KEY* -ecdsa_generate_private_key(u_int bits, int *nid) -{ - EC_KEY *private; - - if ((*nid = key_ecdsa_bits_to_nid(bits)) == -1) - fatal("%s: invalid key length", __func__); - if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL) - fatal("%s: EC_KEY_new_by_curve_name failed", __func__); - if (EC_KEY_generate_key(private) != 1) - fatal("%s: EC_KEY_generate_key failed", __func__); - EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE); - return private; -} -#endif /* OPENSSL_HAS_ECC */ -#endif /* WITH_OPENSSL */ - Key * key_generate(int type, u_int bits) { - Key *k = key_new(KEY_UNSPEC); - switch (type) { -#ifdef WITH_OPENSSL - case KEY_DSA: - k->dsa = dsa_generate_private_key(bits); - break; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: - k->ecdsa = ecdsa_generate_private_key(bits, &k->ecdsa_nid); - break; -#endif - case KEY_RSA: - case KEY_RSA1: - k->rsa = rsa_generate_private_key(bits); - break; -#endif - case KEY_RSA_CERT_V00: - case KEY_DSA_CERT_V00: - case KEY_RSA_CERT: - case KEY_DSA_CERT: - fatal("key_generate: cert keys cannot be generated directly"); -#endif - case KEY_ED25519: - k->ed25519_pk = xmalloc(ED25519_PK_SZ); - k->ed25519_sk = xmalloc(ED25519_SK_SZ); - crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk); - break; - default: - fatal("key_generate: unknown type %d", type); - } - k->type = type; - return k; + int r; + Key *ret = NULL; + + if ((r = sshkey_generate(type, bits, &ret)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + return ret; } void -key_cert_copy(const Key *from_key, struct Key *to_key) +key_cert_copy(const Key *from_key, Key *to_key) { - u_int i; - const struct KeyCert *from; - struct KeyCert *to; - - if (to_key->cert != NULL) { - cert_free(to_key->cert); - to_key->cert = NULL; - } + int r; - if ((from = from_key->cert) == NULL) - return; - - to = to_key->cert = cert_new(); - - buffer_append(&to->certblob, buffer_ptr(&from->certblob), - buffer_len(&from->certblob)); - - buffer_append(&to->critical, - buffer_ptr(&from->critical), buffer_len(&from->critical)); - buffer_append(&to->extensions, - buffer_ptr(&from->extensions), buffer_len(&from->extensions)); - - to->serial = from->serial; - to->type = from->type; - to->key_id = from->key_id == NULL ? NULL : xstrdup(from->key_id); - to->valid_after = from->valid_after; - to->valid_before = from->valid_before; - to->signature_key = from->signature_key == NULL ? - NULL : key_from_private(from->signature_key); - - to->nprincipals = from->nprincipals; - if (to->nprincipals > CERT_MAX_PRINCIPALS) - fatal("%s: nprincipals (%u) > CERT_MAX_PRINCIPALS (%u)", - __func__, to->nprincipals, CERT_MAX_PRINCIPALS); - if (to->nprincipals > 0) { - to->principals = xcalloc(from->nprincipals, - sizeof(*to->principals)); - for (i = 0; i < to->nprincipals; i++) - to->principals[i] = xstrdup(from->principals[i]); - } + if ((r = sshkey_cert_copy(from_key, to_key)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); } Key * key_from_private(const Key *k) { - Key *n = NULL; - switch (k->type) { -#ifdef WITH_OPENSSL - case KEY_DSA: - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - n = key_new(k->type); - if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || - (BN_copy(n->dsa->q, k->dsa->q) == NULL) || - (BN_copy(n->dsa->g, k->dsa->g) == NULL) || - (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL)) - fatal("key_from_private: BN_copy failed"); - break; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: - case KEY_ECDSA_CERT: - n = key_new(k->type); - n->ecdsa_nid = k->ecdsa_nid; - if ((n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL) - fatal("%s: EC_KEY_new_by_curve_name failed", __func__); - if (EC_KEY_set_public_key(n->ecdsa, - EC_KEY_get0_public_key(k->ecdsa)) != 1) - fatal("%s: EC_KEY_set_public_key failed", __func__); - break; -#endif - case KEY_RSA: - case KEY_RSA1: - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - n = key_new(k->type); - if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || - (BN_copy(n->rsa->e, k->rsa->e) == NULL)) - fatal("key_from_private: BN_copy failed"); - break; -#endif - case KEY_ED25519: - case KEY_ED25519_CERT: - n = key_new(k->type); - if (k->ed25519_pk != NULL) { - n->ed25519_pk = xmalloc(ED25519_PK_SZ); - memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ); - } - break; - default: - fatal("key_from_private: unknown type %d", k->type); - break; - } - if (key_is_cert(k)) - key_cert_copy(k, n); - return n; -} - -int -key_names_valid2(const char *names) -{ - char *s, *cp, *p; - - if (names == NULL || strcmp(names, "") == 0) - return 0; - s = cp = xstrdup(names); - for ((p = strsep(&cp, ",")); p && *p != '\0'; - (p = strsep(&cp, ","))) { - switch (key_type_from_name(p)) { - case KEY_RSA1: - case KEY_UNSPEC: - free(s); - return 0; - } - } - debug3("key names ok: [%s]", names); - free(s); - return 1; -} - -static int -cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen) -{ - u_char *principals, *critical, *exts, *sig_key, *sig; - u_int signed_len, plen, clen, sklen, slen, kidlen, elen; - Buffer tmp; - char *principal; - int ret = -1; - int v00 = key->type == KEY_DSA_CERT_V00 || - key->type == KEY_RSA_CERT_V00; - - buffer_init(&tmp); - - /* Copy the entire key blob for verification and later serialisation */ - buffer_append(&key->cert->certblob, blob, blen); - - elen = 0; /* Not touched for v00 certs */ - principals = exts = critical = sig_key = sig = NULL; - if ((!v00 && buffer_get_int64_ret(&key->cert->serial, b) != 0) || - buffer_get_int_ret(&key->cert->type, b) != 0 || - (key->cert->key_id = buffer_get_cstring_ret(b, &kidlen)) == NULL || - (principals = buffer_get_string_ret(b, &plen)) == NULL || - buffer_get_int64_ret(&key->cert->valid_after, b) != 0 || - buffer_get_int64_ret(&key->cert->valid_before, b) != 0 || - (critical = buffer_get_string_ret(b, &clen)) == NULL || - (!v00 && (exts = buffer_get_string_ret(b, &elen)) == NULL) || - (v00 && buffer_get_string_ptr_ret(b, NULL) == NULL) || /* nonce */ - buffer_get_string_ptr_ret(b, NULL) == NULL || /* reserved */ - (sig_key = buffer_get_string_ret(b, &sklen)) == NULL) { - error("%s: parse error", __func__); - goto out; - } - - /* Signature is left in the buffer so we can calculate this length */ - signed_len = buffer_len(&key->cert->certblob) - buffer_len(b); - - if ((sig = buffer_get_string_ret(b, &slen)) == NULL) { - error("%s: parse error", __func__); - goto out; - } - - if (key->cert->type != SSH2_CERT_TYPE_USER && - key->cert->type != SSH2_CERT_TYPE_HOST) { - error("Unknown certificate type %u", key->cert->type); - goto out; - } - - buffer_append(&tmp, principals, plen); - while (buffer_len(&tmp) > 0) { - if (key->cert->nprincipals >= CERT_MAX_PRINCIPALS) { - error("%s: Too many principals", __func__); - goto out; - } - if ((principal = buffer_get_cstring_ret(&tmp, &plen)) == NULL) { - error("%s: Principals data invalid", __func__); - goto out; - } - key->cert->principals = xrealloc(key->cert->principals, - key->cert->nprincipals + 1, sizeof(*key->cert->principals)); - key->cert->principals[key->cert->nprincipals++] = principal; - } - - buffer_clear(&tmp); - - buffer_append(&key->cert->critical, critical, clen); - buffer_append(&tmp, critical, clen); - /* validate structure */ - while (buffer_len(&tmp) != 0) { - if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL || - buffer_get_string_ptr_ret(&tmp, NULL) == NULL) { - error("%s: critical option data invalid", __func__); - goto out; - } - } - buffer_clear(&tmp); - - buffer_append(&key->cert->extensions, exts, elen); - buffer_append(&tmp, exts, elen); - /* validate structure */ - while (buffer_len(&tmp) != 0) { - if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL || - buffer_get_string_ptr_ret(&tmp, NULL) == NULL) { - error("%s: extension data invalid", __func__); - goto out; - } - } - buffer_clear(&tmp); - - if ((key->cert->signature_key = key_from_blob2(sig_key, sklen, 0)) - == NULL) { - error("%s: Signature key invalid", __func__); - goto out; - } - if (!key_type_is_valid_ca(key->cert->signature_key->type)) { - error("%s: Invalid signature key type %s (%d)", __func__, - key_type(key->cert->signature_key), - key->cert->signature_key->type); - goto out; - } + int r; + Key *ret = NULL; - switch (key_verify(key->cert->signature_key, sig, slen, - buffer_ptr(&key->cert->certblob), signed_len)) { - case 1: - ret = 0; - break; /* Good signature */ - case 0: - error("%s: Invalid signature on certificate", __func__); - goto out; - case -1: - error("%s: Certificate signature verification failed", - __func__); - goto out; - } - - out: - buffer_free(&tmp); - free(principals); - free(critical); - free(exts); - free(sig_key); - free(sig); + if ((r = sshkey_from_private(k, &ret)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); return ret; } -static Key * -key_from_blob2(const u_char *blob, u_int blen, int allow_cert) +static void +fatal_on_fatal_errors(int r, const char *func, int extra_fatal) { - Buffer b; - int rlen, type; - u_int len; - char *ktype = NULL, *curve = NULL; - u_char *pk = NULL; - Key *key = NULL; -#ifdef OPENSSL_HAS_ECC - EC_POINT *q = NULL; - int nid = -1; -#endif - -#ifdef DEBUG_PK - dump_base64(stderr, blob, blen); -#endif - buffer_init(&b); - buffer_append(&b, blob, blen); - if ((ktype = buffer_get_cstring_ret(&b, NULL)) == NULL) { - error("key_from_blob: can't read key type"); - goto out; - } - - type = key_type_from_name(ktype); -#ifdef OPENSSL_HAS_ECC - if (key_type_plain(type) == KEY_ECDSA) - nid = key_ecdsa_nid_from_name(ktype); -#endif - if (!allow_cert && key_type_is_cert(type)) { - error("key_from_blob: certificate not allowed in this context"); - goto out; - } - switch (type) { -#ifdef WITH_OPENSSL - case KEY_RSA_CERT: - (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */ - /* FALLTHROUGH */ - case KEY_RSA: - case KEY_RSA_CERT_V00: - key = key_new(type); - if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 || - buffer_get_bignum2_ret(&b, key->rsa->n) == -1) { - error("key_from_blob: can't read rsa key"); - goto badkey; - } -#ifdef DEBUG_PK - RSA_print_fp(stderr, key->rsa, 8); -#endif - break; - case KEY_DSA_CERT: - (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */ - /* FALLTHROUGH */ - case KEY_DSA: - case KEY_DSA_CERT_V00: - key = key_new(type); - if (buffer_get_bignum2_ret(&b, key->dsa->p) == -1 || - buffer_get_bignum2_ret(&b, key->dsa->q) == -1 || - buffer_get_bignum2_ret(&b, key->dsa->g) == -1 || - buffer_get_bignum2_ret(&b, key->dsa->pub_key) == -1) { - error("key_from_blob: can't read dsa key"); - goto badkey; - } -#ifdef DEBUG_PK - DSA_print_fp(stderr, key->dsa, 8); -#endif - break; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA_CERT: - (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */ - /* FALLTHROUGH */ - case KEY_ECDSA: - key = key_new(type); - key->ecdsa_nid = nid; - if ((curve = buffer_get_string_ret(&b, NULL)) == NULL) { - error("key_from_blob: can't read ecdsa curve"); - goto badkey; - } - if (key->ecdsa_nid != key_curve_name_to_nid(curve)) { - error("key_from_blob: ecdsa curve doesn't match type"); - goto badkey; - } - if (key->ecdsa != NULL) - EC_KEY_free(key->ecdsa); - if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) - == NULL) - fatal("key_from_blob: EC_KEY_new_by_curve_name failed"); - if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL) - fatal("key_from_blob: EC_POINT_new failed"); - if (buffer_get_ecpoint_ret(&b, EC_KEY_get0_group(key->ecdsa), - q) == -1) { - error("key_from_blob: can't read ecdsa key point"); - goto badkey; - } - if (key_ec_validate_public(EC_KEY_get0_group(key->ecdsa), - q) != 0) - goto badkey; - if (EC_KEY_set_public_key(key->ecdsa, q) != 1) - fatal("key_from_blob: EC_KEY_set_public_key failed"); -#ifdef DEBUG_PK - key_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q); -#endif - break; -#endif /* OPENSSL_HAS_ECC */ - case KEY_ED25519_CERT: - (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */ - /* FALLTHROUGH */ - case KEY_ED25519: - if ((pk = buffer_get_string_ret(&b, &len)) == NULL) { - error("key_from_blob: can't read ed25519 key"); - goto badkey; - } - if (len != ED25519_PK_SZ) { - error("key_from_blob: ed25519 len %d != %d", - len, ED25519_PK_SZ); - goto badkey; - } - key = key_new(type); - key->ed25519_pk = pk; - pk = NULL; - break; - case KEY_UNSPEC: - key = key_new(type); - break; - default: - error("key_from_blob: cannot handle type %s", ktype); - goto out; - } - if (key_is_cert(key) && cert_parse(&b, key, blob, blen) == -1) { - error("key_from_blob: can't parse cert data"); - goto badkey; - } - rlen = buffer_len(&b); - if (key != NULL && rlen != 0) - error("key_from_blob: remaining bytes in key blob %d", rlen); - out: - free(ktype); - free(curve); - free(pk); -#ifdef OPENSSL_HAS_ECC - if (q != NULL) - EC_POINT_free(q); -#endif - buffer_free(&b); - return key; - - badkey: - key_free(key); - key = NULL; - goto out; + if (r == SSH_ERR_INTERNAL_ERROR || + r == SSH_ERR_ALLOC_FAIL || + (extra_fatal != 0 && r == extra_fatal)) + fatal("%s: %s", func, ssh_err(r)); } Key * key_from_blob(const u_char *blob, u_int blen) { - return key_from_blob2(blob, blen, 1); + int r; + Key *ret = NULL; + + if ((r = sshkey_from_blob(blob, blen, &ret)) != 0) { + fatal_on_fatal_errors(r, __func__, 0); + error("%s: %s", __func__, ssh_err(r)); + return NULL; + } + return ret; } -static int -to_blob(const Key *key, u_char **blobp, u_int *lenp, int force_plain) +int +key_to_blob(const Key *key, u_char **blobp, u_int *lenp) { - Buffer b; - int len, type; + u_char *blob; + size_t blen; + int r; if (blobp != NULL) *blobp = NULL; if (lenp != NULL) *lenp = 0; - if (key == NULL) { - error("key_to_blob: key == NULL"); + if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) { + fatal_on_fatal_errors(r, __func__, 0); + error("%s: %s", __func__, ssh_err(r)); return 0; } - buffer_init(&b); - type = force_plain ? key_type_plain(key->type) : key->type; - switch (type) { -#ifdef WITH_OPENSSL - case KEY_DSA_CERT_V00: - case KEY_RSA_CERT_V00: - case KEY_DSA_CERT: - case KEY_ECDSA_CERT: - case KEY_RSA_CERT: -#endif - case KEY_ED25519_CERT: - /* Use the existing blob */ - buffer_append(&b, buffer_ptr(&key->cert->certblob), - buffer_len(&key->cert->certblob)); - break; -#ifdef WITH_OPENSSL - case KEY_DSA: - buffer_put_cstring(&b, - key_ssh_name_from_type_nid(type, key->ecdsa_nid)); - buffer_put_bignum2(&b, key->dsa->p); - buffer_put_bignum2(&b, key->dsa->q); - buffer_put_bignum2(&b, key->dsa->g); - buffer_put_bignum2(&b, key->dsa->pub_key); - break; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: - buffer_put_cstring(&b, - key_ssh_name_from_type_nid(type, key->ecdsa_nid)); - buffer_put_cstring(&b, key_curve_nid_to_name(key->ecdsa_nid)); - buffer_put_ecpoint(&b, EC_KEY_get0_group(key->ecdsa), - EC_KEY_get0_public_key(key->ecdsa)); - break; -#endif - case KEY_RSA: - buffer_put_cstring(&b, - key_ssh_name_from_type_nid(type, key->ecdsa_nid)); - buffer_put_bignum2(&b, key->rsa->e); - buffer_put_bignum2(&b, key->rsa->n); - break; -#endif - case KEY_ED25519: - buffer_put_cstring(&b, - key_ssh_name_from_type_nid(type, key->ecdsa_nid)); - buffer_put_string(&b, key->ed25519_pk, ED25519_PK_SZ); - break; - default: - error("key_to_blob: unsupported key type %d", key->type); - buffer_free(&b); - return 0; - } - len = buffer_len(&b); + if (blen > INT_MAX) + fatal("%s: giant len %zu", __func__, blen); + if (blobp != NULL) + *blobp = blob; if (lenp != NULL) - *lenp = len; - if (blobp != NULL) { - *blobp = xmalloc(len); - memcpy(*blobp, buffer_ptr(&b), len); - } - explicit_bzero(buffer_ptr(&b), len); - buffer_free(&b); - return len; + *lenp = blen; + return blen; } int -key_to_blob(const Key *key, u_char **blobp, u_int *lenp) -{ - return to_blob(key, blobp, lenp, 0); -} - -int -key_sign( - const Key *key, - u_char **sigp, u_int *lenp, +key_sign(const Key *key, u_char **sigp, u_int *lenp, const u_char *data, u_int datalen) { - switch (key->type) { -#ifdef WITH_OPENSSL - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - case KEY_DSA: - return ssh_dss_sign(key, sigp, lenp, data, datalen); -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA_CERT: - case KEY_ECDSA: - return ssh_ecdsa_sign(key, sigp, lenp, data, datalen); -#endif - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - case KEY_RSA: - return ssh_rsa_sign(key, sigp, lenp, data, datalen); -#endif - case KEY_ED25519: - case KEY_ED25519_CERT: - return ssh_ed25519_sign(key, sigp, lenp, data, datalen); - default: - error("key_sign: invalid key type %d", key->type); + int r; + u_char *sig; + size_t siglen; + + if (sigp != NULL) + *sigp = NULL; + if (lenp != NULL) + *lenp = 0; + if ((r = sshkey_sign(key, &sig, &siglen, + data, datalen, datafellows)) != 0) { + fatal_on_fatal_errors(r, __func__, 0); + error("%s: %s", __func__, ssh_err(r)); return -1; } + if (siglen > INT_MAX) + fatal("%s: giant len %zu", __func__, siglen); + if (sigp != NULL) + *sigp = sig; + if (lenp != NULL) + *lenp = siglen; + return 0; } -/* - * key_verify returns 1 for a correct signature, 0 for an incorrect signature - * and -1 on error. - */ int -key_verify( - const Key *key, - const u_char *signature, u_int signaturelen, +key_verify(const Key *key, const u_char *signature, u_int signaturelen, const u_char *data, u_int datalen) { - if (signaturelen == 0) - return -1; + int r; - switch (key->type) { -#ifdef WITH_OPENSSL - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - case KEY_DSA: - return ssh_dss_verify(key, signature, signaturelen, data, datalen); -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA_CERT: - case KEY_ECDSA: - return ssh_ecdsa_verify(key, signature, signaturelen, data, datalen); -#endif - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - case KEY_RSA: - return ssh_rsa_verify(key, signature, signaturelen, data, datalen); -#endif - case KEY_ED25519: - case KEY_ED25519_CERT: - return ssh_ed25519_verify(key, signature, signaturelen, data, datalen); - default: - error("key_verify: invalid key type %d", key->type); - return -1; + if ((r = sshkey_verify(key, signature, signaturelen, + data, datalen, datafellows)) != 0) { + fatal_on_fatal_errors(r, __func__, 0); + error("%s: %s", __func__, ssh_err(r)); + return r == SSH_ERR_SIGNATURE_INVALID ? 0 : -1; } + return 1; } -/* Converts a private to a public key */ Key * key_demote(const Key *k) { - Key *pk; - - pk = xcalloc(1, sizeof(*pk)); - pk->type = k->type; - pk->flags = k->flags; - pk->ecdsa_nid = k->ecdsa_nid; - pk->dsa = NULL; - pk->ecdsa = NULL; - pk->rsa = NULL; - pk->ed25519_pk = NULL; - pk->ed25519_sk = NULL; - - switch (k->type) { -#ifdef WITH_OPENSSL - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - key_cert_copy(k, pk); - /* FALLTHROUGH */ - case KEY_RSA1: - case KEY_RSA: - if ((pk->rsa = RSA_new()) == NULL) - fatal("key_demote: RSA_new failed"); - if ((pk->rsa->e = BN_dup(k->rsa->e)) == NULL) - fatal("key_demote: BN_dup failed"); - if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL) - fatal("key_demote: BN_dup failed"); - break; - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - key_cert_copy(k, pk); - /* FALLTHROUGH */ - case KEY_DSA: - if ((pk->dsa = DSA_new()) == NULL) - fatal("key_demote: DSA_new failed"); - if ((pk->dsa->p = BN_dup(k->dsa->p)) == NULL) - fatal("key_demote: BN_dup failed"); - if ((pk->dsa->q = BN_dup(k->dsa->q)) == NULL) - fatal("key_demote: BN_dup failed"); - if ((pk->dsa->g = BN_dup(k->dsa->g)) == NULL) - fatal("key_demote: BN_dup failed"); - if ((pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) - fatal("key_demote: BN_dup failed"); - break; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA_CERT: - key_cert_copy(k, pk); - /* FALLTHROUGH */ - case KEY_ECDSA: - if ((pk->ecdsa = EC_KEY_new_by_curve_name(pk->ecdsa_nid)) == NULL) - fatal("key_demote: EC_KEY_new_by_curve_name failed"); - if (EC_KEY_set_public_key(pk->ecdsa, - EC_KEY_get0_public_key(k->ecdsa)) != 1) - fatal("key_demote: EC_KEY_set_public_key failed"); - break; -#endif - case KEY_ED25519_CERT: - key_cert_copy(k, pk); - /* FALLTHROUGH */ - case KEY_ED25519: - if (k->ed25519_pk != NULL) { - pk->ed25519_pk = xmalloc(ED25519_PK_SZ); - memcpy(pk->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ); - } - break; - default: - fatal("key_demote: bad key type %d", k->type); - break; - } - - return (pk); -} - -int -key_is_cert(const Key *k) -{ - if (k == NULL) - return 0; - return key_type_is_cert(k->type); -} + int r; + Key *ret = NULL; -/* Return the cert-less equivalent to a certified key type */ -int -key_type_plain(int type) -{ - switch (type) { - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - return KEY_RSA; - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - return KEY_DSA; - case KEY_ECDSA_CERT: - return KEY_ECDSA; - case KEY_ED25519_CERT: - return KEY_ED25519; - default: - return type; - } + if ((r = sshkey_demote(k, &ret)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + return ret; } -/* Convert a plain key to their _CERT equivalent */ int key_to_certified(Key *k, int legacy) { - switch (k->type) { - case KEY_RSA: - k->cert = cert_new(); - k->type = legacy ? KEY_RSA_CERT_V00 : KEY_RSA_CERT; - return 0; - case KEY_DSA: - k->cert = cert_new(); - k->type = legacy ? KEY_DSA_CERT_V00 : KEY_DSA_CERT; - return 0; - case KEY_ECDSA: - if (legacy) - fatal("%s: legacy ECDSA certificates are not supported", - __func__); - k->cert = cert_new(); - k->type = KEY_ECDSA_CERT; - return 0; - case KEY_ED25519: - if (legacy) - fatal("%s: legacy ED25519 certificates are not " - "supported", __func__); - k->cert = cert_new(); - k->type = KEY_ED25519_CERT; - return 0; - default: - error("%s: key has incorrect type %s", __func__, key_type(k)); + int r; + + if ((r = sshkey_to_certified(k, legacy)) != 0) { + fatal_on_fatal_errors(r, __func__, 0); + error("%s: %s", __func__, ssh_err(r)); return -1; } + return 0; } -/* Convert a certificate to its raw key equivalent */ int key_drop_cert(Key *k) { - if (!key_type_is_cert(k->type)) { - error("%s: key has incorrect type %s", __func__, key_type(k)); + int r; + + if ((r = sshkey_drop_cert(k)) != 0) { + fatal_on_fatal_errors(r, __func__, 0); + error("%s: %s", __func__, ssh_err(r)); return -1; } - cert_free(k->cert); - k->cert = NULL; - k->type = key_type_plain(k->type); return 0; } -/* Sign a certified key, (re-)generating the signed certblob. */ int key_certify(Key *k, Key *ca) { - Buffer principals; - u_char *ca_blob, *sig_blob, nonce[32]; - u_int i, ca_len, sig_len; + int r; - if (k->cert == NULL) { - error("%s: key lacks cert info", __func__); + if ((r = sshkey_certify(k, ca)) != 0) { + fatal_on_fatal_errors(r, __func__, 0); + error("%s: %s", __func__, ssh_err(r)); return -1; } + return 0; +} - if (!key_is_cert(k)) { - error("%s: certificate has unknown type %d", __func__, - k->cert->type); - return -1; - } +int +key_cert_check_authority(const Key *k, int want_host, int require_principal, + const char *name, const char **reason) +{ + int r; - if (!key_type_is_valid_ca(ca->type)) { - error("%s: CA key has unsupported type %s", __func__, - key_type(ca)); + if ((r = sshkey_cert_check_authority(k, want_host, require_principal, + name, reason)) != 0) { + fatal_on_fatal_errors(r, __func__, 0); + error("%s: %s", __func__, ssh_err(r)); return -1; } + return 0; +} - key_to_blob(ca, &ca_blob, &ca_len); - - buffer_clear(&k->cert->certblob); - buffer_put_cstring(&k->cert->certblob, key_ssh_name(k)); - - /* -v01 certs put nonce first */ - arc4random_buf(&nonce, sizeof(nonce)); - if (!key_cert_is_legacy(k)) - buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce)); - - /* XXX this substantially duplicates to_blob(); refactor */ - switch (k->type) { #ifdef WITH_OPENSSL - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - buffer_put_bignum2(&k->cert->certblob, k->dsa->p); - buffer_put_bignum2(&k->cert->certblob, k->dsa->q); - buffer_put_bignum2(&k->cert->certblob, k->dsa->g); - buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key); - break; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA_CERT: - buffer_put_cstring(&k->cert->certblob, - key_curve_nid_to_name(k->ecdsa_nid)); - buffer_put_ecpoint(&k->cert->certblob, - EC_KEY_get0_group(k->ecdsa), - EC_KEY_get0_public_key(k->ecdsa)); - break; -#endif - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - buffer_put_bignum2(&k->cert->certblob, k->rsa->e); - buffer_put_bignum2(&k->cert->certblob, k->rsa->n); - break; -#endif - case KEY_ED25519_CERT: - buffer_put_string(&k->cert->certblob, - k->ed25519_pk, ED25519_PK_SZ); - break; - default: - error("%s: key has incorrect type %s", __func__, key_type(k)); - buffer_clear(&k->cert->certblob); - free(ca_blob); - return -1; - } - - /* -v01 certs have a serial number next */ - if (!key_cert_is_legacy(k)) - buffer_put_int64(&k->cert->certblob, k->cert->serial); - - buffer_put_int(&k->cert->certblob, k->cert->type); - buffer_put_cstring(&k->cert->certblob, k->cert->key_id); - - buffer_init(&principals); - for (i = 0; i < k->cert->nprincipals; i++) - buffer_put_cstring(&principals, k->cert->principals[i]); - buffer_put_string(&k->cert->certblob, buffer_ptr(&principals), - buffer_len(&principals)); - buffer_free(&principals); - - buffer_put_int64(&k->cert->certblob, k->cert->valid_after); - buffer_put_int64(&k->cert->certblob, k->cert->valid_before); - buffer_put_string(&k->cert->certblob, - buffer_ptr(&k->cert->critical), buffer_len(&k->cert->critical)); - - /* -v01 certs have non-critical options here */ - if (!key_cert_is_legacy(k)) { - buffer_put_string(&k->cert->certblob, - buffer_ptr(&k->cert->extensions), - buffer_len(&k->cert->extensions)); - } - - /* -v00 certs put the nonce at the end */ - if (key_cert_is_legacy(k)) - buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce)); - - buffer_put_string(&k->cert->certblob, NULL, 0); /* reserved */ - buffer_put_string(&k->cert->certblob, ca_blob, ca_len); - free(ca_blob); +int +key_ec_validate_public(const EC_GROUP *group, const EC_POINT *public) +{ + int r; - /* Sign the whole mess */ - if (key_sign(ca, &sig_blob, &sig_len, buffer_ptr(&k->cert->certblob), - buffer_len(&k->cert->certblob)) != 0) { - error("%s: signature operation failed", __func__); - buffer_clear(&k->cert->certblob); + if ((r = sshkey_ec_validate_public(group, public)) != 0) { + fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR); + error("%s: %s", __func__, ssh_err(r)); return -1; } - /* Append signature and we are done */ - buffer_put_string(&k->cert->certblob, sig_blob, sig_len); - free(sig_blob); - return 0; } int -key_cert_check_authority(const Key *k, int want_host, int require_principal, - const char *name, const char **reason) +key_ec_validate_private(const EC_KEY *key) { - u_int i, principal_matches; - time_t now = time(NULL); - - if (want_host) { - if (k->cert->type != SSH2_CERT_TYPE_HOST) { - *reason = "Certificate invalid: not a host certificate"; - return -1; - } - } else { - if (k->cert->type != SSH2_CERT_TYPE_USER) { - *reason = "Certificate invalid: not a user certificate"; - return -1; - } - } - if (now < 0) { - error("%s: system clock lies before epoch", __func__); - *reason = "Certificate invalid: not yet valid"; - return -1; - } - if ((u_int64_t)now < k->cert->valid_after) { - *reason = "Certificate invalid: not yet valid"; - return -1; - } - if ((u_int64_t)now >= k->cert->valid_before) { - *reason = "Certificate invalid: expired"; + int r; + + if ((r = sshkey_ec_validate_private(key)) != 0) { + fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR); + error("%s: %s", __func__, ssh_err(r)); return -1; } - if (k->cert->nprincipals == 0) { - if (require_principal) { - *reason = "Certificate lacks principal list"; - return -1; - } - } else if (name != NULL) { - principal_matches = 0; - for (i = 0; i < k->cert->nprincipals; i++) { - if (strcmp(name, k->cert->principals[i]) == 0) { - principal_matches = 1; - break; - } - } - if (!principal_matches) { - *reason = "Certificate invalid: name is not a listed " - "principal"; - return -1; - } - } return 0; } +#endif /* WITH_OPENSSL */ -int -key_cert_is_legacy(const Key *k) +void +key_private_serialize(const Key *key, struct sshbuf *b) { - switch (k->type) { - case KEY_DSA_CERT_V00: - case KEY_RSA_CERT_V00: - return 1; - default: - return 0; - } -} + int r; -#ifdef WITH_OPENSSL -/* XXX: these are really begging for a table-driven approach */ -int -key_curve_name_to_nid(const char *name) -{ -#ifdef OPENSSL_HAS_ECC - if (strcmp(name, "nistp256") == 0) - return NID_X9_62_prime256v1; - else if (strcmp(name, "nistp384") == 0) - return NID_secp384r1; -# ifdef OPENSSL_HAS_NISTP521 - else if (strcmp(name, "nistp521") == 0) - return NID_secp521r1; -# endif -#endif - - debug("%s: unsupported EC curve name \"%.100s\"", __func__, name); - return -1; + if ((r = sshkey_private_serialize(key, b)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); } -u_int -key_curve_nid_to_bits(int nid) +Key * +key_private_deserialize(struct sshbuf *blob) { - switch (nid) { -#ifdef OPENSSL_HAS_ECC - case NID_X9_62_prime256v1: - return 256; - case NID_secp384r1: - return 384; -# ifdef OPENSSL_HAS_NISTP521 - case NID_secp521r1: - return 521; -# endif -#endif - default: - error("%s: unsupported EC curve nid %d", __func__, nid); - return 0; + int r; + Key *ret = NULL; + + if ((r = sshkey_private_deserialize(blob, &ret)) != 0) { + fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR); + error("%s: %s", __func__, ssh_err(r)); + return NULL; } + return ret; } -const char * -key_curve_nid_to_name(int nid) -{ -#ifdef OPENSSL_HAS_ECC - if (nid == NID_X9_62_prime256v1) - return "nistp256"; - else if (nid == NID_secp384r1) - return "nistp384"; -# ifdef OPENSSL_HAS_NISTP521 - else if (nid == NID_secp521r1) - return "nistp521"; -# endif -#endif - error("%s: unsupported EC curve nid %d", __func__, nid); - return NULL; -} +/* authfile.c */ -#ifdef OPENSSL_HAS_ECC int -key_ec_nid_to_hash_alg(int nid) +key_save_private(Key *key, const char *filename, const char *passphrase, + const char *comment, int force_new_format, const char *new_format_cipher, + int new_format_rounds) { - int kbits = key_curve_nid_to_bits(nid); - - if (kbits == 0) - fatal("%s: invalid nid %d", __func__, nid); - /* RFC5656 section 6.2.1 */ - if (kbits <= 256) - return SSH_DIGEST_SHA256; - else if (kbits <= 384) - return SSH_DIGEST_SHA384; - else - return SSH_DIGEST_SHA512; + int r; + + if ((r = sshkey_save_private(key, filename, passphrase, comment, + force_new_format, new_format_cipher, new_format_rounds)) != 0) { + fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR); + error("%s: %s", __func__, ssh_err(r)); + return 0; + } + return 1; } int -key_ec_validate_public(const EC_GROUP *group, const EC_POINT *public) +key_load_file(int fd, const char *filename, struct sshbuf *blob) { - BN_CTX *bnctx; - EC_POINT *nq = NULL; - BIGNUM *order, *x, *y, *tmp; - int ret = -1; - - if ((bnctx = BN_CTX_new()) == NULL) - fatal("%s: BN_CTX_new failed", __func__); - BN_CTX_start(bnctx); - - /* - * We shouldn't ever hit this case because bignum_get_ecpoint() - * refuses to load GF2m points. - */ - if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) != - NID_X9_62_prime_field) { - error("%s: group is not a prime field", __func__); - goto out; - } - - /* Q != infinity */ - if (EC_POINT_is_at_infinity(group, public)) { - error("%s: received degenerate public key (infinity)", - __func__); - goto out; - } + int r; - if ((x = BN_CTX_get(bnctx)) == NULL || - (y = BN_CTX_get(bnctx)) == NULL || - (order = BN_CTX_get(bnctx)) == NULL || - (tmp = BN_CTX_get(bnctx)) == NULL) - fatal("%s: BN_CTX_get failed", __func__); - - /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */ - if (EC_GROUP_get_order(group, order, bnctx) != 1) - fatal("%s: EC_GROUP_get_order failed", __func__); - if (EC_POINT_get_affine_coordinates_GFp(group, public, - x, y, bnctx) != 1) - fatal("%s: EC_POINT_get_affine_coordinates_GFp", __func__); - if (BN_num_bits(x) <= BN_num_bits(order) / 2) { - error("%s: public key x coordinate too small: " - "bits(x) = %d, bits(order)/2 = %d", __func__, - BN_num_bits(x), BN_num_bits(order) / 2); - goto out; - } - if (BN_num_bits(y) <= BN_num_bits(order) / 2) { - error("%s: public key y coordinate too small: " - "bits(y) = %d, bits(order)/2 = %d", __func__, - BN_num_bits(x), BN_num_bits(order) / 2); - goto out; + if ((r = sshkey_load_file(fd, filename, blob)) != 0) { + fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR); + error("%s: %s", __func__, ssh_err(r)); + return 0; } + return 1; +} - /* nQ == infinity (n == order of subgroup) */ - if ((nq = EC_POINT_new(group)) == NULL) - fatal("%s: BN_CTX_tmp failed", __func__); - if (EC_POINT_mul(group, nq, NULL, public, order, bnctx) != 1) - fatal("%s: EC_GROUP_mul failed", __func__); - if (EC_POINT_is_at_infinity(group, nq) != 1) { - error("%s: received degenerate public key (nQ != infinity)", - __func__); - goto out; - } +Key * +key_load_cert(const char *filename) +{ + int r; + Key *ret = NULL; - /* x < order - 1, y < order - 1 */ - if (!BN_sub(tmp, order, BN_value_one())) - fatal("%s: BN_sub failed", __func__); - if (BN_cmp(x, tmp) >= 0) { - error("%s: public key x coordinate >= group order - 1", - __func__); - goto out; - } - if (BN_cmp(y, tmp) >= 0) { - error("%s: public key y coordinate >= group order - 1", - __func__); - goto out; + if ((r = sshkey_load_cert(filename, &ret)) != 0) { + fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR); + if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) + debug("%s: %s", __func__, ssh_err(r)); + else + error("%s: %s", __func__, ssh_err(r)); + return NULL; } - ret = 0; - out: - BN_CTX_free(bnctx); - EC_POINT_free(nq); return ret; + } -int -key_ec_validate_private(const EC_KEY *key) +Key * +key_load_public(const char *filename, char **commentp) { - BN_CTX *bnctx; - BIGNUM *order, *tmp; - int ret = -1; - - if ((bnctx = BN_CTX_new()) == NULL) - fatal("%s: BN_CTX_new failed", __func__); - BN_CTX_start(bnctx); - - if ((order = BN_CTX_get(bnctx)) == NULL || - (tmp = BN_CTX_get(bnctx)) == NULL) - fatal("%s: BN_CTX_get failed", __func__); - - /* log2(private) > log2(order)/2 */ - if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, bnctx) != 1) - fatal("%s: EC_GROUP_get_order failed", __func__); - if (BN_num_bits(EC_KEY_get0_private_key(key)) <= - BN_num_bits(order) / 2) { - error("%s: private key too small: " - "bits(y) = %d, bits(order)/2 = %d", __func__, - BN_num_bits(EC_KEY_get0_private_key(key)), - BN_num_bits(order) / 2); - goto out; - } + int r; + Key *ret = NULL; - /* private < order - 1 */ - if (!BN_sub(tmp, order, BN_value_one())) - fatal("%s: BN_sub failed", __func__); - if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0) { - error("%s: private key >= group order - 1", __func__); - goto out; + if ((r = sshkey_load_public(filename, &ret, commentp)) != 0) { + fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR); + if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) + debug("%s: %s", __func__, ssh_err(r)); + else + error("%s: %s", __func__, ssh_err(r)); + return NULL; } - ret = 0; - out: - BN_CTX_free(bnctx); return ret; } -#endif -#if defined(DEBUG_KEXECDH) || defined(DEBUG_PK) -void -key_dump_ec_point(const EC_GROUP *group, const EC_POINT *point) -{ - BIGNUM *x, *y; - BN_CTX *bnctx; - - if (point == NULL) { - fputs("point=(NULL)\n", stderr); - return; +Key * +key_load_private(const char *path, const char *passphrase, + char **commentp) +{ + int r; + Key *ret = NULL; + + if ((r = sshkey_load_private(path, passphrase, &ret, commentp)) != 0) { + fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR); + if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) + debug("%s: %s", __func__, ssh_err(r)); + else + error("%s: %s", __func__, ssh_err(r)); + return NULL; } - if ((bnctx = BN_CTX_new()) == NULL) - fatal("%s: BN_CTX_new failed", __func__); - BN_CTX_start(bnctx); - if ((x = BN_CTX_get(bnctx)) == NULL || (y = BN_CTX_get(bnctx)) == NULL) - fatal("%s: BN_CTX_get failed", __func__); - if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) != - NID_X9_62_prime_field) - fatal("%s: group is not a prime field", __func__); - if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bnctx) != 1) - fatal("%s: EC_POINT_get_affine_coordinates_GFp", __func__); - fputs("x=", stderr); - BN_print_fp(stderr, x); - fputs("\ny=", stderr); - BN_print_fp(stderr, y); - fputs("\n", stderr); - BN_CTX_free(bnctx); + return ret; } -void -key_dump_ec_key(const EC_KEY *key) -{ - const BIGNUM *exponent; - - key_dump_ec_point(EC_KEY_get0_group(key), EC_KEY_get0_public_key(key)); - fputs("exponent=", stderr); - if ((exponent = EC_KEY_get0_private_key(key)) == NULL) - fputs("(NULL)", stderr); - else - BN_print_fp(stderr, EC_KEY_get0_private_key(key)); - fputs("\n", stderr); +Key * +key_load_private_cert(int type, const char *filename, const char *passphrase, + int *perm_ok) +{ + int r; + Key *ret = NULL; + + if ((r = sshkey_load_private_cert(type, filename, passphrase, + &ret, perm_ok)) != 0) { + fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR); + if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) + debug("%s: %s", __func__, ssh_err(r)); + else + error("%s: %s", __func__, ssh_err(r)); + return NULL; + } + return ret; } -#endif /* defined(DEBUG_KEXECDH) || defined(DEBUG_PK) */ -#endif /* OPENSSL_HAS_ECC */ -void -key_private_serialize(const Key *key, Buffer *b) -{ - buffer_put_cstring(b, key_ssh_name(key)); - switch (key->type) { -#ifdef WITH_OPENSSL - case KEY_RSA: - buffer_put_bignum2(b, key->rsa->n); - buffer_put_bignum2(b, key->rsa->e); - buffer_put_bignum2(b, key->rsa->d); - buffer_put_bignum2(b, key->rsa->iqmp); - buffer_put_bignum2(b, key->rsa->p); - buffer_put_bignum2(b, key->rsa->q); - break; - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) - fatal("%s: no cert/certblob", __func__); - buffer_put_string(b, buffer_ptr(&key->cert->certblob), - buffer_len(&key->cert->certblob)); - buffer_put_bignum2(b, key->rsa->d); - buffer_put_bignum2(b, key->rsa->iqmp); - buffer_put_bignum2(b, key->rsa->p); - buffer_put_bignum2(b, key->rsa->q); - break; - case KEY_DSA: - buffer_put_bignum2(b, key->dsa->p); - buffer_put_bignum2(b, key->dsa->q); - buffer_put_bignum2(b, key->dsa->g); - buffer_put_bignum2(b, key->dsa->pub_key); - buffer_put_bignum2(b, key->dsa->priv_key); - break; - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) - fatal("%s: no cert/certblob", __func__); - buffer_put_string(b, buffer_ptr(&key->cert->certblob), - buffer_len(&key->cert->certblob)); - buffer_put_bignum2(b, key->dsa->priv_key); - break; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: - buffer_put_cstring(b, key_curve_nid_to_name(key->ecdsa_nid)); - buffer_put_ecpoint(b, EC_KEY_get0_group(key->ecdsa), - EC_KEY_get0_public_key(key->ecdsa)); - buffer_put_bignum2(b, EC_KEY_get0_private_key(key->ecdsa)); - break; - case KEY_ECDSA_CERT: - if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) - fatal("%s: no cert/certblob", __func__); - buffer_put_string(b, buffer_ptr(&key->cert->certblob), - buffer_len(&key->cert->certblob)); - buffer_put_bignum2(b, EC_KEY_get0_private_key(key->ecdsa)); - break; -#endif /* OPENSSL_HAS_ECC */ - case KEY_ED25519: - buffer_put_string(b, key->ed25519_pk, ED25519_PK_SZ); - buffer_put_string(b, key->ed25519_sk, ED25519_SK_SZ); - break; -#endif -#endif - case KEY_ED25519_CERT: - if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) - fatal("%s: no cert/certblob", __func__); - buffer_put_string(b, buffer_ptr(&key->cert->certblob), - buffer_len(&key->cert->certblob)); - buffer_put_string(b, key->ed25519_pk, ED25519_PK_SZ); - buffer_put_string(b, key->ed25519_sk, ED25519_SK_SZ); - break; +Key * +key_load_private_type(int type, const char *filename, const char *passphrase, + char **commentp, int *perm_ok) +{ + int r; + Key *ret = NULL; + + if ((r = sshkey_load_private_type(type, filename, passphrase, + &ret, commentp, perm_ok)) != 0) { + fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR); + if ((r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) || + (r == SSH_ERR_KEY_WRONG_PASSPHRASE)) + debug("%s: %s", __func__, ssh_err(r)); + else + error("%s: %s", __func__, ssh_err(r)); + return NULL; } + return ret; } +#ifdef WITH_OPENSSL Key * -key_private_deserialize(Buffer *blob) +key_load_private_pem(int fd, int type, const char *passphrase, + char **commentp) { - char *type_name; - Key *k = NULL; - u_char *cert; - u_int len, pklen, sklen; - int type; -#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) - char *curve; - BIGNUM *exponent; - EC_POINT *q; -#endif - - type_name = buffer_get_string(blob, NULL); - type = key_type_from_name(type_name); - switch (type) { -#ifdef WITH_OPENSSL - case KEY_DSA: - k = key_new_private(type); - buffer_get_bignum2(blob, k->dsa->p); - buffer_get_bignum2(blob, k->dsa->q); - buffer_get_bignum2(blob, k->dsa->g); - buffer_get_bignum2(blob, k->dsa->pub_key); - buffer_get_bignum2(blob, k->dsa->priv_key); - break; - case KEY_DSA_CERT_V00: - case KEY_DSA_CERT: - cert = buffer_get_string(blob, &len); - if ((k = key_from_blob(cert, len)) == NULL) - fatal("Certificate parse failed"); - free(cert); - key_add_private(k); - buffer_get_bignum2(blob, k->dsa->priv_key); - break; -#ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: - k = key_new_private(type); - k->ecdsa_nid = key_ecdsa_nid_from_name(type_name); - curve = buffer_get_string(blob, NULL); - if (k->ecdsa_nid != key_curve_name_to_nid(curve)) - fatal("%s: curve names mismatch", __func__); - free(curve); - k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); - if (k->ecdsa == NULL) - fatal("%s: EC_KEY_new_by_curve_name failed", - __func__); - q = EC_POINT_new(EC_KEY_get0_group(k->ecdsa)); - if (q == NULL) - fatal("%s: BN_new failed", __func__); - if ((exponent = BN_new()) == NULL) - fatal("%s: BN_new failed", __func__); - buffer_get_ecpoint(blob, - EC_KEY_get0_group(k->ecdsa), q); - buffer_get_bignum2(blob, exponent); - if (EC_KEY_set_public_key(k->ecdsa, q) != 1) - fatal("%s: EC_KEY_set_public_key failed", - __func__); - if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) - fatal("%s: EC_KEY_set_private_key failed", - __func__); - if (key_ec_validate_public(EC_KEY_get0_group(k->ecdsa), - EC_KEY_get0_public_key(k->ecdsa)) != 0) - fatal("%s: bad ECDSA public key", __func__); - if (key_ec_validate_private(k->ecdsa) != 0) - fatal("%s: bad ECDSA private key", __func__); - BN_clear_free(exponent); - EC_POINT_free(q); - break; - case KEY_ECDSA_CERT: - cert = buffer_get_string(blob, &len); - if ((k = key_from_blob(cert, len)) == NULL) - fatal("Certificate parse failed"); - free(cert); - key_add_private(k); - if ((exponent = BN_new()) == NULL) - fatal("%s: BN_new failed", __func__); - buffer_get_bignum2(blob, exponent); - if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) - fatal("%s: EC_KEY_set_private_key failed", - __func__); - if (key_ec_validate_public(EC_KEY_get0_group(k->ecdsa), - EC_KEY_get0_public_key(k->ecdsa)) != 0 || - key_ec_validate_private(k->ecdsa) != 0) - fatal("%s: bad ECDSA key", __func__); - BN_clear_free(exponent); - break; -#endif - case KEY_RSA: - k = key_new_private(type); - buffer_get_bignum2(blob, k->rsa->n); - buffer_get_bignum2(blob, k->rsa->e); - buffer_get_bignum2(blob, k->rsa->d); - buffer_get_bignum2(blob, k->rsa->iqmp); - buffer_get_bignum2(blob, k->rsa->p); - buffer_get_bignum2(blob, k->rsa->q); - - /* Generate additional parameters */ - rsa_generate_additional_parameters(k->rsa); - break; - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - cert = buffer_get_string(blob, &len); - if ((k = key_from_blob(cert, len)) == NULL) - fatal("Certificate parse failed"); - free(cert); - key_add_private(k); - buffer_get_bignum2(blob, k->rsa->d); - buffer_get_bignum2(blob, k->rsa->iqmp); - buffer_get_bignum2(blob, k->rsa->p); - buffer_get_bignum2(blob, k->rsa->q); - break; -#endif -#endif - case KEY_ED25519: - k = key_new_private(type); - k->ed25519_pk = buffer_get_string(blob, &pklen); - k->ed25519_sk = buffer_get_string(blob, &sklen); - if (pklen != ED25519_PK_SZ) - fatal("%s: ed25519 pklen %d != %d", - __func__, pklen, ED25519_PK_SZ); - if (sklen != ED25519_SK_SZ) - fatal("%s: ed25519 sklen %d != %d", - __func__, sklen, ED25519_SK_SZ); - break; - case KEY_ED25519_CERT: - cert = buffer_get_string(blob, &len); - if ((k = key_from_blob(cert, len)) == NULL) - fatal("Certificate parse failed"); - free(cert); - key_add_private(k); - k->ed25519_pk = buffer_get_string(blob, &pklen); - k->ed25519_sk = buffer_get_string(blob, &sklen); - if (pklen != ED25519_PK_SZ) - fatal("%s: ed25519 pklen %d != %d", - __func__, pklen, ED25519_PK_SZ); - if (sklen != ED25519_SK_SZ) - fatal("%s: ed25519 sklen %d != %d", - __func__, sklen, ED25519_SK_SZ); - break; - default: - free(type_name); - buffer_clear(blob); + int r; + Key *ret = NULL; + + if ((r = sshkey_load_private_pem(fd, type, passphrase, + &ret, commentp)) != 0) { + fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR); + error("%s: %s", __func__, ssh_err(r)); return NULL; } - free(type_name); + return ret; +} +#endif /* WITH_OPENSSL */ - /* enable blinding */ - switch (k->type) { -#ifdef WITH_OPENSSL - case KEY_RSA: - case KEY_RSA_CERT_V00: - case KEY_RSA_CERT: - case KEY_RSA1: - if (RSA_blinding_on(k->rsa, NULL) != 1) { - error("%s: RSA_blinding_on failed", __func__); - key_free(k); - return NULL; - } - break; -#endif +int +key_perm_ok(int fd, const char *filename) +{ + return sshkey_perm_ok(fd, filename) == 0 ? 1 : 0; +} + +int +key_in_file(Key *key, const char *filename, int strict_type) +{ + int r; + + if ((r = sshkey_in_file(key, filename, strict_type)) != 0) { + fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR); + if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) + return 0; + error("%s: %s", __func__, ssh_err(r)); + return r == SSH_ERR_KEY_NOT_FOUND ? 0 : -1; } - return k; + return 1; } diff --git a/key.h b/key.h index d8ad13d08..4be4fedd6 100644 --- a/key.h +++ b/key.h @@ -1,4 +1,4 @@ -/* $OpenBSD: key.h,v 1.41 2014/01/09 23:20:00 djm Exp $ */ +/* $OpenBSD: key.h,v 1.42 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -26,141 +26,86 @@ #ifndef KEY_H #define KEY_H -#include "buffer.h" -#include -#include -#ifdef OPENSSL_HAS_ECC -#include +#include "sshkey.h" + +typedef struct sshkey Key; + +#define types sshkey_types +#define fp_type sshkey_fp_type +#define fp_rep sshkey_fp_rep + +#ifndef SSH_KEY_NO_DEFINE +#define key_new sshkey_new +#define key_free sshkey_free +#define key_equal_public sshkey_equal_public +#define key_equal sshkey_equal +#define key_fingerprint sshkey_fingerprint +#define key_type sshkey_type +#define key_cert_type sshkey_cert_type +#define key_ssh_name sshkey_ssh_name +#define key_ssh_name_plain sshkey_ssh_name_plain +#define key_type_from_name sshkey_type_from_name +#define key_ecdsa_nid_from_name sshkey_ecdsa_nid_from_name +#define key_type_is_cert sshkey_type_is_cert +#define key_size sshkey_size +#define key_ecdsa_bits_to_nid sshkey_ecdsa_bits_to_nid +#define key_ecdsa_key_to_nid sshkey_ecdsa_key_to_nid +#define key_names_valid2 sshkey_names_valid2 +#define key_is_cert sshkey_is_cert +#define key_type_plain sshkey_type_plain +#define key_cert_is_legacy sshkey_cert_is_legacy +#define key_curve_name_to_nid sshkey_curve_name_to_nid +#define key_curve_nid_to_bits sshkey_curve_nid_to_bits +#define key_curve_nid_to_name sshkey_curve_nid_to_name +#define key_ec_nid_to_hash_alg sshkey_ec_nid_to_hash_alg +#define key_dump_ec_point sshkey_dump_ec_point +#define key_dump_ec_key sshkey_dump_ec_key +#define key_fingerprint sshkey_fingerprint #endif -typedef struct Key Key; -enum types { - KEY_RSA1, - KEY_RSA, - KEY_DSA, - KEY_ECDSA, - KEY_ED25519, - KEY_RSA_CERT, - KEY_DSA_CERT, - KEY_ECDSA_CERT, - KEY_ED25519_CERT, - KEY_RSA_CERT_V00, - KEY_DSA_CERT_V00, - KEY_UNSPEC -}; -enum fp_type { - SSH_FP_SHA1, - SSH_FP_MD5, - SSH_FP_SHA256 -}; -enum fp_rep { - SSH_FP_HEX, - SSH_FP_BUBBLEBABBLE, - SSH_FP_RANDOMART -}; - -/* key is stored in external hardware */ -#define KEY_FLAG_EXT 0x0001 - -#define CERT_MAX_PRINCIPALS 256 -struct KeyCert { - Buffer certblob; /* Kept around for use on wire */ - u_int type; /* SSH2_CERT_TYPE_USER or SSH2_CERT_TYPE_HOST */ - u_int64_t serial; - char *key_id; - u_int nprincipals; - char **principals; - u_int64_t valid_after, valid_before; - Buffer critical; - Buffer extensions; - Key *signature_key; -}; - -struct Key { - int type; - int flags; - RSA *rsa; - DSA *dsa; - int ecdsa_nid; /* NID of curve */ -#ifdef OPENSSL_HAS_ECC - EC_KEY *ecdsa; -#else - void *ecdsa; -#endif - struct KeyCert *cert; - u_char *ed25519_sk; - u_char *ed25519_pk; -}; - -#define ED25519_SK_SZ crypto_sign_ed25519_SECRETKEYBYTES -#define ED25519_PK_SZ crypto_sign_ed25519_PUBLICKEYBYTES - -Key *key_new(int); -void key_add_private(Key *); -Key *key_new_private(int); -void key_free(Key *); -Key *key_demote(const Key *); -int key_equal_public(const Key *, const Key *); -int key_equal(const Key *, const Key *); -char *key_fingerprint(const Key *, enum fp_type, enum fp_rep); -u_char *key_fingerprint_raw(const Key *, enum fp_type, u_int *); -const char *key_type(const Key *); -const char *key_cert_type(const Key *); -int key_write(const Key *, FILE *); -int key_read(Key *, char **); -u_int key_size(const Key *); +void key_add_private(Key *); +Key *key_new_private(int); +void key_free(Key *); +Key *key_demote(const Key *); +u_char *key_fingerprint_raw(const Key *, enum fp_type, u_int *); +int key_write(const Key *, FILE *); +int key_read(Key *, char **); Key *key_generate(int, u_int); Key *key_from_private(const Key *); -int key_type_from_name(char *); -int key_is_cert(const Key *); -int key_type_is_cert(int); -int key_type_plain(int); int key_to_certified(Key *, int); int key_drop_cert(Key *); int key_certify(Key *, Key *); -void key_cert_copy(const Key *, struct Key *); +void key_cert_copy(const Key *, Key *); int key_cert_check_authority(const Key *, int, int, const char *, const char **); -int key_cert_is_legacy(const Key *); +char *key_alg_list(int, int); -int key_ecdsa_nid_from_name(const char *); -int key_curve_name_to_nid(const char *); -const char *key_curve_nid_to_name(int); -u_int key_curve_nid_to_bits(int); -int key_ecdsa_bits_to_nid(int); -#ifdef OPENSSL_HAS_ECC -int key_ecdsa_key_to_nid(EC_KEY *); -int key_ec_nid_to_hash_alg(int nid); -int key_ec_validate_public(const EC_GROUP *, const EC_POINT *); -int key_ec_validate_private(const EC_KEY *); -#endif -char *key_alg_list(int, int); +#ifdef WITH_OPENSSL +int key_ec_validate_public(const EC_GROUP *, const EC_POINT *); +int key_ec_validate_private(const EC_KEY *); +#endif /* WITH_OPENSSL */ -Key *key_from_blob(const u_char *, u_int); -int key_to_blob(const Key *, u_char **, u_int *); -const char *key_ssh_name(const Key *); -const char *key_ssh_name_plain(const Key *); -int key_names_valid2(const char *); +Key *key_from_blob(const u_char *, u_int); +int key_to_blob(const Key *, u_char **, u_int *); int key_sign(const Key *, u_char **, u_int *, const u_char *, u_int); int key_verify(const Key *, const u_char *, u_int, const u_char *, u_int); -int ssh_dss_sign(const Key *, u_char **, u_int *, const u_char *, u_int); -int ssh_dss_verify(const Key *, const u_char *, u_int, const u_char *, u_int); -int ssh_ecdsa_sign(const Key *, u_char **, u_int *, const u_char *, u_int); -int ssh_ecdsa_verify(const Key *, const u_char *, u_int, const u_char *, u_int); -int ssh_rsa_sign(const Key *, u_char **, u_int *, const u_char *, u_int); -int ssh_rsa_verify(const Key *, const u_char *, u_int, const u_char *, u_int); -int ssh_ed25519_sign(const Key *, u_char **, u_int *, const u_char *, u_int); -int ssh_ed25519_verify(const Key *, const u_char *, u_int, const u_char *, u_int); - -#if defined(OPENSSL_HAS_ECC) && (defined(DEBUG_KEXECDH) || defined(DEBUG_PK)) -void key_dump_ec_point(const EC_GROUP *, const EC_POINT *); -void key_dump_ec_key(const EC_KEY *); -#endif - -void key_private_serialize(const Key *, Buffer *); -Key *key_private_deserialize(Buffer *); +void key_private_serialize(const Key *, struct sshbuf *); +Key *key_private_deserialize(struct sshbuf *); + +/* authfile.c */ +int key_save_private(Key *, const char *, const char *, const char *, + int, const char *, int); +int key_load_file(int, const char *, struct sshbuf *); +Key *key_load_cert(const char *); +Key *key_load_public(const char *, char **); +Key *key_load_private(const char *, const char *, char **); +Key *key_load_private_cert(int, const char *, const char *, int *); +Key *key_load_private_type(int, const char *, const char *, char **, int *); +Key *key_load_private_pem(int, int, const char *, char **); +int key_perm_ok(int, const char *); +int key_in_file(Key *, const char *, int); #endif diff --git a/krl.c b/krl.c index 557a48ebb..eb31df90f 100644 --- a/krl.c +++ b/krl.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $OpenBSD: krl.c,v 1.16 2014/06/24 00:52:02 djm Exp $ */ +/* $OpenBSD: krl.c,v 1.17 2014/06/24 01:13:21 djm Exp $ */ #include "includes.h" @@ -366,7 +366,7 @@ plain_key_blob(const Key *key, u_char **blob, u_int *blen) } r = key_to_blob(kcopy, blob, blen); free(kcopy); - return r == 0 ? -1 : 0; + return r; } /* Revoke a key blob. Ownership of blob is transferred to the tree */ @@ -394,7 +394,7 @@ ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const Key *key) u_int len; debug3("%s: revoke type %s", __func__, key_type(key)); - if (plain_key_blob(key, &blob, &len) != 0) + if (plain_key_blob(key, &blob, &len) < 0) return -1; return revoke_blob(&krl->revoked_keys, blob, len); } @@ -1130,7 +1130,7 @@ is_key_revoked(struct ssh_krl *krl, const Key *key) /* Next, explicit keys */ memset(&rb, 0, sizeof(rb)); - if (plain_key_blob(key, &rb.blob, &rb.len) != 0) + if (plain_key_blob(key, &rb.blob, &rb.len) < 0) return -1; erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb); free(rb.blob); diff --git a/monitor.c b/monitor.c index 9391ae8d1..68f4dbc71 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.133 2014/05/03 17:20:34 markus Exp $ */ +/* $OpenBSD: monitor.c,v 1.134 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -40,9 +40,10 @@ #endif #include #include -#include #include #include +#include +#include #include #ifdef HAVE_POLL_H #include diff --git a/openbsd-compat/openssl-compat.c b/openbsd-compat/openssl-compat.c index 0e5f2cea5..36570e4ad 100644 --- a/openbsd-compat/openssl-compat.c +++ b/openbsd-compat/openssl-compat.c @@ -1,4 +1,4 @@ -/* $Id: openssl-compat.c,v 1.18 2014/06/17 13:06:08 dtucker Exp $ */ +/* $Id: openssl-compat.c,v 1.19 2014/07/02 05:28:07 djm Exp $ */ /* * Copyright (c) 2005 Darren Tucker @@ -16,6 +16,7 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define SSH_DONT_OVERLOAD_OPENSSL_FUNCS #include "includes.h" #include @@ -26,13 +27,8 @@ # include #endif -#ifndef HAVE_RSA_GET_DEFAULT_METHOD -# include -#endif - #include "log.h" -#define SSH_DONT_OVERLOAD_OPENSSL_FUNCS #include "openssl-compat.h" /* @@ -70,139 +66,6 @@ ssh_compatible_openssl(long headerver, long libver) return 0; } -#ifdef SSH_OLD_EVP -int -ssh_EVP_CipherInit(EVP_CIPHER_CTX *evp, const EVP_CIPHER *type, - unsigned char *key, unsigned char *iv, int enc) -{ - EVP_CipherInit(evp, type, key, iv, enc); - return 1; -} - -int -ssh_EVP_Cipher(EVP_CIPHER_CTX *evp, char *dst, char *src, int len) -{ - EVP_Cipher(evp, dst, src, len); - return 1; -} - -int -ssh_EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *evp) -{ - EVP_CIPHER_CTX_cleanup(evp); - return 1; -} -#endif - -#ifndef HAVE_EVP_DIGESTINIT_EX -int -EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *md, void *engine) -{ - if (engine != NULL) - fatal("%s: ENGINE is not supported", __func__); -# ifdef OPENSSL_EVP_DIGESTUPDATE_VOID - EVP_DigestInit(ctx, md); - return 1; -# else - return EVP_DigestInit(ctx, md); -# endif -} -#endif - -#ifndef HAVE_EVP_DIGESTFINAL_EX -int -EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s) -{ -# ifdef OPENSSL_EVP_DIGESTUPDATE_VOID - EVP_DigestFinal(ctx, md, s); - return 1; -# else - return EVP_DigestFinal(ctx, md, s); -# endif -} -#endif - -#ifdef OPENSSL_EVP_DIGESTUPDATE_VOID -int -ssh_EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, unsigned int cnt) -{ - EVP_DigestUpdate(ctx, d, cnt); - return 1; -} -#endif - -#ifndef HAVE_EVP_MD_CTX_COPY_EX -int -EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) -{ - return EVP_MD_CTX_copy(out, in); -} -#endif - -#ifndef HAVE_BN_IS_PRIME_EX -int -BN_is_prime_ex(const BIGNUM *p, int nchecks, BN_CTX *ctx, void *cb) -{ - if (cb != NULL) - fatal("%s: callback args not supported", __func__); - return BN_is_prime(p, nchecks, NULL, ctx, NULL); -} -#endif - -#ifndef HAVE_RSA_GENERATE_KEY_EX -int -RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *bn_e, void *cb) -{ - RSA *new_rsa, tmp_rsa; - unsigned long e; - - if (cb != NULL) - fatal("%s: callback args not supported", __func__); - e = BN_get_word(bn_e); - if (e == 0xffffffffL) - fatal("%s: value of e too large", __func__); - new_rsa = RSA_generate_key(bits, e, NULL, NULL); - if (new_rsa == NULL) - return 0; - /* swap rsa/new_rsa then free new_rsa */ - tmp_rsa = *rsa; - *rsa = *new_rsa; - *new_rsa = tmp_rsa; - RSA_free(new_rsa); - return 1; -} -#endif - -#ifndef HAVE_DSA_GENERATE_PARAMETERS_EX -int -DSA_generate_parameters_ex(DSA *dsa, int bits, const unsigned char *seed, - int seed_len, int *counter_ret, unsigned long *h_ret, void *cb) -{ - DSA *new_dsa, tmp_dsa; - - if (cb != NULL) - fatal("%s: callback args not supported", __func__); - new_dsa = DSA_generate_parameters(bits, (unsigned char *)seed, seed_len, - counter_ret, h_ret, NULL, NULL); - if (new_dsa == NULL) - return 0; - /* swap dsa/new_dsa then free new_dsa */ - tmp_dsa = *dsa; - *dsa = *new_dsa; - *new_dsa = tmp_dsa; - DSA_free(new_dsa); - return 1; -} -#endif - -#ifndef HAVE_RSA_GET_DEFAULT_METHOD -RSA_METHOD * -RSA_get_default_method(void) -{ - return RSA_PKCS1_SSLeay(); -} -#endif - #ifdef USE_OPENSSL_ENGINE void ssh_OpenSSL_add_all_algorithms(void) diff --git a/openbsd-compat/openssl-compat.h b/openbsd-compat/openssl-compat.h index 199dcc882..d088d2962 100644 --- a/openbsd-compat/openssl-compat.h +++ b/openbsd-compat/openssl-compat.h @@ -1,4 +1,4 @@ -/* $Id: openssl-compat.h,v 1.27 2014/06/17 13:06:08 dtucker Exp $ */ +/* $Id: openssl-compat.h,v 1.28 2014/07/02 05:28:07 djm Exp $ */ /* * Copyright (c) 2005 Darren Tucker @@ -24,22 +24,8 @@ int ssh_compatible_openssl(long, long); -/* Only in 0.9.8 */ -#ifndef OPENSSL_DSA_MAX_MODULUS_BITS -# define OPENSSL_DSA_MAX_MODULUS_BITS 10000 -#endif -#ifndef OPENSSL_RSA_MAX_MODULUS_BITS -# define OPENSSL_RSA_MAX_MODULUS_BITS 16384 -#endif - -/* OPENSSL_free() is Free() in versions before OpenSSL 0.9.6 */ -#if !defined(OPENSSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x0090600f) -# define OPENSSL_free(x) Free(x) -#endif - -#if OPENSSL_VERSION_NUMBER < 0x00906000L -# define SSH_OLD_EVP -# define EVP_CIPHER_CTX_get_app_data(e) ((e)->app_data) +#if (OPENSSL_VERSION_NUMBER <= 0x0090805fL) +#error OpenSSL 0.9.8f or greater is required #endif #if OPENSSL_VERSION_NUMBER < 0x10000001L @@ -48,31 +34,6 @@ int ssh_compatible_openssl(long, long); # define LIBCRYPTO_EVP_INL_TYPE size_t #endif -#if (OPENSSL_VERSION_NUMBER < 0x00907000L) || defined(OPENSSL_LOBOTOMISED_AES) -# define USE_BUILTIN_RIJNDAEL -#endif - -#ifdef USE_BUILTIN_RIJNDAEL -# include "rijndael.h" -# define AES_KEY rijndael_ctx -# define AES_BLOCK_SIZE 16 -# define AES_encrypt(a, b, c) rijndael_encrypt(c, a, b) -# define AES_set_encrypt_key(a, b, c) rijndael_set_key(c, (char *)a, b, 1) -# define EVP_aes_128_cbc evp_rijndael -# define EVP_aes_192_cbc evp_rijndael -# define EVP_aes_256_cbc evp_rijndael -const EVP_CIPHER *evp_rijndael(void); -void ssh_rijndael_iv(EVP_CIPHER_CTX *, int, u_char *, u_int); -#endif - -#ifndef OPENSSL_HAVE_EVPCTR -#define EVP_aes_128_ctr evp_aes_128_ctr -#define EVP_aes_192_ctr evp_aes_128_ctr -#define EVP_aes_256_ctr evp_aes_128_ctr -const EVP_CIPHER *evp_aes_128_ctr(void); -void ssh_aes_ctr_iv(EVP_CIPHER_CTX *, int, u_char *, size_t); -#endif - /* Avoid some #ifdef. Code that uses these is unreachable without GCM */ #if !defined(OPENSSL_HAVE_EVPGCM) && !defined(EVP_CTRL_GCM_SET_IV_FIXED) # define EVP_CTRL_GCM_SET_IV_FIXED -1 @@ -90,26 +51,9 @@ void ssh_aes_ctr_iv(EVP_CIPHER_CTX *, int, u_char *, size_t); # endif #endif -#if OPENSSL_VERSION_NUMBER < 0x00907000L -#define EVP_X_STATE(evp) &(evp).c -#define EVP_X_STATE_LEN(evp) sizeof((evp).c) -#else -#define EVP_X_STATE(evp) (evp).cipher_data -#define EVP_X_STATE_LEN(evp) (evp).cipher->ctx_size -#endif - -/* OpenSSL 0.9.8e returns cipher key len not context key len */ -#if (OPENSSL_VERSION_NUMBER == 0x0090805fL) -# define EVP_CIPHER_CTX_key_length(c) ((c)->key_len) -#endif - -#ifndef HAVE_RSA_GET_DEFAULT_METHOD -RSA_METHOD *RSA_get_default_method(void); -#endif - /* * We overload some of the OpenSSL crypto functions with ssh_* equivalents - * which cater for older and/or less featureful OpenSSL version. + * to automatically handle OpenSSL engine initialisation. * * In order for the compat library to call the real functions, it must * define SSH_DONT_OVERLOAD_OPENSSL_FUNCS before including this file and @@ -117,19 +61,6 @@ RSA_METHOD *RSA_get_default_method(void); */ #ifndef SSH_DONT_OVERLOAD_OPENSSL_FUNCS -# ifdef SSH_OLD_EVP -# ifdef EVP_Cipher -# undef EVP_Cipher -# endif -# define EVP_CipherInit(a,b,c,d,e) ssh_EVP_CipherInit((a),(b),(c),(d),(e)) -# define EVP_Cipher(a,b,c,d) ssh_EVP_Cipher((a),(b),(c),(d)) -# define EVP_CIPHER_CTX_cleanup(a) ssh_EVP_CIPHER_CTX_cleanup((a)) -# endif /* SSH_OLD_EVP */ - -# ifdef OPENSSL_EVP_DIGESTUPDATE_VOID -# define EVP_DigestUpdate(a,b,c) ssh_EVP_DigestUpdate((a),(b),(c)) -# endif - # ifdef USE_OPENSSL_ENGINE # ifdef OpenSSL_add_all_algorithms # undef OpenSSL_add_all_algorithms @@ -137,48 +68,7 @@ RSA_METHOD *RSA_get_default_method(void); # define OpenSSL_add_all_algorithms() ssh_OpenSSL_add_all_algorithms() # endif -# ifndef HAVE_BN_IS_PRIME_EX -int BN_is_prime_ex(const BIGNUM *, int, BN_CTX *, void *); -# endif - -# ifndef HAVE_DSA_GENERATE_PARAMETERS_EX -int DSA_generate_parameters_ex(DSA *, int, const unsigned char *, int, int *, - unsigned long *, void *); -# endif - -# ifndef HAVE_RSA_GENERATE_KEY_EX -int RSA_generate_key_ex(RSA *, int, BIGNUM *, void *); -# endif - -# ifndef HAVE_EVP_DIGESTINIT_EX -int EVP_DigestInit_ex(EVP_MD_CTX *, const EVP_MD *, void *); -# endif - -# ifndef HAVE_EVP_DISESTFINAL_EX -int EVP_DigestFinal_ex(EVP_MD_CTX *, unsigned char *, unsigned int *); -# endif - -# ifndef EVP_MD_CTX_COPY_EX -int EVP_MD_CTX_copy_ex(EVP_MD_CTX *, const EVP_MD_CTX *); -# endif - -int ssh_EVP_CipherInit(EVP_CIPHER_CTX *, const EVP_CIPHER *, unsigned char *, - unsigned char *, int); -int ssh_EVP_Cipher(EVP_CIPHER_CTX *, char *, char *, int); -int ssh_EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *); void ssh_OpenSSL_add_all_algorithms(void); -# ifndef HAVE_HMAC_CTX_INIT -# define HMAC_CTX_init(a) -# endif - -# ifndef HAVE_EVP_MD_CTX_INIT -# define EVP_MD_CTX_init(a) -# endif - -# ifndef HAVE_EVP_MD_CTX_CLEANUP -# define EVP_MD_CTX_cleanup(a) -# endif - #endif /* SSH_DONT_OVERLOAD_OPENSSL_FUNCS */ diff --git a/packet.c b/packet.c index 3dd66d7d9..b97257986 100644 --- a/packet.c +++ b/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.196 2014/05/03 17:20:34 markus Exp $ */ +/* $OpenBSD: packet.c,v 1.197 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -78,6 +78,7 @@ #include "canohost.h" #include "misc.h" #include "ssh.h" +#include "ssherr.h" #include "roaming.h" #ifdef PACKET_DEBUG @@ -222,6 +223,7 @@ void packet_set_connection(int fd_in, int fd_out) { const Cipher *none = cipher_by_name("none"); + int r; if (none == NULL) fatal("packet_set_connection: cannot load cipher 'none'"); @@ -229,10 +231,11 @@ packet_set_connection(int fd_in, int fd_out) active_state = alloc_session_state(); active_state->connection_in = fd_in; active_state->connection_out = fd_out; - cipher_init(&active_state->send_context, none, (const u_char *)"", - 0, NULL, 0, CIPHER_ENCRYPT); - cipher_init(&active_state->receive_context, none, (const u_char *)"", - 0, NULL, 0, CIPHER_DECRYPT); + if ((r = cipher_init(&active_state->send_context, none, + (const u_char *)"", 0, NULL, 0, CIPHER_ENCRYPT)) != 0 || + (r = cipher_init(&active_state->receive_context, none, + (const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0) + fatal("%s: cipher_init: %s", __func__, ssh_err(r)); active_state->newkeys[MODE_IN] = active_state->newkeys[MODE_OUT] = NULL; if (!active_state->initialized) { active_state->initialized = 1; @@ -329,13 +332,15 @@ void packet_get_keyiv(int mode, u_char *iv, u_int len) { CipherContext *cc; + int r; if (mode == MODE_OUT) cc = &active_state->send_context; else cc = &active_state->receive_context; - cipher_get_keyiv(cc, iv, len); + if ((r = cipher_get_keyiv(cc, iv, len)) != 0) + fatal("%s: cipher_get_keyiv: %s", __func__, ssh_err(r)); } int @@ -381,13 +386,15 @@ void packet_set_iv(int mode, u_char *dat) { CipherContext *cc; + int r; if (mode == MODE_OUT) cc = &active_state->send_context; else cc = &active_state->receive_context; - cipher_set_keyiv(cc, dat); + if ((r = cipher_set_keyiv(cc, dat)) != 0) + fatal("%s: cipher_set_keyiv: %s", __func__, ssh_err(r)); } int @@ -552,6 +559,7 @@ void packet_set_encryption_key(const u_char *key, u_int keylen, int number) { const Cipher *cipher = cipher_by_number(number); + int r; if (cipher == NULL) fatal("packet_set_encryption_key: unknown cipher number %d", number); @@ -561,10 +569,11 @@ packet_set_encryption_key(const u_char *key, u_int keylen, int number) fatal("packet_set_encryption_key: keylen too big: %d", keylen); memcpy(active_state->ssh1_key, key, keylen); active_state->ssh1_keylen = keylen; - cipher_init(&active_state->send_context, cipher, key, keylen, NULL, - 0, CIPHER_ENCRYPT); - cipher_init(&active_state->receive_context, cipher, key, keylen, NULL, - 0, CIPHER_DECRYPT); + if ((r = cipher_init(&active_state->send_context, cipher, + key, keylen, NULL, 0, CIPHER_ENCRYPT)) != 0 || + (r = cipher_init(&active_state->receive_context, cipher, + key, keylen, NULL, 0, CIPHER_DECRYPT)) != 0) + fatal("%s: cipher_init: %s", __func__, ssh_err(r)); } u_int @@ -744,7 +753,7 @@ set_newkeys(int mode) Comp *comp; CipherContext *cc; u_int64_t *max_blocks; - int crypt_type; + int r, crypt_type; debug2("set_newkeys: mode %d", mode); @@ -786,8 +795,9 @@ set_newkeys(int mode) if (cipher_authlen(enc->cipher) == 0 && mac_init(mac) == 0) mac->enabled = 1; DBG(debug("cipher_init_context: %d", mode)); - cipher_init(cc, enc->cipher, enc->key, enc->key_len, - enc->iv, enc->iv_len, crypt_type); + if ((r = cipher_init(cc, enc->cipher, enc->key, enc->key_len, + enc->iv, enc->iv_len, crypt_type)) != 0) + fatal("%s: cipher_init: %s", __func__, ssh_err(r)); /* Deleting the keys does not gain extra security */ /* explicit_bzero(enc->iv, enc->block_size); explicit_bzero(enc->key, enc->key_len); diff --git a/rsa.c b/rsa.c index d0b5bbf5e..5ecacef90 100644 --- a/rsa.c +++ b/rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rsa.c,v 1.31 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: rsa.c,v 1.32 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -67,85 +67,122 @@ #include #include -#include "xmalloc.h" #include "rsa.h" #include "log.h" +#include "ssherr.h" -void +int rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA *key) { - u_char *inbuf, *outbuf; - int len, ilen, olen; + u_char *inbuf = NULL, *outbuf = NULL; + int len, ilen, olen, r = SSH_ERR_INTERNAL_ERROR; if (BN_num_bits(key->e) < 2 || !BN_is_odd(key->e)) - fatal("rsa_public_encrypt() exponent too small or not odd"); + return SSH_ERR_INVALID_ARGUMENT; olen = BN_num_bytes(key->n); - outbuf = xmalloc(olen); + if ((outbuf = malloc(olen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } ilen = BN_num_bytes(in); - inbuf = xmalloc(ilen); + if ((inbuf = malloc(ilen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } BN_bn2bin(in, inbuf); if ((len = RSA_public_encrypt(ilen, inbuf, outbuf, key, - RSA_PKCS1_PADDING)) <= 0) - fatal("rsa_public_encrypt() failed"); + RSA_PKCS1_PADDING)) <= 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } - if (BN_bin2bn(outbuf, len, out) == NULL) - fatal("rsa_public_encrypt: BN_bin2bn failed"); + if (BN_bin2bn(outbuf, len, out) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + r = 0; - explicit_bzero(outbuf, olen); - explicit_bzero(inbuf, ilen); - free(outbuf); - free(inbuf); + out: + if (outbuf != NULL) { + explicit_bzero(outbuf, olen); + free(outbuf); + } + if (inbuf != NULL) { + explicit_bzero(inbuf, ilen); + free(inbuf); + } + return r; } int rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key) { - u_char *inbuf, *outbuf; - int len, ilen, olen; + u_char *inbuf = NULL, *outbuf = NULL; + int len, ilen, olen, r = SSH_ERR_INTERNAL_ERROR; olen = BN_num_bytes(key->n); - outbuf = xmalloc(olen); + if ((outbuf = malloc(olen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } ilen = BN_num_bytes(in); - inbuf = xmalloc(ilen); + if ((inbuf = malloc(ilen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } BN_bn2bin(in, inbuf); if ((len = RSA_private_decrypt(ilen, inbuf, outbuf, key, RSA_PKCS1_PADDING)) <= 0) { - error("rsa_private_decrypt() failed"); - } else { - if (BN_bin2bn(outbuf, len, out) == NULL) - fatal("rsa_private_decrypt: BN_bin2bn failed"); + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } else if (BN_bin2bn(outbuf, len, out) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + r = 0; + out: + if (outbuf != NULL) { + explicit_bzero(outbuf, olen); + free(outbuf); + } + if (inbuf != NULL) { + explicit_bzero(inbuf, ilen); + free(inbuf); } - explicit_bzero(outbuf, olen); - explicit_bzero(inbuf, ilen); - free(outbuf); - free(inbuf); - return len; + return r; } /* calculate p-1 and q-1 */ -void +int rsa_generate_additional_parameters(RSA *rsa) { - BIGNUM *aux; - BN_CTX *ctx; + BIGNUM *aux = NULL; + BN_CTX *ctx = NULL; + int r; - if ((aux = BN_new()) == NULL) - fatal("rsa_generate_additional_parameters: BN_new failed"); if ((ctx = BN_CTX_new()) == NULL) - fatal("rsa_generate_additional_parameters: BN_CTX_new failed"); + return SSH_ERR_ALLOC_FAIL; + if ((aux = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } if ((BN_sub(aux, rsa->q, BN_value_one()) == 0) || (BN_mod(rsa->dmq1, rsa->d, aux, ctx) == 0) || (BN_sub(aux, rsa->p, BN_value_one()) == 0) || - (BN_mod(rsa->dmp1, rsa->d, aux, ctx) == 0)) - fatal("rsa_generate_additional_parameters: BN_sub/mod failed"); - + (BN_mod(rsa->dmp1, rsa->d, aux, ctx) == 0)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + r = 0; + out: BN_clear_free(aux); BN_CTX_free(ctx); + return r; } diff --git a/rsa.h b/rsa.h index b841ea4e1..c476707d5 100644 --- a/rsa.h +++ b/rsa.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rsa.h,v 1.16 2006/03/25 22:22:43 djm Exp $ */ +/* $OpenBSD: rsa.h,v 1.17 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen @@ -19,8 +19,8 @@ #include #include -void rsa_public_encrypt(BIGNUM *, BIGNUM *, RSA *); +int rsa_public_encrypt(BIGNUM *, BIGNUM *, RSA *); int rsa_private_decrypt(BIGNUM *, BIGNUM *, RSA *); -void rsa_generate_additional_parameters(RSA *); +int rsa_generate_additional_parameters(RSA *); #endif /* RSA_H */ diff --git a/ssh-add.c b/ssh-add.c index 3421452af..46b91cbde 100644 --- a/ssh-add.c +++ b/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.109 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.110 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -62,6 +62,7 @@ #include "authfile.h" #include "pathnames.h" #include "misc.h" +#include "ssherr.h" /* argv0 */ extern char *__progname; @@ -170,7 +171,7 @@ add_file(AuthenticationConnection *ac, const char *filename, int key_only) Key *private, *cert; char *comment = NULL; char msg[1024], *certpath = NULL; - int fd, perms_ok, ret = -1; + int r, fd, perms_ok, ret = -1; Buffer keyblob; if (strcmp(filename, "-") == 0) { @@ -201,12 +202,18 @@ add_file(AuthenticationConnection *ac, const char *filename, int key_only) close(fd); /* At first, try empty passphrase */ - private = key_parse_private(&keyblob, filename, "", &comment); + if ((r = sshkey_parse_private_fileblob(&keyblob, filename, "", + &private, &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) + fatal("Cannot parse %s: %s", filename, ssh_err(r)); if (comment == NULL) comment = xstrdup(filename); /* try last */ - if (private == NULL && pass != NULL) - private = key_parse_private(&keyblob, filename, pass, NULL); + if (private == NULL && pass != NULL) { + if ((r = sshkey_parse_private_fileblob(&keyblob, filename, pass, + &private, &comment)) != 0 && + r != SSH_ERR_KEY_WRONG_PASSPHRASE) + fatal("Cannot parse %s: %s", filename, ssh_err(r)); + } if (private == NULL) { /* clear passphrase since it did not work */ clear_pass(); @@ -220,8 +227,11 @@ add_file(AuthenticationConnection *ac, const char *filename, int key_only) buffer_free(&keyblob); return -1; } - private = key_parse_private(&keyblob, filename, pass, - &comment); + if ((r = sshkey_parse_private_fileblob(&keyblob, + filename, pass, &private, &comment)) != 0 && + r != SSH_ERR_KEY_WRONG_PASSPHRASE) + fatal("Cannot parse %s: %s", + filename, ssh_err(r)); if (private != NULL) break; clear_pass(); diff --git a/ssh-agent.c b/ssh-agent.c index bc96ad705..693d763e2 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.185 2014/04/29 18:01:49 markus Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.186 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -278,7 +278,7 @@ process_authentication_challenge1(SocketEntry *e) if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { Key *private = id->key; /* Decrypt the challenge using the private key. */ - if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0) + if (rsa_private_decrypt(challenge, challenge, private->rsa) != 0) goto failure; /* The response is MD5 of decrypted challenge plus session id. */ @@ -365,12 +365,16 @@ process_sign_request2(SocketEntry *e) static void process_remove_identity(SocketEntry *e, int version) { - u_int blen, bits; + u_int blen; int success = 0; Key *key = NULL; u_char *blob; +#ifdef WITH_SSH1 + u_int bits; +#endif /* WITH_SSH1 */ switch (version) { +#ifdef WITH_SSH1 case 1: key = key_new(KEY_RSA1); bits = buffer_get_int(&e->request); @@ -381,6 +385,7 @@ process_remove_identity(SocketEntry *e, int version) logit("Warning: identity keysize mismatch: actual %u, announced %u", key_size(key), bits); break; +#endif /* WITH_SSH1 */ case 2: blob = buffer_get_string(&e->request, &blen); key = key_from_blob(blob, blen); @@ -477,6 +482,7 @@ process_add_identity(SocketEntry *e, int version) Key *k = NULL; switch (version) { +#ifdef WITH_SSH1 case 1: k = key_new_private(KEY_RSA1); (void) buffer_get_int(&e->request); /* ignored */ @@ -490,7 +496,9 @@ process_add_identity(SocketEntry *e, int version) buffer_get_bignum(&e->request, k->rsa->p); /* q */ /* Generate additional parameters */ - rsa_generate_additional_parameters(k->rsa); + if (rsa_generate_additional_parameters(k->rsa) != 0) + fatal("%s: rsa_generate_additional_parameters " + "error", __func__); /* enable blinding */ if (RSA_blinding_on(k->rsa, NULL) != 1) { @@ -499,6 +507,7 @@ process_add_identity(SocketEntry *e, int version) goto send; } break; +#endif /* WITH_SSH1 */ case 2: k = key_private_deserialize(&e->request); if (k == NULL) { @@ -507,11 +516,10 @@ process_add_identity(SocketEntry *e, int version) } break; } - comment = buffer_get_string(&e->request, NULL); - if (k == NULL) { - free(comment); + if (k == NULL) goto send; - } + comment = buffer_get_string(&e->request, NULL); + while (buffer_len(&e->request)) { switch ((type = buffer_get_char(&e->request))) { case SSH_AGENT_CONSTRAIN_LIFETIME: diff --git a/ssh-dss.c b/ssh-dss.c index 6b4abcb7d..02d1ec2d6 100644 --- a/ssh-dss.c +++ b/ssh-dss.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-dss.c,v 1.31 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: ssh-dss.c,v 1.32 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -33,157 +33,186 @@ #include #include -#include "xmalloc.h" -#include "buffer.h" +#include "sshbuf.h" #include "compat.h" -#include "log.h" -#include "key.h" +#include "ssherr.h" #include "digest.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" #define INTBLOB_LEN 20 #define SIGBLOB_LEN (2*INTBLOB_LEN) int -ssh_dss_sign(const Key *key, u_char **sigp, u_int *lenp, - const u_char *data, u_int datalen) +ssh_dss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) { - DSA_SIG *sig; + DSA_SIG *sig = NULL; u_char digest[SSH_DIGEST_MAX_LENGTH], sigblob[SIGBLOB_LEN]; - u_int rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); - Buffer b; - - if (key == NULL || key_type_plain(key->type) != KEY_DSA || - key->dsa == NULL) { - error("%s: no DSA key", __func__); - return -1; - } - - if (ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, - digest, sizeof(digest)) != 0) { - error("%s: ssh_digest_memory failed", __func__); - return -1; - } - - sig = DSA_do_sign(digest, dlen, key->dsa); - explicit_bzero(digest, sizeof(digest)); - - if (sig == NULL) { - error("ssh_dss_sign: sign failed"); - return -1; + size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); + struct sshbuf *b = NULL; + int ret = SSH_ERR_INVALID_ARGUMENT; + + if (lenp != NULL) + *lenp = 0; + if (sigp != NULL) + *sigp = NULL; + + if (key == NULL || key->dsa == NULL || + sshkey_type_plain(key->type) != KEY_DSA) + return SSH_ERR_INVALID_ARGUMENT; + if (dlen == 0) + return SSH_ERR_INTERNAL_ERROR; + + if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, + digest, sizeof(digest))) != 0) + goto out; + + if ((sig = DSA_do_sign(digest, dlen, key->dsa)) == NULL) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } rlen = BN_num_bytes(sig->r); slen = BN_num_bytes(sig->s); if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { - error("bad sig size %u %u", rlen, slen); - DSA_SIG_free(sig); - return -1; + ret = SSH_ERR_INTERNAL_ERROR; + goto out; } explicit_bzero(sigblob, SIGBLOB_LEN); - BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen); - BN_bn2bin(sig->s, sigblob+ SIGBLOB_LEN - slen); - DSA_SIG_free(sig); + BN_bn2bin(sig->r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen); + BN_bn2bin(sig->s, sigblob + SIGBLOB_LEN - slen); - if (datafellows & SSH_BUG_SIGBLOB) { - if (lenp != NULL) - *lenp = SIGBLOB_LEN; + if (compat & SSH_BUG_SIGBLOB) { if (sigp != NULL) { - *sigp = xmalloc(SIGBLOB_LEN); + if ((*sigp = malloc(SIGBLOB_LEN)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } memcpy(*sigp, sigblob, SIGBLOB_LEN); } + if (lenp != NULL) + *lenp = SIGBLOB_LEN; + ret = 0; } else { /* ietf-drafts */ - buffer_init(&b); - buffer_put_cstring(&b, "ssh-dss"); - buffer_put_string(&b, sigblob, SIGBLOB_LEN); - len = buffer_len(&b); - if (lenp != NULL) - *lenp = len; + if ((b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshbuf_put_cstring(b, "ssh-dss")) != 0 || + (ret = sshbuf_put_string(b, sigblob, SIGBLOB_LEN)) != 0) + goto out; + len = sshbuf_len(b); if (sigp != NULL) { - *sigp = xmalloc(len); - memcpy(*sigp, buffer_ptr(&b), len); + if ((*sigp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); } - buffer_free(&b); + if (lenp != NULL) + *lenp = len; + ret = 0; } - return 0; + out: + explicit_bzero(digest, sizeof(digest)); + if (sig != NULL) + DSA_SIG_free(sig); + if (b != NULL) + sshbuf_free(b); + return ret; } + int -ssh_dss_verify(const Key *key, const u_char *signature, u_int signaturelen, - const u_char *data, u_int datalen) +ssh_dss_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) { - DSA_SIG *sig; - u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob; - u_int len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); - int rlen, ret; - Buffer b; - - if (key == NULL || key_type_plain(key->type) != KEY_DSA || - key->dsa == NULL) { - error("%s: no DSA key", __func__); - return -1; - } + DSA_SIG *sig = NULL; + u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob = NULL; + size_t len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL; + char *ktype = NULL; + + if (key == NULL || key->dsa == NULL || + sshkey_type_plain(key->type) != KEY_DSA) + return SSH_ERR_INVALID_ARGUMENT; + if (dlen == 0) + return SSH_ERR_INTERNAL_ERROR; /* fetch signature */ - if (datafellows & SSH_BUG_SIGBLOB) { - sigblob = xmalloc(signaturelen); + if (compat & SSH_BUG_SIGBLOB) { + if ((sigblob = malloc(signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; memcpy(sigblob, signature, signaturelen); len = signaturelen; } else { /* ietf-drafts */ - char *ktype; - buffer_init(&b); - buffer_append(&b, signature, signaturelen); - ktype = buffer_get_cstring(&b, NULL); + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || + sshbuf_get_string(b, &sigblob, &len) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } if (strcmp("ssh-dss", ktype) != 0) { - error("%s: cannot handle type %s", __func__, ktype); - buffer_free(&b); - free(ktype); - return -1; + ret = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; } - free(ktype); - sigblob = buffer_get_string(&b, &len); - rlen = buffer_len(&b); - buffer_free(&b); - if (rlen != 0) { - error("%s: remaining bytes in signature %d", - __func__, rlen); - free(sigblob); - return -1; + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; } } if (len != SIGBLOB_LEN) { - fatal("bad sigbloblen %u != SIGBLOB_LEN", len); + ret = SSH_ERR_INVALID_FORMAT; + goto out; } /* parse signature */ - if ((sig = DSA_SIG_new()) == NULL) - fatal("%s: DSA_SIG_new failed", __func__); - if ((sig->r = BN_new()) == NULL) - fatal("%s: BN_new failed", __func__); - if ((sig->s = BN_new()) == NULL) - fatal("ssh_dss_verify: BN_new failed"); + if ((sig = DSA_SIG_new()) == NULL || + (sig->r = BN_new()) == NULL || + (sig->s = BN_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig->r) == NULL) || - (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s) == NULL)) - fatal("%s: BN_bin2bn failed", __func__); - - /* clean up */ - explicit_bzero(sigblob, len); - free(sigblob); + (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s) == NULL)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } /* sha1 the data */ - if (ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, - digest, sizeof(digest)) != 0) { - error("%s: digest_memory failed", __func__); - return -1; + if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, + digest, sizeof(digest))) != 0) + goto out; + + switch (DSA_do_verify(digest, dlen, sig, key->dsa)) { + case 1: + ret = 0; + break; + case 0: + ret = SSH_ERR_SIGNATURE_INVALID; + goto out; + default: + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } - ret = DSA_do_verify(digest, dlen, sig, key->dsa); + out: explicit_bzero(digest, sizeof(digest)); - - DSA_SIG_free(sig); - - debug("%s: signature %s", __func__, - ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error"); + if (sig != NULL) + DSA_SIG_free(sig); + if (b != NULL) + sshbuf_free(b); + if (ktype != NULL) + free(ktype); + if (sigblob != NULL) { + explicit_bzero(sigblob, len); + free(sigblob); + } return ret; } diff --git a/ssh-ecdsa.c b/ssh-ecdsa.c index 551c9c460..1119db045 100644 --- a/ssh-ecdsa.c +++ b/ssh-ecdsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-ecdsa.c,v 1.10 2014/02/03 23:28:00 djm Exp $ */ +/* $OpenBSD: ssh-ecdsa.c,v 1.11 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -37,141 +37,155 @@ #include -#include "xmalloc.h" -#include "buffer.h" -#include "compat.h" -#include "log.h" -#include "key.h" +#include "sshbuf.h" +#include "ssherr.h" #include "digest.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" +/* ARGSUSED */ int -ssh_ecdsa_sign(const Key *key, u_char **sigp, u_int *lenp, - const u_char *data, u_int datalen) +ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) { - ECDSA_SIG *sig; + ECDSA_SIG *sig = NULL; int hash_alg; u_char digest[SSH_DIGEST_MAX_LENGTH]; - u_int len, dlen; - Buffer b, bb; + size_t len, dlen; + struct sshbuf *b = NULL, *bb = NULL; + int ret = SSH_ERR_INTERNAL_ERROR; - if (key == NULL || key_type_plain(key->type) != KEY_ECDSA || - key->ecdsa == NULL) { - error("%s: no ECDSA key", __func__); - return -1; + if (lenp != NULL) + *lenp = 0; + if (sigp != NULL) + *sigp = NULL; + + if (key == NULL || key->ecdsa == NULL || + sshkey_type_plain(key->type) != KEY_ECDSA) + return SSH_ERR_INVALID_ARGUMENT; + + if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || + (dlen = ssh_digest_bytes(hash_alg)) == 0) + return SSH_ERR_INTERNAL_ERROR; + if ((ret = ssh_digest_memory(hash_alg, data, datalen, + digest, sizeof(digest))) != 0) + goto out; + + if ((sig = ECDSA_do_sign(digest, dlen, key->ecdsa)) == NULL) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } - hash_alg = key_ec_nid_to_hash_alg(key->ecdsa_nid); - if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { - error("%s: bad hash algorithm %d", __func__, hash_alg); - return -1; - } - if (ssh_digest_memory(hash_alg, data, datalen, - digest, sizeof(digest)) != 0) { - error("%s: digest_memory failed", __func__); - return -1; + if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; } - - sig = ECDSA_do_sign(digest, dlen, key->ecdsa); - explicit_bzero(digest, sizeof(digest)); - - if (sig == NULL) { - error("%s: sign failed", __func__); - return -1; + if ((ret = sshbuf_put_bignum2(bb, sig->r)) != 0 || + (ret = sshbuf_put_bignum2(bb, sig->s)) != 0) + goto out; + if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 || + (ret = sshbuf_put_stringb(b, bb)) != 0) + goto out; + len = sshbuf_len(b); + if (sigp != NULL) { + if ((*sigp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); } - - buffer_init(&bb); - buffer_put_bignum2(&bb, sig->r); - buffer_put_bignum2(&bb, sig->s); - ECDSA_SIG_free(sig); - - buffer_init(&b); - buffer_put_cstring(&b, key_ssh_name_plain(key)); - buffer_put_string(&b, buffer_ptr(&bb), buffer_len(&bb)); - buffer_free(&bb); - len = buffer_len(&b); if (lenp != NULL) *lenp = len; - if (sigp != NULL) { - *sigp = xmalloc(len); - memcpy(*sigp, buffer_ptr(&b), len); - } - buffer_free(&b); - - return 0; + ret = 0; + out: + explicit_bzero(digest, sizeof(digest)); + if (b != NULL) + sshbuf_free(b); + if (bb != NULL) + sshbuf_free(bb); + if (sig != NULL) + ECDSA_SIG_free(sig); + return ret; } + +/* ARGSUSED */ int -ssh_ecdsa_verify(const Key *key, const u_char *signature, u_int signaturelen, - const u_char *data, u_int datalen) +ssh_ecdsa_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) { - ECDSA_SIG *sig; + ECDSA_SIG *sig = NULL; int hash_alg; - u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob; - u_int len, dlen; - int rlen, ret; - Buffer b, bb; - char *ktype; - - if (key == NULL || key_type_plain(key->type) != KEY_ECDSA || - key->ecdsa == NULL) { - error("%s: no ECDSA key", __func__); - return -1; - } + u_char digest[SSH_DIGEST_MAX_LENGTH]; + size_t dlen; + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL, *sigbuf = NULL; + char *ktype = NULL; + + if (key == NULL || key->ecdsa == NULL || + sshkey_type_plain(key->type) != KEY_ECDSA) + return SSH_ERR_INVALID_ARGUMENT; + + if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || + (dlen = ssh_digest_bytes(hash_alg)) == 0) + return SSH_ERR_INTERNAL_ERROR; /* fetch signature */ - buffer_init(&b); - buffer_append(&b, signature, signaturelen); - ktype = buffer_get_string(&b, NULL); - if (strcmp(key_ssh_name_plain(key), ktype) != 0) { - error("%s: cannot handle type %s", __func__, ktype); - buffer_free(&b); - free(ktype); - return -1; + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || + sshbuf_froms(b, &sigbuf) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; } - free(ktype); - sigblob = buffer_get_string(&b, &len); - rlen = buffer_len(&b); - buffer_free(&b); - if (rlen != 0) { - error("%s: remaining bytes in signature %d", __func__, rlen); - free(sigblob); - return -1; + if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { + ret = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; } /* parse signature */ - if ((sig = ECDSA_SIG_new()) == NULL) - fatal("%s: ECDSA_SIG_new failed", __func__); - - buffer_init(&bb); - buffer_append(&bb, sigblob, len); - buffer_get_bignum2(&bb, sig->r); - buffer_get_bignum2(&bb, sig->s); - if (buffer_len(&bb) != 0) - fatal("%s: remaining bytes in inner sigblob", __func__); - buffer_free(&bb); - - /* clean up */ - explicit_bzero(sigblob, len); - free(sigblob); - - /* hash the data */ - hash_alg = key_ec_nid_to_hash_alg(key->ecdsa_nid); - if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { - error("%s: bad hash algorithm %d", __func__, hash_alg); - return -1; + if ((sig = ECDSA_SIG_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshbuf_get_bignum2(sigbuf, sig->r) != 0 || + sshbuf_get_bignum2(sigbuf, sig->s) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(sigbuf) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; } - if (ssh_digest_memory(hash_alg, data, datalen, - digest, sizeof(digest)) != 0) { - error("%s: digest_memory failed", __func__); - return -1; + if ((ret = ssh_digest_memory(hash_alg, data, datalen, + digest, sizeof(digest))) != 0) + goto out; + + switch (ECDSA_do_verify(digest, dlen, sig, key->ecdsa)) { + case 1: + ret = 0; + break; + case 0: + ret = SSH_ERR_SIGNATURE_INVALID; + goto out; + default: + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } - ret = ECDSA_do_verify(digest, dlen, sig, key->ecdsa); + out: explicit_bzero(digest, sizeof(digest)); - - ECDSA_SIG_free(sig); - - debug("%s: signature %s", __func__, - ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error"); + if (sigbuf != NULL) + sshbuf_free(sigbuf); + if (b != NULL) + sshbuf_free(b); + if (sig != NULL) + ECDSA_SIG_free(sig); + free(ktype); return ret; } diff --git a/ssh-ed25519.c b/ssh-ed25519.c index 160d1f23b..cb87d4790 100644 --- a/ssh-ed25519.c +++ b/ssh-ed25519.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-ed25519.c,v 1.3 2014/02/23 20:03:42 djm Exp $ */ +/* $OpenBSD: ssh-ed25519.c,v 1.4 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2013 Markus Friedl * @@ -18,132 +18,149 @@ #include "includes.h" #include +#include #include "crypto_api.h" -#include #include #include #include "xmalloc.h" #include "log.h" #include "buffer.h" -#include "key.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" +#include "ssherr.h" #include "ssh.h" int -ssh_ed25519_sign(const Key *key, u_char **sigp, u_int *lenp, - const u_char *data, u_int datalen) +ssh_ed25519_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) { - u_char *sig; - u_int slen, len; + u_char *sig = NULL; + size_t slen = 0, len; unsigned long long smlen; - int ret; - Buffer b; + int r, ret; + struct sshbuf *b = NULL; - if (key == NULL || key_type_plain(key->type) != KEY_ED25519 || - key->ed25519_sk == NULL) { - error("%s: no ED25519 key", __func__); - return -1; - } + if (lenp != NULL) + *lenp = 0; + if (sigp != NULL) + *sigp = NULL; - if (datalen >= UINT_MAX - crypto_sign_ed25519_BYTES) { - error("%s: datalen %u too long", __func__, datalen); - return -1; - } + if (key == NULL || + sshkey_type_plain(key->type) != KEY_ED25519 || + key->ed25519_sk == NULL || + datalen >= INT_MAX - crypto_sign_ed25519_BYTES) + return SSH_ERR_INVALID_ARGUMENT; smlen = slen = datalen + crypto_sign_ed25519_BYTES; - sig = xmalloc(slen); + if ((sig = malloc(slen)) == NULL) + return SSH_ERR_ALLOC_FAIL; if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen, key->ed25519_sk)) != 0 || smlen <= datalen) { - error("%s: crypto_sign_ed25519 failed: %d", __func__, ret); - free(sig); - return -1; + r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */ + goto out; } /* encode signature */ - buffer_init(&b); - buffer_put_cstring(&b, "ssh-ed25519"); - buffer_put_string(&b, sig, smlen - datalen); - len = buffer_len(&b); + if ((b = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_cstring(b, "ssh-ed25519")) != 0 || + (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0) + goto out; + len = sshbuf_len(b); + if (sigp != NULL) { + if ((*sigp = malloc(len)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); + } if (lenp != NULL) *lenp = len; - if (sigp != NULL) { - *sigp = xmalloc(len); - memcpy(*sigp, buffer_ptr(&b), len); + /* success */ + r = 0; + out: + sshbuf_free(b); + if (sig != NULL) { + explicit_bzero(sig, slen); + free(sig); } - buffer_free(&b); - explicit_bzero(sig, slen); - free(sig); - return 0; + return r; } int -ssh_ed25519_verify(const Key *key, const u_char *signature, u_int signaturelen, - const u_char *data, u_int datalen) +ssh_ed25519_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) { - Buffer b; - char *ktype; - u_char *sigblob, *sm, *m; - u_int len; - unsigned long long smlen, mlen; - int rlen, ret; + struct sshbuf *b = NULL; + char *ktype = NULL; + const u_char *sigblob; + u_char *sm = NULL, *m = NULL; + size_t len; + unsigned long long smlen = 0, mlen = 0; + int r, ret; - if (key == NULL || key_type_plain(key->type) != KEY_ED25519 || - key->ed25519_pk == NULL) { - error("%s: no ED25519 key", __func__); - return -1; - } - buffer_init(&b); - buffer_append(&b, signature, signaturelen); - ktype = buffer_get_cstring(&b, NULL); + if (key == NULL || + sshkey_type_plain(key->type) != KEY_ED25519 || + key->ed25519_pk == NULL || + datalen >= INT_MAX - crypto_sign_ed25519_BYTES) + return SSH_ERR_INVALID_ARGUMENT; + + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || + (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) + goto out; if (strcmp("ssh-ed25519", ktype) != 0) { - error("%s: cannot handle type %s", __func__, ktype); - buffer_free(&b); - free(ktype); - return -1; + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; } - free(ktype); - sigblob = buffer_get_string(&b, &len); - rlen = buffer_len(&b); - buffer_free(&b); - if (rlen != 0) { - error("%s: remaining bytes in signature %d", __func__, rlen); - free(sigblob); - return -1; + if (sshbuf_len(b) != 0) { + r = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; } if (len > crypto_sign_ed25519_BYTES) { - error("%s: len %u > crypto_sign_ed25519_BYTES %u", __func__, - len, crypto_sign_ed25519_BYTES); - free(sigblob); - return -1; + r = SSH_ERR_INVALID_FORMAT; + goto out; } + if (datalen >= SIZE_MAX - len) + return SSH_ERR_INVALID_ARGUMENT; smlen = len + datalen; - sm = xmalloc(smlen); + mlen = smlen; + if ((sm = malloc(smlen)) == NULL || (m = xmalloc(mlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } memcpy(sm, sigblob, len); memcpy(sm+len, data, datalen); - mlen = smlen; - m = xmalloc(mlen); if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, key->ed25519_pk)) != 0) { debug2("%s: crypto_sign_ed25519_open failed: %d", __func__, ret); } - if (ret == 0 && mlen != datalen) { - debug2("%s: crypto_sign_ed25519_open " - "mlen != datalen (%llu != %u)", __func__, mlen, datalen); - ret = -1; + if (ret != 0 || mlen != datalen) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; } /* XXX compare 'm' and 'data' ? */ - - explicit_bzero(sigblob, len); - explicit_bzero(sm, smlen); - explicit_bzero(m, smlen); /* NB. mlen may be invalid if ret != 0 */ - free(sigblob); - free(sm); - free(m); - debug("%s: signature %scorrect", __func__, (ret != 0) ? "in" : ""); - - /* translate return code carefully */ - return (ret == 0) ? 1 : -1; + /* success */ + r = 0; + out: + if (sm != NULL) { + explicit_bzero(sm, smlen); + free(sm); + } + if (m != NULL) { + explicit_bzero(m, smlen); /* NB mlen may be invalid if r != 0 */ + free(m); + } + sshbuf_free(b); + free(ktype); + return r; } + diff --git a/ssh-keygen.c b/ssh-keygen.c index 085f1ec55..e2aa215b2 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.246 2014/04/29 18:01:49 markus Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.247 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -482,7 +482,9 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) buffer_get_bignum_bits(&b, key->rsa->iqmp); buffer_get_bignum_bits(&b, key->rsa->q); buffer_get_bignum_bits(&b, key->rsa->p); - rsa_generate_additional_parameters(key->rsa); + if (rsa_generate_additional_parameters(key->rsa) != 0) + fatal("%s: rsa_generate_additional_parameters " + "error", __func__); break; } rlen = buffer_len(&b); @@ -1637,12 +1639,12 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) public->cert->valid_after = cert_valid_from; public->cert->valid_before = cert_valid_to; if (v00) { - prepare_options_buf(&public->cert->critical, + prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL|OPTIONS_EXTENSIONS); } else { - prepare_options_buf(&public->cert->critical, + prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL); - prepare_options_buf(&public->cert->extensions, + prepare_options_buf(public->cert->extensions, OPTIONS_EXTENSIONS); } public->cert->signature_key = key_from_private(ca); @@ -1913,19 +1915,19 @@ do_show_cert(struct passwd *pw) printf("\n"); } printf(" Critical Options: "); - if (buffer_len(&key->cert->critical) == 0) + if (buffer_len(key->cert->critical) == 0) printf("(none)\n"); else { printf("\n"); - show_options(&key->cert->critical, v00, 1); + show_options(key->cert->critical, v00, 1); } if (!v00) { printf(" Extensions: "); - if (buffer_len(&key->cert->extensions) == 0) + if (buffer_len(key->cert->extensions) == 0) printf("(none)\n"); else { printf("\n"); - show_options(&key->cert->extensions, v00, 0); + show_options(key->cert->extensions, v00, 0); } } exit(0); diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c index 6c9f9d2c1..8c74864aa 100644 --- a/ssh-pkcs11-client.c +++ b/ssh-pkcs11-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-pkcs11-client.c,v 1.4 2013/05/17 00:13:14 djm Exp $ */ +/* $OpenBSD: ssh-pkcs11-client.c,v 1.5 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2010 Markus Friedl. All rights reserved. * @@ -30,6 +30,8 @@ #include #include +#include + #include "pathnames.h" #include "xmalloc.h" #include "buffer.h" diff --git a/ssh-pkcs11-helper.c b/ssh-pkcs11-helper.c index b7c52beb8..0b1d8e4cc 100644 --- a/ssh-pkcs11-helper.c +++ b/ssh-pkcs11-helper.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-pkcs11-helper.c,v 1.7 2013/12/02 02:56:17 djm Exp $ */ +/* $OpenBSD: ssh-pkcs11-helper.c,v 1.8 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2010 Markus Friedl. All rights reserved. * @@ -169,7 +169,7 @@ process_sign(void) { u_char *blob, *data, *signature = NULL; u_int blen, dlen, slen = 0; - int ok = -1, ret; + int ok = -1; Key *key, *found; Buffer msg; @@ -179,6 +179,9 @@ process_sign(void) if ((key = key_from_blob(blob, blen)) != NULL) { if ((found = lookup_key(key)) != NULL) { +#ifdef WITH_OPENSSL + int ret; + slen = RSA_size(key->rsa); signature = xmalloc(slen); if ((ret = RSA_private_encrypt(dlen, data, signature, @@ -186,6 +189,7 @@ process_sign(void) slen = ret; ok = 0; } +#endif /* WITH_OPENSSL */ } key_free(key); } diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c index d3e877291..c96be3bd2 100644 --- a/ssh-pkcs11.c +++ b/ssh-pkcs11.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-pkcs11.c,v 1.13 2014/05/02 03:27:54 djm Exp $ */ +/* $OpenBSD: ssh-pkcs11.c,v 1.14 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2010 Markus Friedl. All rights reserved. * @@ -520,7 +520,7 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, key = key_new(KEY_UNSPEC); key->rsa = rsa; key->type = KEY_RSA; - key->flags |= KEY_FLAG_EXT; + key->flags |= SSHKEY_FLAG_EXT; if (pkcs11_key_included(keysp, nkeys, key)) { key_free(key); } else { diff --git a/ssh-rsa.c b/ssh-rsa.c index c6f25b3ee..fec1953b4 100644 --- a/ssh-rsa.c +++ b/ssh-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-rsa.c,v 1.51 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: ssh-rsa.c,v 1.52 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2000, 2003 Markus Friedl * @@ -25,163 +25,167 @@ #include #include -#include "xmalloc.h" -#include "log.h" -#include "buffer.h" -#include "key.h" +#include "sshbuf.h" #include "compat.h" -#include "misc.h" -#include "ssh.h" +#include "ssherr.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" #include "digest.h" -static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *); +static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *); /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ int -ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp, - const u_char *data, u_int datalen) +ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) { int hash_alg; - u_char digest[SSH_DIGEST_MAX_LENGTH], *sig; - u_int slen, dlen, len; - int ok, nid; - Buffer b; + u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL; + size_t slen; + u_int dlen, len; + int nid, ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL; - if (key == NULL || key_type_plain(key->type) != KEY_RSA || - key->rsa == NULL) { - error("%s: no RSA key", __func__); - return -1; - } + if (lenp != NULL) + *lenp = 0; + if (sigp != NULL) + *sigp = NULL; + + if (key == NULL || key->rsa == NULL || + sshkey_type_plain(key->type) != KEY_RSA) + return SSH_ERR_INVALID_ARGUMENT; + slen = RSA_size(key->rsa); + if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) + return SSH_ERR_INVALID_ARGUMENT; /* hash the data */ hash_alg = SSH_DIGEST_SHA1; nid = NID_sha1; - if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { - error("%s: bad hash algorithm %d", __func__, hash_alg); - return -1; - } - if (ssh_digest_memory(hash_alg, data, datalen, - digest, sizeof(digest)) != 0) { - error("%s: ssh_digest_memory failed", __func__); - return -1; - } - - slen = RSA_size(key->rsa); - sig = xmalloc(slen); - - ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa); - explicit_bzero(digest, sizeof(digest)); + if ((dlen = ssh_digest_bytes(hash_alg)) == 0) + return SSH_ERR_INTERNAL_ERROR; + if ((ret = ssh_digest_memory(hash_alg, data, datalen, + digest, sizeof(digest))) != 0) + goto out; - if (ok != 1) { - int ecode = ERR_get_error(); + if ((sig = malloc(slen)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } - error("%s: RSA_sign failed: %s", __func__, - ERR_error_string(ecode, NULL)); - free(sig); - return -1; + if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } if (len < slen) { - u_int diff = slen - len; - debug("slen %u > len %u", slen, len); + size_t diff = slen - len; memmove(sig + diff, sig, len); explicit_bzero(sig, diff); } else if (len > slen) { - error("%s: slen %u slen2 %u", __func__, slen, len); - free(sig); - return -1; + ret = SSH_ERR_INTERNAL_ERROR; + goto out; } /* encode signature */ - buffer_init(&b); - buffer_put_cstring(&b, "ssh-rsa"); - buffer_put_string(&b, sig, slen); - len = buffer_len(&b); + if ((b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshbuf_put_cstring(b, "ssh-rsa")) != 0 || + (ret = sshbuf_put_string(b, sig, slen)) != 0) + goto out; + len = sshbuf_len(b); + if (sigp != NULL) { + if ((*sigp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); + } if (lenp != NULL) *lenp = len; - if (sigp != NULL) { - *sigp = xmalloc(len); - memcpy(*sigp, buffer_ptr(&b), len); + ret = 0; + out: + explicit_bzero(digest, sizeof(digest)); + if (sig != NULL) { + explicit_bzero(sig, slen); + free(sig); } - buffer_free(&b); - explicit_bzero(sig, slen); - free(sig); - + if (b != NULL) + sshbuf_free(b); return 0; } int -ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen, - const u_char *data, u_int datalen) +ssh_rsa_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) { - Buffer b; - int hash_alg; - char *ktype; - u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob; - u_int len, dlen, modlen; - int rlen, ret; + char *ktype = NULL; + int hash_alg, ret = SSH_ERR_INTERNAL_ERROR; + size_t len, diff, modlen, dlen; + struct sshbuf *b = NULL; + u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL; - if (key == NULL || key_type_plain(key->type) != KEY_RSA || - key->rsa == NULL) { - error("%s: no RSA key", __func__); - return -1; - } + if (key == NULL || key->rsa == NULL || + sshkey_type_plain(key->type) != KEY_RSA || + BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) + return SSH_ERR_INVALID_ARGUMENT; - if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { - error("%s: RSA modulus too small: %d < minimum %d bits", - __func__, BN_num_bits(key->rsa->n), - SSH_RSA_MINIMUM_MODULUS_SIZE); - return -1; + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; } - buffer_init(&b); - buffer_append(&b, signature, signaturelen); - ktype = buffer_get_cstring(&b, NULL); if (strcmp("ssh-rsa", ktype) != 0) { - error("%s: cannot handle type %s", __func__, ktype); - buffer_free(&b); - free(ktype); - return -1; + ret = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; } - free(ktype); - sigblob = buffer_get_string(&b, &len); - rlen = buffer_len(&b); - buffer_free(&b); - if (rlen != 0) { - error("%s: remaining bytes in signature %d", __func__, rlen); - free(sigblob); - return -1; + if (sshbuf_get_string(b, &sigblob, &len) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; } /* RSA_verify expects a signature of RSA_size */ modlen = RSA_size(key->rsa); if (len > modlen) { - error("%s: len %u > modlen %u", __func__, len, modlen); - free(sigblob); - return -1; + ret = SSH_ERR_KEY_BITS_MISMATCH; + goto out; } else if (len < modlen) { - u_int diff = modlen - len; - debug("%s: add padding: modlen %u > len %u", __func__, - modlen, len); - sigblob = xrealloc(sigblob, 1, modlen); + diff = modlen - len; + osigblob = sigblob; + if ((sigblob = realloc(sigblob, modlen)) == NULL) { + sigblob = osigblob; /* put it back for clear/free */ + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } memmove(sigblob + diff, sigblob, len); explicit_bzero(sigblob, diff); len = modlen; } - /* hash the data */ hash_alg = SSH_DIGEST_SHA1; if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { - error("%s: bad hash algorithm %d", __func__, hash_alg); - return -1; - } - if (ssh_digest_memory(hash_alg, data, datalen, - digest, sizeof(digest)) != 0) { - error("%s: ssh_digest_memory failed", __func__); - return -1; + ret = SSH_ERR_INTERNAL_ERROR; + goto out; } + if ((ret = ssh_digest_memory(hash_alg, data, datalen, + digest, sizeof(digest))) != 0) + goto out; ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len, key->rsa); + out: + if (sigblob != NULL) { + explicit_bzero(sigblob, len); + free(sigblob); + } + if (ktype != NULL) + free(ktype); + if (b != NULL) + sshbuf_free(b); explicit_bzero(digest, sizeof(digest)); - explicit_bzero(sigblob, len); - free(sigblob); - debug("%s: signature %scorrect", __func__, (ret == 0) ? "in" : ""); return ret; } @@ -204,15 +208,15 @@ static const u_char id_sha1[] = { }; static int -openssh_RSA_verify(int hash_alg, u_char *hash, u_int hashlen, - u_char *sigbuf, u_int siglen, RSA *rsa) +openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, + u_char *sigbuf, size_t siglen, RSA *rsa) { - u_int ret, rsasize, oidlen = 0, hlen = 0; + size_t ret, rsasize = 0, oidlen = 0, hlen = 0; int len, oidmatch, hashmatch; const u_char *oid = NULL; u_char *decrypted = NULL; - ret = 0; + ret = SSH_ERR_INTERNAL_ERROR; switch (hash_alg) { case SSH_DIGEST_SHA1: oid = id_sha1; @@ -223,37 +227,39 @@ openssh_RSA_verify(int hash_alg, u_char *hash, u_int hashlen, goto done; } if (hashlen != hlen) { - error("bad hashlen"); + ret = SSH_ERR_INVALID_ARGUMENT; goto done; } rsasize = RSA_size(rsa); - if (siglen == 0 || siglen > rsasize) { - error("bad siglen"); + if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM || + siglen == 0 || siglen > rsasize) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto done; + } + if ((decrypted = malloc(rsasize)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; goto done; } - decrypted = xmalloc(rsasize); if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, RSA_PKCS1_PADDING)) < 0) { - error("RSA_public_decrypt failed: %s", - ERR_error_string(ERR_get_error(), NULL)); + ret = SSH_ERR_LIBCRYPTO_ERROR; goto done; } - if (len < 0 || (u_int)len != hlen + oidlen) { - error("bad decrypted len: %d != %d + %d", len, hlen, oidlen); + if (len < 0 || (size_t)len != hlen + oidlen) { + ret = SSH_ERR_INVALID_FORMAT; goto done; } oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; - if (!oidmatch) { - error("oid mismatch"); - goto done; - } - if (!hashmatch) { - error("hash mismatch"); + if (!oidmatch || !hashmatch) { + ret = SSH_ERR_SIGNATURE_INVALID; goto done; } - ret = 1; + ret = 0; done: - free(decrypted); + if (decrypted) { + explicit_bzero(decrypted, rsasize); + free(decrypted); + } return ret; } diff --git a/sshbuf-misc.c b/sshbuf-misc.c index 22dbfd5a4..bfeffe674 100644 --- a/sshbuf-misc.c +++ b/sshbuf-misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshbuf-misc.c,v 1.1 2014/04/30 05:29:56 djm Exp $ */ +/* $OpenBSD: sshbuf-misc.c,v 1.2 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2011 Damien Miller * @@ -33,12 +33,11 @@ #include "sshbuf.h" void -sshbuf_dump(struct sshbuf *buf, FILE *f) +sshbuf_dump_data(const void *s, size_t len, FILE *f) { - const u_char *p = sshbuf_ptr(buf); - size_t i, j, len = sshbuf_len(buf); + size_t i, j; + const u_char *p = (const u_char *)s; - fprintf(f, "buffer %p len = %zu\n", buf, len); for (i = 0; i < len; i += 16) { fprintf(f, "%.4zd: ", i); for (j = i; j < i + 16; j++) { @@ -60,6 +59,13 @@ sshbuf_dump(struct sshbuf *buf, FILE *f) } } +void +sshbuf_dump(struct sshbuf *buf, FILE *f) +{ + fprintf(f, "buffer %p len = %zu\n", buf, sshbuf_len(buf)); + sshbuf_dump_data(sshbuf_ptr(buf), sshbuf_len(buf), f); +} + char * sshbuf_dtob16(struct sshbuf *buf) { diff --git a/sshbuf.h b/sshbuf.h index 1dd9be45c..4561c0637 100644 --- a/sshbuf.h +++ b/sshbuf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshbuf.h,v 1.2 2014/06/10 21:46:11 dtucker Exp $ */ +/* $OpenBSD: sshbuf.h,v 1.3 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2011 Damien Miller * @@ -216,9 +216,12 @@ int sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g); int sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v); #endif -/* Dump the contents of the buffer to stderr in a human-readable format */ +/* Dump the contents of the buffer in a human-readable format */ void sshbuf_dump(struct sshbuf *buf, FILE *f); +/* Dump specified memory in a human-readable format */ +void sshbuf_dump_data(const void *s, size_t len, FILE *f); + /* Return the hexadecimal representation of the contents of the buffer */ char *sshbuf_dtob16(struct sshbuf *buf); diff --git a/sshconnect.c b/sshconnect.c index 5d14ca6cc..590dfe0f7 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.248 2014/04/29 18:01:49 markus Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.249 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -709,7 +709,7 @@ check_host_cert(const char *host, const Key *host_key) error("%s", reason); return 0; } - if (buffer_len(&host_key->cert->critical) != 0) { + if (buffer_len(host_key->cert->critical) != 0) { error("Certificate for %s contains unsupported " "critical options(s)", host); return 0; diff --git a/sshconnect1.c b/sshconnect1.c index 921408ec1..62a7bd17f 100644 --- a/sshconnect1.c +++ b/sshconnect1.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect1.c,v 1.74 2014/02/02 03:44:32 djm Exp $ */ +/* $OpenBSD: sshconnect1.c,v 1.75 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -166,7 +166,7 @@ respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv) /* Decrypt the challenge using the private key. */ /* XXX think about Bleichenbacher, too */ - if (rsa_private_decrypt(challenge, challenge, prv) <= 0) + if (rsa_private_decrypt(challenge, challenge, prv) != 0) packet_disconnect( "respond_to_rsa_challenge: rsa_private_decrypt failed"); @@ -253,7 +253,7 @@ try_rsa_authentication(int idx) * load the private key. Try first with empty passphrase; if it * fails, ask for a passphrase. */ - if (public->flags & KEY_FLAG_EXT) + if (public->flags & SSHKEY_FLAG_EXT) private = public; else private = key_load_private_type(KEY_RSA1, authfile, "", NULL, @@ -302,7 +302,7 @@ try_rsa_authentication(int idx) respond_to_rsa_challenge(challenge, private->rsa); /* Destroy the private key unless it in external hardware. */ - if (!(private->flags & KEY_FLAG_EXT)) + if (!(private->flags & SSHKEY_FLAG_EXT)) key_free(private); /* We no longer need the challenge. */ @@ -592,8 +592,9 @@ ssh_kex(char *host, struct sockaddr *hostaddr) BN_num_bits(server_key->rsa->n), SSH_KEY_BITS_RESERVED); } - rsa_public_encrypt(key, key, server_key->rsa); - rsa_public_encrypt(key, key, host_key->rsa); + if (rsa_public_encrypt(key, key, server_key->rsa) != 0 || + rsa_public_encrypt(key, key, host_key->rsa) != 0) + fatal("%s: rsa_public_encrypt failed", __func__); } else { /* Host key has smaller modulus (or they are equal). */ if (BN_num_bits(server_key->rsa->n) < @@ -604,8 +605,9 @@ ssh_kex(char *host, struct sockaddr *hostaddr) BN_num_bits(host_key->rsa->n), SSH_KEY_BITS_RESERVED); } - rsa_public_encrypt(key, key, host_key->rsa); - rsa_public_encrypt(key, key, server_key->rsa); + if (rsa_public_encrypt(key, key, host_key->rsa) != 0 || + rsa_public_encrypt(key, key, server_key->rsa) != 0) + fatal("%s: rsa_public_encrypt failed", __func__); } /* Destroy the public keys since we no longer need them. */ diff --git a/sshconnect2.c b/sshconnect2.c index 658398436..eb1b0ae3c 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.208 2014/06/05 22:17:50 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.209 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -970,7 +970,7 @@ identity_sign(Identity *id, u_char **sigp, u_int *lenp, * we have already loaded the private key or * the private key is stored in external hardware */ - if (id->isprivate || (id->key->flags & KEY_FLAG_EXT)) + if (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT)) return (key_sign(id->key, sigp, lenp, data, datalen)); /* load the private key from the file */ if ((prv = load_identity_file(id->filename, id->userprovided)) == NULL) @@ -1178,12 +1178,12 @@ pubkey_prepare(Authctxt *authctxt) } /* Prefer PKCS11 keys that are explicitly listed */ TAILQ_FOREACH_SAFE(id, &files, next, tmp) { - if (id->key == NULL || (id->key->flags & KEY_FLAG_EXT) == 0) + if (id->key == NULL || (id->key->flags & SSHKEY_FLAG_EXT) == 0) continue; found = 0; TAILQ_FOREACH(id2, &files, next) { if (id2->key == NULL || - (id2->key->flags & KEY_FLAG_EXT) == 0) + (id2->key->flags & SSHKEY_FLAG_EXT) == 0) continue; if (key_equal(id->key, id2->key)) { TAILQ_REMOVE(&files, id, next); diff --git a/sshd.c b/sshd.c index 6e7192cf5..67eb66a11 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.426 2014/04/29 18:01:49 markus Exp $ */ +/* $OpenBSD: sshd.c,v 1.427 2014/06/24 01:13:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1031,8 +1031,10 @@ recv_rexec_state(int fd, Buffer *conf) buffer_get_bignum(&m, sensitive_data.server_key->rsa->iqmp); buffer_get_bignum(&m, sensitive_data.server_key->rsa->p); buffer_get_bignum(&m, sensitive_data.server_key->rsa->q); - rsa_generate_additional_parameters( - sensitive_data.server_key->rsa); + if (rsa_generate_additional_parameters( + sensitive_data.server_key->rsa) != 0) + fatal("%s: rsa_generate_additional_parameters " + "error", __func__); #else fatal("ssh1 not supported"); #endif @@ -2215,10 +2217,10 @@ ssh1_session_key(BIGNUM *session_key_int) SSH_KEY_BITS_RESERVED); } if (rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.server_key->rsa) <= 0) + sensitive_data.server_key->rsa) != 0) rsafail++; if (rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.ssh1_host_key->rsa) <= 0) + sensitive_data.ssh1_host_key->rsa) != 0) rsafail++; } else { /* Host key has bigger modulus (or they are equal). */ @@ -2233,10 +2235,10 @@ ssh1_session_key(BIGNUM *session_key_int) SSH_KEY_BITS_RESERVED); } if (rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.ssh1_host_key->rsa) < 0) + sensitive_data.ssh1_host_key->rsa) != 0) rsafail++; if (rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.server_key->rsa) < 0) + sensitive_data.server_key->rsa) != 0) rsafail++; } return (rsafail); diff --git a/sshkey.c b/sshkey.c new file mode 100644 index 000000000..24023d036 --- /dev/null +++ b/sshkey.c @@ -0,0 +1,3843 @@ +/* $OpenBSD: sshkey.c,v 1.2 2014/06/27 18:50:39 markus Exp $ */ +/* + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2008 Alexander von Gernler. All rights reserved. + * Copyright (c) 2010,2011 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include + +#include "crypto_api.h" + +#include +#include +#include +#include + +#include "ssh2.h" +#include "ssherr.h" +#include "misc.h" +#include "sshbuf.h" +#include "rsa.h" +#include "cipher.h" +#include "digest.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" + +/* openssh private key file format */ +#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n" +#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n" +#define MARK_BEGIN_LEN (sizeof(MARK_BEGIN) - 1) +#define MARK_END_LEN (sizeof(MARK_END) - 1) +#define KDFNAME "bcrypt" +#define AUTH_MAGIC "openssh-key-v1" +#define SALT_LEN 16 +#define DEFAULT_CIPHERNAME "aes256-cbc" +#define DEFAULT_ROUNDS 16 + +/* Version identification string for SSH v1 identity files. */ +#define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n" + +static int sshkey_from_blob_internal(const u_char *blob, size_t blen, + struct sshkey **keyp, int allow_cert); + +/* Supported key types */ +struct keytype { + const char *name; + const char *shortname; + int type; + int nid; + int cert; +}; +static const struct keytype keytypes[] = { + { "ssh-ed25519", "ED25519", KEY_ED25519, 0, 0 }, + { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", + KEY_ED25519_CERT, 0, 1 }, +#ifdef WITH_OPENSSL + { NULL, "RSA1", KEY_RSA1, 0, 0 }, + { "ssh-rsa", "RSA", KEY_RSA, 0, 0 }, + { "ssh-dss", "DSA", KEY_DSA, 0, 0 }, +# ifdef OPENSSL_HAS_ECC + { "ecdsa-sha2-nistp256", "ECDSA", KEY_ECDSA, NID_X9_62_prime256v1, 0 }, + { "ecdsa-sha2-nistp384", "ECDSA", KEY_ECDSA, NID_secp384r1, 0 }, +# ifdef OPENSSL_HAS_NISTP521 + { "ecdsa-sha2-nistp521", "ECDSA", KEY_ECDSA, NID_secp521r1, 0 }, +# endif /* OPENSSL_HAS_NISTP521 */ +# endif /* OPENSSL_HAS_ECC */ + { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", KEY_RSA_CERT, 0, 1 }, + { "ssh-dss-cert-v01@openssh.com", "DSA-CERT", KEY_DSA_CERT, 0, 1 }, +# ifdef OPENSSL_HAS_ECC + { "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-CERT", + KEY_ECDSA_CERT, NID_X9_62_prime256v1, 1 }, + { "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT", + KEY_ECDSA_CERT, NID_secp384r1, 1 }, +# ifdef OPENSSL_HAS_NISTP521 + { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT", + KEY_ECDSA_CERT, NID_secp521r1, 1 }, +# endif /* OPENSSL_HAS_NISTP521 */ +# endif /* OPENSSL_HAS_ECC */ + { "ssh-rsa-cert-v00@openssh.com", "RSA-CERT-V00", + KEY_RSA_CERT_V00, 0, 1 }, + { "ssh-dss-cert-v00@openssh.com", "DSA-CERT-V00", + KEY_DSA_CERT_V00, 0, 1 }, +#endif /* WITH_OPENSSL */ + { NULL, NULL, -1, -1, 0 } +}; + +const char * +sshkey_type(const struct sshkey *k) +{ + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + if (kt->type == k->type) + return kt->shortname; + } + return "unknown"; +} + +static const char * +sshkey_ssh_name_from_type_nid(int type, int nid) +{ + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + if (kt->type == type && (kt->nid == 0 || kt->nid == nid)) + return kt->name; + } + return "ssh-unknown"; +} + +int +sshkey_type_is_cert(int type) +{ + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + if (kt->type == type) + return kt->cert; + } + return 0; +} + +const char * +sshkey_ssh_name(const struct sshkey *k) +{ + return sshkey_ssh_name_from_type_nid(k->type, k->ecdsa_nid); +} + +const char * +sshkey_ssh_name_plain(const struct sshkey *k) +{ + return sshkey_ssh_name_from_type_nid(sshkey_type_plain(k->type), + k->ecdsa_nid); +} + +int +sshkey_type_from_name(const char *name) +{ + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + /* Only allow shortname matches for plain key types */ + if ((kt->name != NULL && strcmp(name, kt->name) == 0) || + (!kt->cert && strcasecmp(kt->shortname, name) == 0)) + return kt->type; + } + return KEY_UNSPEC; +} + +int +sshkey_ecdsa_nid_from_name(const char *name) +{ + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + if (kt->type != KEY_ECDSA && kt->type != KEY_ECDSA_CERT) + continue; + if (kt->name != NULL && strcmp(name, kt->name) == 0) + return kt->nid; + } + return -1; +} + +char * +key_alg_list(int certs_only, int plain_only) +{ + char *tmp, *ret = NULL; + size_t nlen, rlen = 0; + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + if (kt->name == NULL) + continue; + if ((certs_only && !kt->cert) || (plain_only && kt->cert)) + continue; + if (ret != NULL) + ret[rlen++] = '\n'; + nlen = strlen(kt->name); + if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { + free(ret); + return NULL; + } + ret = tmp; + memcpy(ret + rlen, kt->name, nlen + 1); + rlen += nlen; + } + return ret; +} + +int +sshkey_names_valid2(const char *names) +{ + char *s, *cp, *p; + + if (names == NULL || strcmp(names, "") == 0) + return 0; + if ((s = cp = strdup(names)) == NULL) + return 0; + for ((p = strsep(&cp, ",")); p && *p != '\0'; + (p = strsep(&cp, ","))) { + switch (sshkey_type_from_name(p)) { + case KEY_RSA1: + case KEY_UNSPEC: + free(s); + return 0; + } + } + free(s); + return 1; +} + +u_int +sshkey_size(const struct sshkey *k) +{ + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_RSA1: + case KEY_RSA: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + return BN_num_bits(k->rsa->n); + case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + return BN_num_bits(k->dsa->p); + case KEY_ECDSA: + case KEY_ECDSA_CERT: + return sshkey_curve_nid_to_bits(k->ecdsa_nid); +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + return 256; /* XXX */ + } + return 0; +} + +int +sshkey_cert_is_legacy(const struct sshkey *k) +{ + switch (k->type) { + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT_V00: + return 1; + default: + return 0; + } +} + +static int +sshkey_type_is_valid_ca(int type) +{ + switch (type) { + case KEY_RSA: + case KEY_DSA: + case KEY_ECDSA: + case KEY_ED25519: + return 1; + default: + return 0; + } +} + +int +sshkey_is_cert(const struct sshkey *k) +{ + if (k == NULL) + return 0; + return sshkey_type_is_cert(k->type); +} + +/* Return the cert-less equivalent to a certified key type */ +int +sshkey_type_plain(int type) +{ + switch (type) { + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + return KEY_RSA; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + return KEY_DSA; + case KEY_ECDSA_CERT: + return KEY_ECDSA; + case KEY_ED25519_CERT: + return KEY_ED25519; + default: + return type; + } +} + +#ifdef WITH_OPENSSL +/* XXX: these are really begging for a table-driven approach */ +int +sshkey_curve_name_to_nid(const char *name) +{ + if (strcmp(name, "nistp256") == 0) + return NID_X9_62_prime256v1; + else if (strcmp(name, "nistp384") == 0) + return NID_secp384r1; +# ifdef OPENSSL_HAS_NISTP521 + else if (strcmp(name, "nistp521") == 0) + return NID_secp521r1; +# endif /* OPENSSL_HAS_NISTP521 */ + else + return -1; +} + +u_int +sshkey_curve_nid_to_bits(int nid) +{ + switch (nid) { + case NID_X9_62_prime256v1: + return 256; + case NID_secp384r1: + return 384; +# ifdef OPENSSL_HAS_NISTP521 + case NID_secp521r1: + return 521; +# endif /* OPENSSL_HAS_NISTP521 */ + default: + return 0; + } +} + +int +sshkey_ecdsa_bits_to_nid(int bits) +{ + switch (bits) { + case 256: + return NID_X9_62_prime256v1; + case 384: + return NID_secp384r1; +# ifdef OPENSSL_HAS_NISTP521 + case 521: + return NID_secp521r1; +# endif /* OPENSSL_HAS_NISTP521 */ + default: + return -1; + } +} + +const char * +sshkey_curve_nid_to_name(int nid) +{ + switch (nid) { + case NID_X9_62_prime256v1: + return "nistp256"; + case NID_secp384r1: + return "nistp384"; +# ifdef OPENSSL_HAS_NISTP521 + case NID_secp521r1: + return "nistp521"; +# endif /* OPENSSL_HAS_NISTP521 */ + default: + return NULL; + } +} + +int +sshkey_ec_nid_to_hash_alg(int nid) +{ + int kbits = sshkey_curve_nid_to_bits(nid); + + if (kbits <= 0) + return -1; + + /* RFC5656 section 6.2.1 */ + if (kbits <= 256) + return SSH_DIGEST_SHA256; + else if (kbits <= 384) + return SSH_DIGEST_SHA384; + else + return SSH_DIGEST_SHA512; +} +#endif /* WITH_OPENSSL */ + +static void +cert_free(struct sshkey_cert *cert) +{ + u_int i; + + if (cert == NULL) + return; + if (cert->certblob != NULL) + sshbuf_free(cert->certblob); + if (cert->critical != NULL) + sshbuf_free(cert->critical); + if (cert->extensions != NULL) + sshbuf_free(cert->extensions); + if (cert->key_id != NULL) + free(cert->key_id); + for (i = 0; i < cert->nprincipals; i++) + free(cert->principals[i]); + if (cert->principals != NULL) + free(cert->principals); + if (cert->signature_key != NULL) + sshkey_free(cert->signature_key); + explicit_bzero(cert, sizeof(*cert)); + free(cert); +} + +static struct sshkey_cert * +cert_new(void) +{ + struct sshkey_cert *cert; + + if ((cert = calloc(1, sizeof(*cert))) == NULL) + return NULL; + if ((cert->certblob = sshbuf_new()) == NULL || + (cert->critical = sshbuf_new()) == NULL || + (cert->extensions = sshbuf_new()) == NULL) { + cert_free(cert); + return NULL; + } + cert->key_id = NULL; + cert->principals = NULL; + cert->signature_key = NULL; + return cert; +} + +struct sshkey * +sshkey_new(int type) +{ + struct sshkey *k; +#ifdef WITH_OPENSSL + RSA *rsa; + DSA *dsa; +#endif /* WITH_OPENSSL */ + + if ((k = calloc(1, sizeof(*k))) == NULL) + return NULL; + k->type = type; + k->ecdsa = NULL; + k->ecdsa_nid = -1; + k->dsa = NULL; + k->rsa = NULL; + k->cert = NULL; + k->ed25519_sk = NULL; + k->ed25519_pk = NULL; + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_RSA1: + case KEY_RSA: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + if ((rsa = RSA_new()) == NULL || + (rsa->n = BN_new()) == NULL || + (rsa->e = BN_new()) == NULL) { + if (rsa != NULL) + RSA_free(rsa); + free(k); + return NULL; + } + k->rsa = rsa; + break; + case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + if ((dsa = DSA_new()) == NULL || + (dsa->p = BN_new()) == NULL || + (dsa->q = BN_new()) == NULL || + (dsa->g = BN_new()) == NULL || + (dsa->pub_key = BN_new()) == NULL) { + if (dsa != NULL) + DSA_free(dsa); + free(k); + return NULL; + } + k->dsa = dsa; + break; + case KEY_ECDSA: + case KEY_ECDSA_CERT: + /* Cannot do anything until we know the group */ + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + /* no need to prealloc */ + break; + case KEY_UNSPEC: + break; + default: + free(k); + return NULL; + break; + } + + if (sshkey_is_cert(k)) { + if ((k->cert = cert_new()) == NULL) { + sshkey_free(k); + return NULL; + } + } + + return k; +} + +int +sshkey_add_private(struct sshkey *k) +{ + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_RSA1: + case KEY_RSA: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: +#define bn_maybe_alloc_failed(p) (p == NULL && (p = BN_new()) == NULL) + if (bn_maybe_alloc_failed(k->rsa->d) || + bn_maybe_alloc_failed(k->rsa->iqmp) || + bn_maybe_alloc_failed(k->rsa->q) || + bn_maybe_alloc_failed(k->rsa->p) || + bn_maybe_alloc_failed(k->rsa->dmq1) || + bn_maybe_alloc_failed(k->rsa->dmp1)) + return SSH_ERR_ALLOC_FAIL; + break; + case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + if (bn_maybe_alloc_failed(k->dsa->priv_key)) + return SSH_ERR_ALLOC_FAIL; + break; +#undef bn_maybe_alloc_failed + case KEY_ECDSA: + case KEY_ECDSA_CERT: + /* Cannot do anything until we know the group */ + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + /* no need to prealloc */ + break; + case KEY_UNSPEC: + break; + default: + return SSH_ERR_INVALID_ARGUMENT; + } + return 0; +} + +struct sshkey * +sshkey_new_private(int type) +{ + struct sshkey *k = sshkey_new(type); + + if (k == NULL) + return NULL; + if (sshkey_add_private(k) != 0) { + sshkey_free(k); + return NULL; + } + return k; +} + +void +sshkey_free(struct sshkey *k) +{ + if (k == NULL) + return; + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_RSA1: + case KEY_RSA: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + if (k->rsa != NULL) + RSA_free(k->rsa); + k->rsa = NULL; + break; + case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + if (k->dsa != NULL) + DSA_free(k->dsa); + k->dsa = NULL; + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + case KEY_ECDSA_CERT: + if (k->ecdsa != NULL) + EC_KEY_free(k->ecdsa); + k->ecdsa = NULL; + break; +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + if (k->ed25519_pk) { + explicit_bzero(k->ed25519_pk, ED25519_PK_SZ); + free(k->ed25519_pk); + k->ed25519_pk = NULL; + } + if (k->ed25519_sk) { + explicit_bzero(k->ed25519_sk, ED25519_SK_SZ); + free(k->ed25519_sk); + k->ed25519_sk = NULL; + } + break; + case KEY_UNSPEC: + break; + default: + break; + } + if (sshkey_is_cert(k)) + cert_free(k->cert); + explicit_bzero(k, sizeof(*k)); + free(k); +} + +static int +cert_compare(struct sshkey_cert *a, struct sshkey_cert *b) +{ + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; + if (sshbuf_len(a->certblob) != sshbuf_len(b->certblob)) + return 0; + if (timingsafe_bcmp(sshbuf_ptr(a->certblob), sshbuf_ptr(b->certblob), + sshbuf_len(a->certblob)) != 0) + return 0; + return 1; +} + +/* + * Compare public portions of key only, allowing comparisons between + * certificates and plain keys too. + */ +int +sshkey_equal_public(const struct sshkey *a, const struct sshkey *b) +{ +#ifdef WITH_OPENSSL + BN_CTX *bnctx; +#endif /* WITH_OPENSSL */ + + if (a == NULL || b == NULL || + sshkey_type_plain(a->type) != sshkey_type_plain(b->type)) + return 0; + + switch (a->type) { +#ifdef WITH_OPENSSL + case KEY_RSA1: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + case KEY_RSA: + return a->rsa != NULL && b->rsa != NULL && + BN_cmp(a->rsa->e, b->rsa->e) == 0 && + BN_cmp(a->rsa->n, b->rsa->n) == 0; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + case KEY_DSA: + return a->dsa != NULL && b->dsa != NULL && + BN_cmp(a->dsa->p, b->dsa->p) == 0 && + BN_cmp(a->dsa->q, b->dsa->q) == 0 && + BN_cmp(a->dsa->g, b->dsa->g) == 0 && + BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA: + if (a->ecdsa == NULL || b->ecdsa == NULL || + EC_KEY_get0_public_key(a->ecdsa) == NULL || + EC_KEY_get0_public_key(b->ecdsa) == NULL) + return 0; + if ((bnctx = BN_CTX_new()) == NULL) + return 0; + if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa), + EC_KEY_get0_group(b->ecdsa), bnctx) != 0 || + EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa), + EC_KEY_get0_public_key(a->ecdsa), + EC_KEY_get0_public_key(b->ecdsa), bnctx) != 0) { + BN_CTX_free(bnctx); + return 0; + } + BN_CTX_free(bnctx); + return 1; +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + return a->ed25519_pk != NULL && b->ed25519_pk != NULL && + memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0; + default: + return 0; + } + /* NOTREACHED */ +} + +int +sshkey_equal(const struct sshkey *a, const struct sshkey *b) +{ + if (a == NULL || b == NULL || a->type != b->type) + return 0; + if (sshkey_is_cert(a)) { + if (!cert_compare(a->cert, b->cert)) + return 0; + } + return sshkey_equal_public(a, b); +} + +static int +to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain) +{ + int type, ret = SSH_ERR_INTERNAL_ERROR; + const char *typename; + + if (key == NULL) + return SSH_ERR_INVALID_ARGUMENT; + + type = force_plain ? sshkey_type_plain(key->type) : key->type; + typename = sshkey_ssh_name_from_type_nid(type, key->ecdsa_nid); + + switch (type) { +#ifdef WITH_OPENSSL + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT: + case KEY_ECDSA_CERT: + case KEY_RSA_CERT: +#endif /* WITH_OPENSSL */ + case KEY_ED25519_CERT: + /* Use the existing blob */ + /* XXX modified flag? */ + if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0) + return ret; + break; +#ifdef WITH_OPENSSL + case KEY_DSA: + if (key->dsa == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((ret = sshbuf_put_cstring(b, typename)) != 0 || + (ret = sshbuf_put_bignum2(b, key->dsa->p)) != 0 || + (ret = sshbuf_put_bignum2(b, key->dsa->q)) != 0 || + (ret = sshbuf_put_bignum2(b, key->dsa->g)) != 0 || + (ret = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0) + return ret; + break; + case KEY_ECDSA: + if (key->ecdsa == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((ret = sshbuf_put_cstring(b, typename)) != 0 || + (ret = sshbuf_put_cstring(b, + sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 || + (ret = sshbuf_put_eckey(b, key->ecdsa)) != 0) + return ret; + break; + case KEY_RSA: + if (key->rsa == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((ret = sshbuf_put_cstring(b, typename)) != 0 || + (ret = sshbuf_put_bignum2(b, key->rsa->e)) != 0 || + (ret = sshbuf_put_bignum2(b, key->rsa->n)) != 0) + return ret; + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + if (key->ed25519_pk == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((ret = sshbuf_put_cstring(b, typename)) != 0 || + (ret = sshbuf_put_string(b, + key->ed25519_pk, ED25519_PK_SZ)) != 0) + return ret; + break; + default: + return SSH_ERR_KEY_TYPE_UNKNOWN; + } + return 0; +} + +int +sshkey_to_blob_buf(const struct sshkey *key, struct sshbuf *b) +{ + return to_blob_buf(key, b, 0); +} + +int +sshkey_plain_to_blob_buf(const struct sshkey *key, struct sshbuf *b) +{ + return to_blob_buf(key, b, 1); +} + +static int +to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain) +{ + int ret = SSH_ERR_INTERNAL_ERROR; + size_t len; + struct sshbuf *b = NULL; + + if (lenp != NULL) + *lenp = 0; + if (blobp != NULL) + *blobp = NULL; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((ret = to_blob_buf(key, b, force_plain)) != 0) + goto out; + len = sshbuf_len(b); + if (lenp != NULL) + *lenp = len; + if (blobp != NULL) { + if ((*blobp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*blobp, sshbuf_ptr(b), len); + } + ret = 0; + out: + sshbuf_free(b); + return ret; +} + +int +sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) +{ + return to_blob(key, blobp, lenp, 0); +} + +int +sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) +{ + return to_blob(key, blobp, lenp, 1); +} + +int +sshkey_fingerprint_raw(const struct sshkey *k, enum sshkey_fp_type dgst_type, + u_char **retp, size_t *lenp) +{ + u_char *blob = NULL, *ret = NULL; + size_t blob_len = 0; + int hash_alg = -1, r = SSH_ERR_INTERNAL_ERROR; + + if (retp != NULL) + *retp = NULL; + if (lenp != NULL) + *lenp = 0; + + switch (dgst_type) { + case SSH_FP_MD5: + hash_alg = SSH_DIGEST_MD5; + break; + case SSH_FP_SHA1: + hash_alg = SSH_DIGEST_SHA1; + break; + case SSH_FP_SHA256: + hash_alg = SSH_DIGEST_SHA256; + break; + default: + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + + if (k->type == KEY_RSA1) { +#ifdef WITH_OPENSSL + int nlen = BN_num_bytes(k->rsa->n); + int elen = BN_num_bytes(k->rsa->e); + + blob_len = nlen + elen; + if (nlen >= INT_MAX - elen || + (blob = malloc(blob_len)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + BN_bn2bin(k->rsa->n, blob); + BN_bn2bin(k->rsa->e, blob + nlen); +#endif /* WITH_OPENSSL */ + } else if ((r = to_blob(k, &blob, &blob_len, 1)) != 0) + goto out; + if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = ssh_digest_memory(hash_alg, blob, blob_len, + ret, SSH_DIGEST_MAX_LENGTH)) != 0) + goto out; + /* success */ + if (retp != NULL) { + *retp = ret; + ret = NULL; + } + if (lenp != NULL) + *lenp = ssh_digest_bytes(hash_alg); + r = 0; + out: + free(ret); + if (blob != NULL) { + explicit_bzero(blob, blob_len); + free(blob); + } + return r; +} + +static char * +fingerprint_hex(u_char *dgst_raw, size_t dgst_raw_len) +{ + char *retval; + size_t i; + + if ((retval = calloc(1, dgst_raw_len * 3 + 1)) == NULL) + return NULL; + for (i = 0; i < dgst_raw_len; i++) { + char hex[4]; + snprintf(hex, sizeof(hex), "%02x:", dgst_raw[i]); + strlcat(retval, hex, dgst_raw_len * 3 + 1); + } + + /* Remove the trailing ':' character */ + retval[(dgst_raw_len * 3) - 1] = '\0'; + return retval; +} + +static char * +fingerprint_bubblebabble(u_char *dgst_raw, size_t dgst_raw_len) +{ + char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' }; + char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm', + 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' }; + u_int i, j = 0, rounds, seed = 1; + char *retval; + + rounds = (dgst_raw_len / 2) + 1; + if ((retval = calloc(rounds, 6)) == NULL) + return NULL; + retval[j++] = 'x'; + for (i = 0; i < rounds; i++) { + u_int idx0, idx1, idx2, idx3, idx4; + if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) { + idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) + + seed) % 6; + idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15; + idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) + + (seed / 6)) % 6; + retval[j++] = vowels[idx0]; + retval[j++] = consonants[idx1]; + retval[j++] = vowels[idx2]; + if ((i + 1) < rounds) { + idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15; + idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15; + retval[j++] = consonants[idx3]; + retval[j++] = '-'; + retval[j++] = consonants[idx4]; + seed = ((seed * 5) + + ((((u_int)(dgst_raw[2 * i])) * 7) + + ((u_int)(dgst_raw[(2 * i) + 1])))) % 36; + } + } else { + idx0 = seed % 6; + idx1 = 16; + idx2 = seed / 6; + retval[j++] = vowels[idx0]; + retval[j++] = consonants[idx1]; + retval[j++] = vowels[idx2]; + } + } + retval[j++] = 'x'; + retval[j++] = '\0'; + return retval; +} + +/* + * Draw an ASCII-Art representing the fingerprint so human brain can + * profit from its built-in pattern recognition ability. + * This technique is called "random art" and can be found in some + * scientific publications like this original paper: + * + * "Hash Visualization: a New Technique to improve Real-World Security", + * Perrig A. and Song D., 1999, International Workshop on Cryptographic + * Techniques and E-Commerce (CrypTEC '99) + * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf + * + * The subject came up in a talk by Dan Kaminsky, too. + * + * If you see the picture is different, the key is different. + * If the picture looks the same, you still know nothing. + * + * The algorithm used here is a worm crawling over a discrete plane, + * leaving a trace (augmenting the field) everywhere it goes. + * Movement is taken from dgst_raw 2bit-wise. Bumping into walls + * makes the respective movement vector be ignored for this turn. + * Graphs are not unambiguous, because circles in graphs can be + * walked in either direction. + */ + +/* + * Field sizes for the random art. Have to be odd, so the starting point + * can be in the exact middle of the picture, and FLDBASE should be >=8 . + * Else pictures would be too dense, and drawing the frame would + * fail, too, because the key type would not fit in anymore. + */ +#define FLDBASE 8 +#define FLDSIZE_Y (FLDBASE + 1) +#define FLDSIZE_X (FLDBASE * 2 + 1) +static char * +fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len, + const struct sshkey *k) +{ + /* + * Chars to be used after each other every time the worm + * intersects with itself. Matter of taste. + */ + char *augmentation_string = " .o+=*BOX@%&#/^SE"; + char *retval, *p; + u_char field[FLDSIZE_X][FLDSIZE_Y]; + size_t i; + u_int b; + int x, y; + size_t len = strlen(augmentation_string) - 1; + + if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL) + return NULL; + + /* initialize field */ + memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); + x = FLDSIZE_X / 2; + y = FLDSIZE_Y / 2; + + /* process raw key */ + for (i = 0; i < dgst_raw_len; i++) { + int input; + /* each byte conveys four 2-bit move commands */ + input = dgst_raw[i]; + for (b = 0; b < 4; b++) { + /* evaluate 2 bit, rest is shifted later */ + x += (input & 0x1) ? 1 : -1; + y += (input & 0x2) ? 1 : -1; + + /* assure we are still in bounds */ + x = MAX(x, 0); + y = MAX(y, 0); + x = MIN(x, FLDSIZE_X - 1); + y = MIN(y, FLDSIZE_Y - 1); + + /* augment the field */ + if (field[x][y] < len - 2) + field[x][y]++; + input = input >> 2; + } + } + + /* mark starting point and end point*/ + field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1; + field[x][y] = len; + + /* fill in retval */ + snprintf(retval, FLDSIZE_X, "+--[%4s %4u]", + sshkey_type(k), sshkey_size(k)); + p = strchr(retval, '\0'); + + /* output upper border */ + for (i = p - retval - 1; i < FLDSIZE_X; i++) + *p++ = '-'; + *p++ = '+'; + *p++ = '\n'; + + /* output content */ + for (y = 0; y < FLDSIZE_Y; y++) { + *p++ = '|'; + for (x = 0; x < FLDSIZE_X; x++) + *p++ = augmentation_string[MIN(field[x][y], len)]; + *p++ = '|'; + *p++ = '\n'; + } + + /* output lower border */ + *p++ = '+'; + for (i = 0; i < FLDSIZE_X; i++) + *p++ = '-'; + *p++ = '+'; + + return retval; +} + +char * +sshkey_fingerprint(const struct sshkey *k, enum sshkey_fp_type dgst_type, + enum sshkey_fp_rep dgst_rep) +{ + char *retval = NULL; + u_char *dgst_raw; + size_t dgst_raw_len; + + if (sshkey_fingerprint_raw(k, dgst_type, &dgst_raw, &dgst_raw_len) != 0) + return NULL; + switch (dgst_rep) { + case SSH_FP_HEX: + retval = fingerprint_hex(dgst_raw, dgst_raw_len); + break; + case SSH_FP_BUBBLEBABBLE: + retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len); + break; + case SSH_FP_RANDOMART: + retval = fingerprint_randomart(dgst_raw, dgst_raw_len, k); + break; + default: + explicit_bzero(dgst_raw, dgst_raw_len); + free(dgst_raw); + return NULL; + } + explicit_bzero(dgst_raw, dgst_raw_len); + free(dgst_raw); + return retval; +} + +#ifdef WITH_SSH1 +/* + * Reads a multiple-precision integer in decimal from the buffer, and advances + * the pointer. The integer must already be initialized. This function is + * permitted to modify the buffer. This leaves *cpp to point just beyond the + * last processed character. + */ +static int +read_decimal_bignum(char **cpp, BIGNUM *v) +{ + char *cp; + size_t e; + int skip = 1; /* skip white space */ + + cp = *cpp; + while (*cp == ' ' || *cp == '\t') + cp++; + e = strspn(cp, "0123456789"); + if (e == 0) + return SSH_ERR_INVALID_FORMAT; + if (e > SSHBUF_MAX_BIGNUM * 3) + return SSH_ERR_BIGNUM_TOO_LARGE; + if (cp[e] == '\0') + skip = 0; + else if (index(" \t\r\n", cp[e]) == NULL) + return SSH_ERR_INVALID_FORMAT; + cp[e] = '\0'; + if (BN_dec2bn(&v, cp) <= 0) + return SSH_ERR_INVALID_FORMAT; + *cpp = cp + e + skip; + return 0; +} +#endif /* WITH_SSH1 */ + +/* returns 0 ok, and < 0 error */ +int +sshkey_read(struct sshkey *ret, char **cpp) +{ + struct sshkey *k; + int retval = SSH_ERR_INVALID_FORMAT; + char *cp, *space; + int r, type, curve_nid = -1; + struct sshbuf *blob; +#ifdef WITH_SSH1 + char *ep; + u_long bits; +#endif /* WITH_SSH1 */ + + cp = *cpp; + + switch (ret->type) { + case KEY_RSA1: +#ifdef WITH_SSH1 + /* Get number of bits. */ + bits = strtoul(cp, &ep, 10); + if (*cp == '\0' || index(" \t\r\n", *ep) == NULL || + bits == 0 || bits > SSHBUF_MAX_BIGNUM * 8) + return SSH_ERR_INVALID_FORMAT; /* Bad bit count... */ + /* Get public exponent, public modulus. */ + if ((r = read_decimal_bignum(&ep, ret->rsa->e)) < 0) + return r; + if ((r = read_decimal_bignum(&ep, ret->rsa->n)) < 0) + return r; + *cpp = ep; + /* validate the claimed number of bits */ + if (BN_num_bits(ret->rsa->n) != (int)bits) + return SSH_ERR_KEY_BITS_MISMATCH; + retval = 0; +#endif /* WITH_SSH1 */ + break; + case KEY_UNSPEC: + case KEY_RSA: + case KEY_DSA: + case KEY_ECDSA: + case KEY_ED25519: + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT: + case KEY_ECDSA_CERT: + case KEY_RSA_CERT: + case KEY_ED25519_CERT: + space = strchr(cp, ' '); + if (space == NULL) + return SSH_ERR_INVALID_FORMAT; + *space = '\0'; + type = sshkey_type_from_name(cp); + if (sshkey_type_plain(type) == KEY_ECDSA && + (curve_nid = sshkey_ecdsa_nid_from_name(cp)) == -1) + return SSH_ERR_EC_CURVE_INVALID; + *space = ' '; + if (type == KEY_UNSPEC) + return SSH_ERR_INVALID_FORMAT; + cp = space+1; + if (*cp == '\0') + return SSH_ERR_INVALID_FORMAT; + if (ret->type == KEY_UNSPEC) { + ret->type = type; + } else if (ret->type != type) + return SSH_ERR_KEY_TYPE_MISMATCH; + if ((blob = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + /* trim comment */ + space = strchr(cp, ' '); + if (space) + *space = '\0'; + if ((r = sshbuf_b64tod(blob, cp)) != 0) { + sshbuf_free(blob); + return r; + } + if ((r = sshkey_from_blob(sshbuf_ptr(blob), + sshbuf_len(blob), &k)) != 0) { + sshbuf_free(blob); + return r; + } + sshbuf_free(blob); + if (k->type != type) { + sshkey_free(k); + return SSH_ERR_KEY_TYPE_MISMATCH; + } + if (sshkey_type_plain(type) == KEY_ECDSA && + curve_nid != k->ecdsa_nid) { + sshkey_free(k); + return SSH_ERR_EC_CURVE_MISMATCH; + } +/*XXXX*/ + if (sshkey_is_cert(ret)) { + if (!sshkey_is_cert(k)) { + sshkey_free(k); + return SSH_ERR_EXPECTED_CERT; + } + if (ret->cert != NULL) + cert_free(ret->cert); + ret->cert = k->cert; + k->cert = NULL; + } +#ifdef WITH_OPENSSL + if (sshkey_type_plain(ret->type) == KEY_RSA) { + if (ret->rsa != NULL) + RSA_free(ret->rsa); + ret->rsa = k->rsa; + k->rsa = NULL; +#ifdef DEBUG_PK + RSA_print_fp(stderr, ret->rsa, 8); +#endif + } + if (sshkey_type_plain(ret->type) == KEY_DSA) { + if (ret->dsa != NULL) + DSA_free(ret->dsa); + ret->dsa = k->dsa; + k->dsa = NULL; +#ifdef DEBUG_PK + DSA_print_fp(stderr, ret->dsa, 8); +#endif + } +# ifdef OPENSSL_HAS_ECC + if (sshkey_type_plain(ret->type) == KEY_ECDSA) { + if (ret->ecdsa != NULL) + EC_KEY_free(ret->ecdsa); + ret->ecdsa = k->ecdsa; + ret->ecdsa_nid = k->ecdsa_nid; + k->ecdsa = NULL; + k->ecdsa_nid = -1; +#ifdef DEBUG_PK + sshkey_dump_ec_key(ret->ecdsa); +#endif + } +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + if (sshkey_type_plain(ret->type) == KEY_ED25519) { + free(ret->ed25519_pk); + ret->ed25519_pk = k->ed25519_pk; + k->ed25519_pk = NULL; +#ifdef DEBUG_PK + /* XXX */ +#endif + } + retval = 0; +/*XXXX*/ + sshkey_free(k); + if (retval != 0) + break; + /* advance cp: skip whitespace and data */ + while (*cp == ' ' || *cp == '\t') + cp++; + while (*cp != '\0' && *cp != ' ' && *cp != '\t') + cp++; + *cpp = cp; + break; + default: + return SSH_ERR_INVALID_ARGUMENT; + } + return retval; +} + +int +sshkey_write(const struct sshkey *key, FILE *f) +{ + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL, *bb = NULL; + char *uu = NULL; +#ifdef WITH_SSH1 + u_int bits = 0; + char *dec_e = NULL, *dec_n = NULL; +#endif /* WITH_SSH1 */ + + if (sshkey_is_cert(key)) { + if (key->cert == NULL) + return SSH_ERR_EXPECTED_CERT; + if (sshbuf_len(key->cert->certblob) == 0) + return SSH_ERR_KEY_LACKS_CERTBLOB; + } + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + switch (key->type) { +#ifdef WITH_SSH1 + case KEY_RSA1: + if (key->rsa == NULL || key->rsa->e == NULL || + key->rsa->n == NULL) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((dec_e = BN_bn2dec(key->rsa->e)) == NULL || + (dec_n = BN_bn2dec(key->rsa->n)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + /* size of modulus 'n' */ + if ((bits = BN_num_bits(key->rsa->n)) <= 0) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((ret = sshbuf_putf(b, "%u %s %s", bits, dec_e, dec_n)) != 0) + goto out; +#endif /* WITH_SSH1 */ + break; +#ifdef WITH_OPENSSL + case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + case KEY_ECDSA: + case KEY_ECDSA_CERT: + case KEY_RSA: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + if ((bb = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshkey_to_blob_buf(key, bb)) != 0) + goto out; + if ((uu = sshbuf_dtob64(bb)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshbuf_putf(b, "%s ", sshkey_ssh_name(key))) != 0) + goto out; + if ((ret = sshbuf_put(b, uu, strlen(uu))) != 0) + goto out; + break; + default: + ret = SSH_ERR_KEY_TYPE_UNKNOWN; + goto out; + } + if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) { + if (feof(f)) + errno = EPIPE; + ret = SSH_ERR_SYSTEM_ERROR; + goto out; + } + ret = 0; + out: + if (b != NULL) + sshbuf_free(b); + if (bb != NULL) + sshbuf_free(bb); + if (uu != NULL) + free(uu); +#ifdef WITH_SSH1 + if (dec_e != NULL) + OPENSSL_free(dec_e); + if (dec_n != NULL) + OPENSSL_free(dec_n); +#endif /* WITH_SSH1 */ + return ret; +} + +const char * +sshkey_cert_type(const struct sshkey *k) +{ + switch (k->cert->type) { + case SSH2_CERT_TYPE_USER: + return "user"; + case SSH2_CERT_TYPE_HOST: + return "host"; + default: + return "unknown"; + } +} + +#ifdef WITH_OPENSSL +static int +rsa_generate_private_key(u_int bits, RSA **rsap) +{ + RSA *private = NULL; + BIGNUM *f4 = NULL; + int ret = SSH_ERR_INTERNAL_ERROR; + + if (rsap == NULL || + bits < SSH_RSA_MINIMUM_MODULUS_SIZE || + bits > SSHBUF_MAX_BIGNUM * 8) + return SSH_ERR_INVALID_ARGUMENT; + *rsap = NULL; + if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (!BN_set_word(f4, RSA_F4) || + !RSA_generate_key_ex(private, bits, f4, NULL)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + *rsap = private; + private = NULL; + ret = 0; + out: + if (private != NULL) + RSA_free(private); + if (f4 != NULL) + BN_free(f4); + return ret; +} + +static int +dsa_generate_private_key(u_int bits, DSA **dsap) +{ + DSA *private; + int ret = SSH_ERR_INTERNAL_ERROR; + + if (dsap == NULL || bits != 1024) + return SSH_ERR_INVALID_ARGUMENT; + if ((private = DSA_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + *dsap = NULL; + if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL, + NULL, NULL) || !DSA_generate_key(private)) { + DSA_free(private); + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + *dsap = private; + private = NULL; + ret = 0; + out: + if (private != NULL) + DSA_free(private); + return ret; +} + +# ifdef OPENSSL_HAS_ECC +int +sshkey_ecdsa_key_to_nid(EC_KEY *k) +{ + EC_GROUP *eg; + int nids[] = { + NID_X9_62_prime256v1, + NID_secp384r1, +# ifdef OPENSSL_HAS_NISTP521 + NID_secp521r1, +# endif /* OPENSSL_HAS_NISTP521 */ + -1 + }; + int nid; + u_int i; + BN_CTX *bnctx; + const EC_GROUP *g = EC_KEY_get0_group(k); + + /* + * The group may be stored in a ASN.1 encoded private key in one of two + * ways: as a "named group", which is reconstituted by ASN.1 object ID + * or explicit group parameters encoded into the key blob. Only the + * "named group" case sets the group NID for us, but we can figure + * it out for the other case by comparing against all the groups that + * are supported. + */ + if ((nid = EC_GROUP_get_curve_name(g)) > 0) + return nid; + if ((bnctx = BN_CTX_new()) == NULL) + return -1; + for (i = 0; nids[i] != -1; i++) { + if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL) { + BN_CTX_free(bnctx); + return -1; + } + if (EC_GROUP_cmp(g, eg, bnctx) == 0) + break; + EC_GROUP_free(eg); + } + BN_CTX_free(bnctx); + if (nids[i] != -1) { + /* Use the group with the NID attached */ + EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE); + if (EC_KEY_set_group(k, eg) != 1) { + EC_GROUP_free(eg); + return -1; + } + } + return nids[i]; +} + +static int +ecdsa_generate_private_key(u_int bits, int *nid, EC_KEY **ecdsap) +{ + EC_KEY *private; + int ret = SSH_ERR_INTERNAL_ERROR; + + if (nid == NULL || ecdsap == NULL || + (*nid = sshkey_ecdsa_bits_to_nid(bits)) == -1) + return SSH_ERR_INVALID_ARGUMENT; + *ecdsap = NULL; + if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_KEY_generate_key(private) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE); + *ecdsap = private; + private = NULL; + ret = 0; + out: + if (private != NULL) + EC_KEY_free(private); + return ret; +} +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + +int +sshkey_generate(int type, u_int bits, struct sshkey **keyp) +{ + struct sshkey *k; + int ret = SSH_ERR_INTERNAL_ERROR; + + if (keyp == NULL) + return SSH_ERR_INVALID_ARGUMENT; + *keyp = NULL; + if ((k = sshkey_new(KEY_UNSPEC)) == NULL) + return SSH_ERR_ALLOC_FAIL; + switch (type) { + case KEY_ED25519: + if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL || + (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + break; + } + crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk); + ret = 0; + break; +#ifdef WITH_OPENSSL + case KEY_DSA: + ret = dsa_generate_private_key(bits, &k->dsa); + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + ret = ecdsa_generate_private_key(bits, &k->ecdsa_nid, + &k->ecdsa); + break; +# endif /* OPENSSL_HAS_ECC */ + case KEY_RSA: + case KEY_RSA1: + ret = rsa_generate_private_key(bits, &k->rsa); + break; +#endif /* WITH_OPENSSL */ + default: + ret = SSH_ERR_INVALID_ARGUMENT; + } + if (ret == 0) { + k->type = type; + *keyp = k; + } else + sshkey_free(k); + return ret; +} + +int +sshkey_cert_copy(const struct sshkey *from_key, struct sshkey *to_key) +{ + u_int i; + const struct sshkey_cert *from; + struct sshkey_cert *to; + int ret = SSH_ERR_INTERNAL_ERROR; + + if (to_key->cert != NULL) { + cert_free(to_key->cert); + to_key->cert = NULL; + } + + if ((from = from_key->cert) == NULL) + return SSH_ERR_INVALID_ARGUMENT; + + if ((to = to_key->cert = cert_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + + if ((ret = sshbuf_putb(to->certblob, from->certblob)) != 0 || + (ret = sshbuf_putb(to->critical, from->critical)) != 0 || + (ret = sshbuf_putb(to->extensions, from->extensions) != 0)) + return ret; + + to->serial = from->serial; + to->type = from->type; + if (from->key_id == NULL) + to->key_id = NULL; + else if ((to->key_id = strdup(from->key_id)) == NULL) + return SSH_ERR_ALLOC_FAIL; + to->valid_after = from->valid_after; + to->valid_before = from->valid_before; + if (from->signature_key == NULL) + to->signature_key = NULL; + else if ((ret = sshkey_from_private(from->signature_key, + &to->signature_key)) != 0) + return ret; + + if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS) + return SSH_ERR_INVALID_ARGUMENT; + if (from->nprincipals > 0) { + if ((to->principals = calloc(from->nprincipals, + sizeof(*to->principals))) == NULL) + return SSH_ERR_ALLOC_FAIL; + for (i = 0; i < from->nprincipals; i++) { + to->principals[i] = strdup(from->principals[i]); + if (to->principals[i] == NULL) { + to->nprincipals = i; + return SSH_ERR_ALLOC_FAIL; + } + } + } + to->nprincipals = from->nprincipals; + return 0; +} + +int +sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) +{ + struct sshkey *n = NULL; + int ret = SSH_ERR_INTERNAL_ERROR; + + if (pkp != NULL) + *pkp = NULL; + + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + if ((n = sshkey_new(k->type)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || + (BN_copy(n->dsa->q, k->dsa->q) == NULL) || + (BN_copy(n->dsa->g, k->dsa->g) == NULL) || + (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL)) { + sshkey_free(n); + return SSH_ERR_ALLOC_FAIL; + } + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + case KEY_ECDSA_CERT: + if ((n = sshkey_new(k->type)) == NULL) + return SSH_ERR_ALLOC_FAIL; + n->ecdsa_nid = k->ecdsa_nid; + n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); + if (n->ecdsa == NULL) { + sshkey_free(n); + return SSH_ERR_ALLOC_FAIL; + } + if (EC_KEY_set_public_key(n->ecdsa, + EC_KEY_get0_public_key(k->ecdsa)) != 1) { + sshkey_free(n); + return SSH_ERR_LIBCRYPTO_ERROR; + } + break; +# endif /* OPENSSL_HAS_ECC */ + case KEY_RSA: + case KEY_RSA1: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + if ((n = sshkey_new(k->type)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || + (BN_copy(n->rsa->e, k->rsa->e) == NULL)) { + sshkey_free(n); + return SSH_ERR_ALLOC_FAIL; + } + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + if ((n = sshkey_new(k->type)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (k->ed25519_pk != NULL) { + if ((n->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { + sshkey_free(n); + return SSH_ERR_ALLOC_FAIL; + } + memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ); + } + break; + default: + return SSH_ERR_KEY_TYPE_UNKNOWN; + } + if (sshkey_is_cert(k)) { + if ((ret = sshkey_cert_copy(k, n)) != 0) { + sshkey_free(n); + return ret; + } + } + *pkp = n; + return 0; +} + +static int +cert_parse(struct sshbuf *b, struct sshkey *key, const u_char *blob, + size_t blen) +{ + u_char *principals = NULL, *critical = NULL, *exts = NULL; + u_char *sig_key = NULL, *sig = NULL; + size_t signed_len, plen, clen, sklen, slen, kidlen, elen; + struct sshbuf *tmp; + char *principal; + int ret = SSH_ERR_INTERNAL_ERROR; + int v00 = sshkey_cert_is_legacy(key); + char **oprincipals; + + if ((tmp = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + + /* Copy the entire key blob for verification and later serialisation */ + if ((ret = sshbuf_put(key->cert->certblob, blob, blen)) != 0) + return ret; + + elen = 0; /* Not touched for v00 certs */ + principals = exts = critical = sig_key = sig = NULL; + if ((!v00 && (ret = sshbuf_get_u64(b, &key->cert->serial)) != 0) || + (ret = sshbuf_get_u32(b, &key->cert->type)) != 0 || + (ret = sshbuf_get_cstring(b, &key->cert->key_id, &kidlen)) != 0 || + (ret = sshbuf_get_string(b, &principals, &plen)) != 0 || + (ret = sshbuf_get_u64(b, &key->cert->valid_after)) != 0 || + (ret = sshbuf_get_u64(b, &key->cert->valid_before)) != 0 || + (ret = sshbuf_get_string(b, &critical, &clen)) != 0 || + (!v00 && (ret = sshbuf_get_string(b, &exts, &elen)) != 0) || + (v00 && (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0) || + (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0 || + (ret = sshbuf_get_string(b, &sig_key, &sklen)) != 0) { + /* XXX debug print error for ret */ + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* Signature is left in the buffer so we can calculate this length */ + signed_len = sshbuf_len(key->cert->certblob) - sshbuf_len(b); + + if ((ret = sshbuf_get_string(b, &sig, &slen)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + + if (key->cert->type != SSH2_CERT_TYPE_USER && + key->cert->type != SSH2_CERT_TYPE_HOST) { + ret = SSH_ERR_KEY_CERT_UNKNOWN_TYPE; + goto out; + } + + if ((ret = sshbuf_put(tmp, principals, plen)) != 0) + goto out; + while (sshbuf_len(tmp) > 0) { + if (key->cert->nprincipals >= SSHKEY_CERT_MAX_PRINCIPALS) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((ret = sshbuf_get_cstring(tmp, &principal, &plen)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + oprincipals = key->cert->principals; + key->cert->principals = realloc(key->cert->principals, + (key->cert->nprincipals + 1) * + sizeof(*key->cert->principals)); + if (key->cert->principals == NULL) { + free(principal); + key->cert->principals = oprincipals; + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + key->cert->principals[key->cert->nprincipals++] = principal; + } + + sshbuf_reset(tmp); + + if ((ret = sshbuf_put(key->cert->critical, critical, clen)) != 0 || + (ret = sshbuf_put(tmp, critical, clen)) != 0) + goto out; + + /* validate structure */ + while (sshbuf_len(tmp) != 0) { + if ((ret = sshbuf_get_string_direct(tmp, NULL, NULL)) != 0 || + (ret = sshbuf_get_string_direct(tmp, NULL, NULL)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + sshbuf_reset(tmp); + + if ((ret = sshbuf_put(key->cert->extensions, exts, elen)) != 0 || + (ret = sshbuf_put(tmp, exts, elen)) != 0) + goto out; + + /* validate structure */ + while (sshbuf_len(tmp) != 0) { + if ((ret = sshbuf_get_string_direct(tmp, NULL, NULL)) != 0 || + (ret = sshbuf_get_string_direct(tmp, NULL, NULL)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + sshbuf_reset(tmp); + + if (sshkey_from_blob_internal(sig_key, sklen, + &key->cert->signature_key, 0) != 0) { + ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; + goto out; + } + if (!sshkey_type_is_valid_ca(key->cert->signature_key->type)) { + ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; + goto out; + } + + if ((ret = sshkey_verify(key->cert->signature_key, sig, slen, + sshbuf_ptr(key->cert->certblob), signed_len, 0)) != 0) + goto out; + ret = 0; + + out: + sshbuf_free(tmp); + free(principals); + free(critical); + free(exts); + free(sig_key); + free(sig); + return ret; +} + +static int +sshkey_from_blob_internal(const u_char *blob, size_t blen, + struct sshkey **keyp, int allow_cert) +{ + struct sshbuf *b = NULL; + int type, nid = -1, ret = SSH_ERR_INTERNAL_ERROR; + char *ktype = NULL, *curve = NULL; + struct sshkey *key = NULL; + size_t len; + u_char *pk = NULL; +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) + EC_POINT *q = NULL; +#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ + +#ifdef DEBUG_PK /* XXX */ + dump_base64(stderr, blob, blen); +#endif + *keyp = NULL; + if ((b = sshbuf_from(blob, blen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + + type = sshkey_type_from_name(ktype); + if (sshkey_type_plain(type) == KEY_ECDSA) + nid = sshkey_ecdsa_nid_from_name(ktype); + if (!allow_cert && sshkey_type_is_cert(type)) { + ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; + goto out; + } + switch (type) { +#ifdef WITH_OPENSSL + case KEY_RSA_CERT: + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* FALLTHROUGH */ + case KEY_RSA: + case KEY_RSA_CERT_V00: + if ((key = sshkey_new(type)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshbuf_get_bignum2(b, key->rsa->e) == -1 || + sshbuf_get_bignum2(b, key->rsa->n) == -1) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } +#ifdef DEBUG_PK + RSA_print_fp(stderr, key->rsa, 8); +#endif + break; + case KEY_DSA_CERT: + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* FALLTHROUGH */ + case KEY_DSA: + case KEY_DSA_CERT_V00: + if ((key = sshkey_new(type)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshbuf_get_bignum2(b, key->dsa->p) == -1 || + sshbuf_get_bignum2(b, key->dsa->q) == -1 || + sshbuf_get_bignum2(b, key->dsa->g) == -1 || + sshbuf_get_bignum2(b, key->dsa->pub_key) == -1) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } +#ifdef DEBUG_PK + DSA_print_fp(stderr, key->dsa, 8); +#endif + break; + case KEY_ECDSA_CERT: + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* FALLTHROUGH */ +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + if ((key = sshkey_new(type)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + key->ecdsa_nid = nid; + if (sshbuf_get_cstring(b, &curve, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) { + ret = SSH_ERR_EC_CURVE_MISMATCH; + goto out; + } + if (key->ecdsa != NULL) + EC_KEY_free(key->ecdsa); + if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) + == NULL) { + ret = SSH_ERR_EC_CURVE_INVALID; + goto out; + } + if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), + q) != 0) { + ret = SSH_ERR_KEY_INVALID_EC_VALUE; + goto out; + } + if (EC_KEY_set_public_key(key->ecdsa, q) != 1) { + /* XXX assume it is a allocation error */ + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } +#ifdef DEBUG_PK + sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q); +#endif + break; +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + case KEY_ED25519_CERT: + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* FALLTHROUGH */ + case KEY_ED25519: + if ((ret = sshbuf_get_string(b, &pk, &len)) != 0) + goto out; + if (len != ED25519_PK_SZ) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((key = sshkey_new(type)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + key->ed25519_pk = pk; + pk = NULL; + break; + case KEY_UNSPEC: + if ((key = sshkey_new(type)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + break; + default: + ret = SSH_ERR_KEY_TYPE_UNKNOWN; + goto out; + } + + /* Parse certificate potion */ + if (sshkey_is_cert(key) && + (ret = cert_parse(b, key, blob, blen)) != 0) + goto out; + + if (key != NULL && sshbuf_len(b) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + ret = 0; + *keyp = key; + key = NULL; + out: + sshbuf_free(b); + sshkey_free(key); + free(ktype); + free(curve); + free(pk); +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) + if (q != NULL) + EC_POINT_free(q); +#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ + return ret; +} + +int +sshkey_from_blob(const u_char *blob, size_t blen, struct sshkey **keyp) +{ + return sshkey_from_blob_internal(blob, blen, keyp, 1); +} + +int +sshkey_sign(const struct sshkey *key, + u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) +{ + if (sigp != NULL) + *sigp = NULL; + if (lenp != NULL) + *lenp = 0; + if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE) + return SSH_ERR_INVALID_ARGUMENT; + switch (key->type) { +#ifdef WITH_OPENSSL + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + case KEY_DSA: + return ssh_dss_sign(key, sigp, lenp, data, datalen, compat); +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA: + return ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat); +# endif /* OPENSSL_HAS_ECC */ + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + case KEY_RSA: + return ssh_rsa_sign(key, sigp, lenp, data, datalen, compat); +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + return ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat); + default: + return SSH_ERR_KEY_TYPE_UNKNOWN; + } +} + +/* + * ssh_key_verify returns 0 for a correct signature and < 0 on error. + */ +int +sshkey_verify(const struct sshkey *key, + const u_char *sig, size_t siglen, + const u_char *data, size_t dlen, u_int compat) +{ + if (siglen == 0) + return -1; + + if (dlen > SSH_KEY_MAX_SIGN_DATA_SIZE) + return SSH_ERR_INVALID_ARGUMENT; + switch (key->type) { +#ifdef WITH_OPENSSL + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + case KEY_DSA: + return ssh_dss_verify(key, sig, siglen, data, dlen, compat); +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA: + return ssh_ecdsa_verify(key, sig, siglen, data, dlen, compat); +# endif /* OPENSSL_HAS_ECC */ + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + case KEY_RSA: + return ssh_rsa_verify(key, sig, siglen, data, dlen, compat); +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + return ssh_ed25519_verify(key, sig, siglen, data, dlen, compat); + default: + return SSH_ERR_KEY_TYPE_UNKNOWN; + } +} + +/* Converts a private to a public key */ +int +sshkey_demote(const struct sshkey *k, struct sshkey **dkp) +{ + struct sshkey *pk; + int ret = SSH_ERR_INTERNAL_ERROR; + + if (dkp != NULL) + *dkp = NULL; + + if ((pk = calloc(1, sizeof(*pk))) == NULL) + return SSH_ERR_ALLOC_FAIL; + pk->type = k->type; + pk->flags = k->flags; + pk->ecdsa_nid = k->ecdsa_nid; + pk->dsa = NULL; + pk->ecdsa = NULL; + pk->rsa = NULL; + pk->ed25519_pk = NULL; + pk->ed25519_sk = NULL; + + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + if ((ret = sshkey_cert_copy(k, pk)) != 0) + goto fail; + /* FALLTHROUGH */ + case KEY_RSA1: + case KEY_RSA: + if ((pk->rsa = RSA_new()) == NULL || + (pk->rsa->e = BN_dup(k->rsa->e)) == NULL || + (pk->rsa->n = BN_dup(k->rsa->n)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto fail; + } + break; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + if ((ret = sshkey_cert_copy(k, pk)) != 0) + goto fail; + /* FALLTHROUGH */ + case KEY_DSA: + if ((pk->dsa = DSA_new()) == NULL || + (pk->dsa->p = BN_dup(k->dsa->p)) == NULL || + (pk->dsa->q = BN_dup(k->dsa->q)) == NULL || + (pk->dsa->g = BN_dup(k->dsa->g)) == NULL || + (pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto fail; + } + break; + case KEY_ECDSA_CERT: + if ((ret = sshkey_cert_copy(k, pk)) != 0) + goto fail; + /* FALLTHROUGH */ +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + pk->ecdsa = EC_KEY_new_by_curve_name(pk->ecdsa_nid); + if (pk->ecdsa == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto fail; + } + if (EC_KEY_set_public_key(pk->ecdsa, + EC_KEY_get0_public_key(k->ecdsa)) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto fail; + } + break; +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + case KEY_ED25519_CERT: + if ((ret = sshkey_cert_copy(k, pk)) != 0) + goto fail; + /* FALLTHROUGH */ + case KEY_ED25519: + if (k->ed25519_pk != NULL) { + if ((pk->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto fail; + } + memcpy(pk->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ); + } + break; + default: + ret = SSH_ERR_KEY_TYPE_UNKNOWN; + fail: + sshkey_free(pk); + return ret; + } + *dkp = pk; + return 0; +} + +/* Convert a plain key to their _CERT equivalent */ +int +sshkey_to_certified(struct sshkey *k, int legacy) +{ + int newtype; + + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_RSA: + newtype = legacy ? KEY_RSA_CERT_V00 : KEY_RSA_CERT; + break; + case KEY_DSA: + newtype = legacy ? KEY_DSA_CERT_V00 : KEY_DSA_CERT; + break; + case KEY_ECDSA: + if (legacy) + return SSH_ERR_INVALID_ARGUMENT; + newtype = KEY_ECDSA_CERT; + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + if (legacy) + return SSH_ERR_INVALID_ARGUMENT; + newtype = KEY_ED25519_CERT; + break; + default: + return SSH_ERR_INVALID_ARGUMENT; + } + if ((k->cert = cert_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + k->type = newtype; + return 0; +} + +/* Convert a certificate to its raw key equivalent */ +int +sshkey_drop_cert(struct sshkey *k) +{ + if (!sshkey_type_is_cert(k->type)) + return SSH_ERR_KEY_TYPE_UNKNOWN; + cert_free(k->cert); + k->cert = NULL; + k->type = sshkey_type_plain(k->type); + return 0; +} + +/* Sign a certified key, (re-)generating the signed certblob. */ +int +sshkey_certify(struct sshkey *k, struct sshkey *ca) +{ + struct sshbuf *principals = NULL; + u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32]; + size_t i, ca_len, sig_len; + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *cert; + + if (k == NULL || k->cert == NULL || + k->cert->certblob == NULL || ca == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (!sshkey_is_cert(k)) + return SSH_ERR_KEY_TYPE_UNKNOWN; + if (!sshkey_type_is_valid_ca(ca->type)) + return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; + + if ((ret = sshkey_to_blob(ca, &ca_blob, &ca_len)) != 0) + return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; + + cert = k->cert->certblob; /* for readability */ + sshbuf_reset(cert); + if ((ret = sshbuf_put_cstring(cert, sshkey_ssh_name(k))) != 0) + goto out; + + /* -v01 certs put nonce first */ + arc4random_buf(&nonce, sizeof(nonce)); + if (!sshkey_cert_is_legacy(k)) { + if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0) + goto out; + } + + /* XXX this substantially duplicates to_blob(); refactor */ + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + if ((ret = sshbuf_put_bignum2(cert, k->dsa->p)) != 0 || + (ret = sshbuf_put_bignum2(cert, k->dsa->q)) != 0 || + (ret = sshbuf_put_bignum2(cert, k->dsa->g)) != 0 || + (ret = sshbuf_put_bignum2(cert, k->dsa->pub_key)) != 0) + goto out; + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + if ((ret = sshbuf_put_cstring(cert, + sshkey_curve_nid_to_name(k->ecdsa_nid))) != 0 || + (ret = sshbuf_put_ec(cert, + EC_KEY_get0_public_key(k->ecdsa), + EC_KEY_get0_group(k->ecdsa))) != 0) + goto out; + break; +# endif /* OPENSSL_HAS_ECC */ + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + if ((ret = sshbuf_put_bignum2(cert, k->rsa->e)) != 0 || + (ret = sshbuf_put_bignum2(cert, k->rsa->n)) != 0) + goto out; + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519_CERT: + if ((ret = sshbuf_put_string(cert, + k->ed25519_pk, ED25519_PK_SZ)) != 0) + goto out; + break; + default: + ret = SSH_ERR_INVALID_ARGUMENT; + } + + /* -v01 certs have a serial number next */ + if (!sshkey_cert_is_legacy(k)) { + if ((ret = sshbuf_put_u64(cert, k->cert->serial)) != 0) + goto out; + } + + if ((ret = sshbuf_put_u32(cert, k->cert->type)) != 0 || + (ret = sshbuf_put_cstring(cert, k->cert->key_id)) != 0) + goto out; + + if ((principals = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + for (i = 0; i < k->cert->nprincipals; i++) { + if ((ret = sshbuf_put_cstring(principals, + k->cert->principals[i])) != 0) + goto out; + } + if ((ret = sshbuf_put_stringb(cert, principals)) != 0 || + (ret = sshbuf_put_u64(cert, k->cert->valid_after)) != 0 || + (ret = sshbuf_put_u64(cert, k->cert->valid_before)) != 0 || + (ret = sshbuf_put_stringb(cert, k->cert->critical)) != 0) + goto out; + + /* -v01 certs have non-critical options here */ + if (!sshkey_cert_is_legacy(k)) { + if ((ret = sshbuf_put_stringb(cert, k->cert->extensions)) != 0) + goto out; + } + + /* -v00 certs put the nonce at the end */ + if (sshkey_cert_is_legacy(k)) { + if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0) + goto out; + } + + if ((ret = sshbuf_put_string(cert, NULL, 0)) != 0 || /* Reserved */ + (ret = sshbuf_put_string(cert, ca_blob, ca_len)) != 0) + goto out; + + /* Sign the whole mess */ + if ((ret = sshkey_sign(ca, &sig_blob, &sig_len, sshbuf_ptr(cert), + sshbuf_len(cert), 0)) != 0) + goto out; + + /* Append signature and we are done */ + if ((ret = sshbuf_put_string(cert, sig_blob, sig_len)) != 0) + goto out; + ret = 0; + out: + if (ret != 0) + sshbuf_reset(cert); + if (sig_blob != NULL) + free(sig_blob); + if (ca_blob != NULL) + free(ca_blob); + if (principals != NULL) + sshbuf_free(principals); + return ret; +} + +int +sshkey_cert_check_authority(const struct sshkey *k, + int want_host, int require_principal, + const char *name, const char **reason) +{ + u_int i, principal_matches; + time_t now = time(NULL); + + if (reason != NULL) + *reason = NULL; + + if (want_host) { + if (k->cert->type != SSH2_CERT_TYPE_HOST) { + *reason = "Certificate invalid: not a host certificate"; + return SSH_ERR_KEY_CERT_INVALID; + } + } else { + if (k->cert->type != SSH2_CERT_TYPE_USER) { + *reason = "Certificate invalid: not a user certificate"; + return SSH_ERR_KEY_CERT_INVALID; + } + } + if (now < 0) { + /* yikes - system clock before epoch! */ + *reason = "Certificate invalid: not yet valid"; + return SSH_ERR_KEY_CERT_INVALID; + } + if ((u_int64_t)now < k->cert->valid_after) { + *reason = "Certificate invalid: not yet valid"; + return SSH_ERR_KEY_CERT_INVALID; + } + if ((u_int64_t)now >= k->cert->valid_before) { + *reason = "Certificate invalid: expired"; + return SSH_ERR_KEY_CERT_INVALID; + } + if (k->cert->nprincipals == 0) { + if (require_principal) { + *reason = "Certificate lacks principal list"; + return SSH_ERR_KEY_CERT_INVALID; + } + } else if (name != NULL) { + principal_matches = 0; + for (i = 0; i < k->cert->nprincipals; i++) { + if (strcmp(name, k->cert->principals[i]) == 0) { + principal_matches = 1; + break; + } + } + if (!principal_matches) { + *reason = "Certificate invalid: name is not a listed " + "principal"; + return SSH_ERR_KEY_CERT_INVALID; + } + } + return 0; +} + +int +sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b) +{ + int r = SSH_ERR_INTERNAL_ERROR; + + if ((r = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0) + goto out; + switch (key->type) { +#ifdef WITH_OPENSSL + case KEY_RSA: + if ((r = sshbuf_put_bignum2(b, key->rsa->n)) != 0 || + (r = sshbuf_put_bignum2(b, key->rsa->e)) != 0 || + (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 || + (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 || + (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 || + (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0) + goto out; + break; + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 || + (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 || + (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 || + (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0) + goto out; + break; + case KEY_DSA: + if ((r = sshbuf_put_bignum2(b, key->dsa->p)) != 0 || + (r = sshbuf_put_bignum2(b, key->dsa->q)) != 0 || + (r = sshbuf_put_bignum2(b, key->dsa->g)) != 0 || + (r = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0 || + (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0) + goto out; + break; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0) + goto out; + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + if ((r = sshbuf_put_cstring(b, + sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 || + (r = sshbuf_put_eckey(b, key->ecdsa)) != 0 || + (r = sshbuf_put_bignum2(b, + EC_KEY_get0_private_key(key->ecdsa))) != 0) + goto out; + break; + case KEY_ECDSA_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_bignum2(b, + EC_KEY_get0_private_key(key->ecdsa))) != 0) + goto out; + break; +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + if ((r = sshbuf_put_string(b, key->ed25519_pk, + ED25519_PK_SZ)) != 0 || + (r = sshbuf_put_string(b, key->ed25519_sk, + ED25519_SK_SZ)) != 0) + goto out; + break; + case KEY_ED25519_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_string(b, key->ed25519_pk, + ED25519_PK_SZ)) != 0 || + (r = sshbuf_put_string(b, key->ed25519_sk, + ED25519_SK_SZ)) != 0) + goto out; + break; + default: + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + /* success */ + r = 0; + out: + return r; +} + +int +sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) +{ + char *tname = NULL, *curve = NULL; + struct sshkey *k = NULL; + const u_char *cert; + size_t len, pklen = 0, sklen = 0; + int type, r = SSH_ERR_INTERNAL_ERROR; + u_char *ed25519_pk = NULL, *ed25519_sk = NULL; +#ifdef WITH_OPENSSL + BIGNUM *exponent = NULL; +#endif /* WITH_OPENSSL */ + + if (kp != NULL) + *kp = NULL; + if ((r = sshbuf_get_cstring(buf, &tname, NULL)) != 0) + goto out; + type = sshkey_type_from_name(tname); + switch (type) { +#ifdef WITH_OPENSSL + case KEY_DSA: + if ((k = sshkey_new_private(type)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_bignum2(buf, k->dsa->p)) != 0 || + (r = sshbuf_get_bignum2(buf, k->dsa->q)) != 0 || + (r = sshbuf_get_bignum2(buf, k->dsa->g)) != 0 || + (r = sshbuf_get_bignum2(buf, k->dsa->pub_key)) != 0 || + (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0) + goto out; + break; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + if ((r = sshbuf_get_string_direct(buf, &cert, &len)) != 0 || + (r = sshkey_from_blob(cert, len, &k)) != 0 || + (r = sshkey_add_private(k)) != 0 || + (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0) + goto out; + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + if ((k = sshkey_new_private(type)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_get_cstring(buf, &curve, NULL)) != 0) + goto out; + if (k->ecdsa_nid != sshkey_curve_name_to_nid(curve)) { + r = SSH_ERR_EC_CURVE_MISMATCH; + goto out; + } + k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); + if (k->ecdsa == NULL || (exponent = BN_new()) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((r = sshbuf_get_eckey(buf, k->ecdsa)) != 0 || + (r = sshbuf_get_bignum2(buf, exponent))) + goto out; + if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa), + EC_KEY_get0_public_key(k->ecdsa)) != 0) || + (r = sshkey_ec_validate_private(k->ecdsa)) != 0) + goto out; + break; + case KEY_ECDSA_CERT: + if ((exponent = BN_new()) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((r = sshbuf_get_string_direct(buf, &cert, &len)) != 0 || + (r = sshkey_from_blob(cert, len, &k)) != 0 || + (r = sshkey_add_private(k)) != 0 || + (r = sshbuf_get_bignum2(buf, exponent)) != 0) + goto out; + if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa), + EC_KEY_get0_public_key(k->ecdsa)) != 0) || + (r = sshkey_ec_validate_private(k->ecdsa)) != 0) + goto out; + break; +# endif /* OPENSSL_HAS_ECC */ + case KEY_RSA: + if ((k = sshkey_new_private(type)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_bignum2(buf, k->rsa->n)) != 0 || + (r = sshbuf_get_bignum2(buf, k->rsa->e)) != 0 || + (r = sshbuf_get_bignum2(buf, k->rsa->d)) != 0 || + (r = sshbuf_get_bignum2(buf, k->rsa->iqmp)) != 0 || + (r = sshbuf_get_bignum2(buf, k->rsa->p)) != 0 || + (r = sshbuf_get_bignum2(buf, k->rsa->q)) != 0 || + (r = rsa_generate_additional_parameters(k->rsa)) != 0) + goto out; + break; + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + if ((r = sshbuf_get_string_direct(buf, &cert, &len)) != 0 || + (r = sshkey_from_blob(cert, len, &k)) != 0 || + (r = sshkey_add_private(k)) != 0 || + (r = sshbuf_get_bignum2(buf, k->rsa->d) != 0) || + (r = sshbuf_get_bignum2(buf, k->rsa->iqmp) != 0) || + (r = sshbuf_get_bignum2(buf, k->rsa->p) != 0) || + (r = sshbuf_get_bignum2(buf, k->rsa->q) != 0) || + (r = rsa_generate_additional_parameters(k->rsa)) != 0) + goto out; + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + if ((k = sshkey_new_private(type)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 || + (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0) + goto out; + if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + k->ed25519_pk = ed25519_pk; + k->ed25519_sk = ed25519_sk; + ed25519_pk = ed25519_sk = NULL; + break; + case KEY_ED25519_CERT: + if ((r = sshbuf_get_string_direct(buf, &cert, &len)) != 0 || + (r = sshkey_from_blob(cert, len, &k)) != 0 || + (r = sshkey_add_private(k)) != 0 || + (r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 || + (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0) + goto out; + if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + k->ed25519_pk = ed25519_pk; + k->ed25519_sk = ed25519_sk; + ed25519_pk = ed25519_sk = NULL; + break; + default: + r = SSH_ERR_KEY_TYPE_UNKNOWN; + goto out; + } +#ifdef WITH_OPENSSL + /* enable blinding */ + switch (k->type) { + case KEY_RSA: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + case KEY_RSA1: + if (RSA_blinding_on(k->rsa, NULL) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + break; + } +#endif /* WITH_OPENSSL */ + /* success */ + r = 0; + if (kp != NULL) { + *kp = k; + k = NULL; + } + out: + free(tname); + free(curve); +#ifdef WITH_OPENSSL + if (exponent != NULL) + BN_clear_free(exponent); +#endif /* WITH_OPENSSL */ + sshkey_free(k); + if (ed25519_pk != NULL) { + explicit_bzero(ed25519_pk, pklen); + free(ed25519_pk); + } + if (ed25519_sk != NULL) { + explicit_bzero(ed25519_sk, sklen); + free(ed25519_sk); + } + return r; +} + +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) +int +sshkey_ec_validate_public(const EC_GROUP *group, const EC_POINT *public) +{ + BN_CTX *bnctx; + EC_POINT *nq = NULL; + BIGNUM *order, *x, *y, *tmp; + int ret = SSH_ERR_KEY_INVALID_EC_VALUE; + + if ((bnctx = BN_CTX_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + BN_CTX_start(bnctx); + + /* + * We shouldn't ever hit this case because bignum_get_ecpoint() + * refuses to load GF2m points. + */ + if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) != + NID_X9_62_prime_field) + goto out; + + /* Q != infinity */ + if (EC_POINT_is_at_infinity(group, public)) + goto out; + + if ((x = BN_CTX_get(bnctx)) == NULL || + (y = BN_CTX_get(bnctx)) == NULL || + (order = BN_CTX_get(bnctx)) == NULL || + (tmp = BN_CTX_get(bnctx)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */ + if (EC_GROUP_get_order(group, order, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, public, + x, y, bnctx) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (BN_num_bits(x) <= BN_num_bits(order) / 2 || + BN_num_bits(y) <= BN_num_bits(order) / 2) + goto out; + + /* nQ == infinity (n == order of subgroup) */ + if ((nq = EC_POINT_new(group)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_POINT_mul(group, nq, NULL, public, order, bnctx) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (EC_POINT_is_at_infinity(group, nq) != 1) + goto out; + + /* x < order - 1, y < order - 1 */ + if (!BN_sub(tmp, order, BN_value_one())) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (BN_cmp(x, tmp) >= 0 || BN_cmp(y, tmp) >= 0) + goto out; + ret = 0; + out: + BN_CTX_free(bnctx); + if (nq != NULL) + EC_POINT_free(nq); + return ret; +} + +int +sshkey_ec_validate_private(const EC_KEY *key) +{ + BN_CTX *bnctx; + BIGNUM *order, *tmp; + int ret = SSH_ERR_KEY_INVALID_EC_VALUE; + + if ((bnctx = BN_CTX_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + BN_CTX_start(bnctx); + + if ((order = BN_CTX_get(bnctx)) == NULL || + (tmp = BN_CTX_get(bnctx)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* log2(private) > log2(order)/2 */ + if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, bnctx) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (BN_num_bits(EC_KEY_get0_private_key(key)) <= + BN_num_bits(order) / 2) + goto out; + + /* private < order - 1 */ + if (!BN_sub(tmp, order, BN_value_one())) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0) + goto out; + ret = 0; + out: + BN_CTX_free(bnctx); + return ret; +} + +void +sshkey_dump_ec_point(const EC_GROUP *group, const EC_POINT *point) +{ + BIGNUM *x, *y; + BN_CTX *bnctx; + + if (point == NULL) { + fputs("point=(NULL)\n", stderr); + return; + } + if ((bnctx = BN_CTX_new()) == NULL) { + fprintf(stderr, "%s: BN_CTX_new failed\n", __func__); + return; + } + BN_CTX_start(bnctx); + if ((x = BN_CTX_get(bnctx)) == NULL || + (y = BN_CTX_get(bnctx)) == NULL) { + fprintf(stderr, "%s: BN_CTX_get failed\n", __func__); + return; + } + if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) != + NID_X9_62_prime_field) { + fprintf(stderr, "%s: group is not a prime field\n", __func__); + return; + } + if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y, + bnctx) != 1) { + fprintf(stderr, "%s: EC_POINT_get_affine_coordinates_GFp\n", + __func__); + return; + } + fputs("x=", stderr); + BN_print_fp(stderr, x); + fputs("\ny=", stderr); + BN_print_fp(stderr, y); + fputs("\n", stderr); + BN_CTX_free(bnctx); +} + +void +sshkey_dump_ec_key(const EC_KEY *key) +{ + const BIGNUM *exponent; + + sshkey_dump_ec_point(EC_KEY_get0_group(key), + EC_KEY_get0_public_key(key)); + fputs("exponent=", stderr); + if ((exponent = EC_KEY_get0_private_key(key)) == NULL) + fputs("(NULL)", stderr); + else + BN_print_fp(stderr, EC_KEY_get0_private_key(key)); + fputs("\n", stderr); +} +#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ + +static int +sshkey_private_to_blob2(const struct sshkey *prv, struct sshbuf *blob, + const char *passphrase, const char *comment, const char *ciphername, + int rounds) +{ + u_char *cp, *b64 = NULL, *key = NULL, *pubkeyblob = NULL; + u_char salt[SALT_LEN]; + size_t i, pubkeylen, keylen, ivlen, blocksize, authlen; + u_int check; + int r = SSH_ERR_INTERNAL_ERROR; + struct sshcipher_ctx ciphercontext; + const struct sshcipher *cipher; + const char *kdfname = KDFNAME; + struct sshbuf *encoded = NULL, *encrypted = NULL, *kdf = NULL; + + memset(&ciphercontext, 0, sizeof(ciphercontext)); + + if (rounds <= 0) + rounds = DEFAULT_ROUNDS; + if (passphrase == NULL || !strlen(passphrase)) { + ciphername = "none"; + kdfname = "none"; + } else if (ciphername == NULL) + ciphername = DEFAULT_CIPHERNAME; + else if (cipher_number(ciphername) != SSH_CIPHER_SSH2) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((cipher = cipher_by_name(ciphername)) == NULL) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + + if ((kdf = sshbuf_new()) == NULL || + (encoded = sshbuf_new()) == NULL || + (encrypted = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + blocksize = cipher_blocksize(cipher); + keylen = cipher_keylen(cipher); + ivlen = cipher_ivlen(cipher); + authlen = cipher_authlen(cipher); + if ((key = calloc(1, keylen + ivlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (strcmp(kdfname, "bcrypt") == 0) { + arc4random_buf(salt, SALT_LEN); + if (bcrypt_pbkdf(passphrase, strlen(passphrase), + salt, SALT_LEN, key, keylen + ivlen, rounds) < 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_string(kdf, salt, SALT_LEN)) != 0 || + (r = sshbuf_put_u32(kdf, rounds)) != 0) + goto out; + } else if (strcmp(kdfname, "none") != 0) { + /* Unsupported KDF type */ + r = SSH_ERR_KEY_UNKNOWN_CIPHER; + goto out; + } + if ((r = cipher_init(&ciphercontext, cipher, key, keylen, + key + keylen, ivlen, 1)) != 0) + goto out; + + if ((r = sshbuf_put(encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC))) != 0 || + (r = sshbuf_put_cstring(encoded, ciphername)) != 0 || + (r = sshbuf_put_cstring(encoded, kdfname)) != 0 || + (r = sshbuf_put_stringb(encoded, kdf)) != 0 || + (r = sshbuf_put_u32(encoded, 1)) != 0 || /* number of keys */ + (r = sshkey_to_blob(prv, &pubkeyblob, &pubkeylen)) != 0 || + (r = sshbuf_put_string(encoded, pubkeyblob, pubkeylen)) != 0) + goto out; + + /* set up the buffer that will be encrypted */ + + /* Random check bytes */ + check = arc4random(); + if ((r = sshbuf_put_u32(encrypted, check)) != 0 || + (r = sshbuf_put_u32(encrypted, check)) != 0) + goto out; + + /* append private key and comment*/ + if ((r = sshkey_private_serialize(prv, encrypted)) != 0 || + (r = sshbuf_put_cstring(encrypted, comment)) != 0) + goto out; + + /* padding */ + i = 0; + while (sshbuf_len(encrypted) % blocksize) { + if ((r = sshbuf_put_u8(encrypted, ++i & 0xff)) != 0) + goto out; + } + + /* length in destination buffer */ + if ((r = sshbuf_put_u32(encoded, sshbuf_len(encrypted))) != 0) + goto out; + + /* encrypt */ + if ((r = sshbuf_reserve(encoded, + sshbuf_len(encrypted) + authlen, &cp)) != 0) + goto out; + if ((r = cipher_crypt(&ciphercontext, 0, cp, + sshbuf_ptr(encrypted), sshbuf_len(encrypted), 0, authlen)) != 0) + goto out; + + /* uuencode */ + if ((b64 = sshbuf_dtob64(encoded)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + sshbuf_reset(blob); + if ((r = sshbuf_put(blob, MARK_BEGIN, MARK_BEGIN_LEN)) != 0) + goto out; + for (i = 0; i < strlen(b64); i++) { + if ((r = sshbuf_put_u8(blob, b64[i])) != 0) + goto out; + /* insert line breaks */ + if (i % 70 == 69 && (r = sshbuf_put_u8(blob, '\n')) != 0) + goto out; + } + if (i % 70 != 69 && (r = sshbuf_put_u8(blob, '\n')) != 0) + goto out; + if ((r = sshbuf_put(blob, MARK_END, MARK_END_LEN)) != 0) + goto out; + + /* success */ + r = 0; + + out: + sshbuf_free(kdf); + sshbuf_free(encoded); + sshbuf_free(encrypted); + cipher_cleanup(&ciphercontext); + explicit_bzero(salt, sizeof(salt)); + if (key != NULL) { + explicit_bzero(key, keylen + ivlen); + free(key); + } + if (pubkeyblob != NULL) { + explicit_bzero(pubkeyblob, pubkeylen); + free(pubkeyblob); + } + if (b64 != NULL) { + explicit_bzero(b64, strlen(b64)); + free(b64); + } + return r; +} + +static int +sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, + struct sshkey **keyp, char **commentp) +{ + 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, slen = 0; + struct sshbuf *encoded = NULL, *decoded = NULL; + struct sshbuf *kdf = NULL, *decrypted = NULL; + struct sshcipher_ctx ciphercontext; + struct sshkey *k = NULL; + u_char *key = NULL, *salt = NULL, *dp, pad, last; + u_int blocksize, rounds, nkeys, encrypted_len, check1, check2; + + memset(&ciphercontext, 0, sizeof(ciphercontext)); + if (keyp != NULL) + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; + + if ((encoded = sshbuf_new()) == NULL || + (decoded = sshbuf_new()) == NULL || + (decrypted = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* check preamble */ + cp = sshbuf_ptr(blob); + encoded_len = sshbuf_len(blob); + if (encoded_len < (MARK_BEGIN_LEN + MARK_END_LEN) || + memcmp(cp, MARK_BEGIN, MARK_BEGIN_LEN) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + cp += MARK_BEGIN_LEN; + encoded_len -= MARK_BEGIN_LEN; + + /* Look for end marker, removing whitespace as we go */ + while (encoded_len > 0) { + if (*cp != '\n' && *cp != '\r') { + if ((r = sshbuf_put_u8(encoded, *cp)) != 0) + goto out; + } + last = *cp; + encoded_len--; + cp++; + if (last == '\n') { + if (encoded_len >= MARK_END_LEN && + memcmp(cp, MARK_END, MARK_END_LEN) == 0) { + /* \0 terminate */ + if ((r = sshbuf_put_u8(encoded, 0)) != 0) + goto out; + break; + } + } + } + if (encoded_len == 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* decode base64 */ + if ((r = sshbuf_b64tod(decoded, sshbuf_ptr(encoded))) != 0) + goto out; + + /* check magic */ + if (sshbuf_len(decoded) < sizeof(AUTH_MAGIC) || + memcmp(sshbuf_ptr(decoded), AUTH_MAGIC, sizeof(AUTH_MAGIC))) { + r = SSH_ERR_INVALID_FORMAT; + 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, &encrypted_len)) != 0) + goto out; + + if ((cipher = cipher_by_name(ciphername)) == NULL) { + r = SSH_ERR_KEY_UNKNOWN_CIPHER; + goto out; + } + if ((passphrase == NULL || strlen(passphrase) == 0) && + strcmp(ciphername, "none") != 0) { + /* passphrase required */ + r = SSH_ERR_KEY_WRONG_PASSPHRASE; + goto out; + } + if (strcmp(kdfname, "none") != 0 && strcmp(kdfname, "bcrypt") != 0) { + r = SSH_ERR_KEY_UNKNOWN_CIPHER; + goto out; + } + if (!strcmp(kdfname, "none") && strcmp(ciphername, "none") != 0) { + r = SSH_ERR_INVALID_FORMAT; + 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); + if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* setup key */ + keylen = cipher_keylen(cipher); + ivlen = cipher_ivlen(cipher); + if ((key = calloc(1, keylen + ivlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (strcmp(kdfname, "bcrypt") == 0) { + if ((r = sshbuf_get_string(kdf, &salt, &slen)) != 0 || + (r = sshbuf_get_u32(kdf, &rounds)) != 0) + goto out; + if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen, + key, keylen + ivlen, rounds) < 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + + /* decrypt private portion of key */ + if ((r = sshbuf_reserve(decrypted, encrypted_len, &dp)) != 0 || + (r = cipher_init(&ciphercontext, cipher, key, keylen, + key + keylen, ivlen, 0)) != 0) + goto out; + if ((r = cipher_crypt(&ciphercontext, 0, dp, sshbuf_ptr(decoded), + sshbuf_len(decoded), 0, cipher_authlen(cipher))) != 0) { + /* an integrity error here indicates an incorrect passphrase */ + if (r == SSH_ERR_MAC_INVALID) + r = SSH_ERR_KEY_WRONG_PASSPHRASE; + goto out; + } + if ((r = sshbuf_consume(decoded, encrypted_len)) != 0) + goto out; + /* there should be no trailing data */ + if (sshbuf_len(decoded) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* check check bytes */ + if ((r = sshbuf_get_u32(decrypted, &check1)) != 0 || + (r = sshbuf_get_u32(decrypted, &check2)) != 0) + goto out; + if (check1 != check2) { + r = SSH_ERR_KEY_WRONG_PASSPHRASE; + 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 */ + i = 0; + while (sshbuf_len(decrypted)) { + if ((r = sshbuf_get_u8(decrypted, &pad)) != 0) + goto out; + if (pad != (++i & 0xff)) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + + /* XXX decode pubkey and check against private */ + + /* success */ + r = 0; + if (keyp != NULL) { + *keyp = k; + k = NULL; + } + if (commentp != NULL) { + *commentp = comment; + comment = NULL; + } + out: + pad = 0; + cipher_cleanup(&ciphercontext); + free(ciphername); + free(kdfname); + free(comment); + if (salt != NULL) { + explicit_bzero(salt, slen); + free(salt); + } + if (key != NULL) { + explicit_bzero(key, keylen + ivlen); + free(key); + } + sshbuf_free(encoded); + sshbuf_free(decoded); + sshbuf_free(kdf); + sshbuf_free(decrypted); + sshkey_free(k); + return r; +} + +#if WITH_SSH1 +/* + * Serialises the authentication (private) key to a blob, encrypting it with + * passphrase. The identification of the blob (lowest 64 bits of n) will + * precede the key to provide identification of the key without needing a + * passphrase. + */ +static int +sshkey_private_rsa1_to_blob(struct sshkey *key, struct sshbuf *blob, + const char *passphrase, const char *comment) +{ + struct sshbuf *buffer = NULL, *encrypted = NULL; + u_char buf[8]; + int r, cipher_num; + struct sshcipher_ctx ciphercontext; + const struct sshcipher *cipher; + u_char *cp; + + /* + * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting + * to another cipher; otherwise use SSH_AUTHFILE_CIPHER. + */ + cipher_num = (strcmp(passphrase, "") == 0) ? + SSH_CIPHER_NONE : SSH_CIPHER_3DES; + if ((cipher = cipher_by_number(cipher_num)) == NULL) + return SSH_ERR_INTERNAL_ERROR; + + /* This buffer is used to build the secret part of the private key. */ + if ((buffer = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + + /* Put checkbytes for checking passphrase validity. */ + if ((r = sshbuf_reserve(buffer, 4, &cp)) != 0) + goto out; + arc4random_buf(cp, 2); + memcpy(cp + 2, cp, 2); + + /* + * Store the private key (n and e will not be stored because they + * will be stored in plain text, and storing them also in encrypted + * format would just give known plaintext). + * Note: q and p are stored in reverse order to SSL. + */ + if ((r = sshbuf_put_bignum1(buffer, key->rsa->d)) != 0 || + (r = sshbuf_put_bignum1(buffer, key->rsa->iqmp)) != 0 || + (r = sshbuf_put_bignum1(buffer, key->rsa->q)) != 0 || + (r = sshbuf_put_bignum1(buffer, key->rsa->p)) != 0) + goto out; + + /* Pad the part to be encrypted to a size that is a multiple of 8. */ + explicit_bzero(buf, 8); + if ((r = sshbuf_put(buffer, buf, 8 - (sshbuf_len(buffer) % 8))) != 0) + goto out; + + /* This buffer will be used to contain the data in the file. */ + if ((encrypted = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* First store keyfile id string. */ + if ((r = sshbuf_put(encrypted, LEGACY_BEGIN, + sizeof(LEGACY_BEGIN))) != 0) + goto out; + + /* Store cipher type and "reserved" field. */ + if ((r = sshbuf_put_u8(encrypted, cipher_num)) != 0 || + (r = sshbuf_put_u32(encrypted, 0)) != 0) + goto out; + + /* Store public key. This will be in plain text. */ + if ((r = sshbuf_put_u32(encrypted, BN_num_bits(key->rsa->n))) != 0 || + (r = sshbuf_put_bignum1(encrypted, key->rsa->n) != 0) || + (r = sshbuf_put_bignum1(encrypted, key->rsa->e) != 0) || + (r = sshbuf_put_cstring(encrypted, comment) != 0)) + goto out; + + /* Allocate space for the private part of the key in the buffer. */ + if ((r = sshbuf_reserve(encrypted, sshbuf_len(buffer), &cp)) != 0) + goto out; + + if ((r = cipher_set_key_string(&ciphercontext, cipher, passphrase, + CIPHER_ENCRYPT)) != 0) + goto out; + if ((r = cipher_crypt(&ciphercontext, 0, cp, + sshbuf_ptr(buffer), sshbuf_len(buffer), 0, 0)) != 0) + goto out; + if ((r = cipher_cleanup(&ciphercontext)) != 0) + goto out; + + r = sshbuf_putb(blob, encrypted); + + out: + explicit_bzero(&ciphercontext, sizeof(ciphercontext)); + explicit_bzero(buf, sizeof(buf)); + if (buffer != NULL) + sshbuf_free(buffer); + if (encrypted != NULL) + sshbuf_free(encrypted); + + return r; +} +#endif /* WITH_SSH1 */ + +#ifdef WITH_OPENSSL +/* convert SSH v2 key in OpenSSL PEM format */ +static int +sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob, + const char *_passphrase, const char *comment) +{ + int success, r; + int blen, len = strlen(_passphrase); + u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL; +#if (OPENSSL_VERSION_NUMBER < 0x00907000L) + const EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL; +#else + const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; +#endif + const u_char *bptr; + BIO *bio = NULL; + + if (len > 0 && len <= 4) + return SSH_ERR_PASSPHRASE_TOO_SHORT; + if ((bio = BIO_new(BIO_s_mem())) == NULL) + return SSH_ERR_ALLOC_FAIL; + + switch (key->type) { + case KEY_DSA: + success = PEM_write_bio_DSAPrivateKey(bio, key->dsa, + cipher, passphrase, len, NULL, NULL); + break; +#ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa, + cipher, passphrase, len, NULL, NULL); + break; +#endif + case KEY_RSA: + success = PEM_write_bio_RSAPrivateKey(bio, key->rsa, + cipher, passphrase, len, NULL, NULL); + break; + default: + success = 0; + break; + } + if (success == 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + if ((r = sshbuf_put(blob, bptr, blen)) != 0) + goto out; + r = 0; + out: + BIO_free(bio); + return r; +} +#endif /* WITH_OPENSSL */ + +/* Serialise "key" to buffer "blob" */ +int +sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, + const char *passphrase, const char *comment, + int force_new_format, const char *new_format_cipher, int new_format_rounds) +{ + switch (key->type) { +#ifdef WITH_OPENSSL + case KEY_RSA1: + return sshkey_private_rsa1_to_blob(key, blob, + passphrase, comment); + case KEY_DSA: + case KEY_ECDSA: + case KEY_RSA: + if (force_new_format) { + return sshkey_private_to_blob2(key, blob, passphrase, + comment, new_format_cipher, new_format_rounds); + } + return sshkey_private_pem_to_blob(key, blob, + passphrase, comment); +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + return sshkey_private_to_blob2(key, blob, passphrase, + comment, new_format_cipher, new_format_rounds); + default: + return SSH_ERR_KEY_TYPE_UNKNOWN; + } +} + +#ifdef WITH_SSH1 +/* + * Parse the public, unencrypted portion of a RSA1 key. + */ +int +sshkey_parse_public_rsa1_fileblob(struct sshbuf *blob, + struct sshkey **keyp, char **commentp) +{ + int r; + struct sshkey *pub = NULL; + struct sshbuf *copy = NULL; + + if (keyp != NULL) + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; + + /* Check that it is at least big enough to contain the ID string. */ + if (sshbuf_len(blob) < sizeof(LEGACY_BEGIN)) + return SSH_ERR_INVALID_FORMAT; + + /* + * Make sure it begins with the id string. Consume the id string + * from the buffer. + */ + if (memcmp(sshbuf_ptr(blob), LEGACY_BEGIN, sizeof(LEGACY_BEGIN)) != 0) + return SSH_ERR_INVALID_FORMAT; + /* Make a working copy of the keyblob and skip past the magic */ + if ((copy = sshbuf_fromb(blob)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_consume(copy, sizeof(LEGACY_BEGIN))) != 0) + goto out; + + /* Skip cipher type, reserved data and key bits. */ + if ((r = sshbuf_get_u8(copy, NULL)) != 0 || /* cipher type */ + (r = sshbuf_get_u32(copy, NULL)) != 0 || /* reserved */ + (r = sshbuf_get_u32(copy, NULL)) != 0) /* key bits */ + goto out; + + /* Read the public key from the buffer. */ + if ((pub = sshkey_new(KEY_RSA1)) == NULL || + (r = sshbuf_get_bignum1(copy, pub->rsa->n)) != 0 || + (r = sshbuf_get_bignum1(copy, pub->rsa->e)) != 0) + goto out; + + /* Finally, the comment */ + if ((r = sshbuf_get_string(copy, (u_char**)commentp, NULL)) != 0) + goto out; + + /* The encrypted private part is not parsed by this function. */ + + r = 0; + if (keyp != NULL) + *keyp = pub; + else + sshkey_free(pub); + pub = NULL; + + out: + if (copy != NULL) + sshbuf_free(copy); + if (pub != NULL) + sshkey_free(pub); + return r; +} + +static int +sshkey_parse_private_rsa1(struct sshbuf *blob, const char *passphrase, + struct sshkey **keyp, char **commentp) +{ + int r; + u_int16_t check1, check2; + u_int8_t cipher_type; + struct sshbuf *decrypted = NULL, *copy = NULL; + u_char *cp; + char *comment = NULL; + struct sshcipher_ctx ciphercontext; + const struct sshcipher *cipher; + struct sshkey *prv = NULL; + + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; + + /* Check that it is at least big enough to contain the ID string. */ + if (sshbuf_len(blob) < sizeof(LEGACY_BEGIN)) + return SSH_ERR_INVALID_FORMAT; + + /* + * Make sure it begins with the id string. Consume the id string + * from the buffer. + */ + if (memcmp(sshbuf_ptr(blob), LEGACY_BEGIN, sizeof(LEGACY_BEGIN)) != 0) + return SSH_ERR_INVALID_FORMAT; + + if ((prv = sshkey_new_private(KEY_RSA1)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((copy = sshbuf_fromb(blob)) == NULL || + (decrypted = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_consume(copy, sizeof(LEGACY_BEGIN))) != 0) + goto out; + + /* Read cipher type. */ + if ((r = sshbuf_get_u8(copy, &cipher_type)) != 0 || + (r = sshbuf_get_u32(copy, NULL)) != 0) /* reserved */ + goto out; + + /* Read the public key and comment from the buffer. */ + if ((r = sshbuf_get_u32(copy, NULL)) != 0 || /* key bits */ + (r = sshbuf_get_bignum1(copy, prv->rsa->n)) != 0 || + (r = sshbuf_get_bignum1(copy, prv->rsa->e)) != 0 || + (r = sshbuf_get_cstring(copy, &comment, NULL)) != 0) + goto out; + + /* Check that it is a supported cipher. */ + cipher = cipher_by_number(cipher_type); + if (cipher == NULL) { + r = SSH_ERR_KEY_UNKNOWN_CIPHER; + goto out; + } + /* Initialize space for decrypted data. */ + if ((r = sshbuf_reserve(decrypted, sshbuf_len(copy), &cp)) != 0) + goto out; + + /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ + if ((r = cipher_set_key_string(&ciphercontext, cipher, passphrase, + CIPHER_DECRYPT)) != 0) + goto out; + if ((r = cipher_crypt(&ciphercontext, 0, cp, + sshbuf_ptr(copy), sshbuf_len(copy), 0, 0)) != 0) { + cipher_cleanup(&ciphercontext); + goto out; + } + if ((r = cipher_cleanup(&ciphercontext)) != 0) + goto out; + + if ((r = sshbuf_get_u16(decrypted, &check1)) != 0 || + (r = sshbuf_get_u16(decrypted, &check2)) != 0) + goto out; + if (check1 != check2) { + r = SSH_ERR_KEY_WRONG_PASSPHRASE; + goto out; + } + + /* Read the rest of the private key. */ + if ((r = sshbuf_get_bignum1(decrypted, prv->rsa->d)) != 0 || + (r = sshbuf_get_bignum1(decrypted, prv->rsa->iqmp)) != 0 || + (r = sshbuf_get_bignum1(decrypted, prv->rsa->q)) != 0 || + (r = sshbuf_get_bignum1(decrypted, prv->rsa->p)) != 0) + goto out; + + /* calculate p-1 and q-1 */ + if ((r = rsa_generate_additional_parameters(prv->rsa)) != 0) + goto out; + + /* enable blinding */ + if (RSA_blinding_on(prv->rsa, NULL) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + r = 0; + *keyp = prv; + prv = NULL; + if (commentp != NULL) { + *commentp = comment; + comment = NULL; + } + out: + explicit_bzero(&ciphercontext, sizeof(ciphercontext)); + if (comment != NULL) + free(comment); + if (prv != NULL) + sshkey_free(prv); + if (copy != NULL) + sshbuf_free(copy); + if (decrypted != NULL) + sshbuf_free(decrypted); + return r; +} +#endif /* WITH_SSH1 */ + +#ifdef WITH_OPENSSL +/* XXX make private once ssh-keysign.c fixed */ +int +sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type, + const char *passphrase, struct sshkey **keyp, char **commentp) +{ + EVP_PKEY *pk = NULL; + struct sshkey *prv = NULL; + char *name = ""; + BIO *bio = NULL; + int r; + + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; + + if ((bio = BIO_new(BIO_s_mem())) == NULL || sshbuf_len(blob) > INT_MAX) + return SSH_ERR_ALLOC_FAIL; + if (BIO_write(bio, sshbuf_ptr(blob), sshbuf_len(blob)) != + (int)sshbuf_len(blob)) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, + (char *)passphrase)) == NULL) { + r = SSH_ERR_KEY_WRONG_PASSPHRASE; + goto out; + } + if (pk->type == EVP_PKEY_RSA && + (type == KEY_UNSPEC || type == KEY_RSA)) { + if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + prv->rsa = EVP_PKEY_get1_RSA(pk); + prv->type = KEY_RSA; + name = "rsa w/o comment"; +#ifdef DEBUG_PK + RSA_print_fp(stderr, prv->rsa, 8); +#endif + if (RSA_blinding_on(prv->rsa, NULL) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + } else if (pk->type == EVP_PKEY_DSA && + (type == KEY_UNSPEC || type == KEY_DSA)) { + if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + prv->dsa = EVP_PKEY_get1_DSA(pk); + prv->type = KEY_DSA; + name = "dsa w/o comment"; +#ifdef DEBUG_PK + DSA_print_fp(stderr, prv->dsa, 8); +#endif +#ifdef OPENSSL_HAS_ECC + } else if (pk->type == EVP_PKEY_EC && + (type == KEY_UNSPEC || type == KEY_ECDSA)) { + if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk); + prv->type = KEY_ECDSA; + prv->ecdsa_nid = sshkey_ecdsa_key_to_nid(prv->ecdsa); + if (prv->ecdsa_nid == -1 || + sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL || + sshkey_ec_validate_public(EC_KEY_get0_group(prv->ecdsa), + EC_KEY_get0_public_key(prv->ecdsa)) != 0 || + sshkey_ec_validate_private(prv->ecdsa) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + name = "ecdsa w/o comment"; +# ifdef DEBUG_PK + if (prv != NULL && prv->ecdsa != NULL) + sshkey_dump_ec_key(prv->ecdsa); +# endif +#endif /* OPENSSL_HAS_ECC */ + } else { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (commentp != NULL && + (*commentp = strdup(name)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + r = 0; + *keyp = prv; + prv = NULL; + out: + BIO_free(bio); + if (pk != NULL) + EVP_PKEY_free(pk); + if (prv != NULL) + sshkey_free(prv); + return r; +} +#endif /* WITH_OPENSSL */ + +int +sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, + const char *passphrase, struct sshkey **keyp, char **commentp) +{ + int r; + + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; + + switch (type) { +#ifdef WITH_OPENSSL + case KEY_RSA1: + return sshkey_parse_private_rsa1(blob, passphrase, + keyp, commentp); + case KEY_DSA: + case KEY_ECDSA: + case KEY_RSA: + return sshkey_parse_private_pem_fileblob(blob, type, passphrase, + keyp, commentp); +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + return sshkey_parse_private2(blob, type, passphrase, + keyp, commentp); + case KEY_UNSPEC: + if ((r = sshkey_parse_private2(blob, type, passphrase, keyp, + commentp)) == 0) + return 0; +#ifdef WITH_OPENSSL + return sshkey_parse_private_pem_fileblob(blob, type, passphrase, + keyp, commentp); +#else + return SSH_ERR_INVALID_FORMAT; +#endif /* WITH_OPENSSL */ + default: + return SSH_ERR_KEY_TYPE_UNKNOWN; + } +} + +int +sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase, + const char *filename, struct sshkey **keyp, char **commentp) +{ + int r; + + if (keyp != NULL) + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; + +#ifdef WITH_SSH1 + /* it's a SSH v1 key if the public key part is readable */ + if ((r = sshkey_parse_public_rsa1_fileblob(buffer, NULL, NULL)) == 0) { + return sshkey_parse_private_fileblob_type(buffer, KEY_RSA1, + passphrase, keyp, commentp); + } +#endif /* WITH_SSH1 */ + if ((r = sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC, + passphrase, keyp, commentp)) == 0) + return 0; + return r; +} diff --git a/sshkey.h b/sshkey.h new file mode 100644 index 000000000..4127db244 --- /dev/null +++ b/sshkey.h @@ -0,0 +1,222 @@ +/* $OpenBSD: sshkey.h,v 1.1 2014/06/24 01:16:58 djm Exp $ */ + +/* + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SSHKEY_H +#define SSHKEY_H + +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#else /* OPENSSL */ +#define RSA void +#define DSA void +#define EC_KEY void +#define EC_GROUP void +#define EC_POINT void +#endif /* WITH_OPENSSL */ + +#define SSH_RSA_MINIMUM_MODULUS_SIZE 768 +#define SSH_KEY_MAX_SIGN_DATA_SIZE (1 << 20) + +struct sshbuf; + +/* Key types */ +enum sshkey_types { + KEY_RSA1, + KEY_RSA, + KEY_DSA, + KEY_ECDSA, + KEY_ED25519, + KEY_RSA_CERT, + KEY_DSA_CERT, + KEY_ECDSA_CERT, + KEY_ED25519_CERT, + KEY_RSA_CERT_V00, + KEY_DSA_CERT_V00, + KEY_UNSPEC +}; + +/* Fingerprint hash algorithms */ +enum sshkey_fp_type { + SSH_FP_SHA1, + SSH_FP_MD5, + SSH_FP_SHA256 +}; + +/* Fingerprint representation formats */ +enum sshkey_fp_rep { + SSH_FP_HEX, + SSH_FP_BUBBLEBABBLE, + SSH_FP_RANDOMART +}; + +/* key is stored in external hardware */ +#define SSHKEY_FLAG_EXT 0x0001 + +#define SSHKEY_CERT_MAX_PRINCIPALS 256 +/* XXX opaquify? */ +struct sshkey_cert { + struct sshbuf *certblob; /* Kept around for use on wire */ + u_int type; /* SSH2_CERT_TYPE_USER or SSH2_CERT_TYPE_HOST */ + u_int64_t serial; + char *key_id; + u_int nprincipals; + char **principals; + u_int64_t valid_after, valid_before; + struct sshbuf *critical; + struct sshbuf *extensions; + struct sshkey *signature_key; +}; + +/* XXX opaquify? */ +struct sshkey { + int type; + int flags; + RSA *rsa; + DSA *dsa; + int ecdsa_nid; /* NID of curve */ + EC_KEY *ecdsa; + u_char *ed25519_sk; + u_char *ed25519_pk; + struct sshkey_cert *cert; +}; + +#define ED25519_SK_SZ crypto_sign_ed25519_SECRETKEYBYTES +#define ED25519_PK_SZ crypto_sign_ed25519_PUBLICKEYBYTES + +struct sshkey *sshkey_new(int); +int sshkey_add_private(struct sshkey *); +struct sshkey *sshkey_new_private(int); +void sshkey_free(struct sshkey *); +int sshkey_demote(const struct sshkey *, struct sshkey **); +int sshkey_equal_public(const struct sshkey *, + const struct sshkey *); +int sshkey_equal(const struct sshkey *, const struct sshkey *); +char *sshkey_fingerprint(const struct sshkey *, + enum sshkey_fp_type, enum sshkey_fp_rep); +int sshkey_fingerprint_raw(const struct sshkey *k, + enum sshkey_fp_type dgst_type, u_char **retp, size_t *lenp); +const char *sshkey_type(const struct sshkey *); +const char *sshkey_cert_type(const struct sshkey *); +int sshkey_write(const struct sshkey *, FILE *); +int sshkey_read(struct sshkey *, char **); +u_int sshkey_size(const struct sshkey *); + +int sshkey_generate(int type, u_int bits, struct sshkey **keyp); +int sshkey_from_private(const struct sshkey *, struct sshkey **); +int sshkey_type_from_name(const char *); +int sshkey_is_cert(const struct sshkey *); +int sshkey_type_is_cert(int); +int sshkey_type_plain(int); +int sshkey_to_certified(struct sshkey *, int); +int sshkey_drop_cert(struct sshkey *); +int sshkey_certify(struct sshkey *, struct sshkey *); +int sshkey_cert_copy(const struct sshkey *, struct sshkey *); +int sshkey_cert_check_authority(const struct sshkey *, int, int, + const char *, const char **); +int sshkey_cert_is_legacy(const struct sshkey *); + +int sshkey_ecdsa_nid_from_name(const char *); +int sshkey_curve_name_to_nid(const char *); +const char * sshkey_curve_nid_to_name(int); +u_int sshkey_curve_nid_to_bits(int); +int sshkey_ecdsa_bits_to_nid(int); +int sshkey_ecdsa_key_to_nid(EC_KEY *); +int sshkey_ec_nid_to_hash_alg(int nid); +int sshkey_ec_validate_public(const EC_GROUP *, const EC_POINT *); +int sshkey_ec_validate_private(const EC_KEY *); +const char *sshkey_ssh_name(const struct sshkey *); +const char *sshkey_ssh_name_plain(const struct sshkey *); +int sshkey_names_valid2(const char *); +char *key_alg_list(int, int); + +int sshkey_from_blob(const u_char *, size_t, struct sshkey **); +int sshkey_to_blob_buf(const struct sshkey *, struct sshbuf *); +int sshkey_to_blob(const struct sshkey *, u_char **, size_t *); +int sshkey_plain_to_blob_buf(const struct sshkey *, struct sshbuf *); +int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *); + +int sshkey_sign(const struct sshkey *, u_char **, size_t *, + const u_char *, size_t, u_int); +int sshkey_verify(const struct sshkey *, const u_char *, size_t, + const u_char *, size_t, u_int); + +/* for debug */ +void sshkey_dump_ec_point(const EC_GROUP *, const EC_POINT *); +void sshkey_dump_ec_key(const EC_KEY *); + +/* private key parsing and serialisation */ +int sshkey_private_serialize(const struct sshkey *key, struct sshbuf *buf); +int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **keyp); + +/* private key file format parsing and serialisation */ +int sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, + const char *passphrase, const char *comment, + int force_new_format, const char *new_format_cipher, int new_format_rounds); +int sshkey_parse_public_rsa1_fileblob(struct sshbuf *blob, + struct sshkey **keyp, char **commentp); +int sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type, + const char *passphrase, struct sshkey **keyp, char **commentp); +int sshkey_parse_private_fileblob(struct sshbuf *buffer, + const char *passphrase, const char *filename, struct sshkey **keyp, + char **commentp); +int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, + const char *passphrase, struct sshkey **keyp, char **commentp); + +#ifdef SSHKEY_INTERNAL +int ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat); +int ssh_rsa_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat); +int ssh_dss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat); +int ssh_dss_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat); +int ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat); +int ssh_ecdsa_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat); +int ssh_ed25519_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat); +int ssh_ed25519_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat); +#endif + +#ifndef WITH_OPENSSL +#undef RSA +#undef DSA +#undef EC_KEY +#undef EC_GROUP +#undef EC_POINT +#endif /* WITH_OPENSSL */ + +#endif /* SSHKEY_H */ -- cgit v1.2.3