SFTP bug fixes (#53)

https://github.com/PowerShell/Win32-OpenSSH/issues/479
https://github.com/PowerShell/Win32-OpenSSH/issues/476
https://github.com/PowerShell/Win32-OpenSSH/issues/474
https://github.com/PowerShell/Win32-OpenSSH/issues/467


bug #479 - "ls c:" is not working
 sanitized_path() is modified to handle the edge case "\x:"


2.bug #476 - "cd c:" is not working
 If "c:" is passed to _fullpath() then it is returning existing path but not "c:", so if we append "\" to "c:" then it is working fine.


3.bug #474 - code cleanup MAX_PATH
 Using PATH_MAX variable instead of MAX_PATH
 In dirent.h, used PATH_MAX instead of hardcoding 256 characters
 In readdir(), changed the pdirentry to be a static variable. Before this, we are leaking the memory.


4.bug #467 - SFTP rename failed if the newpath already exists.
 The _wrename throws error if the newpath exists.
 To make it consistent with the linux behavrior,
 a) if the newpath is a file and if it exists then delete it so that _wrename will succeed.
 b) if the newpath is a directory and if it is empty then delete it so that _wrename will succeed.
This commit is contained in:
bagajjal 2017-01-14 22:12:19 -08:00 committed by Manoj Ampalam
parent e95aef2cf3
commit 039f2eca7a
11 changed files with 120 additions and 64 deletions

View File

@ -37,6 +37,7 @@
#include <errno.h>
#include <stddef.h>
#include "inc\utf.h"
#include "misc_internal.h"
/* internal read buffer size */
#define READ_BUFFER_SIZE 100*1024
@ -76,7 +77,7 @@ int
fileio_pipe(struct w32_io* pio[2]) {
HANDLE read_handle = INVALID_HANDLE_VALUE, write_handle = INVALID_HANDLE_VALUE;
struct w32_io *pio_read = NULL, *pio_write = NULL;
char pipe_name[MAX_PATH];
char pipe_name[PATH_MAX];
SECURITY_ATTRIBUTES sec_attributes;
if (pio == NULL) {
@ -86,7 +87,7 @@ fileio_pipe(struct w32_io* pio[2]) {
}
/* create name for named pipe */
if (-1 == sprintf_s(pipe_name, MAX_PATH, "\\\\.\\Pipe\\W32PosixPipe.%08x.%08x",
if (-1 == sprintf_s(pipe_name, PATH_MAX, "\\\\.\\Pipe\\W32PosixPipe.%08x.%08x",
GetCurrentProcessId(), pipe_counter++)) {
errno = EOTHER;
debug("pipe - ERROR sprintf_s %d", errno);
@ -564,7 +565,7 @@ fileio_fstat(struct w32_io* pio, struct _stat64 *buf) {
int
fileio_stat(const char *path, struct _stat64 *buf) {
wchar_t wpath[MAX_PATH];
wchar_t wpath[PATH_MAX];
wchar_t* wtmp = NULL;
if ((wtmp = utf8_to_utf16(path)) == NULL)

View File

@ -10,10 +10,11 @@
#include <direct.h>
#include <io.h>
#include <fcntl.h>
#include "..\misc_internal.h"
struct dirent {
int d_ino; /* Inode number */
char d_name[256]; /* Null-terminated filename */
char d_name[PATH_MAX]; /* Null-terminated filename */
};
typedef struct DIR_ DIR;

View File

@ -38,6 +38,7 @@
#include <NTSecPkg.h>
#include <ntstatus.h>
#include <stdio.h>
#include "..\misc_internal.h"
#define Unsigned unsigned
#define Char char
@ -264,7 +265,7 @@ LsaApLogonUser(PLSA_CLIENT_REQUEST request, SECURITY_LOGON_TYPE logonType,
UNICODE_STRING *flatName = NULL;
UCHAR *userAuth = NULL;
ULONG userAuthSize;
wchar_t homeDir[MAX_PATH];
wchar_t homeDir[PATH_MAX];
TOKEN_SOURCE tokenSource;
HANDLE token = NULL;
@ -292,9 +293,9 @@ LsaApLogonUser(PLSA_CLIENT_REQUEST request, SECURITY_LOGON_TYPE logonType,
*authority, &token, logonId,
*accountName, subStat));
NTFAIL(LsaApi.AllocateClientBuffer(request, MAX_PATH * sizeof(wchar_t), profile));
*profileSize = MAX_PATH;
NTFAIL(LsaApi.CopyToClientBuffer(request, MAX_PATH * sizeof(wchar_t),
NTFAIL(LsaApi.AllocateClientBuffer(request, PATH_MAX * sizeof(wchar_t), profile));
*profileSize = PATH_MAX;
NTFAIL(LsaApi.CopyToClientBuffer(request, PATH_MAX * sizeof(wchar_t),
*profile, homeDir));
PLSA_TOKEN_INFORMATION_V1 outTokenInfo;

View File

@ -38,6 +38,7 @@
#include <Shlwapi.h>
#include "misc_internal.h"
#include "inc\dlfcn.h"
#include "inc\dirent.h"
int usleep(unsigned int useconds)
{
@ -134,7 +135,7 @@ FARPROC dlsym(HMODULE handle, const char *symbol) {
*/
FILE*
w32_fopen_utf8(const char *path, const char *mode) {
wchar_t wpath[MAX_PATH], wmode[5];
wchar_t wpath[PATH_MAX], wmode[5];
FILE* f;
char utf8_bom[] = { 0xEF,0xBB,0xBF };
char first3_bytes[3];
@ -144,7 +145,7 @@ w32_fopen_utf8(const char *path, const char *mode) {
return NULL;
}
if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH) == 0 ||
if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, PATH_MAX) == 0 ||
MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, 5) == 0) {
errno = EFAULT;
debug("WideCharToMultiByte failed for %c - ERROR:%d", path, GetLastError());
@ -478,8 +479,7 @@ settimes(wchar_t * path, FILETIME *cretime, FILETIME *acttime, FILETIME *modtime
return -1;
}
if (SetFileTime(handle, cretime, acttime, modtime) == 0)
{
if (SetFileTime(handle, cretime, acttime, modtime) == 0) {
errno = GetLastError();
debug("w32_settimes - SetFileTime ERROR:%d", errno);
CloseHandle(handle);
@ -533,6 +533,28 @@ w32_rename(const char *old_name, const char *new_name) {
return -1;
}
/*
* To be consistent with linux rename(),
* 1) if the new_name is file, then delete it so that _wrename will succeed.
* 2) if the new_name is directory and it is empty then delete it so that _wrename will succeed.
*/
struct _stat64 st;
if (fileio_stat(sanitized_path(new_name), &st) != -1) {
if(((st.st_mode & _S_IFMT) == _S_IFREG)) {
w32_unlink(new_name);
} else {
DIR *dirp = opendir(new_name);
if (NULL != dirp) {
struct dirent *dp = readdir(dirp);
closedir(dirp);
if (dp == NULL) {
w32_rmdir(new_name);
}
}
}
}
int returnStatus = _wrename(resolvedOldPathName_utf16, resolvedNewPathName_utf16);
free(resolvedOldPathName_utf16);
free(resolvedNewPathName_utf16);
@ -585,10 +607,10 @@ w32_chdir(const char *dirname_utf8) {
char *
w32_getcwd(char *buffer, int maxlen) {
wchar_t wdirname[MAX_PATH];
wchar_t wdirname[PATH_MAX];
char* putf8 = NULL;
_wgetcwd(&wdirname[0], MAX_PATH);
_wgetcwd(&wdirname[0], PATH_MAX);
if ((putf8 = utf16_to_utf8(&wdirname[0])) == NULL)
fatal("failed to convert input arguments");
@ -655,19 +677,25 @@ convertToForwardslash(char *str) {
}
/*
* This method will resolves references to /./, /../ and extra '/' characters in the null-terminated string named by
* path to produce a canonicalized absolute pathname.
*/
* This method will resolves references to /./, /../ and extra '/' characters in the null-terminated string named by
* path to produce a canonicalized absolute pathname.
*/
char *
realpath(const char *path, char resolved[MAX_PATH]) {
char tempPath[MAX_PATH];
realpath(const char *path, char resolved[PATH_MAX]) {
char tempPath[PATH_MAX];
if ( (strlen(path) >= 2) && (path[0] == '/') && (path[2] == ':') )
if ((path[0] == '/') && path[1] && (path[2] == ':')) {
strncpy(resolved, path + 1, strlen(path)); // skip the first '/'
else
} else {
strncpy(resolved, path, strlen(path) + 1);
}
if (_fullpath(tempPath, resolved, MAX_PATH) == NULL)
if ((resolved[0]) && (resolved[1] == ':') && (resolved[2] == '\0')) { // make "x:" as "x:\\"
resolved[2] = '\\';
resolved[3] = '\0';
}
if (_fullpath(tempPath, resolved, PATH_MAX) == NULL)
return NULL;
convertToForwardslash(tempPath);
@ -677,6 +705,27 @@ realpath(const char *path, char resolved[MAX_PATH]) {
return resolved;
}
char*
sanitized_path(const char *path) {
static char newPath[PATH_MAX] = { '\0', };
if (path[0] == '/' && path[1]) {
if (path[2] == ':') {
if (path[3] == '\0') { // make "/x:" as "x:\\"
strncpy(newPath, path + 1, strlen(path) - 1);
newPath[2] = '\\';
newPath[3] = '\0';
return newPath;
} else {
return (char *)(path + 1); // skip the first "/"
}
}
}
return (char *)path;
}
// Maximum reparse buffer info size. The max user defined reparse
// data is 16KB, plus there's a header.
#define MAX_REPARSE_SIZE 17000
@ -813,7 +862,7 @@ int statvfs(const char *path, struct statvfs *buf) {
buf->f_favail = -1;
buf->f_fsid = 0;
buf->f_flag = 0;
buf->f_namemax = MAX_PATH - 1;
buf->f_namemax = PATH_MAX - 1;
free(path_utf16);
return 0;

View File

@ -1,3 +1,4 @@
#define PATH_MAX MAX_PATH
/* removes first '/' for Windows paths that are unix styled. Ex: /c:/ab.cd */
#define sanitized_path(p) (((p)[0] == '/' && (p)[1] != '\0' && (p)[2] == ':')? (p)+1 : (p))
char * sanitized_path(const char *);

View File

@ -39,6 +39,7 @@
#include "inc\pwd.h"
#include "inc\grp.h"
#include "inc\utf.h"
#include "misc_internal.h"
static struct passwd pw;
static char* pw_shellpath = NULL;
@ -87,9 +88,9 @@ get_passwd(const char *user_utf8, LPWSTR user_sid) {
char *uname_utf8 = NULL, *pw_home_utf8 = NULL;
LPBYTE user_info = NULL;
LPWSTR user_sid_local = NULL;
wchar_t reg_path[MAX_PATH], profile_home[MAX_PATH];
wchar_t reg_path[PATH_MAX], profile_home[PATH_MAX];
HKEY reg_key = 0;
int tmp_len = MAX_PATH;
int tmp_len = PATH_MAX;
PDOMAIN_CONTROLLER_INFOW pdc = NULL;
errno = 0;
@ -141,10 +142,10 @@ get_passwd(const char *user_utf8, LPWSTR user_sid) {
user_sid = user_sid_local;
}
if (swprintf(reg_path, MAX_PATH, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\%ls", user_sid) == MAX_PATH ||
if (swprintf(reg_path, PATH_MAX, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\%ls", user_sid) == PATH_MAX ||
RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_WOW64_64KEY, &reg_key) != 0 ||
RegQueryValueExW(reg_key, L"ProfileImagePath", 0, NULL, (LPBYTE)profile_home, &tmp_len) != 0)
GetWindowsDirectoryW(profile_home, MAX_PATH);
GetWindowsDirectoryW(profile_home, PATH_MAX);
if ((uname_utf8 = _strdup(user_utf8)) == NULL ||
(pw_home_utf8 = utf16_to_utf8(profile_home)) == NULL) {

View File

@ -35,6 +35,7 @@
#include <Strsafe.h>
#include <stdio.h>
#include <io.h>
#include "misc_internal.h"
#define MAX_CONSOLE_COLUMNS 9999
#define MAX_CONSOLE_ROWS 9999
@ -1068,15 +1069,15 @@ int start_with_pty(int ac, wchar_t **av) {
/*TODO - pick this up from system32*/
cmd[0] = L'\0';
if (ac)
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_PATH, L"cmd.exe"));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, PATH_MAX, L"cmd.exe"));
ac--;
av++;
if (ac)
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_PATH, L" /c"));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, PATH_MAX, L" /c"));
while (ac) {
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_PATH, L" "));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_PATH, *av));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, PATH_MAX, L" "));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, PATH_MAX, *av));
ac--;
av++;
}
@ -1182,14 +1183,14 @@ int start_withno_pty(int ac, wchar_t **av) {
/*TODO - pick this up from system32*/
cmd[0] = L'\0';
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_PATH, L"cmd.exe"));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, PATH_MAX, L"cmd.exe"));
ac -= 2;
av += 2;
if (ac)
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_PATH, L" /c"));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, PATH_MAX, L" /c"));
while (ac) {
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_PATH, L" "));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_PATH, *av));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, PATH_MAX, L" "));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, PATH_MAX, *av));
ac--;
av++;
}

View File

@ -31,6 +31,7 @@
#include "agent.h"
#include <sddl.h>
#include <UserEnv.h>
#include "..\misc_internal.h"
#define BUFSIZE 5 * 1024
static HANDLE ioc_port = NULL;
@ -179,15 +180,15 @@ agent_listen_loop() {
}
else {
/* spawn a child to take care of this*/
wchar_t path[MAX_PATH], module_path[MAX_PATH];
wchar_t path[PATH_MAX], module_path[PATH_MAX];
PROCESS_INFORMATION pi;
STARTUPINFOW si;
si.cb = sizeof(STARTUPINFOW);
memset(&si, 0, sizeof(STARTUPINFOW));
GetModuleFileNameW(NULL, module_path, MAX_PATH);
GetModuleFileNameW(NULL, module_path, PATH_MAX);
SetHandleInformation(con, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
if ((swprintf_s(path, MAX_PATH, L"%s %d", module_path, (int)(intptr_t)con) == -1 ) ||
if ((swprintf_s(path, PATH_MAX, L"%s %d", module_path, (int)(intptr_t)con) == -1 ) ||
(CreateProcessW(NULL, path, NULL, NULL, TRUE,
DETACHED_PROCESS, NULL, NULL,
&si, &pi) == FALSE)) {

View File

@ -87,15 +87,15 @@ int GetCurrentModulePath(wchar_t *path, int pathSize)
}
int load_config() {
wchar_t basePath[MAX_PATH] = { 0 };
wchar_t path[MAX_PATH] = { 0 };
wchar_t basePath[PATH_MAX] = { 0 };
wchar_t path[PATH_MAX] = { 0 };
/* TODO - account for UNICODE paths*/
if (GetCurrentModulePath(basePath, MAX_PATH) == -1)
if (GetCurrentModulePath(basePath, PATH_MAX) == -1)
return -1;
wcsncpy(path, basePath, MAX_PATH);
wcsncat(path, L"/sshd_config", MAX_PATH);
wcsncpy(path, basePath, PATH_MAX);
wcsncat(path, L"/sshd_config", PATH_MAX);
if ((config_file_name = utf16_to_utf8(path)) == NULL)
return -1;

View File

@ -33,6 +33,7 @@
#include <fcntl.h>
#include <sys/stat.h>
#include "inc\syslog.h"
#include "misc_internal.h"
#define MSGBUFSIZ 1024
static int logfd = -1;
@ -42,10 +43,11 @@ openlog(char *ident, unsigned int option, int facility) {
if (logfd != -1 || ident == NULL)
return;
wchar_t path[MAX_PATH], log_file[MAX_PATH + 12];
if (GetModuleFileNameW(NULL, path, MAX_PATH) == 0)
wchar_t path[PATH_MAX], log_file[PATH_MAX + 12];
if (GetModuleFileNameW(NULL, path, PATH_MAX) == 0)
return;
path[MAX_PATH - 1] = '\0';
path[PATH_MAX - 1] = '\0';
/* split path root and module */
{

View File

@ -27,7 +27,7 @@ DIR * opendir(const char *name)
struct _wfinddata_t c_file;
intptr_t hFile;
DIR *pdir;
wchar_t searchstr[MAX_PATH];
wchar_t searchstr[PATH_MAX];
wchar_t* wname = NULL;
int needed;
@ -37,7 +37,7 @@ DIR * opendir(const char *name)
}
// add *.* for Windows _findfirst() search pattern
swprintf_s(searchstr, MAX_PATH, L"%s\\*.*", wname);
swprintf_s(searchstr, PATH_MAX, L"%s\\*.*", wname);
free(wname);
if ((hFile = _wfindfirst(searchstr, &c_file)) == -1L)
@ -77,7 +77,7 @@ int closedir(DIR *dirp)
by a later readdir call on the same DIR stream. */
struct dirent *readdir(void *avp)
{
struct dirent *pdirentry;
static struct dirent pdirentry;
struct _wfinddata_t c_file;
DIR *dirp = (DIR *)avp;
char *tmp = NULL;
@ -93,17 +93,16 @@ struct dirent *readdir(void *avp)
if (wcscmp(c_file.name, L".") == 0 || wcscmp(c_file.name, L"..") == 0 )
continue;
if ((pdirentry = malloc(sizeof(struct dirent))) == NULL ||
(tmp = utf16_to_utf8(c_file.name)) == NULL) {
if ((tmp = utf16_to_utf8(c_file.name)) == NULL) {
errno = ENOMEM;
return NULL;
}
strncpy(pdirentry->d_name, tmp, strlen(tmp) + 1);
strncpy(pdirentry.d_name, tmp, strlen(tmp) + 1);
free(tmp);
pdirentry->d_ino = 1; // a fictious one like UNIX to say it is nonzero
return pdirentry ;
pdirentry.d_ino = 1; // a fictious one like UNIX to say it is nonzero
return &pdirentry ;
}
}
@ -123,4 +122,3 @@ char *basename(char *path)
return path; // path does not have a slash
}
// end of dirent functions in Windows