From djm@mindrot.org Wed Jul 29 11:08:21 2015 Delivered-To: david@gwynne.id.au Received: by 10.27.85.227 with SMTP id l96csp2098282wli; Tue, 28 Jul 2015 18:08:23 -0700 (PDT) X-Received: by 10.70.41.202 with SMTP id h10mr86290726pdl.155.1438132103377; Tue, 28 Jul 2015 18:08:23 -0700 (PDT) Return-Path: Received: from cvs.openbsd.org (cvs.openbsd.org. [199.185.137.3]) by mx.google.com with ESMTPS id mi8si57299100pdb.134.2015.07.28.18.08.20 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 28 Jul 2015 18:08:21 -0700 (PDT) Received-SPF: neutral (google.com: 199.185.137.3 is neither permitted nor denied by domain of djm@mindrot.org) client-ip=199.185.137.3; Authentication-Results: mx.google.com; spf=neutral (google.com: 199.185.137.3 is neither permitted nor denied by domain of djm@mindrot.org) smtp.mail=djm@mindrot.org Received: from shear.ucar.edu (lists.openbsd.org [192.43.244.163]) by cvs.openbsd.org (OpenSMTPD) with ESMTPS id 58c74ec0 TLS version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL for ; Tue, 28 Jul 2015 19:08:20 -0600 (MDT) Received: from newmailhub.uq.edu.au (mailhub2.soe.uq.edu.au [130.102.132.209]) by shear.ucar.edu (8.14.7/8.14.7) with ESMTP id t6T18H8b028931 for ; Tue, 28 Jul 2015 19:08:19 -0600 (MDT) Received: from smtp2.soe.uq.edu.au (smtp2.soe.uq.edu.au [10.138.113.41]) by newmailhub.uq.edu.au (8.14.5/8.14.5) with ESMTP id t6T18F9M018024 for ; Wed, 29 Jul 2015 11:08:15 +1000 Received: from mailhub.eait.uq.edu.au (hazel.eait.uq.edu.au [130.102.60.17]) by smtp2.soe.uq.edu.au (8.14.5/8.14.5) with ESMTP id t6T18FWX013800 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Wed, 29 Jul 2015 11:08:15 +1000 Received: from natsu.mindrot.org (natsu.mindrot.org [130.102.96.2]) by mailhub.eait.uq.edu.au (8.15.1/8.15.1) with ESMTP id t6T18FxU018565 for ; Wed, 29 Jul 2015 11:08:15 +1000 (AEST) Received: by natsu.mindrot.org (Postfix, from userid 1000) id 77393A4F4D; Wed, 29 Jul 2015 11:08:15 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by natsu.mindrot.org (Postfix) with ESMTP id 75F13A4F2F for ; Wed, 29 Jul 2015 11:08:15 +1000 (AEST) Date: Wed, 29 Jul 2015 11:08:15 +1000 (AEST) From: Damien Miller To: dlg@openbsd.org Subject: KexAlgorithms +something Message-ID: User-Agent: Alpine 2.20 (BSO 67 2015-01-07) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII X-Scanned-By: MIMEDefang 2.73 on UQ Mailhub X-Scanned-By: MIMEDefang 2.75 on 130.102.60.17 X-UQ-FilterTime: 1438132096 Status: RO Content-Length: 12755 Lines: 367 still need to improve error messages Index: kex.c =================================================================== RCS file: /cvs/src/usr.bin/ssh/kex.c,v retrieving revision 1.106 diff -u -p -r1.106 kex.c --- kex.c 17 Apr 2015 13:25:52 -0000 1.106 +++ kex.c 28 Jul 2015 01:57:40 -0000 @@ -136,6 +136,68 @@ kex_names_valid(const char *names) return 1; } +/* + * Concatenate algorithm names, avoiding duplicates in the process. + * Caller must free returned string. + */ +char * +kex_names_cat(const char *a, const char *b) +{ + char *ret = NULL, *tmp = NULL, *cp, *p; + size_t len; + + if (a == NULL || *a == '\0') + return NULL; + if (b == NULL || *b == '\0') + return strdup(a); + if (strlen(b) > 1024*1024) + return NULL; + len = strlen(a) + strlen(b) + 2; + if ((tmp = cp = strdup(b)) == NULL || + (ret = calloc(1, len)) == NULL) { + free(tmp); + return NULL; + } + strlcpy(ret, a, len); + for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { + if (match_list(ret, p, NULL) != NULL) + continue; /* Algorithm already present */ + if (strlcat(ret, ",", len) >= len || + strlcat(ret, p, len) >= len) { + free(tmp); + free(ret); + return NULL; /* Shouldn't happen */ + } + } + free(tmp); + return ret; +} + +/* + * Assemble a list of algorithms from a default list and a string from a + * configuration file. The user-provided string may begin with '+' to + * indicate that it should be appended to the default. + */ +int +kex_assemble_names(const char *def, char **list) +{ + char *ret; + + if (list == NULL || *list == NULL || **list == '\0') { + *list = strdup(def); + return 0; + } + if (**list != '+') { + return 0; + } + + if ((ret = kex_names_cat(def, *list + 1)) == NULL) + return SSH_ERR_ALLOC_FAIL; + free(*list); + *list = ret; + return 0; +} + /* put algorithm proposal into buffer */ int kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) Index: kex.h =================================================================== RCS file: /cvs/src/usr.bin/ssh/kex.h,v retrieving revision 1.71 diff -u -p -r1.71 kex.h --- kex.h 16 Feb 2015 22:13:32 -0000 1.71 +++ kex.h 28 Jul 2015 01:57:40 -0000 @@ -145,6 +145,8 @@ struct kex { int kex_names_valid(const char *); char *kex_alg_list(char); +char *kex_names_cat(const char *, const char *); +int kex_assemble_names(const char *, char **); int kex_new(struct ssh *, char *[PROPOSAL_MAX], struct kex **); int kex_setup(struct ssh *, char *[PROPOSAL_MAX]); Index: readconf.c =================================================================== RCS file: /cvs/src/usr.bin/ssh/readconf.c,v retrieving revision 1.238 diff -u -p -r1.238 readconf.c --- readconf.c 10 Jul 2015 06:21:53 -0000 1.238 +++ readconf.c 28 Jul 2015 01:57:41 -0000 @@ -1074,7 +1074,7 @@ parse_int: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!ciphers_valid(arg)) + if (!ciphers_valid(*arg == '+' ? arg + 1 : arg)) fatal("%.200s line %d: Bad SSH2 cipher spec '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->ciphers == NULL) @@ -1085,7 +1085,7 @@ parse_int: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!mac_valid(arg)) + if (!mac_valid(*arg == '+' ? arg + 1 : arg)) fatal("%.200s line %d: Bad SSH2 Mac spec '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->macs == NULL) @@ -1097,7 +1097,7 @@ parse_int: if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!kex_names_valid(arg)) + if (!kex_names_valid(*arg == '+' ? arg + 1 : arg)) fatal("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->kex_algorithms == NULL) @@ -1111,7 +1111,7 @@ parse_keytypes: if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!sshkey_names_valid2(arg, 1)) + if (!sshkey_names_valid2(*arg == '+' ? arg + 1 : arg, 1)) fatal("%s line %d: Bad key types '%s'.", filename, linenum, arg ? arg : ""); if (*activep && *charptr == NULL) @@ -1750,9 +1750,6 @@ fill_default_options(Options * options) /* Selected in ssh_login(). */ if (options->cipher == -1) 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; @@ -1844,10 +1841,14 @@ fill_default_options(Options * options) options->fingerprint_hash = SSH_FP_HASH_DEFAULT; if (options->update_hostkeys == -1) options->update_hostkeys = 0; - if (options->hostbased_key_types == NULL) - options->hostbased_key_types = xstrdup(KEX_DEFAULT_PK_ALG); - if (options->pubkey_key_types == NULL) - options->pubkey_key_types = xstrdup(KEX_DEFAULT_PK_ALG); + if (kex_assemble_names(KEX_CLIENT_ENCRYPT, &options->ciphers) != 0 || + kex_assemble_names(KEX_CLIENT_MAC, &options->macs) != 0 || + kex_assemble_names(KEX_CLIENT_KEX, &options->kex_algorithms) != 0 || + kex_assemble_names(KEX_DEFAULT_PK_ALG, + &options->hostbased_key_types) != 0 || + kex_assemble_names(KEX_DEFAULT_PK_ALG, + &options->pubkey_key_types) != 0) + fatal("%s: allocation failed", __func__); #define CLEAR_ON_NONE(v) \ do { \ Index: ssh.c =================================================================== RCS file: /cvs/src/usr.bin/ssh/ssh.c,v retrieving revision 1.419 diff -u -p -r1.419 ssh.c --- ssh.c 20 Jul 2015 18:42:35 -0000 1.419 +++ ssh.c 28 Jul 2015 01:57:41 -0000 @@ -98,6 +98,7 @@ #include "roaming.h" #include "version.h" #include "ssherr.h" +#include "myproposal.h" #ifdef ENABLE_PKCS11 #include "ssh-pkcs11.h" @@ -764,26 +765,26 @@ main(int ac, char **av) } break; case 'c': - if (ciphers_valid(optarg)) { + if (ciphers_valid(*optarg == '+' ? + optarg + 1 : optarg)) { /* SSH2 only */ options.ciphers = xstrdup(optarg); options.cipher = SSH_CIPHER_INVALID; - } else { - /* SSH1 only */ - options.cipher = cipher_number(optarg); - if (options.cipher == -1) { - fprintf(stderr, - "Unknown cipher type '%s'\n", - optarg); - exit(255); - } - if (options.cipher == SSH_CIPHER_3DES) - options.ciphers = "3des-cbc"; - else if (options.cipher == SSH_CIPHER_BLOWFISH) - options.ciphers = "blowfish-cbc"; - else - options.ciphers = (char *)-1; + break; + } + /* SSH1 only */ + options.cipher = cipher_number(optarg); + if (options.cipher == -1) { + fprintf(stderr, "Unknown cipher type '%s'\n", + optarg); + exit(255); } + if (options.cipher == SSH_CIPHER_3DES) + options.ciphers = xstrdup("3des-cbc"); + else if (options.cipher == SSH_CIPHER_BLOWFISH) + options.ciphers = xstrdup("blowfish-cbc"); + else + options.ciphers = xstrdup(KEX_CLIENT_ENCRYPT); break; case 'm': if (mac_valid(optarg)) Index: ssh_config.5 =================================================================== RCS file: /cvs/src/usr.bin/ssh/ssh_config.5,v retrieving revision 1.213 diff -u -p -r1.213 ssh_config.5 --- ssh_config.5 10 Jul 2015 06:21:53 -0000 1.213 +++ ssh_config.5 28 Jul 2015 01:57:41 -0000 @@ -373,6 +373,11 @@ The default is Specifies the ciphers allowed for protocol version 2 in order of preference. Multiple ciphers must be comma-separated. +If the specified value begins with a +.Sq + +character, then the specified ciphers will be appended to the default set +instead of replacing them. +.Pp The supported ciphers are: .Pp .Bl -item -compact -offset indent @@ -781,6 +786,10 @@ is similar to .It Cm HostbasedKeyTypes Specifies the key types that will be used for hostbased authentication as a comma-separated pattern list. +Alternately if the specified value begins with a +.Sq + +character, then the specified key types will be appended to the default set +instead of replacing them. The default for this option is: .Bd -literal -offset 3n ecdsa-sha2-nistp256-cert-v01@openssh.com, @@ -800,6 +809,10 @@ may be used to list supported key types. .It Cm HostKeyAlgorithms Specifies the protocol version 2 host key algorithms that the client wants to use in order of preference. +Alternately if the specified value begins with a +.Sq + +character, then the specified key types will be appended to the default set +instead of replacing them. The default for this option is: .Bd -literal -offset 3n ecdsa-sha2-nistp256-cert-v01@openssh.com, @@ -981,6 +994,10 @@ and .It Cm KexAlgorithms Specifies the available KEX (Key Exchange) algorithms. Multiple algorithms must be comma-separated. +Alternately if the specified value begins with a +.Sq + +character, then the specified methods will be appended to the default set +instead of replacing them. The default is: .Bd -literal -offset indent curve25519-sha256@libssh.org, @@ -1069,10 +1086,16 @@ in order of preference. The MAC algorithm is used in protocol version 2 for data integrity protection. Multiple algorithms must be comma-separated. +If the specified value begins with a +.Sq + +character, then the specified algorithms will be appended to the default set +instead of replacing them. +.Pp The algorithms that contain .Dq -etm calculate the MAC after encryption (encrypt-then-mac). These are considered safer and their use recommended. +.Pp The default is: .Bd -literal -offset indent umac-64-etm@openssh.com,umac-128-etm@openssh.com, @@ -1216,6 +1239,10 @@ The default is .It Cm PubkeyAcceptedKeyTypes Specifies the key types that will be used for public key authentication as a comma-separated pattern list. +Alternately if the specified value begins with a +.Sq + +character, then the key types after it will be appended to the default +instead of replacing it. The default for this option is: .Bd -literal -offset 3n ecdsa-sha2-nistp256-cert-v01@openssh.com, Index: sshconnect2.c =================================================================== RCS file: /cvs/src/usr.bin/ssh/sshconnect2.c,v retrieving revision 1.225 diff -u -p -r1.225 sshconnect2.c --- sshconnect2.c 10 Jul 2015 06:21:53 -0000 1.225 +++ sshconnect2.c 28 Jul 2015 01:57:41 -0000 @@ -157,18 +157,12 @@ ssh_kex2(char *host, struct sockaddr *ho xxx_host = host; xxx_hostaddr = hostaddr; - if (options.ciphers == (char *)-1) { - logit("No valid ciphers for protocol version 2 given, using defaults."); - options.ciphers = NULL; - } - if (options.ciphers != NULL) { - myproposal[PROPOSAL_ENC_ALGS_CTOS] = - myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; - } + myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( + options.kex_algorithms); myproposal[PROPOSAL_ENC_ALGS_CTOS] = - compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); + compat_cipher_proposal(options.ciphers); myproposal[PROPOSAL_ENC_ALGS_STOC] = - compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); + compat_cipher_proposal(options.ciphers); if (options.compression) { myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib@openssh.com,zlib,none"; @@ -176,14 +170,15 @@ ssh_kex2(char *host, struct sockaddr *ho myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com,zlib"; } - if (options.macs != NULL) { - myproposal[PROPOSAL_MAC_ALGS_CTOS] = - myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; - } - if (options.hostkeyalgorithms != NULL) + myproposal[PROPOSAL_MAC_ALGS_CTOS] = + myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; + if (options.hostkeyalgorithms != NULL) { + if (kex_assemble_names(KEX_DEFAULT_PK_ALG, + &options.hostkeyalgorithms) != 0) + fatal("%s: kex_assemble_namelist", __func__); myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(options.hostkeyalgorithms); - else { + } else { /* Enforce default */ options.hostkeyalgorithms = xstrdup(KEX_DEFAULT_PK_ALG); /* Prefer algorithms that we already have keys for */ @@ -191,10 +186,6 @@ ssh_kex2(char *host, struct sockaddr *ho compat_pkalg_proposal( order_hostkeyalgs(host, hostaddr, port)); } - if (options.kex_algorithms != NULL) - myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms; - myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( - myproposal[PROPOSAL_KEX_ALGS]); if (options.rekey_limit || options.rekey_interval) packet_set_rekey_limits((u_int32_t)options.rekey_limit,