1263970Sdes/* $OpenBSD: compat.c,v 1.82 2013/12/30 23:52:27 djm Exp $ */ 2224638Sbrooks/* $FreeBSD$ */ 357429Smarkm/* 492559Sdes * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. 557429Smarkm * 657429Smarkm * Redistribution and use in source and binary forms, with or without 757429Smarkm * modification, are permitted provided that the following conditions 857429Smarkm * are met: 957429Smarkm * 1. Redistributions of source code must retain the above copyright 1057429Smarkm * notice, this list of conditions and the following disclaimer. 1157429Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1257429Smarkm * notice, this list of conditions and the following disclaimer in the 1357429Smarkm * documentation and/or other materials provided with the distribution. 1457429Smarkm * 1557429Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1657429Smarkm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1757429Smarkm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1857429Smarkm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1957429Smarkm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2057429Smarkm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2157429Smarkm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2257429Smarkm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2357429Smarkm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2457429Smarkm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2557429Smarkm */ 2657429Smarkm 2757429Smarkm#include "includes.h" 28263970Sdes__RCSID("$FreeBSD$"); 2957429Smarkm 30162856Sdes#include <sys/types.h> 31162856Sdes 32162856Sdes#include <stdlib.h> 33162856Sdes#include <string.h> 34162856Sdes#include <stdarg.h> 35162856Sdes 36162856Sdes#include "xmalloc.h" 3792559Sdes#include "buffer.h" 3860573Skris#include "packet.h" 3960573Skris#include "compat.h" 4076262Sgreen#include "log.h" 4192559Sdes#include "match.h" 4257429Smarkm 4357429Smarkmint compat13 = 0; 4460573Skrisint compat20 = 0; 4560573Skrisint datafellows = 0; 4657429Smarkm 4760573Skrisvoid 4860573Skrisenable_compat20(void) 4960573Skris{ 50251135Sdes if (compat20) 51251135Sdes return; 52106130Sdes debug("Enabling compatibility mode for protocol 2.0"); 5360573Skris compat20 = 1; 5460573Skris} 5560573Skrisvoid 5657429Smarkmenable_compat13(void) 5757429Smarkm{ 58106130Sdes debug("Enabling compatibility mode for protocol 1.3"); 5957429Smarkm compat13 = 1; 6057429Smarkm} 6160573Skris/* datafellows bug compatibility */ 6260573Skrisvoid 6360573Skriscompat_datafellows(const char *version) 6460573Skris{ 6592559Sdes int i; 6669587Sgreen static struct { 6769587Sgreen char *pat; 6860573Skris int bugs; 6960573Skris } check[] = { 7092559Sdes { "OpenSSH-2.0*," 7192559Sdes "OpenSSH-2.1*," 7292559Sdes "OpenSSH_2.1*," 7392559Sdes "OpenSSH_2.2*", SSH_OLD_SESSIONID|SSH_BUG_BANNER| 7498684Sdes SSH_OLD_DHGEX|SSH_BUG_NOREKEY| 75147005Sdes SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, 7692559Sdes { "OpenSSH_2.3.0*", SSH_BUG_BANNER|SSH_BUG_BIGENDIANAES| 7798684Sdes SSH_OLD_DHGEX|SSH_BUG_NOREKEY| 78147005Sdes SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, 7992559Sdes { "OpenSSH_2.3.*", SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX| 80147005Sdes SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| 81147005Sdes SSH_OLD_FORWARD_ADDR}, 8292559Sdes { "OpenSSH_2.5.0p1*," 8392559Sdes "OpenSSH_2.5.1p1*", 8476262Sgreen SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX| 85147005Sdes SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| 86147005Sdes SSH_OLD_FORWARD_ADDR}, 8792559Sdes { "OpenSSH_2.5.0*," 8892559Sdes "OpenSSH_2.5.1*," 8998684Sdes "OpenSSH_2.5.2*", SSH_OLD_DHGEX|SSH_BUG_NOREKEY| 90147005Sdes SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, 91147005Sdes { "OpenSSH_2.5.3*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| 92147005Sdes SSH_OLD_FORWARD_ADDR}, 9398684Sdes { "OpenSSH_2.*," 9498684Sdes "OpenSSH_3.0*," 95147005Sdes "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, 96147005Sdes { "OpenSSH_3.*", SSH_OLD_FORWARD_ADDR }, 9798684Sdes { "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF}, 98192595Sdes { "OpenSSH_4*", 0 }, 99247485Sdes { "OpenSSH_5*", SSH_NEW_OPENSSH|SSH_BUG_DYNAMIC_RPORT}, 100264693Sdes { "OpenSSH_6.6.1*", SSH_NEW_OPENSSH}, 101264693Sdes { "OpenSSH_6.5*," 102264693Sdes "OpenSSH_6.6*", SSH_NEW_OPENSSH|SSH_BUG_CURVE25519PAD}, 103192595Sdes { "OpenSSH*", SSH_NEW_OPENSSH }, 10492559Sdes { "*MindTerm*", 0 }, 10592559Sdes { "2.1.0*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| 10676262Sgreen SSH_OLD_SESSIONID|SSH_BUG_DEBUG| 107113911Sdes SSH_BUG_RSASIGMD5|SSH_BUG_HBSERVICE| 108113911Sdes SSH_BUG_FIRSTKEX }, 10992559Sdes { "2.1 *", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| 11076262Sgreen SSH_OLD_SESSIONID|SSH_BUG_DEBUG| 111113911Sdes SSH_BUG_RSASIGMD5|SSH_BUG_HBSERVICE| 112113911Sdes SSH_BUG_FIRSTKEX }, 11392559Sdes { "2.0.13*," 11492559Sdes "2.0.14*," 11592559Sdes "2.0.15*," 11692559Sdes "2.0.16*," 11792559Sdes "2.0.17*," 11892559Sdes "2.0.18*," 11992559Sdes "2.0.19*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| 12076262Sgreen SSH_OLD_SESSIONID|SSH_BUG_DEBUG| 12176262Sgreen SSH_BUG_PKSERVICE|SSH_BUG_X11FWD| 12276262Sgreen SSH_BUG_PKOK|SSH_BUG_RSASIGMD5| 12392559Sdes SSH_BUG_HBSERVICE|SSH_BUG_OPENFAILURE| 124113911Sdes SSH_BUG_DUMMYCHAN|SSH_BUG_FIRSTKEX }, 12592559Sdes { "2.0.11*," 12692559Sdes "2.0.12*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| 12776262Sgreen SSH_OLD_SESSIONID|SSH_BUG_DEBUG| 12876262Sgreen SSH_BUG_PKSERVICE|SSH_BUG_X11FWD| 12976262Sgreen SSH_BUG_PKAUTH|SSH_BUG_PKOK| 13092559Sdes SSH_BUG_RSASIGMD5|SSH_BUG_OPENFAILURE| 131113911Sdes SSH_BUG_DUMMYCHAN|SSH_BUG_FIRSTKEX }, 13292559Sdes { "2.0.*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| 13392559Sdes SSH_OLD_SESSIONID|SSH_BUG_DEBUG| 13492559Sdes SSH_BUG_PKSERVICE|SSH_BUG_X11FWD| 13592559Sdes SSH_BUG_PKAUTH|SSH_BUG_PKOK| 13692559Sdes SSH_BUG_RSASIGMD5|SSH_BUG_OPENFAILURE| 137113911Sdes SSH_BUG_DERIVEKEY|SSH_BUG_DUMMYCHAN| 138113911Sdes SSH_BUG_FIRSTKEX }, 13992559Sdes { "2.2.0*," 14092559Sdes "2.3.0*", SSH_BUG_HMAC|SSH_BUG_DEBUG| 141113911Sdes SSH_BUG_RSASIGMD5|SSH_BUG_FIRSTKEX }, 142113911Sdes { "2.3.*", SSH_BUG_DEBUG|SSH_BUG_RSASIGMD5| 143113911Sdes SSH_BUG_FIRSTKEX }, 14492559Sdes { "2.4", SSH_OLD_SESSIONID }, /* Van Dyke */ 145181111Sdes { "2.*", SSH_BUG_DEBUG|SSH_BUG_FIRSTKEX| 146181111Sdes SSH_BUG_RFWD_ADDR }, 14792559Sdes { "3.0.*", SSH_BUG_DEBUG }, 14892559Sdes { "3.0 SecureCRT*", SSH_OLD_SESSIONID }, 14992559Sdes { "1.7 SecureFX*", SSH_OLD_SESSIONID }, 15092559Sdes { "1.2.18*," 15192559Sdes "1.2.19*," 15292559Sdes "1.2.20*," 15392559Sdes "1.2.21*," 154124211Sdes "1.2.22*", SSH_BUG_IGNOREMSG }, 15598684Sdes { "1.3.2*", /* F-Secure */ 156124211Sdes SSH_BUG_IGNOREMSG }, 15792559Sdes { "*SSH Compatible Server*", /* Netscreen */ 15876262Sgreen SSH_BUG_PASSWORDPAD }, 15992559Sdes { "*OSU_0*," 16092559Sdes "OSU_1.0*," 16192559Sdes "OSU_1.1*," 16292559Sdes "OSU_1.2*," 16392559Sdes "OSU_1.3*," 16492559Sdes "OSU_1.4*," 16592559Sdes "OSU_1.5alpha1*," 16692559Sdes "OSU_1.5alpha2*," 16792559Sdes "OSU_1.5alpha3*", SSH_BUG_PASSWORDPAD }, 16892559Sdes { "*SSH_Version_Mapper*", 16976262Sgreen SSH_BUG_SCANNER }, 170106130Sdes { "Probe-*", 171106130Sdes SSH_BUG_PROBE }, 17269587Sgreen { NULL, 0 } 17360573Skris }; 17492559Sdes 17565668Skris /* process table, return first match */ 17669587Sgreen for (i = 0; check[i].pat; i++) { 17792559Sdes if (match_pattern_list(version, check[i].pat, 17892559Sdes strlen(check[i].pat), 0) == 1) { 17960573Skris datafellows = check[i].bugs; 180263970Sdes debug("match: %s pat %s compat 0x%08x", 181263970Sdes version, check[i].pat, datafellows); 182224638Sbrooks /* 183224638Sbrooks * Check to see if the remote side is OpenSSH and not 184224638Sbrooks * HPN. It is utterly strange to check it from the 185224638Sbrooks * version string and expose the option that way. 186224638Sbrooks */ 187224638Sbrooks if (strstr(version,"OpenSSH") != NULL && 188224638Sbrooks strstr(version,"hpn") == NULL) { 189224638Sbrooks datafellows |= SSH_BUG_LARGEWINDOW; 190224638Sbrooks debug("Remote is not HPN-aware"); 191224638Sbrooks } 19260573Skris return; 19360573Skris } 19460573Skris } 19569587Sgreen debug("no match: %s", version); 19660573Skris} 19760573Skris 19860573Skris#define SEP "," 19960573Skrisint 20060573Skrisproto_spec(const char *spec) 20160573Skris{ 20265668Skris char *s, *p, *q; 20360573Skris int ret = SSH_PROTO_UNKNOWN; 20460573Skris 20561209Skris if (spec == NULL) 20661209Skris return ret; 20765668Skris q = s = xstrdup(spec); 20865668Skris for ((p = strsep(&q, SEP)); p && *p != '\0'; (p = strsep(&q, SEP))) { 20992559Sdes switch (atoi(p)) { 21060573Skris case 1: 21160573Skris if (ret == SSH_PROTO_UNKNOWN) 21260573Skris ret |= SSH_PROTO_1_PREFERRED; 21360573Skris ret |= SSH_PROTO_1; 21460573Skris break; 21560573Skris case 2: 21660573Skris ret |= SSH_PROTO_2; 21760573Skris break; 21860573Skris default: 219124211Sdes logit("ignoring bad proto spec: '%s'.", p); 22060573Skris break; 22160573Skris } 22260573Skris } 223263970Sdes free(s); 22460573Skris return ret; 22560573Skris} 22676262Sgreen 227263970Sdes/* 228263970Sdes * Filters a proposal string, excluding any algorithm matching the 'filter' 229263970Sdes * pattern list. 230263970Sdes */ 231263970Sdesstatic char * 232263970Sdesfilter_proposal(char *proposal, const char *filter) 23376262Sgreen{ 23492559Sdes Buffer b; 235263970Sdes char *orig_prop, *fix_prop; 23676262Sgreen char *cp, *tmp; 23776262Sgreen 23892559Sdes buffer_init(&b); 239263970Sdes tmp = orig_prop = xstrdup(proposal); 24092559Sdes while ((cp = strsep(&tmp, ",")) != NULL) { 241263970Sdes if (match_pattern_list(cp, filter, strlen(cp), 0) != 1) { 24292559Sdes if (buffer_len(&b) > 0) 24392559Sdes buffer_append(&b, ",", 1); 24492559Sdes buffer_append(&b, cp, strlen(cp)); 245263970Sdes } else 246263970Sdes debug2("Compat: skipping algorithm \"%s\"", cp); 24776262Sgreen } 24892559Sdes buffer_append(&b, "\0", 1); 249263970Sdes fix_prop = xstrdup(buffer_ptr(&b)); 25092559Sdes buffer_free(&b); 251263970Sdes free(orig_prop); 25276262Sgreen 253263970Sdes return fix_prop; 25476262Sgreen} 255263970Sdes 256263970Sdeschar * 257263970Sdescompat_cipher_proposal(char *cipher_prop) 258263970Sdes{ 259263970Sdes if (!(datafellows & SSH_BUG_BIGENDIANAES)) 260263970Sdes return cipher_prop; 261263970Sdes debug2("%s: original cipher proposal: %s", __func__, cipher_prop); 262263970Sdes cipher_prop = filter_proposal(cipher_prop, "aes*"); 263263970Sdes debug2("%s: compat cipher proposal: %s", __func__, cipher_prop); 264263970Sdes if (*cipher_prop == '\0') 265263970Sdes fatal("No supported ciphers found"); 266263970Sdes return cipher_prop; 267263970Sdes} 268263970Sdes 269263970Sdeschar * 270263970Sdescompat_pkalg_proposal(char *pkalg_prop) 271263970Sdes{ 272263970Sdes if (!(datafellows & SSH_BUG_RSASIGMD5)) 273263970Sdes return pkalg_prop; 274263970Sdes debug2("%s: original public key proposal: %s", __func__, pkalg_prop); 275263970Sdes pkalg_prop = filter_proposal(pkalg_prop, "ssh-rsa"); 276263970Sdes debug2("%s: compat public key proposal: %s", __func__, pkalg_prop); 277263970Sdes if (*pkalg_prop == '\0') 278263970Sdes fatal("No supported PK algorithms found"); 279263970Sdes return pkalg_prop; 280263970Sdes} 281263970Sdes 282264693Sdeschar * 283264693Sdescompat_kex_proposal(char *kex_prop) 284264693Sdes{ 285264693Sdes if (!(datafellows & SSH_BUG_CURVE25519PAD)) 286264693Sdes return kex_prop; 287264693Sdes debug2("%s: original KEX proposal: %s", __func__, kex_prop); 288264693Sdes kex_prop = filter_proposal(kex_prop, "curve25519-sha256@libssh.org"); 289264693Sdes debug2("%s: compat KEX proposal: %s", __func__, kex_prop); 290264693Sdes if (*kex_prop == '\0') 291264693Sdes fatal("No supported key exchange algorithms found"); 292264693Sdes return kex_prop; 293264693Sdes} 294264693Sdes 295