Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix for Issue648 #136

Merged
merged 8 commits into from
May 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions contrib/win32/openssh/OpenSSHBuildHelper.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ function Package-OpenSSH

if ($DestinationPath -ne "") {
if (Test-Path $DestinationPath) {
Remove-Item $DestinationPath\* -Force -Recurse
Remove-Item $DestinationPath\* -Force -Recurse -ErrorAction SilentlyContinue
}
else {
New-Item -ItemType Directory $DestinationPath -Force | Out-Null
Expand Down Expand Up @@ -542,18 +542,22 @@ function UnInstall-OpenSSH
[string]$OpenSSHDir = "$env:SystemDrive\OpenSSH"
)

if (-not (Test-Path $OpenSSHDir))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be independent of stopping the ssh-agent.. can you have this check down?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this check is needed. if the folder does not exists, there is no need to do any of the below steps. The purpose of stopping ssh-agent is to run uninstall script. If the folder does not exists, there is no need to stop ssh-agent either.

{
return
}

Push-Location $OpenSSHDir
if((Get-Service ssh-agent -ErrorAction Ignore) -ne $null) {
Stop-Service ssh-agent -Force
}
&( "$OpenSSHDir\uninstall-sshd.ps1")
&( "$OpenSSHDir\uninstall-sshlsa.ps1")

$machinePath = [Environment]::GetEnvironmentVariable('Path', 'MACHINE')
$newMachineEnvironmentPath = $machinePath
if ($machinePath.ToLower().Contains($OpenSSHDir.ToLower()))
{
$newMachineEnvironmentPath.Replace("$OpenSSHDir;", '')
$newMachineEnvironmentPath = $newMachineEnvironmentPath.Replace("$OpenSSHDir;", '')
$env:Path = $env:Path.Replace("$OpenSSHDir;", '')
}

Expand Down
58 changes: 45 additions & 13 deletions contrib/win32/win32compat/fileio.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <io.h>
#include <errno.h>
#include <stddef.h>
#include <direct.h>

#include "w32fd.h"
#include "inc\utf.h"
Expand Down Expand Up @@ -638,22 +639,53 @@ int
fileio_stat(const char *path, struct _stat64 *buf)
{
wchar_t* wpath = NULL;
int r = -1;

if ((wpath = utf8_to_utf16(path)) == NULL)
fatal("failed to covert input arguments");

r = _wstat64(wpath, buf);

/*
* If we doesn't have sufficient permissions then _wstat64() is returning "file not found"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to remove all this code and have _wstat64() but before making that attempt call GetFileAttributesExW() since we are just worried about the wrong error message.. we shouldn't be re-writing _wstat64() implementation again? If GetFileAttributesExW() success then call _wstat64

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, with GetFileAttributesExW, we don't need to have that permission. GetFileAttributesExW success does not mean you can call _wstat64.

* TODO - Replace the above call with GetFileAttributesEx
*/
WIN32_FILE_ATTRIBUTE_DATA attributes = { 0 };
int ret = -1, len = 0;
if ((wpath = utf8_to_utf16(path)) == NULL) {
errno = errno_from_Win32LastError();
debug3("utf8_to_utf16 failed for file:%s error:%d", path, GetLastError());
return -1;
}
memset(buf, 0, sizeof(struct _stat64));

if (GetFileAttributesExW(wpath, GetFileExInfoStandard, &attributes) == FALSE) {
errno = errno_from_Win32LastError();
debug3("GetFileAttributesExW with last error %d", GetLastError());
goto cleanup;
}

len = wcslen(wpath);

buf->st_ino = 0; /* Has no meaning in the FAT, HPFS, or NTFS file systems*/
buf->st_gid = 0; /* UNIX - specific; has no meaning on windows */
buf->st_uid = 0; /* UNIX - specific; has no meaning on windows */
buf->st_nlink = 1; /* number of hard links. Always 1 on non - NTFS file systems.*/
buf->st_mode |= file_attr_to_st_mode(wpath, attributes.dwFileAttributes);
buf->st_size = attributes.nFileSizeLow | (((off_t)attributes.nFileSizeHigh) << 32);
if (len > 1 && __ascii_iswalpha(*wpath) && (*(wpath + 1) == ':'))
buf->st_dev = buf->st_rdev = towupper(*wpath) - L'A'; /* drive num */
else
buf->st_dev = buf->st_rdev = _getdrive() - 1;
file_time_to_unix_time(&(attributes.ftLastAccessTime), &(buf->st_atime));
file_time_to_unix_time(&(attributes.ftLastWriteTime), &(buf->st_mtime));
file_time_to_unix_time(&(attributes.ftCreationTime), &(buf->st_ctime));

if (attributes.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
WIN32_FIND_DATAW findbuf = { 0 };
HANDLE handle = FindFirstFileW(wpath, &findbuf);
if (handle != INVALID_HANDLE_VALUE) {
if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
(findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
buf->st_mode |= S_IFLNK;
}
FindClose(handle);
}
}
ret = 0;
cleanup:
if (wpath)
free(wpath);
return r;
free(wpath);
return ret;
}

long
Expand Down
6 changes: 3 additions & 3 deletions contrib/win32/win32compat/inc/sys/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
#define utimes w32_utimes

int usleep(unsigned int);
int gettimeofday(struct timeval *tv, void *tz);
int nanosleep(const struct timespec *req, struct timespec *rem);
int w32_utimes(const char *filename, struct timeval *tvp);
int gettimeofday(struct timeval *, void *);
int nanosleep(const struct timespec *, struct timespec *);
int w32_utimes(const char *, struct timeval *);
89 changes: 77 additions & 12 deletions contrib/win32/win32compat/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ static char* s_programdir = NULL;
#define IO_REPARSE_TAG_SIS (0x80000007L) /* winnt ntifs */
#define REPARSE_MOUNTPOINT_HEADER_SIZE 8

/* Difference in us between UNIX Epoch and Win32 Epoch */
#define EPOCH_DELTA_US 116444736000000000ULL
#define RATE_DIFF 10000000ULL /* 1000 nsecs */

typedef struct _REPARSE_DATA_BUFFER {
ULONG ReparseTag;
USHORT ReparseDataLength;
Expand Down Expand Up @@ -174,9 +178,6 @@ nanosleep(const struct timespec *req, struct timespec *rem)
}
}

/* Difference in us between UNIX Epoch and Win32 Epoch */
#define EPOCH_DELTA_US 11644473600000000ULL

/* This routine is contributed by * Author: NoMachine <[email protected]>
* Copyright (c) 2009, 2010 NoMachine
* All rights reserved
Expand All @@ -191,17 +192,14 @@ gettimeofday(struct timeval *tv, void *tz)
unsigned long long us;

/* Fetch time since Jan 1, 1601 in 100ns increments */
GetSystemTimeAsFileTime(&timehelper.ft);

/* Convert to microseconds from 100 ns units */
us = timehelper.ns / 10;
GetSystemTimeAsFileTime(&timehelper.ft);

/* Remove the epoch difference */
us -= EPOCH_DELTA_US;
us = timehelper.ns - EPOCH_DELTA_US;

/* Stuff result into the timeval */
tv->tv_sec = (long)(us / 1000000ULL);
tv->tv_usec = (long)(us % 1000000ULL);
tv->tv_sec = (long)(us / RATE_DIFF);
tv->tv_usec = (long)(us % RATE_DIFF);

return 0;
}
Expand Down Expand Up @@ -550,16 +548,83 @@ w32_chown(const char *pathname, unsigned int owner, unsigned int group)
return -1;
}

static void
/* Convert a UNIX time into a Windows file time */
void
unix_time_to_file_time(ULONG t, LPFILETIME pft)
{
ULONGLONG ull;
ull = UInt32x32To64(t, 10000000) + 116444736000000000;
ull = UInt32x32To64(t, RATE_DIFF) + EPOCH_DELTA_US;

pft->dwLowDateTime = (DWORD)ull;
pft->dwHighDateTime = (DWORD)(ull >> 32);
}

/* Convert a Windows file time into a UNIX time_t */
void
file_time_to_unix_time(const LPFILETIME pft, time_t * winTime)
{
*winTime = ((long long)pft->dwHighDateTime << 32) + pft->dwLowDateTime;
*winTime -= EPOCH_DELTA_US;
*winTime /= RATE_DIFF; /* Nano to seconds resolution */
}

static BOOL
is_root_or_empty(wchar_t * path)
{
wchar_t * path_start;
BOOL has_drive_letter_and_colon;
int len;
if (!path)
return FALSE;
len = wcslen(path);
if((len > 1) && __ascii_iswalpha(path[0]) && path[1] == L':')
path_start = path + 2;
else
path_start = path;
/*path like c:\, /, \ are root directory*/
if ((*path_start == L'\0') || ((*path_start == L'\\' || *path_start == L'/' ) && path_start[1] == L'\0'))
return TRUE;
return FALSE;
}

static BOOL
has_executable_extension(wchar_t * path)
{
wchar_t * last_dot;
if (!path)
return FALSE;

last_dot = wcsrchr(path, L'.');
if (!last_dot)
return FALSE;
if (_wcsnicmp(last_dot, L".exe", 4) != 0 && _wcsnicmp(last_dot, L".cmd", 4) != 0 &&

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might need to retrieve the executable extension from %PATHEXT% environment variable..
http://environmentvariables.org/PathExt

If you fail to find / retrieve the environment variable then you can fall back on to the standard ones
.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC (This is not an exhaustive list)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I compared the results for other extension, the current implementation mirror the functionality of _wstat64 except it doesn't require permission to access the file. I will keep it for now until three is a need to consider other file extension.

_wcsnicmp(last_dot, L".bat", 4) != 0 && _wcsnicmp(last_dot, L".com", 4) != 0)
return FALSE;
return TRUE;
}

int
file_attr_to_st_mode(wchar_t * path, DWORD attributes)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change is to replace the usage of stat because stat require some (read) permission. When the user doesn't have the permission, stat return file_not_found. that is the reason we don't want to use stat.

{
int mode = S_IREAD;
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0 || is_root_or_empty(path))
mode |= S_IFDIR | _S_IEXEC;
else {
mode |= S_IFREG;
/* See if file appears to be an executable by checking its extension */
if (has_executable_extension(path))
mode |= _S_IEXEC;

}
if (!(attributes & FILE_ATTRIBUTE_READONLY))
mode |= S_IWRITE;

// propagate owner read/write/execute bits to group/other fields.
mode |= (mode & 0700) >> 3;
mode |= (mode & 0700) >> 6;
return mode;
}

static int
settimes(wchar_t * path, FILETIME *cretime, FILETIME *acttime, FILETIME *modtime)
{
Expand Down
3 changes: 3 additions & 0 deletions contrib/win32/win32compat/misc_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ char* w32_programdir();

void convertToBackslash(char *str);
void convertToForwardslash(char *str);

void unix_time_to_file_time(ULONG, LPFILETIME);
void file_time_unix_time(const LPFILETIME, time_t *);
9 changes: 9 additions & 0 deletions regress/pesterTests/Hostkey_fileperm.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ Describe "Tests for host keys file permission" -Tags "Scenario" {
Remove-Item -Path $filePath -Force -ErrorAction ignore
}

AfterAll {
if(Test-path $hostKeyFilePath -PathType Leaf){
Set-SecureFileACL -filepath $hostKeyFilePath
}
if(Test-path "$hostKeyFilePath.pub" -PathType Leaf){
Set-SecureFileACL -filepath "$hostKeyFilePath.pub"
}
}

It 'Host keys -- positive (Secured private key and sshd can access to public key file)' {
#setup to have current user as owner and grant it full control
Set-SecureFileACL -filepath $hostKeyFilePath
Expand Down
44 changes: 43 additions & 1 deletion regress/unittests/win32compat/file_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,49 @@ void file_simple_fileio()
ASSERT_INT_EQ(ret, 0);
close(f);
TEST_DONE();
}

void file_simple_fileio_mode()
{
char * small_write_buf = "sample payload", *c, small_read_buf[SMALL_RECV_BUF_SIZE];
int ret;
FILE* f;
struct stat st;

TEST_START("file mode");
f = fopen("tmp.txt", "w");
ASSERT_PTR_NE(f, NULL);
fclose(f);
f = fopen("tmp.txt", "r");
ASSERT_PTR_NE(f, NULL);
c = fgets(small_read_buf, sizeof(small_read_buf), f);
ASSERT_PTR_EQ(c, NULL);
fclose(f);

ret = stat("tmp.txt", &st);
ASSERT_INT_EQ(ret, 0);
ASSERT_INT_EQ(st.st_size, 0);

f = fopen("tmp.txt", "w");
ASSERT_PTR_NE(f, NULL);
ret = fputs(small_write_buf, f);
ASSERT_INT_EQ(ret, 0);
fclose(f);

ret = stat("tmp.txt", &st);
ASSERT_INT_EQ(ret, 0);
ASSERT_INT_EQ(st.st_size, strlen(small_write_buf));

f = fopen("tmp.txt", "r");
ASSERT_PTR_NE(f, NULL);
c = fgets(small_read_buf, sizeof(small_read_buf), f);
ASSERT_PTR_NE(c, NULL);
ASSERT_STRING_EQ(small_write_buf, small_read_buf);

c = fgets(small_read_buf, sizeof(small_read_buf), f);
ASSERT_PTR_EQ(c, NULL);
fclose(f);
TEST_DONE();
}

void
Expand Down Expand Up @@ -240,7 +281,8 @@ void
file_tests()
{
//console_io_test();
//file_simple_fileio();
file_simple_fileio();
file_simple_fileio_mode();
file_blocking_io_tests();
file_nonblocking_io_tests();
file_select_tests();
Expand Down