Index: openssh-6.6p1/defines.h
===================================================================
--- openssh-6.6p1.orig/defines.h
+++ openssh-6.6p1/defines.h
@@ -826,4 +826,10 @@ struct winsize {
 # define arc4random_stir()
 #endif
 
+/*
+ * Define to enable additional scp file name validation against
+ * malicious servers.
+ */
+#define USE_SCP_NAME_VALIDATOR 1
+
 #endif /* _DEFINES_H */
Index: openssh-6.6p1/progressmeter.c
===================================================================
--- openssh-6.6p1.orig/progressmeter.c
+++ openssh-6.6p1/progressmeter.c
@@ -39,6 +39,7 @@
 #include "progressmeter.h"
 #include "atomicio.h"
 #include "misc.h"
+#include "utf8.h"
 
 #define DEFAULT_WINSIZE 80
 #define MAX_WINSIZE 512
@@ -161,7 +162,10 @@ refresh_progress_meter(void)
 	buf[0] = '\0';
 	file_len = win_size - 35;
 	if (file_len > 0) {
-		len = snprintf(buf, file_len + 1, "\r%s", file);
+		char visbuf[MAX_WINSIZE + 1];
+		(void) snmprintf(visbuf, sizeof(visbuf),
+		                 NULL, "%s", file);
+		len = snprintf(buf, file_len + 1, "\r%s", visbuf);
 		if (len < 0)
 			len = 0;
 		if (len >= file_len + 1)
Index: openssh-6.6p1/regress/scp-ssh-wrapper.sh
===================================================================
--- openssh-6.6p1.orig/regress/scp-ssh-wrapper.sh
+++ openssh-6.6p1/regress/scp-ssh-wrapper.sh
@@ -51,6 +51,18 @@ badserver_4)
 	echo "C755 2 file"
 	echo "X"
 	;;
+badserver_5)
+	echo "D0555 0 "
+	echo "X"
+	;;
+badserver_6)
+	echo "D0555 0 ."
+	echo "X"
+	;;
+badserver_7)
+	echo "C0755 2 extrafile"
+	echo "X"
+	;;
 *)
 	set -- $arg
 	shift
Index: openssh-6.6p1/regress/scp.sh
===================================================================
--- openssh-6.6p1.orig/regress/scp.sh
+++ openssh-6.6p1/regress/scp.sh
@@ -25,6 +25,7 @@ export SCP # used in scp-ssh-wrapper.scp
 scpclean() {
 	rm -rf ${COPY} ${COPY2} ${DIR} ${DIR2}
 	mkdir ${DIR} ${DIR2}
+	chmod 755 ${DIR} ${DIR2}
 }
 
 verbose "$tid: simple copy local file to local file"
@@ -101,7 +102,7 @@ if [ ! -z "$SUDO" ]; then
 	$SUDO rm ${DIR2}/copy
 fi
 
-for i in 0 1 2 3 4; do
+for i in 0 1 2 3 4 5 6 7; do
 	verbose "$tid: disallow bad server #$i"
 	SCPTESTMODE=badserver_$i
 	export DIR SCPTESTMODE
@@ -113,6 +114,15 @@ for i in 0 1 2 3 4; do
 	scpclean
 	$SCP -r $scpopts somehost:${DATA} ${DIR2} >/dev/null 2>/dev/null
 	[ -d ${DIR}/dotpathdir ] && fail "allows dir creation outside of subdir"
+
+	scpclean
+	$SCP -pr $scpopts somehost:${DATA} ${DIR2} >/dev/null 2>/dev/null
+	[ ! -w ${DIR2} ] && fail "allows target root attribute change"
+
+	scpclean
+	$SCP $scpopts somehost:${DATA} ${DIR2} >/dev/null 2>/dev/null
+	[ -e ${DIR2}/extrafile ] && fail "allows extranous object creation"
+	rm -f ${DIR2}/extrafile
 done
 
 verbose "$tid: detect non-directory target"
Index: openssh-6.6p1/scp.c
===================================================================
--- openssh-6.6p1.orig/scp.c
+++ openssh-6.6p1/scp.c
@@ -17,6 +17,7 @@
 /*
  * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
  * Copyright (c) 1999 Aaron Campbell.  All rights reserved.
+ * Copyright (c) 2019 Harry Sintonen.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -88,6 +89,14 @@
 #ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
 #endif
+#ifdef USE_SCP_NAME_VALIDATOR
+# include <libgen.h>
+# ifdef USE_SYSTEM_GLOB
+#  include <glob.h>
+# else
+#  include "openbsd-compat/glob.h"
+# endif
+#endif
 #include <sys/wait.h>
 #include <sys/uio.h>
 
@@ -265,6 +274,18 @@ do_cmd(char *host, char *remuser, char *
 		close(pout[0]);
 		dup2(pin[0], 0);
 		dup2(pout[1], 1);
+		/*
+		 * If we're not expecting output to stderr, redirect it to void.
+		 * This helps avoiding output manipulation attacks by malicious
+		 * servers.
+		 */
+		if (!verbose_mode) {
+			int fd = open("/dev/null", O_WRONLY);
+			if (fd != -1) {
+				dup2(fd, 2);
+				close(fd);
+			}
+		}
 		close(pin[0]);
 		close(pout[1]);
 
@@ -357,9 +378,20 @@ int pflag, iamremote, iamrecursive, targ
 #define	CMDNEEDS	64
 char cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
 
+#ifdef USE_SCP_NAME_VALIDATOR
+typedef struct {
+	const char *pattern;
+	int depth;
+} SINKDATA;
+#endif
+
 int response(void);
 void rsource(char *, struct stat *);
+#ifdef USE_SCP_NAME_VALIDATOR
+void sink(int, char *[], SINKDATA *);
+#else
 void sink(int, char *[]);
+#endif
 void source(int, char *[]);
 void tolocal(int, char *[]);
 void toremote(char *, int, char *[]);
@@ -494,7 +526,11 @@ main(int argc, char **argv)
 	}
 	if (tflag) {
 		/* Receive data. */
+#ifdef USE_SCP_NAME_VALIDATOR
+		sink(argc, argv, NULL);
+#else
 		sink(argc, argv);
+#endif
 		exit(errs != 0);
 	}
 	if (argc < 2)
@@ -694,6 +730,9 @@ tolocal(int argc, char **argv)
 	char *bp, *host, *src, *suser;
 	arglist alist;
 	int i;
+#ifdef USE_SCP_NAME_VALIDATOR
+	SINKDATA sinkdata;
+#endif
 
 	memset(&alist, '\0', sizeof(alist));
 	alist.list = NULL;
@@ -734,7 +773,13 @@ tolocal(int argc, char **argv)
 			continue;
 		}
 		free(bp);
+#ifdef USE_SCP_NAME_VALIDATOR
+		sinkdata.pattern = basename(xstrdup(src));
+		sinkdata.depth = 0;
+		sink(1, argv + argc - 1, &sinkdata);
+#else
 		sink(1, argv + argc - 1);
+#endif
 		(void) close(remin);
 		remin = remout = -1;
 	}
@@ -898,8 +943,71 @@ rsource(char *name, struct stat *statp)
 	(void) response();
 }
 
+#ifdef USE_SCP_NAME_VALIDATOR
+#ifdef GLOB_ALTDIRFUNC
+struct fakedir {
+	struct dirent de;
+#ifdef BROKEN_ONE_BYTE_DIRENT_D_NAME
+	char denamebuf[256];
+#endif
+	struct dirent tmpde;
+#ifdef BROKEN_ONE_BYTE_DIRENT_D_NAME
+	char tmpdenamebuf[2]; /* only needs to hold "." or ".." */
+#endif
+	int dirindex;
+};
+static struct fakedir fakedir;
+static void
+g_closedir(void *ptr)
+{
+}
+static struct dirent *
+g_readdir(void *ptr)
+{
+	struct fakedir *fd = ptr;
+	switch (fd->dirindex) {
+		case 1:
+		case 2:
+			strcpy(fd->tmpde.d_name, fd->dirindex == 1 ? "." : "..");
+			fd->tmpde.d_type = DT_DIR;
+			fd->tmpde.d_ino = fd->dirindex++;
+			return &fd->tmpde;
+		case 3:
+			fd->de.d_ino = fd->dirindex++;
+			return &fd->de;
+	}
+	return NULL;
+}
+static void *
+g_opendir(const char *name)
+{
+	if (strcmp(name, ".") != 0) {
+		errno = ENOENT;
+		return NULL;
+	}
+	fakedir.dirindex = 1;
+	return &fakedir;
+}
+static int
+g_stat(const char *name, struct stat *st)
+{
+	if (strcmp(name, fakedir.de.d_name) != 0) {
+		errno = ENOENT;
+		return -1;
+	}
+	memset(st, 0, sizeof(*st));
+	st->st_mode = fakedir.de.d_type == DT_DIR ? S_IFDIR : S_IFREG;
+	return 0;
+}
+#endif
+#endif
+
 void
+#ifdef USE_SCP_NAME_VALIDATOR
+sink(int argc, char **argv, SINKDATA *sinkdata)
+#else
 sink(int argc, char **argv)
+#endif
 {
 	static BUF buffer;
 	struct stat stb;
@@ -1032,6 +1140,65 @@ sink(int argc, char **argv)
 			run_err("error: unexpected filename: %s", cp);
 			exit(1);
 		}
+
+#ifdef USE_SCP_NAME_VALIDATOR
+		if (sinkdata) {
+			/*
+			 * Validate the item name returned by the server for
+			 * attempts to modify the current directory attributes.
+			 *
+			 * Only allow it on root level and only if it was
+			 * explicitly requested by using "host:" or "dirname/."
+			 */
+			if (strcmp(cp, ".") == 0 &&
+			    (sinkdata->depth != 0 || strcmp(sinkdata->pattern, ".") != 0)) {
+				run_err("error: unexpected filename: %s", cp);
+				exit(1);
+			}
+		}
+#ifdef GLOB_ALTDIRFUNC
+		/* Use glob(3) function to validate the item name against
+		 * the last path element (stored in sinkdata->pattern).
+		 *
+		 * We verify that the items returned at the target
+		 * directory level (depth 0) match this pattern.
+		 *
+		 * While a limited check, it will prevent some of the
+		 * potential attacks by a malicious server.
+		 */
+		if (sinkdata && sinkdata->depth == 0) {
+			glob_t gl;
+			int rc;
+#ifdef BROKEN_ONE_BYTE_DIRENT_D_NAME
+			if (strlen(cp) >= 256) {
+#else
+			if (strlen(cp) >= sizeof(fakedir.de.d_name)) {
+#endif
+				run_err("error: excessively long filename: %s", cp);
+				exit(1);
+			}
+			fakedir.de.d_type = buf[0] == 'D' ? DT_DIR : DT_REG;
+			strcpy(fakedir.de.d_name, cp);
+
+			memset(&gl, 0, sizeof(gl));
+			gl.gl_closedir = g_closedir;
+			gl.gl_readdir = g_readdir;
+			gl.gl_opendir = g_opendir;
+			gl.gl_lstat = g_stat;
+			gl.gl_stat = g_stat;
+
+			rc = glob(sinkdata->pattern, GLOB_ALTDIRFUNC|GLOB_NOSORT, NULL, &gl);
+			globfree(&gl);
+			if (rc != 0) {
+				if (rc == GLOB_NOMATCH)
+					run_err("error: unexpected filename: %s", cp);
+				else
+					run_err("error: glob error %d", rc);
+				exit(1);
+			}
+		}
+#endif
+#endif
 		if (targisdir) {
 			static char *namebuf;
 			static size_t cursize;
@@ -1069,7 +1236,15 @@ sink(int argc, char **argv)
 					goto bad;
 			}
 			vect[0] = xstrdup(np);
+#ifdef USE_SCP_NAME_VALIDATOR
+			if (sinkdata)
+				sinkdata->depth++;
+			sink(1, vect, sinkdata);
+			if (sinkdata)
+				sinkdata->depth--;
+#else
 			sink(1, vect);
+#endif
 			if (setimes) {
 				setimes = 0;
 				if (utimes(vect[0], tv) < 0)
Index: openssh-6.6p1/utf8.h
===================================================================
--- /dev/null
+++ openssh-6.6p1/utf8.h
@@ -0,0 +1,25 @@
+/* $OpenBSD: utf8.h,v 1.1 2016/05/25 23:48:45 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+int	 mprintf(const char *, ...)
+	     __attribute__((format(printf, 1, 2)));
+int	 fmprintf(FILE *, const char *, ...)
+	     __attribute__((format(printf, 2, 3)));
+int	 vfmprintf(FILE *, const char *, va_list);
+int	 snmprintf(char *, size_t, int *, const char *, ...)
+	     __attribute__((format(printf, 4, 5)));
+void	 msetlocale(void);
Index: openssh-6.6p1/utf8.c
===================================================================
--- /dev/null
+++ openssh-6.6p1/utf8.c
@@ -0,0 +1,340 @@
+/* $OpenBSD: utf8.c,v 1.8 2018/08/21 13:56:27 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Utility functions for multibyte-character handling,
+ * in particular to sanitize untrusted strings for terminal output.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#ifdef HAVE_LANGINFO_H
+# include <langinfo.h>
+#endif
+#include <limits.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
+# include <vis.h>
+#endif
+#ifdef HAVE_WCHAR_H
+# include <wchar.h>
+#endif
+
+#include "utf8.h"
+
+static int	 dangerous_locale(void);
+static int	 grow_dst(char **, size_t *, size_t, char **, size_t);
+static int	 vasnmprintf(char **, size_t, int *, const char *, va_list);
+
+
+/*
+ * For US-ASCII and UTF-8 encodings, we can safely recover from
+ * encoding errors and from non-printable characters.  For any
+ * other encodings, err to the side of caution and abort parsing:
+ * For state-dependent encodings, recovery is impossible.
+ * For arbitrary encodings, replacement of non-printable
+ * characters would be non-trivial and too fragile.
+ * The comments indicate what nl_langinfo(CODESET)
+ * returns for US-ASCII on various operating systems.
+ */
+
+static int
+dangerous_locale(void) {
+	char	*loc;
+
+	loc = nl_langinfo(CODESET);
+	return strcmp(loc, "UTF-8") != 0 &&
+	    strcmp(loc, "US-ASCII") != 0 &&		/* OpenBSD */
+	    strcmp(loc, "ANSI_X3.4-1968") != 0 &&	/* Linux */
+	    strcmp(loc, "ISO8859-1") != 0 &&		/* AIX */
+	    strcmp(loc, "646") != 0 &&			/* Solaris, NetBSD */
+	    strcmp(loc, "") != 0;			/* Solaris 6 */
+}
+
+static int
+grow_dst(char **dst, size_t *sz, size_t maxsz, char **dp, size_t need)
+{
+	char	*tp;
+	size_t	 tsz;
+
+	if (*dp + need < *dst + *sz)
+		return 0;
+	tsz = *sz + 128;
+	if (tsz > maxsz)
+		tsz = maxsz;
+	if ((tp = recallocarray(*dst, *sz, tsz, 1)) == NULL)
+		return -1;
+	*dp = tp + (*dp - *dst);
+	*dst = tp;
+	*sz = tsz;
+	return 0;
+}
+
+/*
+ * The following two functions limit the number of bytes written,
+ * including the terminating '\0', to sz.  Unless wp is NULL,
+ * they limit the number of display columns occupied to *wp.
+ * Whichever is reached first terminates the output string.
+ * To stay close to the standard interfaces, they return the number of
+ * non-NUL bytes that would have been written if both were unlimited.
+ * If wp is NULL, newline, carriage return, and tab are allowed;
+ * otherwise, the actual number of columns occupied by what was
+ * written is returned in *wp.
+ */
+
+static int
+vasnmprintf(char **str, size_t maxsz, int *wp, const char *fmt, va_list ap)
+{
+	char	*src;	/* Source string returned from vasprintf. */
+	char	*sp;	/* Pointer into src. */
+	char	*dst;	/* Destination string to be returned. */
+	char	*dp;	/* Pointer into dst. */
+	char	*tp;	/* Temporary pointer for dst. */
+	size_t	 sz;	/* Number of bytes allocated for dst. */
+	wchar_t	 wc;	/* Wide character at sp. */
+	int	 len;	/* Number of bytes in the character at sp. */
+	int	 ret;	/* Number of bytes needed to format src. */
+	int	 width;	/* Display width of the character wc. */
+	int	 total_width, max_width, print;
+
+	src = NULL;
+	if ((ret = vasprintf(&src, fmt, ap)) <= 0)
+		goto fail;
+
+	sz = strlen(src) + 1;
+	if ((dst = malloc(sz)) == NULL) {
+		free(src);
+		ret = -1;
+		goto fail;
+	}
+
+	if (maxsz > INT_MAX)
+		maxsz = INT_MAX;
+
+	sp = src;
+	dp = dst;
+	ret = 0;
+	print = 1;
+	total_width = 0;
+	max_width = wp == NULL ? INT_MAX : *wp;
+	while (*sp != '\0') {
+		if ((len = mbtowc(&wc, sp, MB_CUR_MAX)) == -1) {
+			(void)mbtowc(NULL, NULL, MB_CUR_MAX);
+			if (dangerous_locale()) {
+				ret = -1;
+				break;
+			}
+			len = 1;
+			width = -1;
+		} else if (wp == NULL &&
+		    (wc == L'\n' || wc == L'\r' || wc == L'\t')) {
+			/*
+			 * Don't use width uninitialized; the actual
+			 * value doesn't matter because total_width
+			 * is only returned for wp != NULL.
+			 */
+			width = 0;
+		} else if ((width = wcwidth(wc)) == -1 &&
+		    dangerous_locale()) {
+			ret = -1;
+			break;
+		}
+
+		/* Valid, printable character. */
+
+		if (width >= 0) {
+			if (print && (dp - dst >= (int)maxsz - len ||
+			    total_width > max_width - width))
+				print = 0;
+			if (print) {
+				if (grow_dst(&dst, &sz, maxsz,
+				    &dp, len) == -1) {
+					ret = -1;
+					break;
+				}
+				total_width += width;
+				memcpy(dp, sp, len);
+				dp += len;
+			}
+			sp += len;
+			if (ret >= 0)
+				ret += len;
+			continue;
+		}
+
+		/* Escaping required. */
+
+		while (len > 0) {
+			if (print && (dp - dst >= (int)maxsz - 4 ||
+			    total_width > max_width - 4))
+				print = 0;
+			if (print) {
+				if (grow_dst(&dst, &sz, maxsz,
+				    &dp, 4) == -1) {
+					ret = -1;
+					break;
+				}
+				tp = vis(dp, *sp, VIS_OCTAL | VIS_ALL, 0);
+				width = tp - dp;
+				total_width += width;
+				dp = tp;
+			} else
+				width = 4;
+			len--;
+			sp++;
+			if (ret >= 0)
+				ret += width;
+		}
+		if (len > 0)
+			break;
+	}
+	free(src);
+	*dp = '\0';
+	*str = dst;
+	if (wp != NULL)
+		*wp = total_width;
+
+	/*
+	 * If the string was truncated by the width limit but
+	 * would have fit into the size limit, the only sane way
+	 * to report the problem is using the return value, such
+	 * that the usual idiom "if (ret < 0 || ret >= sz) error"
+	 * works as expected.
+	 */
+
+	if (ret < (int)maxsz && !print)
+		ret = -1;
+	return ret;
+
+fail:
+	if (wp != NULL)
+		*wp = 0;
+	if (ret == 0) {
+		*str = src;
+		return 0;
+	} else {
+		*str = NULL;
+		return -1;
+	}
+}
+
+int
+snmprintf(char *str, size_t sz, int *wp, const char *fmt, ...)
+{
+	va_list	 ap;
+	char	*cp;
+	int	 ret;
+
+	va_start(ap, fmt);
+	ret = vasnmprintf(&cp, sz, wp, fmt, ap);
+	va_end(ap);
+	if (cp != NULL) {
+		(void)strlcpy(str, cp, sz);
+		free(cp);
+	} else
+		*str = '\0';
+	return ret;
+}
+
+/*
+ * To stay close to the standard interfaces, the following functions
+ * return the number of non-NUL bytes written.
+ */
+
+int
+vfmprintf(FILE *stream, const char *fmt, va_list ap)
+{
+	char	*str;
+	int	 ret;
+
+	if ((ret = vasnmprintf(&str, INT_MAX, NULL, fmt, ap)) < 0)
+		return -1;
+	if (fputs(str, stream) == EOF)
+		ret = -1;
+	free(str);
+	return ret;
+}
+
+int
+fmprintf(FILE *stream, const char *fmt, ...)
+{
+	va_list	 ap;
+	int	 ret;
+
+	va_start(ap, fmt);
+	ret = vfmprintf(stream, fmt, ap);
+	va_end(ap);
+	return ret;
+}
+
+int
+mprintf(const char *fmt, ...)
+{
+	va_list	 ap;
+	int	 ret;
+
+	va_start(ap, fmt);
+	ret = vfmprintf(stdout, fmt, ap);
+	va_end(ap);
+	return ret;
+}
+
+/*
+ * Set up libc for multibyte output in the user's chosen locale.
+ *
+ * XXX: we are known to have problems with Turkish (i/I confusion) so we
+ *      deliberately fall back to the C locale for now. Longer term we should
+ *      always prefer to select C.[encoding] if possible, but there's no
+ *      standardisation in locales between systems, so we'll need to survey
+ *      what's out there first.
+ */
+void
+msetlocale(void)
+{
+	const char *vars[] = { "LC_ALL", "LC_CTYPE", "LANG", NULL };
+	char *cp;
+	int i;
+
+	/*
+	 * We can't yet cope with dotless/dotted I in Turkish locales,
+	 * so fall back to the C locale for these.
+	 */
+	for (i = 0; vars[i] != NULL; i++) {
+		if ((cp = getenv(vars[i])) == NULL)
+			continue;
+		if (strncasecmp(cp, "TR", 2) != 0)
+			break;
+		/*
+		 * If we're in a UTF-8 locale then prefer to use
+		 * the C.UTF-8 locale (or equivalent) if it exists.
+		 */
+		if ((strcasestr(cp, "UTF-8") != NULL ||
+		    strcasestr(cp, "UTF8") != NULL) &&
+		    (setlocale(LC_CTYPE, "C.UTF-8") != NULL ||
+		    setlocale(LC_CTYPE, "POSIX.UTF-8") != NULL))
+			return;
+		setlocale(LC_CTYPE, "C");
+		return;
+	}
+	/* We can handle this locale */
+	setlocale(LC_CTYPE, "");
+}
Index: openssh-6.6p1/Makefile.in
===================================================================
--- openssh-6.6p1.orig/Makefile.in
+++ openssh-6.6p1/Makefile.in
@@ -75,7 +75,7 @@ LIBSSH_OBJS=authfd.o authfile.o bufaux.o
 	compat.o compress.o crc32.o deattack.o fatal.o hostfile.o \
 	log.o match.o md-sha256.o moduli.o nchan.o packet.o \
 	readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \
-	atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \
+	atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o utf8.o \
 	monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
 	kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \
 	kexgssc.o \
@@ -162,7 +162,7 @@ sshd$(EXEEXT): libssh.a	$(LIBCOMPAT) $(S
 	$(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS)
 
 scp$(EXEEXT): $(LIBCOMPAT) libssh.a scp.o progressmeter.o
-	$(LD) -o $@ scp.o progressmeter.o bufaux.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
+	$(LD) -o $@ scp.o progressmeter.o bufaux.o utf8.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
 
 ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o
 	$(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
Index: openssh-6.6p1/openbsd-compat/openbsd-compat.h
===================================================================
--- openssh-6.6p1.orig/openbsd-compat/openbsd-compat.h
+++ openssh-6.6p1/openbsd-compat/openbsd-compat.h
@@ -62,6 +62,10 @@ void closefrom(int);
 char *getcwd(char *pt, size_t size);
 #endif 
 
+#ifndef HAVE_RECALLOCARRAY
+void *recallocarray(void *, size_t, size_t, size_t);
+#endif
+
 #if !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH)
 char *realpath(const char *path, char *resolved);
 #endif 
@@ -217,6 +221,18 @@ long long strtonum(const char *, long lo
 # define mblen(x, y)	1
 #endif
 
+#ifndef HAVE_WCWIDTH
+# define wcwidth(x)     (((x) >= 0x20 && (x) <= 0x7e) ? 1 : -1)
+/* force our no-op nl_langinfo and mbtowc */
+# undef HAVE_NL_LANGINFO
+# undef HAVE_MBTOWC
+# undef HAVE_LANGINFO_H
+#endif
+
+#ifndef HAVE_NL_LANGINFO
+# define nl_langinfo(x) ""
+#endif
+
 #if !defined(HAVE_VASPRINTF) || !defined(HAVE_VSNPRINTF)
 # include <stdarg.h>
 #endif
Index: openssh-6.6p1/openbsd-compat/recallocarray.c
===================================================================
--- /dev/null
+++ openssh-6.6p1/openbsd-compat/recallocarray.c
@@ -0,0 +1,90 @@
+/*	$OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $	*/
+/*
+ * Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/stdlib/recallocarray.c */
+
+#include "includes.h"
+#ifndef HAVE_RECALLOCARRAY
+
+#include <errno.h>
+#include <stdlib.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
+ * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
+ */
+#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
+
+void *
+recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
+{
+	size_t oldsize, newsize;
+	void *newptr;
+
+	if (ptr == NULL)
+		return calloc(newnmemb, size);
+
+	if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+	    newnmemb > 0 && SIZE_MAX / newnmemb < size) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	newsize = newnmemb * size;
+
+	if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+	    oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
+		errno = EINVAL;
+		return NULL;
+	}
+	oldsize = oldnmemb * size;
+	
+	/*
+	 * Don't bother too much if we're shrinking just a bit,
+	 * we do not shrink for series of small steps, oh well.
+	 */
+	if (newsize <= oldsize) {
+		size_t d = oldsize - newsize;
+
+		if (d < oldsize / 2 && d < (size_t)getpagesize()) {
+			memset((char *)ptr + newsize, 0, d);
+			return ptr;
+		}
+	}
+
+	newptr = malloc(newsize);
+	if (newptr == NULL)
+		return NULL;
+
+	if (newsize > oldsize) {
+		memcpy(newptr, ptr, oldsize);
+		memset((char *)newptr + oldsize, 0, newsize - oldsize);
+	} else
+		memcpy(newptr, ptr, newsize);
+
+	explicit_bzero(ptr, oldsize);
+	free(ptr);
+
+	return newptr;
+}
+/* DEF_WEAK(recallocarray); */
+
+#endif /* HAVE_RECALLOCARRAY */
Index: openssh-6.6p1/openbsd-compat/Makefile.in
===================================================================
--- openssh-6.6p1.orig/openbsd-compat/Makefile.in
+++ openssh-6.6p1/openbsd-compat/Makefile.in
@@ -16,7 +16,7 @@ RANLIB=@RANLIB@
 INSTALL=@INSTALL@
 LDFLAGS=-L. @LDFLAGS@
 
-OPENBSD=base64.o basename.o bcrypt_pbkdf.o bindresvport.o blowfish.o daemon.o dirname.o fmt_scaled.o getcwd.o getgrouplist.o getopt_long.o getrrsetbyname.o glob.o inet_aton.o inet_ntoa.o inet_ntop.o mktemp.o pwcache.o readpassphrase.o realpath.o rresvport.o setenv.o setproctitle.o sha2.o sigact.o strlcat.o strlcpy.o strmode.o strnlen.o strptime.o strsep.o strtonum.o strtoll.o strtoul.o strtoull.o timingsafe_bcmp.o vis.o blowfish.o bcrypt_pbkdf.o explicit_bzero.o
+OPENBSD=base64.o basename.o bcrypt_pbkdf.o bindresvport.o blowfish.o daemon.o dirname.o fmt_scaled.o getcwd.o getgrouplist.o getopt_long.o getrrsetbyname.o glob.o inet_aton.o inet_ntoa.o inet_ntop.o mktemp.o pwcache.o readpassphrase.o realpath.o recallocarray.o rresvport.o setenv.o setproctitle.o sha2.o sigact.o strlcat.o strlcpy.o strmode.o strnlen.o strptime.o strsep.o strtonum.o strtoll.o strtoul.o strtoull.o timingsafe_bcmp.o vis.o blowfish.o bcrypt_pbkdf.o explicit_bzero.o
 
 COMPAT=arc4random.o bsd-asprintf.o bsd-closefrom.o bsd-cray.o bsd-cygwin_util.o bsd-getpeereid.o getrrsetbyname-ldns.o bsd-misc.o bsd-nextstep.o bsd-openpty.o bsd-poll.o bsd-setres_id.o bsd-snprintf.o bsd-statvfs.o bsd-waitpid.o fake-rfc2553.o openssl-compat.o xcrypt.o
 
Index: openssh-6.6p1/openbsd-compat/vis.h
===================================================================
--- openssh-6.6p1.orig/openbsd-compat/vis.h
+++ openssh-6.6p1/openbsd-compat/vis.h
@@ -58,6 +58,7 @@
 #define	VIS_NL		0x10	/* also encode newline */
 #define	VIS_WHITE	(VIS_SP | VIS_TAB | VIS_NL)
 #define	VIS_SAFE	0x20	/* only encode "unsafe" characters */
+#define VIS_ALL         0x400   /* encode all characters */
 
 /*
  * other
