summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorschwarze@openbsd.org <schwarze@openbsd.org>2016-05-25 23:48:45 +0000
committerDarren Tucker <dtucker@zip.com.au>2016-06-06 11:27:38 +1000
commit0e059cdf5fd86297546c63fa8607c24059118832 (patch)
tree830942b6fd6f34250a42f265c1c90b9a398a6ab4
parent8c02e3639acefe1e447e293dbe23a0917abd3734 (diff)
upstream commit
To prevent screwing up terminal settings when printing to the terminal, for ASCII and UTF-8, escape bytes not forming characters and bytes forming non-printable characters with vis(3) VIS_OCTAL. For other character sets, abort printing of the current string in these cases. In particular, * let scp(1) respect the local user's LC_CTYPE locale(1); * sanitize data received from the remote host; * sanitize filenames, usernames, and similar data even locally; * take character display widths into account for the progressmeter. This is believed to be sufficient to keep the local terminal safe on OpenBSD, but bad things can still happen on other systems with state-dependent locales because many places in the code print unencoded ASCII characters into the output stream. Using feedback from djm@ and martijn@, various aspects discussed with many others. deraadt@ says it should go in now, i probably already hesitated too long Upstream-ID: e66afbc94ee396ddcaffd433b9a3b80f387647e0
-rw-r--r--Makefile.in2
-rw-r--r--progressmeter.c51
-rw-r--r--scp.c45
-rw-r--r--sftp-client.c9
-rw-r--r--sftp.c46
-rw-r--r--utf8.c258
-rw-r--r--utf8.h24
7 files changed, 369 insertions, 66 deletions
diff --git a/Makefile.in b/Makefile.in
index af758d035..6267fefda 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -82,7 +82,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
82 compat.o crc32.o deattack.o fatal.o hostfile.o \ 82 compat.o crc32.o deattack.o fatal.o hostfile.o \
83 log.o match.o md-sha256.o moduli.o nchan.o packet.o opacket.o \ 83 log.o match.o md-sha256.o moduli.o nchan.o packet.o opacket.o \
84 readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \ 84 readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \
85 atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o \ 85 atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o utf.o \
86 monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ 86 monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
87 msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ 87 msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
88 ssh-pkcs11.o smult_curve25519_ref.o \ 88 ssh-pkcs11.o smult_curve25519_ref.o \
diff --git a/progressmeter.c b/progressmeter.c
index 3a455408c..4fed2f4f0 100644
--- a/progressmeter.c
+++ b/progressmeter.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: progressmeter.c,v 1.42 2016/03/02 22:42:40 dtucker Exp $ */ 1/* $OpenBSD: progressmeter.c,v 1.43 2016/05/25 23:48:45 schwarze Exp $ */
2/* 2/*
3 * Copyright (c) 2003 Nils Nordman. All rights reserved. 3 * Copyright (c) 2003 Nils Nordman. All rights reserved.
4 * 4 *
@@ -31,6 +31,7 @@
31 31
32#include <errno.h> 32#include <errno.h>
33#include <signal.h> 33#include <signal.h>
34#include <stdarg.h>
34#include <stdio.h> 35#include <stdio.h>
35#include <string.h> 36#include <string.h>
36#include <time.h> 37#include <time.h>
@@ -39,6 +40,7 @@
39#include "progressmeter.h" 40#include "progressmeter.h"
40#include "atomicio.h" 41#include "atomicio.h"
41#include "misc.h" 42#include "misc.h"
43#include "utf8.h"
42 44
43#define DEFAULT_WINSIZE 80 45#define DEFAULT_WINSIZE 80
44#define MAX_WINSIZE 512 46#define MAX_WINSIZE 512
@@ -119,14 +121,14 @@ format_size(char *buf, int size, off_t bytes)
119void 121void
120refresh_progress_meter(void) 122refresh_progress_meter(void)
121{ 123{
122 char buf[MAX_WINSIZE + 1]; 124 char buf[MAX_WINSIZE * 4 + 1];
123 off_t transferred; 125 off_t transferred;
124 double elapsed, now; 126 double elapsed, now;
125 int percent; 127 int percent;
126 off_t bytes_left; 128 off_t bytes_left;
127 int cur_speed; 129 int cur_speed;
128 int hours, minutes, seconds; 130 int hours, minutes, seconds;
129 int i, len; 131 size_t i;
130 int file_len; 132 int file_len;
131 133
132 transferred = *counter - (cur_pos ? cur_pos : start_pos); 134 transferred = *counter - (cur_pos ? cur_pos : start_pos);
@@ -157,17 +159,16 @@ refresh_progress_meter(void)
157 bytes_per_second = cur_speed; 159 bytes_per_second = cur_speed;
158 160
159 /* filename */ 161 /* filename */
160 buf[0] = '\0'; 162 buf[0] = '\r';
163 buf[1] = '\0';
161 file_len = win_size - 35; 164 file_len = win_size - 35;
162 if (file_len > 0) { 165 if (file_len > 0) {
163 len = snprintf(buf, file_len + 1, "\r%s", file); 166 (void) snmprintf(buf + 1, sizeof(buf) - 1 - 35,
164 if (len < 0) 167 &file_len, "%s", file);
165 len = 0; 168 i = strlen(buf);
166 if (len >= file_len + 1) 169 while (++file_len < win_size - 35 && i + 1 < sizeof(buf))
167 len = file_len; 170 buf[i++] = ' ';
168 for (i = len; i < file_len; i++) 171 buf[i] = '\0';
169 buf[i] = ' ';
170 buf[file_len] = '\0';
171 } 172 }
172 173
173 /* percent of transfer done */ 174 /* percent of transfer done */
@@ -175,18 +176,18 @@ refresh_progress_meter(void)
175 percent = ((float)cur_pos / end_pos) * 100; 176 percent = ((float)cur_pos / end_pos) * 100;
176 else 177 else
177 percent = 100; 178 percent = 100;
178 snprintf(buf + strlen(buf), win_size - strlen(buf), 179 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
179 " %3d%% ", percent); 180 " %3d%% ", percent);
180 181
181 /* amount transferred */ 182 /* amount transferred */
182 format_size(buf + strlen(buf), win_size - strlen(buf), 183 format_size(buf + strlen(buf), sizeof(buf) - strlen(buf),
183 cur_pos); 184 cur_pos);
184 strlcat(buf, " ", win_size); 185 strlcat(buf, " ", sizeof(buf));
185 186
186 /* bandwidth usage */ 187 /* bandwidth usage */
187 format_rate(buf + strlen(buf), win_size - strlen(buf), 188 format_rate(buf + strlen(buf), sizeof(buf) - strlen(buf),
188 (off_t)bytes_per_second); 189 (off_t)bytes_per_second);
189 strlcat(buf, "/s ", win_size); 190 strlcat(buf, "/s ", sizeof(buf));
190 191
191 /* ETA */ 192 /* ETA */
192 if (!transferred) 193 if (!transferred)
@@ -195,9 +196,9 @@ refresh_progress_meter(void)
195 stalled = 0; 196 stalled = 0;
196 197
197 if (stalled >= STALL_TIME) 198 if (stalled >= STALL_TIME)
198 strlcat(buf, "- stalled -", win_size); 199 strlcat(buf, "- stalled -", sizeof(buf));
199 else if (bytes_per_second == 0 && bytes_left) 200 else if (bytes_per_second == 0 && bytes_left)
200 strlcat(buf, " --:-- ETA", win_size); 201 strlcat(buf, " --:-- ETA", sizeof(buf));
201 else { 202 else {
202 if (bytes_left > 0) 203 if (bytes_left > 0)
203 seconds = bytes_left / bytes_per_second; 204 seconds = bytes_left / bytes_per_second;
@@ -210,19 +211,21 @@ refresh_progress_meter(void)
210 seconds -= minutes * 60; 211 seconds -= minutes * 60;
211 212
212 if (hours != 0) 213 if (hours != 0)
213 snprintf(buf + strlen(buf), win_size - strlen(buf), 214 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
214 "%d:%02d:%02d", hours, minutes, seconds); 215 "%d:%02d:%02d", hours, minutes, seconds);
215 else 216 else
216 snprintf(buf + strlen(buf), win_size - strlen(buf), 217 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
217 " %02d:%02d", minutes, seconds); 218 " %02d:%02d", minutes, seconds);
218 219
219 if (bytes_left > 0) 220 if (bytes_left > 0)
220 strlcat(buf, " ETA", win_size); 221 strlcat(buf, " ETA", sizeof(buf));
221 else 222 else
222 strlcat(buf, " ", win_size); 223 strlcat(buf, " ", sizeof(buf));
223 } 224 }
225 if (win_size < 35)
226 buf[win_size] = '\0';
224 227
225 atomicio(vwrite, STDOUT_FILENO, buf, win_size - 1); 228 atomicio(vwrite, STDOUT_FILENO, buf, strlen(buf));
226 last_update = now; 229 last_update = now;
227} 230}
228 231
diff --git a/scp.c b/scp.c
index 3f0d75090..43ca3fa09 100644
--- a/scp.c
+++ b/scp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: scp.c,v 1.185 2016/03/02 22:43:52 dtucker Exp $ */ 1/* $OpenBSD: scp.c,v 1.186 2016/05/25 23:48:45 schwarze Exp $ */
2/* 2/*
3 * scp - secure remote copy. This is basically patched BSD rcp which 3 * scp - secure remote copy. This is basically patched BSD rcp which
4 * uses ssh to do the data transfer (instead of using rcmd). 4 * uses ssh to do the data transfer (instead of using rcmd).
@@ -96,6 +96,7 @@
96#include <errno.h> 96#include <errno.h>
97#include <fcntl.h> 97#include <fcntl.h>
98#include <limits.h> 98#include <limits.h>
99#include <locale.h>
99#include <pwd.h> 100#include <pwd.h>
100#include <signal.h> 101#include <signal.h>
101#include <stdarg.h> 102#include <stdarg.h>
@@ -114,6 +115,7 @@
114#include "log.h" 115#include "log.h"
115#include "misc.h" 116#include "misc.h"
116#include "progressmeter.h" 117#include "progressmeter.h"
118#include "utf8.h"
117 119
118extern char *__progname; 120extern char *__progname;
119 121
@@ -191,7 +193,7 @@ do_local_cmd(arglist *a)
191 if (verbose_mode) { 193 if (verbose_mode) {
192 fprintf(stderr, "Executing:"); 194 fprintf(stderr, "Executing:");
193 for (i = 0; i < a->num; i++) 195 for (i = 0; i < a->num; i++)
194 fprintf(stderr, " %s", a->list[i]); 196 fmprintf(stderr, " %s", a->list[i]);
195 fprintf(stderr, "\n"); 197 fprintf(stderr, "\n");
196 } 198 }
197 if ((pid = fork()) == -1) 199 if ((pid = fork()) == -1)
@@ -232,7 +234,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
232 int pin[2], pout[2], reserved[2]; 234 int pin[2], pout[2], reserved[2];
233 235
234 if (verbose_mode) 236 if (verbose_mode)
235 fprintf(stderr, 237 fmprintf(stderr,
236 "Executing: program %s host %s, user %s, command %s\n", 238 "Executing: program %s host %s, user %s, command %s\n",
237 ssh_program, host, 239 ssh_program, host,
238 remuser ? remuser : "(unspecified)", cmd); 240 remuser ? remuser : "(unspecified)", cmd);
@@ -307,7 +309,7 @@ do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout)
307 int status; 309 int status;
308 310
309 if (verbose_mode) 311 if (verbose_mode)
310 fprintf(stderr, 312 fmprintf(stderr,
311 "Executing: 2nd program %s host %s, user %s, command %s\n", 313 "Executing: 2nd program %s host %s, user %s, command %s\n",
312 ssh_program, host, 314 ssh_program, host,
313 remuser ? remuser : "(unspecified)", cmd); 315 remuser ? remuser : "(unspecified)", cmd);
@@ -378,6 +380,8 @@ main(int argc, char **argv)
378 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 380 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
379 sanitise_stdfd(); 381 sanitise_stdfd();
380 382
383 setlocale(LC_CTYPE, "");
384
381 /* Copy argv, because we modify it */ 385 /* Copy argv, because we modify it */
382 newargv = xcalloc(MAX(argc + 1, 1), sizeof(*newargv)); 386 newargv = xcalloc(MAX(argc + 1, 1), sizeof(*newargv));
383 for (n = 0; n < argc; n++) 387 for (n = 0; n < argc; n++)
@@ -810,9 +814,8 @@ syserr: run_err("%s: %s", name, strerror(errno));
810 snprintf(buf, sizeof buf, "C%04o %lld %s\n", 814 snprintf(buf, sizeof buf, "C%04o %lld %s\n",
811 (u_int) (stb.st_mode & FILEMODEMASK), 815 (u_int) (stb.st_mode & FILEMODEMASK),
812 (long long)stb.st_size, last); 816 (long long)stb.st_size, last);
813 if (verbose_mode) { 817 if (verbose_mode)
814 fprintf(stderr, "Sending file modes: %s", buf); 818 fmprintf(stderr, "Sending file modes: %s", buf);
815 }
816 (void) atomicio(vwrite, remout, buf, strlen(buf)); 819 (void) atomicio(vwrite, remout, buf, strlen(buf));
817 if (response() < 0) 820 if (response() < 0)
818 goto next; 821 goto next;
@@ -889,7 +892,7 @@ rsource(char *name, struct stat *statp)
889 (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", 892 (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n",
890 (u_int) (statp->st_mode & FILEMODEMASK), 0, last); 893 (u_int) (statp->st_mode & FILEMODEMASK), 0, last);
891 if (verbose_mode) 894 if (verbose_mode)
892 fprintf(stderr, "Entering directory: %s", path); 895 fmprintf(stderr, "Entering directory: %s", path);
893 (void) atomicio(vwrite, remout, path, strlen(path)); 896 (void) atomicio(vwrite, remout, path, strlen(path));
894 if (response() < 0) { 897 if (response() < 0) {
895 closedir(dirp); 898 closedir(dirp);
@@ -929,7 +932,7 @@ sink(int argc, char **argv)
929 off_t size, statbytes; 932 off_t size, statbytes;
930 unsigned long long ull; 933 unsigned long long ull;
931 int setimes, targisdir, wrerrno = 0; 934 int setimes, targisdir, wrerrno = 0;
932 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; 935 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
933 struct timeval tv[2]; 936 struct timeval tv[2];
934 937
935#define atime tv[0] 938#define atime tv[0]
@@ -964,12 +967,15 @@ sink(int argc, char **argv)
964 } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 967 } while (cp < &buf[sizeof(buf) - 1] && ch != '\n');
965 *cp = 0; 968 *cp = 0;
966 if (verbose_mode) 969 if (verbose_mode)
967 fprintf(stderr, "Sink: %s", buf); 970 fmprintf(stderr, "Sink: %s", buf);
968 971
969 if (buf[0] == '\01' || buf[0] == '\02') { 972 if (buf[0] == '\01' || buf[0] == '\02') {
970 if (iamremote == 0) 973 if (iamremote == 0) {
974 (void) snmprintf(visbuf, sizeof(visbuf),
975 NULL, "%s", buf + 1);
971 (void) atomicio(vwrite, STDERR_FILENO, 976 (void) atomicio(vwrite, STDERR_FILENO,
972 buf + 1, strlen(buf + 1)); 977 visbuf, strlen(visbuf));
978 }
973 if (buf[0] == '\02') 979 if (buf[0] == '\02')
974 exit(1); 980 exit(1);
975 ++errs; 981 ++errs;
@@ -1212,7 +1218,7 @@ screwup:
1212int 1218int
1213response(void) 1219response(void)
1214{ 1220{
1215 char ch, *cp, resp, rbuf[2048]; 1221 char ch, *cp, resp, rbuf[2048], visbuf[2048];
1216 1222
1217 if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) 1223 if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp))
1218 lostconn(0); 1224 lostconn(0);
@@ -1232,8 +1238,13 @@ response(void)
1232 *cp++ = ch; 1238 *cp++ = ch;
1233 } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 1239 } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n');
1234 1240
1235 if (!iamremote) 1241 if (!iamremote) {
1236 (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); 1242 cp[-1] = '\0';
1243 (void) snmprintf(visbuf, sizeof(visbuf),
1244 NULL, "%s\n", rbuf);
1245 (void) atomicio(vwrite, STDERR_FILENO,
1246 visbuf, strlen(visbuf));
1247 }
1237 ++errs; 1248 ++errs;
1238 if (resp == 1) 1249 if (resp == 1)
1239 return (-1); 1250 return (-1);
@@ -1271,7 +1282,7 @@ run_err(const char *fmt,...)
1271 1282
1272 if (!iamremote) { 1283 if (!iamremote) {
1273 va_start(ap, fmt); 1284 va_start(ap, fmt);
1274 vfprintf(stderr, fmt, ap); 1285 vfmprintf(stderr, fmt, ap);
1275 va_end(ap); 1286 va_end(ap);
1276 fprintf(stderr, "\n"); 1287 fprintf(stderr, "\n");
1277 } 1288 }
@@ -1317,7 +1328,7 @@ okname(char *cp0)
1317 } while (*++cp); 1328 } while (*++cp);
1318 return (1); 1329 return (1);
1319 1330
1320bad: fprintf(stderr, "%s: invalid user name\n", cp0); 1331bad: fmprintf(stderr, "%s: invalid user name\n", cp0);
1321 return (0); 1332 return (0);
1322} 1333}
1323 1334
diff --git a/sftp-client.c b/sftp-client.c
index faf14684c..0ca44a4d6 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.c,v 1.123 2016/05/02 08:49:03 djm Exp $ */ 1/* $OpenBSD: sftp-client.c,v 1.124 2016/05/25 23:48:45 schwarze Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -53,6 +53,7 @@
53#include "atomicio.h" 53#include "atomicio.h"
54#include "progressmeter.h" 54#include "progressmeter.h"
55#include "misc.h" 55#include "misc.h"
56#include "utf8.h"
56 57
57#include "sftp.h" 58#include "sftp.h"
58#include "sftp-common.h" 59#include "sftp-common.h"
@@ -610,7 +611,7 @@ do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag,
610 } 611 }
611 612
612 if (print_flag) 613 if (print_flag)
613 printf("%s\n", longname); 614 mprintf("%s\n", longname);
614 615
615 /* 616 /*
616 * Directory entries should never contain '/' 617 * Directory entries should never contain '/'
@@ -1460,7 +1461,7 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
1460 return -1; 1461 return -1;
1461 } 1462 }
1462 if (print_flag) 1463 if (print_flag)
1463 printf("Retrieving %s\n", src); 1464 mprintf("Retrieving %s\n", src);
1464 1465
1465 if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 1466 if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1466 mode = dirattrib->perm & 01777; 1467 mode = dirattrib->perm & 01777;
@@ -1793,7 +1794,7 @@ upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
1793 return -1; 1794 return -1;
1794 } 1795 }
1795 if (print_flag) 1796 if (print_flag)
1796 printf("Entering %s\n", src); 1797 mprintf("Entering %s\n", src);
1797 1798
1798 attrib_clear(&a); 1799 attrib_clear(&a);
1799 stat_to_attrib(&sb, &a); 1800 stat_to_attrib(&sb, &a);
diff --git a/sftp.c b/sftp.c
index 3d2d13aaf..6a7048431 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp.c,v 1.173 2016/04/08 08:19:17 djm Exp $ */ 1/* $OpenBSD: sftp.c,v 1.174 2016/05/25 23:48:45 schwarze Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -49,6 +49,7 @@ typedef void EditLine;
49#endif 49#endif
50#include <limits.h> 50#include <limits.h>
51#include <signal.h> 51#include <signal.h>
52#include <stdarg.h>
52#include <stdlib.h> 53#include <stdlib.h>
53#include <stdio.h> 54#include <stdio.h>
54#include <string.h> 55#include <string.h>
@@ -63,6 +64,7 @@ typedef void EditLine;
63#include "log.h" 64#include "log.h"
64#include "pathnames.h" 65#include "pathnames.h"
65#include "misc.h" 66#include "misc.h"
67#include "utf8.h"
66 68
67#include "sftp.h" 69#include "sftp.h"
68#include "ssherr.h" 70#include "ssherr.h"
@@ -644,9 +646,11 @@ process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
644 646
645 resume |= global_aflag; 647 resume |= global_aflag;
646 if (!quiet && resume) 648 if (!quiet && resume)
647 printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst); 649 mprintf("Resuming %s to %s\n",
650 g.gl_pathv[i], abs_dst);
648 else if (!quiet && !resume) 651 else if (!quiet && !resume)
649 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 652 mprintf("Fetching %s to %s\n",
653 g.gl_pathv[i], abs_dst);
650 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 654 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
651 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, 655 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
652 pflag || global_pflag, 1, resume, 656 pflag || global_pflag, 1, resume,
@@ -735,10 +739,11 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
735 739
736 resume |= global_aflag; 740 resume |= global_aflag;
737 if (!quiet && resume) 741 if (!quiet && resume)
738 printf("Resuming upload of %s to %s\n", g.gl_pathv[i], 742 mprintf("Resuming upload of %s to %s\n",
739 abs_dst); 743 g.gl_pathv[i], abs_dst);
740 else if (!quiet && !resume) 744 else if (!quiet && !resume)
741 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 745 mprintf("Uploading %s to %s\n",
746 g.gl_pathv[i], abs_dst);
742 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 747 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
743 if (upload_dir(conn, g.gl_pathv[i], abs_dst, 748 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
744 pflag || global_pflag, 1, resume, 749 pflag || global_pflag, 1, resume,
@@ -839,12 +844,12 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
839 attrib_to_stat(&d[n]->a, &sb); 844 attrib_to_stat(&d[n]->a, &sb);
840 lname = ls_file(fname, &sb, 1, 845 lname = ls_file(fname, &sb, 1,
841 (lflag & LS_SI_UNITS)); 846 (lflag & LS_SI_UNITS));
842 printf("%s\n", lname); 847 mprintf("%s\n", lname);
843 free(lname); 848 free(lname);
844 } else 849 } else
845 printf("%s\n", d[n]->longname); 850 mprintf("%s\n", d[n]->longname);
846 } else { 851 } else {
847 printf("%-*s", colspace, fname); 852 mprintf("%-*s", colspace, fname);
848 if (c >= columns) { 853 if (c >= columns) {
849 printf("\n"); 854 printf("\n");
850 c = 1; 855 c = 1;
@@ -925,10 +930,10 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
925 } 930 }
926 lname = ls_file(fname, g.gl_statv[i], 1, 931 lname = ls_file(fname, g.gl_statv[i], 1,
927 (lflag & LS_SI_UNITS)); 932 (lflag & LS_SI_UNITS));
928 printf("%s\n", lname); 933 mprintf("%s\n", lname);
929 free(lname); 934 free(lname);
930 } else { 935 } else {
931 printf("%-*s", colspace, fname); 936 mprintf("%-*s", colspace, fname);
932 if (c >= columns) { 937 if (c >= columns) {
933 printf("\n"); 938 printf("\n");
934 c = 1; 939 c = 1;
@@ -1456,7 +1461,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1456 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1461 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1457 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1462 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1458 if (!quiet) 1463 if (!quiet)
1459 printf("Removing %s\n", g.gl_pathv[i]); 1464 mprintf("Removing %s\n", g.gl_pathv[i]);
1460 err = do_rm(conn, g.gl_pathv[i]); 1465 err = do_rm(conn, g.gl_pathv[i]);
1461 if (err != 0 && err_abort) 1466 if (err != 0 && err_abort)
1462 break; 1467 break;
@@ -1556,7 +1561,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1556 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1561 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1557 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1562 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1558 if (!quiet) 1563 if (!quiet)
1559 printf("Changing mode on %s\n", g.gl_pathv[i]); 1564 mprintf("Changing mode on %s\n",
1565 g.gl_pathv[i]);
1560 err = do_setstat(conn, g.gl_pathv[i], &a); 1566 err = do_setstat(conn, g.gl_pathv[i], &a);
1561 if (err != 0 && err_abort) 1567 if (err != 0 && err_abort)
1562 break; 1568 break;
@@ -1586,12 +1592,12 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1586 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1592 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1587 if (cmdnum == I_CHOWN) { 1593 if (cmdnum == I_CHOWN) {
1588 if (!quiet) 1594 if (!quiet)
1589 printf("Changing owner on %s\n", 1595 mprintf("Changing owner on %s\n",
1590 g.gl_pathv[i]); 1596 g.gl_pathv[i]);
1591 aa->uid = n_arg; 1597 aa->uid = n_arg;
1592 } else { 1598 } else {
1593 if (!quiet) 1599 if (!quiet)
1594 printf("Changing group on %s\n", 1600 mprintf("Changing group on %s\n",
1595 g.gl_pathv[i]); 1601 g.gl_pathv[i]);
1596 aa->gid = n_arg; 1602 aa->gid = n_arg;
1597 } 1603 }
@@ -1601,7 +1607,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1601 } 1607 }
1602 break; 1608 break;
1603 case I_PWD: 1609 case I_PWD:
1604 printf("Remote working directory: %s\n", *pwd); 1610 mprintf("Remote working directory: %s\n", *pwd);
1605 break; 1611 break;
1606 case I_LPWD: 1612 case I_LPWD:
1607 if (!getcwd(path_buf, sizeof(path_buf))) { 1613 if (!getcwd(path_buf, sizeof(path_buf))) {
@@ -1609,7 +1615,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1609 err = -1; 1615 err = -1;
1610 break; 1616 break;
1611 } 1617 }
1612 printf("Local working directory: %s\n", path_buf); 1618 mprintf("Local working directory: %s\n", path_buf);
1613 break; 1619 break;
1614 case I_QUIT: 1620 case I_QUIT:
1615 /* Processed below */ 1621 /* Processed below */
@@ -1678,7 +1684,7 @@ complete_display(char **list, u_int len)
1678 for (y = 0; list[y]; y++) { 1684 for (y = 0; list[y]; y++) {
1679 llen = strlen(list[y]); 1685 llen = strlen(list[y]);
1680 tmp = llen > len ? list[y] + len : ""; 1686 tmp = llen > len ? list[y] + len : "";
1681 printf("%-*s", colspace, tmp); 1687 mprintf("%-*s", colspace, tmp);
1682 if (m >= columns) { 1688 if (m >= columns) {
1683 printf("\n"); 1689 printf("\n");
1684 m = 1; 1690 m = 1;
@@ -2062,7 +2068,7 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2062 2068
2063 if (remote_is_dir(conn, dir) && file2 == NULL) { 2069 if (remote_is_dir(conn, dir) && file2 == NULL) {
2064 if (!quiet) 2070 if (!quiet)
2065 printf("Changing to: %s\n", dir); 2071 mprintf("Changing to: %s\n", dir);
2066 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 2072 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2067 if (parse_dispatch_command(conn, cmd, 2073 if (parse_dispatch_command(conn, cmd,
2068 &remote_path, 1) != 0) { 2074 &remote_path, 1) != 0) {
@@ -2106,7 +2112,7 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2106 break; 2112 break;
2107 } 2113 }
2108 if (!interactive) { /* Echo command */ 2114 if (!interactive) { /* Echo command */
2109 printf("sftp> %s", cmd); 2115 mprintf("sftp> %s", cmd);
2110 if (strlen(cmd) > 0 && 2116 if (strlen(cmd) > 0 &&
2111 cmd[strlen(cmd) - 1] != '\n') 2117 cmd[strlen(cmd) - 1] != '\n')
2112 printf("\n"); 2118 printf("\n");
diff --git a/utf8.c b/utf8.c
new file mode 100644
index 000000000..d6089bdec
--- /dev/null
+++ b/utf8.c
@@ -0,0 +1,258 @@
1/* $OpenBSD: utf8.c,v 1.1 2016/05/25 23:48:45 schwarze Exp $ */
2/*
3 * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/*
19 * Utility functions for multibyte-character handling,
20 * in particular to sanitize untrusted strings for terminal output.
21 */
22
23#include <sys/types.h>
24#include <langinfo.h>
25#include <limits.h>
26#include <stdarg.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <vis.h>
31#include <wchar.h>
32
33#include "utf8.h"
34
35static int dangerous_locale(void);
36static int vasnmprintf(char **, size_t, int *, const char *, va_list);
37
38
39/*
40 * For US-ASCII and UTF-8 encodings, we can safely recover from
41 * encoding errors and from non-printable characters. For any
42 * other encodings, err to the side of caution and abort parsing:
43 * For state-dependent encodings, recovery is impossible.
44 * For arbitrary encodings, replacement of non-printable
45 * characters would be non-trivial and too fragile.
46 */
47
48static int
49dangerous_locale(void) {
50 char *loc;
51
52 loc = nl_langinfo(CODESET);
53 return strcmp(loc, "US-ASCII") && strcmp(loc, "UTF-8");
54}
55
56/*
57 * The following two functions limit the number of bytes written,
58 * including the terminating '\0', to sz. Unless wp is NULL,
59 * they limit the number of display columns occupied to *wp.
60 * Whichever is reached first terminates the output string.
61 * To stay close to the standard interfaces, they return the number of
62 * non-NUL bytes that would have been written if both were unlimited.
63 * If wp is NULL, newline, carriage return, and tab are allowed;
64 * otherwise, the actual number of columns occupied by what was
65 * written is returned in *wp.
66 */
67
68static int
69vasnmprintf(char **str, size_t maxsz, int *wp, const char *fmt, va_list ap)
70{
71 char *src; /* Source string returned from vasprintf. */
72 char *sp; /* Pointer into src. */
73 char *dst; /* Destination string to be returned. */
74 char *dp; /* Pointer into dst. */
75 char *tp; /* Temporary pointer for dst. */
76 size_t sz; /* Number of bytes allocated for dst. */
77 size_t tsz; /* Temporary size while extending dst. */
78 wchar_t wc; /* Wide character at sp. */
79 int len; /* Number of bytes in the character at sp. */
80 int ret; /* Number of bytes needed to format src. */
81 int width; /* Display width of the character wc. */
82 int total_width, max_width, print;
83
84 src = dst = NULL;
85 if (vasprintf(&src, fmt, ap) <= 0)
86 goto fail;
87
88 sz = strlen(src);
89 if ((dst = malloc(sz)) == NULL)
90 goto fail;
91
92 if (maxsz > INT_MAX)
93 maxsz = INT_MAX;
94
95 sp = src;
96 dp = dst;
97 ret = 0;
98 print = 1;
99 total_width = 0;
100 max_width = wp == NULL ? INT_MAX : *wp;
101 while (*sp != '\0') {
102 if ((len = mbtowc(&wc, sp, MB_CUR_MAX)) == -1) {
103 (void)mbtowc(NULL, NULL, MB_CUR_MAX);
104 if (dangerous_locale()) {
105 ret = -1;
106 break;
107 }
108 len = 1;
109 width = -1;
110 } else if (wp == NULL &&
111 (wc == L'\n' || wc == L'\r' || wc == L'\t')) {
112 /*
113 * Don't use width uninitialized; the actual
114 * value doesn't matter because total_width
115 * is only returned for wp != NULL.
116 */
117 width = 0;
118 } else if ((width = wcwidth(wc)) == -1 &&
119 dangerous_locale()) {
120 ret = -1;
121 break;
122 }
123
124 /* Valid, printable character. */
125
126 if (width >= 0) {
127 if (print && (dp - dst >= (int)maxsz - len ||
128 total_width > max_width - width))
129 print = 0;
130 if (print) {
131 total_width += width;
132 memcpy(dp, sp, len);
133 dp += len;
134 }
135 sp += len;
136 if (ret >= 0)
137 ret += len;
138 continue;
139 }
140
141 /* Escaping required. */
142
143 while (len > 0) {
144 if (print && (dp - dst >= (int)maxsz - 4 ||
145 total_width > max_width - 4))
146 print = 0;
147 if (print) {
148 if (dp + 4 >= dst + sz) {
149 tsz = sz + 128;
150 if (tsz > maxsz)
151 tsz = maxsz;
152 tp = realloc(dst, tsz);
153 if (tp == NULL) {
154 ret = -1;
155 break;
156 }
157 dp = tp + (dp - dst);
158 dst = tp;
159 sz = tsz;
160 }
161 tp = vis(dp, *sp, VIS_OCTAL | VIS_ALL, 0);
162 width = tp - dp;
163 total_width += width;
164 dp = tp;
165 } else
166 width = 4;
167 len--;
168 sp++;
169 if (ret >= 0)
170 ret += width;
171 }
172 if (len > 0)
173 break;
174 }
175 free(src);
176 *dp = '\0';
177 *str = dst;
178 if (wp != NULL)
179 *wp = total_width;
180
181 /*
182 * If the string was truncated by the width limit but
183 * would have fit into the size limit, the only sane way
184 * to report the problem is using the return value, such
185 * that the usual idiom "if (ret < 0 || ret >= sz) error"
186 * works as expected.
187 */
188
189 if (ret < (int)maxsz && !print)
190 ret = -1;
191 return ret;
192
193fail:
194 free(src);
195 free(dst);
196 *str = NULL;
197 if (wp != NULL)
198 *wp = 0;
199 return -1;
200}
201
202int
203snmprintf(char *str, size_t sz, int *wp, const char *fmt, ...)
204{
205 va_list ap;
206 char *cp;
207 int ret;
208
209 va_start(ap, fmt);
210 ret = vasnmprintf(&cp, sz, wp, fmt, ap);
211 va_end(ap);
212 (void)strlcpy(str, cp, sz);
213 free(cp);
214 return ret;
215}
216
217/*
218 * To stay close to the standard interfaces, the following functions
219 * return the number of non-NUL bytes written.
220 */
221
222int
223vfmprintf(FILE *stream, const char *fmt, va_list ap)
224{
225 char *str;
226 int ret;
227
228 if ((ret = vasnmprintf(&str, INT_MAX, NULL, fmt, ap)) < 0)
229 return -1;
230 if (fputs(str, stream) == EOF)
231 ret = -1;
232 free(str);
233 return ret;
234}
235
236int
237fmprintf(FILE *stream, const char *fmt, ...)
238{
239 va_list ap;
240 int ret;
241
242 va_start(ap, fmt);
243 ret = vfmprintf(stream, fmt, ap);
244 va_end(ap);
245 return ret;
246}
247
248int
249mprintf(const char *fmt, ...)
250{
251 va_list ap;
252 int ret;
253
254 va_start(ap, fmt);
255 ret = vfmprintf(stdout, fmt, ap);
256 va_end(ap);
257 return ret;
258}
diff --git a/utf8.h b/utf8.h
new file mode 100644
index 000000000..43ce1d55d
--- /dev/null
+++ b/utf8.h
@@ -0,0 +1,24 @@
1/* $OpenBSD: utf8.h,v 1.1 2016/05/25 23:48:45 schwarze Exp $ */
2/*
3 * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18int mprintf(const char *, ...)
19 __attribute__((format(printf, 1, 2)));
20int fmprintf(FILE *, const char *, ...)
21 __attribute__((format(printf, 2, 3)));
22int vfmprintf(FILE *, const char *, va_list);
23int snmprintf(char *, size_t, int *, const char *, ...)
24 __attribute__((format(printf, 4, 5)));