From 9b87e795387f748a21cd4d6c26ae57e800c36b54 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Tue, 31 Aug 2010 22:31:37 +1000 Subject: - tedu@cvs.openbsd.org 2010/08/12 23:34:39 [ssh-add.c ssh-agent.c ssh-keygen.c ssh-keysign.c ssh.c sshd.c] OpenSSL_add_all_algorithms is the name of the function we have a man page for, so use that. ok djm --- ssh.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'ssh.c') diff --git a/ssh.c b/ssh.c index 4419f7642..34215a00d 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.346 2010/08/12 21:49:44 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.347 2010/08/12 23:34:39 tedu Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -599,7 +599,7 @@ main(int ac, char **av) if (!host) usage(); - SSLeay_add_all_algorithms(); + OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); /* Initialize the command to execute on remote host. */ -- cgit v1.2.3 From d96546f5b0f7c57395a338dbb9ac3ac5a48b77fa Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Tue, 31 Aug 2010 22:32:12 +1000 Subject: - djm@cvs.openbsd.org 2010/08/16 04:06:06 [ssh-add.c ssh-agent.c ssh-keygen.c ssh-keysign.c ssh.c sshd.c] backout previous temporarily; discussed with deraadt@ --- ChangeLog | 3 +++ ssh-add.c | 4 ++-- ssh-agent.c | 4 ++-- ssh-keygen.c | 4 ++-- ssh-keysign.c | 4 ++-- ssh.c | 4 ++-- sshd.c | 4 ++-- 7 files changed, 15 insertions(+), 12 deletions(-) (limited to 'ssh.c') diff --git a/ChangeLog b/ChangeLog index f18c8de92..a56f04349 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,9 @@ [ssh-add.c ssh-agent.c ssh-keygen.c ssh-keysign.c ssh.c sshd.c] OpenSSL_add_all_algorithms is the name of the function we have a man page for, so use that. ok djm + - djm@cvs.openbsd.org 2010/08/16 04:06:06 + [ssh-add.c ssh-agent.c ssh-keygen.c ssh-keysign.c ssh.c sshd.c] + backout previous temporarily; discussed with deraadt@ 20100827 - (dtucker) [contrib/redhat/sshd.init] Bug #1810: initlog is deprecated, diff --git a/ssh-add.c b/ssh-add.c index e3e89d34c..7f8fb2c6d 100644 --- a/ssh-add.c +++ b/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.97 2010/08/12 23:34:38 tedu Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.98 2010/08/16 04:06:06 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -372,7 +372,7 @@ main(int argc, char **argv) init_rng(); seed_rng(); - OpenSSL_add_all_algorithms(); + SSLeay_add_all_algorithms(); /* At first, get a connection to the authentication agent. */ ac = ssh_get_authentication_connection(); diff --git a/ssh-agent.c b/ssh-agent.c index d5690e0c5..e6725ea88 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.167 2010/08/12 23:34:38 tedu Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.168 2010/08/16 04:06:06 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1092,7 +1092,7 @@ main(int ac, char **av) prctl(PR_SET_DUMPABLE, 0); #endif - OpenSSL_add_all_algorithms(); + SSLeay_add_all_algorithms(); __progname = ssh_get_progname(av[0]); init_rng(); diff --git a/ssh-keygen.c b/ssh-keygen.c index 37670ba67..93f598004 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.198 2010/08/12 23:34:38 tedu Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.199 2010/08/16 04:06:06 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -1781,7 +1781,7 @@ main(int argc, char **argv) __progname = ssh_get_progname(argv[0]); - OpenSSL_add_all_algorithms(); + SSLeay_add_all_algorithms(); log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); init_rng(); diff --git a/ssh-keysign.c b/ssh-keysign.c index eddbcf707..cf3bf1b9b 100644 --- a/ssh-keysign.c +++ b/ssh-keysign.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keysign.c,v 1.33 2010/08/12 23:34:39 tedu Exp $ */ +/* $OpenBSD: ssh-keysign.c,v 1.34 2010/08/16 04:06:06 djm Exp $ */ /* * Copyright (c) 2002 Markus Friedl. All rights reserved. * @@ -194,7 +194,7 @@ main(int argc, char **argv) if (key_fd[0] == -1 && key_fd[1] == -1) fatal("could not open any host key"); - OpenSSL_add_all_algorithms(); + SSLeay_add_all_algorithms(); for (i = 0; i < 256; i++) rnd[i] = arc4random(); RAND_seed(rnd, sizeof(rnd)); diff --git a/ssh.c b/ssh.c index 34215a00d..44b570bf9 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.347 2010/08/12 23:34:39 tedu Exp $ */ +/* $OpenBSD: ssh.c,v 1.348 2010/08/16 04:06:06 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -599,7 +599,7 @@ main(int ac, char **av) if (!host) usage(); - OpenSSL_add_all_algorithms(); + SSLeay_add_all_algorithms(); ERR_load_crypto_strings(); /* Initialize the command to execute on remote host. */ diff --git a/sshd.c b/sshd.c index e61c0b9d6..52a3789bb 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.376 2010/08/12 23:34:39 tedu Exp $ */ +/* $OpenBSD: sshd.c,v 1.377 2010/08/16 04:06:06 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1464,7 +1464,7 @@ main(int ac, char **av) else closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); - OpenSSL_add_all_algorithms(); + SSLeay_add_all_algorithms(); /* * Force logging to stderr until we have loaded the private host -- cgit v1.2.3 From eb8b60e320cdade9f4c07e2abacfb92c52e01348 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Tue, 31 Aug 2010 22:41:14 +1000 Subject: - djm@cvs.openbsd.org 2010/08/31 11:54:45 [PROTOCOL PROTOCOL.agent PROTOCOL.certkeys auth2-jpake.c authfd.c] [authfile.c buffer.h dns.c kex.c kex.h key.c key.h monitor.c] [monitor_wrap.c myproposal.h packet.c packet.h pathnames.h readconf.c] [ssh-add.1 ssh-add.c ssh-agent.1 ssh-agent.c ssh-keygen.1 ssh-keygen.c] [ssh-keyscan.1 ssh-keyscan.c ssh-keysign.8 ssh.1 ssh.c ssh2.h] [ssh_config.5 sshconnect.c sshconnect2.c sshd.8 sshd.c sshd_config.5] [uuencode.c uuencode.h bufec.c kexecdh.c kexecdhc.c kexecdhs.c ssh-ecdsa.c] Implement Elliptic Curve Cryptography modes for key exchange (ECDH) and host/user keys (ECDSA) as specified by RFC5656. ECDH and ECDSA offer better performance than plain DH and DSA at the same equivalent symmetric key length, as well as much shorter keys. Only the mandatory sections of RFC5656 are implemented, specifically the three REQUIRED curves nistp256, nistp384 and nistp521 and only ECDH and ECDSA. Point compression (optional in RFC5656 is NOT implemented). Certificate host and user keys using the new ECDSA key types are supported. Note that this code has not been tested for interoperability and may be subject to change. feedback and ok markus@ --- ChangeLog | 23 +++ PROTOCOL | 45 +++-- PROTOCOL.agent | 44 +++-- PROTOCOL.certkeys | 89 ++++++--- auth2-jpake.c | 7 +- authfd.c | 20 +- authfile.c | 32 +++- bufec.c | 140 ++++++++++++++ buffer.h | 9 +- dns.c | 3 +- kex.c | 10 +- kex.h | 16 +- kexecdh.c | 108 +++++++++++ kexecdhc.c | 156 ++++++++++++++++ kexecdhs.c | 161 ++++++++++++++++ key.c | 541 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- key.h | 23 ++- monitor.c | 3 +- monitor_wrap.c | 3 +- myproposal.h | 32 +++- packet.c | 14 +- packet.h | 5 +- pathnames.h | 4 +- readconf.c | 8 +- ssh-add.1 | 13 +- ssh-add.c | 3 +- ssh-agent.1 | 11 +- ssh-agent.c | 60 +++++- ssh-ecdsa.c | 160 ++++++++++++++++ ssh-keygen.1 | 9 +- ssh-keygen.c | 40 +++- ssh-keyscan.1 | 20 +- ssh-keyscan.c | 13 +- ssh-keysign.8 | 6 +- ssh.1 | 25 ++- ssh.c | 27 ++- ssh2.h | 6 +- ssh_config.5 | 23 ++- sshconnect.c | 4 +- sshconnect2.c | 3 +- sshd.8 | 20 +- sshd.c | 7 +- sshd_config.5 | 12 +- uuencode.c | 4 +- uuencode.h | 4 +- 45 files changed, 1793 insertions(+), 173 deletions(-) create mode 100644 bufec.c create mode 100644 kexecdh.c create mode 100644 kexecdhc.c create mode 100644 kexecdhs.c create mode 100644 ssh-ecdsa.c (limited to 'ssh.c') diff --git a/ChangeLog b/ChangeLog index 2f4acd9de..889580e5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -25,6 +25,29 @@ * actually, we allow a single one at the end of the string for now because we don't know how many deployed implementations get this wrong, but don't count on this to remain indefinitely. + - djm@cvs.openbsd.org 2010/08/31 11:54:45 + [PROTOCOL PROTOCOL.agent PROTOCOL.certkeys auth2-jpake.c authfd.c] + [authfile.c buffer.h dns.c kex.c kex.h key.c key.h monitor.c] + [monitor_wrap.c myproposal.h packet.c packet.h pathnames.h readconf.c] + [ssh-add.1 ssh-add.c ssh-agent.1 ssh-agent.c ssh-keygen.1 ssh-keygen.c] + [ssh-keyscan.1 ssh-keyscan.c ssh-keysign.8 ssh.1 ssh.c ssh2.h] + [ssh_config.5 sshconnect.c sshconnect2.c sshd.8 sshd.c sshd_config.5] + [uuencode.c uuencode.h bufec.c kexecdh.c kexecdhc.c kexecdhs.c ssh-ecdsa.c] + Implement Elliptic Curve Cryptography modes for key exchange (ECDH) and + host/user keys (ECDSA) as specified by RFC5656. ECDH and ECDSA offer + better performance than plain DH and DSA at the same equivalent symmetric + key length, as well as much shorter keys. + + Only the mandatory sections of RFC5656 are implemented, specifically the + three REQUIRED curves nistp256, nistp384 and nistp521 and only ECDH and + ECDSA. Point compression (optional in RFC5656 is NOT implemented). + + Certificate host and user keys using the new ECDSA key types are supported. + + Note that this code has not been tested for interoperability and may be + subject to change. + + feedback and ok markus@ 20100827 - (dtucker) [contrib/redhat/sshd.init] Bug #1810: initlog is deprecated, diff --git a/PROTOCOL b/PROTOCOL index 5fc31eade..5d2a7118a 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -12,7 +12,9 @@ are individually implemented as extensions described below. The protocol used by OpenSSH's ssh-agent is described in the file PROTOCOL.agent -1. transport: Protocol 2 MAC algorithm "umac-64@openssh.com" +1. Transport protocol changes + +1.1. transport: Protocol 2 MAC algorithm "umac-64@openssh.com" This is a new transport-layer MAC method using the UMAC algorithm (rfc4418). This method is identical to the "umac-64" method documented @@ -20,7 +22,7 @@ in: http://www.openssh.com/txt/draft-miller-secsh-umac-01.txt -2. transport: Protocol 2 compression algorithm "zlib@openssh.com" +1.2. transport: Protocol 2 compression algorithm "zlib@openssh.com" This transport-layer compression method uses the zlib compression algorithm (identical to the "zlib" method in rfc4253), but delays the @@ -31,14 +33,27 @@ The method is documented in: http://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt -3. transport: New public key algorithms "ssh-rsa-cert-v00@openssh.com" and - "ssh-dsa-cert-v00@openssh.com" +1.3. transport: New public key algorithms "ssh-rsa-cert-v00@openssh.com", + "ssh-dsa-cert-v00@openssh.com", + "ecdsa-sha2-nistp256-cert-v01@openssh.com", + "ecdsa-sha2-nistp384-cert-v01@openssh.com" and + "ecdsa-sha2-nistp521-cert-v01@openssh.com" -OpenSSH introduces two new public key algorithms to support certificate +OpenSSH introduces new public key algorithms to support certificate authentication for users and hostkeys. These methods are documented in the file PROTOCOL.certkeys -4. connection: Channel write close extension "eow@openssh.com" +1.4. transport: Elliptic Curve cryptography + +OpenSSH supports ECC key exchange and public key authentication as +specified in RFC5656. Only the ecdsa-sha2-nistp256, ecdsa-sha2-nistp384 +and ecdsa-sha2-nistp521 curves over GF(p) are supported. Elliptic +curve points encoded using point compression are NOT accepted or +generated. + +2. Connection protocol changes + +2.1. connection: Channel write close extension "eow@openssh.com" The SSH connection protocol (rfc4254) provides the SSH_MSG_CHANNEL_EOF message to allow an endpoint to signal its peer that it will send no @@ -77,8 +92,8 @@ message is only sent to OpenSSH peers (identified by banner). Other SSH implementations may be whitelisted to receive this message upon request. -5. connection: disallow additional sessions extension - "no-more-sessions@openssh.com" +2.2. connection: disallow additional sessions extension + "no-more-sessions@openssh.com" Most SSH connections will only ever request a single session, but a attacker may abuse a running ssh client to surreptitiously open @@ -105,7 +120,7 @@ of this message, the no-more-sessions request is only sent to OpenSSH servers (identified by banner). Other SSH implementations may be whitelisted to receive this message upon request. -6. connection: Tunnel forward extension "tun@openssh.com" +2.3. connection: Tunnel forward extension "tun@openssh.com" OpenSSH supports layer 2 and layer 3 tunnelling via the "tun@openssh.com" channel type. This channel type supports forwarding of network packets @@ -166,7 +181,9 @@ The contents of the "data" field for layer 2 packets is: The "frame" field contains an IEEE 802.3 Ethernet frame, including header. -7. sftp: Reversal of arguments to SSH_FXP_SYMLINK +3. SFTP protocol changes + +3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK When OpenSSH's sftp-server was implemented, the order of the arguments to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately, @@ -179,7 +196,7 @@ SSH_FXP_SYMLINK as follows: string targetpath string linkpath -8. sftp: Server extension announcement in SSH_FXP_VERSION +3.2. sftp: Server extension announcement in SSH_FXP_VERSION OpenSSH's sftp-server lists the extensions it supports using the standard extension announcement mechanism in the SSH_FXP_VERSION server @@ -200,7 +217,7 @@ ever changed in an incompatible way. The server MAY advertise the same extension with multiple versions (though this is unlikely). Clients MUST check the version number before attempting to use the extension. -9. sftp: Extension request "posix-rename@openssh.com" +3.3. sftp: Extension request "posix-rename@openssh.com" This operation provides a rename operation with POSIX semantics, which are different to those provided by the standard SSH_FXP_RENAME in @@ -217,7 +234,7 @@ rename(oldpath, newpath) and will respond with a SSH_FXP_STATUS message. This extension is advertised in the SSH_FXP_VERSION hello with version "1". -10. sftp: Extension requests "statvfs@openssh.com" and +3.4. sftp: Extension requests "statvfs@openssh.com" and "fstatvfs@openssh.com" These requests correspond to the statvfs and fstatvfs POSIX system @@ -258,4 +275,4 @@ The values of the f_flag bitmask are as follows: Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are advertised in the SSH_FXP_VERSION hello with version "2". -$OpenBSD: PROTOCOL,v 1.15 2010/02/26 20:29:54 djm Exp $ +$OpenBSD: PROTOCOL,v 1.16 2010/08/31 11:54:45 djm Exp $ diff --git a/PROTOCOL.agent b/PROTOCOL.agent index b34fcd318..de94d037d 100644 --- a/PROTOCOL.agent +++ b/PROTOCOL.agent @@ -159,8 +159,8 @@ successfully added or a SSH_AGENT_FAILURE if an error occurred. 2.2.3 Add protocol 2 key -The OpenSSH agent supports DSA and RSA keys for protocol 2. DSA keys may -be added using the following request +The OpenSSH agent supports DSA, ECDSA and RSA keys for protocol 2. DSA +keys may be added using the following request byte SSH2_AGENTC_ADD_IDENTITY or SSH2_AGENTC_ADD_ID_CONSTRAINED @@ -182,6 +182,30 @@ DSA certificates may be added with: string key_comment constraint[] key_constraints +ECDSA keys may be added using the following request + + byte SSH2_AGENTC_ADD_IDENTITY or + SSH2_AGENTC_ADD_ID_CONSTRAINED + string "ecdsa-sha2-nistp256" | + "ecdsa-sha2-nistp384" | + "ecdsa-sha2-nistp521" + string ecdsa_curve_name + string ecdsa_public_key + mpint ecdsa_private + string key_comment + constraint[] key_constraints + +ECDSA certificates may be added with: + byte SSH2_AGENTC_ADD_IDENTITY or + SSH2_AGENTC_ADD_ID_CONSTRAINED + string "ecdsa-sha2-nistp256-cert-v01@openssh.com" | + "ecdsa-sha2-nistp384-cert-v01@openssh.com" | + "ecdsa-sha2-nistp521-cert-v01@openssh.com" + string certificate + mpint ecdsa_private_key + string key_comment + constraint[] key_constraints + RSA keys may be added with this request: byte SSH2_AGENTC_ADD_IDENTITY or @@ -214,7 +238,7 @@ order to the protocol 1 add keys message. As with the corresponding protocol 1 "add key" request, the private key is overspecified to avoid redundant processing. -For both DSA and RSA key add requests, "key_constraints" may only be +For DSA, ECDSA and RSA key add requests, "key_constraints" may only be present if the request type is SSH2_AGENTC_ADD_ID_CONSTRAINED. The agent will reply with a SSH_AGENT_SUCCESS if the key has been @@ -294,8 +318,7 @@ Protocol 2 keys may be removed with the following request: string key_blob Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key -Algorithms" for either of the supported key types: "ssh-dss" or -"ssh-rsa". +Algorithms" for any of the supported protocol 2 key types. The agent will delete any private key matching the specified public key and return SSH_AGENT_SUCCESS. If no such key was found, the agent will @@ -364,8 +387,7 @@ Followed by zero or more consecutive keys, encoded as: string key_comment Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key -Algorithms" for either of the supported key types: "ssh-dss" or -"ssh-rsa". +Algorithms" for any of the supported protocol 2 key types. 2.6 Private key operations @@ -429,9 +451,9 @@ a protocol 2 key: uint32 flags Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key -Algorithms" for either of the supported key types: "ssh-dss" or -"ssh-rsa". "flags" is a bit-mask, but at present only one possible value -is defined (see below for its meaning): +Algorithms" for any of the supported protocol 2 key types. "flags" is +a bit-mask, but at present only one possible value is defined (see below +for its meaning): SSH_AGENT_OLD_SIGNATURE 1 @@ -535,4 +557,4 @@ Locking and unlocking affects both protocol 1 and protocol 2 keys. SSH_AGENT_CONSTRAIN_LIFETIME 1 SSH_AGENT_CONSTRAIN_CONFIRM 2 -$OpenBSD: PROTOCOL.agent,v 1.5 2010/02/26 20:29:54 djm Exp $ +$OpenBSD: PROTOCOL.agent,v 1.6 2010/08/31 11:54:45 djm Exp $ diff --git a/PROTOCOL.certkeys b/PROTOCOL.certkeys index 1d1be13da..2f9764981 100644 --- a/PROTOCOL.certkeys +++ b/PROTOCOL.certkeys @@ -5,31 +5,37 @@ Background ---------- The SSH protocol currently supports a simple public key authentication -mechanism. Unlike other public key implementations, SSH eschews the -use of X.509 certificates and uses raw keys. This approach has some -benefits relating to simplicity of configuration and minimisation -of attack surface, but it does not support the important use-cases -of centrally managed, passwordless authentication and centrally -certified host keys. +mechanism. Unlike other public key implementations, SSH eschews the use +of X.509 certificates and uses raw keys. This approach has some benefits +relating to simplicity of configuration and minimisation of attack +surface, but it does not support the important use-cases of centrally +managed, passwordless authentication and centrally certified host keys. These protocol extensions build on the simple public key authentication -system already in SSH to allow certificate-based authentication. -The certificates used are not traditional X.509 certificates, with -numerous options and complex encoding rules, but something rather -more minimal: a key, some identity information and usage options -that have been signed with some other trusted key. +system already in SSH to allow certificate-based authentication. The +certificates used are not traditional X.509 certificates, with numerous +options and complex encoding rules, but something rather more minimal: a +key, some identity information and usage options that have been signed +with some other trusted key. A sshd server may be configured to allow authentication via certified -keys, by extending the existing ~/.ssh/authorized_keys mechanism -to allow specification of certification authority keys in addition -to raw user keys. The ssh client will support automatic verification -of acceptance of certified host keys, by adding a similar ability -to specify CA keys in ~/.ssh/known_hosts. +keys, by extending the existing ~/.ssh/authorized_keys mechanism to +allow specification of certification authority keys in addition to +raw user keys. The ssh client will support automatic verification of +acceptance of certified host keys, by adding a similar ability to +specify CA keys in ~/.ssh/known_hosts. -Certified keys are represented using two new key types: -ssh-rsa-cert-v01@openssh.com and ssh-dss-cert-v01@openssh.com that -include certification information along with the public key that is used -to sign challenges. ssh-keygen performs the CA signing operation. +Certified keys are represented using new key types: + + ssh-rsa-cert-v01@openssh.com + ssh-dss-cert-v01@openssh.com + ecdsa-sha2-nistp256-cert-v01@openssh.com + ecdsa-sha2-nistp384-cert-v01@openssh.com + ecdsa-sha2-nistp521-cert-v01@openssh.com + +These include certification information along with the public key +that is used to sign challenges. ssh-keygen performs the CA signing +operation. Protocol extensions ------------------- @@ -47,10 +53,9 @@ in RFC4252 section 7. New public key formats ---------------------- -The ssh-rsa-cert-v01@openssh.com and ssh-dss-cert-v01@openssh.com key -types take a similar high-level format (note: data types and -encoding are as per RFC4251 section 5). The serialised wire encoding of -these certificates is also used for storing them on disk. +The certificate key types take a similar high-level format (note: data +types and encoding are as per RFC4251 section 5). The serialised wire +encoding of these certificates is also used for storing them on disk. #define SSH_CERT_TYPE_USER 1 #define SSH_CERT_TYPE_HOST 2 @@ -93,6 +98,26 @@ DSA certificate string signature key string signature +ECDSA certificate + + string "ecdsa-sha2-nistp256@openssh.com" | + "ecdsa-sha2-nistp384@openssh.com" | + "ecdsa-sha2-nistp521@openssh.com" + string nonce + string curve + string public_key + uint64 serial + uint32 type + string key id + string valid principals + uint64 valid after + uint64 valid before + string critical options + string extensions + string reserved + string signature key + string signature + The nonce field is a CA-provided random bitstring of arbitrary length (but typically 16 or 32 bytes) included to make attacks that depend on inducing collisions in the signature hash infeasible. @@ -101,6 +126,9 @@ e and n are the RSA exponent and public modulus respectively. p, q, g, y are the DSA parameters as described in FIPS-186-2. +curve and public key are respectively the ECDSA "[identifier]" and "Q" +defined in section 3.1 of RFC5656. + serial is an optional certificate serial number set by the CA to provide an abbreviated way to refer to certificates from that CA. If a CA does not wish to number its certificates it must set this @@ -123,7 +151,8 @@ any principal of the specified type. XXX DNS wildcards? "valid after" and "valid before" specify a validity period for the certificate. Each represents a time in seconds since 1970-01-01 00:00:00. A certificate is considered valid if: - valid after <= current time < valid before + + valid after <= current time < valid before criticial options is a set of zero or more key options encoded as below. All such options are "critical" in the sense that an implementation @@ -137,15 +166,17 @@ The reserved field is currently unused and is ignored in this version of the protocol. signature key contains the CA key used to sign the certificate. -The valid key types for CA keys are ssh-rsa and ssh-dss. "Chained" +The valid key types for CA keys are ssh-rsa, ssh-dss and the ECDSA types +ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521. "Chained" certificates, where the signature key type is a certificate type itself are NOT supported. Note that it is possible for a RSA certificate key to -be signed by a DSS CA key and vice-versa. +be signed by a DSS or ECDSA CA key and vice-versa. signature is computed over all preceding fields from the initial string up to, and including the signature key. Signatures are computed and encoded according to the rules defined for the CA's public key algorithm -(RFC4253 section 6.6 for ssh-rsa and ssh-dss). +(RFC4253 section 6.6 for ssh-rsa and ssh-dss, RFC5656 for the ECDSA +types). Critical options ---------------- @@ -222,4 +253,4 @@ permit-user-rc empty Flag indicating that execution of of this script will not be permitted if this option is not present. -$OpenBSD: PROTOCOL.certkeys,v 1.7 2010/08/04 05:40:39 djm Exp $ +$OpenBSD: PROTOCOL.certkeys,v 1.8 2010/08/31 11:54:45 djm Exp $ diff --git a/auth2-jpake.c b/auth2-jpake.c index 5de5506a6..a460e8216 100644 --- a/auth2-jpake.c +++ b/auth2-jpake.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-jpake.c,v 1.3 2009/03/05 07:18:19 djm Exp $ */ +/* $OpenBSD: auth2-jpake.c,v 1.4 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright (c) 2008 Damien Miller. All rights reserved. * @@ -162,6 +162,11 @@ derive_rawsalt(const char *username, u_char *rawsalt, u_int len) fatal("%s: DSA key missing priv_key", __func__); buffer_put_bignum2(&b, k->dsa->priv_key); break; + case KEY_ECDSA: + if (EC_KEY_get0_private_key(k->ecdsa) == NULL) + fatal("%s: ECDSA key missing priv_key", __func__); + buffer_put_bignum2(&b, EC_KEY_get0_private_key(k->ecdsa)); + break; default: fatal("%s: unknown key type %d", __func__, k->type); } diff --git a/authfd.c b/authfd.c index 739722fbf..ec537d2e9 100644 --- a/authfd.c +++ b/authfd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.c,v 1.83 2010/04/16 01:47:26 djm Exp $ */ +/* $OpenBSD: authfd.c,v 1.84 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -509,6 +509,19 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) buffer_len(&key->cert->certblob)); buffer_put_bignum2(b, key->dsa->priv_key); break; + 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; } buffer_put_cstring(b, comment); } @@ -541,6 +554,8 @@ ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key, case KEY_DSA: case KEY_DSA_CERT: case KEY_DSA_CERT_V00: + case KEY_ECDSA: + case KEY_ECDSA_CERT: type = constrained ? SSH2_AGENTC_ADD_ID_CONSTRAINED : SSH2_AGENTC_ADD_IDENTITY; @@ -589,7 +604,8 @@ ssh_remove_identity(AuthenticationConnection *auth, Key *key) buffer_put_bignum(&msg, key->rsa->e); buffer_put_bignum(&msg, key->rsa->n); } else if (key_type_plain(key->type) == KEY_DSA || - key_type_plain(key->type) == KEY_RSA) { + key_type_plain(key->type) == KEY_RSA || + key_type_plain(key->type) == KEY_ECDSA) { key_to_blob(key, &blob, &blen); buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY); buffer_put_string(&msg, blob, blen); diff --git a/authfile.c b/authfile.c index 2bd887845..865e7faf9 100644 --- a/authfile.c +++ b/authfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfile.c,v 1.82 2010/08/04 05:49:22 djm Exp $ */ +/* $OpenBSD: authfile.c,v 1.83 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -213,6 +213,10 @@ key_save_private_pem(Key *key, const char *filename, const char *_passphrase, success = PEM_write_DSAPrivateKey(fp, key->dsa, cipher, passphrase, len, NULL, NULL); break; + case KEY_ECDSA: + success = PEM_write_ECPrivateKey(fp, key->ecdsa, + cipher, passphrase, len, NULL, NULL); + break; case KEY_RSA: success = PEM_write_RSAPrivateKey(fp, key->rsa, cipher, passphrase, len, NULL, NULL); @@ -231,6 +235,7 @@ key_save_private(Key *key, const char *filename, const char *passphrase, return key_save_private_rsa1(key, filename, passphrase, comment); case KEY_DSA: + case KEY_ECDSA: case KEY_RSA: return key_save_private_pem(key, filename, passphrase, comment); @@ -509,6 +514,29 @@ key_load_private_pem(int fd, int type, const char *passphrase, name = "dsa w/o comment"; #ifdef DEBUG_PK DSA_print_fp(stderr, prv->dsa, 8); +#endif + } 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; + prv->ecdsa_nid = key_ecdsa_group_to_nid( + EC_KEY_get0_group(prv->ecdsa)); + if (key_curve_nid_to_name(prv->ecdsa_nid) == NULL) { + key_free(prv); + prv = NULL; + } + if (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 = "dsa w/o comment"; +#ifdef DEBUG_PK + if (prv->ecdsa != NULL) + key_dump_ec_key(prv->ecdsa); #endif } else { error("PEM_read_PrivateKey: mismatch or " @@ -581,6 +609,7 @@ key_load_private_type(int type, const char *filename, const char *passphrase, commentp); /* closes fd */ case KEY_DSA: + case KEY_ECDSA: case KEY_RSA: case KEY_UNSPEC: return key_load_private_pem(fd, type, passphrase, commentp); @@ -721,6 +750,7 @@ key_load_private_cert(int type, const char *filename, const char *passphrase, switch (type) { case KEY_RSA: case KEY_DSA: + case KEY_ECDSA: break; default: error("%s: unsupported key type", __func__); diff --git a/bufec.c b/bufec.c new file mode 100644 index 000000000..dff9c69c9 --- /dev/null +++ b/bufec.c @@ -0,0 +1,140 @@ +/* $OpenBSD: bufec.c,v 1.1 2010/08/31 11:54:45 djm Exp $ */ +/* + * Copyright (c) 2010 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include + +#include +#include + +#include +#include + +#include "xmalloc.h" +#include "buffer.h" +#include "log.h" +#include "misc.h" + +/* + * Maximum supported EC GFp field length is 528 bits. SEC1 uncompressed + * encoding represents this as two bitstring points that should each + * be no longer than the field length, SEC1 specifies a 1 byte + * point type header. + * Being paranoid here may insulate us to parsing problems in + * EC_POINT_oct2point. + */ +#define BUFFER_MAX_ECPOINT_LEN ((528*2 / 8) + 1) + +/* + * Append an EC_POINT to the buffer as a string containing a SEC1 encoded + * uncompressed point. Fortunately OpenSSL handles the gory details for us. + */ +int +buffer_put_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve, + const EC_POINT *point) +{ + u_char *buf = NULL; + size_t len; + BN_CTX *bnctx; + int ret = -1; + + /* Determine length */ + if ((bnctx = BN_CTX_new()) == NULL) + fatal("%s: BN_CTX_new failed", __func__); + len = EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED, + NULL, 0, bnctx); + if (len > BUFFER_MAX_ECPOINT_LEN) { + error("%s: giant EC point: len = %lu (max %u)", + __func__, (u_long)len, BUFFER_MAX_ECPOINT_LEN); + goto out; + } + /* Convert */ + buf = xmalloc(len); + if (EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED, + buf, len, bnctx) != len) { + error("%s: EC_POINT_point2oct length mismatch", __func__); + goto out; + } + /* Append */ + buffer_put_string(buffer, buf, len); + ret = 0; + out: + if (buf != NULL) { + bzero(buf, len); + xfree(buf); + } + BN_CTX_free(bnctx); + return ret; +} + +void +buffer_put_ecpoint(Buffer *buffer, const EC_GROUP *curve, + const EC_POINT *point) +{ + if (buffer_put_ecpoint_ret(buffer, curve, point) == -1) + fatal("%s: buffer error", __func__); +} + +int +buffer_get_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve, + EC_POINT *point) +{ + u_char *buf; + u_int len; + BN_CTX *bnctx; + int ret = -1; + + if ((buf = buffer_get_string_ret(buffer, &len)) == NULL) { + error("%s: invalid point", __func__); + return -1; + } + if ((bnctx = BN_CTX_new()) == NULL) + fatal("%s: BN_CTX_new failed", __func__); + if (len > BUFFER_MAX_ECPOINT_LEN) { + error("%s: EC_POINT too long: %u > max %u", __func__, + len, BUFFER_MAX_ECPOINT_LEN); + goto out; + } + if (len == 0) { + error("%s: EC_POINT buffer is empty", __func__); + goto out; + } + if (buf[0] != POINT_CONVERSION_UNCOMPRESSED) { + error("%s: EC_POINT is in an incorrect form: " + "0x%02x (want 0x%02x)", __func__, buf[0], + POINT_CONVERSION_UNCOMPRESSED); + goto out; + } + if (EC_POINT_oct2point(curve, point, buf, len, bnctx) != 1) { + error("buffer_get_bignum2_ret: BN_bin2bn failed"); + goto out; + } + /* EC_POINT_oct2point verifies that the point is on the curve for us */ + ret = 0; + out: + BN_CTX_free(bnctx); + bzero(buf, len); + xfree(buf); + return ret; +} + +void +buffer_get_ecpoint(Buffer *buffer, const EC_GROUP *curve, + EC_POINT *point) +{ + if (buffer_get_ecpoint_ret(buffer, curve, point) == -1) + fatal("%s: buffer error", __func__); +} + diff --git a/buffer.h b/buffer.h index 93baae2c8..1fb3f1666 100644 --- a/buffer.h +++ b/buffer.h @@ -1,4 +1,4 @@ -/* $OpenBSD: buffer.h,v 1.20 2010/08/31 09:58:37 djm Exp $ */ +/* $OpenBSD: buffer.h,v 1.21 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen @@ -86,4 +86,11 @@ char *buffer_get_cstring_ret(Buffer *, u_int *); void *buffer_get_string_ptr_ret(Buffer *, u_int *); int buffer_get_char_ret(char *, Buffer *); +#include + +int buffer_put_ecpoint_ret(Buffer *, const EC_GROUP *, const EC_POINT *); +void buffer_put_ecpoint(Buffer *, const EC_GROUP *, const EC_POINT *); +int buffer_get_ecpoint_ret(Buffer *, const EC_GROUP *, EC_POINT *); +void buffer_get_ecpoint(Buffer *, const EC_GROUP *, EC_POINT *); + #endif /* BUFFER_H */ diff --git a/dns.c b/dns.c index 2e7bb5aae..131cb3d8b 100644 --- a/dns.c +++ b/dns.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dns.c,v 1.26 2010/02/26 20:29:54 djm Exp $ */ +/* $OpenBSD: dns.c,v 1.27 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. @@ -86,6 +86,7 @@ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, case KEY_DSA: *algorithm = SSHFP_KEY_DSA; break; + /* XXX KEY_ECDSA */ default: *algorithm = SSHFP_KEY_RESERVED; /* 0 */ } diff --git a/kex.c b/kex.c index ca5aae3e4..abe9b9f5d 100644 --- a/kex.c +++ b/kex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.c,v 1.83 2010/08/31 09:58:37 djm Exp $ */ +/* $OpenBSD: kex.c,v 1.84 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * @@ -325,6 +325,10 @@ choose_kex(Kex *k, char *client, char *server) } else if (strcmp(k->name, KEX_DHGEX_SHA256) == 0) { k->kex_type = KEX_DH_GEX_SHA256; k->evp_md = evp_ssh_sha256(); + } else if (strncmp(k->name, KEX_ECDH_SHA256, + sizeof(KEX_ECDH_SHA256) - 1) == 0) { + k->kex_type = KEX_ECDH_SHA2; + k->evp_md = evp_ssh_sha256(); #endif } else fatal("bad kex alg %s", k->name); @@ -559,11 +563,11 @@ derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus, memset(&md, 0, sizeof(md)); } -#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) +#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) void dump_digest(char *msg, u_char *digest, int len) { - u_int i; + int i; fprintf(stderr, "%s\n", msg); for (i = 0; i < len; i++) { diff --git a/kex.h b/kex.h index 62fa2ea50..a183ffda2 100644 --- a/kex.h +++ b/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.49 2010/02/26 20:29:54 djm Exp $ */ +/* $OpenBSD: kex.h,v 1.50 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -29,6 +29,7 @@ #include #include #include +#include #define KEX_COOKIE_LEN 16 @@ -37,6 +38,8 @@ #define KEX_DHGEX_SHA1 "diffie-hellman-group-exchange-sha1" #define KEX_DHGEX_SHA256 "diffie-hellman-group-exchange-sha256" #define KEX_RESUME "resume@appgate.com" +/* The following represents the family of ECDH methods */ +#define KEX_ECDH_SHA256 "ecdh-sha2-" #define COMP_NONE 0 #define COMP_ZLIB 1 @@ -67,6 +70,7 @@ enum kex_exchange { KEX_DH_GRP14_SHA1, KEX_DH_GEX_SHA1, KEX_DH_GEX_SHA256, + KEX_ECDH_SHA2, KEX_MAX }; @@ -145,6 +149,8 @@ void kexdh_client(Kex *); void kexdh_server(Kex *); void kexgex_client(Kex *); void kexgex_server(Kex *); +void kexecdh_client(Kex *); +void kexecdh_server(Kex *); void kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int, @@ -153,11 +159,17 @@ void kexgex_hash(const EVP_MD *, char *, char *, char *, int, char *, int, u_char *, int, int, int, int, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *); +void +kex_ecdh_hash(const EVP_MD *, const EC_GROUP *, char *, char *, char *, int, + char *, int, u_char *, int, const EC_POINT *, const EC_POINT *, + const BIGNUM *, u_char **, u_int *); + +int kex_ecdh_name_to_nid(const char *); void derive_ssh1_session_id(BIGNUM *, BIGNUM *, u_int8_t[8], u_int8_t[16]); -#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) +#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) void dump_digest(char *, u_char *, int); #endif diff --git a/kexecdh.c b/kexecdh.c new file mode 100644 index 000000000..a5a14f4bd --- /dev/null +++ b/kexecdh.c @@ -0,0 +1,108 @@ +/* $OpenBSD: kexecdh.c,v 1.1 2010/08/31 11:54:45 djm Exp $ */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2010 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 + +#include +#include + +#include +#include +#include +#include + +#include "buffer.h" +#include "ssh2.h" +#include "key.h" +#include "cipher.h" +#include "kex.h" +#include "log.h" + +int +kex_ecdh_name_to_nid(const char *kexname) +{ + int ret; + + if (strlen(kexname) < sizeof(KEX_ECDH_SHA256) - 1) + fatal("%s: kexname too short \"%s\"", __func__, kexname); + ret = key_curve_name_to_nid(kexname + sizeof(KEX_ECDH_SHA256) - 1); + if (ret == -1) + fatal("%s: unsupported curve negotiated \"%s\"", __func__, + kexname); + return ret; +} + +void +kex_ecdh_hash( + const EVP_MD *evp_md, + const EC_GROUP *ec_group, + char *client_version_string, + char *server_version_string, + char *ckexinit, int ckexinitlen, + char *skexinit, int skexinitlen, + u_char *serverhostkeyblob, int sbloblen, + const EC_POINT *client_dh_pub, + const EC_POINT *server_dh_pub, + const BIGNUM *shared_secret, + u_char **hash, u_int *hashlen) +{ + Buffer b; + EVP_MD_CTX md; + static u_char digest[EVP_MAX_MD_SIZE]; + + buffer_init(&b); + buffer_put_cstring(&b, client_version_string); + buffer_put_cstring(&b, server_version_string); + + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + buffer_put_int(&b, ckexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, ckexinit, ckexinitlen); + buffer_put_int(&b, skexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, skexinit, skexinitlen); + + buffer_put_string(&b, serverhostkeyblob, sbloblen); + buffer_put_ecpoint(&b, ec_group, client_dh_pub); + buffer_put_ecpoint(&b, ec_group, server_dh_pub); + buffer_put_bignum2(&b, shared_secret); + +#ifdef DEBUG_KEX + buffer_dump(&b); +#endif + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestFinal(&md, digest, NULL); + + buffer_free(&b); + +#ifdef DEBUG_KEX + dump_digest("hash", digest, EVP_MD_size(evp_md)); +#endif + *hash = digest; + *hashlen = EVP_MD_size(evp_md); +} + diff --git a/kexecdhc.c b/kexecdhc.c new file mode 100644 index 000000000..f6d9977c5 --- /dev/null +++ b/kexecdhc.c @@ -0,0 +1,156 @@ +/* $OpenBSD: kexecdhc.c,v 1.1 2010/08/31 11:54:45 djm Exp $ */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2010 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 + +#include +#include +#include + +#include + +#include "xmalloc.h" +#include "buffer.h" +#include "key.h" +#include "cipher.h" +#include "kex.h" +#include "log.h" +#include "packet.h" +#include "dh.h" +#include "ssh2.h" + +void +kexecdh_client(Kex *kex) +{ + EC_KEY *client_key; + EC_POINT *server_public; + const EC_GROUP *group; + BIGNUM *shared_secret; + Key *server_host_key; + u_char *server_host_key_blob = NULL, *signature = NULL; + u_char *kbuf, *hash; + u_int klen, slen, sbloblen, hashlen; + int curve_nid; + + curve_nid = kex_ecdh_name_to_nid(kex->name); + if ((client_key = EC_KEY_new_by_curve_name(curve_nid)) == NULL) + fatal("%s: EC_KEY_new_by_curve_name failed", __func__); + if (EC_KEY_generate_key(client_key) != 1) + fatal("%s: EC_KEY_generate_key failed", __func__); + group = EC_KEY_get0_group(client_key); + + packet_start(SSH2_MSG_KEX_ECDH_INIT); + packet_put_ecpoint(group, EC_KEY_get0_public_key(client_key)); + packet_send(); + debug("sending SSH2_MSG_KEX_ECDH_INIT"); + +#ifdef DEBUG_KEXECDH + fputs("client private key:\n", stderr); + key_dump_ec_key(client_key); +#endif + + debug("expecting SSH2_MSG_KEX_ECDH_REPLY"); + packet_read_expect(SSH2_MSG_KEX_ECDH_REPLY); + + /* hostkey */ + server_host_key_blob = packet_get_string(&sbloblen); + server_host_key = key_from_blob(server_host_key_blob, sbloblen); + if (server_host_key == NULL) + fatal("cannot decode server_host_key_blob"); + if (server_host_key->type != kex->hostkey_type) + fatal("type mismatch for decoded server_host_key_blob"); + if (kex->verify_host_key == NULL) + fatal("cannot verify server_host_key"); + if (kex->verify_host_key(server_host_key) == -1) + fatal("server_host_key verification failed"); + + /* Q_S, server public key */ + if ((server_public = EC_POINT_new(group)) == NULL) + fatal("%s: EC_POINT_new failed", __func__); + packet_get_ecpoint(group, server_public); + + if (key_ec_validate_public(group, server_public) != 0) + fatal("%s: invalid server public key", __func__); + +#ifdef DEBUG_KEXECDH + fputs("server public key:\n", stderr); + key_dump_ec_point(group, server_public); +#endif + + /* signed H */ + signature = packet_get_string(&slen); + packet_check_eom(); + + klen = (EC_GROUP_get_degree(group) + 7) / 8; + kbuf = xmalloc(klen); + if (ECDH_compute_key(kbuf, klen, server_public, + client_key, NULL) != (int)klen) + fatal("%s: ECDH_compute_key failed", __func__); + +#ifdef DEBUG_KEXECDH + dump_digest("shared secret", kbuf, klen); +#endif + if ((shared_secret = BN_new()) == NULL) + fatal("%s: BN_new failed", __func__); + if (BN_bin2bn(kbuf, klen, shared_secret) == NULL) + fatal("%s: BN_bin2bn failed", __func__); + memset(kbuf, 0, klen); + xfree(kbuf); + + /* calc and verify H */ + kex_ecdh_hash( + kex->evp_md, + group, + kex->client_version_string, + kex->server_version_string, + buffer_ptr(&kex->my), buffer_len(&kex->my), + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + server_host_key_blob, sbloblen, + EC_KEY_get0_public_key(client_key), + server_public, + shared_secret, + &hash, &hashlen + ); + xfree(server_host_key_blob); + EC_POINT_clear_free(server_public); + EC_KEY_free(client_key); + + if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) + fatal("key_verify failed for server_host_key"); + key_free(server_host_key); + xfree(signature); + + /* save session id */ + if (kex->session_id == NULL) { + kex->session_id_len = hashlen; + kex->session_id = xmalloc(kex->session_id_len); + memcpy(kex->session_id, hash, kex->session_id_len); + } + + kex_derive_keys(kex, hash, hashlen, shared_secret); + BN_clear_free(shared_secret); + kex_finish(kex); +} diff --git a/kexecdhs.c b/kexecdhs.c new file mode 100644 index 000000000..d73333893 --- /dev/null +++ b/kexecdhs.c @@ -0,0 +1,161 @@ +/* $OpenBSD: kexecdhs.c,v 1.1 2010/08/31 11:54:45 djm Exp $ */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2010 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 +#include +#include + +#include + +#include "xmalloc.h" +#include "buffer.h" +#include "key.h" +#include "cipher.h" +#include "kex.h" +#include "log.h" +#include "packet.h" +#include "dh.h" +#include "ssh2.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" + +void +kexecdh_server(Kex *kex) +{ + EC_POINT *client_public; + EC_KEY *server_key; + const EC_GROUP *group; + BIGNUM *shared_secret; + Key *server_host_private, *server_host_public; + u_char *server_host_key_blob = NULL, *signature = NULL; + u_char *kbuf, *hash; + u_int klen, slen, sbloblen, hashlen; + int curve_nid; + + curve_nid = kex_ecdh_name_to_nid(kex->name); + if ((server_key = EC_KEY_new_by_curve_name(curve_nid)) == NULL) + fatal("%s: EC_KEY_new_by_curve_name failed", __func__); + if (EC_KEY_generate_key(server_key) != 1) + fatal("%s: EC_KEY_generate_key failed", __func__); + group = EC_KEY_get0_group(server_key); + +#ifdef DEBUG_KEXECDH + fputs("server private key:\n", stderr); + key_dump_ec_key(server_key); +#endif + + if (kex->load_host_public_key == NULL || + kex->load_host_private_key == NULL) + fatal("Cannot load hostkey"); + server_host_public = kex->load_host_public_key(kex->hostkey_type); + if (server_host_public == NULL) + fatal("Unsupported hostkey type %d", kex->hostkey_type); + server_host_private = kex->load_host_private_key(kex->hostkey_type); + if (server_host_private == NULL) + fatal("Missing private key for hostkey type %d", + kex->hostkey_type); + + debug("expecting SSH2_MSG_KEX_ECDH_INIT"); + packet_read_expect(SSH2_MSG_KEX_ECDH_INIT); + if ((client_public = EC_POINT_new(group)) == NULL) + fatal("%s: EC_POINT_new failed", __func__); + packet_get_ecpoint(group, client_public); + packet_check_eom(); + + if (key_ec_validate_public(group, client_public) != 0) + fatal("%s: invalid client public key", __func__); + +#ifdef DEBUG_KEXECDH + fputs("client public key:\n", stderr); + key_dump_ec_point(group, client_public); +#endif + + /* Calculate shared_secret */ + klen = (EC_GROUP_get_degree(group) + 7) / 8; + kbuf = xmalloc(klen); + if (ECDH_compute_key(kbuf, klen, client_public, + server_key, NULL) != (int)klen) + fatal("%s: ECDH_compute_key failed", __func__); + +#ifdef DEBUG_KEXDH + dump_digest("shared secret", kbuf, klen); +#endif + if ((shared_secret = BN_new()) == NULL) + fatal("%s: BN_new failed", __func__); + if (BN_bin2bn(kbuf, klen, shared_secret) == NULL) + fatal("%s: BN_bin2bn failed", __func__); + memset(kbuf, 0, klen); + xfree(kbuf); + + /* calc H */ + key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); + kex_ecdh_hash( + kex->evp_md, + group, + kex->client_version_string, + kex->server_version_string, + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + buffer_ptr(&kex->my), buffer_len(&kex->my), + server_host_key_blob, sbloblen, + client_public, + EC_KEY_get0_public_key(server_key), + shared_secret, + &hash, &hashlen + ); + EC_POINT_clear_free(client_public); + + /* save session id := H */ + if (kex->session_id == NULL) { + kex->session_id_len = hashlen; + kex->session_id = xmalloc(kex->session_id_len); + memcpy(kex->session_id, hash, kex->session_id_len); + } + + /* sign H */ + if (PRIVSEP(key_sign(server_host_private, &signature, &slen, + hash, hashlen)) < 0) + fatal("kexdh_server: key_sign failed"); + + /* destroy_sensitive_data(); */ + + /* send server hostkey, ECDH pubkey 'Q_S' and signed H */ + packet_start(SSH2_MSG_KEX_ECDH_REPLY); + packet_put_string(server_host_key_blob, sbloblen); + packet_put_ecpoint(group, EC_KEY_get0_public_key(server_key)); + packet_put_string(signature, slen); + packet_send(); + + xfree(signature); + xfree(server_host_key_blob); + /* have keys, free server key */ + EC_KEY_free(server_key); + + kex_derive_keys(kex, hash, hashlen, shared_secret); + BN_clear_free(shared_secret); + kex_finish(kex); +} diff --git a/key.c b/key.c index aed4678cb..842280a9f 100644 --- a/key.c +++ b/key.c @@ -1,4 +1,4 @@ -/* $OpenBSD: key.c,v 1.91 2010/08/31 09:58:37 djm Exp $ */ +/* $OpenBSD: key.c,v 1.92 2010/08/31 11:54:45 djm Exp $ */ /* * read_bignum(): * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -78,6 +78,8 @@ key_new(int type) DSA *dsa; k = xcalloc(1, sizeof(*k)); k->type = type; + k->ecdsa = NULL; + k->ecdsa_nid = -1; k->dsa = NULL; k->rsa = NULL; k->cert = NULL; @@ -109,6 +111,10 @@ key_new(int type) fatal("key_new: BN_new failed"); k->dsa = dsa; break; + case KEY_ECDSA: + case KEY_ECDSA_CERT: + /* Cannot do anything until we know the group */ + break; case KEY_UNSPEC: break; default: @@ -149,6 +155,10 @@ key_add_private(Key *k) 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; case KEY_UNSPEC: break; default: @@ -204,6 +214,12 @@ key_free(Key *k) DSA_free(k->dsa); k->dsa = NULL; break; + case KEY_ECDSA: + case KEY_ECDSA_CERT: + if (k->ecdsa != NULL) + EC_KEY_free(k->ecdsa); + k->ecdsa = NULL; + break; case KEY_UNSPEC: break; default: @@ -241,6 +257,8 @@ cert_compare(struct KeyCert *a, struct KeyCert *b) int key_equal_public(const Key *a, const Key *b) { + BN_CTX *bnctx; + if (a == NULL || b == NULL || key_type_plain(a->type) != key_type_plain(b->type)) return 0; @@ -261,6 +279,24 @@ key_equal_public(const Key *a, const Key *b) 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; + 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; default: fatal("key_equal: bad key type %d", a->type); } @@ -312,12 +348,14 @@ key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length) BN_bn2bin(k->rsa->e, blob + nlen); break; case KEY_DSA: + case KEY_ECDSA: case KEY_RSA: key_to_blob(k, &blob, &len); break; case KEY_DSA_CERT_V00: case KEY_RSA_CERT_V00: case KEY_DSA_CERT: + case KEY_ECDSA_CERT: case KEY_RSA_CERT: /* We want a fingerprint of the _key_ not of the cert */ otype = k->type; @@ -612,7 +650,7 @@ key_read(Key *ret, char **cpp) Key *k; int success = -1; char *cp, *space; - int len, n, type; + int len, n, type, curve_nid = -1; u_int bits; u_char *blob; @@ -644,9 +682,11 @@ key_read(Key *ret, char **cpp) case KEY_UNSPEC: case KEY_RSA: case KEY_DSA: + case KEY_ECDSA: case KEY_DSA_CERT_V00: case KEY_RSA_CERT_V00: case KEY_DSA_CERT: + case KEY_ECDSA_CERT: case KEY_RSA_CERT: space = strchr(cp, ' '); if (space == NULL) { @@ -655,6 +695,11 @@ key_read(Key *ret, char **cpp) } *space = '\0'; type = key_type_from_name(cp); + if (key_type_plain(type) == KEY_ECDSA && + (curve_nid = key_ecdsa_nid_from_name(cp)) == -1) { + debug("key_read: invalid curve"); + return -1; + } *space = ' '; if (type == KEY_UNSPEC) { debug3("key_read: missing keytype"); @@ -691,6 +736,12 @@ key_read(Key *ret, char **cpp) key_free(k); return -1; } + 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; + } /*XXXX*/ if (key_is_cert(ret)) { if (!key_is_cert(k)) { @@ -719,6 +770,17 @@ key_read(Key *ret, char **cpp) k->dsa = NULL; #ifdef DEBUG_PK DSA_print_fp(stderr, ret->dsa, 8); +#endif + } + 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 } success = 1; @@ -777,6 +839,11 @@ key_write(const Key *key, FILE *f) if (key->dsa == NULL) return 0; break; + case KEY_ECDSA: + case KEY_ECDSA_CERT: + if (key->ecdsa == NULL) + return 0; + break; case KEY_RSA: case KEY_RSA_CERT_V00: case KEY_RSA_CERT: @@ -810,6 +877,8 @@ key_type(const Key *k) return "RSA"; case KEY_DSA: return "DSA"; + case KEY_ECDSA: + return "ECDSA"; case KEY_RSA_CERT_V00: return "RSA-CERT-V00"; case KEY_DSA_CERT_V00: @@ -818,6 +887,8 @@ key_type(const Key *k) return "RSA-CERT"; case KEY_DSA_CERT: return "DSA-CERT"; + case KEY_ECDSA_CERT: + return "ECDSA-CERT"; } return "unknown"; } @@ -835,10 +906,10 @@ key_cert_type(const Key *k) } } -const char * -key_ssh_name(const Key *k) +static const char * +key_ssh_name_from_type_nid(int type, int nid) { - switch (k->type) { + switch (type) { case KEY_RSA: return "ssh-rsa"; case KEY_DSA: @@ -851,10 +922,47 @@ key_ssh_name(const Key *k) return "ssh-rsa-cert-v01@openssh.com"; case KEY_DSA_CERT: return "ssh-dss-cert-v01@openssh.com"; + case KEY_ECDSA: + switch (nid) { + case NID_X9_62_prime256v1: + return "ecdsa-sha2-nistp256"; + case NID_secp384r1: + return "ecdsa-sha2-nistp384"; + case NID_secp521r1: + return "ecdsa-sha2-nistp521"; + default: + break; + } + break; + case KEY_ECDSA_CERT: + switch (nid) { + case NID_X9_62_prime256v1: + return "ecdsa-sha2-nistp256-cert-v01@openssh.com"; + case NID_secp384r1: + return "ecdsa-sha2-nistp384-cert-v01@openssh.com"; + case NID_secp521r1: + return "ecdsa-sha2-nistp521-cert-v01@openssh.com"; + default: + break; + } + break; } 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); +} + u_int key_size(const Key *k) { @@ -868,6 +976,19 @@ key_size(const Key *k) case KEY_DSA_CERT_V00: case KEY_DSA_CERT: return BN_num_bits(k->dsa->p); + case KEY_ECDSA: + case KEY_ECDSA_CERT: + switch (k->ecdsa_nid) { + case NID_X9_62_prime256v1: + return 256; + case NID_secp384r1: + return 384; + case NID_secp521r1: + return 521; + default: + break; + } + break; } return 0; } @@ -897,6 +1018,69 @@ dsa_generate_private_key(u_int bits) return private; } +int +key_ecdsa_bits_to_nid(int bits) +{ + switch (bits) { + case 256: + return NID_X9_62_prime256v1; + case 384: + return NID_secp384r1; + case 521: + return NID_secp521r1; + default: + return -1; + } +} + +/* + * This is horrid, but OpenSSL's PEM_read_PrivateKey seems not to restore + * the EC_GROUP nid when loading a key... + */ +int +key_ecdsa_group_to_nid(const EC_GROUP *g) +{ + EC_GROUP *eg; + int nids[] = { + NID_X9_62_prime256v1, + NID_secp384r1, + NID_secp521r1, + -1 + }; + u_int i; + BN_CTX *bnctx; + + 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) { + EC_GROUP_free(eg); + break; + } + EC_GROUP_free(eg); + } + BN_CTX_free(bnctx); + debug3("%s: nid = %d", __func__, nids[i]); + return nids[i]; +} + +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__); + return private; +} + Key * key_generate(int type, u_int bits) { @@ -905,6 +1089,9 @@ key_generate(int type, u_int bits) case KEY_DSA: k->dsa = dsa_generate_private_key(bits); break; + case KEY_ECDSA: + k->ecdsa = ecdsa_generate_private_key(bits, &k->ecdsa_nid); + break; case KEY_RSA: case KEY_RSA1: k->rsa = rsa_generate_private_key(bits); @@ -981,6 +1168,16 @@ key_from_private(const Key *k) (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL)) fatal("key_from_private: BN_copy failed"); break; + 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; case KEY_RSA: case KEY_RSA1: case KEY_RSA_CERT_V00: @@ -1012,6 +1209,11 @@ key_type_from_name(char *name) return KEY_RSA; } else if (strcmp(name, "ssh-dss") == 0) { return KEY_DSA; + } else if (strcmp(name, "ecdsa") == 0 || + strcmp(name, "ecdsa-sha2-nistp256") == 0 || + strcmp(name, "ecdsa-sha2-nistp384") == 0 || + strcmp(name, "ecdsa-sha2-nistp521") == 0) { + return KEY_ECDSA; } else if (strcmp(name, "ssh-rsa-cert-v00@openssh.com") == 0) { return KEY_RSA_CERT_V00; } else if (strcmp(name, "ssh-dss-cert-v00@openssh.com") == 0) { @@ -1020,11 +1222,32 @@ key_type_from_name(char *name) return KEY_RSA_CERT; } else if (strcmp(name, "ssh-dss-cert-v01@openssh.com") == 0) { return KEY_DSA_CERT; - } + } else if (strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0 || + strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0 || + strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) + return KEY_ECDSA_CERT; + debug2("key_type_from_name: unknown key type '%s'", name); return KEY_UNSPEC; } +int +key_ecdsa_nid_from_name(const char *name) +{ + if (strcmp(name, "ecdsa-sha2-nistp256") == 0 || + strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0) + return NID_X9_62_prime256v1; + if (strcmp(name, "ecdsa-sha2-nistp384") == 0 || + strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0) + return NID_secp384r1; + if (strcmp(name, "ecdsa-sha2-nistp521") == 0 || + strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) + return NID_secp521r1; + + debug2("%s: unknown/non-ECDSA key type '%s'", __func__, name); + return -1; +} + int key_names_valid2(const char *names) { @@ -1146,7 +1369,8 @@ cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen) goto out; } if (key->cert->signature_key->type != KEY_RSA && - key->cert->signature_key->type != KEY_DSA) { + key->cert->signature_key->type != KEY_DSA && + key->cert->signature_key->type != KEY_ECDSA) { error("%s: Invalid signature key type %s (%d)", __func__, key_type(key->cert->signature_key), key->cert->signature_key->type); @@ -1186,9 +1410,10 @@ Key * key_from_blob(const u_char *blob, u_int blen) { Buffer b; - int rlen, type; - char *ktype = NULL; + int rlen, type, nid = -1; + char *ktype = NULL, *curve = NULL; Key *key = NULL; + EC_POINT *q = NULL; #ifdef DEBUG_PK dump_base64(stderr, blob, blen); @@ -1201,6 +1426,8 @@ key_from_blob(const u_char *blob, u_int blen) } type = key_type_from_name(ktype); + if (key_type_plain(type) == KEY_ECDSA) + nid = key_ecdsa_nid_from_name(ktype); switch (type) { case KEY_RSA_CERT: @@ -1236,6 +1463,41 @@ key_from_blob(const u_char *blob, u_int blen) } #ifdef DEBUG_PK DSA_print_fp(stderr, key->dsa, 8); +#endif + break; + 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; case KEY_UNSPEC: @@ -1255,6 +1517,10 @@ key_from_blob(const u_char *blob, u_int blen) out: if (ktype != NULL) xfree(ktype); + if (curve != NULL) + xfree(curve); + if (q != NULL) + EC_POINT_free(q); buffer_free(&b); return key; } @@ -1274,6 +1540,7 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp) case KEY_DSA_CERT_V00: case KEY_RSA_CERT_V00: case KEY_DSA_CERT: + case KEY_ECDSA_CERT: case KEY_RSA_CERT: /* Use the existing blob */ buffer_append(&b, buffer_ptr(&key->cert->certblob), @@ -1286,6 +1553,12 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp) buffer_put_bignum2(&b, key->dsa->g); buffer_put_bignum2(&b, key->dsa->pub_key); break; + case KEY_ECDSA: + buffer_put_cstring(&b, key_ssh_name(key)); + 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; case KEY_RSA: buffer_put_cstring(&b, key_ssh_name(key)); buffer_put_bignum2(&b, key->rsa->e); @@ -1319,6 +1592,9 @@ key_sign( case KEY_DSA_CERT: case KEY_DSA: return ssh_dss_sign(key, sigp, lenp, data, datalen); + case KEY_ECDSA_CERT: + case KEY_ECDSA: + return ssh_ecdsa_sign(key, sigp, lenp, data, datalen); case KEY_RSA_CERT_V00: case KEY_RSA_CERT: case KEY_RSA: @@ -1347,6 +1623,9 @@ key_verify( case KEY_DSA_CERT: case KEY_DSA: return ssh_dss_verify(key, signature, signaturelen, data, datalen); + case KEY_ECDSA_CERT: + case KEY_ECDSA: + return ssh_ecdsa_verify(key, signature, signaturelen, data, datalen); case KEY_RSA_CERT_V00: case KEY_RSA_CERT: case KEY_RSA: @@ -1366,7 +1645,9 @@ key_demote(const Key *k) 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; switch (k->type) { @@ -1399,6 +1680,16 @@ key_demote(const Key *k) if ((pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) fatal("key_demote: BN_dup failed"); break; + 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; default: fatal("key_free: bad key type %d", k->type); break; @@ -1417,6 +1708,7 @@ key_is_cert(const Key *k) case KEY_DSA_CERT_V00: case KEY_RSA_CERT: case KEY_DSA_CERT: + case KEY_ECDSA_CERT: return 1; default: return 0; @@ -1434,6 +1726,8 @@ key_type_plain(int type) case KEY_DSA_CERT_V00: case KEY_DSA_CERT: return KEY_DSA; + case KEY_ECDSA_CERT: + return KEY_ECDSA; default: return type; } @@ -1452,6 +1746,10 @@ key_to_certified(Key *k, int legacy) k->cert = cert_new(); k->type = legacy ? KEY_DSA_CERT_V00 : KEY_DSA_CERT; return 0; + case KEY_ECDSA: + k->cert = cert_new(); + k->type = KEY_ECDSA_CERT; + return 0; default: error("%s: key has incorrect type %s", __func__, key_type(k)); return -1; @@ -1473,13 +1771,20 @@ key_drop_cert(Key *k) cert_free(k->cert); k->type = KEY_DSA; return 0; + case KEY_ECDSA_CERT: + cert_free(k->cert); + k->type = KEY_ECDSA; + return 0; default: error("%s: key has incorrect type %s", __func__, key_type(k)); return -1; } } -/* Sign a KEY_RSA_CERT or KEY_DSA_CERT, (re-)generating the signed certblob */ +/* + * Sign a KEY_RSA_CERT, KEY_DSA_CERT or KEY_ECDSA_CERT, (re-)generating + * the signed certblob + */ int key_certify(Key *k, Key *ca) { @@ -1498,7 +1803,8 @@ key_certify(Key *k, Key *ca) return -1; } - if (ca->type != KEY_RSA && ca->type != KEY_DSA) { + if (ca->type != KEY_RSA && ca->type != KEY_DSA && + ca->type != KEY_ECDSA) { error("%s: CA key has unsupported type %s", __func__, key_type(ca)); return -1; @@ -1510,7 +1816,7 @@ key_certify(Key *k, Key *ca) buffer_put_cstring(&k->cert->certblob, key_ssh_name(k)); /* -v01 certs put nonce first */ - if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) { + if (!key_cert_is_legacy(k)) { arc4random_buf(&nonce, sizeof(nonce)); buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce)); } @@ -1523,6 +1829,13 @@ key_certify(Key *k, Key *ca) buffer_put_bignum2(&k->cert->certblob, k->dsa->g); buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key); break; + 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; case KEY_RSA_CERT_V00: case KEY_RSA_CERT: buffer_put_bignum2(&k->cert->certblob, k->rsa->e); @@ -1536,7 +1849,7 @@ key_certify(Key *k, Key *ca) } /* -v01 certs have a serial number next */ - if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) + if (!key_cert_is_legacy(k)) buffer_put_int64(&k->cert->certblob, k->cert->serial); buffer_put_int(&k->cert->certblob, k->cert->type); @@ -1555,14 +1868,14 @@ key_certify(Key *k, Key *ca) buffer_ptr(&k->cert->critical), buffer_len(&k->cert->critical)); /* -v01 certs have non-critical options here */ - if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) { + 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 (k->type == KEY_DSA_CERT_V00 || k->type == KEY_RSA_CERT_V00) + if (key_cert_is_legacy(k)) buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce)); buffer_put_string(&k->cert->certblob, NULL, 0); /* reserved */ @@ -1647,3 +1960,201 @@ key_cert_is_legacy(Key *k) return 0; } } + +int +key_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; + else if (strcmp(name, "nistp521") == 0) + return NID_secp521r1; + + debug("%s: unsupported EC curve name \"%.100s\"", __func__, name); + return -1; +} + +const char * +key_curve_nid_to_name(int nid) +{ + if (nid == NID_X9_62_prime256v1) + return "nistp256"; + else if (nid == NID_secp384r1) + return "nistp384"; + else if (nid == NID_secp521r1) + return "nistp521"; + + error("%s: unsupported EC curve nid %d", __func__, nid); + return NULL; +} + +int +key_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 = -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; + } + + 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; + } + + /* 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; + } + + /* 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; + } + ret = 0; + out: + BN_CTX_free(bnctx); + EC_POINT_free(nq); + return ret; +} + +int +key_ec_validate_private(const EC_KEY *key) +{ + 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; + } + + /* 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; + } + ret = 0; + out: + BN_CTX_free(bnctx); + return ret; +} + +#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; + } + 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); +} + +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); +} +#endif /* defined(DEBUG_KEXECDH) || defined(DEBUG_PK) */ + diff --git a/key.h b/key.h index 11d30eae6..2eb124364 100644 --- a/key.h +++ b/key.h @@ -1,4 +1,4 @@ -/* $OpenBSD: key.h,v 1.30 2010/04/16 01:47:26 djm Exp $ */ +/* $OpenBSD: key.h,v 1.31 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -29,14 +29,17 @@ #include "buffer.h" #include #include +#include typedef struct Key Key; enum types { KEY_RSA1, KEY_RSA, KEY_DSA, + KEY_ECDSA, KEY_RSA_CERT, KEY_DSA_CERT, + KEY_ECDSA_CERT, KEY_RSA_CERT_V00, KEY_DSA_CERT_V00, KEY_UNSPEC @@ -73,6 +76,8 @@ struct Key { int flags; RSA *rsa; DSA *dsa; + int ecdsa_nid; /* NID of curve */ + EC_KEY *ecdsa; struct KeyCert *cert; }; @@ -104,9 +109,18 @@ int key_cert_check_authority(const Key *, int, int, const char *, const char **); int key_cert_is_legacy(Key *); +int key_ecdsa_nid_from_name(const char *); +int key_curve_name_to_nid(const char *); +const char * key_curve_nid_to_name(int); +int key_ecdsa_bits_to_nid(int); +int key_ecdsa_group_to_nid(const EC_GROUP *); +int key_ec_validate_public(const EC_GROUP *, const EC_POINT *); +int key_ec_validate_private(const EC_KEY *); + 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 *); int key_sign(const Key *, u_char **, u_int *, const u_char *, u_int); @@ -114,7 +128,14 @@ 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); +#if 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 + #endif diff --git a/monitor.c b/monitor.c index 9eb4e35c9..32395ee44 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.108 2010/07/13 23:13:16 djm Exp $ */ +/* $OpenBSD: monitor.c,v 1.109 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -1691,6 +1691,7 @@ mm_get_kex(Buffer *m) kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; + kex->kex[KEX_ECDH_SHA2] = kexecdh_server; kex->server = 1; kex->hostkey_type = buffer_get_int(m); kex->kex_type = buffer_get_int(m); diff --git a/monitor_wrap.c b/monitor_wrap.c index faeb02cfa..1a5dda561 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.c,v 1.69 2010/03/07 11:57:13 dtucker Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.70 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -73,6 +73,7 @@ #include "misc.h" #include "schnorr.h" #include "jpake.h" +#include "uuencode.h" #include "channels.h" #include "session.h" diff --git a/myproposal.h b/myproposal.h index 7bedfab0a..71f90ee5e 100644 --- a/myproposal.h +++ b/myproposal.h @@ -1,4 +1,4 @@ -/* $OpenBSD: myproposal.h,v 1.25 2010/04/16 01:47:26 djm Exp $ */ +/* $OpenBSD: myproposal.h,v 1.26 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -32,20 +32,38 @@ "diffie-hellman-group-exchange-sha1," \ "diffie-hellman-group14-sha1," \ "diffie-hellman-group1-sha1" + +#define KEX_DEFAULT_PK_ALG \ + "ssh-rsa-cert-v01@openssh.com," \ + "ssh-dss-cert-v01@openssh.com," \ + "ssh-rsa-cert-v00@openssh.com," \ + "ssh-dss-cert-v00@openssh.com," \ + "ssh-rsa," \ + "ssh-dss" #else # define KEX_DEFAULT_KEX \ + "ecdh-sha2-nistp521," \ + "ecdh-sha2-nistp256," \ + "ecdh-sha2-nistp384," \ "diffie-hellman-group-exchange-sha256," \ "diffie-hellman-group-exchange-sha1," \ "diffie-hellman-group14-sha1," \ "diffie-hellman-group1-sha1" -#endif #define KEX_DEFAULT_PK_ALG \ - "ssh-rsa-cert-v01@openssh.com," \ - "ssh-dss-cert-v01@openssh.com," \ - "ssh-rsa-cert-v00@openssh.com," \ - "ssh-dss-cert-v00@openssh.com," \ - "ssh-rsa,ssh-dss" + "ecdsa-sha2-nistp256-cert-v01@openssh.com," \ + "ecdsa-sha2-nistp384-cert-v01@openssh.com," \ + "ecdsa-sha2-nistp521-cert-v01@openssh.com," \ + "ssh-rsa-cert-v01@openssh.com," \ + "ssh-dss-cert-v01@openssh.com," \ + "ssh-rsa-cert-v00@openssh.com," \ + "ssh-dss-cert-v00@openssh.com," \ + "ecdsa-sha2-nistp256," \ + "ecdsa-sha2-nistp384," \ + "ecdsa-sha2-nistp521," \ + "ssh-rsa," \ + "ssh-dss" +#endif #define KEX_DEFAULT_ENCRYPT \ "aes128-ctr,aes192-ctr,aes256-ctr," \ diff --git a/packet.c b/packet.c index 49aa97335..a06c5e3ef 100644 --- a/packet.c +++ b/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.169 2010/08/31 09:58:37 djm Exp $ */ +/* $OpenBSD: packet.c,v 1.170 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -641,6 +641,12 @@ packet_put_bignum2(BIGNUM * value) buffer_put_bignum2(&active_state->outgoing_packet, value); } +void +packet_put_ecpoint(const EC_GROUP *curve, const EC_POINT *point) +{ + buffer_put_ecpoint(&active_state->outgoing_packet, curve, point); +} + /* * Finalizes and sends the packet. If the encryption key has been set, * encrypts the packet before sending. @@ -1511,6 +1517,12 @@ packet_get_bignum2(BIGNUM * value) buffer_get_bignum2(&active_state->incoming_packet, value); } +void +packet_get_ecpoint(const EC_GROUP *curve, EC_POINT *point) +{ + buffer_get_ecpoint(&active_state->incoming_packet, curve, point); +} + void * packet_get_raw(u_int *length_ptr) { diff --git a/packet.h b/packet.h index fd0b056fd..827561cdb 100644 --- a/packet.h +++ b/packet.h @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.h,v 1.53 2010/08/31 09:58:37 djm Exp $ */ +/* $OpenBSD: packet.h,v 1.54 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen @@ -19,6 +19,7 @@ #include #include +#include void packet_set_connection(int, int); void packet_set_timeout(int, int); @@ -42,6 +43,7 @@ void packet_put_int(u_int value); void packet_put_int64(u_int64_t value); void packet_put_bignum(BIGNUM * value); void packet_put_bignum2(BIGNUM * value); +void packet_put_ecpoint(const EC_GROUP *, const EC_POINT *); void packet_put_string(const void *buf, u_int len); void packet_put_cstring(const char *str); void packet_put_raw(const void *buf, u_int len); @@ -59,6 +61,7 @@ u_int packet_get_int(void); u_int64_t packet_get_int64(void); void packet_get_bignum(BIGNUM * value); void packet_get_bignum2(BIGNUM * value); +void packet_get_ecpoint(const EC_GROUP *, EC_POINT *); void *packet_get_raw(u_int *length_ptr); void *packet_get_string(u_int *length_ptr); char *packet_get_cstring(u_int *length_ptr); diff --git a/pathnames.h b/pathnames.h index 9e50950fe..e2dd49a9b 100644 --- a/pathnames.h +++ b/pathnames.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pathnames.h,v 1.19 2010/02/11 20:37:47 djm Exp $ */ +/* $OpenBSD: pathnames.h,v 1.20 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen @@ -38,6 +38,7 @@ #define _PATH_HOST_CONFIG_FILE SSHDIR "/ssh_config" #define _PATH_HOST_KEY_FILE SSHDIR "/ssh_host_key" #define _PATH_HOST_DSA_KEY_FILE SSHDIR "/ssh_host_dsa_key" +#define _PATH_HOST_ECDSA_KEY_FILE SSHDIR "/ssh_host_ecdsa_key" #define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key" #define _PATH_DH_MODULI SSHDIR "/moduli" /* Backwards compatibility */ @@ -74,6 +75,7 @@ */ #define _PATH_SSH_CLIENT_IDENTITY ".ssh/identity" #define _PATH_SSH_CLIENT_ID_DSA ".ssh/id_dsa" +#define _PATH_SSH_CLIENT_ID_ECDSA ".ssh/id_ecdsa" #define _PATH_SSH_CLIENT_ID_RSA ".ssh/id_rsa" /* diff --git a/readconf.c b/readconf.c index 0296590e2..98ce3017f 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.187 2010/07/19 09:15:12 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.188 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1214,6 +1214,12 @@ fill_default_options(Options * options) xmalloc(len); snprintf(options->identity_files[options->num_identity_files++], len, "~/%.100s", _PATH_SSH_CLIENT_ID_DSA); + + len = 2 + strlen(_PATH_SSH_CLIENT_ID_ECDSA) + 1; + options->identity_files[options->num_identity_files] = + xmalloc(len); + snprintf(options->identity_files[options->num_identity_files++], + len, "~/%.100s", _PATH_SSH_CLIENT_ID_ECDSA); } } if (options->escape_char == -1) diff --git a/ssh-add.1 b/ssh-add.1 index d7cc53101..3699db5eb 100644 --- a/ssh-add.1 +++ b/ssh-add.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-add.1,v 1.52 2010/03/05 10:28:21 djm Exp $ +.\" $OpenBSD: ssh-add.1,v 1.53 2010/08/31 11:54:45 djm Exp $ .\" .\" -*- nroff -*- .\" @@ -37,12 +37,12 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: March 5 2010 $ +.Dd $Mdocdate: August 31 2010 $ .Dt SSH-ADD 1 .Os .Sh NAME .Nm ssh-add -.Nd adds RSA or DSA identities to the authentication agent +.Nd adds private key identities to the authentication agent .Sh SYNOPSIS .Nm ssh-add .Op Fl cDdLlXx @@ -54,11 +54,12 @@ .Fl e Ar pkcs11 .Sh DESCRIPTION .Nm -adds RSA or DSA identities to the authentication agent, +adds private key identities to the authentication agent, .Xr ssh-agent 1 . When run without arguments, it adds the files .Pa ~/.ssh/id_rsa , -.Pa ~/.ssh/id_dsa +.Pa ~/.ssh/id_dsa , +.Pa ~/.ssh/id_ecdsa and .Pa ~/.ssh/identity . After loading a private key, @@ -165,6 +166,8 @@ socket used to communicate with the agent. Contains the protocol version 1 RSA authentication identity of the user. .It Pa ~/.ssh/id_dsa Contains the protocol version 2 DSA authentication identity of the user. +.It Pa ~/.ssh/id_ecdsa +Contains the protocol version 2 ECDSA authentication identity of the user. .It Pa ~/.ssh/id_rsa Contains the protocol version 2 RSA authentication identity of the user. .El diff --git a/ssh-add.c b/ssh-add.c index 7f8fb2c6d..31e618390 100644 --- a/ssh-add.c +++ b/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.98 2010/08/16 04:06:06 djm Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.99 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -70,6 +70,7 @@ extern char *__progname; static char *default_files[] = { _PATH_SSH_CLIENT_ID_RSA, _PATH_SSH_CLIENT_ID_DSA, + _PATH_SSH_CLIENT_ID_ECDSA, _PATH_SSH_CLIENT_IDENTITY, NULL }; diff --git a/ssh-agent.1 b/ssh-agent.1 index f65e8e625..88ad490bc 100644 --- a/ssh-agent.1 +++ b/ssh-agent.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-agent.1,v 1.50 2010/01/17 21:49:09 tedu Exp $ +.\" $OpenBSD: ssh-agent.1,v 1.51 2010/08/31 11:54:45 djm Exp $ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -34,7 +34,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: January 17 2010 $ +.Dd $Mdocdate: August 31 2010 $ .Dt SSH-AGENT 1 .Os .Sh NAME @@ -53,7 +53,7 @@ .Sh DESCRIPTION .Nm is a program to hold private keys used for public key authentication -(RSA, DSA). +(RSA, DSA, ECDSA). The idea is that .Nm is started in the beginning of an X-session or a login session, and @@ -114,7 +114,8 @@ When executed without arguments, .Xr ssh-add 1 adds the files .Pa ~/.ssh/id_rsa , -.Pa ~/.ssh/id_dsa +.Pa ~/.ssh/id_dsa , +.Pa ~/.ssh/id_ecdsa and .Pa ~/.ssh/identity . If the identity has a passphrase, @@ -187,6 +188,8 @@ line terminates. Contains the protocol version 1 RSA authentication identity of the user. .It Pa ~/.ssh/id_dsa Contains the protocol version 2 DSA authentication identity of the user. +.It Pa ~/.ssh/id_ecdsa +Contains the protocol version 2 ECDSA authentication identity of the user. .It Pa ~/.ssh/id_rsa Contains the protocol version 2 RSA authentication identity of the user. .It Pa /tmp/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt diff --git a/ssh-agent.c b/ssh-agent.c index e6725ea88..fbfd79c13 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.168 2010/08/16 04:06:06 djm Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.169 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -466,8 +466,10 @@ process_add_identity(SocketEntry *e, int version) Idtab *tab = idtab_lookup(version); Identity *id; int type, success = 0, death = 0, confirm = 0; - char *type_name, *comment; + char *type_name, *comment, *curve; Key *k = NULL; + BIGNUM *exponent; + EC_POINT *q; u_char *cert; u_int len; @@ -490,7 +492,6 @@ process_add_identity(SocketEntry *e, int version) case 2: type_name = buffer_get_string(&e->request, NULL); type = key_type_from_name(type_name); - xfree(type_name); switch (type) { case KEY_DSA: k = key_new_private(type); @@ -509,6 +510,57 @@ process_add_identity(SocketEntry *e, int version) key_add_private(k); buffer_get_bignum2(&e->request, k->dsa->priv_key); break; + case KEY_ECDSA: + k = key_new_private(type); + k->ecdsa_nid = key_ecdsa_nid_from_name(type_name); + curve = buffer_get_string(&e->request, NULL); + if (k->ecdsa_nid != key_curve_name_to_nid(curve)) + fatal("%s: curve names mismatch", __func__); + xfree(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(&e->request, + EC_KEY_get0_group(k->ecdsa), q); + buffer_get_bignum2(&e->request, 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(&e->request, &len); + if ((k = key_from_blob(cert, len)) == NULL) + fatal("Certificate parse failed"); + xfree(cert); + key_add_private(k); + if ((exponent = BN_new()) == NULL) + fatal("%s: BN_new failed", __func__); + buffer_get_bignum2(&e->request, 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; case KEY_RSA: k = key_new_private(type); buffer_get_bignum2(&e->request, k->rsa->n); @@ -534,9 +586,11 @@ process_add_identity(SocketEntry *e, int version) buffer_get_bignum2(&e->request, k->rsa->q); break; default: + xfree(type_name); buffer_clear(&e->request); goto send; } + xfree(type_name); break; } /* enable blinding */ diff --git a/ssh-ecdsa.c b/ssh-ecdsa.c new file mode 100644 index 000000000..a1c1bdb60 --- /dev/null +++ b/ssh-ecdsa.c @@ -0,0 +1,160 @@ +/* $OpenBSD */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * Copyright (c) 2010 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 + +#include +#include +#include +#include + +#include + +#include "xmalloc.h" +#include "buffer.h" +#include "compat.h" +#include "log.h" +#include "key.h" + +int +ssh_ecdsa_sign(const Key *key, u_char **sigp, u_int *lenp, + const u_char *data, u_int datalen) +{ + ECDSA_SIG *sig; + const EVP_MD *evp_md = EVP_sha256(); + EVP_MD_CTX md; + u_char digest[EVP_MAX_MD_SIZE]; + u_int len, dlen; + Buffer b, bb; + + if (key == NULL || key->ecdsa == NULL || + (key->type != KEY_ECDSA && key->type != KEY_ECDSA_CERT)) { + error("%s: no ECDSA key", __func__); + return -1; + } + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, data, datalen); + EVP_DigestFinal(&md, digest, &dlen); + + sig = ECDSA_do_sign(digest, dlen, key->ecdsa); + memset(digest, 'd', sizeof(digest)); + + if (sig == NULL) { + error("%s: sign failed", __func__); + return -1; + } + + 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; +} +int +ssh_ecdsa_verify(const Key *key, const u_char *signature, u_int signaturelen, + const u_char *data, u_int datalen) +{ + ECDSA_SIG *sig; + const EVP_MD *evp_md = EVP_sha256(); + EVP_MD_CTX md; + u_char digest[EVP_MAX_MD_SIZE], *sigblob; + u_int len, dlen; + int rlen, ret; + Buffer b, bb; + + if (key == NULL || key->ecdsa == NULL || + (key->type != KEY_ECDSA && key->type != KEY_ECDSA_CERT)) { + error("%s: no ECDSA key", __func__); + return -1; + } + + /* fetch signature */ + char *ktype; + 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); + xfree(ktype); + return -1; + } + xfree(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); + xfree(sigblob); + return -1; + } + + /* parse signature */ + if ((sig = ECDSA_SIG_new()) == NULL) + fatal("%s: ECDSA_SIG_new failed", __func__); + if ((sig->r = BN_new()) == NULL || + (sig->s = BN_new()) == NULL) + fatal("%s: BN_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__); + + /* clean up */ + memset(sigblob, 0, len); + xfree(sigblob); + + /* hash the data */ + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, data, datalen); + EVP_DigestFinal(&md, digest, &dlen); + + ret = ECDSA_do_verify(digest, dlen, sig, key->ecdsa); + memset(digest, 'd', sizeof(digest)); + + ECDSA_SIG_free(sig); + + debug("%s: signature %s", __func__, + ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error"); + return ret; +} diff --git a/ssh-keygen.1 b/ssh-keygen.1 index 9acd8f8c9..4b95a4e1c 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-keygen.1,v 1.98 2010/08/04 06:07:11 djm Exp $ +.\" $OpenBSD: ssh-keygen.1,v 1.99 2010/08/31 11:54:45 djm Exp $ .\" .\" -*- nroff -*- .\" @@ -37,7 +37,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: August 4 2010 $ +.Dd $Mdocdate: August 31 2010 $ .Dt SSH-KEYGEN 1 .Os .Sh NAME @@ -125,7 +125,7 @@ generates, manages and converts authentication keys for .Xr ssh 1 . .Nm -can create RSA keys for use by SSH protocol version 1 and RSA or DSA +can create RSA keys for use by SSH protocol version 1 and RSA, DSA or ECDSA keys for use by SSH protocol version 2. The type of key to be generated is specified with the .Fl t @@ -142,9 +142,10 @@ See the section for details. .Pp Normally each user wishing to use SSH -with RSA or DSA authentication runs this once to create the authentication +with public key authentication runs this once to create the authentication key in .Pa ~/.ssh/identity , +.Pa ~/.ssh/id_ecdsa , .Pa ~/.ssh/id_dsa or .Pa ~/.ssh/id_rsa . diff --git a/ssh-keygen.c b/ssh-keygen.c index 93f598004..448585185 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.199 2010/08/16 04:06:06 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.200 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -57,6 +57,7 @@ /* Number of bits in the RSA/DSA key. This value can be set on the command line. */ #define DEFAULT_BITS 2048 #define DEFAULT_BITS_DSA 1024 +#define DEFAULT_BITS_ECDSA 521 u_int32_t bits = 0; /* @@ -176,6 +177,10 @@ ask_filename(struct passwd *pw, const char *prompt) case KEY_DSA: name = _PATH_SSH_CLIENT_ID_DSA; break; + case KEY_ECDSA_CERT: + case KEY_ECDSA: + name = _PATH_SSH_CLIENT_ID_ECDSA; + break; case KEY_RSA_CERT: case KEY_RSA_CERT_V00: case KEY_RSA: @@ -260,6 +265,10 @@ do_convert_to_pkcs8(Key *k) if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) fatal("PEM_write_DSA_PUBKEY failed"); break; + case KEY_ECDSA: + if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa)) + fatal("PEM_write_EC_PUBKEY failed"); + break; default: fatal("%s: unsupported key type %s", __func__, key_type(k)); } @@ -280,6 +289,7 @@ do_convert_to_pem(Key *k) fatal("PEM_write_DSAPublicKey failed"); break; #endif + /* XXX ECDSA? */ default: fatal("%s: unsupported key type %s", __func__, key_type(k)); } @@ -539,6 +549,13 @@ do_convert_from_pkcs8(Key **k, int *private) (*k)->type = KEY_DSA; (*k)->dsa = EVP_PKEY_get1_DSA(pubkey); break; + case EVP_PKEY_EC: + *k = key_new(KEY_UNSPEC); + (*k)->type = KEY_ECDSA; + (*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey); + (*k)->ecdsa_nid = key_ecdsa_group_to_nid( + EC_KEY_get0_group((*k)->ecdsa)); + break; default: fatal("%s: unsupported pubkey type %d", __func__, EVP_PKEY_type(pubkey->type)); @@ -574,6 +591,7 @@ do_convert_from_pem(Key **k, int *private) fclose(fp); return; } + /* XXX ECDSA */ #endif fatal("%s: unrecognised raw private key format", __func__); } @@ -614,6 +632,10 @@ do_convert_from(struct passwd *pw) ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL); break; + case KEY_ECDSA: + ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL, + NULL, 0, NULL, NULL); + break; case KEY_RSA: ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL); @@ -1404,7 +1426,8 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) tmp = tilde_expand_filename(argv[i], pw->pw_uid); if ((public = key_load_public(tmp, &comment)) == NULL) fatal("%s: unable to open \"%s\"", __func__, tmp); - if (public->type != KEY_RSA && public->type != KEY_DSA) + if (public->type != KEY_RSA && public->type != KEY_DSA && + public->type != KEY_ECDSA) fatal("%s: key \"%s\" type %s cannot be certified", __func__, tmp, key_type(public)); @@ -2086,8 +2109,14 @@ main(int argc, char **argv) fprintf(stderr, "unknown key type %s\n", key_type_name); exit(1); } - if (bits == 0) - bits = (type == KEY_DSA) ? DEFAULT_BITS_DSA : DEFAULT_BITS; + if (bits == 0) { + if (type == KEY_DSA) + bits = DEFAULT_BITS_DSA; + else if (type == KEY_ECDSA) + bits = DEFAULT_BITS_ECDSA; + else + bits = DEFAULT_BITS; + } maxbits = (type == KEY_DSA) ? OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS; if (bits > maxbits) { @@ -2096,6 +2125,9 @@ main(int argc, char **argv) } if (type == KEY_DSA && bits != 1024) fatal("DSA keys must be 1024 bits"); + else if (type == KEY_ECDSA && key_ecdsa_bits_to_nid(bits) == -1) + fatal("Invalid ECDSA key length - valid lengths are " + "256, 384 or 521 bits"); if (!quiet) printf("Generating public/private %s key pair.\n", key_type_name); private = key_generate(type, bits); diff --git a/ssh-keyscan.1 b/ssh-keyscan.1 index 78255ff79..fe9bb6e07 100644 --- a/ssh-keyscan.1 +++ b/ssh-keyscan.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-keyscan.1,v 1.28 2010/01/09 23:04:13 dtucker Exp $ +.\" $OpenBSD: ssh-keyscan.1,v 1.29 2010/08/31 11:54:45 djm Exp $ .\" .\" Copyright 1995, 1996 by David Mazieres . .\" @@ -6,7 +6,7 @@ .\" permitted provided that due credit is given to the author and the .\" OpenBSD project by leaving this copyright notice intact. .\" -.Dd $Mdocdate: January 9 2010 $ +.Dd $Mdocdate: August 31 2010 $ .Dt SSH-KEYSCAN 1 .Os .Sh NAME @@ -88,9 +88,10 @@ Specifies the type of the key to fetch from the scanned hosts. The possible values are .Dq rsa1 for protocol version 1 and -.Dq rsa +.Dq dsa , +.Dq ecdsa or -.Dq dsa +.Dq rsa for protocol version 2. Multiple values may be specified by separating them with commas. The default is @@ -122,7 +123,7 @@ attacks which have begun after the ssh_known_hosts file was created. host-or-namelist bits exponent modulus .Ed .Pp -.Pa Output format for rsa and dsa keys: +.Pa Output format for rsa, dsa and ecdsa keys: .Bd -literal host-or-namelist keytype base64-encoded-key .Ed @@ -130,9 +131,12 @@ host-or-namelist keytype base64-encoded-key Where .Pa keytype is either -.Dq ssh-rsa +.Dq ecdsa-sha2-nistp256 , +.Dq ecdsa-sha2-nistp384 , +.Dq ecdsa-sha2-nistp521 , +.Dq ssh-dss or -.Dq ssh-dss . +.Dq ssh-rsa . .Pp .Pa /etc/ssh/ssh_known_hosts .Sh EXAMPLES @@ -149,7 +153,7 @@ Find all hosts from the file which have new or different keys from those in the sorted file .Pa ssh_known_hosts : .Bd -literal -$ ssh-keyscan -t rsa,dsa -f ssh_hosts | \e +$ ssh-keyscan -t rsa,dsa,ecdsa -f ssh_hosts | \e sort -u - ssh_known_hosts | diff ssh_known_hosts - .Ed .Sh SEE ALSO diff --git a/ssh-keyscan.c b/ssh-keyscan.c index b6cf427cd..3fb1214e2 100644 --- a/ssh-keyscan.c +++ b/ssh-keyscan.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keyscan.c,v 1.82 2010/06/22 04:54:30 djm Exp $ */ +/* $OpenBSD: ssh-keyscan.c,v 1.83 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright 1995, 1996 by David Mazieres . * @@ -52,9 +52,10 @@ int IPv4or6 = AF_UNSPEC; int ssh_port = SSH_DEFAULT_PORT; -#define KT_RSA1 1 -#define KT_DSA 2 -#define KT_RSA 4 +#define KT_RSA1 1 +#define KT_DSA 2 +#define KT_RSA 4 +#define KT_ECDSA 8 int get_keytypes = KT_RSA; /* Get only RSA keys by default */ @@ -251,6 +252,7 @@ keygrab_ssh2(con *c) c->c_kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; c->c_kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; + c->c_kex->kex[KEX_ECDH_SHA2] = kexecdh_client; c->c_kex->verify_host_key = hostjump; if (!(j = setjmp(kexjmp))) { @@ -673,6 +675,9 @@ main(int argc, char **argv) case KEY_DSA: get_keytypes |= KT_DSA; break; + case KEY_ECDSA: + get_keytypes |= KT_ECDSA; + break; case KEY_RSA: get_keytypes |= KT_RSA; break; diff --git a/ssh-keysign.8 b/ssh-keysign.8 index 2e47f1203..5e09e0271 100644 --- a/ssh-keysign.8 +++ b/ssh-keysign.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-keysign.8,v 1.11 2010/08/08 19:36:30 jmc Exp $ +.\" $OpenBSD: ssh-keysign.8,v 1.12 2010/08/31 11:54:45 djm Exp $ .\" .\" Copyright (c) 2002 Markus Friedl. All rights reserved. .\" @@ -22,7 +22,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: August 8 2010 $ +.Dd $Mdocdate: August 31 2010 $ .Dt SSH-KEYSIGN 8 .Os .Sh NAME @@ -62,6 +62,7 @@ Controls whether is enabled. .Pp .It Pa /etc/ssh/ssh_host_dsa_key +.It Pa /etc/ssh/ssh_host_ecdsa_key .It Pa /etc/ssh/ssh_host_rsa_key These files contain the private parts of the host keys used to generate the digital signature. @@ -72,6 +73,7 @@ Since they are readable only by root, must be set-uid root if host-based authentication is used. .Pp .It Pa /etc/ssh/ssh_host_dsa_key-cert.pub +.It Pa /etc/ssh/ssh_host_ecdsa_key-cert.pub .It Pa /etc/ssh/ssh_host_rsa_key-cert.pub If these files exist they are assumed to contain public certificate information corresponding with the private keys above. diff --git a/ssh.1 b/ssh.1 index 9b134f4ba..a3d001152 100644 --- a/ssh.1 +++ b/ssh.1 @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh.1,v 1.309 2010/08/08 19:36:30 jmc Exp $ -.Dd $Mdocdate: August 8 2010 $ +.\" $OpenBSD: ssh.1,v 1.310 2010/08/31 11:54:45 djm Exp $ +.Dd $Mdocdate: August 31 2010 $ .Dt SSH 1 .Os .Sh NAME @@ -269,13 +269,14 @@ should use to communicate with a PKCS#11 token providing the user's private RSA key. .It Fl i Ar identity_file Selects a file from which the identity (private key) for -RSA or DSA authentication is read. +public key authentication is read. The default is .Pa ~/.ssh/identity for protocol version 1, and -.Pa ~/.ssh/id_rsa +.Pa ~/.ssh/id_dsa , +.Pa ~/.ssh/id_ecdsa and -.Pa ~/.ssh/id_dsa +.Pa ~/.ssh/id_rsa for protocol version 2. Identity files may also be specified on a per-host basis in the configuration file. @@ -721,9 +722,9 @@ key pair for authentication purposes. The server knows the public key, and only the user knows the private key. .Nm implements public key authentication protocol automatically, -using either the RSA or DSA algorithms. +using one of the DSA, ECDSA or RSA algorithms. Protocol 1 is restricted to using only RSA keys, -but protocol 2 may use either. +but protocol 2 may use any. The .Sx HISTORY section of @@ -748,6 +749,8 @@ This stores the private key in (protocol 1), .Pa ~/.ssh/id_dsa (protocol 2 DSA), +.Pa ~/.ssh/id_ecdsa +(protocol 2 ECDSA), or .Pa ~/.ssh/id_rsa (protocol 2 RSA) @@ -756,6 +759,8 @@ and stores the public key in (protocol 1), .Pa ~/.ssh/id_dsa.pub (protocol 2 DSA), +.Pa ~/.ssh/id_ecdsa.pub +(protocol 2 ECDSA), or .Pa ~/.ssh/id_rsa.pub (protocol 2 RSA) @@ -1277,7 +1282,8 @@ secret, but the recommended permissions are read/write/execute for the user, and not accessible by others. .Pp .It Pa ~/.ssh/authorized_keys -Lists the public keys (RSA/DSA) that can be used for logging in as this user. +Lists the public keys (DSA/ECDSA/RSA) that can be used for logging in as +this user. The format of this file is described in the .Xr sshd 8 manual page. @@ -1298,6 +1304,7 @@ above. .Pp .It Pa ~/.ssh/identity .It Pa ~/.ssh/id_dsa +.It Pa ~/.ssh/id_ecdsa .It Pa ~/.ssh/id_rsa Contains the private key for authentication. These files @@ -1311,6 +1318,7 @@ sensitive part of this file using 3DES. .Pp .It Pa ~/.ssh/identity.pub .It Pa ~/.ssh/id_dsa.pub +.It Pa ~/.ssh/id_ecdsa.pub .It Pa ~/.ssh/id_rsa.pub Contains the public key for authentication. These files are not @@ -1349,6 +1357,7 @@ The file format and configuration options are described in .Pp .It Pa /etc/ssh/ssh_host_key .It Pa /etc/ssh/ssh_host_dsa_key +.It Pa /etc/ssh/ssh_host_ecdsa_key .It Pa /etc/ssh/ssh_host_rsa_key These three files contain the private parts of the host keys and are used for host-based authentication. diff --git a/ssh.c b/ssh.c index 44b570bf9..1cdfc58e3 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.348 2010/08/16 04:06:06 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.349 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -780,7 +780,7 @@ main(int ac, char **av) sensitive_data.external_keysign = 0; if (options.rhosts_rsa_authentication || options.hostbased_authentication) { - sensitive_data.nkeys = 5; + sensitive_data.nkeys = 7; sensitive_data.keys = xcalloc(sensitive_data.nkeys, sizeof(Key)); @@ -789,25 +789,34 @@ main(int ac, char **av) _PATH_HOST_KEY_FILE, "", NULL, NULL); sensitive_data.keys[1] = key_load_private_cert(KEY_DSA, _PATH_HOST_DSA_KEY_FILE, "", NULL); - sensitive_data.keys[2] = key_load_private_cert(KEY_RSA, + sensitive_data.keys[2] = key_load_private_cert(KEY_ECDSA, + _PATH_HOST_ECDSA_KEY_FILE, "", NULL); + sensitive_data.keys[3] = key_load_private_cert(KEY_RSA, _PATH_HOST_RSA_KEY_FILE, "", NULL); - sensitive_data.keys[3] = key_load_private_type(KEY_DSA, + sensitive_data.keys[4] = key_load_private_type(KEY_DSA, _PATH_HOST_DSA_KEY_FILE, "", NULL, NULL); - sensitive_data.keys[4] = key_load_private_type(KEY_RSA, + sensitive_data.keys[5] = key_load_private_type(KEY_ECDSA, + _PATH_HOST_ECDSA_KEY_FILE, "", NULL, NULL); + sensitive_data.keys[6] = key_load_private_type(KEY_RSA, _PATH_HOST_RSA_KEY_FILE, "", NULL, NULL); PRIV_END; if (options.hostbased_authentication == 1 && sensitive_data.keys[0] == NULL && - sensitive_data.keys[3] == NULL && - sensitive_data.keys[4] == NULL) { + sensitive_data.keys[4] == NULL && + sensitive_data.keys[5] == NULL && + sensitive_data.keys[6] == NULL) { sensitive_data.keys[1] = key_load_cert( _PATH_HOST_DSA_KEY_FILE); sensitive_data.keys[2] = key_load_cert( + _PATH_HOST_ECDSA_KEY_FILE); + sensitive_data.keys[3] = key_load_cert( _PATH_HOST_RSA_KEY_FILE); - sensitive_data.keys[3] = key_load_public( - _PATH_HOST_DSA_KEY_FILE, NULL); sensitive_data.keys[4] = key_load_public( + _PATH_HOST_DSA_KEY_FILE, NULL); + sensitive_data.keys[5] = key_load_public( + _PATH_HOST_ECDSA_KEY_FILE, NULL); + sensitive_data.keys[6] = key_load_public( _PATH_HOST_RSA_KEY_FILE, NULL); sensitive_data.external_keysign = 1; } diff --git a/ssh2.h b/ssh2.h index 3ffaf686b..51a963cae 100644 --- a/ssh2.h +++ b/ssh2.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh2.h,v 1.13 2010/02/26 20:29:54 djm Exp $ */ +/* $OpenBSD: ssh2.h,v 1.14 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -98,6 +98,10 @@ #define SSH2_MSG_KEX_DH_GEX_REPLY 33 #define SSH2_MSG_KEX_DH_GEX_REQUEST 34 +/* ecdh */ +#define SSH2_MSG_KEX_ECDH_INIT 30 +#define SSH2_MSG_KEX_ECDH_REPLY 31 + /* user authentication: generic */ #define SSH2_MSG_USERAUTH_REQUEST 50 diff --git a/ssh_config.5 b/ssh_config.5 index ddb806ec0..33038ffcf 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.138 2010/08/04 05:37:01 djm Exp $ -.Dd $Mdocdate: August 4 2010 $ +.\" $OpenBSD: ssh_config.5,v 1.139 2010/08/31 11:54:45 djm Exp $ +.Dd $Mdocdate: August 31 2010 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -547,7 +547,15 @@ is similar to Specifies the protocol version 2 host key algorithms that the client wants to use in order of preference. The default for this option is: -.Dq ssh-rsa,ssh-dss . +.Bd -literal -offset 3n +ecdsa-sha2-nistp256-cert-v01@openssh.com, +ecdsa-sha2-nistp384-cert-v01@openssh.com, +ecdsa-sha2-nistp521-cert-v01@openssh.com, +ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com, +ssh-rsa-cert-v00@openssh.com,ssh-dss-cert-v00@openssh.com, +ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +ssh-rsa,ssh-dss +.Ed .It Cm HostKeyAlias Specifies an alias that should be used instead of the real host name when looking up or saving the host key @@ -583,14 +591,15 @@ offers many different identities. The default is .Dq no . .It Cm IdentityFile -Specifies a file from which the user's RSA or DSA authentication identity -is read. +Specifies a file from which the user's DSA, ECDSA or DSA authentication +identity is read. The default is .Pa ~/.ssh/identity for protocol version 1, and -.Pa ~/.ssh/id_rsa +.Pa ~/.ssh/id_dsa , +.Pa ~/.ssh/id_ecdsa and -.Pa ~/.ssh/id_dsa +.Pa ~/.ssh/id_rsa for protocol version 2. Additionally, any identities represented by the authentication agent will be used for authentication. diff --git a/sshconnect.c b/sshconnect.c index f55beffe4..4d3a08551 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.224 2010/04/16 21:14:27 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.225 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1173,7 +1173,7 @@ show_key_from_file(const char *file, const char *host, int keytype) static int show_other_keys(const char *host, Key *key) { - int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, -1}; + int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1}; int i, found = 0; for (i = 0; type[i] != -1; i++) { diff --git a/sshconnect2.c b/sshconnect2.c index 4c379ae59..a31a663d4 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.183 2010/04/26 22:28:24 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.184 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -145,6 +145,7 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; + kex->kex[KEX_ECDH_SHA2] = kexecdh_client; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; kex->verify_host_key=&verify_host_key_callback; diff --git a/sshd.8 b/sshd.8 index bf9d6a2ec..9d2efc7e1 100644 --- a/sshd.8 +++ b/sshd.8 @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.258 2010/08/08 19:36:30 jmc Exp $ -.Dd $Mdocdate: August 8 2010 $ +.\" $OpenBSD: sshd.8,v 1.259 2010/08/31 11:54:45 djm Exp $ +.Dd $Mdocdate: August 31 2010 $ .Dt SSHD 8 .Os .Sh NAME @@ -170,9 +170,10 @@ host key files are normally not readable by anyone but root). The default is .Pa /etc/ssh/ssh_host_key for protocol version 1, and -.Pa /etc/ssh/ssh_host_rsa_key +.Pa /etc/ssh/ssh_host_dsa_key , +.Pa /etc/ssh/ssh_host_ecdsa_key and -.Pa /etc/ssh/ssh_host_dsa_key +.Pa /etc/ssh/ssh_host_rsa_key for protocol version 2. It is possible to have multiple host key files for the different protocol versions and host key algorithms. @@ -275,7 +276,7 @@ though this can be changed via the .Cm Protocol option in .Xr sshd_config 5 . -Protocol 2 supports both RSA and DSA keys; +Protocol 2 supports DSA, ECDSA and RSA keys; protocol 1 only supports RSA keys. For both protocols, each host has a host-specific key, @@ -483,6 +484,9 @@ protocol version 1; the comment field is not used for anything (but may be convenient for the user to identify the key). For protocol version 2 the keytype is +.Dq ecdsa-sha2-nistp256 , +.Dq ecdsa-sha2-nistp384 , +.Dq ecdsa-sha2-nistp521 , .Dq ssh-dss or .Dq ssh-rsa . @@ -494,6 +498,7 @@ keys up to 16 kilobits. You don't want to type them in; instead, copy the .Pa identity.pub , .Pa id_dsa.pub , +.Pa id_ecdsa.pub , or the .Pa id_rsa.pub file and edit it. @@ -792,7 +797,8 @@ secret, but the recommended permissions are read/write/execute for the user, and not accessible by others. .Pp .It Pa ~/.ssh/authorized_keys -Lists the public keys (RSA/DSA) that can be used for logging in as this user. +Lists the public keys (DSA/ECDSA/RSA) that can be used for logging in +as this user. The format of this file is described above. The content of the file is not highly sensitive, but the recommended permissions are read/write for the user, and not accessible by others. @@ -871,6 +877,7 @@ rlogin/rsh. .Pp .It Pa /etc/ssh/ssh_host_key .It Pa /etc/ssh/ssh_host_dsa_key +.It Pa /etc/ssh/ssh_host_ecdsa_key .It Pa /etc/ssh/ssh_host_rsa_key These three files contain the private parts of the host keys. These files should only be owned by root, readable only by root, and not @@ -881,6 +888,7 @@ does not start if these files are group/world-accessible. .Pp .It Pa /etc/ssh/ssh_host_key.pub .It Pa /etc/ssh/ssh_host_dsa_key.pub +.It Pa /etc/ssh/ssh_host_ecdsa_key.pub .It Pa /etc/ssh/ssh_host_rsa_key.pub These three files contain the public parts of the host keys. These files should be world-readable but writable only by diff --git a/sshd.c b/sshd.c index 52a3789bb..658a4978d 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.377 2010/08/16 04:06:06 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.378 2010/08/31 11:54:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -733,6 +733,7 @@ list_hostkey_types(void) switch (key->type) { case KEY_RSA: case KEY_DSA: + case KEY_ECDSA: if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); p = key_ssh_name(key); @@ -748,6 +749,7 @@ list_hostkey_types(void) case KEY_DSA_CERT_V00: case KEY_RSA_CERT: case KEY_DSA_CERT: + case KEY_ECDSA_CERT: if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); p = key_ssh_name(key); @@ -774,6 +776,7 @@ get_hostkey_by_type(int type, int need_private) case KEY_DSA_CERT_V00: case KEY_RSA_CERT: case KEY_DSA_CERT: + case KEY_ECDSA_CERT: key = sensitive_data.host_certificates[i]; break; default: @@ -1576,6 +1579,7 @@ main(int ac, char **av) break; case KEY_RSA: case KEY_DSA: + case KEY_ECDSA: sensitive_data.have_ssh2_key = 1; break; } @@ -2302,6 +2306,7 @@ do_ssh2_kex(void) kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; + kex->kex[KEX_ECDH_SHA2] = kexecdh_server; kex->server = 1; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; diff --git a/sshd_config.5 b/sshd_config.5 index 596a728f8..af3d89b80 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.125 2010/06/30 07:28:34 jmc Exp $ -.Dd $Mdocdate: June 30 2010 $ +.\" $OpenBSD: sshd_config.5,v 1.126 2010/08/31 11:54:45 djm Exp $ +.Dd $Mdocdate: August 31 2010 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -470,9 +470,10 @@ used by SSH. The default is .Pa /etc/ssh/ssh_host_key for protocol version 1, and -.Pa /etc/ssh/ssh_host_rsa_key +.Pa /etc/ssh/ssh_host_dsa_key , +.Pa /etc/ssh/ssh_host_ecdsa_key and -.Pa /etc/ssh/ssh_host_dsa_key +.Pa /etc/ssh/ssh_host_rsa_key for protocol version 2. Note that .Xr sshd 8 @@ -480,7 +481,8 @@ will refuse to use a file if it is group/world-accessible. It is possible to have multiple host key files. .Dq rsa1 keys are used for version 1 and -.Dq dsa +.Dq dsa , +.Dq ecdsa or .Dq rsa are used for version 2 of the SSH protocol. diff --git a/uuencode.c b/uuencode.c index b9e57e993..09d80d2fc 100644 --- a/uuencode.c +++ b/uuencode.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uuencode.c,v 1.25 2009/03/05 11:30:50 djm Exp $ */ +/* $OpenBSD: uuencode.c,v 1.26 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -72,7 +72,7 @@ uudecode(const char *src, u_char *target, size_t targsize) } void -dump_base64(FILE *fp, u_char *data, u_int len) +dump_base64(FILE *fp, const u_char *data, u_int len) { char *buf; int i, n; diff --git a/uuencode.h b/uuencode.h index fec55b491..4d9888126 100644 --- a/uuencode.h +++ b/uuencode.h @@ -1,4 +1,4 @@ -/* $OpenBSD: uuencode.h,v 1.13 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: uuencode.h,v 1.14 2010/08/31 11:54:45 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -26,4 +26,4 @@ int uuencode(const u_char *, u_int, char *, size_t); int uudecode(const char *, u_char *, size_t); -void dump_base64(FILE *, u_char *, u_int); +void dump_base64(FILE *, const u_char *, u_int); -- cgit v1.2.3 From 4314c2b5489da5af3b70c440fcd2c44ddc4745a8 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 10 Sep 2010 11:12:09 +1000 Subject: - djm@cvs.openbsd.org 2010/08/31 12:33:38 [ssh-add.c ssh-agent.c ssh-keygen.c ssh-keysign.c ssh.c sshd.c] reintroduce commit from tedu@, which I pulled out for release engineering: OpenSSL_add_all_algorithms is the name of the function we have a man page for, so use that. ok djm --- ChangeLog | 9 ++++++++- ssh-add.c | 4 ++-- ssh-agent.c | 4 ++-- ssh-ecdsa.c | 2 +- ssh-keygen.c | 4 ++-- ssh-keysign.c | 4 ++-- ssh.c | 4 ++-- sshd.c | 4 ++-- 8 files changed, 21 insertions(+), 14 deletions(-) (limited to 'ssh.c') diff --git a/ChangeLog b/ChangeLog index 63216685b..594ebc24c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,14 @@ -2010910 +20100910 - (dtucker) [openbsd-compat/port-linux.c] Check is_selinux_enabled for exact return code since it can apparently return -1 under some conditions. From openssh bugs werbittewas de, ok djm@ + - OpenBSD CVS Sync + - djm@cvs.openbsd.org 2010/08/31 12:33:38 + [ssh-add.c ssh-agent.c ssh-keygen.c ssh-keysign.c ssh.c sshd.c] + reintroduce commit from tedu@, which I pulled out for release + engineering: + OpenSSL_add_all_algorithms is the name of the function we have a + man page for, so use that. ok djm 20100831 - OpenBSD CVS Sync diff --git a/ssh-add.c b/ssh-add.c index 31e618390..8bf5675fb 100644 --- a/ssh-add.c +++ b/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.99 2010/08/31 11:54:45 djm Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.100 2010/08/31 12:33:38 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -373,7 +373,7 @@ main(int argc, char **argv) init_rng(); seed_rng(); - SSLeay_add_all_algorithms(); + OpenSSL_add_all_algorithms(); /* At first, get a connection to the authentication agent. */ ac = ssh_get_authentication_connection(); diff --git a/ssh-agent.c b/ssh-agent.c index fbfd79c13..87939b2b6 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.169 2010/08/31 11:54:45 djm Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.170 2010/08/31 12:33:38 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1146,7 +1146,7 @@ main(int ac, char **av) prctl(PR_SET_DUMPABLE, 0); #endif - SSLeay_add_all_algorithms(); + OpenSSL_add_all_algorithms(); __progname = ssh_get_progname(av[0]); init_rng(); diff --git a/ssh-ecdsa.c b/ssh-ecdsa.c index 3069ca5bc..0627ee5c0 100644 --- a/ssh-ecdsa.c +++ b/ssh-ecdsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD */ +/* $OpenBSD$ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. diff --git a/ssh-keygen.c b/ssh-keygen.c index 448585185..a66e8508c 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.200 2010/08/31 11:54:45 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.201 2010/08/31 12:33:38 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -1804,7 +1804,7 @@ main(int argc, char **argv) __progname = ssh_get_progname(argv[0]); - SSLeay_add_all_algorithms(); + OpenSSL_add_all_algorithms(); log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); init_rng(); diff --git a/ssh-keysign.c b/ssh-keysign.c index cf3bf1b9b..d05156005 100644 --- a/ssh-keysign.c +++ b/ssh-keysign.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keysign.c,v 1.34 2010/08/16 04:06:06 djm Exp $ */ +/* $OpenBSD: ssh-keysign.c,v 1.35 2010/08/31 12:33:38 djm Exp $ */ /* * Copyright (c) 2002 Markus Friedl. All rights reserved. * @@ -194,7 +194,7 @@ main(int argc, char **argv) if (key_fd[0] == -1 && key_fd[1] == -1) fatal("could not open any host key"); - SSLeay_add_all_algorithms(); + OpenSSL_add_all_algorithms(); for (i = 0; i < 256; i++) rnd[i] = arc4random(); RAND_seed(rnd, sizeof(rnd)); diff --git a/ssh.c b/ssh.c index 1cdfc58e3..e5b643258 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.349 2010/08/31 11:54:45 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.350 2010/08/31 12:33:38 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -599,7 +599,7 @@ main(int ac, char **av) if (!host) usage(); - SSLeay_add_all_algorithms(); + OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); /* Initialize the command to execute on remote host. */ diff --git a/sshd.c b/sshd.c index 658a4978d..7995f5a1d 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.378 2010/08/31 11:54:45 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.379 2010/08/31 12:33:38 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1467,7 +1467,7 @@ main(int ac, char **av) else closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); - SSLeay_add_all_algorithms(); + OpenSSL_add_all_algorithms(); /* * Force logging to stderr until we have loaded the private host -- cgit v1.2.3 From 5929c52f65fc2029fb9b496ccfa91bd4cecca0be Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 10 Sep 2010 11:17:02 +1000 Subject: - markus@cvs.openbsd.org 2010/09/02 16:08:39 [ssh.c] unbreak ControlPersist=yes for ControlMaster=yes; ok djm@ --- ChangeLog | 3 +++ ssh.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'ssh.c') diff --git a/ChangeLog b/ChangeLog index e3b6da975..d90bf37c8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -21,6 +21,9 @@ - markus@cvs.openbsd.org 2010/09/02 16:07:25 [ssh-keygen.c] permit -b 256, 384 or 521 as key size for ECDSA; ok djm@ + - markus@cvs.openbsd.org 2010/09/02 16:08:39 + [ssh.c] + unbreak ControlPersist=yes for ControlMaster=yes; ok djm@ 20100831 - OpenBSD CVS Sync diff --git a/ssh.c b/ssh.c index e5b643258..51c68d7da 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.350 2010/08/31 12:33:38 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.351 2010/09/02 16:08:39 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -936,6 +936,7 @@ control_persist_detach(void) tty_flag = otty_flag; close(muxserver_sock); muxserver_sock = -1; + options.control_master = SSHCTL_MASTER_NO; muxclient(options.control_path); /* muxclient() doesn't return on success. */ fatal("Failed to connect to new control master"); -- cgit v1.2.3 From 6af914a15c0c33e8b5bab5ca61919b8562ff1db9 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 10 Sep 2010 11:39:26 +1000 Subject: - (djm) [authfd.c authfile.c bufec.c buffer.h configure.ac kex.h kexecdh.c] [kexecdhc.c kexecdhs.c key.c key.h myproposal.h packet.c readconf.c] [ssh-agent.c ssh-ecdsa.c ssh-keygen.c ssh.c] Disable ECDH and ECDSA on platforms that don't have the requisite OpenSSL support. ok dtucker@ --- ChangeLog | 4 ++++ authfd.c | 2 ++ authfile.c | 4 ++++ bufec.c | 3 +++ buffer.h | 2 ++ configure.ac | 26 +++++++++++++++++++++-- kex.h | 5 ++++- kexecdh.c | 3 +++ kexecdhc.c | 13 ++++++++++-- kexecdhs.c | 13 ++++++++++-- key.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- key.h | 10 ++++++++- myproposal.h | 53 +++++++++++++++++++++++++---------------------- packet.c | 4 ++++ readconf.c | 3 ++- ssh-agent.c | 4 ++++ ssh-ecdsa.c | 4 ++++ ssh-keygen.c | 6 ++++++ ssh.c | 10 +++++++++ 19 files changed, 200 insertions(+), 36 deletions(-) (limited to 'ssh.c') diff --git a/ChangeLog b/ChangeLog index 87fee3bf0..742e966c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -62,6 +62,10 @@ client. ok naddy@ + - (djm) [authfd.c authfile.c bufec.c buffer.h configure.ac kex.h kexecdh.c] + [kexecdhc.c kexecdhs.c key.c key.h myproposal.h packet.c readconf.c] + [ssh-agent.c ssh-ecdsa.c ssh-keygen.c ssh.c] Disable ECDH and ECDSA on + platforms that don't have the requisite OpenSSL support. ok dtucker@ 20100831 - OpenBSD CVS Sync diff --git a/authfd.c b/authfd.c index ec537d2e9..c11c3f5a8 100644 --- a/authfd.c +++ b/authfd.c @@ -509,6 +509,7 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) 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), @@ -522,6 +523,7 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) buffer_len(&key->cert->certblob)); buffer_put_bignum2(b, EC_KEY_get0_private_key(key->ecdsa)); break; +#endif } buffer_put_cstring(b, comment); } diff --git a/authfile.c b/authfile.c index 20ac8c76d..b1e3eda5c 100644 --- a/authfile.c +++ b/authfile.c @@ -213,10 +213,12 @@ key_save_private_pem(Key *key, const char *filename, const char *_passphrase, success = PEM_write_DSAPrivateKey(fp, key->dsa, cipher, passphrase, len, NULL, NULL); break; +#ifdef OPENSSL_HAS_ECC case KEY_ECDSA: success = PEM_write_ECPrivateKey(fp, key->ecdsa, cipher, passphrase, len, NULL, NULL); break; +#endif case KEY_RSA: success = PEM_write_RSAPrivateKey(fp, key->rsa, cipher, passphrase, len, NULL, NULL); @@ -515,6 +517,7 @@ key_load_private_pem(int fd, int type, const char *passphrase, #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); @@ -538,6 +541,7 @@ key_load_private_pem(int fd, int type, const char *passphrase, if (prv->ecdsa != NULL) key_dump_ec_key(prv->ecdsa); #endif +#endif /* OPENSSL_HAS_ECC */ } else { error("PEM_read_PrivateKey: mismatch or " "unknown EVP_PKEY save_type %d", pk->save_type); diff --git a/bufec.c b/bufec.c index c77d1ecb9..3dcb49477 100644 --- a/bufec.c +++ b/bufec.c @@ -17,6 +17,8 @@ #include "includes.h" +#ifdef OPENSSL_HAS_ECC + #include #include @@ -141,3 +143,4 @@ buffer_get_ecpoint(Buffer *buffer, const EC_GROUP *curve, fatal("%s: buffer error", __func__); } +#endif /* OPENSSL_HAS_ECC */ diff --git a/buffer.h b/buffer.h index 1fb3f1666..e2a9dd100 100644 --- a/buffer.h +++ b/buffer.h @@ -86,11 +86,13 @@ char *buffer_get_cstring_ret(Buffer *, u_int *); void *buffer_get_string_ptr_ret(Buffer *, u_int *); int buffer_get_char_ret(char *, Buffer *); +#ifdef OPENSSL_HAS_ECC #include int buffer_put_ecpoint_ret(Buffer *, const EC_GROUP *, const EC_POINT *); void buffer_put_ecpoint(Buffer *, const EC_GROUP *, const EC_POINT *); int buffer_get_ecpoint_ret(Buffer *, const EC_GROUP *, EC_POINT *); void buffer_get_ecpoint(Buffer *, const EC_GROUP *, EC_POINT *); +#endif #endif /* BUFFER_H */ diff --git a/configure.ac b/configure.ac index 637e7b536..d267ba2b1 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -# $Id: configure.ac,v 1.451 2010/08/16 03:15:23 dtucker Exp $ +# $Id: configure.ac,v 1.452 2010/09/10 01:39:27 djm Exp $ # # Copyright (c) 1999-2004 Damien Miller # @@ -15,7 +15,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_INIT(OpenSSH, Portable, openssh-unix-dev@mindrot.org) -AC_REVISION($Revision: 1.451 $) +AC_REVISION($Revision: 1.452 $) AC_CONFIG_SRCDIR([ssh.c]) AC_CONFIG_HEADER(config.h) @@ -2158,6 +2158,28 @@ fi # Search for SHA256 support in libc and/or OpenSSL AC_CHECK_FUNCS(SHA256_Update EVP_sha256) +# Check complete ECC support in OpenSSL +AC_MSG_CHECKING([whether OpenSSL has complete ECC support]) +AC_LINK_IFELSE( + [AC_LANG_SOURCE([[ +#include +#include +#include +int main(void) { + EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp521r1); + const EVP_MD *m = EVP_sha512(); /* We need this too */ +} + ]])], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(OPENSSL_HAS_ECC, 1, + [libcrypto includes complete ECC support]) + ], + [ + AC_MSG_RESULT(no) + ] +) + saved_LIBS="$LIBS" AC_CHECK_LIB(iaf, ia_openinfo, [ LIBS="$LIBS -liaf" diff --git a/kex.h b/kex.h index f5dcc8791..06914756a 100644 --- a/kex.h +++ b/kex.h @@ -159,13 +159,16 @@ void kexgex_hash(const EVP_MD *, char *, char *, char *, int, char *, int, u_char *, int, int, int, int, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *); +#ifdef OPENSSL_HAS_ECC void kex_ecdh_hash(const EVP_MD *, const EC_GROUP *, char *, char *, char *, int, char *, int, u_char *, int, const EC_POINT *, const EC_POINT *, const BIGNUM *, u_char **, u_int *); - int kex_ecdh_name_to_nid(const char *); const EVP_MD *kex_ecdh_name_to_evpmd(const char *); +#else +# define kex_ecdh_name_to_evpmd(x) NULL +#endif void derive_ssh1_session_id(BIGNUM *, BIGNUM *, u_int8_t[8], u_int8_t[16]); diff --git a/kexecdh.c b/kexecdh.c index f59d7b903..4c58a5122 100644 --- a/kexecdh.c +++ b/kexecdh.c @@ -26,6 +26,8 @@ #include "includes.h" +#ifdef OPENSSL_HAS_ECC + #include #include @@ -116,3 +118,4 @@ kex_ecdh_hash( *hashlen = EVP_MD_size(evp_md); } +#endif /* OPENSSL_HAS_ECC */ diff --git a/kexecdhc.c b/kexecdhc.c index 7ac7b1ee2..297a0e5a9 100644 --- a/kexecdhc.c +++ b/kexecdhc.c @@ -32,8 +32,6 @@ #include #include -#include - #include "xmalloc.h" #include "buffer.h" #include "key.h" @@ -44,6 +42,10 @@ #include "dh.h" #include "ssh2.h" +#ifdef OPENSSL_HAS_ECC + +#include + void kexecdh_client(Kex *kex) { @@ -156,3 +158,10 @@ kexecdh_client(Kex *kex) BN_clear_free(shared_secret); kex_finish(kex); } +#else /* OPENSSL_HAS_ECC */ +void +kexecdh_client(Kex *kex) +{ + fatal("ECC support is not enabled"); +} +#endif /* OPENSSL_HAS_ECC */ diff --git a/kexecdhs.c b/kexecdhs.c index e49a0ef37..d2c3feb09 100644 --- a/kexecdhs.c +++ b/kexecdhs.c @@ -30,8 +30,6 @@ #include #include -#include - #include "xmalloc.h" #include "buffer.h" #include "key.h" @@ -46,6 +44,10 @@ #endif #include "monitor_wrap.h" +#ifdef OPENSSL_HAS_ECC + +#include + void kexecdh_server(Kex *kex) { @@ -161,3 +163,10 @@ kexecdh_server(Kex *kex) BN_clear_free(shared_secret); kex_finish(kex); } +#else /* OPENSSL_HAS_ECC */ +void +kexecdh_server(Kex *kex) +{ + fatal("ECC support is not enabled"); +} +#endif /* OPENSSL_HAS_ECC */ diff --git a/key.c b/key.c index b9dc2355b..3cda8f2cb 100644 --- a/key.c +++ b/key.c @@ -111,10 +111,12 @@ key_new(int type) 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 case KEY_UNSPEC: break; default: @@ -214,12 +216,14 @@ key_free(Key *k) 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_UNSPEC: break; default: @@ -279,6 +283,7 @@ key_equal_public(const Key *a, const Key *b) 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 || @@ -297,6 +302,7 @@ key_equal_public(const Key *a, const Key *b) } BN_CTX_free(bnctx); return 1; +#endif /* OPENSSL_HAS_ECC */ default: fatal("key_equal: bad key type %d", a->type); } @@ -695,11 +701,13 @@ key_read(Key *ret, char **cpp) } *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"); @@ -736,12 +744,14 @@ key_read(Key *ret, char **cpp) 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)) { @@ -772,6 +782,7 @@ key_read(Key *ret, char **cpp) 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); @@ -783,6 +794,7 @@ key_read(Key *ret, char **cpp) key_dump_ec_key(ret->ecdsa); #endif } +#endif success = 1; /*XXXX*/ key_free(k); @@ -839,11 +851,13 @@ key_write(const Key *key, FILE *f) 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: @@ -877,8 +891,10 @@ key_type(const Key *k) return "RSA"; case KEY_DSA: return "DSA"; +#ifdef OPENSSL_HAS_ECC case KEY_ECDSA: return "ECDSA"; +#endif case KEY_RSA_CERT_V00: return "RSA-CERT-V00"; case KEY_DSA_CERT_V00: @@ -887,8 +903,10 @@ key_type(const Key *k) return "RSA-CERT"; case KEY_DSA_CERT: return "DSA-CERT"; +#ifdef OPENSSL_HAS_ECC case KEY_ECDSA_CERT: return "ECDSA-CERT"; +#endif } return "unknown"; } @@ -922,6 +940,7 @@ key_ssh_name_from_type_nid(int type, int nid) return "ssh-rsa-cert-v01@openssh.com"; case KEY_DSA_CERT: return "ssh-dss-cert-v01@openssh.com"; +#ifdef OPENSSL_HAS_ECC case KEY_ECDSA: switch (nid) { case NID_X9_62_prime256v1: @@ -946,6 +965,7 @@ key_ssh_name_from_type_nid(int type, int nid) break; } break; +#endif /* OPENSSL_HAS_ECC */ } return "ssh-unknown"; } @@ -976,9 +996,11 @@ key_size(const Key *k) 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 } return 0; } @@ -1012,17 +1034,20 @@ 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; case 521: return NID_secp521r1; +#endif default: return -1; } } +#ifdef OPENSSL_HAS_ECC /* * This is horrid, but OpenSSL's PEM_read_PrivateKey seems not to restore * the EC_GROUP nid when loading a key... @@ -1070,6 +1095,7 @@ ecdsa_generate_private_key(u_int bits, int *nid) fatal("%s: EC_KEY_generate_key failed", __func__); return private; } +#endif /* OPENSSL_HAS_ECC */ Key * key_generate(int type, u_int bits) @@ -1079,9 +1105,11 @@ key_generate(int type, u_int bits) 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); @@ -1158,6 +1186,7 @@ key_from_private(const Key *k) (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); @@ -1168,6 +1197,7 @@ key_from_private(const Key *k) 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: @@ -1199,11 +1229,13 @@ key_type_from_name(char *name) return KEY_RSA; } else if (strcmp(name, "ssh-dss") == 0) { return KEY_DSA; +#ifdef OPENSSL_HAS_ECC } else if (strcmp(name, "ecdsa") == 0 || strcmp(name, "ecdsa-sha2-nistp256") == 0 || strcmp(name, "ecdsa-sha2-nistp384") == 0 || strcmp(name, "ecdsa-sha2-nistp521") == 0) { return KEY_ECDSA; +#endif } else if (strcmp(name, "ssh-rsa-cert-v00@openssh.com") == 0) { return KEY_RSA_CERT_V00; } else if (strcmp(name, "ssh-dss-cert-v00@openssh.com") == 0) { @@ -1212,10 +1244,13 @@ key_type_from_name(char *name) return KEY_RSA_CERT; } else if (strcmp(name, "ssh-dss-cert-v01@openssh.com") == 0) { return KEY_DSA_CERT; +#ifdef OPENSSL_HAS_ECC } else if (strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0 || strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0 || - strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) + strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) { return KEY_ECDSA_CERT; +#endif + } debug2("key_type_from_name: unknown key type '%s'", name); return KEY_UNSPEC; @@ -1224,6 +1259,7 @@ key_type_from_name(char *name) int key_ecdsa_nid_from_name(const char *name) { +#ifdef OPENSSL_HAS_ECC if (strcmp(name, "ecdsa-sha2-nistp256") == 0 || strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0) return NID_X9_62_prime256v1; @@ -1233,6 +1269,7 @@ key_ecdsa_nid_from_name(const char *name) if (strcmp(name, "ecdsa-sha2-nistp521") == 0 || strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) return NID_secp521r1; +#endif /* OPENSSL_HAS_ECC */ debug2("%s: unknown/non-ECDSA key type '%s'", __func__, name); return -1; @@ -1403,7 +1440,9 @@ key_from_blob(const u_char *blob, u_int blen) int rlen, type, nid = -1; char *ktype = NULL, *curve = NULL; Key *key = NULL; +#ifdef OPENSSL_HAS_ECC EC_POINT *q = NULL; +#endif #ifdef DEBUG_PK dump_base64(stderr, blob, blen); @@ -1416,8 +1455,10 @@ key_from_blob(const u_char *blob, u_int blen) } 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 switch (type) { case KEY_RSA_CERT: @@ -1455,6 +1496,7 @@ key_from_blob(const u_char *blob, u_int blen) 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 */ @@ -1490,6 +1532,7 @@ key_from_blob(const u_char *blob, u_int blen) key_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q); #endif break; +#endif /* OPENSSL_HAS_ECC */ case KEY_UNSPEC: key = key_new(type); break; @@ -1509,8 +1552,10 @@ key_from_blob(const u_char *blob, u_int blen) xfree(ktype); if (curve != NULL) xfree(curve); +#ifdef OPENSSL_HAS_ECC if (q != NULL) EC_POINT_free(q); +#endif buffer_free(&b); return key; } @@ -1543,12 +1588,14 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp) 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(key)); 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(key)); buffer_put_bignum2(&b, key->rsa->e); @@ -1582,9 +1629,11 @@ key_sign( 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: @@ -1613,9 +1662,11 @@ key_verify( 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: @@ -1670,6 +1721,7 @@ key_demote(const Key *k) 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 */ @@ -1680,6 +1732,7 @@ key_demote(const Key *k) EC_KEY_get0_public_key(k->ecdsa)) != 1) fatal("key_demote: EC_KEY_set_public_key failed"); break; +#endif default: fatal("key_free: bad key type %d", k->type); break; @@ -1819,6 +1872,7 @@ key_certify(Key *k, Key *ca) 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)); @@ -1826,6 +1880,7 @@ key_certify(Key *k, Key *ca) 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); @@ -1955,12 +2010,14 @@ key_cert_is_legacy(Key *k) 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; else if (strcmp(name, "nistp521") == 0) return NID_secp521r1; +#endif debug("%s: unsupported EC curve name \"%.100s\"", __func__, name); return -1; @@ -1970,12 +2027,14 @@ u_int key_curve_nid_to_bits(int nid) { switch (nid) { +#ifdef OPENSSL_HAS_ECC case NID_X9_62_prime256v1: return 256; case NID_secp384r1: return 384; case NID_secp521r1: return 521; +#endif default: error("%s: unsupported EC curve nid %d", __func__, nid); return 0; @@ -1985,17 +2044,19 @@ key_curve_nid_to_bits(int nid) 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"; else if (nid == NID_secp521r1) return "nistp521"; - +#endif error("%s: unsupported EC curve nid %d", __func__, nid); return NULL; } +#ifdef OPENSSL_HAS_ECC const EVP_MD * key_ec_nid_to_evpmd(int nid) { @@ -2180,4 +2241,4 @@ key_dump_ec_key(const EC_KEY *key) fputs("\n", stderr); } #endif /* defined(DEBUG_KEXECDH) || defined(DEBUG_PK) */ - +#endif /* OPENSSL_HAS_ECC */ diff --git a/key.h b/key.h index ba1a20c07..86a1d889c 100644 --- a/key.h +++ b/key.h @@ -29,7 +29,9 @@ #include "buffer.h" #include #include +#ifdef OPENSSL_HAS_ECC #include +#endif typedef struct Key Key; enum types { @@ -77,7 +79,11 @@ struct Key { RSA *rsa; DSA *dsa; int ecdsa_nid; /* NID of curve */ +#ifdef OPENSSL_HAS_ECC EC_KEY *ecdsa; +#else + void *ecdsa; +#endif struct KeyCert *cert; }; @@ -114,10 +120,12 @@ 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_group_to_nid(const EC_GROUP *); const EVP_MD * key_ec_nid_to_evpmd(int nid); int key_ec_validate_public(const EC_GROUP *, const EC_POINT *); int key_ec_validate_private(const EC_KEY *); +#endif Key *key_from_blob(const u_char *, u_int); int key_to_blob(const Key *, u_char **, u_int *); @@ -135,7 +143,7 @@ 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); -#if defined(DEBUG_KEXECDH) || defined(DEBUG_PK) +#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 diff --git a/myproposal.h b/myproposal.h index 5daca533b..893190788 100644 --- a/myproposal.h +++ b/myproposal.h @@ -26,44 +26,49 @@ #include +#ifdef OPENSSL_HAS_ECC +# define KEX_ECDH_METHODS \ + "ecdh-sha2-nistp256," \ + "ecdh-sha2-nistp384," \ + "ecdh-sha2-nistp521," +# define HOSTKEY_ECDSA_CERT_METHODS \ + "ecdsa-sha2-nistp256-cert-v01@openssh.com," \ + "ecdsa-sha2-nistp384-cert-v01@openssh.com," \ + "ecdsa-sha2-nistp521-cert-v01@openssh.com," +# define HOSTKEY_ECDSA_METHODS \ + "ecdsa-sha2-nistp256," \ + "ecdsa-sha2-nistp384," \ + "ecdsa-sha2-nistp521," +#else +# define KEX_ECDH_METHODS +# define HOSTKEY_ECDSA_CERT_METHODS +# define HOSTKEY_ECDSA_METHODS +#endif + /* Old OpenSSL doesn't support what we need for DHGEX-sha256 */ #if OPENSSL_VERSION_NUMBER < 0x00907000L -# define KEX_DEFAULT_KEX \ - "diffie-hellman-group-exchange-sha1," \ - "diffie-hellman-group14-sha1," \ - "diffie-hellman-group1-sha1" - -#define KEX_DEFAULT_PK_ALG \ - "ssh-rsa-cert-v01@openssh.com," \ - "ssh-dss-cert-v01@openssh.com," \ - "ssh-rsa-cert-v00@openssh.com," \ - "ssh-dss-cert-v00@openssh.com," \ - "ssh-rsa," \ - "ssh-dss" +# define KEX_SHA256_METHODS \ + "diffie-hellman-group-exchange-sha1," #else -# define KEX_DEFAULT_KEX \ - "ecdh-sha2-nistp256," \ - "ecdh-sha2-nistp384," \ - "ecdh-sha2-nistp521," \ - "diffie-hellman-group-exchange-sha256," \ +# define KEX_SHA256_METHODS +#endif + +# define KEX_DEFAULT_KEX \ + KEX_ECDH_METHODS \ + KEX_SHA256_METHODS \ "diffie-hellman-group-exchange-sha1," \ "diffie-hellman-group14-sha1," \ "diffie-hellman-group1-sha1" #define KEX_DEFAULT_PK_ALG \ - "ecdsa-sha2-nistp256-cert-v01@openssh.com," \ - "ecdsa-sha2-nistp384-cert-v01@openssh.com," \ - "ecdsa-sha2-nistp521-cert-v01@openssh.com," \ + HOSTKEY_ECDSA_CERT_METHODS \ "ssh-rsa-cert-v01@openssh.com," \ "ssh-dss-cert-v01@openssh.com," \ "ssh-rsa-cert-v00@openssh.com," \ "ssh-dss-cert-v00@openssh.com," \ - "ecdsa-sha2-nistp256," \ - "ecdsa-sha2-nistp384," \ - "ecdsa-sha2-nistp521," \ + HOSTKEY_ECDSA_METHODS \ "ssh-rsa," \ "ssh-dss" -#endif #define KEX_DEFAULT_ENCRYPT \ "aes128-ctr,aes192-ctr,aes256-ctr," \ diff --git a/packet.c b/packet.c index a06c5e3ef..0018d5839 100644 --- a/packet.c +++ b/packet.c @@ -641,11 +641,13 @@ packet_put_bignum2(BIGNUM * value) buffer_put_bignum2(&active_state->outgoing_packet, value); } +#ifdef OPENSSL_HAS_ECC void packet_put_ecpoint(const EC_GROUP *curve, const EC_POINT *point) { buffer_put_ecpoint(&active_state->outgoing_packet, curve, point); } +#endif /* * Finalizes and sends the packet. If the encryption key has been set, @@ -1517,11 +1519,13 @@ packet_get_bignum2(BIGNUM * value) buffer_get_bignum2(&active_state->incoming_packet, value); } +#ifdef OPENSSL_HAS_ECC void packet_get_ecpoint(const EC_GROUP *curve, EC_POINT *point) { buffer_get_ecpoint(&active_state->incoming_packet, curve, point); } +#endif void * packet_get_raw(u_int *length_ptr) diff --git a/readconf.c b/readconf.c index 98ce3017f..586422930 100644 --- a/readconf.c +++ b/readconf.c @@ -1214,12 +1214,13 @@ fill_default_options(Options * options) xmalloc(len); snprintf(options->identity_files[options->num_identity_files++], len, "~/%.100s", _PATH_SSH_CLIENT_ID_DSA); - +#ifdef OPENSSL_HAS_ECC len = 2 + strlen(_PATH_SSH_CLIENT_ID_ECDSA) + 1; options->identity_files[options->num_identity_files] = xmalloc(len); snprintf(options->identity_files[options->num_identity_files++], len, "~/%.100s", _PATH_SSH_CLIENT_ID_ECDSA); +#endif } } if (options->escape_char == -1) diff --git a/ssh-agent.c b/ssh-agent.c index 87939b2b6..8f19fb157 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -468,8 +468,10 @@ process_add_identity(SocketEntry *e, int version) int type, success = 0, death = 0, confirm = 0; char *type_name, *comment, *curve; Key *k = NULL; +#ifdef OPENSSL_HAS_ECC BIGNUM *exponent; EC_POINT *q; +#endif u_char *cert; u_int len; @@ -510,6 +512,7 @@ process_add_identity(SocketEntry *e, int version) key_add_private(k); buffer_get_bignum2(&e->request, 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); @@ -561,6 +564,7 @@ process_add_identity(SocketEntry *e, int version) fatal("%s: bad ECDSA key", __func__); BN_clear_free(exponent); break; +#endif /* OPENSSL_HAS_ECC */ case KEY_RSA: k = key_new_private(type); buffer_get_bignum2(&e->request, k->rsa->n); diff --git a/ssh-ecdsa.c b/ssh-ecdsa.c index 5c4ce2311..c8276b460 100644 --- a/ssh-ecdsa.c +++ b/ssh-ecdsa.c @@ -26,6 +26,8 @@ #include "includes.h" +#ifdef OPENSSL_HAS_ECC + #include #include @@ -162,3 +164,5 @@ ssh_ecdsa_verify(const Key *key, const u_char *signature, u_int signaturelen, ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error"); return ret; } + +#endif /* OPENSSL_HAS_ECC */ diff --git a/ssh-keygen.c b/ssh-keygen.c index 43b8c7f97..bbd434b0b 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -265,10 +265,12 @@ do_convert_to_pkcs8(Key *k) if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) fatal("PEM_write_DSA_PUBKEY failed"); break; +#ifdef OPENSSL_HAS_ECC case KEY_ECDSA: if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa)) fatal("PEM_write_EC_PUBKEY failed"); break; +#endif default: fatal("%s: unsupported key type %s", __func__, key_type(k)); } @@ -549,6 +551,7 @@ do_convert_from_pkcs8(Key **k, int *private) (*k)->type = KEY_DSA; (*k)->dsa = EVP_PKEY_get1_DSA(pubkey); break; +#ifdef OPENSSL_HAS_ECC case EVP_PKEY_EC: *k = key_new(KEY_UNSPEC); (*k)->type = KEY_ECDSA; @@ -556,6 +559,7 @@ do_convert_from_pkcs8(Key **k, int *private) (*k)->ecdsa_nid = key_ecdsa_group_to_nid( EC_KEY_get0_group((*k)->ecdsa)); break; +#endif default: fatal("%s: unsupported pubkey type %d", __func__, EVP_PKEY_type(pubkey->type)); @@ -632,10 +636,12 @@ do_convert_from(struct passwd *pw) ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL); break; +#ifdef OPENSSL_HAS_ECC case KEY_ECDSA: ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL, NULL, 0, NULL, NULL); break; +#endif case KEY_RSA: ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL); diff --git a/ssh.c b/ssh.c index 51c68d7da..3ade744b6 100644 --- a/ssh.c +++ b/ssh.c @@ -783,20 +783,26 @@ main(int ac, char **av) sensitive_data.nkeys = 7; sensitive_data.keys = xcalloc(sensitive_data.nkeys, sizeof(Key)); + for (i = 0; i < sensitive_data.nkeys; i++) + sensitive_data.keys[i] = NULL; PRIV_START; sensitive_data.keys[0] = key_load_private_type(KEY_RSA1, _PATH_HOST_KEY_FILE, "", NULL, NULL); sensitive_data.keys[1] = key_load_private_cert(KEY_DSA, _PATH_HOST_DSA_KEY_FILE, "", NULL); +#ifdef OPENSSL_HAS_ECC sensitive_data.keys[2] = key_load_private_cert(KEY_ECDSA, _PATH_HOST_ECDSA_KEY_FILE, "", NULL); +#endif sensitive_data.keys[3] = key_load_private_cert(KEY_RSA, _PATH_HOST_RSA_KEY_FILE, "", NULL); sensitive_data.keys[4] = key_load_private_type(KEY_DSA, _PATH_HOST_DSA_KEY_FILE, "", NULL, NULL); +#ifdef OPENSSL_HAS_ECC sensitive_data.keys[5] = key_load_private_type(KEY_ECDSA, _PATH_HOST_ECDSA_KEY_FILE, "", NULL, NULL); +#endif sensitive_data.keys[6] = key_load_private_type(KEY_RSA, _PATH_HOST_RSA_KEY_FILE, "", NULL, NULL); PRIV_END; @@ -808,14 +814,18 @@ main(int ac, char **av) sensitive_data.keys[6] == NULL) { sensitive_data.keys[1] = key_load_cert( _PATH_HOST_DSA_KEY_FILE); +#ifdef OPENSSL_HAS_ECC sensitive_data.keys[2] = key_load_cert( _PATH_HOST_ECDSA_KEY_FILE); +#endif sensitive_data.keys[3] = key_load_cert( _PATH_HOST_RSA_KEY_FILE); sensitive_data.keys[4] = key_load_public( _PATH_HOST_DSA_KEY_FILE, NULL); +#ifdef OPENSSL_HAS_ECC sensitive_data.keys[5] = key_load_public( _PATH_HOST_ECDSA_KEY_FILE, NULL); +#endif sensitive_data.keys[6] = key_load_public( _PATH_HOST_RSA_KEY_FILE, NULL); sensitive_data.external_keysign = 1; -- cgit v1.2.3 From 8ccb7392e7ea45d2997afdfa981a450c7db85211 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Fri, 10 Sep 2010 12:28:24 +1000 Subject: - (dtucker) [kex.h key.c packet.h ssh-agent.c ssh.c] A few more ECC ifdefs for missing headers and compiler warnings. --- ChangeLog | 2 ++ kex.h | 2 ++ key.c | 10 ++++++++-- packet.h | 6 ++++++ ssh-agent.c | 3 ++- ssh.c | 13 +++++++++++-- 6 files changed, 31 insertions(+), 5 deletions(-) (limited to 'ssh.c') diff --git a/ChangeLog b/ChangeLog index 742e966c5..c3c18d722 100644 --- a/ChangeLog +++ b/ChangeLog @@ -66,6 +66,8 @@ [kexecdhc.c kexecdhs.c key.c key.h myproposal.h packet.c readconf.c] [ssh-agent.c ssh-ecdsa.c ssh-keygen.c ssh.c] Disable ECDH and ECDSA on platforms that don't have the requisite OpenSSL support. ok dtucker@ + - (dtucker) [kex.h key.c packet.h ssh-agent.c ssh.c] A few more ECC ifdefs + for missing headers and compiler warnings. 20100831 - OpenBSD CVS Sync diff --git a/kex.h b/kex.h index 06914756a..3e312fb44 100644 --- a/kex.h +++ b/kex.h @@ -29,7 +29,9 @@ #include #include #include +#ifdef OPENSSL_HAS_ECC #include +#endif #define KEX_COOKIE_LEN 16 diff --git a/key.c b/key.c index 3cda8f2cb..196092de5 100644 --- a/key.c +++ b/key.c @@ -261,7 +261,9 @@ cert_compare(struct KeyCert *a, struct KeyCert *b) int key_equal_public(const Key *a, const Key *b) { +#ifdef OPENSSL_HAS_ECC BN_CTX *bnctx; +#endif if (a == NULL || b == NULL || key_type_plain(a->type) != key_type_plain(b->type)) @@ -656,9 +658,12 @@ key_read(Key *ret, char **cpp) Key *k; int success = -1; char *cp, *space; - int len, n, type, curve_nid = -1; + int len, n, type; u_int bits; u_char *blob; +#ifdef OPENSSL_HAS_ECC + int curve_nid = -1; +#endif cp = *cpp; @@ -1437,11 +1442,12 @@ Key * key_from_blob(const u_char *blob, u_int blen) { Buffer b; - int rlen, type, nid = -1; + int rlen, type; char *ktype = NULL, *curve = NULL; Key *key = NULL; #ifdef OPENSSL_HAS_ECC EC_POINT *q = NULL; + int nid = -1; #endif #ifdef DEBUG_PK diff --git a/packet.h b/packet.h index 827561cdb..864b82855 100644 --- a/packet.h +++ b/packet.h @@ -19,7 +19,9 @@ #include #include +#ifdef OPENSSL_HAS_ECC #include +#endif void packet_set_connection(int, int); void packet_set_timeout(int, int); @@ -43,7 +45,9 @@ void packet_put_int(u_int value); void packet_put_int64(u_int64_t value); void packet_put_bignum(BIGNUM * value); void packet_put_bignum2(BIGNUM * value); +#ifdef OPENSSL_HAS_ECC void packet_put_ecpoint(const EC_GROUP *, const EC_POINT *); +#endif void packet_put_string(const void *buf, u_int len); void packet_put_cstring(const char *str); void packet_put_raw(const void *buf, u_int len); @@ -61,7 +65,9 @@ u_int packet_get_int(void); u_int64_t packet_get_int64(void); void packet_get_bignum(BIGNUM * value); void packet_get_bignum2(BIGNUM * value); +#ifdef OPENSSL_HAS_ECC void packet_get_ecpoint(const EC_GROUP *, EC_POINT *); +#endif void *packet_get_raw(u_int *length_ptr); void *packet_get_string(u_int *length_ptr); char *packet_get_cstring(u_int *length_ptr); diff --git a/ssh-agent.c b/ssh-agent.c index 8f19fb157..45c8e37a3 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -466,11 +466,12 @@ process_add_identity(SocketEntry *e, int version) Idtab *tab = idtab_lookup(version); Identity *id; int type, success = 0, death = 0, confirm = 0; - char *type_name, *comment, *curve; + char *type_name, *comment; Key *k = NULL; #ifdef OPENSSL_HAS_ECC BIGNUM *exponent; EC_POINT *q; + int *curve; #endif u_char *cert; u_int len; diff --git a/ssh.c b/ssh.c index 3ade744b6..70c71bc00 100644 --- a/ssh.c +++ b/ssh.c @@ -849,10 +849,19 @@ main(int ac, char **av) */ r = snprintf(buf, sizeof buf, "%s%s%s", pw->pw_dir, strcmp(pw->pw_dir, "/") ? "/" : "", _PATH_SSH_USER_DIR); - if (r > 0 && (size_t)r < sizeof(buf) && stat(buf, &st) < 0) + if (r > 0 && (size_t)r < sizeof(buf) && stat(buf, &st) < 0) { +#ifdef WITH_SELINUX + char *scon; + + matchpathcon(buf, 0700, &scon); + setfscreatecon(scon); +#endif if (mkdir(buf, 0700) < 0) error("Could not create directory '%.200s'.", buf); - +#ifdef WITH_SELINUX + setfscreatecon(NULL); +#endif + } /* load options.identity_files */ load_public_identity_files(); -- cgit v1.2.3 From 857b02e37f3bd6d5390711521e2dd021beca3a3c Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 24 Sep 2010 22:02:56 +1000 Subject: - djm@cvs.openbsd.org 2010/09/20 04:41:47 [ssh.c] install a SIGCHLD handler to reap expiried child process; ok markus@ --- ChangeLog | 3 +++ ssh.c | 21 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'ssh.c') diff --git a/ChangeLog b/ChangeLog index f8596098d..f9e0f6c09 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,9 @@ - jmc@cvs.openbsd.org 2010/09/19 21:30:05 [sftp.1] more wacky macro fixing; + - djm@cvs.openbsd.org 2010/09/20 04:41:47 + [ssh.c] + install a SIGCHLD handler to reap expiried child process; ok markus@ 20100910 - (dtucker) [openbsd-compat/port-linux.c] Check is_selinux_enabled for exact diff --git a/ssh.c b/ssh.c index 70c71bc00..20de28a64 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.351 2010/09/02 16:08:39 markus Exp $ */ +/* $OpenBSD: ssh.c,v 1.352 2010/09/20 04:41:47 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -210,6 +211,7 @@ usage(void) static int ssh_session(void); static int ssh_session2(void); static void load_public_identity_files(void); +static void main_sigchld_handler(int); /* from muxclient.c */ void muxclient(const char *); @@ -877,6 +879,7 @@ main(int ac, char **av) tilde_expand_filename(options.user_hostfile2, original_real_uid); signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */ + signal(SIGCHLD, main_sigchld_handler); /* Log into the remote system. Never returns if the login fails. */ ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr, @@ -1545,3 +1548,19 @@ load_public_identity_files(void) bzero(pwdir, strlen(pwdir)); xfree(pwdir); } + +static void +main_sigchld_handler(int sig) +{ + int save_errno = errno; + pid_t pid; + int status; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || + (pid < 0 && errno == EINTR)) + ; + + signal(sig, main_sigchld_handler); + errno = save_errno; +} + -- cgit v1.2.3 From a41ccca643364b3b1b65d7a818577dd35360fa20 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Thu, 7 Oct 2010 22:07:32 +1100 Subject: - djm@cvs.openbsd.org 2010/10/06 06:39:28 [clientloop.c ssh.c sshconnect.c sshconnect.h] kill proxy command on fatal() (we already kill it on clean exit); ok markus@ --- ChangeLog | 4 ++++ clientloop.c | 3 ++- ssh.c | 13 +++---------- sshconnect.c | 16 ++++++++++++++-- sshconnect.h | 3 ++- 5 files changed, 25 insertions(+), 14 deletions(-) (limited to 'ssh.c') diff --git a/ChangeLog b/ChangeLog index f5253a431..69711b9b2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -30,6 +30,10 @@ - djm@cvs.openbsd.org 2010/10/05 05:13:18 [sftp.c sshconnect.c] use default shell /bin/sh if $SHELL is ""; ok markus@ + - djm@cvs.openbsd.org 2010/10/06 06:39:28 + [clientloop.c ssh.c sshconnect.c sshconnect.h] + kill proxy command on fatal() (we already kill it on clean exit); + ok markus@ 20100924 - (djm) OpenBSD CVS Sync diff --git a/clientloop.c b/clientloop.c index de7979366..848aacd4a 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.222 2010/07/19 09:15:12 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.223 2010/10/06 06:39:28 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -2129,5 +2129,6 @@ cleanup_exit(int i) leave_non_blocking(); if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path); + ssh_kill_proxy_command(); _exit(i); } diff --git a/ssh.c b/ssh.c index 20de28a64..7632cf51e 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.352 2010/09/20 04:41:47 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.353 2010/10/06 06:39:28 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -183,9 +183,6 @@ int subsystem_flag = 0; /* # of replies received for global requests */ static int remote_forward_confirms_received = 0; -/* pid of proxycommand child process */ -pid_t proxy_command_pid = 0; - /* mux.c */ extern int muxserver_sock; extern u_int muxclient_command; @@ -921,12 +918,8 @@ main(int ac, char **av) if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path); - /* - * Send SIGHUP to proxy command if used. We don't wait() in - * case it hangs and instead rely on init to reap the child - */ - if (proxy_command_pid > 1) - kill(proxy_command_pid, SIGHUP); + /* Kill ProxyCommand if it is running. */ + ssh_kill_proxy_command(); return exit_status; } diff --git a/sshconnect.c b/sshconnect.c index 6d2f1341c..c849ca393 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.226 2010/10/05 05:13:18 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.227 2010/10/06 06:39:28 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -66,12 +66,13 @@ char *server_version_string = NULL; static int matching_host_key_dns = 0; +static pid_t proxy_command_pid = 0; + /* import */ extern Options options; extern char *__progname; extern uid_t original_real_uid; extern uid_t original_effective_uid; -extern pid_t proxy_command_pid; static int show_other_keys(const char *, Key *); static void warn_changed_key(Key *); @@ -167,6 +168,17 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) return 0; } +void +ssh_kill_proxy_command(void) +{ + /* + * Send SIGHUP to proxy command if used. We don't wait() in + * case it hangs and instead rely on init to reap the child + */ + if (proxy_command_pid > 1) + kill(SIGHUP, proxy_command_pid); +} + /* * Creates a (possibly privileged) socket for use as the ssh connection. */ diff --git a/sshconnect.h b/sshconnect.h index c59a097f4..69163afbc 100644 --- a/sshconnect.h +++ b/sshconnect.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.h,v 1.25 2009/05/27 06:38:16 andreas Exp $ */ +/* $OpenBSD: sshconnect.h,v 1.26 2010/10/06 06:39:28 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -34,6 +34,7 @@ struct Sensitive { int ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int, int *, int, int, const char *); +void ssh_kill_proxy_command(void); void ssh_login(Sensitive *, const char *, struct sockaddr *, struct passwd *, int); -- cgit v1.2.3 From 0dac6fb6b228a96f4ab3717e3d73871595a291a8 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Sat, 20 Nov 2010 15:19:38 +1100 Subject: - djm@cvs.openbsd.org 2010/11/13 23:27:51 [clientloop.c misc.c misc.h packet.c packet.h readconf.c readconf.h] [servconf.c servconf.h session.c ssh.c ssh_config.5 sshd_config.5] allow ssh and sshd to set arbitrary TOS/DSCP/QoS values instead of hardcoding lowdelay/throughput. bz#1733 patch from philipp AT redfish-solutions.com; ok markus@ deraadt@ --- ChangeLog | 7 +++++++ clientloop.c | 5 ++++- misc.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- misc.h | 3 ++- packet.c | 9 +++++---- packet.h | 4 ++-- readconf.c | 30 ++++++++++++++++++++++++++++-- readconf.h | 4 +++- servconf.c | 38 +++++++++++++++++++++++++++++++++++--- servconf.h | 4 +++- session.c | 8 +++++--- ssh.c | 7 +++---- ssh_config.5 | 41 +++++++++++++++++++++++++++++++++++++++-- sshd_config.5 | 41 +++++++++++++++++++++++++++++++++++++++-- 14 files changed, 230 insertions(+), 27 deletions(-) (limited to 'ssh.c') diff --git a/ChangeLog b/ChangeLog index 0f9ed8524..1ddba8a9c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,13 @@ [kexdhc.c kexdhs.c kexgexc.c kexgexs.c key.c moduli.c] use only libcrypto APIs that are retained with OPENSSL_NO_DEPRECATED. these have been around for years by this time. ok markus + - djm@cvs.openbsd.org 2010/11/13 23:27:51 + [clientloop.c misc.c misc.h packet.c packet.h readconf.c readconf.h] + [servconf.c servconf.h session.c ssh.c ssh_config.5 sshd_config.5] + allow ssh and sshd to set arbitrary TOS/DSCP/QoS values instead of + hardcoding lowdelay/throughput. + + bz#1733 patch from philipp AT redfish-solutions.com; ok markus@ deraadt@ 20101111 - (djm) [servconf.c ssh-add.c ssh-keygen.c] don't look for ECDSA keys on diff --git a/clientloop.c b/clientloop.c index 848aacd4a..52dcb4c04 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.223 2010/10/06 06:39:28 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.224 2010/11/13 23:27:50 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1973,6 +1973,9 @@ client_session2_setup(int id, int want_tty, int want_subsystem, if ((c = channel_lookup(id)) == NULL) fatal("client_session2_setup: channel %d: unknown channel", id); + packet_set_interactive(want_tty, + options.ip_qos_interactive, options.ip_qos_bulk); + if (want_tty) { struct winsize ws; diff --git a/misc.c b/misc.c index ff09becf9..b88f5aaa8 100644 --- a/misc.c +++ b/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.82 2010/09/24 13:33:00 matthew Exp $ */ +/* $OpenBSD: misc.c,v 1.83 2010/11/13 23:27:50 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2005,2006 Damien Miller. All rights reserved. @@ -38,6 +38,8 @@ #include #include +#include +#include #include #include @@ -913,6 +915,58 @@ bandwidth_limit(struct bwlimit *bw, size_t read_len) bw->lamt = 0; gettimeofday(&bw->bwstart, NULL); } + +static const struct { + const char *name; + int value; +} ipqos[] = { + { "af11", IPTOS_DSCP_AF11 }, + { "af12", IPTOS_DSCP_AF12 }, + { "af13", IPTOS_DSCP_AF13 }, + { "af14", IPTOS_DSCP_AF21 }, + { "af22", IPTOS_DSCP_AF22 }, + { "af23", IPTOS_DSCP_AF23 }, + { "af31", IPTOS_DSCP_AF31 }, + { "af32", IPTOS_DSCP_AF32 }, + { "af33", IPTOS_DSCP_AF33 }, + { "af41", IPTOS_DSCP_AF41 }, + { "af42", IPTOS_DSCP_AF42 }, + { "af43", IPTOS_DSCP_AF43 }, + { "cs0", IPTOS_DSCP_CS0 }, + { "cs1", IPTOS_DSCP_CS1 }, + { "cs2", IPTOS_DSCP_CS2 }, + { "cs3", IPTOS_DSCP_CS3 }, + { "cs4", IPTOS_DSCP_CS4 }, + { "cs5", IPTOS_DSCP_CS5 }, + { "cs6", IPTOS_DSCP_CS6 }, + { "cs7", IPTOS_DSCP_CS7 }, + { "ef", IPTOS_DSCP_EF }, + { "lowdelay", IPTOS_LOWDELAY }, + { "throughput", IPTOS_THROUGHPUT }, + { "reliability", IPTOS_RELIABILITY }, + { NULL, -1 } +}; + +int +parse_ipqos(const char *cp) +{ + u_int i; + char *ep; + long val; + + if (cp == NULL) + return -1; + for (i = 0; ipqos[i].name != NULL; i++) { + if (strcasecmp(cp, ipqos[i].name) == 0) + return ipqos[i].value; + } + /* Try parsing as an integer */ + val = strtol(cp, &ep, 0); + if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255) + return -1; + return val; +} + void sock_set_v6only(int s) { diff --git a/misc.h b/misc.h index 1368931a0..a81ace309 100644 --- a/misc.h +++ b/misc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.h,v 1.45 2010/09/24 13:33:00 matthew Exp $ */ +/* $OpenBSD: misc.h,v 1.46 2010/11/13 23:27:50 djm Exp $ */ /* * Author: Tatu Ylonen @@ -88,6 +88,7 @@ struct bwlimit { void bandwidth_limit_init(struct bwlimit *, u_int64_t, size_t); void bandwidth_limit(struct bwlimit *, size_t); +int parse_ipqos(const char *); /* readpass.c */ diff --git a/packet.c b/packet.c index 698920013..012c39a3c 100644 --- a/packet.c +++ b/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.171 2010/11/05 02:46:47 djm Exp $ */ +/* $OpenBSD: packet.c,v 1.172 2010/11/13 23:27:50 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1750,7 +1750,7 @@ packet_not_very_much_data_to_write(void) } static void -packet_set_tos(int interactive) +packet_set_tos(int tos) { #if defined(IP_TOS) && !defined(IP_TOS_IS_BROKEN) int tos = interactive ? IPTOS_LOWDELAY : IPTOS_THROUGHPUT; @@ -1758,6 +1758,7 @@ packet_set_tos(int interactive) if (!packet_connection_is_on_socket() || !packet_connection_is_ipv4()) return; + debug3("%s: set IP_TOS 0x%02x", __func__, tos); if (setsockopt(active_state->connection_in, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) error("setsockopt IP_TOS %d: %.100s:", @@ -1768,7 +1769,7 @@ packet_set_tos(int interactive) /* Informs that the current session is interactive. Sets IP flags for that. */ void -packet_set_interactive(int interactive) +packet_set_interactive(int interactive, int qos_interactive, int qos_bulk) { if (active_state->set_interactive_called) return; @@ -1781,7 +1782,7 @@ packet_set_interactive(int interactive) if (!packet_connection_is_on_socket()) return; set_nodelay(active_state->connection_in); - packet_set_tos(interactive); + packet_set_tos(interactive ? qos_interactive : qos_bulk); } /* Returns true if the current connection is interactive. */ diff --git a/packet.h b/packet.h index 864b82855..d516aae8d 100644 --- a/packet.h +++ b/packet.h @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.h,v 1.54 2010/08/31 11:54:45 djm Exp $ */ +/* $OpenBSD: packet.h,v 1.55 2010/11/13 23:27:50 djm Exp $ */ /* * Author: Tatu Ylonen @@ -34,7 +34,7 @@ u_int packet_get_encryption_key(u_char *); void packet_set_protocol_flags(u_int); u_int packet_get_protocol_flags(void); void packet_start_compression(int); -void packet_set_interactive(int); +void packet_set_interactive(int, int, int); int packet_is_interactive(void); void packet_set_server(void); void packet_set_authenticated(void); diff --git a/readconf.c b/readconf.c index da7efd193..eb4a8b9ee 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.189 2010/09/22 05:01:29 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.190 2010/11/13 23:27:50 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include @@ -132,7 +134,7 @@ typedef enum { oHashKnownHosts, oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, - oKexAlgorithms, + oKexAlgorithms, oIPQoS, oDeprecated, oUnsupported } OpCodes; @@ -242,6 +244,7 @@ static struct { { "zeroknowledgepasswordauthentication", oUnsupported }, #endif { "kexalgorithms", oKexAlgorithms }, + { "ipqos", oIPQoS }, { NULL, oBadOption } }; @@ -973,6 +976,23 @@ parse_int: intptr = &options->visual_host_key; goto parse_flag; + case oIPQoS: + arg = strdelim(&s); + if ((value = parse_ipqos(arg)) == -1) + fatal("%s line %d: Bad IPQoS value: %s", + filename, linenum, arg); + arg = strdelim(&s); + if (arg == NULL) + value2 = value; + else if ((value2 = parse_ipqos(arg)) == -1) + fatal("%s line %d: Bad IPQoS value: %s", + filename, linenum, arg); + if (*activep) { + options->ip_qos_interactive = value; + options->ip_qos_bulk = value2; + } + break; + case oUseRoaming: intptr = &options->use_roaming; goto parse_flag; @@ -1135,6 +1155,8 @@ initialize_options(Options * options) options->use_roaming = -1; options->visual_host_key = -1; options->zero_knowledge_password_authentication = -1; + options->ip_qos_interactive = -1; + options->ip_qos_bulk = -1; } /* @@ -1289,6 +1311,10 @@ fill_default_options(Options * options) options->visual_host_key = 0; if (options->zero_knowledge_password_authentication == -1) options->zero_knowledge_password_authentication = 0; + if (options->ip_qos_interactive == -1) + options->ip_qos_interactive = IPTOS_LOWDELAY; + if (options->ip_qos_bulk == -1) + options->ip_qos_bulk = IPTOS_THROUGHPUT; /* options->local_command should not be set by default */ /* options->proxy_command should not be set by default */ /* options->user will be set in the main program if appropriate */ diff --git a/readconf.h b/readconf.h index ae61466df..ee160dfe7 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.87 2010/09/22 05:01:29 djm Exp $ */ +/* $OpenBSD: readconf.h,v 1.88 2010/11/13 23:27:50 djm Exp $ */ /* * Author: Tatu Ylonen @@ -59,6 +59,8 @@ typedef struct { int compression_level; /* Compression level 1 (fast) to 9 * (best). */ int tcp_keep_alive; /* Set SO_KEEPALIVE. */ + int ip_qos_interactive; /* IP ToS/DSCP/class for interactive */ + int ip_qos_bulk; /* IP ToS/DSCP/class for bulk traffic */ LogLevel log_level; /* Level for logging. */ int port; /* Port to connect. */ diff --git a/servconf.c b/servconf.c index 4e5fd2f04..e2f20a3d1 100644 --- a/servconf.c +++ b/servconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.c,v 1.212 2010/09/30 11:04:51 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.213 2010/11/13 23:27:50 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -15,6 +15,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -133,6 +137,8 @@ initialize_server_options(ServerOptions *options) options->revoked_keys_file = NULL; options->trusted_user_ca_keys = NULL; options->authorized_principals_file = NULL; + options->ip_qos_interactive = -1; + options->ip_qos_bulk = -1; } void @@ -271,6 +277,10 @@ fill_default_server_options(ServerOptions *options) options->permit_tun = SSH_TUNMODE_NO; if (options->zero_knowledge_password_authentication == -1) options->zero_knowledge_password_authentication = 0; + if (options->ip_qos_interactive == -1) + options->ip_qos_interactive = IPTOS_LOWDELAY; + if (options->ip_qos_bulk == -1) + options->ip_qos_bulk = IPTOS_THROUGHPUT; /* Turn privilege separation on by default */ if (use_privsep == -1) @@ -317,7 +327,7 @@ typedef enum { sUsePrivilegeSeparation, sAllowAgentForwarding, sZeroKnowledgePasswordAuthentication, sHostCertificate, sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, - sKexAlgorithms, + sKexAlgorithms, sIPQoS, sDeprecated, sUnsupported } ServerOpCodes; @@ -441,6 +451,7 @@ static struct { { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, + { "ipqos", sIPQoS, SSHCFG_ALL }, { NULL, sBadOption, 0 } }; @@ -670,7 +681,7 @@ process_server_config_line(ServerOptions *options, char *line, const char *host, const char *address) { char *cp, **charptr, *arg, *p; - int cmdline = 0, *intptr, value, n; + int cmdline = 0, *intptr, value, value2, n; SyslogFacility *log_facility_ptr; LogLevel *log_level_ptr; ServerOpCodes opcode; @@ -1370,6 +1381,23 @@ process_server_config_line(ServerOptions *options, char *line, charptr = &options->revoked_keys_file; goto parse_filename; + case sIPQoS: + arg = strdelim(&cp); + if ((value = parse_ipqos(arg)) == -1) + fatal("%s line %d: Bad IPQoS value: %s", + filename, linenum, arg); + arg = strdelim(&cp); + if (arg == NULL) + value2 = value; + else if ((value2 = parse_ipqos(arg)) == -1) + fatal("%s line %d: Bad IPQoS value: %s", + filename, linenum, arg); + if (*activep) { + options->ip_qos_interactive = value; + options->ip_qos_bulk = value2; + } + break; + case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); @@ -1480,6 +1508,8 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) M_CP_INTOPT(x11_use_localhost); M_CP_INTOPT(max_sessions); M_CP_INTOPT(max_authtries); + M_CP_INTOPT(ip_qos_interactive); + M_CP_INTOPT(ip_qos_bulk); M_CP_STROPT(banner); if (preauth) @@ -1745,5 +1775,7 @@ dump_config(ServerOptions *o) } dump_cfg_string(sPermitTunnel, s); + printf("ipqos 0x%02x 0x%02x\n", o->ip_qos_interactive, o->ip_qos_bulk); + channel_print_adm_permitted_opens(); } diff --git a/servconf.h b/servconf.h index ad13f2edd..5a058a416 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.94 2010/09/22 05:01:29 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.95 2010/11/13 23:27:50 djm Exp $ */ /* * Author: Tatu Ylonen @@ -70,6 +70,8 @@ typedef struct { char *xauth_location; /* Location of xauth program */ int strict_modes; /* If true, require string home dir modes. */ int tcp_keep_alive; /* If true, set SO_KEEPALIVE. */ + int ip_qos_interactive; /* IP ToS/DSCP/class for interactive */ + int ip_qos_bulk; /* IP ToS/DSCP/class for bulk traffic */ char *ciphers; /* Supported SSH2 ciphers. */ char *macs; /* Supported SSH2 macs. */ char *kex_algorithms; /* SSH2 kex methods in order of preference. */ diff --git a/session.c b/session.c index 58e681282..8c6022bf6 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.256 2010/06/25 07:20:04 djm Exp $ */ +/* $OpenBSD: session.c,v 1.257 2010/11/13 23:27:50 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -585,7 +585,8 @@ do_exec_no_pty(Session *s, const char *command) s->pid = pid; /* Set interactive/non-interactive mode. */ - packet_set_interactive(s->display != NULL); + packet_set_interactive(s->display != NULL, + options.ip_qos_interactive, options.ip_qos_bulk); /* * Clear loginmsg, since it's the child's responsibility to display @@ -739,7 +740,8 @@ do_exec_pty(Session *s, const char *command) /* Enter interactive session. */ s->ptymaster = ptymaster; - packet_set_interactive(1); + packet_set_interactive(1, + options.ip_qos_interactive, options.ip_qos_bulk); if (compat20) { session_set_fds(s, ptyfd, fdout, -1, 1, 1); } else { diff --git a/ssh.c b/ssh.c index 7632cf51e..f413f8a5c 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.353 2010/10/06 06:39:28 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.354 2010/11/13 23:27:50 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1224,7 +1224,8 @@ ssh_session(void) } } /* Tell the packet module whether this is an interactive session. */ - packet_set_interactive(interactive); + packet_set_interactive(interactive, + options.ip_qos_interactive, options.ip_qos_bulk); /* Request authentication agent forwarding if appropriate. */ check_agent_present(); @@ -1322,8 +1323,6 @@ ssh_session2_setup(int id, int success, void *arg) client_session2_setup(id, tty_flag, subsystem_flag, getenv("TERM"), NULL, fileno(stdin), &command, environ); - - packet_set_interactive(interactive); } /* open new channel for a session */ diff --git a/ssh_config.5 b/ssh_config.5 index dd39bfafb..9e82fa864 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.142 2010/10/28 18:33:28 jmc Exp $ -.Dd $Mdocdate: October 28 2010 $ +.\" $OpenBSD: ssh_config.5,v 1.143 2010/11/13 23:27:50 djm Exp $ +.Dd $Mdocdate: November 13 2010 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -626,6 +626,43 @@ escape characters: It is possible to have multiple identity files specified in configuration files; all these identities will be tried in sequence. +.It Cm IPQoS +Specifies the IPv4 type-of-service or DSCP class for connections. +Accepted values are +.Dq af11 , +.Dq af12 , +.Dq af13 , +.Dq af14 , +.Dq af22 , +.Dq af23 , +.Dq af31 , +.Dq af32 , +.Dq af33 , +.Dq af41 , +.Dq af42 , +.Dq af43 , +.Dq cs0 , +.Dq cs1 , +.Dq cs2 , +.Dq cs3 , +.Dq cs4 , +.Dq cs5 , +.Dq cs6 , +.Dq cs7 , +.Dq ef , +.Dq lowdelay , +.Dq throughput , +.Dq reliability , +or a numeric value. +This option may take one or two arguments. +If one argument is specified, it is used as the packet class unconditionally. +If two values are specified, the first is automatically selected for +interactive sessions and the second for non-interactive sessions. +The default is +.Dq lowdelay +for interactive sessions and +.Dq throughput +for non-interactive sessions. .It Cm KbdInteractiveAuthentication Specifies whether to use keyboard-interactive authentication. The argument to this keyword must be diff --git a/sshd_config.5 b/sshd_config.5 index d0c02ca7c..60808d233 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.128 2010/10/28 18:33:28 jmc Exp $ -.Dd $Mdocdate: October 28 2010 $ +.\" $OpenBSD: sshd_config.5,v 1.129 2010/11/13 23:27:51 djm Exp $ +.Dd $Mdocdate: November 13 2010 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -512,6 +512,43 @@ or .Cm HostbasedAuthentication . The default is .Dq no . +.It Cm IPQoS +Specifies the IPv4 type-of-service or DSCP class for the connection. +Accepted values are +.Dq af11 , +.Dq af12 , +.Dq af13 , +.Dq af14 , +.Dq af22 , +.Dq af23 , +.Dq af31 , +.Dq af32 , +.Dq af33 , +.Dq af41 , +.Dq af42 , +.Dq af43 , +.Dq cs0 , +.Dq cs1 , +.Dq cs2 , +.Dq cs3 , +.Dq cs4 , +.Dq cs5 , +.Dq cs6 , +.Dq cs7 , +.Dq ef , +.Dq lowdelay , +.Dq throughput , +.Dq reliability , +or a numeric value. +This option may take one or two arguments. +If one argument is specified, it is used as the packet class unconditionally. +If two values are specified, the first is automatically selected for +interactive sessions and the second for non-interactive sessions. +The default is +.Dq lowdelay +for interactive sessions and +.Dq throughput +for non-interactive sessions. .It Cm KerberosAuthentication Specifies whether the password provided by the user for .Cm PasswordAuthentication -- cgit v1.2.3 From d925dcd8a5d1a3070061006788352bed93260582 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Wed, 1 Dec 2010 12:21:51 +1100 Subject: - djm@cvs.openbsd.org 2010/11/29 23:45:51 [auth.c hostfile.c hostfile.h ssh.c ssh_config.5 sshconnect.c] [sshconnect.h sshconnect2.c] automatically order the hostkeys requested by the client based on which hostkeys are already recorded in known_hosts. This avoids hostkey warnings when connecting to servers with new ECDSA keys that are preferred by default; with markus@ --- ChangeLog | 7 ++ auth.c | 30 +++--- hostfile.c | 301 ++++++++++++++++++++++++++++++++++------------------------ hostfile.h | 30 ++++-- ssh.c | 4 +- ssh_config.5 | 7 +- sshconnect.c | 291 +++++++++++++++++++++++++++++--------------------------- sshconnect.h | 11 ++- sshconnect2.c | 62 +++++++++++- 9 files changed, 452 insertions(+), 291 deletions(-) (limited to 'ssh.c') diff --git a/ChangeLog b/ChangeLog index 44e45eb8a..6ee7c0014 100644 --- a/ChangeLog +++ b/ChangeLog @@ -38,6 +38,13 @@ [authfile.c] correctly load comment for encrypted rsa1 keys; report/fix Joachim Schipper; ok djm@ + - djm@cvs.openbsd.org 2010/11/29 23:45:51 + [auth.c hostfile.c hostfile.h ssh.c ssh_config.5 sshconnect.c] + [sshconnect.h sshconnect2.c] + automatically order the hostkeys requested by the client based on + which hostkeys are already recorded in known_hosts. This avoids + hostkey warnings when connecting to servers with new ECDSA keys + that are preferred by default; with markus@ 20101124 - (dtucker) [platform.c session.c] Move the getluid call out of session.c and diff --git a/auth.c b/auth.c index 6fe1b21a4..33680b91b 100644 --- a/auth.c +++ b/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.90 2010/11/23 02:35:50 djm Exp $ */ +/* $OpenBSD: auth.c,v 1.91 2010/11/29 23:45:51 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -379,16 +379,15 @@ HostStatus check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, const char *sysfile, const char *userfile) { - Key *found; char *user_hostfile; struct stat st; HostStatus host_status; + struct hostkeys *hostkeys; + const struct hostkey_entry *found; - /* Check if we know the host and its host key. */ - found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); - host_status = check_host_in_hostfile(sysfile, host, key, found, NULL); - - if (host_status != HOST_OK && userfile != NULL) { + hostkeys = init_hostkeys(); + load_hostkeys(hostkeys, host, sysfile); + if (userfile != NULL) { user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); if (options.strict_modes && (stat(user_hostfile, &st) == 0) && @@ -401,16 +400,23 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, user_hostfile); } else { temporarily_use_uid(pw); - host_status = check_host_in_hostfile(user_hostfile, - host, key, found, NULL); + load_hostkeys(hostkeys, host, user_hostfile); restore_uid(); } xfree(user_hostfile); } - key_free(found); + host_status = check_key_in_hostkeys(hostkeys, key, &found); + if (host_status == HOST_REVOKED) + error("WARNING: revoked key for %s attempted authentication", + found->host); + else if (host_status == HOST_OK) + debug("%s: key for %s found at %s:%ld", __func__, + found->host, found->file, found->line); + else + debug("%s: key for host %s not found", __func__, host); + + free_hostkeys(hostkeys); - debug2("check_key_in_hostfiles: key %s for %s", host_status == HOST_OK ? - "ok" : "not found", host); return host_status; } diff --git a/hostfile.c b/hostfile.c index afab6dad1..9145529cb 100644 --- a/hostfile.c +++ b/hostfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.c,v 1.48 2010/03/04 10:36:03 djm Exp $ */ +/* $OpenBSD: hostfile.c,v 1.49 2010/11/29 23:45:51 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -56,6 +56,12 @@ #include "key.h" #include "hostfile.h" #include "log.h" +#include "misc.h" + +struct hostkeys { + struct hostkey_entry *entries; + u_int num_entries; +}; static int extract_salt(const char *s, u_int l, char *salt, size_t salt_len) @@ -164,26 +170,28 @@ hostfile_read_key(char **cpp, u_int *bitsp, Key *ret) /* Return results. */ *cpp = cp; - *bitsp = key_size(ret); + if (bitsp != NULL) + *bitsp = key_size(ret); return 1; } static int -hostfile_check_key(int bits, const Key *key, const char *host, const char *filename, int linenum) +hostfile_check_key(int bits, const Key *key, const char *host, + const char *filename, u_long linenum) { if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL) return 1; if (bits != BN_num_bits(key->rsa->n)) { - logit("Warning: %s, line %d: keysize mismatch for host %s: " + logit("Warning: %s, line %lu: keysize mismatch for host %s: " "actual %d vs. announced %d.", filename, linenum, host, BN_num_bits(key->rsa->n), bits); - logit("Warning: replace %d with %d in %s, line %d.", + logit("Warning: replace %d with %d in %s, line %lu.", bits, BN_num_bits(key->rsa->n), filename, linenum); } return 1; } -static enum { MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA } +static HostkeyMarker check_markers(char **cpp) { char marker[32], *sp, *cp = *cpp; @@ -218,49 +226,32 @@ check_markers(char **cpp) return ret; } -/* - * Checks whether the given host (which must be in all lowercase) is already - * in the list of our known hosts. Returns HOST_OK if the host is known and - * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED - * if the host is known but used to have a different host key. - * - * If no 'key' has been specified and a key of type 'keytype' is known - * for the specified host, then HOST_FOUND is returned. - */ +struct hostkeys * +init_hostkeys(void) +{ + struct hostkeys *ret = xcalloc(1, sizeof(*ret)); -static HostStatus -check_host_in_hostfile_by_key_or_type(const char *filename, - const char *host, const Key *key, int keytype, Key *found, - int want_revocation, int *numret) + ret->entries = NULL; + return ret; +} + +void +load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) { FILE *f; char line[8192]; - int want, have, linenum = 0, want_cert = key_is_cert(key); - u_int kbits; + u_long linenum = 0, num_loaded = 0; char *cp, *cp2, *hashed_host; - HostStatus end_return; - - debug3("check_host_in_hostfile: host %s filename %s", host, filename); - - if (want_revocation && (key == NULL || keytype != 0 || found != NULL)) - fatal("%s: invalid arguments", __func__); - - /* Open the file containing the list of known hosts. */ - f = fopen(filename, "r"); - if (!f) - return HOST_NEW; - - /* - * Return value when the loop terminates. This is set to - * HOST_CHANGED if we have seen a different key for the host and have - * not found the proper one. - */ - end_return = HOST_NEW; - - /* Go through the file. */ - while (fgets(line, sizeof(line), f)) { + HostkeyMarker marker; + Key *key; + int kbits; + + if ((f = fopen(path, "r")) == NULL) + return; + debug3("%s: loading entries for host \"%.100s\" from file \"%s\"", + __func__, host, path); + while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) { cp = line; - linenum++; /* Skip any leading whitespace, comments and empty lines. */ for (; *cp == ' ' || *cp == '\t'; cp++) @@ -268,19 +259,11 @@ check_host_in_hostfile_by_key_or_type(const char *filename, if (!*cp || *cp == '#' || *cp == '\n') continue; - if (want_revocation) - want = MRK_REVOKE; - else if (want_cert) - want = MRK_CA; - else - want = MRK_NONE; - - if ((have = check_markers(&cp)) == MRK_ERROR) { - verbose("%s: invalid marker at %s:%d", - __func__, filename, linenum); - continue; - } else if (want != have) + if ((marker = check_markers(&cp)) == MRK_ERROR) { + verbose("%s: invalid marker at %s:%lu", + __func__, path, linenum); continue; + } /* Find the end of the host name portion. */ for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) @@ -292,8 +275,8 @@ check_host_in_hostfile_by_key_or_type(const char *filename, continue; hashed_host = host_hash(host, cp, (u_int) (cp2 - cp)); if (hashed_host == NULL) { - debug("Invalid hashed host line %d of %s", - linenum, filename); + debug("Invalid hashed host line %lu of %s", + linenum, path); continue; } if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0) @@ -303,98 +286,166 @@ check_host_in_hostfile_by_key_or_type(const char *filename, /* Got a match. Skip host name. */ cp = cp2; - if (want_revocation) - found = key_new(KEY_UNSPEC); - /* * Extract the key from the line. This will skip any leading * whitespace. Ignore badly formatted lines. */ - if (!hostfile_read_key(&cp, &kbits, found)) + key = key_new(KEY_UNSPEC); + if (!hostfile_read_key(&cp, &kbits, key)) { + key_free(key); + key = key_new(KEY_RSA1); + if (!hostfile_read_key(&cp, &kbits, key)) { + key_free(key); + continue; + } + } + if (!hostfile_check_key(kbits, key, host, path, linenum)) continue; - if (numret != NULL) - *numret = linenum; + debug3("%s: found %skey type %s in file %s:%lu", __func__, + marker == MRK_NONE ? "" : + (marker == MRK_CA ? "ca " : "revoked "), + key_type(key), path, linenum); + hostkeys->entries = xrealloc(hostkeys->entries, + hostkeys->num_entries + 1, sizeof(*hostkeys->entries)); + hostkeys->entries[hostkeys->num_entries].host = xstrdup(host); + hostkeys->entries[hostkeys->num_entries].file = xstrdup(path); + hostkeys->entries[hostkeys->num_entries].line = linenum; + hostkeys->entries[hostkeys->num_entries].key = key; + hostkeys->entries[hostkeys->num_entries].marker = marker; + hostkeys->num_entries++; + num_loaded++; + } + debug3("%s: loaded %lu keys", __func__, num_loaded); + return; +} - if (key == NULL) { - /* we found a key of the requested type */ - if (found->type == keytype) { - fclose(f); - return HOST_FOUND; - } - continue; - } +void +free_hostkeys(struct hostkeys *hostkeys) +{ + u_int i; + + for (i = 0; i < hostkeys->num_entries; i++) { + xfree(hostkeys->entries[i].host); + xfree(hostkeys->entries[i].file); + key_free(hostkeys->entries[i].key); + bzero(hostkeys->entries + i, sizeof(*hostkeys->entries)); + } + if (hostkeys->entries != NULL) + xfree(hostkeys->entries); + hostkeys->entries = NULL; + hostkeys->num_entries = 0; + xfree(hostkeys); +} - if (!hostfile_check_key(kbits, found, host, filename, linenum)) +static int +check_key_not_revoked(struct hostkeys *hostkeys, Key *k) +{ + int is_cert = key_is_cert(k); + u_int i; + + for (i = 0; i < hostkeys->num_entries; i++) { + if (hostkeys->entries[i].marker != MRK_REVOKE) continue; + if (key_equal_public(k, hostkeys->entries[i].key)) + return -1; + if (is_cert && + key_equal_public(k->cert->signature_key, + hostkeys->entries[i].key)) + return -1; + } + return 0; +} - if (want_revocation) { - if (key_is_cert(key) && - key_equal_public(key->cert->signature_key, found)) { - verbose("check_host_in_hostfile: revoked CA " - "line %d", linenum); - key_free(found); - return HOST_REVOKED; - } - if (key_equal_public(key, found)) { - verbose("check_host_in_hostfile: revoked key " - "line %d", linenum); - key_free(found); - return HOST_REVOKED; - } - key_free(found); +/* + * Match keys against a specified key, or look one up by key type. + * + * If looking for a keytype (key == NULL) and one is found then return + * HOST_FOUND, otherwise HOST_NEW. + * + * If looking for a key (key != NULL): + * 1. If the key is a cert and a matching CA is found, return HOST_OK + * 2. If the key is not a cert and a matching key is found, return HOST_OK + * 3. If no key matches but a key with a different type is found, then + * return HOST_CHANGED + * 4. If no matching keys are found, then return HOST_NEW. + * + * Finally, check any found key is not revoked. + */ +static HostStatus +check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, + Key *k, int keytype, const struct hostkey_entry **found) +{ + u_int i; + HostStatus end_return = HOST_NEW; + int want_cert = key_is_cert(k); + HostkeyMarker want_marker = want_cert ? MRK_CA : MRK_NONE; + int proto = (k ? k->type : keytype) == KEY_RSA1 ? 1 : 2; + + if (found != NULL) + *found = NULL; + + for (i = 0; i < hostkeys->num_entries; i++) { + if (proto == 1 && hostkeys->entries[i].key->type != KEY_RSA1) + continue; + if (proto == 2 && hostkeys->entries[i].key->type == KEY_RSA1) continue; + if (hostkeys->entries[i].marker != want_marker) + continue; + if (k == NULL) { + if (hostkeys->entries[i].key->type != keytype) + continue; + end_return = HOST_FOUND; + if (found != NULL) + *found = hostkeys->entries + i; + k = hostkeys->entries[i].key; + break; } - - /* Check if the current key is the same as the given key. */ - if (want_cert && key_equal(key->cert->signature_key, found)) { - /* Found CA cert for key */ - debug3("check_host_in_hostfile: CA match line %d", - linenum); - fclose(f); - return HOST_OK; - } else if (!want_cert && key_equal(key, found)) { - /* Found identical key */ - debug3("check_host_in_hostfile: match line %d", linenum); - fclose(f); - return HOST_OK; + if (want_cert) { + if (key_equal_public(k->cert->signature_key, + hostkeys->entries[i].key)) { + /* A matching CA exists */ + end_return = HOST_OK; + if (found != NULL) + *found = hostkeys->entries + i; + break; + } + } else { + if (key_equal(k, hostkeys->entries[i].key)) { + end_return = HOST_OK; + if (found != NULL) + *found = hostkeys->entries + i; + break; + } + /* A non-maching key exists */ + end_return = HOST_CHANGED; + if (found != NULL) + *found = hostkeys->entries + i; } - /* - * They do not match. We will continue to go through the - * file; however, we note that we will not return that it is - * new. - */ - end_return = HOST_CHANGED; } - /* Clear variables and close the file. */ - fclose(f); - - /* - * Return either HOST_NEW or HOST_CHANGED, depending on whether we - * saw a different key for the host. - */ + if (check_key_not_revoked(hostkeys, k) != 0) { + end_return = HOST_REVOKED; + if (found != NULL) + *found = NULL; + } return end_return; } - + HostStatus -check_host_in_hostfile(const char *filename, const char *host, const Key *key, - Key *found, int *numret) +check_key_in_hostkeys(struct hostkeys *hostkeys, Key *key, + const struct hostkey_entry **found) { if (key == NULL) fatal("no key to look up"); - if (check_host_in_hostfile_by_key_or_type(filename, host, - key, 0, NULL, 1, NULL) == HOST_REVOKED) - return HOST_REVOKED; - return check_host_in_hostfile_by_key_or_type(filename, host, key, 0, - found, 0, numret); + return check_hostkeys_by_key_or_type(hostkeys, key, 0, found); } int -lookup_key_in_hostfile_by_type(const char *filename, const char *host, - int keytype, Key *found, int *numret) +lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype, + const struct hostkey_entry **found) { - return (check_host_in_hostfile_by_key_or_type(filename, host, NULL, - keytype, found, 0, numret) == HOST_FOUND); + return (check_hostkeys_by_key_or_type(hostkeys, NULL, keytype, + found) == HOST_FOUND); } /* diff --git a/hostfile.h b/hostfile.h index 1d460c1a9..d84d422ff 100644 --- a/hostfile.h +++ b/hostfile.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.h,v 1.18 2010/03/04 10:36:03 djm Exp $ */ +/* $OpenBSD: hostfile.h,v 1.19 2010/11/29 23:45:51 djm Exp $ */ /* * Author: Tatu Ylonen @@ -18,12 +18,30 @@ typedef enum { HOST_OK, HOST_NEW, HOST_CHANGED, HOST_REVOKED, HOST_FOUND } HostStatus; +typedef enum { + MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA +} HostkeyMarker; + +struct hostkey_entry { + char *host; + char *file; + u_long line; + Key *key; + HostkeyMarker marker; +}; +struct hostkeys; + +struct hostkeys *init_hostkeys(void); +void load_hostkeys(struct hostkeys *, const char *, const char *); +void free_hostkeys(struct hostkeys *); + +HostStatus check_key_in_hostkeys(struct hostkeys *, Key *, + const struct hostkey_entry **); +int lookup_key_in_hostkeys_by_type(struct hostkeys *, int, + const struct hostkey_entry **); + int hostfile_read_key(char **, u_int *, Key *); -HostStatus check_host_in_hostfile(const char *, const char *, - const Key *, Key *, int *); -int add_host_to_hostfile(const char *, const char *, const Key *, int); -int lookup_key_in_hostfile_by_type(const char *, const char *, - int, Key *, int *); +int add_host_to_hostfile(const char *, const char *, const Key *, int); #define HASH_MAGIC "|1|" #define HASH_DELIM '|' diff --git a/ssh.c b/ssh.c index f413f8a5c..ec690ae38 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.354 2010/11/13 23:27:50 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.355 2010/11/29 23:45:51 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -880,7 +880,7 @@ main(int ac, char **av) /* Log into the remote system. Never returns if the login fails. */ ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr, - pw, timeout_ms); + options.port, pw, timeout_ms); if (packet_connection_is_on_socket()) { verbose("Authenticated to %s ([%s]:%d).", host, diff --git a/ssh_config.5 b/ssh_config.5 index a51a37dde..5c6673de3 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.144 2010/11/15 07:40:14 jmc Exp $ -.Dd $Mdocdate: November 15 2010 $ +.\" $OpenBSD: ssh_config.5,v 1.145 2010/11/29 23:45:51 djm Exp $ +.Dd $Mdocdate: November 29 2010 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -555,6 +555,9 @@ ssh-rsa-cert-v00@openssh.com,ssh-dss-cert-v00@openssh.com, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ssh-rsa,ssh-dss .Ed +.Pp +If hostkeys are known for the destination host then this default is modified +to prefer their algorithms. .It Cm HostKeyAlias Specifies an alias that should be used instead of the real host name when looking up or saving the host key diff --git a/sshconnect.c b/sshconnect.c index 78068c602..064bb74b3 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.228 2010/10/06 21:10:21 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.229 2010/11/29 23:45:51 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -75,7 +75,7 @@ extern char *__progname; extern uid_t original_real_uid; extern uid_t original_effective_uid; -static int show_other_keys(const char *, Key *); +static int show_other_keys(struct hostkeys *, Key *); static void warn_changed_key(Key *); /* @@ -607,6 +607,79 @@ check_host_cert(const char *host, const Key *host_key) return 1; } +static int +sockaddr_is_local(struct sockaddr *hostaddr) +{ + switch (hostaddr->sa_family) { + case AF_INET: + return (ntohl(((struct sockaddr_in *)hostaddr)-> + sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; + case AF_INET6: + return IN6_IS_ADDR_LOOPBACK( + &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); + default: + return 0; + } +} + +/* + * Prepare the hostname and ip address strings that are used to lookup + * host keys in known_hosts files. These may have a port number appended. + */ +void +get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, + u_short port, char **hostfile_hostname, char **hostfile_ipaddr) +{ + char ntop[NI_MAXHOST]; + socklen_t addrlen; + + switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { + case -1: + addrlen = 0; + break; + case AF_INET: + addrlen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + addrlen = sizeof(struct sockaddr_in6); + break; + default: + addrlen = sizeof(struct sockaddr); + break; + } + + /* + * We don't have the remote ip-address for connections + * using a proxy command + */ + if (hostfile_ipaddr != NULL) { + if (options.proxy_command == NULL) { + if (getnameinfo(hostaddr, addrlen, + ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) + fatal("check_host_key: getnameinfo failed"); + *hostfile_ipaddr = put_host_port(ntop, port); + } else { + *hostfile_ipaddr = xstrdup(""); + } + } + + /* + * Allow the user to record the key under a different name or + * differentiate a non-standard port. This is useful for ssh + * tunneling over forwarded connections or if you run multiple + * sshd's on different ports on the same machine. + */ + if (hostfile_hostname != NULL) { + if (options.host_key_alias != NULL) { + *hostfile_hostname = xstrdup(options.host_key_alias); + debug("using hostkeyalias: %s", *hostfile_hostname); + } else { + *hostfile_hostname = put_host_port(hostname, port); + } + } +} + /* * check whether the supplied host key is valid, return -1 if the key * is not valid. the user_hostfile will not be updated if 'readonly' is true. @@ -616,21 +689,21 @@ check_host_cert(const char *host, const Key *host_key) #define ROQUIET 2 static int check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, - Key *host_key, int readonly, const char *user_hostfile, - const char *system_hostfile) + Key *host_key, int readonly, char *user_hostfile, + char *system_hostfile) { - Key *file_key, *raw_key = NULL; + Key *raw_key = NULL; const char *type; char *ip = NULL, *host = NULL; char hostline[1000], *hostp, *fp, *ra; HostStatus host_status; HostStatus ip_status; - int r, want_cert, local = 0, host_ip_differ = 0; - int salen; - char ntop[NI_MAXHOST]; + int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; + int local = sockaddr_is_local(hostaddr); char msg[1024]; - int len, host_line, ip_line, cancelled_forwarding = 0; - const char *host_file = NULL, *ip_file = NULL; + int len, cancelled_forwarding = 0; + struct hostkeys *host_hostkeys, *ip_hostkeys; + const struct hostkey_entry *host_found, *ip_found; /* * Force accepting of the host key for loopback/localhost. The @@ -640,23 +713,6 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, * essentially disables host authentication for localhost; however, * this is probably not a real problem. */ - /** hostaddr == 0! */ - switch (hostaddr->sa_family) { - case AF_INET: - local = (ntohl(((struct sockaddr_in *)hostaddr)-> - sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; - salen = sizeof(struct sockaddr_in); - break; - case AF_INET6: - local = IN6_IS_ADDR_LOOPBACK( - &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); - salen = sizeof(struct sockaddr_in6); - break; - default: - local = 0; - salen = sizeof(struct sockaddr_storage); - break; - } if (options.no_host_authentication_for_localhost == 1 && local && options.host_key_alias == NULL) { debug("Forcing accepting of host key for " @@ -665,17 +721,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, } /* - * We don't have the remote ip-address for connections - * using a proxy command + * Prepare the hostname and address strings used for hostkey lookup. + * In some cases, these will have a port number appended. */ - if (options.proxy_command == NULL) { - if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop), - NULL, 0, NI_NUMERICHOST) != 0) - fatal("check_host_key: getnameinfo failed"); - ip = put_host_port(ntop, port); - } else { - ip = xstrdup(""); - } + get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); /* * Turn off check_host_ip if the connection is to localhost, via proxy @@ -685,74 +734,52 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) options.check_host_ip = 0; - /* - * Allow the user to record the key under a different name or - * differentiate a non-standard port. This is useful for ssh - * tunneling over forwarded connections or if you run multiple - * sshd's on different ports on the same machine. - */ - if (options.host_key_alias != NULL) { - host = xstrdup(options.host_key_alias); - debug("using hostkeyalias: %s", host); - } else { - host = put_host_port(hostname, port); + host_hostkeys = init_hostkeys(); + load_hostkeys(host_hostkeys, host, user_hostfile); + load_hostkeys(host_hostkeys, host, system_hostfile); + + ip_hostkeys = NULL; + if (!want_cert && options.check_host_ip) { + ip_hostkeys = init_hostkeys(); + load_hostkeys(ip_hostkeys, ip, user_hostfile); + load_hostkeys(ip_hostkeys, ip, system_hostfile); } retry: + /* Reload these as they may have changed on cert->key downgrade */ want_cert = key_is_cert(host_key); type = key_type(host_key); - /* - * Store the host key from the known host file in here so that we can - * compare it with the key for the IP address. - */ - file_key = key_new(key_is_cert(host_key) ? KEY_UNSPEC : host_key->type); - /* * Check if the host key is present in the user's list of known * hosts or in the systemwide list. */ - host_file = user_hostfile; - host_status = check_host_in_hostfile(host_file, host, host_key, - file_key, &host_line); - if (host_status == HOST_NEW) { - host_file = system_hostfile; - host_status = check_host_in_hostfile(host_file, host, host_key, - file_key, &host_line); - } + host_status = check_key_in_hostkeys(host_hostkeys, host_key, + &host_found); + /* * Also perform check for the ip address, skip the check if we are * localhost, looking for a certificate, or the hostname was an ip * address to begin with. */ - if (!want_cert && options.check_host_ip) { - Key *ip_key = key_new(host_key->type); - - ip_file = user_hostfile; - ip_status = check_host_in_hostfile(ip_file, ip, host_key, - ip_key, &ip_line); - if (ip_status == HOST_NEW) { - ip_file = system_hostfile; - ip_status = check_host_in_hostfile(ip_file, ip, - host_key, ip_key, &ip_line); - } + if (!want_cert && ip_hostkeys != NULL) { + ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, + &ip_found); if (host_status == HOST_CHANGED && - (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) + (ip_status != HOST_CHANGED || + (ip_found != NULL && + !key_equal(ip_found->key, host_found->key)))) host_ip_differ = 1; - - key_free(ip_key); } else ip_status = host_status; - key_free(file_key); - switch (host_status) { case HOST_OK: /* The host is known and the key matches. */ debug("Host '%.200s' is known and matches the %s host %s.", host, type, want_cert ? "certificate" : "key"); - debug("Found %s in %s:%d", - want_cert ? "CA key" : "key", host_file, host_line); + debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", + host_found->file, host_found->line); if (want_cert && !check_host_cert(hostname, host_key)) goto fail; if (options.check_host_ip && ip_status == HOST_NEW) { @@ -803,7 +830,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, } else if (options.strict_host_key_checking == 2) { char msg1[1024], msg2[1024]; - if (show_other_keys(host, host_key)) + if (show_other_keys(host_hostkeys, host_key)) snprintf(msg1, sizeof(msg1), "\nbut keys of different type are already" " known for this host."); @@ -844,8 +871,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, * local known_hosts file. */ if (options.check_host_ip && ip_status == HOST_NEW) { - snprintf(hostline, sizeof(hostline), "%s,%s", - host, ip); + snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); hostp = hostline; if (options.hash_known_hosts) { /* Add hash of host and IP separately */ @@ -899,8 +925,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, * all hosts that one might visit. */ debug("Host certificate authority does not " - "match %s in %s:%d", CA_MARKER, - host_file, host_line); + "match %s in %s:%lu", CA_MARKER, + host_found->file, host_found->line); goto fail; } if (readonly == ROQUIET) @@ -922,13 +948,15 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, error("DNS SPOOFING is happening or the IP address for the host"); error("and its host key have changed at the same time."); if (ip_status != HOST_NEW) - error("Offending key for IP in %s:%d", ip_file, ip_line); + error("Offending key for IP in %s:%lu", + ip_found->file, ip_found->line); } /* The host key has changed. */ warn_changed_key(host_key); error("Add correct host key in %.100s to get rid of this message.", user_hostfile); - error("Offending key in %s:%d", host_file, host_line); + error("Offending %s key in %s:%lu", key_type(host_found->key), + host_found->file, host_found->line); /* * If strict host key checking is in use, the user will have @@ -1013,13 +1041,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, snprintf(msg, sizeof(msg), "Warning: the %s host key for '%.200s' " "differs from the key for the IP address '%.128s'" - "\nOffending key for IP in %s:%d", - type, host, ip, ip_file, ip_line); + "\nOffending key for IP in %s:%lu", + type, host, ip, ip_found->file, ip_found->line); if (host_status == HOST_OK) { len = strlen(msg); snprintf(msg + len, sizeof(msg) - len, - "\nMatching host key in %s:%d", - host_file, host_line); + "\nMatching host key in %s:%lu", + host_found->file, host_found->line); } if (options.strict_host_key_checking == 1) { logit("%s", msg); @@ -1037,6 +1065,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, xfree(ip); xfree(host); + if (host_hostkeys != NULL) + free_hostkeys(host_hostkeys); + if (ip_hostkeys != NULL) + free_hostkeys(ip_hostkeys); return 0; fail: @@ -1056,6 +1088,10 @@ fail: key_free(raw_key); xfree(ip); xfree(host); + if (host_hostkeys != NULL) + free_hostkeys(host_hostkeys); + if (ip_hostkeys != NULL) + free_hostkeys(ip_hostkeys); return -1; } @@ -1065,6 +1101,11 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) { struct stat st; int flags = 0; + char *fp; + + fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); + debug("Server host key: %s %s", key_type(host_key), fp); + xfree(fp); /* XXX certs are not yet supported for DNS */ if (!key_is_cert(host_key) && options.verify_host_key_dns && @@ -1108,7 +1149,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) */ void ssh_login(Sensitive *sensitive, const char *orighost, - struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms) + struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) { char *host, *cp; char *server_user, *local_user; @@ -1131,7 +1172,7 @@ ssh_login(Sensitive *sensitive, const char *orighost, /* key exchange */ /* authenticate user */ if (compat20) { - ssh_kex2(host, hostaddr); + ssh_kex2(host, hostaddr, port); ssh_userauth2(local_user, server_user, host, sensitive); } else { ssh_kex(host, hostaddr); @@ -1158,61 +1199,35 @@ ssh_put_password(char *password) xfree(padded); } -static int -show_key_from_file(const char *file, const char *host, int keytype) -{ - Key *found; - char *fp, *ra; - int line, ret; - - found = key_new(keytype); - if ((ret = lookup_key_in_hostfile_by_type(file, host, - keytype, found, &line))) { - fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); - ra = key_fingerprint(found, SSH_FP_MD5, SSH_FP_RANDOMART); - logit("WARNING: %s key found for host %s\n" - "in %s:%d\n" - "%s key fingerprint %s.\n%s\n", - key_type(found), host, file, line, - key_type(found), fp, ra); - xfree(ra); - xfree(fp); - } - key_free(found); - return (ret); -} - /* print all known host keys for a given host, but skip keys of given type */ static int -show_other_keys(const char *host, Key *key) +show_other_keys(struct hostkeys *hostkeys, Key *key) { int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1}; - int i, found = 0; + int i, ret = 0; + char *fp, *ra; + const struct hostkey_entry *found; for (i = 0; type[i] != -1; i++) { if (type[i] == key->type) continue; - if (type[i] != KEY_RSA1 && - show_key_from_file(options.user_hostfile2, host, type[i])) { - found = 1; - continue; - } - if (type[i] != KEY_RSA1 && - show_key_from_file(options.system_hostfile2, host, type[i])) { - found = 1; - continue; - } - if (show_key_from_file(options.user_hostfile, host, type[i])) { - found = 1; + if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) continue; - } - if (show_key_from_file(options.system_hostfile, host, type[i])) { - found = 1; - continue; - } - debug2("no key of type %d for host %s", type[i], host); + fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX); + ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART); + logit("WARNING: %s key found for host %s\n" + "in %s:%lu\n" + "%s key fingerprint %s.", + key_type(found->key), + found->host, found->file, found->line, + key_type(found->key), fp); + if (options.visual_host_key) + logit("%s", ra); + xfree(ra); + xfree(fp); + ret = 1; } - return (found); + return ret; } static void diff --git a/sshconnect.h b/sshconnect.h index 69163afbc..fd7f7f7c6 100644 --- a/sshconnect.h +++ b/sshconnect.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.h,v 1.26 2010/10/06 06:39:28 djm Exp $ */ +/* $OpenBSD: sshconnect.h,v 1.27 2010/11/29 23:45:51 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -36,15 +36,18 @@ ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int, int *, int, int, const char *); void ssh_kill_proxy_command(void); -void -ssh_login(Sensitive *, const char *, struct sockaddr *, struct passwd *, int); +void ssh_login(Sensitive *, const char *, struct sockaddr *, u_short, + struct passwd *, int); void ssh_exchange_identification(int); int verify_host_key(char *, struct sockaddr *, Key *); +void get_hostfile_hostname_ipaddr(char *, struct sockaddr *, u_short, + char **, char **); + void ssh_kex(char *, struct sockaddr *); -void ssh_kex2(char *, struct sockaddr *); +void ssh_kex2(char *, struct sockaddr *, u_short); void ssh_userauth1(const char *, const char *, char *, Sensitive *); void ssh_userauth2(const char *, const char *, char *, Sensitive *); diff --git a/sshconnect2.c b/sshconnect2.c index 6fe356cca..3cb9b101c 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.185 2010/09/22 05:01:29 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.186 2010/11/29 23:45:51 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -69,6 +69,7 @@ #include "msg.h" #include "pathnames.h" #include "uidswap.h" +#include "hostfile.h" #include "schnorr.h" #include "jpake.h" @@ -101,8 +102,60 @@ verify_host_key_callback(Key *hostkey) return 0; } +static char * +order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) +{ + char *oavail, *avail, *first, *last, *alg, *hostname, *ret; + size_t maxlen; + struct hostkeys *hostkeys; + int ktype; + + /* Find all hostkeys for this hostname */ + get_hostfile_hostname_ipaddr(host, hostaddr, port, &hostname, NULL); + hostkeys = init_hostkeys(); + load_hostkeys(hostkeys, hostname, options.user_hostfile2); + load_hostkeys(hostkeys, hostname, options.system_hostfile2); + load_hostkeys(hostkeys, hostname, options.user_hostfile); + load_hostkeys(hostkeys, hostname, options.system_hostfile); + + oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG); + maxlen = strlen(avail) + 1; + first = xmalloc(maxlen); + last = xmalloc(maxlen); + *first = *last = '\0'; + +#define ALG_APPEND(to, from) \ + do { \ + if (*to != '\0') \ + strlcat(to, ",", maxlen); \ + strlcat(to, from, maxlen); \ + } while (0) + + while ((alg = strsep(&avail, ",")) && *alg != '\0') { + if ((ktype = key_type_from_name(alg)) == KEY_UNSPEC) + fatal("%s: unknown alg %s", __func__, alg); + if (lookup_key_in_hostkeys_by_type(hostkeys, + key_type_plain(ktype), NULL)) + ALG_APPEND(first, alg); + else + ALG_APPEND(last, alg); + } +#undef ALG_APPEND + xasprintf(&ret, "%s%s%s", first, *first == '\0' ? "" : ",", last); + if (*first != '\0') + debug3("%s: prefer hostkeyalgs: %s", __func__, first); + + xfree(first); + xfree(last); + xfree(hostname); + xfree(oavail); + free_hostkeys(hostkeys); + + return ret; +} + void -ssh_kex2(char *host, struct sockaddr *hostaddr) +ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) { Kex *kex; @@ -135,6 +188,11 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) if (options.hostkeyalgorithms != NULL) myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = options.hostkeyalgorithms; + else { + /* Prefer algorithms that we already have keys for */ + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = + order_hostkeyalgs(host, hostaddr, port); + } if (options.kex_algorithms != NULL) myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms; -- cgit v1.2.3 From 83f8a4014d1818687f4e0ab36d1d388abac78863 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 7 Jan 2011 09:51:17 +1100 Subject: - djm@cvs.openbsd.org 2011/01/06 22:23:53 [ssh.c] unbreak %n expansion in LocalCommand; patch from bert.wesarg AT googlemail.com; ok markus@ --- ChangeLog | 4 ++++ ssh.c | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'ssh.c') diff --git a/ChangeLog b/ChangeLog index 636524bba..7d84b4c30 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,10 @@ 20110107 - (djm) [regress/cert-hostkey.sh regress/cert-userkey.sh] fix shell test for no-ECC case. Patch from cristian.ionescu-idbohrn AT axis.com + - djm@cvs.openbsd.org 2011/01/06 22:23:53 + [ssh.c] + unbreak %n expansion in LocalCommand; patch from bert.wesarg AT + googlemail.com; ok markus@ 20110106 - (djm) OpenBSD CVS Sync diff --git a/ssh.c b/ssh.c index ec690ae38..9409fa713 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.355 2010/11/29 23:45:51 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.356 2011/01/06 22:23:53 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -221,7 +221,7 @@ int main(int ac, char **av) { int i, r, opt, exit_status, use_syslog; - char *p, *cp, *line, *argv0, buf[MAXPATHLEN]; + char *p, *cp, *line, *argv0, buf[MAXPATHLEN], *host_arg; struct stat st; struct passwd *pw; int dummy, timeout_ms; @@ -693,6 +693,8 @@ main(int ac, char **av) options.port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT; } + /* preserve host name given on command line for %n expansion */ + host_arg = host; if (options.hostname != NULL) { host = percent_expand(options.hostname, "h", host, (char *)NULL); @@ -707,7 +709,7 @@ main(int ac, char **av) debug3("expanding LocalCommand: %s", options.local_command); cp = options.local_command; options.local_command = percent_expand(cp, "d", pw->pw_dir, - "h", host, "l", thishost, "n", host, "r", options.user, + "h", host, "l", thishost, "n", host_arg, "r", options.user, "p", buf, "u", pw->pw_name, (char *)NULL); debug3("expanded LocalCommand: %s", options.local_command); xfree(cp); -- cgit v1.2.3