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