summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@cvs.openbsd.org>2020-05-01 06:31:43 +0000
committerDamien Miller <djm@cvs.openbsd.org>2020-05-01 06:31:43 +0000
commit8a65ce289b96a51aef534bc66b023e66cb4c9e5b (patch)
tree993db801f659c353b6ab9a2add0cd65f109c217a
parentb5f0a0cb021e6e5d31c71a412ac21c286c9b42ee (diff)
when receving a file in sink(), be careful to send at most a single
error response after the file has been opened. Otherwise the source() and sink() can become desyncronised. Reported by Daniel Goujot, Georges-Axel Jaloyan, Ryan Lahfa, and David Naccache. ok deraadt@ markus@
-rw-r--r--usr.bin/ssh/scp.c96
1 files changed, 59 insertions, 37 deletions
diff --git a/usr.bin/ssh/scp.c b/usr.bin/ssh/scp.c
index 4b800268bce..c888a7b8ded 100644
--- a/usr.bin/ssh/scp.c
+++ b/usr.bin/ssh/scp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: scp.c,v 1.208 2020/04/30 17:07:10 markus Exp $ */
+/* $OpenBSD: scp.c,v 1.209 2020/05/01 06:31:42 djm Exp $ */
/*
* scp - secure remote copy. This is basically patched BSD rcp which
* uses ssh to do the data transfer (instead of using rcmd).
@@ -354,6 +354,7 @@ BUF *allocbuf(BUF *, int, int);
void lostconn(int);
int okname(char *);
void run_err(const char *,...);
+int note_err(const char *,...);
void verifydir(char *);
struct passwd *pwd;
@@ -1204,9 +1205,6 @@ sink(int argc, char **argv, const char *src)
{
static BUF buffer;
struct stat stb;
- enum {
- YES, NO, DISPLAYED
- } wrerr;
BUF *bp;
off_t i;
size_t j, count;
@@ -1214,7 +1212,7 @@ sink(int argc, char **argv, const char *src)
mode_t mode, omode, mask;
off_t size, statbytes;
unsigned long long ull;
- int setimes, targisdir, wrerrno = 0;
+ int setimes, targisdir, wrerr;
char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
char **patterns = NULL;
size_t n, npatterns = 0;
@@ -1423,8 +1421,13 @@ bad: run_err("%s: %s", np, strerror(errno));
continue;
}
cp = bp->buf;
- wrerr = NO;
+ wrerr = 0;
+ /*
+ * NB. do not use run_err() unless immediately followed by
+ * exit() below as it may send a spurious reply that might
+ * desyncronise us from the peer. Use note_err() instead.
+ */
statbytes = 0;
if (showprogress)
start_progress_meter(curfile, size, &statbytes);
@@ -1449,11 +1452,12 @@ bad: run_err("%s: %s", np, strerror(errno));
if (count == bp->cnt) {
/* Keep reading so we stay sync'd up. */
- if (wrerr == NO) {
+ if (!wrerr) {
if (atomicio(vwrite, ofd, bp->buf,
count) != count) {
- wrerr = YES;
- wrerrno = errno;
+ note_err("%s: %s", np,
+ strerror(errno));
+ wrerr = 1;
}
}
count = 0;
@@ -1461,56 +1465,42 @@ bad: run_err("%s: %s", np, strerror(errno));
}
}
unset_nonblock(remin);
- if (count != 0 && wrerr == NO &&
+ if (count != 0 && !wrerr &&
atomicio(vwrite, ofd, bp->buf, count) != count) {
- wrerr = YES;
- wrerrno = errno;
- }
- if (wrerr == NO && (!exists || S_ISREG(stb.st_mode)) &&
- ftruncate(ofd, size) != 0) {
- run_err("%s: truncate: %s", np, strerror(errno));
- wrerr = DISPLAYED;
+ note_err("%s: %s", np, strerror(errno));
+ wrerr = 1;
}
+ if (!wrerr && (!exists || S_ISREG(stb.st_mode)) &&
+ ftruncate(ofd, size) != 0)
+ note_err("%s: truncate: %s", np, strerror(errno));
if (pflag) {
if (exists || omode != mode)
if (fchmod(ofd, omode)) {
- run_err("%s: set mode: %s",
+ note_err("%s: set mode: %s",
np, strerror(errno));
- wrerr = DISPLAYED;
}
} else {
if (!exists && omode != mode)
if (fchmod(ofd, omode & ~mask)) {
- run_err("%s: set mode: %s",
+ note_err("%s: set mode: %s",
np, strerror(errno));
- wrerr = DISPLAYED;
}
}
- if (close(ofd) == -1) {
- wrerr = YES;
- wrerrno = errno;
- }
+ if (close(ofd) == -1)
+ note_err(np, "%s: close: %s", np, strerror(errno));
(void) response();
if (showprogress)
stop_progress_meter();
- if (setimes && wrerr == NO) {
+ if (setimes && !wrerr) {
setimes = 0;
if (utimes(np, tv) == -1) {
- run_err("%s: set times: %s",
+ note_err("%s: set times: %s",
np, strerror(errno));
- wrerr = DISPLAYED;
}
}
- switch (wrerr) {
- case YES:
- run_err("%s: %s", np, strerror(wrerrno));
- break;
- case NO:
+ /* If no error was noted then signal success for this file */
+ if (note_err(NULL) == 0)
(void) atomicio(vwrite, remout, "", 1);
- break;
- case DISPLAYED:
- break;
- }
}
done:
for (n = 0; n < npatterns; n++)
@@ -1598,6 +1588,38 @@ run_err(const char *fmt,...)
}
}
+/*
+ * Notes a sink error for sending at the end of a file transfer. Returns 0 if
+ * no error has been noted or -1 otherwise. Use note_err(NULL) to flush
+ * any active error at the end of the transfer.
+ */
+int
+note_err(const char *fmt, ...)
+{
+ static char *emsg;
+ va_list ap;
+
+ /* Replay any previously-noted error */
+ if (fmt == NULL) {
+ if (emsg == NULL)
+ return 0;
+ run_err("%s", emsg);
+ free(emsg);
+ emsg = NULL;
+ return -1;
+ }
+
+ errs++;
+ /* Prefer first-noted error */
+ if (emsg != NULL)
+ return -1;
+
+ va_start(ap, fmt);
+ vasnmprintf(&emsg, INT_MAX, NULL, fmt, ap);
+ va_end(ap);
+ return -1;
+}
+
void
verifydir(char *cp)
{