changeset 1317:19358268ea45

merge github
author Matt Johnston <matt@ucc.asn.au>
date Fri, 22 Jul 2016 00:04:47 +0800
parents 2c9dac2d6707 (diff) e742c0e9b941 (current diff)
children 8493172e4f3c 18c0b0e450ff
files
diffstat 11 files changed, 162 insertions(+), 86 deletions(-) [+]
line wrap: on
line diff
--- a/.hgsigs	Tue Jun 21 22:04:30 2016 +0800
+++ b/.hgsigs	Fri Jul 22 00:04:47 2016 +0800
@@ -21,3 +21,4 @@
 926e7275cef4f4f2a4251597ee4814748394824c 0 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
+9030ffdbe5625e35ed7189ab84a41dfc8d413e9c 0 iQIcBAABCgAGBQJXkOg0AAoJEESTFJTynGdzc1kP/3vSKCnhOOvjCjnpTQadYcCUq8vTNnfLHYVu0R4ItPa/jT6RmxoaYP+lZnLnnBx9+aX7kzwHsa9BUX3MbMEyLrOzX2I+bDJbNPhQyupyCuPYlf5Q9KVcO9YlpbsC4q5XBzCn3j2+pT8kSfi9uD8fgY3TgE4w9meINrfQAealfjwMLT8S/I49/ni0r+usSfk/dnSShJYDUO7Ja0VWbJea/GkkZTu30bCnMUZPjRApipU3hPP63WFjkSMT1rp2mAXbWqyr9lf8z32yxzM9nMSjq4ViRFzFlkGtE3EVRJ4PwkO7JuiWAMPJpiQcEr+r52cCsmWhiGyHuINo01MwoMO9/n6uL1WVa3mJcE9se3xBOvfgDu2FRFGCAdm1tef+AGVo9EG1uJXi0sX2yUc6DMeuYaRWrXMMlZh7zp9cuNU9Y/lLui9RFmq66yeXG3Z2B72doju3Ig5QGrNNw2AOsSzeHdAtOp6ychqPcl9QfIeJQG18KyPSefZKM3G8YRKBRIwXFEH6iZJe5ZIP4iXrHDMn2JqtTRtDqKR8VNDAgb9z4Ffx8QRxFyj5JzTTMM1GddHb9udLvTQlO0ULYG7hCSMRNzvUBE2aTw8frjLRyfyyg3QpDu/hz8op8s1ecE8rTCD8RuX9DiiylNozypPtGNS+UDbAmkc1PCWaRpPVl+9K6787
--- a/.hgtags	Tue Jun 21 22:04:30 2016 +0800
+++ b/.hgtags	Fri Jul 22 00:04:47 2016 +0800
@@ -53,3 +53,4 @@
 9a944a243f08be6b22d32f166a0690eb4872462b DROPBEAR_2015.71
 78b12b6549be08b0bea3da329b2578060a76ca31 DROPBEAR_2016.72
 309e1c4a87682b6ca7d80b8555a1db416c3cb7ac DROPBEAR_2016.73
+0ed3d2bbf956cb8a9bf0f4b5a86b7dd9688205cb DROPBEAR_2016.74
--- a/CHANGES	Tue Jun 21 22:04:30 2016 +0800
+++ b/CHANGES	Fri Jul 22 00:04:47 2016 +0800
@@ -1,3 +1,35 @@
+2016.74 - 21 July 2016
+
+- Security: Message printout was vulnerable to format string injection.
+
+  If specific usernames including "%" symbols can be created on a system
+  (validated by getpwnam()) then an attacker could run arbitrary code as root
+  when connecting to Dropbear server.
+
+  A dbclient user who can control username or host arguments could potentially
+  run arbitrary code as the dbclient user. This could be a problem if scripts
+  or webpages pass untrusted input to the dbclient program.
+
+- Security: dropbearconvert import of OpenSSH keys could run arbitrary code as
+  the local dropbearconvert user when parsing malicious key files
+
+- Security: dbclient could run arbitrary code as the local dbclient user if
+  particular -m or -c arguments are provided. This could be an issue where
+  dbclient is used in scripts.
+
+- Security: dbclient or dropbear server could expose process memory to the
+  running user if compiled with DEBUG_TRACE and running with -v
+
+  The security issues were reported by an anonymous researcher working with
+  Beyond Security's SecuriTeam Secure Disclosure www.beyondsecurity.com/ssd.html
+
+- Fix port forwarding failure when connecting to domains that have both
+  IPv4 and IPv6 addresses. The bug was introduced in 2015.68
+
+- Fix 100% CPU use while waiting for rekey to complete. Thanks to Zhang Hui P
+  for the patch
+
+
 2016.73 - 18 March 2016
 
 - Support syslog in dbclient, option -o usesyslog=yes. Patch from Konstantin Tokarev
--- a/buffer.c	Tue Jun 21 22:04:30 2016 +0800
+++ b/buffer.c	Fri Jul 22 00:04:47 2016 +0800
@@ -141,9 +141,10 @@
 /* increment the position by incr, negative values are allowed, to
  * decrement the pos*/
 void buf_incrpos(buffer* buf,  int incr) {
-	if (incr > BUF_MAX_INCR ||
-			(unsigned int)((int)buf->pos + incr) > buf->len 
-			|| ((int)buf->pos + incr) < 0) {
+	if (incr > BUF_MAX_INCR 
+		|| incr < -BUF_MAX_INCR 
+		|| (unsigned int)((int)buf->pos + incr) > buf->len
+		|| ((int)buf->pos + incr) < 0) {
 		dropbear_exit("Bad buf_incrpos");
 	}
 	buf->pos += incr;
@@ -184,7 +185,7 @@
  * the next len bytes from that position can be used */
 unsigned char* buf_getptr(buffer* buf, unsigned int len) {
 
-	if (buf->pos + len > buf->len) {
+	if (len > BUF_MAX_INCR || buf->pos + len > buf->len) {
 		dropbear_exit("Bad buf_getptr");
 	}
 	return &buf->data[buf->pos];
@@ -194,7 +195,7 @@
  * This allows writing past the used length, but not past the size */
 unsigned char* buf_getwriteptr(buffer* buf, unsigned int len) {
 
-	if (buf->pos + len > buf->size) {
+	if (len > BUF_MAX_INCR || buf->pos + len > buf->size) {
 		dropbear_exit("Bad buf_getwriteptr");
 	}
 	return &buf->data[buf->pos];
--- a/cli-main.c	Tue Jun 21 22:04:30 2016 +0800
+++ b/cli-main.c	Fri Jul 22 00:04:47 2016 +0800
@@ -98,29 +98,30 @@
 #endif /* DBMULTI stuff */
 
 static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
+	char exitmsg[150];
+	char fullmsg[300];
 
-	char fmtbuf[300];
-	char exitmsg[500];
+	/* Note that exit message must be rendered before session cleanup */
 
+	/* Render the formatted exit message */
+	vsnprintf(exitmsg, sizeof(exitmsg), format, param);
+
+	/* Add the prefix depending on session/auth state */
 	if (!sessinitdone) {
-		snprintf(fmtbuf, sizeof(fmtbuf), "Exited: %s",
-				format);
+		snprintf(fullmsg, sizeof(fullmsg), "Exited: %s", exitmsg);
 	} else {
-		snprintf(fmtbuf, sizeof(fmtbuf), 
+		snprintf(fullmsg, sizeof(fullmsg), 
 				"Connection to %s@%s:%s exited: %s", 
 				cli_opts.username, cli_opts.remotehost, 
-				cli_opts.remoteport, format);
+				cli_opts.remoteport, exitmsg);
 	}
 
-	/* Arguments to the exit printout may be unsafe to use after session_cleanup() */
-	vsnprintf(exitmsg, sizeof(exitmsg), fmtbuf, param);
-
 	/* Do the cleanup first, since then the terminal will be reset */
 	session_cleanup();
 	/* Avoid printing onwards from terminal cruft */
 	fprintf(stderr, "\n");
 
-	dropbear_log(LOG_INFO, "%s", exitmsg);;
+	dropbear_log(LOG_INFO, "%s", fullmsg);
 	exit(exitcode);
 }
 
--- a/common-algo.c	Tue Jun 21 22:04:30 2016 +0800
+++ b/common-algo.c	Fri Jul 22 00:04:47 2016 +0800
@@ -529,21 +529,6 @@
 	return NULL;
 }
 
-static void
-try_add_algo(const char *algo_name, algo_type *algos, 
-		const char *algo_desc, algo_type * new_algos, int *num_ret)
-{
-	algo_type *match_algo = check_algo(algo_name, algos);
-	if (!match_algo)
-	{
-		dropbear_log(LOG_WARNING, "This Dropbear program does not support '%s' %s algorithm", algo_name, algo_desc);
-		return;
-	}
-
-	new_algos[*num_ret] = *match_algo;
-	(*num_ret)++;
-}
-
 /* Checks a user provided comma-separated algorithm list for available
  * options. Any that are not acceptable are removed in-place. Returns the
  * number of valid algorithms. */
@@ -551,30 +536,43 @@
 check_user_algos(const char* user_algo_list, algo_type * algos, 
 		const char *algo_desc)
 {
-	algo_type new_algos[MAX_PROPOSED_ALGO];
-	/* this has two passes. first we sweep through the given list of
-	 * algorithms and mark them as usable=2 in the algo_type[] array... */
-	int num_ret = 0;
+	algo_type new_algos[MAX_PROPOSED_ALGO+1];
 	char *work_list = m_strdup(user_algo_list);
-	char *last_name = work_list;
+	char *start = work_list;
 	char *c;
-	for (c = work_list; *c; c++)
+	int n;
+	/* So we can iterate and look for null terminator */
+	memset(new_algos, 0x0, sizeof(new_algos));
+	for (c = work_list, n = 0; ; c++)
 	{
-		if (*c == ',')
-		{
+		char oc = *c;
+		if (n >= MAX_PROPOSED_ALGO) {
+			dropbear_exit("Too many algorithms '%s'", user_algo_list);
+		}
+		if (*c == ',' || *c == '\0') {
+			algo_type *match_algo = NULL;
 			*c = '\0';
-			try_add_algo(last_name, algos, algo_desc, new_algos, &num_ret);
+			match_algo = check_algo(start, algos);
+			if (match_algo) {
+				if (check_algo(start, new_algos)) {
+					TRACE(("Skip repeated algorithm '%s'", start))
+				} else {
+					new_algos[n] = *match_algo;
+					n++;
+				}
+			} else {
+				dropbear_log(LOG_WARNING, "This Dropbear program does not support '%s' %s algorithm", start, algo_desc);
+			}
 			c++;
-			last_name = c;
+			start = c;
+		}
+		if (oc == '\0') {
+			break;
 		}
 	}
-	try_add_algo(last_name, algos, algo_desc, new_algos, &num_ret);
 	m_free(work_list);
-
-	new_algos[num_ret].name = NULL;
-
-	/* Copy one more as a blank delimiter */
-	memcpy(algos, new_algos, sizeof(*new_algos) * (num_ret+1));
-	return num_ret;
+	/* n+1 to include a null terminator */
+	memcpy(algos, new_algos, sizeof(*new_algos) * (n+1));
+	return n;
 }
 #endif /* DROPBEAR_USER_ALGO_LIST */
--- a/common-session.c	Tue Jun 21 22:04:30 2016 +0800
+++ b/common-session.c	Fri Jul 22 00:04:47 2016 +0800
@@ -361,7 +361,7 @@
 	}
 
 	if (!done) {
-		TRACE(("err: %s for '%s'\n", strerror(errno), linebuf))
+		TRACE(("error reading remote ident: %s\n", strerror(errno)))
 		ses.remoteclosed();
 	} else {
 		/* linebuf is already null terminated */
--- a/debian/changelog	Tue Jun 21 22:04:30 2016 +0800
+++ b/debian/changelog	Fri Jul 22 00:04:47 2016 +0800
@@ -1,3 +1,9 @@
+dropbear (2016.74-0.1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Matt Johnston <[email protected]>  Thu, 21 Jul 2016 22:51:57 +0800
+
 dropbear (2016.73-0.1) unstable; urgency=low
 
   * New upstream release.
--- a/keyimport.c	Tue Jun 21 22:04:30 2016 +0800
+++ b/keyimport.c	Fri Jul 22 00:04:47 2016 +0800
@@ -62,6 +62,8 @@
 static int dropbear_write(const char*filename, sign_key * key);
 static sign_key *dropbear_read(const char* filename);
 
+static int toint(unsigned u);
+
 #if 0
 static int sshcom_encrypted(const char *filename, char **comment);
 static struct ssh2_userkey *sshcom_read(const char *filename, char *passphrase);
@@ -243,12 +245,11 @@
 	if ((*p & 0x1F) == 0x1F) {
 		*id = 0;
 		while (*p & 0x80) {
-			*id = (*id << 7) | (*p & 0x7F);
 			p++, sourcelen--;
 			if (sourcelen == 0)
 				return -1;
+			*id = (*id << 7) | (*p & 0x7F);
 		}
-		*id = (*id << 7) | (*p & 0x7F);
 		p++, sourcelen--;
 	} else {
 		*id = *p & 0x1F;
@@ -259,19 +260,26 @@
 		return -1;
 
 	if (*p & 0x80) {
+		unsigned len;
 		int n = *p & 0x7F;
 		p++, sourcelen--;
 		if (sourcelen < n)
 			return -1;
-		*length = 0;
+		len = 0;
 		while (n--)
-			*length = (*length << 8) | (*p++);
+			len = (len << 8) | (*p++);
 		sourcelen -= n;
+		*length = toint(len);
 	} else {
 		*length = *p;
 		p++, sourcelen--;
 	}
 
+	if (*length < 0) {
+		printf("Negative ASN.1 length\n");
+		return -1;
+	}
+
 	return p - (unsigned char *) source;
 }
 
@@ -469,7 +477,7 @@
 	m_burn(buffer, sizeof(buffer));
 	return ret;
 
-	error:
+error:
 	m_burn(buffer, sizeof(buffer));
 	if (ret) {
 		if (ret->keyblob) {
@@ -584,8 +592,9 @@
 	/* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */
 	ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags);
 	p += ret;
-	if (ret < 0 || id != 16) {
-		errmsg = "ASN.1 decoding failure - wrong password?";
+	if (ret < 0 || id != 16 || len < 0 ||
+		key->keyblob+key->keyblob_len-p < len) {
+				errmsg = "ASN.1 decoding failure";
 		goto error;
 	}
 
@@ -619,7 +628,7 @@
 		ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
 							  &id, &len, &flags);
 		p += ret;
-		if (ret < 0 || id != 2 ||
+		if (ret < 0 || id != 2 || len < 0 ||
 			key->keyblob+key->keyblob_len-p < len) {
 			errmsg = "ASN.1 decoding failure";
 			goto error;
@@ -685,7 +694,7 @@
 							  &id, &len, &flags);
 		p += ret;
 		/* id==4 for octet string */
-		if (ret < 0 || id != 4 ||
+				if (ret < 0 || id != 4 || len < 0 ||
 			key->keyblob+key->keyblob_len-p < len) {
 			errmsg = "ASN.1 decoding failure";
 			goto error;
@@ -699,7 +708,7 @@
 							  &id, &len, &flags);
 		p += ret;
 		/* id==0 */
-		if (ret < 0 || id != 0) {
+				if (ret < 0 || id != 0 || len < 0) {
 			errmsg = "ASN.1 decoding failure";
 			goto error;
 		}
@@ -708,7 +717,7 @@
 							  &id, &len, &flags);
 		p += ret;
 		/* id==6 for object */
-		if (ret < 0 || id != 6 ||
+				if (ret < 0 || id != 6 || len < 0 ||
 			key->keyblob+key->keyblob_len-p < len) {
 			errmsg = "ASN.1 decoding failure";
 			goto error;
@@ -747,7 +756,7 @@
 							  &id, &len, &flags);
 		p += ret;
 		/* id==1 */
-		if (ret < 0 || id != 1) {
+				if (ret < 0 || id != 1 || len < 0) {
 			errmsg = "ASN.1 decoding failure";
 			goto error;
 		}
@@ -756,7 +765,7 @@
 							  &id, &len, &flags);
 		p += ret;
 		/* id==3 for bit string */
-		if (ret < 0 || id != 3 ||
+				if (ret < 0 || id != 3 || len < 0 ||
 			key->keyblob+key->keyblob_len-p < len) {
 			errmsg = "ASN.1 decoding failure";
 			goto error;
@@ -1381,7 +1390,7 @@
 			memset(ret->keyblob, 0, ret->keyblob_size);
 			m_free(ret->keyblob);
 		}
-		memset(&ret, 0, sizeof(ret));
+		memset(ret, 0, sizeof(*ret));
 		m_free(ret);
 	}
 	return NULL;
@@ -1409,11 +1418,12 @@
 	pos = 8;
 	if (key->keyblob_len < pos+4)
 		goto done;					 /* key is far too short */
-	pos += 4 + GET_32BIT(key->keyblob + pos);   /* skip key type */
-	if (key->keyblob_len < pos+4)
+	len = toint(GET_32BIT(key->keyblob + pos));
+	if (len < 0 || len > key->keyblob_len - pos - 4)
 		goto done;					 /* key is far too short */
-	len = GET_32BIT(key->keyblob + pos);   /* find cipher-type length */
-	if (key->keyblob_len < pos+4+len)
+	pos += 4 + len;                    /* skip key type */
+	len = toint(GET_32BIT(key->keyblob + pos)); /* find cipher-type length */
+	if (len < 0 || len > key->keyblob_len - pos - 4)
 		goto done;					 /* cipher type string is incomplete */
 	if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4))
 		answer = 1;
@@ -1422,15 +1432,14 @@
 	*comment = dupstr(key->comment);
 	memset(key->keyblob, 0, key->keyblob_size);
 	m_free(key->keyblob);
-	memset(&key, 0, sizeof(key));
+	memset(key, 0, sizeof(*key));
 	m_free(key);
 	return answer;
 }
 
 static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret)
 {
-	int bits;
-	int bytes;
+	unsigned bits, bytes;
 	unsigned char *d = (unsigned char *) data;
 
 	if (len < 4)
@@ -1483,7 +1492,7 @@
 	struct ssh2_userkey *ret = NULL, *retkey;
 	const struct ssh_signkey *alg;
 	unsigned char *blob = NULL;
-	int blobsize, publen, privlen;
+	int blobsize = 0, publen, privlen;
 
 	if (!key)
 		return NULL;
@@ -1603,8 +1612,8 @@
 	/*
 	 * Strip away the containing string to get to the real meat.
 	 */
-	len = GET_32BIT(ciphertext);
-	if (len > cipherlen-4) {
+	len = toint(GET_32BIT(ciphertext));
+	if (len < 0 || len > cipherlen-4) {
 		errmsg = "containing string was ill-formed";
 		goto error;
 	}
@@ -1671,7 +1680,8 @@
 		publen = pos;
 		pos += put_mp(blob+pos, x.start, x.bytes);
 		privlen = pos - publen;
-	}
+	} else
+		return NULL;
 
 	dropbear_assert(privlen > 0);			   /* should have bombed by now if not */
 
@@ -1695,7 +1705,7 @@
 	}
 	memset(key->keyblob, 0, key->keyblob_size);
 	m_free(key->keyblob);
-	memset(&key, 0, sizeof(key));
+	memset(key, 0, sizeof(*key));
 	m_free(key);
 	return ret;
 }
@@ -1908,3 +1918,27 @@
 	return ret;
 }
 #endif /* ssh.com stuff disabled */
+
+/* From PuTTY misc.c */
+static int toint(unsigned u)
+{
+	/*
+	 * Convert an unsigned to an int, without running into the
+	 * undefined behaviour which happens by the strict C standard if
+	 * the value overflows. You'd hope that sensible compilers would
+	 * do the sensible thing in response to a cast, but actually I
+	 * don't trust modern compilers not to do silly things like
+	 * assuming that _obviously_ you wouldn't have caused an overflow
+	 * and so they can elide an 'if (i < 0)' test immediately after
+	 * the cast.
+	 *
+	 * Sensible compilers ought of course to optimise this entire
+	 * function into 'just return the input value'!
+	 */
+	if (u <= (unsigned)INT_MAX)
+		return (int)u;
+	else if (u >= (unsigned)INT_MIN)   /* wrap in cast _to_ unsigned is OK */
+		return INT_MIN + (int)(u - (unsigned)INT_MIN);
+	else
+		return INT_MIN; /* fallback; should never occur on binary machines */
+}
--- a/svr-session.c	Tue Jun 21 22:04:30 2016 +0800
+++ b/svr-session.c	Fri Jul 22 00:04:47 2016 +0800
@@ -144,31 +144,33 @@
 
 /* failure exit - format must be <= 100 chars */
 void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
-
-	char fmtbuf[300];
+	char exitmsg[150];
+	char fullmsg[300];
 	int i;
 
+	/* Render the formatted exit message */
+	vsnprintf(exitmsg, sizeof(exitmsg), format, param);
+
+	/* Add the prefix depending on session/auth state */
 	if (!sessinitdone) {
 		/* before session init */
-		snprintf(fmtbuf, sizeof(fmtbuf), 
-				"Early exit: %s", format);
+		snprintf(fullmsg, sizeof(fullmsg), "Early exit: %s", exitmsg);
 	} else if (ses.authstate.authdone) {
 		/* user has authenticated */
-		snprintf(fmtbuf, sizeof(fmtbuf),
+		snprintf(fullmsg, sizeof(fullmsg),
 				"Exit (%s): %s", 
-				ses.authstate.pw_name, format);
+				ses.authstate.pw_name, exitmsg);
 	} else if (ses.authstate.pw_name) {
 		/* we have a potential user */
-		snprintf(fmtbuf, sizeof(fmtbuf), 
+		snprintf(fullmsg, sizeof(fullmsg), 
 				"Exit before auth (user '%s', %u fails): %s",
-				ses.authstate.pw_name, ses.authstate.failcount, format);
+				ses.authstate.pw_name, ses.authstate.failcount, exitmsg);
 	} else {
 		/* before userauth */
-		snprintf(fmtbuf, sizeof(fmtbuf), 
-				"Exit before auth: %s", format);
+		snprintf(fullmsg, sizeof(fullmsg), "Exit before auth: %s", exitmsg);
 	}
 
-	_dropbear_log(LOG_INFO, fmtbuf, param);
+	dropbear_log(LOG_INFO, "%s", fullmsg);
 
 #if DROPBEAR_VFORK
 	/* For uclinux only the main server process should cleanup - we don't want
--- a/sysoptions.h	Tue Jun 21 22:04:30 2016 +0800
+++ b/sysoptions.h	Fri Jul 22 00:04:47 2016 +0800
@@ -4,7 +4,7 @@
  *******************************************************************/
 
 #ifndef DROPBEAR_VERSION
-#define DROPBEAR_VERSION "2016.73"
+#define DROPBEAR_VERSION "2016.74"
 #endif
 
 #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION