From d5f62bf280b0798d7009d4424594a648a4e887fb Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 24 Sep 2010 22:11:14 +1000 Subject: [PATCH] - djm@cvs.openbsd.org 2010/09/22 05:01:30 [kex.c kex.h kexecdh.c kexecdhc.c kexecdhs.c readconf.c readconf.h] [servconf.c servconf.h ssh_config.5 sshconnect2.c sshd.c sshd_config.5] add a KexAlgorithms knob to the client and server configuration to allow selection of which key exchange methods are used by ssh(1) and sshd(8) and their order of preference. ok markus@ --- ChangeLog | 7 +++++++ kex.c | 30 +++++++++++++++++++++++++++++- kex.h | 7 +++++-- kexecdh.c | 12 ++++-------- kexecdhc.c | 5 +++-- kexecdhs.c | 5 +++-- readconf.c | 18 +++++++++++++++++- readconf.h | 3 ++- servconf.c | 17 ++++++++++++++++- servconf.h | 3 ++- ssh_config.5 | 15 +++++++++++++-- sshconnect2.c | 4 +++- sshd.c | 4 +++- sshd_config.5 | 15 +++++++++++++-- 14 files changed, 120 insertions(+), 25 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7d9e994d1..5cb4c880d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -31,6 +31,13 @@ either ready or stale without races. stale server sockets are now automatically removed ok deraadt + - djm@cvs.openbsd.org 2010/09/22 05:01:30 + [kex.c kex.h kexecdh.c kexecdhc.c kexecdhs.c readconf.c readconf.h] + [servconf.c servconf.h ssh_config.5 sshconnect2.c sshd.c sshd_config.5] + add a KexAlgorithms knob to the client and server configuration to allow + selection of which key exchange methods are used by ssh(1) and sshd(8) + and their order of preference. + ok markus@ 20100910 - (dtucker) [openbsd-compat/port-linux.c] Check is_selinux_enabled for exact diff --git a/kex.c b/kex.c index 7c8763191..c65e28f94 100644 --- a/kex.c +++ b/kex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.c,v 1.85 2010/09/09 10:45:45 djm Exp $ */ +/* $OpenBSD: kex.c,v 1.86 2010/09/22 05:01:29 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * @@ -62,6 +62,34 @@ extern const EVP_MD *evp_ssh_sha256(void); static void kex_kexinit_finish(Kex *); static void kex_choose_conf(Kex *); +/* Validate KEX method name list */ +int +kex_names_valid(const char *names) +{ + char *s, *cp, *p; + + if (names == NULL || strcmp(names, "") == 0) + return 0; + s = cp = xstrdup(names); + for ((p = strsep(&cp, ",")); p && *p != '\0'; + (p = strsep(&cp, ","))) { + if (strcmp(p, KEX_DHGEX_SHA256) != 0 && + strcmp(p, KEX_DHGEX_SHA1) != 0 && + strcmp(p, KEX_DH14) != 0 && + strcmp(p, KEX_DH1) != 0 && + (strncmp(p, KEX_ECDH_SHA2_STEM, + sizeof(KEX_ECDH_SHA2_STEM) - 1) != 0 || + kex_ecdh_name_to_nid(p) == -1)) { + error("Unsupported KEX algorithm \"%.100s\"", p); + xfree(s); + return 0; + } + } + debug3("kex names ok: [%s]", names); + xfree(s); + return 1; +} + /* put algorithm proposal into buffer */ static void kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX]) diff --git a/kex.h b/kex.h index 3e312fb44..7373d3c78 100644 --- a/kex.h +++ b/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.51 2010/09/09 10:45:45 djm Exp $ */ +/* $OpenBSD: kex.h,v 1.52 2010/09/22 05:01:29 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -138,6 +138,8 @@ struct Kex { void (*kex[KEX_MAX])(Kex *); }; +int kex_names_valid(const char *); + Kex *kex_setup(char *[PROPOSAL_MAX]); void kex_finish(Kex *); @@ -169,7 +171,8 @@ kex_ecdh_hash(const EVP_MD *, const EC_GROUP *, char *, char *, char *, 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 +# define kex_ecdh_name_to_nid(x) (-1) +# define kex_ecdh_name_to_evpmd(x) (NULL) #endif void diff --git a/kexecdh.c b/kexecdh.c index 4c58a5122..f13f69d3b 100644 --- a/kexecdh.c +++ b/kexecdh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexecdh.c,v 1.2 2010/09/09 10:45:45 djm Exp $ */ +/* $OpenBSD: kexecdh.c,v 1.3 2010/09/22 05:01:29 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -48,15 +48,9 @@ int kex_ecdh_name_to_nid(const char *kexname) { - int ret; - if (strlen(kexname) < sizeof(KEX_ECDH_SHA2_STEM) - 1) fatal("%s: kexname too short \"%s\"", __func__, kexname); - ret = key_curve_name_to_nid(kexname + sizeof(KEX_ECDH_SHA2_STEM) - 1); - if (ret == -1) - fatal("%s: unsupported curve negotiated \"%s\"", __func__, - kexname); - return ret; + return key_curve_name_to_nid(kexname + sizeof(KEX_ECDH_SHA2_STEM) - 1); } const EVP_MD * @@ -64,6 +58,8 @@ kex_ecdh_name_to_evpmd(const char *kexname) { int nid = kex_ecdh_name_to_nid(kexname); + if (nid == -1) + fatal("%s: unsupported ECDH curve \"%s\"", __func__, kexname); return key_ec_nid_to_evpmd(nid); } diff --git a/kexecdhc.c b/kexecdhc.c index 297a0e5a9..115d4bf83 100644 --- a/kexecdhc.c +++ b/kexecdhc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexecdhc.c,v 1.1 2010/08/31 11:54:45 djm Exp $ */ +/* $OpenBSD: kexecdhc.c,v 1.2 2010/09/22 05:01:29 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -59,7 +59,8 @@ kexecdh_client(Kex *kex) u_int klen, slen, sbloblen, hashlen; int curve_nid; - curve_nid = kex_ecdh_name_to_nid(kex->name); + if ((curve_nid = kex_ecdh_name_to_nid(kex->name)) == -1) + fatal("%s: unsupported ECDH curve \"%s\"", __func__, 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) diff --git a/kexecdhs.c b/kexecdhs.c index d2c3feb09..8c515dfa6 100644 --- a/kexecdhs.c +++ b/kexecdhs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexecdhs.c,v 1.1 2010/08/31 11:54:45 djm Exp $ */ +/* $OpenBSD: kexecdhs.c,v 1.2 2010/09/22 05:01:29 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -61,7 +61,8 @@ kexecdh_server(Kex *kex) u_int klen, slen, sbloblen, hashlen; int curve_nid; - curve_nid = kex_ecdh_name_to_nid(kex->name); + if ((curve_nid = kex_ecdh_name_to_nid(kex->name)) == -1) + fatal("%s: unsupported ECDH curve \"%s\"", __func__, 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) diff --git a/readconf.c b/readconf.c index 586422930..da7efd193 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.188 2010/08/31 11:54:45 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.189 2010/09/22 05:01:29 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -132,6 +132,7 @@ typedef enum { oHashKnownHosts, oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, + oKexAlgorithms, oDeprecated, oUnsupported } OpCodes; @@ -240,6 +241,7 @@ static struct { #else { "zeroknowledgepasswordauthentication", oUnsupported }, #endif + { "kexalgorithms", oKexAlgorithms }, { NULL, oBadOption } }; @@ -699,6 +701,18 @@ parse_int: options->macs = xstrdup(arg); break; + case oKexAlgorithms: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", + filename, linenum); + if (!kex_names_valid(arg)) + fatal("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.", + filename, linenum, arg ? arg : ""); + if (*activep && options->kex_algorithms == NULL) + options->kex_algorithms = xstrdup(arg); + break; + case oHostKeyAlgorithms: arg = strdelim(&s); if (!arg || *arg == '\0') @@ -1078,6 +1092,7 @@ initialize_options(Options * options) options->cipher = -1; options->ciphers = NULL; options->macs = NULL; + options->kex_algorithms = NULL; options->hostkeyalgorithms = NULL; options->protocol = SSH_PROTO_UNKNOWN; options->num_identity_files = 0; @@ -1191,6 +1206,7 @@ fill_default_options(Options * options) options->cipher = SSH_CIPHER_NOT_SET; /* options->ciphers, default set in myproposals.h */ /* options->macs, default set in myproposals.h */ + /* options->kex_algorithms, default set in myproposals.h */ /* options->hostkeyalgorithms, default set in myproposals.h */ if (options->protocol == SSH_PROTO_UNKNOWN) options->protocol = SSH_PROTO_2; diff --git a/readconf.h b/readconf.h index 95d104674..ae61466df 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.86 2010/07/19 09:15:12 djm Exp $ */ +/* $OpenBSD: readconf.h,v 1.87 2010/09/22 05:01:29 djm Exp $ */ /* * Author: Tatu Ylonen @@ -73,6 +73,7 @@ typedef struct { char *ciphers; /* SSH2 ciphers in order of preference. */ char *macs; /* SSH2 macs in order of preference. */ char *hostkeyalgorithms; /* SSH2 server key types in order of preference. */ + char *kex_algorithms; /* SSH2 kex methods in order of preference. */ int protocol; /* Protocol in order of preference. */ char *hostname; /* Real host to connect. */ char *host_key_alias; /* hostname alias for .ssh/known_hosts */ diff --git a/servconf.c b/servconf.c index def6b716a..d26a7db05 100644 --- a/servconf.c +++ b/servconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.c,v 1.210 2010/09/01 15:21:35 naddy Exp $ */ +/* $OpenBSD: servconf.c,v 1.211 2010/09/22 05:01:29 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -109,6 +109,7 @@ initialize_server_options(ServerOptions *options) options->num_deny_groups = 0; options->ciphers = NULL; options->macs = NULL; + options->kex_algorithms = NULL; options->protocol = SSH_PROTO_UNKNOWN; options->gateway_ports = -1; options->num_subsystems = 0; @@ -314,6 +315,7 @@ typedef enum { sUsePrivilegeSeparation, sAllowAgentForwarding, sZeroKnowledgePasswordAuthentication, sHostCertificate, sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, + sKexAlgorithms, sDeprecated, sUnsupported } ServerOpCodes; @@ -436,6 +438,7 @@ static struct { { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, + { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, { NULL, sBadOption, 0 } }; @@ -1131,6 +1134,18 @@ process_server_config_line(ServerOptions *options, char *line, options->macs = xstrdup(arg); break; + case sKexAlgorithms: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", + filename, linenum); + if (!kex_names_valid(arg)) + fatal("%s line %d: Bad SSH2 KexAlgorithms '%s'.", + filename, linenum, arg ? arg : ""); + if (options->kex_algorithms == NULL) + options->kex_algorithms = xstrdup(arg); + break; + case sProtocol: intptr = &options->protocol; arg = strdelim(&cp); diff --git a/servconf.h b/servconf.h index 45d2a2ae3..ad13f2edd 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.93 2010/05/07 11:30:30 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.94 2010/09/22 05:01:29 djm Exp $ */ /* * Author: Tatu Ylonen @@ -72,6 +72,7 @@ typedef struct { int tcp_keep_alive; /* If true, set SO_KEEPALIVE. */ char *ciphers; /* Supported SSH2 ciphers. */ char *macs; /* Supported SSH2 macs. */ + char *kex_algorithms; /* SSH2 kex methods in order of preference. */ int protocol; /* Supported protocol versions. */ int gateway_ports; /* If true, allow remote connects to forwarded ports. */ SyslogFacility log_facility; /* Facility for system logging. */ diff --git a/ssh_config.5 b/ssh_config.5 index 33038ffcf..6e49842a7 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.139 2010/08/31 11:54:45 djm Exp $ -.Dd $Mdocdate: August 31 2010 $ +.\" $OpenBSD: ssh_config.5,v 1.140 2010/09/22 05:01:29 djm Exp $ +.Dd $Mdocdate: September 22 2010 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -646,6 +646,17 @@ it may be zero or more of: .Dq pam , and .Dq skey . +.It Cm KexAlgorithms +Specifies the available KEX (Key Exchange) algorithms. +Multiple algorithms must be comma-separated. +The default is +.Dq ecdh-sha2-nistp256 , +.Dq ecdh-sha2-nistp384 , +.Dq ecdh-sha2-nistp521 , +.Dq diffie-hellman-group-exchange-sha256 , +.Dq diffie-hellman-group-exchange-sha1 , +.Dq diffie-hellman-group14-sha1 , +.Dq diffie-hellman-group1-sha1 . .It Cm LocalCommand Specifies a command to execute on the local machine after successfully connecting to the server. diff --git a/sshconnect2.c b/sshconnect2.c index a31a663d4..6fe356cca 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.184 2010/08/31 11:54:45 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.185 2010/09/22 05:01:29 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -135,6 +135,8 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) if (options.hostkeyalgorithms != NULL) myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = options.hostkeyalgorithms; + if (options.kex_algorithms != NULL) + myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms; if (options.rekey_limit) packet_set_rekey_limit((u_int32_t)options.rekey_limit); diff --git a/sshd.c b/sshd.c index 7995f5a1d..5d4d14ae2 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.379 2010/08/31 12:33:38 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.380 2010/09/22 05:01:29 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -2297,6 +2297,8 @@ do_ssh2_kex(void) myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com"; } + if (options.kex_algorithms != NULL) + myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms; myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); diff --git a/sshd_config.5 b/sshd_config.5 index af3d89b80..d87f60246 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.126 2010/08/31 11:54:45 djm Exp $ -.Dd $Mdocdate: August 31 2010 $ +.\" $OpenBSD: sshd_config.5,v 1.127 2010/09/22 05:01:30 djm Exp $ +.Dd $Mdocdate: September 22 2010 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -538,6 +538,17 @@ Specifies whether to automatically destroy the user's ticket cache file on logout. The default is .Dq yes . +.It Cm KexAlgorithms +Specifies the available KEX (Key Exchange) algorithms. +Multiple algorithms must be comma-separated. +The default is +.Dq ecdh-sha2-nistp256 , +.Dq ecdh-sha2-nistp384 , +.Dq ecdh-sha2-nistp521 , +.Dq diffie-hellman-group-exchange-sha256 , +.Dq diffie-hellman-group-exchange-sha1 , +.Dq diffie-hellman-group14-sha1 , +.Dq diffie-hellman-group1-sha1 . .It Cm KeyRegenerationInterval In protocol version 1, the ephemeral server key is automatically regenerated after this many seconds (if it has been used).