From c5c328b30332afe8cb708399990944424383072e Mon Sep 17 00:00:00 2001 From: Ray Hayes Date: Wed, 26 Oct 2016 15:11:36 -0700 Subject: [PATCH 1/2] Scp changes (inline with original). --- contrib/win32/openssh/libssh.vcxproj | 1 + contrib/win32/openssh/libssh.vcxproj.filters | 3 + openbsd-compat/openbsd-compat.h | 35 +++ openbsd-compat/vis.h | 2 +- scp.c | 207 ++----------- utf8.c | 302 +++++++++++++++++++ utf8.h | 24 ++ 7 files changed, 385 insertions(+), 189 deletions(-) create mode 100644 utf8.c create mode 100644 utf8.h diff --git a/contrib/win32/openssh/libssh.vcxproj b/contrib/win32/openssh/libssh.vcxproj index 6413b8d..ffbb9e1 100644 --- a/contrib/win32/openssh/libssh.vcxproj +++ b/contrib/win32/openssh/libssh.vcxproj @@ -266,6 +266,7 @@ true + diff --git a/contrib/win32/openssh/libssh.vcxproj.filters b/contrib/win32/openssh/libssh.vcxproj.filters index fa793bc..156819e 100644 --- a/contrib/win32/openssh/libssh.vcxproj.filters +++ b/contrib/win32/openssh/libssh.vcxproj.filters @@ -294,6 +294,9 @@ Source Files + + Source Files + diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h index 0823028..1e69891 100644 --- a/openbsd-compat/openbsd-compat.h +++ b/openbsd-compat/openbsd-compat.h @@ -232,10 +232,45 @@ long long strtonum(const char *, long long, long long, const char **); # 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 + +#ifndef HAVE_MBTOWC +int mbtowc(wchar_t *, const char*, size_t); +#endif + + #if !defined(HAVE_VASPRINTF) || !defined(HAVE_VSNPRINTF) # include #endif +/* +* Some platforms unconditionally undefine va_copy() so we define VA_COPY() +* instead. This is known to be the case on at least some configurations of +* AIX with the xlc compiler. +*/ +#ifndef VA_COPY +# ifdef HAVE_VA_COPY +# define VA_COPY(dest, src) va_copy(dest, src) +# else +# ifdef HAVE___VA_COPY +# define VA_COPY(dest, src) __va_copy(dest, src) +# else +# define VA_COPY(dest, src) (dest) = (src) +# endif +# endif +#endif + + #ifndef HAVE_VASPRINTF int vasprintf(char **, const char *, va_list); #endif diff --git a/openbsd-compat/vis.h b/openbsd-compat/vis.h index d1286c9..b6ee6ec 100644 --- a/openbsd-compat/vis.h +++ b/openbsd-compat/vis.h @@ -48,7 +48,7 @@ */ #define VIS_OCTAL 0x01 /* use octal \ddd format */ #define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ - +#define VIS_ALL 0x400 /* encode all characters */ /* * to alter set of characters encoded (default is to encode all * non-graphic except space, tab, and newline). diff --git a/scp.c b/scp.c index 2e0dee3..fb19dcd 100644 --- a/scp.c +++ b/scp.c @@ -78,6 +78,7 @@ #else #include #include +#include #include "win32_dirent.h" #endif @@ -104,6 +105,7 @@ #include #include #include +#include #include #include #include @@ -122,10 +124,8 @@ #include "log.h" #include "misc.h" #include "progressmeter.h" +#include "utf8.h" -#ifdef WINDOWS -#include -#endif extern char *__progname; #define COPY_BUFLEN 16384 @@ -319,125 +319,6 @@ int InitForMicrosoftWindows() return 0; } -// start of direntry functions in Windows NT like UNIX -// opendir(), readdir(), closedir(). -// NT_DIR * nt_opendir(char *name) ; -// struct nt_dirent *nt_readdir(NT_DIR *dirp); -// int nt_closedir(NT_DIR *dirp) ; - -// Windows NT directory structure content -struct scp_dirent { - char *d_name ; // name of the directory entry - int d_ino; // UNIX inode - //unsigned attrib ; // its attributes -}; - -typedef struct { - long hFile; - struct _finddata_t c_file; -} SCPDIR; - - -char * fixslashes(char * str) -{ - int i; - if (str == NULL) - return str; - - int len = (int)strlen(str); - - for (i = 0; i < len; i++) - if (str[i] == '/') - str[i] = '\\'; - return str; -} - -// force path separator to sep -char * forcepathsep(char * str, char sep) -{ - int i; - // bail if str is null; - if (str == NULL) - return str; - - // bail if sep isn't valid - if ((sep != '\\') || (sep != '/')) - return str; - - char antisep = '/'; - - if (sep == '/') - antisep = '\\'; - - int len = (int)strlen(str); - - for (i = 0; i < len; i++) - if (str[i] == antisep) - str[i] = sep; - return str; -} - -// get the path separator character -char getpathsep(char * path) -{ - char sep = '/'; - char * p = strpbrk(path,"/\\"); - if (p != NULL) - sep = p[0]; - - return sep; -} - -bool getRootFrompath(char * path, char * root) -{ - strcpy(root,path); - - char sep = getpathsep(root); - forcepathsep(root,sep); - char * lastslash = strrchr(root,sep); - if (lastslash) - *lastslash = 0x00; - return (lastslash != NULL); -} - -/* - * get option letter from argument vector - */ - -char * getfilenamefrompath(char * path) -{ - char * lastslash; - char * lastbackslash; - - lastslash = strrchr(path,'/'); - lastbackslash = strrchr(path, '\\'); - - if (lastslash == NULL && lastbackslash == NULL) - { - // no delimiters, just return the original string - return path; - } - else if (lastslash == NULL) - { - // no slashes, return the backslash search minus the last char - return ++lastbackslash; - } - else if (lastbackslash == NULL) - { - // no backslashes, return the slash search minus the last char - return ++lastslash; - } - else - { - // string has both slashes and backslashes. return whichever is larger - // (i.e. further down the string) - lastslash++; - lastbackslash++; - return ((lastslash > lastbackslash)?lastslash:lastbackslash); - - } - return NULL; -} #endif #define EMSG "" @@ -526,33 +407,6 @@ sgetopt(int nargc, return(optopt); /* dump back option letter */ } - - -/* Open a directory stream on NAME. - Return a SCPDIR stream on the directory, or NULL if it could not be opened. */ -SCPDIR * scp_opendir(char *name) -{ - struct _finddata_t c_file; - long hFile; - SCPDIR *pdir; - char searchstr[256]; - - sprintf_s(searchstr,sizeof(searchstr),"%s\\*.*",name); // add *.* to it for NT - - if( (hFile = (long)_findfirst( searchstr, &c_file )) == -1L ) { - if ( scpverbose) - printf( "No files found for %s search.\n", name ); - return (SCPDIR *) NULL; - } - else { - pdir = (SCPDIR *) malloc( sizeof(SCPDIR) ); - pdir->hFile = hFile ; - pdir->c_file = c_file ; - - return pdir ; - } -} - int _utimedir (char *name, struct _utimbuf *filetime) { int rc, chandle; @@ -579,30 +433,6 @@ int _utimedir (char *name, struct _utimbuf *filetime) HANDLE hprocess=(HANDLE) 0; // we made it a global to stop child process(ssh) of scp #else -/* Read a directory entry from SCPDIRP. -Return a pointer to a `struct scp_dirent' describing the entry, -or NULL for EOF or error. The storage returned may be overwritten -by a later readdir call on the same SCPDIR stream. */ -struct scp_dirent *readdir(SCPDIR *dirp) -{ - struct scp_dirent *pdirentry; - - for (;;) { - if (_findnext(dirp->hFile, &(dirp->c_file)) == 0) { - if ((strcmp(dirp->c_file.name, ".") == 0) || - (strcmp(dirp->c_file.name, "..") == 0)) { - continue; - } - pdirentry = (struct scp_dirent *)malloc(sizeof(struct scp_dirent)); - pdirentry->d_name = dirp->c_file.name; - pdirentry->d_ino = 1; // a fictious one like UNIX to say it is nonzero - return pdirentry; - } - else { - return (struct scp_dirent *) NULL; - } - } -} #endif static void @@ -645,7 +475,7 @@ do_local_cmd(arglist *a) if (verbose_mode) { fprintf(stderr, "Executing:"); for (i = 0; i < a->num; i++) - fprintf(stderr, " %s", a->list[i]); + fmprintf(stderr, " %s", a->list[i]); fprintf(stderr, "\n"); } #ifdef WINDOWS @@ -941,7 +771,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) int pin[2], pout[2], reserved[2]; if (verbose_mode) - fprintf(stderr, + fmprintf(stderr, "Executing: program %s host %s, user %s, command %s\n", ssh_program, host, remuser ? remuser : "(unspecified)", cmd); @@ -1018,7 +848,7 @@ do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) int status; if (verbose_mode) - fprintf(stderr, + fmprintf(stderr, "Executing: 2nd program %s host %s, user %s, command %s\n", ssh_program, host, remuser ? remuser : "(unspecified)", cmd); @@ -1095,6 +925,8 @@ main(int argc, char **argv) /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); + setlocale(LC_CTYPE, ""); + /* Copy argv, because we modify it */ newargv = xcalloc(MAX(argc + 1, 1), sizeof(*newargv)); for (n = 0; n < argc; n++) @@ -1569,9 +1401,8 @@ syserr: run_err("%s: %s", name, strerror(errno)); snprintf(buf, sizeof buf, "C%04o %lld %s\n", (u_int) (stb.st_mode & FILEMODEMASK), (long long)stb.st_size, last); - if (verbose_mode) { - fprintf(stderr, "Sending file modes: %s", buf); - } + if (verbose_mode) + fmprintf(stderr, "Sending file modes: %s", buf); (void) atomicio(vwrite, remout, buf, strlen(buf)); if (response() < 0) goto next; @@ -1607,8 +1438,6 @@ next: if (fd != -1) { haderr = errno; } unset_nonblock(remout); - if (showprogress) - stop_progress_meter(); if (fd != -1) { if (close(fd) < 0 && !haderr) @@ -1620,6 +1449,8 @@ next: if (fd != -1) { else run_err("%s: %s", name, strerror(haderr)); (void) response(); + if (showprogress) + stop_progress_meter(); } } @@ -1635,7 +1466,7 @@ rsource(char *name, struct stat *statp) return; } last = strrchr(name, '/'); - if (last == 0) + if (last == NULL) last = name; else last++; @@ -1656,7 +1487,7 @@ rsource(char *name, struct stat *statp) free(wtmp); } #else - fprintf(stderr, "Entering directory: %s", path); + fmprintf(stderr, "Entering directory: %s", path); #endif (void) atomicio(vwrite, remout, path, strlen(path)); @@ -1741,7 +1572,7 @@ sink(int argc, char **argv) free(wtmp); } #else - fprintf(stderr, "Sink: %s", buf); + fmprintf(stderr, "Sink: %s", buf); #endif if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) @@ -1922,8 +1753,6 @@ bad: run_err("%s: %s", np, strerror(errno)); } } unset_nonblock(remin); - if (showprogress) - stop_progress_meter(); if (count != 0 && wrerr == NO && atomicio(vwrite, ofd, bp->buf, count) != count) { wrerr = YES; @@ -1962,6 +1791,8 @@ bad: run_err("%s: %s", np, strerror(errno)); wrerrno = errno; } (void) response(); + if (showprogress) + stop_progress_meter(); if (setimes && wrerr == NO) { setimes = 0; if (utimes(np, tv) < 0) { @@ -2048,7 +1879,7 @@ run_err(const char *fmt,...) if (!iamremote) { va_start(ap, fmt); - vfprintf(stderr, fmt, ap); + vfmprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } @@ -2094,7 +1925,7 @@ okname(char *cp0) } while (*++cp); return (1); -bad: fprintf(stderr, "%s: invalid user name\n", cp0); +bad: fmprintf(stderr, "%s: invalid user name\n", cp0); return (0); } diff --git a/utf8.c b/utf8.c new file mode 100644 index 0000000..f1b2fc0 --- /dev/null +++ b/utf8.c @@ -0,0 +1,302 @@ +/* $OpenBSD: utf8.c,v 1.3 2016/05/30 12:57:21 schwarze Exp $ */ +/* + * Copyright (c) 2016 Ingo Schwarze + * + * 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 +#ifdef HAVE_LANGINFO_H +# include +#endif +#include +#include +#include +#include +#include +#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) +# include +#endif +#ifdef HAVE_WCHAR_H +# include +#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. + */ + +static int +dangerous_locale(void) { + +#ifndef WINDOWS + char *loc; + +#ifdef HAVE_LANGINFO_H + loc = nl_langinfo(CODESET); +#endif + + return strcmp(loc, "US-ASCII") && strcmp(loc, "UTF-8"); +#else + wchar_t loc[LOCALE_NAME_MAX_LENGTH]; + + GetSystemDefaultLocaleName(loc, LOCALE_NAME_MAX_LENGTH); + + return wcscmp(loc, L"US-ASCII") && wcscmp(loc, L"UTF-8"); +#endif +} + +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 = realloc(*dst, tsz)) == 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); + 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; +} diff --git a/utf8.h b/utf8.h new file mode 100644 index 0000000..43ce1d5 --- /dev/null +++ b/utf8.h @@ -0,0 +1,24 @@ +/* $OpenBSD: utf8.h,v 1.1 2016/05/25 23:48:45 schwarze Exp $ */ +/* + * Copyright (c) 2016 Ingo Schwarze + * + * 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))); From fb904806c1225a09dcbe712c3abb83e274187d04 Mon Sep 17 00:00:00 2001 From: Ray Hayes Date: Thu, 27 Oct 2016 13:07:31 -0700 Subject: [PATCH 2/2] SCP issues. --- contrib/win32/win32compat/w32fd.c | 6 ++---- scp.c | 7 ++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/contrib/win32/win32compat/w32fd.c b/contrib/win32/win32compat/w32fd.c index 8f0022f..ec14c93 100644 --- a/contrib/win32/win32compat/w32fd.c +++ b/contrib/win32/win32compat/w32fd.c @@ -854,12 +854,10 @@ int w32_ftruncate(int fd, off_t length) { CHECK_FD(fd); + if (!SetFilePointer(w32_fd_to_handle(fd), length, 0, FILE_BEGIN)) + return -1; if (!SetEndOfFile(w32_fd_to_handle(fd))) return -1; - if (!SetFileValidData(w32_fd_to_handle(fd), length)) - return -1; - if (!SetFilePointer(w32_fd_to_handle(fd), 0, 0, FILE_BEGIN)) - return -1; return 0; } \ No newline at end of file diff --git a/scp.c b/scp.c index fb19dcd..82e4b99 100644 --- a/scp.c +++ b/scp.c @@ -919,6 +919,11 @@ main(int argc, char **argv) extern int optind; #ifdef WINDOWS + /* + * Initialize I/O wrappers. + */ + + w32posix_initialize(); ConInit(STD_OUTPUT_HANDLE, TRUE); #endif @@ -1079,7 +1084,7 @@ main(int argc, char **argv) exit(errs != 0); } if (tflag) { - /* Receive data. */ + /* Receive data. */ sink(argc, argv); exit(errs != 0); }