From 728923fa07e2387f3279e29618f5d5a0dd96f474d8dc4190177aae118a38d3ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Chv=C3=A1tal?= Date: Thu, 14 Feb 2019 10:30:34 +0000 Subject: [PATCH 1/5] - Change the askpass wrapper to not use x11 interface: * by default we use the -gnome UI (which is gtk3 only, no gnome dep) * if desktop is KDE/LxQt we use ksshaskpass OBS-URL: https://build.opensuse.org/package/show/network/openssh?expand=0&rev=175 --- openssh.changes | 7 +++++++ ssh-askpass | 38 ++++++-------------------------------- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/openssh.changes b/openssh.changes index dae0221..bb3109b 100644 --- a/openssh.changes +++ b/openssh.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Thu Feb 14 10:29:20 UTC 2019 - Tomáš Chvátal + +- Change the askpass wrapper to not use x11 interface: + * by default we use the -gnome UI (which is gtk3 only, no gnome dep) + * if desktop is KDE/LxQt we use ksshaskpass + ------------------------------------------------------------------- Mon Jan 28 10:34:53 UTC 2019 - Pedro Monreal Gonzalez diff --git a/ssh-askpass b/ssh-askpass index 5d8616e..0ad5300 100644 --- a/ssh-askpass +++ b/ssh-askpass @@ -2,49 +2,23 @@ SESSION= -case "$DESKTOP_SESSION" in - kde) SESSION=kde ;; - gnome) SESSION=gnome ;; -esac - -if [ -z "$SESSION" ] ; then - WM="${WINDOWMANAGER##*/}" - case "$WM" in - *kde*) SESSION=kde ;; - *gnome*) SESSION=gnome ;; - esac +if [ -n "$KDE_FULL_SESSION" ] ; then + SESSION=kde fi -if [ -z "$SESSION" ] ; then - if [ -n "$KDE_FULL_SESSION" ] ; then - SESSION=kde - fi - if [ -n "$GNOME_DESKTOP_SESSION_ID" ] ; then - SESSION=gnome - fi +if [ "$DESKTOP_SESSION" = "lxqt" ]; then + SESSION=kde fi GNOME_SSH_ASKPASS="@LIBEXECDIR@/ssh/gnome-ssh-askpass" KDE_SSH_ASKPASS="@LIBEXECDIR@/ssh/ksshaskpass" -X11_SSH_ASKPASS="@LIBEXECDIR@/ssh/x11-ssh-askpass" case "$SESSION" in - gnome) - if [ -f $GNOME_SSH_ASKPASS ]; then - exec $GNOME_SSH_ASKPASS ${1+"$@"} - else - exec $X11_SSH_ASKPASS ${1+"$@"} - fi - ;; kde) - if [ -f $KDE_SSH_ASKPASS ]; then - exec $KDE_SSH_ASKPASS ${1+"$@"} - else - exec $X11_SSH_ASKPASS ${1+"$@"} - fi + exec $KDE_SSH_ASKPASS ${1+"$@"} ;; *) - exec $X11_SSH_ASKPASS ${1+"$@"} + exec $GNOME_SSH_ASKPASS ${1+"$@"} ;; esac From 05c990c804f6b7c0a11ae3f6428a76bb8996b4480a78c01d60b935a913a73821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Chv=C3=A1tal?= Date: Thu, 14 Feb 2019 10:37:25 +0000 Subject: [PATCH 2/5] - Supplement the openssh and libx11 together to ensure this package is installed on machines where there is X stack OBS-URL: https://build.opensuse.org/package/show/network/openssh?expand=0&rev=176 --- openssh-askpass-gnome.changes | 6 ++++++ openssh-askpass-gnome.spec | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/openssh-askpass-gnome.changes b/openssh-askpass-gnome.changes index d45a82a..50f2001 100644 --- a/openssh-askpass-gnome.changes +++ b/openssh-askpass-gnome.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Thu Feb 14 10:36:03 UTC 2019 - Tomáš Chvátal + +- Supplement the openssh and libx11 together to ensure this package + is installed on machines where there is X stack + ------------------------------------------------------------------- Mon Oct 22 08:59:02 UTC 2018 - Pedro Monreal Gonzalez diff --git a/openssh-askpass-gnome.spec b/openssh-askpass-gnome.spec index df79e8c..764615e 100644 --- a/openssh-askpass-gnome.spec +++ b/openssh-askpass-gnome.spec @@ -26,12 +26,13 @@ Group: Productivity/Networking/SSH URL: http://www.openssh.com/ Source: http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/%{_name}-%{version}.tar.gz Source42: http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/%{_name}-%{version}.tar.gz.asc +Requires: %{_name} = %{version} +Supplements: packageand(openssh:libX11-6) %if 0%{?suse_version} >= 1550 BuildRequires: gtk3-devel %else BuildRequires: gtk2-devel %endif -Requires: %{_name} = %{version} %description SSH (Secure Shell) is a program for logging into a remote machine and From e882225f5dd4ca5ba98fed6df6828b1662958020e156284f06c534d355fb831e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Chv=C3=A1tal?= Date: Fri, 15 Feb 2019 09:16:16 +0000 Subject: [PATCH 3/5] Accepting request 676348 from home:pmonrealgonzalez:branches:network - Updated security fixes: * [bsc#1121816, CVE-2019-6109] Sanitize scp filenames via snmprintf and have progressmeter force an update at the beginning and end of each transfer. Added patches: - openssh-CVE-2019-6109-sanitize-scp-filenames.patch - openssh-CVE-2019-6109-force-progressmeter-update.patch * [bsc#1121821, CVE-2019-6111] Check in scp client that filenames sent during remote->local directory copies satisfy the wildcard specified by the user. Added patch: - openssh-CVE-2019-6111-scp-client-wildcard.patch * Removed openssh-7.9p1-scp-name-validator.patch OBS-URL: https://build.opensuse.org/request/show/676348 OBS-URL: https://build.opensuse.org/package/show/network/openssh?expand=0&rev=177 --- openssh-7.9p1-scp-name-validator.patch | 348 ------------------ ...2019-6109-force-progressmeter-update.patch | 110 ++++++ ...CVE-2019-6109-sanitize-scp-filenames.patch | 262 +++++++++++++ ...sh-CVE-2019-6111-scp-client-wildcard.patch | 186 ++++++++++ openssh.changes | 15 + openssh.spec | 4 +- 6 files changed, 576 insertions(+), 349 deletions(-) delete mode 100644 openssh-7.9p1-scp-name-validator.patch create mode 100644 openssh-CVE-2019-6109-force-progressmeter-update.patch create mode 100644 openssh-CVE-2019-6109-sanitize-scp-filenames.patch create mode 100644 openssh-CVE-2019-6111-scp-client-wildcard.patch diff --git a/openssh-7.9p1-scp-name-validator.patch b/openssh-7.9p1-scp-name-validator.patch deleted file mode 100644 index aacdb3d..0000000 --- a/openssh-7.9p1-scp-name-validator.patch +++ /dev/null @@ -1,348 +0,0 @@ -diff --git a/defines.h b/defines.h -index 8f421306..8b4af9b2 100644 ---- a/defines.h -+++ b/defines.h -@@ -873,4 +873,10 @@ struct winsize { - # define USE_SYSTEM_GLOB - #endif - -+/* -+ * Define to enable additional scp file name validation against -+ * malicious servers. -+ */ -+#define USE_SCP_NAME_VALIDATOR 1 -+ - #endif /* _DEFINES_H */ -diff --git a/progressmeter.c b/progressmeter.c -index fe9bf52e..b2a3a38d 100644 ---- a/progressmeter.c -+++ b/progressmeter.c -@@ -160,7 +160,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) -diff --git a/regress/scp-ssh-wrapper.sh b/regress/scp-ssh-wrapper.sh -index 59f1ff63..dd48a482 100644 ---- a/regress/scp-ssh-wrapper.sh -+++ b/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 -diff --git a/regress/scp.sh b/regress/scp.sh -index 57cc7706..104c89e1 100644 ---- a/regress/scp.sh -+++ b/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" -diff --git a/scp.c b/scp.c -index eb17c341..da1a3a44 100644 ---- a/scp.c -+++ b/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 -@@ -87,6 +88,14 @@ - #ifdef HAVE_SYS_TIME_H - # include - #endif -+#ifdef USE_SCP_NAME_VALIDATOR -+# include -+# ifdef USE_SYSTEM_GLOB -+# include -+# else -+# include "openbsd-compat/glob.h" -+# endif -+#endif - #include - #include - -@@ -277,6 +286,18 @@ do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) - 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]); - -@@ -380,9 +401,20 @@ int pflag, iamremote, iamrecursive, targetshouldbedirectory; - #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(int, char *[]); -@@ -536,7 +568,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) -@@ -750,6 +786,9 @@ tolocal(int argc, char **argv) - char *bp, *host = NULL, *src = NULL, *suser = NULL; - arglist alist; - int i, r, sport = -1; -+#ifdef USE_SCP_NAME_VALIDATOR -+ SINKDATA sinkdata; -+#endif - - memset(&alist, '\0', sizeof(alist)); - alist.list = NULL; -@@ -793,7 +832,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; - } -@@ -968,8 +1013,71 @@ rsource(char *name, struct stat *statp) - (sizeof(type) == 8 && (val) > INT64_MAX) || \ - (sizeof(type) != 4 && sizeof(type) != 8)) - -+#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; -@@ -1113,6 +1221,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; -@@ -1150,7 +1317,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) diff --git a/openssh-CVE-2019-6109-force-progressmeter-update.patch b/openssh-CVE-2019-6109-force-progressmeter-update.patch new file mode 100644 index 0000000..0a67d30 --- /dev/null +++ b/openssh-CVE-2019-6109-force-progressmeter-update.patch @@ -0,0 +1,110 @@ +commit bdc6c63c80b55bcbaa66b5fde31c1cb1d09a41eb +Author: dtucker@openbsd.org +Date: Thu Jan 24 16:52:17 2019 +0000 + + upstream: Have progressmeter force an update at the beginning and + + end of each transfer. Fixes the problem recently introduces where very quick + transfers do not display the progressmeter at all. Spotted by naddy@ + + OpenBSD-Commit-ID: 68dc46c259e8fdd4f5db3ec2a130f8e4590a7a9a + +Index: openssh-7.9p1/progressmeter.c +=================================================================== +--- openssh-7.9p1.orig/progressmeter.c ++++ openssh-7.9p1/progressmeter.c +@@ -1,4 +1,4 @@ +-/* $OpenBSD: progressmeter.c,v 1.46 2019/01/23 08:01:46 dtucker Exp $ */ ++/* $OpenBSD: progressmeter.c,v 1.47 2019/01/24 16:52:17 dtucker Exp $ */ + /* + * Copyright (c) 2003 Nils Nordman. All rights reserved. + * +@@ -59,9 +59,6 @@ static void format_rate(char *, int, off + static void sig_winch(int); + static void setscreensize(void); + +-/* updates the progressmeter to reflect the current state of the transfer */ +-void refresh_progress_meter(void); +- + /* signal handler for updating the progress meter */ + static void sig_alarm(int); + +@@ -120,7 +117,7 @@ format_size(char *buf, int size, off_t b + } + + void +-refresh_progress_meter(void) ++refresh_progress_meter(int force_update) + { + char buf[MAX_WINSIZE + 1]; + off_t transferred; +@@ -131,7 +128,7 @@ refresh_progress_meter(void) + int hours, minutes, seconds; + int file_len; + +- if ((!alarm_fired && !win_resized) || !can_output()) ++ if ((!force_update && !alarm_fired && !win_resized) || !can_output()) + return; + alarm_fired = 0; + +@@ -254,7 +251,7 @@ start_progress_meter(const char *f, off_ + bytes_per_second = 0; + + setscreensize(); +- refresh_progress_meter(); ++ refresh_progress_meter(1); + + signal(SIGALRM, sig_alarm); + signal(SIGWINCH, sig_winch); +@@ -271,7 +268,7 @@ stop_progress_meter(void) + + /* Ensure we complete the progress */ + if (cur_pos != end_pos) +- refresh_progress_meter(); ++ refresh_progress_meter(1); + + atomicio(vwrite, STDOUT_FILENO, "\n", 1); + } +Index: openssh-7.9p1/progressmeter.h +=================================================================== +--- openssh-7.9p1.orig/progressmeter.h ++++ openssh-7.9p1/progressmeter.h +@@ -1,4 +1,4 @@ +-/* $OpenBSD: progressmeter.h,v 1.4 2019/01/23 08:01:46 dtucker Exp $ */ ++/* $OpenBSD: progressmeter.h,v 1.5 2019/01/24 16:52:17 dtucker Exp $ */ + /* + * Copyright (c) 2002 Nils Nordman. All rights reserved. + * +@@ -24,5 +24,5 @@ + */ + + void start_progress_meter(const char *, off_t, off_t *); +-void refresh_progress_meter(void); ++void refresh_progress_meter(int); + void stop_progress_meter(void); +Index: openssh-7.9p1/scp.c +=================================================================== +--- openssh-7.9p1.orig/scp.c ++++ openssh-7.9p1/scp.c +@@ -585,7 +585,7 @@ scpio(void *_cnt, size_t s) + off_t *cnt = (off_t *)_cnt; + + *cnt += s; +- refresh_progress_meter(); ++ refresh_progress_meter(0); + if (limit_kbps > 0) + bandwidth_limit(&bwlimit, s); + return 0; +Index: openssh-7.9p1/sftp-client.c +=================================================================== +--- openssh-7.9p1.orig/sftp-client.c ++++ openssh-7.9p1/sftp-client.c +@@ -101,7 +101,7 @@ sftpio(void *_bwlimit, size_t amount) + { + struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit; + +- refresh_progress_meter(); ++ refresh_progress_meter(0); + if (bwlimit != NULL) + bandwidth_limit(bwlimit, amount); + return 0; diff --git a/openssh-CVE-2019-6109-sanitize-scp-filenames.patch b/openssh-CVE-2019-6109-sanitize-scp-filenames.patch new file mode 100644 index 0000000..e2554dd --- /dev/null +++ b/openssh-CVE-2019-6109-sanitize-scp-filenames.patch @@ -0,0 +1,262 @@ +commit 8976f1c4b2721c26e878151f52bdf346dfe2d54c +Author: dtucker@openbsd.org +Date: Wed Jan 23 08:01:46 2019 +0000 + + upstream: Sanitize scp filenames via snmprintf. To do this we move + + the progressmeter formatting outside of signal handler context and have the + atomicio callback called for EINTR too. bz#2434 with contributions from djm + and jjelen at redhat.com, ok djm@ + + OpenBSD-Commit-ID: 1af61c1f70e4f3bd8ab140b9f1fa699481db57d8 + +Index: openssh-7.9p1/atomicio.c +=================================================================== +--- openssh-7.9p1.orig/atomicio.c ++++ openssh-7.9p1/atomicio.c +@@ -1,4 +1,4 @@ +-/* $OpenBSD: atomicio.c,v 1.28 2016/07/27 23:18:12 djm Exp $ */ ++/* $OpenBSD: atomicio.c,v 1.29 2019/01/23 08:01:46 dtucker Exp $ */ + /* + * Copyright (c) 2006 Damien Miller. All rights reserved. + * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved. +@@ -65,9 +65,14 @@ atomicio6(ssize_t (*f) (int, void *, siz + res = (f) (fd, s + pos, n - pos); + switch (res) { + case -1: +- if (errno == EINTR) ++ if (errno == EINTR) { ++ /* possible SIGALARM, update callback */ ++ if (cb != NULL && cb(cb_arg, 0) == -1) { ++ errno = EINTR; ++ return pos; ++ } + continue; +- if (errno == EAGAIN || errno == EWOULDBLOCK) { ++ } else if (errno == EAGAIN || errno == EWOULDBLOCK) { + #ifndef BROKEN_READ_COMPARISON + (void)poll(&pfd, 1, -1); + #endif +@@ -122,9 +127,14 @@ atomiciov6(ssize_t (*f) (int, const stru + res = (f) (fd, iov, iovcnt); + switch (res) { + case -1: +- if (errno == EINTR) ++ if (errno == EINTR) { ++ /* possible SIGALARM, update callback */ ++ if (cb != NULL && cb(cb_arg, 0) == -1) { ++ errno = EINTR; ++ return pos; ++ } + continue; +- if (errno == EAGAIN || errno == EWOULDBLOCK) { ++ } else if (errno == EAGAIN || errno == EWOULDBLOCK) { + #ifndef BROKEN_READV_COMPARISON + (void)poll(&pfd, 1, -1); + #endif +Index: openssh-7.9p1/progressmeter.c +=================================================================== +--- openssh-7.9p1.orig/progressmeter.c ++++ openssh-7.9p1/progressmeter.c +@@ -1,4 +1,4 @@ +-/* $OpenBSD: progressmeter.c,v 1.45 2016/06/30 05:17:05 dtucker Exp $ */ ++/* $OpenBSD: progressmeter.c,v 1.46 2019/01/23 08:01:46 dtucker Exp $ */ + /* + * Copyright (c) 2003 Nils Nordman. All rights reserved. + * +@@ -31,6 +31,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -39,6 +40,7 @@ + #include "progressmeter.h" + #include "atomicio.h" + #include "misc.h" ++#include "utf8.h" + + #define DEFAULT_WINSIZE 80 + #define MAX_WINSIZE 512 +@@ -61,7 +63,7 @@ static void setscreensize(void); + void refresh_progress_meter(void); + + /* signal handler for updating the progress meter */ +-static void update_progress_meter(int); ++static void sig_alarm(int); + + static double start; /* start progress */ + static double last_update; /* last progress update */ +@@ -74,6 +76,7 @@ static long stalled; /* how long we hav + static int bytes_per_second; /* current speed in bytes per second */ + static int win_size; /* terminal window size */ + static volatile sig_atomic_t win_resized; /* for window resizing */ ++static volatile sig_atomic_t alarm_fired; + + /* units for format_size */ + static const char unit[] = " KMGT"; +@@ -126,9 +129,17 @@ refresh_progress_meter(void) + off_t bytes_left; + int cur_speed; + int hours, minutes, seconds; +- int i, len; + int file_len; + ++ if ((!alarm_fired && !win_resized) || !can_output()) ++ return; ++ alarm_fired = 0; ++ ++ if (win_resized) { ++ setscreensize(); ++ win_resized = 0; ++ } ++ + transferred = *counter - (cur_pos ? cur_pos : start_pos); + cur_pos = *counter; + now = monotime_double(); +@@ -158,16 +169,11 @@ refresh_progress_meter(void) + + /* filename */ + buf[0] = '\0'; +- file_len = win_size - 35; ++ file_len = win_size - 36; + if (file_len > 0) { +- len = snprintf(buf, file_len + 1, "\r%s", file); +- if (len < 0) +- len = 0; +- if (len >= file_len + 1) +- len = file_len; +- for (i = len; i < file_len; i++) +- buf[i] = ' '; +- buf[file_len] = '\0'; ++ buf[0] = '\r'; ++ snmprintf(buf+1, sizeof(buf)-1 , &file_len, "%*s", ++ file_len * -1, file); + } + + /* percent of transfer done */ +@@ -228,22 +234,11 @@ refresh_progress_meter(void) + + /*ARGSUSED*/ + static void +-update_progress_meter(int ignore) ++sig_alarm(int ignore) + { +- int save_errno; +- +- save_errno = errno; +- +- if (win_resized) { +- setscreensize(); +- win_resized = 0; +- } +- if (can_output()) +- refresh_progress_meter(); +- +- signal(SIGALRM, update_progress_meter); ++ signal(SIGALRM, sig_alarm); ++ alarm_fired = 1; + alarm(UPDATE_INTERVAL); +- errno = save_errno; + } + + void +@@ -259,10 +254,9 @@ start_progress_meter(const char *f, off_ + bytes_per_second = 0; + + setscreensize(); +- if (can_output()) +- refresh_progress_meter(); ++ refresh_progress_meter(); + +- signal(SIGALRM, update_progress_meter); ++ signal(SIGALRM, sig_alarm); + signal(SIGWINCH, sig_winch); + alarm(UPDATE_INTERVAL); + } +@@ -286,6 +280,7 @@ stop_progress_meter(void) + static void + sig_winch(int sig) + { ++ signal(SIGWINCH, sig_winch); + win_resized = 1; + } + +Index: openssh-7.9p1/progressmeter.h +=================================================================== +--- openssh-7.9p1.orig/progressmeter.h ++++ openssh-7.9p1/progressmeter.h +@@ -1,4 +1,4 @@ +-/* $OpenBSD: progressmeter.h,v 1.3 2015/01/14 13:54:13 djm Exp $ */ ++/* $OpenBSD: progressmeter.h,v 1.4 2019/01/23 08:01:46 dtucker Exp $ */ + /* + * Copyright (c) 2002 Nils Nordman. All rights reserved. + * +@@ -24,4 +24,5 @@ + */ + + void start_progress_meter(const char *, off_t, off_t *); ++void refresh_progress_meter(void); + void stop_progress_meter(void); +Index: openssh-7.9p1/scp.c +=================================================================== +--- openssh-7.9p1.orig/scp.c ++++ openssh-7.9p1/scp.c +@@ -585,6 +585,7 @@ scpio(void *_cnt, size_t s) + off_t *cnt = (off_t *)_cnt; + + *cnt += s; ++ refresh_progress_meter(); + if (limit_kbps > 0) + bandwidth_limit(&bwlimit, s); + return 0; +Index: openssh-7.9p1/sftp-client.c +=================================================================== +--- openssh-7.9p1.orig/sftp-client.c ++++ openssh-7.9p1/sftp-client.c +@@ -101,7 +101,9 @@ sftpio(void *_bwlimit, size_t amount) + { + struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit; + +- bandwidth_limit(bwlimit, amount); ++ refresh_progress_meter(); ++ if (bwlimit != NULL) ++ bandwidth_limit(bwlimit, amount); + return 0; + } + +@@ -121,8 +123,8 @@ send_msg(struct sftp_conn *conn, struct + iov[1].iov_base = (u_char *)sshbuf_ptr(m); + iov[1].iov_len = sshbuf_len(m); + +- if (atomiciov6(writev, conn->fd_out, iov, 2, +- conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) != ++ if (atomiciov6(writev, conn->fd_out, iov, 2, sftpio, ++ conn->limit_kbps > 0 ? &conn->bwlimit_out : NULL) != + sshbuf_len(m) + sizeof(mlen)) + fatal("Couldn't send packet: %s", strerror(errno)); + +@@ -138,8 +140,8 @@ get_msg_extended(struct sftp_conn *conn, + + if ((r = sshbuf_reserve(m, 4, &p)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); +- if (atomicio6(read, conn->fd_in, p, 4, +- conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) { ++ if (atomicio6(read, conn->fd_in, p, 4, sftpio, ++ conn->limit_kbps > 0 ? &conn->bwlimit_in : NULL) != 4) { + if (errno == EPIPE || errno == ECONNRESET) + fatal("Connection closed"); + else +@@ -157,8 +159,8 @@ get_msg_extended(struct sftp_conn *conn, + + if ((r = sshbuf_reserve(m, msg_len, &p)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); +- if (atomicio6(read, conn->fd_in, p, msg_len, +- conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) ++ if (atomicio6(read, conn->fd_in, p, msg_len, sftpio, ++ conn->limit_kbps > 0 ? &conn->bwlimit_in : NULL) + != msg_len) { + if (errno == EPIPE) + fatal("Connection closed"); diff --git a/openssh-CVE-2019-6111-scp-client-wildcard.patch b/openssh-CVE-2019-6111-scp-client-wildcard.patch new file mode 100644 index 0000000..00247d1 --- /dev/null +++ b/openssh-CVE-2019-6111-scp-client-wildcard.patch @@ -0,0 +1,186 @@ +commit 391ffc4b9d31fa1f4ad566499fef9176ff8a07dc +Author: djm@openbsd.org +Date: Sat Jan 26 22:41:28 2019 +0000 + + upstream: check in scp client that filenames sent during + + remote->local directory copies satisfy the wildcard specified by the user. + + This checking provides some protection against a malicious server + sending unexpected filenames, but it comes at a risk of rejecting wanted + files due to differences between client and server wildcard expansion rules. + + For this reason, this also adds a new -T flag to disable the check. + + reported by Harry Sintonen + fix approach suggested by markus@; + has been in snaps for ~1wk courtesy deraadt@ + + OpenBSD-Commit-ID: 00f44b50d2be8e321973f3c6d014260f8f7a8eda + +Index: openssh-7.9p1/scp.1 +=================================================================== +--- openssh-7.9p1.orig/scp.1 ++++ openssh-7.9p1/scp.1 +@@ -18,7 +18,7 @@ + .Nd secure copy (remote file copy program) + .Sh SYNOPSIS + .Nm scp +-.Op Fl 346BCpqrv ++.Op Fl 346BCpqrTv + .Op Fl c Ar cipher + .Op Fl F Ar ssh_config + .Op Fl i Ar identity_file +@@ -208,6 +208,16 @@ to use for the encrypted connection. + The program must understand + .Xr ssh 1 + options. ++.It Fl T ++Disable strict filename checking. ++By default when copying files from a remote host to a local directory ++.Nm ++checks that the received filenames match those requested on the command-line ++to prevent the remote end from sending unexpected or unwanted files. ++Because of differences in how various operating systems and shells interpret ++filename wildcards, these checks may cause wanted files to be rejected. ++This option disables these checks at the expense of fully trusting that ++the server will not send unexpected filenames. + .It Fl v + Verbose mode. + Causes +Index: openssh-7.9p1/scp.c +=================================================================== +--- openssh-7.9p1.orig/scp.c ++++ openssh-7.9p1/scp.c +@@ -94,6 +94,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -375,14 +376,14 @@ void verifydir(char *); + struct passwd *pwd; + uid_t userid; + int errs, remin, remout; +-int pflag, iamremote, iamrecursive, targetshouldbedirectory; ++int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory; + + #define CMDNEEDS 64 + char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ + + int response(void); + void rsource(char *, struct stat *); +-void sink(int, char *[]); ++void sink(int, char *[], const char *); + void source(int, char *[]); + void tolocal(int, char *[]); + void toremote(int, char *[]); +@@ -421,8 +422,9 @@ main(int argc, char **argv) + addargs(&args, "-oRemoteCommand=none"); + addargs(&args, "-oRequestTTY=no"); + +- fflag = tflag = 0; +- while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) ++ fflag = Tflag = tflag = 0; ++ while ((ch = getopt(argc, argv, ++ "dfl:prtTvBCc:i:P:q12346S:o:F:J:")) != -1) { + switch (ch) { + /* User-visible flags. */ + case '1': +@@ -501,9 +503,13 @@ main(int argc, char **argv) + setmode(0, O_BINARY); + #endif + break; ++ case 'T': ++ Tflag = 1; ++ break; + default: + usage(); + } ++ } + argc -= optind; + argv += optind; + +@@ -534,7 +540,7 @@ main(int argc, char **argv) + } + if (tflag) { + /* Receive data. */ +- sink(argc, argv); ++ sink(argc, argv, NULL); + exit(errs != 0); + } + if (argc < 2) +@@ -792,7 +798,7 @@ tolocal(int argc, char **argv) + continue; + } + free(bp); +- sink(1, argv + argc - 1); ++ sink(1, argv + argc - 1, src); + (void) close(remin); + remin = remout = -1; + } +@@ -968,7 +974,7 @@ rsource(char *name, struct stat *statp) + (sizeof(type) != 4 && sizeof(type) != 8)) + + void +-sink(int argc, char **argv) ++sink(int argc, char **argv, const char *src) + { + static BUF buffer; + struct stat stb; +@@ -984,6 +990,7 @@ sink(int argc, char **argv) + unsigned long long ull; + int setimes, targisdir, wrerrno = 0; + char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; ++ char *src_copy = NULL, *restrict_pattern = NULL; + struct timeval tv[2]; + + #define atime tv[0] +@@ -1008,6 +1015,17 @@ sink(int argc, char **argv) + (void) atomicio(vwrite, remout, "", 1); + if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) + targisdir = 1; ++ if (src != NULL && !iamrecursive && !Tflag) { ++ /* ++ * Prepare to try to restrict incoming filenames to match ++ * the requested destination file glob. ++ */ ++ if ((src_copy = strdup(src)) == NULL) ++ fatal("strdup failed"); ++ if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) { ++ *restrict_pattern++ = '\0'; ++ } ++ } + for (first = 1;; first = 0) { + cp = buf; + if (atomicio(read, remin, cp, 1) != 1) +@@ -1112,6 +1130,9 @@ sink(int argc, char **argv) + run_err("error: unexpected filename: %s", cp); + exit(1); + } ++ if (restrict_pattern != NULL && ++ fnmatch(restrict_pattern, cp, 0) != 0) ++ SCREWUP("filename does not match request"); + if (targisdir) { + static char *namebuf; + static size_t cursize; +@@ -1149,7 +1170,7 @@ sink(int argc, char **argv) + goto bad; + } + vect[0] = xstrdup(np); +- sink(1, vect); ++ sink(1, vect, src); + if (setimes) { + setimes = 0; + if (utimes(vect[0], tv) < 0) +@@ -1317,7 +1338,7 @@ void + usage(void) + { + (void) fprintf(stderr, +- "usage: scp [-346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" ++ "usage: scp [-346BCpqrTv] [-c cipher] [-F ssh_config] [-i identity_file]\n" + " [-l limit] [-o ssh_option] [-P port] [-S program] source ... target\n"); + exit(1); + } diff --git a/openssh.changes b/openssh.changes index bb3109b..427b358 100644 --- a/openssh.changes +++ b/openssh.changes @@ -1,3 +1,18 @@ +------------------------------------------------------------------- +Thu Feb 14 15:27:53 UTC 2019 - Pedro Monreal Gonzalez + +- Updated security fixes: + * [bsc#1121816, CVE-2019-6109] Sanitize scp filenames via snmprintf + and have progressmeter force an update at the beginning and end + of each transfer. Added patches: + - openssh-CVE-2019-6109-sanitize-scp-filenames.patch + - openssh-CVE-2019-6109-force-progressmeter-update.patch + * [bsc#1121821, CVE-2019-6111] Check in scp client that filenames + sent during remote->local directory copies satisfy the wildcard + specified by the user. Added patch: + - openssh-CVE-2019-6111-scp-client-wildcard.patch + * Removed openssh-7.9p1-scp-name-validator.patch + ------------------------------------------------------------------- Thu Feb 14 10:29:20 UTC 2019 - Tomáš Chvátal diff --git a/openssh.spec b/openssh.spec index d930044..8720e4a 100644 --- a/openssh.spec +++ b/openssh.spec @@ -101,7 +101,9 @@ Patch32: openssh-7.7p1-IPv6_X_forwarding.patch Patch33: openssh-7.7p1-sftp_print_diagnostic_messages.patch Patch34: openssh-openssl-1_0_0-compatibility.patch Patch35: openssh-7.9p1-CVE-2018-20685.patch -Patch36: openssh-7.9p1-scp-name-validator.patch +Patch36: openssh-CVE-2019-6109-sanitize-scp-filenames.patch +Patch37: openssh-CVE-2019-6109-force-progressmeter-update.patch +Patch38: openssh-CVE-2019-6111-scp-client-wildcard.patch BuildRequires: audit-devel BuildRequires: autoconf BuildRequires: groff From adae0f9df2704a91942b0022aed166ac5c218bfc63796f489d6804050855d4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Chv=C3=A1tal?= Date: Tue, 19 Feb 2019 08:15:17 +0000 Subject: [PATCH 4/5] Accepting request 677200 from home:pmonrealgonzalez:branches:network - Handle brace expansion in scp when checking that filenames sent by the server side match what the client requested [bsc#1125687] OBS-URL: https://build.opensuse.org/request/show/677200 OBS-URL: https://build.opensuse.org/package/show/network/openssh?expand=0&rev=178 --- openssh-7.9p1-brace-expansion.patch | 348 ++++++++++++++++++++++++++++ openssh.changes | 6 + openssh.spec | 1 + 3 files changed, 355 insertions(+) create mode 100644 openssh-7.9p1-brace-expansion.patch diff --git a/openssh-7.9p1-brace-expansion.patch b/openssh-7.9p1-brace-expansion.patch new file mode 100644 index 0000000..ab0aa32 --- /dev/null +++ b/openssh-7.9p1-brace-expansion.patch @@ -0,0 +1,348 @@ +From 3d896c157c722bc47adca51a58dca859225b5874 Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Sun, 10 Feb 2019 11:15:52 +0000 +Subject: [PATCH] upstream: when checking that filenames sent by the server + side + +match what the client requested, be prepared to handle shell-style brace +alternations, e.g. "{foo,bar}". + +"looks good to me" millert@ + in snaps for the last week courtesy +deraadt@ + +OpenBSD-Commit-ID: 3b1ce7639b0b25b2248e3a30f561a548f6815f3e +--- + scp.c | 282 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 270 insertions(+), 12 deletions(-) + +Index: openssh-7.9p1/scp.c +=================================================================== +--- openssh-7.9p1.orig/scp.c ++++ openssh-7.9p1/scp.c +@@ -627,6 +627,253 @@ parse_scp_uri(const char *uri, char **us + return r; + } + ++/* Appends a string to an array; returns 0 on success, -1 on alloc failure */ ++static int ++append(char *cp, char ***ap, size_t *np) ++{ ++ char **tmp; ++ ++ if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL) ++ return -1; ++ tmp[(*np)] = cp; ++ (*np)++; ++ *ap = tmp; ++ return 0; ++} ++ ++/* ++ * Finds the start and end of the first brace pair in the pattern. ++ * returns 0 on success or -1 for invalid patterns. ++ */ ++static int ++find_brace(const char *pattern, int *startp, int *endp) ++{ ++ int i; ++ int in_bracket, brace_level; ++ ++ *startp = *endp = -1; ++ in_bracket = brace_level = 0; ++ for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) { ++ switch (pattern[i]) { ++ case '\\': ++ /* skip next character */ ++ if (pattern[i + 1] != '\0') ++ i++; ++ break; ++ case '[': ++ in_bracket = 1; ++ break; ++ case ']': ++ in_bracket = 0; ++ break; ++ case '{': ++ if (in_bracket) ++ break; ++ if (pattern[i + 1] == '}') { ++ /* Protect a single {}, for find(1), like csh */ ++ i++; /* skip */ ++ break; ++ } ++ if (*startp == -1) ++ *startp = i; ++ brace_level++; ++ break; ++ case '}': ++ if (in_bracket) ++ break; ++ if (*startp < 0) { ++ /* Unbalanced brace */ ++ return -1; ++ } ++ if (--brace_level <= 0) ++ *endp = i; ++ break; ++ } ++ } ++ /* unbalanced brackets/braces */ ++ if (*endp < 0 && (*startp >= 0 || in_bracket)) ++ return -1; ++ return 0; ++} ++ ++/* ++ * Assembles and records a successfully-expanded pattern, returns -1 on ++ * alloc failure. ++ */ ++static int ++emit_expansion(const char *pattern, int brace_start, int brace_end, ++ int sel_start, int sel_end, char ***patternsp, size_t *npatternsp) ++{ ++ char *cp; ++ int o = 0, tail_len = strlen(pattern + brace_end + 1); ++ ++ if ((cp = malloc(brace_start + (sel_end - sel_start) + ++ tail_len + 1)) == NULL) ++ return -1; ++ ++ /* Pattern before initial brace */ ++ if (brace_start > 0) { ++ memcpy(cp, pattern, brace_start); ++ o = brace_start; ++ } ++ /* Current braced selection */ ++ if (sel_end - sel_start > 0) { ++ memcpy(cp + o, pattern + sel_start, ++ sel_end - sel_start); ++ o += sel_end - sel_start; ++ } ++ /* Remainder of pattern after closing brace */ ++ if (tail_len > 0) { ++ memcpy(cp + o, pattern + brace_end + 1, tail_len); ++ o += tail_len; ++ } ++ cp[o] = '\0'; ++ if (append(cp, patternsp, npatternsp) != 0) { ++ free(cp); ++ return -1; ++ } ++ return 0; ++} ++ ++/* ++ * Expand the first encountered brace in pattern, appending the expanded ++ * patterns it yielded to the *patternsp array. ++ * ++ * Returns 0 on success or -1 on allocation failure. ++ * ++ * Signals whether expansion was performed via *expanded and whether ++ * pattern was invalid via *invalid. ++ */ ++static int ++brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp, ++ int *expanded, int *invalid) ++{ ++ int i; ++ int in_bracket, brace_start, brace_end, brace_level; ++ int sel_start, sel_end; ++ ++ *invalid = *expanded = 0; ++ ++ if (find_brace(pattern, &brace_start, &brace_end) != 0) { ++ *invalid = 1; ++ return 0; ++ } else if (brace_start == -1) ++ return 0; ++ ++ in_bracket = brace_level = 0; ++ for (i = sel_start = brace_start + 1; i < brace_end; i++) { ++ switch (pattern[i]) { ++ case '{': ++ if (in_bracket) ++ break; ++ brace_level++; ++ break; ++ case '}': ++ if (in_bracket) ++ break; ++ brace_level--; ++ break; ++ case '[': ++ in_bracket = 1; ++ break; ++ case ']': ++ in_bracket = 0; ++ break; ++ case '\\': ++ if (i < brace_end - 1) ++ i++; /* skip */ ++ break; ++ } ++ if (pattern[i] == ',' || i == brace_end - 1) { ++ if (in_bracket || brace_level > 0) ++ continue; ++ /* End of a selection, emit an expanded pattern */ ++ ++ /* Adjust end index for last selection */ ++ sel_end = (i == brace_end - 1) ? brace_end : i; ++ if (emit_expansion(pattern, brace_start, brace_end, ++ sel_start, sel_end, patternsp, npatternsp) != 0) ++ return -1; ++ /* move on to the next selection */ ++ sel_start = i + 1; ++ continue; ++ } ++ } ++ if (in_bracket || brace_level > 0) { ++ *invalid = 1; ++ return 0; ++ } ++ /* success */ ++ *expanded = 1; ++ return 0; ++} ++ ++/* Expand braces from pattern. Returns 0 on success, -1 on failure */ ++static int ++brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp) ++{ ++ char *cp, *cp2, **active = NULL, **done = NULL; ++ size_t i, nactive = 0, ndone = 0; ++ int ret = -1, invalid = 0, expanded = 0; ++ ++ *patternsp = NULL; ++ *npatternsp = 0; ++ ++ /* Start the worklist with the original pattern */ ++ if ((cp = strdup(pattern)) == NULL) ++ return -1; ++ if (append(cp, &active, &nactive) != 0) { ++ free(cp); ++ return -1; ++ } ++ while (nactive > 0) { ++ cp = active[nactive - 1]; ++ nactive--; ++ if (brace_expand_one(cp, &active, &nactive, ++ &expanded, &invalid) == -1) { ++ free(cp); ++ goto fail; ++ } ++ if (invalid) ++ fatal("%s: invalid brace pattern \"%s\"", __func__, cp); ++ if (expanded) { ++ /* ++ * Current entry expanded to new entries on the ++ * active list; discard the progenitor pattern. ++ */ ++ free(cp); ++ continue; ++ } ++ /* ++ * Pattern did not expand; append the finename component to ++ * the completed list ++ */ ++ if ((cp2 = strrchr(cp, '/')) != NULL) ++ *cp2++ = '\0'; ++ else ++ cp2 = cp; ++ if (append(xstrdup(cp2), &done, &ndone) != 0) { ++ free(cp); ++ goto fail; ++ } ++ free(cp); ++ } ++ /* success */ ++ *patternsp = done; ++ *npatternsp = ndone; ++ done = NULL; ++ ndone = 0; ++ ret = 0; ++ fail: ++ for (i = 0; i < nactive; i++) ++ free(active[i]); ++ free(active); ++ for (i = 0; i < ndone; i++) ++ free(done[i]); ++ free(done); ++ return ret; ++} ++ + void + toremote(int argc, char **argv) + { +@@ -990,7 +1237,8 @@ sink(int argc, char **argv, const char * + unsigned long long ull; + int setimes, targisdir, wrerrno = 0; + char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; +- char *src_copy = NULL, *restrict_pattern = NULL; ++ char **patterns = NULL; ++ size_t n, npatterns = 0; + struct timeval tv[2]; + + #define atime tv[0] +@@ -1020,16 +1268,13 @@ sink(int argc, char **argv, const char * + * Prepare to try to restrict incoming filenames to match + * the requested destination file glob. + */ +- if ((src_copy = strdup(src)) == NULL) +- fatal("strdup failed"); +- if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) { +- *restrict_pattern++ = '\0'; +- } ++ if (brace_expand(src, &patterns, &npatterns) != 0) ++ fatal("%s: could not expand pattern", __func__); + } + for (first = 1;; first = 0) { + cp = buf; + if (atomicio(read, remin, cp, 1) != 1) +- return; ++ goto done; + if (*cp++ == '\n') + SCREWUP("unexpected "); + do { +@@ -1055,7 +1300,7 @@ sink(int argc, char **argv, const char * + } + if (buf[0] == 'E') { + (void) atomicio(vwrite, remout, "", 1); +- return; ++ goto done; + } + if (ch == '\n') + *--cp = 0; +@@ -1130,9 +1375,14 @@ sink(int argc, char **argv, const char * + run_err("error: unexpected filename: %s", cp); + exit(1); + } +- if (restrict_pattern != NULL && +- fnmatch(restrict_pattern, cp, 0) != 0) +- SCREWUP("filename does not match request"); ++ if (npatterns > 0) { ++ for (n = 0; n < npatterns; n++) { ++ if (fnmatch(patterns[n], cp, 0) == 0) ++ break; ++ } ++ if (n >= npatterns) ++ SCREWUP("filename does not match request"); ++ } + if (targisdir) { + static char *namebuf; + static size_t cursize; +@@ -1291,7 +1541,15 @@ bad: run_err("%s: %s", np, strerror(er + break; + } + } ++done: ++ for (n = 0; n < npatterns; n++) ++ free(patterns[n]); ++ free(patterns); ++ return; + screwup: ++ for (n = 0; n < npatterns; n++) ++ free(patterns[n]); ++ free(patterns); + run_err("protocol error: %s", why); + exit(1); + } diff --git a/openssh.changes b/openssh.changes index 427b358..51d3546 100644 --- a/openssh.changes +++ b/openssh.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Mon Feb 18 10:01:45 UTC 2019 - Pedro Monreal Gonzalez + +- Handle brace expansion in scp when checking that filenames sent + by the server side match what the client requested [bsc#1125687] + ------------------------------------------------------------------- Thu Feb 14 15:27:53 UTC 2019 - Pedro Monreal Gonzalez diff --git a/openssh.spec b/openssh.spec index 8720e4a..59e28d8 100644 --- a/openssh.spec +++ b/openssh.spec @@ -104,6 +104,7 @@ Patch35: openssh-7.9p1-CVE-2018-20685.patch Patch36: openssh-CVE-2019-6109-sanitize-scp-filenames.patch Patch37: openssh-CVE-2019-6109-force-progressmeter-update.patch Patch38: openssh-CVE-2019-6111-scp-client-wildcard.patch +Patch39: openssh-7.9p1-brace-expansion.patch BuildRequires: audit-devel BuildRequires: autoconf BuildRequires: groff From afefdefb8aeba7906d63869ed5fbe70a5869ab9253b638802e4ff2780bd80899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Chv=C3=A1tal?= Date: Tue, 19 Feb 2019 08:19:52 +0000 Subject: [PATCH 5/5] * openssh-7.9p1-brace-expansion.patch OBS-URL: https://build.opensuse.org/package/show/network/openssh?expand=0&rev=179 --- openssh.changes | 1 + 1 file changed, 1 insertion(+) diff --git a/openssh.changes b/openssh.changes index 51d3546..c5888e4 100644 --- a/openssh.changes +++ b/openssh.changes @@ -3,6 +3,7 @@ Mon Feb 18 10:01:45 UTC 2019 - Pedro Monreal Gonzalez