2016-12-19 23:46:28 +01:00
/*
* Author : Manoj Ampalam < manoj . ampalam @ microsoft . com >
*
2018-05-02 08:20:42 +02:00
* Author : Bryan Berns < berns @ uwalumni . com >
* Modified group detection use s4u token information
*
2016-12-19 23:46:28 +01:00
* Copyright ( c ) 2016 Microsoft Corp .
* All rights reserved
*
* Misc Unix POSIX routine implementations for Windows
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2018-05-02 08:20:42 +02:00
# define UMDF_USING_NTSTATUS
# define SECURITY_WIN32
2016-12-19 23:46:28 +01:00
# include <Windows.h>
# include <stdio.h>
2017-02-09 20:30:40 +01:00
# include <time.h>
# include <Shlwapi.h>
2017-06-13 19:33:47 +02:00
# include <conio.h>
2017-09-01 23:53:12 +02:00
# include <LM.h>
2017-10-16 22:00:40 +02:00
# include <Sddl.h>
# include <Aclapi.h>
2018-05-02 08:20:42 +02:00
# include <security.h>
# include <ntstatus.h>
2020-04-02 19:17:27 +02:00
# include <malloc.h>
2017-02-09 20:30:40 +01:00
2017-03-24 20:35:52 +01:00
# include "inc\unistd.h"
2017-01-30 08:54:24 +01:00
# include "inc\sys\stat.h"
2016-12-19 23:46:28 +01:00
# include "inc\sys\statvfs.h"
# include "inc\sys\time.h"
2016-12-22 06:17:14 +01:00
# include "misc_internal.h"
2017-01-06 00:01:36 +01:00
# include "inc\dlfcn.h"
2017-01-15 07:12:19 +01:00
# include "inc\dirent.h"
2017-01-25 23:43:55 +01:00
# include "inc\sys\types.h"
# include "inc\sys\ioctl.h"
# include "inc\fcntl.h"
2017-02-16 06:32:50 +01:00
# include "inc\utf.h"
2017-03-24 20:35:52 +01:00
# include "debug.h"
2017-06-13 19:33:47 +02:00
# include "w32fd.h"
# include "inc\string.h"
2018-04-03 07:30:15 +02:00
# include "inc\time.h"
2016-12-19 23:46:28 +01:00
2018-04-24 10:47:09 +02:00
# include <wchar.h>
2016-12-19 23:46:28 +01:00
2018-03-09 23:56:47 +01:00
static char * s_programdir = NULL ;
/* Maximum reparse buffer info size. The max user defined reparse
2017-02-09 20:30:40 +01:00
* data is 16 KB , plus there ' s a header .
*/
# define MAX_REPARSE_SIZE 17000
# define IO_REPARSE_TAG_SYMBOLIC_LINK IO_REPARSE_TAG_RESERVED_ZERO
# define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L) /* winnt ntifs */
# define IO_REPARSE_TAG_HSM (0xC0000004L) /* winnt ntifs */
# define IO_REPARSE_TAG_SIS (0x80000007L) /* winnt ntifs */
# define REPARSE_MOUNTPOINT_HEADER_SIZE 8
2017-05-18 21:15:18 +02:00
/* Difference in us between UNIX Epoch and Win32 Epoch */
2018-03-30 18:45:44 +02:00
# define EPOCH_DELTA 116444736000000000ULL /* in 100 nsecs intervals */
# define RATE_DIFF 10000000ULL /* 100 nsecs */
# define NSEC_IN_SEC 1000000000ULL // 10**9
# define USEC_IN_SEC 1000000ULL // 10**6
2017-05-18 21:15:18 +02:00
2017-02-09 20:30:40 +01:00
/* Windows CRT defines error string messages only till 43 in errno.h
* This is an extended list that defines messages for EADDRINUSE through EWOULDBLOCK
*/
char * _sys_errlist_ext [ ] = {
" Address already in use " , /* EADDRINUSE 100 */
" Address not available " , /* EADDRNOTAVAIL 101 */
" Address family not supported " , /* EAFNOSUPPORT 102 */
" Connection already in progress " , /* EALREADY 103 */
" Bad message " , /* EBADMSG 104 */
" Operation canceled " , /* ECANCELED 105 */
" Connection aborted " , /* ECONNABORTED 106 */
" Connection refused " , /* ECONNREFUSED 107 */
" Connection reset " , /* ECONNRESET 108 */
" Destination address required " , /* EDESTADDRREQ 109 */
" Host is unreachable " , /* EHOSTUNREACH 110 */
" Identifier removed " , /* EIDRM 111 */
" Operation in progress " , /* EINPROGRESS 112 */
" Socket is connected " , /* EISCONN 113 */
" Too many levels of symbolic links " , /* ELOOP 114 */
" Message too long " , /* EMSGSIZE 115 */
" Network is down " , /* ENETDOWN 116 */
" Connection aborted by network " , /* ENETRESET 117 */
" Network unreachable " , /* ENETUNREACH 118 */
" No buffer space available " , /* ENOBUFS 119 */
" No message is available on the STREAM head read queue " , /* ENODATA 120 */
" Link has been severed " , /* ENOLINK 121 */
" No message of the desired type " , /* ENOMSG 122 */
" Protocol not available " , /* ENOPROTOOPT 123 */
" No STREAM resources " , /* ENOSR 124 */
" Not a STREAM " , /* ENOSTR 125 */
" The socket is not connected " , /* ENOTCONN 126 */
2017-05-12 19:31:59 +02:00
" enotrecoverable " , /* ENOTRECOVERABLE 127 */
2017-02-09 20:30:40 +01:00
" Not a socket " , /* ENOTSOCK 128 */
" Operation not supported " , /* ENOTSUP 129 */
" Operation not supported on socket " , /* EOPNOTSUPP 130 */
" eother " , /* EOTHER 131 */
" Value too large to be stored in data type " , /* EOVERFLOW 132 */
" eownerdead " , /* EOWNERDEAD 133 */
" Protocol error " , /* EPROTO 134 */
" Protocol not supported " , /* EPROTONOSUPPORT 135 */
" Protocol wrong type for socket " , /* EPROTOTYPE 136 */
" Timer expired " , /* ETIME 137 */
" Connection timed out " , /* ETIMEDOUT 138 */
" Text file busy " , /* ETXTBSY 139 */
" Operation would block " /* EWOULDBLOCK 140 */
} ;
2018-05-11 23:45:20 +02:00
/* chroot state */
char * chroot_path = NULL ;
int chroot_path_len = 0 ;
/* UTF-16 version of the above */
wchar_t * chroot_pathw = NULL ;
2017-02-09 20:30:40 +01:00
int
usleep ( unsigned int useconds )
2016-12-19 23:46:28 +01:00
{
Sleep ( useconds / 1000 ) ;
return 1 ;
}
2018-03-30 18:45:44 +02:00
static LONGLONG
timespec_to_nsec ( const struct timespec * req )
{
LONGLONG sec = req - > tv_sec ;
return sec * NSEC_IN_SEC + req - > tv_nsec ;
}
2017-02-09 20:30:40 +01:00
int
nanosleep ( const struct timespec * req , struct timespec * rem )
{
HANDLE timer ;
LARGE_INTEGER li ;
if ( req - > tv_sec < 0 | | req - > tv_nsec < 0 | | req - > tv_nsec > 999999999 ) {
errno = EINVAL ;
return - 1 ;
}
if ( ( timer = CreateWaitableTimerW ( NULL , TRUE , NULL ) ) = = NULL ) {
errno = EFAULT ;
return - 1 ;
}
2018-03-30 18:45:44 +02:00
/* convert timespec to 100ns intervals */
li . QuadPart = - ( timespec_to_nsec ( req ) / 100 ) ;
2017-02-09 20:30:40 +01:00
if ( ! SetWaitableTimer ( timer , & li , 0 , NULL , NULL , FALSE ) ) {
CloseHandle ( timer ) ;
errno = EFAULT ;
return - 1 ;
}
/* TODO - use wait_for_any_event, since we want to wake up on interrupts*/
switch ( WaitForSingleObject ( timer , INFINITE ) ) {
case WAIT_OBJECT_0 :
CloseHandle ( timer ) ;
return 0 ;
default :
2018-03-30 18:45:44 +02:00
CloseHandle ( timer ) ;
2017-02-09 20:30:40 +01:00
errno = EFAULT ;
return - 1 ;
}
2016-12-19 23:46:28 +01:00
}
/* This routine is contributed by * Author: NoMachine <developers@nomachine.com>
2017-02-09 20:30:40 +01:00
* Copyright ( c ) 2009 , 2010 NoMachine
* All rights reserved
*/
2016-12-19 23:46:28 +01:00
int
gettimeofday ( struct timeval * tv , void * tz )
{
2017-02-09 20:30:40 +01:00
union {
FILETIME ft ;
unsigned long long ns ;
} timehelper ;
unsigned long long us ;
2016-12-19 23:46:28 +01:00
2017-02-09 20:30:40 +01:00
/* Fetch time since Jan 1, 1601 in 100ns increments */
2017-05-18 21:15:18 +02:00
GetSystemTimeAsFileTime ( & timehelper . ft ) ;
2016-12-19 23:46:28 +01:00
2018-03-30 18:45:44 +02:00
/* Remove the epoch difference & convert 100ns to us */
us = ( timehelper . ns - EPOCH_DELTA ) / 10 ;
2016-12-19 23:46:28 +01:00
2017-02-09 20:30:40 +01:00
/* Stuff result into the timeval */
2018-03-30 18:45:44 +02:00
tv - > tv_sec = ( long ) ( us / USEC_IN_SEC ) ;
tv - > tv_usec = ( long ) ( us % USEC_IN_SEC ) ;
2016-12-19 23:46:28 +01:00
2017-02-09 20:30:40 +01:00
return 0 ;
2016-12-19 23:46:28 +01:00
}
void
2017-02-09 20:30:40 +01:00
explicit_bzero ( void * b , size_t len )
{
2016-12-19 23:46:28 +01:00
SecureZeroMemory ( b , len ) ;
}
2019-05-21 22:02:53 +02:00
static DWORD last_dlerror = ERROR_SUCCESS ;
2017-02-09 20:30:40 +01:00
HMODULE
dlopen ( const char * filename , int flags )
{
2019-05-21 22:02:53 +02:00
wchar_t * wfilename = utf8_to_utf16 ( filename ) ;
if ( wfilename = = NULL ) {
last_dlerror = ERROR_INVALID_PARAMETER ;
return NULL ;
}
HMODULE module = LoadLibraryW ( wfilename ) ;
if ( module = = NULL )
last_dlerror = GetLastError ( ) ;
free ( wfilename ) ;
return module ;
2016-12-19 23:46:28 +01:00
}
2017-02-09 20:30:40 +01:00
int
dlclose ( HMODULE handle )
{
2016-12-19 23:46:28 +01:00
FreeLibrary ( handle ) ;
return 0 ;
}
2019-05-21 22:36:24 +02:00
void *
2017-02-09 20:30:40 +01:00
dlsym ( HMODULE handle , const char * symbol )
{
2019-05-21 22:02:53 +02:00
void * ptr = GetProcAddress ( handle , symbol ) ;
if ( ptr = = NULL )
last_dlerror = GetLastError ( ) ;
return ptr ;
2016-12-19 23:46:28 +01:00
}
2019-05-21 22:02:53 +02:00
char *
dlerror ( )
{
static char * message = NULL ;
if ( message ! = NULL ) {
free ( message ) ;
message = NULL ;
}
if ( last_dlerror = = ERROR_SUCCESS )
return NULL ;
wchar_t * wmessage = NULL ;
DWORD length = FormatMessageW (
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ,
NULL , last_dlerror , 0 , ( wchar_t * ) & wmessage , 0 , NULL ) ;
last_dlerror = ERROR_SUCCESS ;
if ( length = = 0 )
goto error ;
if ( wmessage [ length - 1 ] = = L ' \n ' )
wmessage [ length - 1 ] = L ' \0 ' ;
if ( length > 1 & & wmessage [ length - 2 ] = = L ' \r ' )
wmessage [ length - 2 ] = L ' \0 ' ;
message = utf16_to_utf8 ( wmessage ) ;
LocalFree ( wmessage ) ;
if ( message = = NULL )
goto error ;
return message ;
error :
return " Failed to format error message " ;
}
2016-12-19 23:46:28 +01:00
/*fopen on Windows to mimic https://linux.die.net/man/3/fopen
* only r , w , a are supported for now
*/
2017-02-09 20:30:40 +01:00
FILE *
2018-01-20 00:12:43 +01:00
w32_fopen_utf8 ( const char * input_path , const char * mode )
2017-02-09 20:30:40 +01:00
{
2018-04-02 05:41:09 +02:00
wchar_t * wmode = NULL , * wpath = NULL ;
FILE * f = NULL ;
2016-12-19 23:46:28 +01:00
char utf8_bom [ ] = { 0xEF , 0xBB , 0xBF } ;
char first3_bytes [ 3 ] ;
2017-05-27 00:40:59 +02:00
int status = 1 ;
2018-04-02 05:41:09 +02:00
errno_t r = 0 ;
2018-05-11 23:45:20 +02:00
int nonfs_dev = 0 ; /* opening a non file system device */
2016-12-19 23:46:28 +01:00
2018-04-02 05:41:09 +02:00
if ( mode = = NULL | | mode [ 1 ] ! = ' \0 ' ) {
2016-12-19 23:46:28 +01:00
errno = ENOTSUP ;
return NULL ;
}
2018-01-20 00:12:43 +01:00
if ( NULL = = input_path ) {
2017-05-27 00:40:59 +02:00
errno = EINVAL ;
debug3 ( " fopen - ERROR:%d " , errno ) ;
return NULL ;
}
/* if opening null device, point to Windows equivalent */
2019-12-12 23:30:11 +01:00
if ( strncmp ( input_path , NULL_DEVICE , sizeof ( NULL_DEVICE ) ) = = 0
| | strncmp ( input_path , NULL_DEVICE_WIN , sizeof ( NULL_DEVICE_WIN ) ) = = 0 ) {
2018-05-11 23:45:20 +02:00
nonfs_dev = 1 ;
wpath = utf8_to_utf16 ( NULL_DEVICE_WIN ) ;
}
else
wpath = resolved_path_utf16 ( input_path ) ;
2018-04-02 05:41:09 +02:00
wmode = utf8_to_utf16 ( mode ) ;
if ( wpath = = NULL | | wmode = = NULL )
goto cleanup ;
2016-12-19 23:46:28 +01:00
2017-06-13 19:33:47 +02:00
if ( ( _wfopen_s ( & f , wpath , wmode ) ! = 0 ) | | ( f = = NULL ) ) {
2018-10-04 23:16:02 +02:00
debug3 ( " Failed to open file:%S error:%d " , wpath , errno ) ;
2018-04-02 05:41:09 +02:00
goto cleanup ;
2018-05-11 23:45:20 +02:00
}
if ( chroot_pathw & & ! nonfs_dev ) {
/* ensure final path is within chroot */
HANDLE h = ( HANDLE ) _get_osfhandle ( _fileno ( f ) ) ;
2018-07-26 00:15:05 +02:00
if ( ! file_in_chroot_jail ( h ) ) {
debug3 ( " %s is not in chroot jail " , input_path ) ;
2018-05-11 23:45:20 +02:00
fclose ( f ) ;
f = NULL ;
errno = EACCES ;
goto cleanup ;
}
}
2017-06-13 19:33:47 +02:00
/* BOM adjustments for file streams*/
if ( mode [ 0 ] = = ' w ' & & fseek ( f , 0 , SEEK_SET ) ! = EBADF ) {
/* write UTF-8 BOM - should we ?*/
/*if (fwrite(utf8_bom, sizeof(utf8_bom), 1, f) != 1) {
fclose ( f ) ;
2018-04-02 05:41:09 +02:00
goto cleanup ;
2017-06-13 19:33:47 +02:00
} */
} else if ( mode [ 0 ] = = ' r ' & & fseek ( f , 0 , SEEK_SET ) ! = EBADF ) {
/* read out UTF-8 BOM if present*/
if ( fread ( first3_bytes , 3 , 1 , f ) ! = 1 | |
memcmp ( first3_bytes , utf8_bom , 3 ) ! = 0 ) {
fseek ( f , 0 , SEEK_SET ) ;
2016-12-19 23:46:28 +01:00
}
}
2018-04-02 05:41:09 +02:00
cleanup :
if ( wpath )
free ( wpath ) ;
if ( wmode )
free ( wmode ) ;
2016-12-19 23:46:28 +01:00
return f ;
}
2017-04-27 20:47:01 +02:00
/*
* fgets to support Unicode input
* each UTF - 16 char may bloat up to 4 utf - 8 chars . We cannot determine if the length of
* input unicode string until it is readed and converted to utf8 string .
* There is a risk to miss on unicode char when last unicode char read from console
2017-11-03 21:16:23 +01:00
* does not fit the remain space in str . use cautiously .
2017-04-27 20:47:01 +02:00
*/
2017-03-07 01:18:40 +01:00
char *
w32_fgets ( char * str , int n , FILE * stream ) {
2017-05-23 07:50:51 +02:00
if ( ! str | | ! n | | ! stream ) return NULL ;
2017-03-07 01:18:40 +01:00
HANDLE h = ( HANDLE ) _get_osfhandle ( _fileno ( stream ) ) ;
wchar_t * str_w = NULL ;
2017-04-27 20:47:01 +02:00
char * ret = NULL , * str_tmp = NULL , * cp = NULL ;
int actual_read = 0 ;
2017-07-20 00:28:35 +02:00
errno_t r = 0 ;
2017-03-07 01:18:40 +01:00
if ( h ! = NULL & & h ! = INVALID_HANDLE_VALUE
& & GetFileType ( h ) = = FILE_TYPE_CHAR ) {
2017-04-27 20:47:01 +02:00
/* Allocate memory for one UTF-16 char (up to 4 bytes) and a terminate char (\0) */
if ( ( str_w = malloc ( 3 * sizeof ( wchar_t ) ) ) = = NULL ) {
2017-03-07 01:18:40 +01:00
errno = ENOMEM ;
goto cleanup ;
}
/* prepare for Unicode input */
2017-04-27 20:47:01 +02:00
_setmode ( _fileno ( stream ) , O_U16TEXT ) ;
cp = str ;
/*
* each UTF - 16 char may bloat up to 4 utf - 8 chars
* read one wide chars at time from console and convert it to utf8
* stop reading until reach ' \n ' or the converted utf8 string length is n - 1
*/
do {
if ( str_tmp )
free ( str_tmp ) ;
if ( fgetws ( str_w , 2 , stream ) = = NULL )
goto cleanup ;
if ( ( str_tmp = utf16_to_utf8 ( str_w ) ) = = NULL ) {
debug3 ( " utf16_to_utf8 failed! " ) ;
errno = ENOMEM ;
goto cleanup ;
}
2019-11-01 18:57:34 +01:00
if ( ( actual_read + ( int ) strlen ( str_tmp ) ) > = n )
2017-04-27 20:47:01 +02:00
break ;
2017-07-20 00:28:35 +02:00
if ( ( r = memcpy_s ( cp , n - actual_read , str_tmp , strlen ( str_tmp ) ) ) ! = 0 ) {
debug3 ( " memcpy_s failed with error: %d. " , r ) ;
goto cleanup ;
}
2017-06-13 19:33:47 +02:00
actual_read + = ( int ) strlen ( str_tmp ) ;
2017-04-27 20:47:01 +02:00
cp + = strlen ( str_tmp ) ;
} while ( ( actual_read < n - 1 ) & & * str_tmp ! = ' \n ' ) ;
* cp = ' \0 ' ;
if ( actual_read > n - 1 ) {
2017-03-07 01:18:40 +01:00
/* shouldn't happen. but handling in case */
2017-04-27 20:47:01 +02:00
debug3 ( " actual_read %d exceeds the limit:%d " , actual_read , n - 1 ) ;
2017-03-07 01:18:40 +01:00
errno = EINVAL ;
goto cleanup ;
2017-04-27 20:47:01 +02:00
}
2017-03-07 01:18:40 +01:00
ret = str ;
}
else
ret = fgets ( str , n , stream ) ;
cleanup :
if ( str_w )
free ( str_w ) ;
if ( str_tmp )
free ( str_tmp ) ;
return ret ;
}
/* Account for differences between Unix's and Windows versions of setvbuf */
int
w32_setvbuf ( FILE * stream , char * buffer , int mode , size_t size ) {
/* BUG: setvbuf on console stream interferes with Unicode I/O */
HANDLE h = ( HANDLE ) _get_osfhandle ( _fileno ( stream ) ) ;
if ( h ! = NULL & & h ! = INVALID_HANDLE_VALUE
& & GetFileType ( h ) = = FILE_TYPE_CHAR )
return 0 ;
/* BUG: setvbuf on file stream is interfering with w32_fopen */
/* short circuit for now*/
return 0 ;
/*
* if size is 0 , set no buffering .
* Windows does not differentiate __IOLBF and _IOFBF
*/
if ( size = = 0 )
return setvbuf ( stream , NULL , _IONBF , 0 ) ;
else
return setvbuf ( stream , buffer , mode , size ) ;
}
2017-02-09 20:30:40 +01:00
int
2016-12-19 23:46:28 +01:00
daemon ( int nochdir , int noclose )
{
2017-02-09 20:30:40 +01:00
FreeConsole ( ) ;
return 0 ;
}
int
w32_ioctl ( int d , int request , . . . )
{
va_list valist ;
va_start ( valist , request ) ;
switch ( request ) {
case TIOCGWINSZ : {
struct winsize * wsize = va_arg ( valist , struct winsize * ) ;
CONSOLE_SCREEN_BUFFER_INFO c_info ;
if ( wsize = = NULL | | ! GetConsoleScreenBufferInfo ( GetStdHandle ( STD_OUTPUT_HANDLE ) , & c_info ) ) {
errno = EINVAL ;
return - 1 ;
}
2017-03-13 22:53:58 +01:00
2017-03-14 00:55:04 +01:00
wsize - > ws_col = c_info . dwSize . X ;
2017-03-13 22:53:58 +01:00
wsize - > ws_row = c_info . srWindow . Bottom - c_info . srWindow . Top + 1 ;
2017-02-09 20:30:40 +01:00
wsize - > ws_xpixel = 640 ;
wsize - > ws_ypixel = 480 ;
2017-03-13 22:53:58 +01:00
2017-02-09 20:30:40 +01:00
return 0 ;
}
default :
errno = ENOTSUP ;
return - 1 ;
}
}
2017-06-13 19:33:47 +02:00
/* p should be at least 12 bytes long*/
2016-12-19 23:46:28 +01:00
void
strmode ( mode_t mode , char * p )
{
/* print type */
switch ( mode & S_IFMT ) {
case S_IFDIR : /* directory */
* p + + = ' d ' ;
break ;
case S_IFCHR : /* character special */
* p + + = ' c ' ;
break ;
case S_IFREG : /* regular */
* p + + = ' - ' ;
break ;
2018-03-16 01:13:13 +01:00
case S_IFLNK : /* symbolic link */
* p + + = ' l ' ;
break ;
2016-12-19 23:46:28 +01:00
# ifdef S_IFSOCK
case S_IFSOCK : /* socket */
* p + + = ' s ' ;
break ;
# endif
case _S_IFIFO : /* fifo */
* p + + = ' p ' ;
break ;
default : /* unknown */
* p + + = ' ? ' ;
break ;
}
2017-02-09 20:30:40 +01:00
2017-06-29 21:48:13 +02:00
/* group, other are not applicable on the windows */
/* usr */
if ( mode & S_IREAD )
* p + + = ' r ' ;
else
* p + + = ' - ' ;
if ( mode & S_IWRITE )
* p + + = ' w ' ;
else
* p + + = ' - ' ;
if ( mode & S_IEXEC )
* p + + = ' x ' ;
else
* p + + = ' - ' ;
const char * permissions = " ****** " ;
2019-11-01 18:57:34 +01:00
for ( int i = 0 ; i < ( int ) strlen ( permissions ) ; i + + )
2017-06-14 06:25:01 +02:00
* p + + = permissions [ i ] ;
2016-12-19 23:46:28 +01:00
* p = ' \0 ' ;
}
2017-02-09 20:30:40 +01:00
int
w32_chmod ( const char * pathname , mode_t mode )
{
2018-10-04 23:16:02 +02:00
/* TODO -
* _wchmod ( ) doesn ' t behave like unix " chmod " command .
* _wchmod ( ) only toggles the read - only bit and it doesn ' t touch ACL .
*/
2017-01-06 19:03:06 +01:00
int ret ;
2018-04-02 05:41:09 +02:00
wchar_t * resolvedPathName_utf16 = resolved_path_utf16 ( pathname ) ;
2018-05-11 23:45:20 +02:00
if ( resolvedPathName_utf16 = = NULL )
2017-01-06 19:03:06 +01:00
return - 1 ;
2018-05-11 23:45:20 +02:00
2017-01-06 19:03:06 +01:00
ret = _wchmod ( resolvedPathName_utf16 , mode ) ;
free ( resolvedPathName_utf16 ) ;
return ret ;
2016-12-19 23:46:28 +01:00
}
2017-02-09 20:30:40 +01:00
int
w32_chown ( const char * pathname , unsigned int owner , unsigned int group )
{
2016-12-19 23:46:28 +01:00
/* TODO - implement this */
errno = EOPNOTSUPP ;
return - 1 ;
}
2019-05-25 08:08:06 +02:00
int
w32_fchown ( int fd , unsigned int owner , unsigned int group )
{
/* TODO - implement this */
errno = EOPNOTSUPP ;
return - 1 ;
}
2017-05-18 21:15:18 +02:00
/* Convert a UNIX time into a Windows file time */
void
2017-02-09 20:30:40 +01:00
unix_time_to_file_time ( ULONG t , LPFILETIME pft )
{
2017-01-10 04:07:51 +01:00
ULONGLONG ull ;
2018-03-30 18:45:44 +02:00
ull = UInt32x32To64 ( t , RATE_DIFF ) + EPOCH_DELTA ;
2017-01-10 04:07:51 +01:00
pft - > dwLowDateTime = ( DWORD ) ull ;
pft - > dwHighDateTime = ( DWORD ) ( ull > > 32 ) ;
}
2017-05-18 21:15:18 +02:00
/* 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 ;
2018-03-30 18:45:44 +02:00
* winTime - = EPOCH_DELTA ;
2017-05-18 21:15:18 +02:00
* winTime / = RATE_DIFF ; /* Nano to seconds resolution */
}
static BOOL
is_root_or_empty ( wchar_t * path )
{
wchar_t * path_start ;
int len ;
if ( ! path )
return FALSE ;
2017-06-14 06:25:01 +02:00
len = ( int ) wcslen ( path ) ;
2017-05-18 21:15:18 +02:00
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 & &
_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 )
{
2017-10-16 22:00:40 +02:00
int mode = S_IREAD ;
BOOL isReadOnlyFile = FALSE ;
2017-05-18 21:15:18 +02:00
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 ;
2017-10-16 22:00:40 +02:00
else
isReadOnlyFile = TRUE ;
// We don't populate the group permissions as its not applicable to windows OS.
// propagate owner read/write/execute bits to other fields.
mode | = get_others_file_permissions ( path , isReadOnlyFile ) ;
2017-05-18 21:15:18 +02:00
return mode ;
}
2017-01-10 04:07:51 +01:00
static int
2017-02-09 20:30:40 +01:00
settimes ( wchar_t * path , FILETIME * cretime , FILETIME * acttime , FILETIME * modtime )
{
2017-01-10 04:07:51 +01:00
HANDLE handle ;
handle = CreateFileW ( path , GENERIC_WRITE , FILE_SHARE_WRITE ,
NULL , OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL ) ;
if ( handle = = INVALID_HANDLE_VALUE ) {
2017-01-13 22:25:31 +01:00
/* TODO - convert Win32 error to errno */
errno = GetLastError ( ) ;
2017-03-24 20:35:52 +01:00
debug3 ( " w32_settimes - CreateFileW ERROR:%d " , errno ) ;
2017-01-10 04:07:51 +01:00
return - 1 ;
}
2017-01-15 07:12:19 +01:00
if ( SetFileTime ( handle , cretime , acttime , modtime ) = = 0 ) {
2017-01-13 22:25:31 +01:00
errno = GetLastError ( ) ;
2017-03-24 20:35:52 +01:00
debug3 ( " w32_settimes - SetFileTime ERROR:%d " , errno ) ;
2017-01-10 04:07:51 +01:00
CloseHandle ( handle ) ;
return - 1 ;
}
CloseHandle ( handle ) ;
return 0 ;
}
2016-12-19 23:46:28 +01:00
int
2017-02-09 20:30:40 +01:00
w32_utimes ( const char * filename , struct timeval * tvp )
{
2016-12-19 23:46:28 +01:00
int ret ;
2017-01-10 04:07:51 +01:00
FILETIME acttime , modtime ;
2018-04-02 05:41:09 +02:00
wchar_t * resolvedPathName_utf16 = resolved_path_utf16 ( filename ) ;
2018-05-11 23:45:20 +02:00
if ( resolvedPathName_utf16 = = NULL )
2016-12-19 23:46:28 +01:00
return - 1 ;
2018-05-11 23:45:20 +02:00
2017-01-10 04:07:51 +01:00
memset ( & acttime , 0 , sizeof ( FILETIME ) ) ;
memset ( & modtime , 0 , sizeof ( FILETIME ) ) ;
unix_time_to_file_time ( ( ULONG ) tvp [ 0 ] . tv_sec , & acttime ) ;
unix_time_to_file_time ( ( ULONG ) tvp [ 1 ] . tv_sec , & modtime ) ;
ret = settimes ( resolvedPathName_utf16 , NULL , & acttime , & modtime ) ;
2016-12-19 23:46:28 +01:00
free ( resolvedPathName_utf16 ) ;
return ret ;
}
2017-02-09 20:30:40 +01:00
int
w32_symlink ( const char * target , const char * linkpath )
{
2018-03-29 19:54:38 +02:00
return fileio_symlink ( target , linkpath ) ;
2016-12-19 23:46:28 +01:00
}
2017-02-09 20:30:40 +01:00
int
2018-04-05 18:57:41 +02:00
w32_link ( const char * oldpath , const char * newpath )
2017-02-09 20:30:40 +01:00
{
2018-04-05 18:57:41 +02:00
return fileio_link ( oldpath , newpath ) ;
2016-12-19 23:46:28 +01:00
}
int
2017-02-09 20:30:40 +01:00
w32_rename ( const char * old_name , const char * new_name )
{
2018-03-16 17:32:26 +01:00
if ( old_name = = NULL | | new_name = = NULL ) {
errno = EFAULT ;
return - 1 ;
}
2018-04-02 05:41:09 +02:00
wchar_t * resolvedOldPathName_utf16 = resolved_path_utf16 ( old_name ) ;
wchar_t * resolvedNewPathName_utf16 = resolved_path_utf16 ( new_name ) ;
2017-02-09 20:30:40 +01:00
2018-05-11 23:45:20 +02:00
if ( NULL = = resolvedOldPathName_utf16 | | NULL = = resolvedNewPathName_utf16 )
2016-12-19 23:46:28 +01:00
return - 1 ;
2018-05-11 23:45:20 +02:00
2017-01-15 07:12:19 +01:00
/*
2020-04-02 19:17:27 +02:00
* To be consistent with POSIX rename ( ) , if the new_name is directory
* and it is empty then delete it so that MoveFileEx will succeed .
2017-01-15 07:12:19 +01:00
*/
2018-10-04 23:16:02 +02:00
struct _stat64 st_new ;
struct _stat64 st_old ;
if ( ( fileio_stat ( new_name , & st_new ) ! = - 1 ) & &
2020-04-02 19:17:27 +02:00
( fileio_stat ( old_name , & st_old ) ! = - 1 ) & &
( ( st_old . st_mode & _S_IFMT ) = = _S_IFDIR ) & &
( ( st_new . st_mode & _S_IFMT ) = = _S_IFDIR ) ) {
2018-04-02 05:41:09 +02:00
DIR * dirp = opendir ( new_name ) ;
2017-01-15 07:12:19 +01:00
if ( NULL ! = dirp ) {
struct dirent * dp = readdir ( dirp ) ;
closedir ( dirp ) ;
2017-02-09 20:30:40 +01:00
if ( dp = = NULL )
2018-04-02 05:41:09 +02:00
w32_rmdir ( new_name ) ;
2017-01-15 07:12:19 +01:00
}
}
2020-04-02 19:17:27 +02:00
const int returnStatus = MoveFileExW ( resolvedOldPathName_utf16 , resolvedNewPathName_utf16 , MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED ) ;
2016-12-19 23:46:28 +01:00
free ( resolvedOldPathName_utf16 ) ;
free ( resolvedNewPathName_utf16 ) ;
2020-04-02 19:17:27 +02:00
/* Adjust errors and return codes to be consistent with rename() syscall */
if ( returnStatus = = 0 ) {
errno = errno_from_Win32LastError ( ) ;
return - 1 ;
}
return 0 ;
2016-12-19 23:46:28 +01:00
}
int
2017-02-09 20:30:40 +01:00
w32_unlink ( const char * path )
{
2018-04-02 05:41:09 +02:00
wchar_t * resolvedPathName_utf16 = resolved_path_utf16 ( path ) ;
2018-05-11 23:45:20 +02:00
if ( NULL = = resolvedPathName_utf16 )
2016-12-19 23:46:28 +01:00
return - 1 ;
int returnStatus = _wunlink ( resolvedPathName_utf16 ) ;
free ( resolvedPathName_utf16 ) ;
return returnStatus ;
}
int
2017-02-09 20:30:40 +01:00
w32_rmdir ( const char * path )
{
2018-04-02 05:41:09 +02:00
wchar_t * resolvedPathName_utf16 = resolved_path_utf16 ( path ) ;
2018-05-11 23:45:20 +02:00
if ( NULL = = resolvedPathName_utf16 )
2016-12-19 23:46:28 +01:00
return - 1 ;
int returnStatus = _wrmdir ( resolvedPathName_utf16 ) ;
free ( resolvedPathName_utf16 ) ;
return returnStatus ;
}
2017-02-09 20:30:40 +01:00
int
w32_chdir ( const char * dirname_utf8 )
{
2018-05-11 23:45:20 +02:00
wchar_t * dirname_utf16 = resolved_path_utf16 ( dirname_utf8 ) ;
if ( dirname_utf16 = = NULL )
2016-12-19 23:46:28 +01:00
return - 1 ;
int returnStatus = _wchdir ( dirname_utf16 ) ;
free ( dirname_utf16 ) ;
return returnStatus ;
}
char *
2017-02-09 20:30:40 +01:00
w32_getcwd ( char * buffer , int maxlen )
{
2017-05-23 07:50:51 +02:00
if ( ! buffer ) return NULL ;
2017-01-15 07:12:19 +01:00
wchar_t wdirname [ PATH_MAX ] ;
2016-12-19 23:46:28 +01:00
char * putf8 = NULL ;
2017-06-13 19:33:47 +02:00
if ( _wgetcwd ( wdirname , PATH_MAX ) = = NULL )
return NULL ;
if ( ( putf8 = utf16_to_utf8 ( wdirname ) ) = = NULL ) {
errno = ENOMEM ;
return NULL ;
}
2019-11-01 18:57:34 +01:00
if ( ( int ) strlen ( putf8 ) > = maxlen ) {
2017-06-13 19:33:47 +02:00
errno = ERANGE ;
free ( putf8 ) ;
return NULL ;
}
2016-12-19 23:46:28 +01:00
2018-10-04 23:16:02 +02:00
if ( strcpy_s ( buffer , maxlen , putf8 ) )
2017-07-20 00:28:35 +02:00
return NULL ;
2016-12-19 23:46:28 +01:00
free ( putf8 ) ;
2018-05-11 23:45:20 +02:00
to_lower_case ( buffer ) ;
if ( chroot_path ) {
/* ensure we are within chroot jail */
char c = buffer [ chroot_path_len ] ;
2019-11-01 18:57:34 +01:00
if ( ( int ) strlen ( buffer ) < chroot_path_len | |
2018-05-11 23:45:20 +02:00
memcmp ( chroot_path , buffer , chroot_path_len ) ! = 0 | |
( c ! = ' \0 ' & & c ! = ' \\ ' ) ) {
errno = EOTHER ;
2018-10-04 23:16:02 +02:00
error ( " cwd is not currently within chroot " ) ;
2018-05-11 23:45:20 +02:00
return NULL ;
}
/* is cwd chroot ?*/
if ( c = = ' \0 ' ) {
buffer [ 0 ] = ' \\ ' ;
buffer [ 1 ] = ' \0 ' ;
}
else {
char * tail = buffer + chroot_path_len ;
memmove_s ( buffer , maxlen , tail , strlen ( tail ) + 1 ) ;
}
2018-10-04 23:16:02 +02:00
}
2018-05-11 23:45:20 +02:00
2016-12-19 23:46:28 +01:00
return buffer ;
}
int
2017-02-09 20:30:40 +01:00
w32_mkdir ( const char * path_utf8 , unsigned short mode )
{
2017-06-13 19:33:47 +02:00
int curmask ;
2018-04-02 05:41:09 +02:00
wchar_t * path_utf16 = resolved_path_utf16 ( path_utf8 ) ;
2018-05-11 23:45:20 +02:00
if ( path_utf16 = = NULL )
2016-12-19 23:46:28 +01:00
return - 1 ;
2018-05-11 23:45:20 +02:00
2016-12-19 23:46:28 +01:00
int returnStatus = _wmkdir ( path_utf16 ) ;
2017-01-06 19:03:06 +01:00
if ( returnStatus < 0 ) {
2017-01-10 04:07:51 +01:00
free ( path_utf16 ) ;
2017-01-06 19:03:06 +01:00
return - 1 ;
}
2017-02-09 20:30:40 +01:00
2017-06-13 19:33:47 +02:00
errno_t error = _umask_s ( 0 , & curmask ) ;
if ( ! error )
_umask_s ( curmask , & curmask ) ;
2017-02-09 20:30:40 +01:00
2017-01-10 04:07:51 +01:00
returnStatus = _wchmod ( path_utf16 , mode & ~ curmask & ( _S_IREAD | _S_IWRITE ) ) ;
2016-12-19 23:46:28 +01:00
free ( path_utf16 ) ;
2017-02-09 20:30:40 +01:00
2016-12-19 23:46:28 +01:00
return returnStatus ;
}
int
2018-01-20 00:12:43 +01:00
w32_stat ( const char * input_path , struct w32_stat * buf )
2017-02-09 20:30:40 +01:00
{
2018-04-02 05:41:09 +02:00
return fileio_stat ( input_path , ( struct _stat64 * ) buf ) ;
2016-12-19 23:46:28 +01:00
}
2018-03-16 01:13:13 +01:00
int
w32_lstat ( const char * input_path , struct w32_stat * buf )
{
2018-04-02 05:41:09 +02:00
return fileio_lstat ( input_path , ( struct _stat64 * ) buf ) ;
2018-03-16 01:13:13 +01:00
}
2017-02-09 20:30:40 +01:00
/* if file is symbolic link, copy its link into "link" */
int
2018-04-05 18:57:41 +02:00
w32_readlink ( const char * path , char * link , int linklen )
2016-12-19 23:46:28 +01:00
{
2018-04-02 05:41:09 +02:00
return fileio_readlink ( path , link , linklen ) ;
2016-12-19 23:46:28 +01:00
}
2017-02-09 20:30:40 +01:00
/* convert forward slash to back slash */
2016-12-21 20:51:37 +01:00
void
2017-02-09 20:30:40 +01:00
convertToBackslash ( char * str )
{
2016-12-21 20:51:37 +01:00
while ( * str ) {
if ( * str = = ' / ' )
* str = ' \\ ' ;
str + + ;
}
}
2017-05-27 00:54:34 +02:00
void
convertToBackslashW ( wchar_t * str )
{
while ( * str ) {
if ( * str = = L ' / ' )
* str = L ' \\ ' ;
str + + ;
}
}
2017-02-09 20:30:40 +01:00
/* convert back slash to forward slash */
void
convertToForwardslash ( char * str )
{
2016-12-21 20:51:37 +01:00
while ( * str ) {
if ( * str = = ' \\ ' )
* str = ' / ' ;
str + + ;
}
}
2016-12-19 23:46:28 +01:00
/*
2017-01-15 07:12:19 +01:00
* This method will resolves references to / . / , / . . / and extra ' / ' characters in the null - terminated string named by
* path to produce a canonicalized absolute pathname .
*/
2016-12-19 23:46:28 +01:00
char *
2019-07-16 20:42:02 +02:00
realpath ( const char * inputpath , char * resolved )
2017-02-09 20:30:40 +01:00
{
2019-07-16 20:42:02 +02:00
wchar_t * temppath_utf16 = NULL ;
wchar_t * resolved_utf16 = NULL ;
2018-10-04 23:16:02 +02:00
char path [ PATH_MAX ] = { 0 , } , tempPath [ PATH_MAX ] = { 0 , } , * ret = NULL ;
int is_win_path = 1 ;
2017-05-23 07:50:51 +02:00
2018-10-04 23:16:02 +02:00
if ( ! inputpath | | ! resolved )
return NULL ;
size_t path_len = strlen ( inputpath ) ;
2018-05-11 23:45:20 +02:00
resolved [ 0 ] = ' \0 ' ;
2017-02-09 20:30:40 +01:00
2018-10-04 23:16:02 +02:00
if ( path_len > PATH_MAX ) {
errno = EINVAL ;
return NULL ;
}
if ( is_bash_test_env ( ) & & bash_to_win_path ( inputpath , path , _countof ( path ) ) )
is_win_path = 0 ;
if ( is_win_path ) {
if ( _strnicmp ( inputpath , PROGRAM_DATA , strlen ( PROGRAM_DATA ) ) = = 0 ) {
2018-11-05 21:22:20 +01:00
strcpy_s ( path , PATH_MAX , __progdata ) ;
2018-10-04 23:16:02 +02:00
strcat_s ( path , PATH_MAX , & inputpath [ strlen ( PROGRAM_DATA ) ] ) ;
} else {
memcpy_s ( path , PATH_MAX , inputpath , strlen ( inputpath ) ) ;
}
}
path_len = strlen ( path ) ;
if ( path_len > PATH_MAX ) {
2017-06-13 19:33:47 +02:00
errno = EINVAL ;
return NULL ;
}
2018-05-11 23:45:20 +02:00
/* resolve root directory to the same */
if ( path_len = = 1 & & ( path [ 0 ] = = ' / ' | | path [ 0 ] = = ' \\ ' ) ) {
resolved [ 0 ] = ' / ' ;
resolved [ 1 ] = ' \0 ' ;
2018-10-04 23:16:02 +02:00
ret = resolved ;
goto done ;
2018-05-11 23:45:20 +02:00
}
/* resolve this common case scenario to root */
/* "cd .." from within a drive root */
if ( path_len = = 6 & & ! chroot_path ) {
char * tmplate = " /x:/.. " ;
2018-06-05 06:10:46 +02:00
strcat_s ( resolved , PATH_MAX , path ) ;
2018-05-11 23:45:20 +02:00
resolved [ 1 ] = ' x ' ;
if ( strcmp ( tmplate , resolved ) = = 0 ) {
resolved [ 0 ] = ' / ' ;
resolved [ 1 ] = ' \0 ' ;
2018-10-04 23:16:02 +02:00
ret = resolved ;
goto done ;
2018-05-11 23:45:20 +02:00
}
}
if ( chroot_path ) {
resolved [ 0 ] = ' \0 ' ;
2018-06-05 06:10:46 +02:00
strcat_s ( resolved , PATH_MAX , chroot_path ) ;
2018-05-11 23:45:20 +02:00
/* if path is relative, add cwd within chroot */
if ( path [ 0 ] ! = ' / ' & & path [ 0 ] ! = ' \\ ' ) {
w32_getcwd ( resolved + chroot_path_len , PATH_MAX - chroot_path_len ) ;
2018-06-05 06:10:46 +02:00
strcat_s ( resolved , PATH_MAX , " / " ) ;
2018-05-11 23:45:20 +02:00
}
2018-10-04 23:16:02 +02:00
/* TODO - This logic will fail if the chroot_path is more than PATH_MAX/2.
* resolved variable is of PATH_MAX .
* We first copy chroot_path to resolved variable then incoming path ( which can be again chroot_path ) .
* In this case strcat_s will thrown a run time insufficient buffer exception .
*/
2018-06-05 06:10:46 +02:00
strcat_s ( resolved , PATH_MAX , path ) ;
2018-05-11 23:45:20 +02:00
}
else if ( ( path_len > = 2 ) & & ( path [ 0 ] = = ' / ' ) & & path [ 1 ] & & ( path [ 2 ] = = ' : ' ) ) {
if ( ( errno = strncpy_s ( resolved , PATH_MAX , path + 1 , path_len ) ) ! = 0 ) /* skip the first '/' */ {
debug3 ( " memcpy_s failed with error: %d. " , errno ) ;
2018-10-04 23:16:02 +02:00
goto done ;
2017-07-20 00:28:35 +02:00
}
}
2018-05-11 23:45:20 +02:00
else if ( ( errno = strncpy_s ( resolved , PATH_MAX , path , path_len + 1 ) ) ! = 0 ) {
debug3 ( " memcpy_s failed with error: %d. " , errno ) ;
2018-10-04 23:16:02 +02:00
goto done ;
2017-07-20 00:28:35 +02:00
}
2016-12-19 23:46:28 +01:00
2017-02-09 20:30:40 +01:00
if ( ( resolved [ 0 ] ) & & ( resolved [ 1 ] = = ' : ' ) & & ( resolved [ 2 ] = = ' \0 ' ) ) { /* make "x:" as "x:\\" */
2017-01-15 07:12:19 +01:00
resolved [ 2 ] = ' \\ ' ;
resolved [ 3 ] = ' \0 ' ;
}
2019-07-16 20:42:02 +02:00
/* note: _wfullpath() is required to resolve paths containing unicode characters */
if ( ( resolved_utf16 = utf8_to_utf16 ( resolved ) ) = = NULL | |
( temppath_utf16 = _wfullpath ( NULL , resolved_utf16 , 0 ) ) = = NULL | |
WideCharToMultiByte ( CP_UTF8 , 0 , temppath_utf16 , - 1 , tempPath , sizeof ( tempPath ) , NULL , NULL ) = = 0 ) {
2018-05-11 23:45:20 +02:00
errno = EINVAL ;
2018-10-04 23:16:02 +02:00
goto done ;
2018-05-11 23:45:20 +02:00
}
2017-02-09 20:30:40 +01:00
2018-05-11 23:45:20 +02:00
if ( chroot_path ) {
if ( strlen ( tempPath ) < strlen ( chroot_path ) ) {
errno = EACCES ;
2018-10-04 23:16:02 +02:00
goto done ;
2018-05-11 23:45:20 +02:00
}
if ( memcmp ( chroot_path , tempPath , strlen ( chroot_path ) ) ! = 0 ) {
errno = EACCES ;
2018-10-04 23:16:02 +02:00
goto done ;
2018-05-11 23:45:20 +02:00
}
2016-12-19 23:46:28 +01:00
2018-05-11 23:45:20 +02:00
resolved [ 0 ] = ' \0 ' ;
if ( strlen ( tempPath ) = = strlen ( chroot_path ) )
/* realpath is the same as chroot_path */
2018-06-05 06:10:46 +02:00
strcat_s ( resolved , PATH_MAX , " \\ " ) ;
2018-05-11 23:45:20 +02:00
else
2018-06-05 06:10:46 +02:00
strcat_s ( resolved , PATH_MAX , tempPath + strlen ( chroot_path ) ) ;
2018-05-11 23:45:20 +02:00
if ( resolved [ 0 ] ! = ' \\ ' ) {
errno = EACCES ;
2018-10-04 23:16:02 +02:00
goto done ;
2018-05-11 23:45:20 +02:00
}
convertToForwardslash ( resolved ) ;
2018-10-04 23:16:02 +02:00
ret = resolved ;
goto done ;
2018-05-11 23:45:20 +02:00
}
else {
convertToForwardslash ( tempPath ) ;
resolved [ 0 ] = ' / ' ; /* will be our first slash in /x:/users/test1 format */
if ( ( errno = strncpy_s ( resolved + 1 , PATH_MAX - 1 , tempPath , sizeof ( tempPath ) - 1 ) ) ! = 0 ) {
debug3 ( " memcpy_s failed with error: %d. " , errno ) ;
2018-10-04 23:16:02 +02:00
goto done ;
2018-05-11 23:45:20 +02:00
}
2018-10-04 23:16:02 +02:00
ret = resolved ;
goto done ;
}
done :
2019-07-16 20:42:02 +02:00
if ( resolved_utf16 ! = NULL )
free ( resolved_utf16 ) ;
if ( temppath_utf16 ! = NULL )
free ( temppath_utf16 ) ;
2018-10-04 23:16:02 +02:00
return ret ;
}
/* on error returns NULL and sets errno */
char *
resolved_path_utf8 ( const char * input_path )
{
wchar_t * resolved_path_w = resolved_path_utf16 ( input_path ) ;
char * resolved_path = NULL ;
if ( resolved_path_w ) {
resolved_path = utf16_to_utf8 ( resolved_path_w ) ;
free ( resolved_path_w ) ;
2017-07-20 00:28:35 +02:00
}
2018-10-04 23:16:02 +02:00
return resolved_path ;
2016-12-19 23:46:28 +01:00
}
2018-05-11 23:45:20 +02:00
/* on error returns NULL and sets errno */
2018-04-02 05:41:09 +02:00
wchar_t *
resolved_path_utf16 ( const char * input_path )
2017-02-09 20:30:40 +01:00
{
2018-05-11 23:45:20 +02:00
wchar_t * resolved_path = NULL ;
2018-10-04 23:16:02 +02:00
char real_path [ PATH_MAX ] ;
2018-01-20 00:12:43 +01:00
2018-05-11 23:45:20 +02:00
if ( ! input_path ) {
errno = EINVAL ;
2018-04-02 05:41:09 +02:00
return NULL ;
2018-05-11 23:45:20 +02:00
}
2018-10-04 23:16:02 +02:00
if ( realpath ( input_path , real_path ) = = NULL )
2018-05-11 23:45:20 +02:00
return NULL ;
2018-01-20 00:12:43 +01:00
2018-10-04 23:16:02 +02:00
if ( chroot_path ) {
char actual_path [ PATH_MAX ] = { 0 } ;
strcat_s ( actual_path , _countof ( actual_path ) , chroot_path ) ;
strcat_s ( actual_path , _countof ( actual_path ) , real_path ) ;
resolved_path = utf8_to_utf16 ( actual_path ) ;
} else {
if ( ( strlen ( real_path ) = = 1 ) & & ( real_path [ 0 ] = = ' / ' ) )
resolved_path = utf8_to_utf16 ( real_path ) ;
else
resolved_path = utf8_to_utf16 ( real_path + 1 ) ; /* account for preceding / in real_path */
2017-02-09 20:30:40 +01:00
}
2017-01-15 07:12:19 +01:00
2018-04-02 05:41:09 +02:00
return resolved_path ;
2017-01-15 07:12:19 +01:00
}
2017-02-09 20:30:40 +01:00
int
statvfs ( const char * path , struct statvfs * buf )
{
2017-01-06 00:01:36 +01:00
DWORD sectorsPerCluster ;
DWORD bytesPerSector ;
DWORD freeClusters ;
DWORD totalClusters ;
2018-04-02 05:41:09 +02:00
wchar_t * path_utf16 = resolved_path_utf16 ( path ) ;
2018-05-11 23:45:20 +02:00
if ( path_utf16 = = NULL )
return - 1 ;
if ( GetDiskFreeSpaceW ( path_utf16 , & sectorsPerCluster , & bytesPerSector ,
& freeClusters , & totalClusters ) ) {
2017-03-24 20:35:52 +01:00
debug5 ( " path : [%s] " , path ) ;
debug5 ( " sectorsPerCluster : [%lu] " , sectorsPerCluster ) ;
debug5 ( " bytesPerSector : [%lu] " , bytesPerSector ) ;
debug5 ( " bytesPerCluster : [%lu] " , sectorsPerCluster * bytesPerSector ) ;
debug5 ( " freeClusters : [%lu] " , freeClusters ) ;
debug5 ( " totalClusters : [%lu] " , totalClusters ) ;
2017-01-06 00:01:36 +01:00
buf - > f_bsize = sectorsPerCluster * bytesPerSector ;
buf - > f_frsize = sectorsPerCluster * bytesPerSector ;
buf - > f_blocks = totalClusters ;
buf - > f_bfree = freeClusters ;
buf - > f_bavail = freeClusters ;
buf - > f_files = - 1 ;
buf - > f_ffree = - 1 ;
buf - > f_favail = - 1 ;
buf - > f_fsid = 0 ;
buf - > f_flag = 0 ;
2017-01-15 07:12:19 +01:00
buf - > f_namemax = PATH_MAX - 1 ;
2017-01-06 00:01:36 +01:00
free ( path_utf16 ) ;
return 0 ;
2017-02-09 20:30:40 +01:00
} else {
2018-06-19 07:39:31 +02:00
debug5 ( " ERROR: Cannot get free space for [%s]. Error code is : %d. " , path , GetLastError ( ) ) ;
2018-05-11 23:45:20 +02:00
errno = errno_from_Win32LastError ( ) ;
2017-01-06 00:01:36 +01:00
free ( path_utf16 ) ;
return - 1 ;
}
}
2017-02-09 20:30:40 +01:00
int
fstatvfs ( int fd , struct statvfs * buf )
{
2017-01-06 00:01:36 +01:00
errno = ENOTSUP ;
return - 1 ;
}
2017-01-14 17:10:28 +01:00
char *
2017-02-09 20:30:40 +01:00
w32_strerror ( int errnum )
{
2017-01-14 17:10:28 +01:00
if ( errnum > = EADDRINUSE & & errnum < = EWOULDBLOCK )
return _sys_errlist_ext [ errnum - EADDRINUSE ] ;
2017-06-13 19:33:47 +02:00
strerror_s ( errorBuf , ERROR_MSG_MAXLEN , errnum ) ;
return errorBuf ;
2017-01-14 17:10:28 +01:00
}
2017-03-24 20:35:52 +01:00
2017-06-08 20:47:59 +02:00
char *
2017-06-29 21:48:13 +02:00
readpassphrase ( const char * prompt , char * outBuf , size_t outBufLen , int flags )
{
2017-06-08 20:47:59 +02:00
int current_index = 0 ;
2019-12-12 23:33:05 +01:00
int utf8_read = 0 ;
char utf8_char [ 4 ] ;
wchar_t ch ;
2017-06-08 20:47:59 +02:00
wchar_t * wtmp = NULL ;
if ( outBufLen = = 0 ) {
errno = EINVAL ;
return NULL ;
}
2017-03-24 20:35:52 +01:00
2019-12-12 23:33:05 +01:00
while ( _kbhit ( ) ) _getwch ( ) ;
2017-06-08 20:47:59 +02:00
wtmp = utf8_to_utf16 ( prompt ) ;
2017-03-24 20:35:52 +01:00
if ( wtmp = = NULL )
fatal ( " unable to alloc memory " ) ;
2017-06-08 20:47:59 +02:00
2017-03-24 20:35:52 +01:00
_cputws ( wtmp ) ;
free ( wtmp ) ;
2019-11-01 18:57:34 +01:00
while ( current_index < ( int ) outBufLen - 1 ) {
2019-12-12 23:33:05 +01:00
ch = _getwch ( ) ;
2017-06-08 20:47:59 +02:00
2019-12-12 23:33:05 +01:00
if ( ch = = L ' \r ' ) {
if ( _kbhit ( ) ) _getwch ( ) ; /* read linefeed if its there */
2017-03-24 20:35:52 +01:00
break ;
2019-12-12 23:33:05 +01:00
} else if ( ch = = L ' \n ' ) {
2017-03-24 20:35:52 +01:00
break ;
2019-12-12 23:33:05 +01:00
} else if ( ch = = L ' \b ' ) { /* backspace */
2017-06-08 20:47:59 +02:00
if ( current_index > 0 ) {
if ( flags & RPP_ECHO_ON )
2019-12-12 23:33:05 +01:00
wprintf_s ( L " %c \b " , ch ) ;
2017-06-08 20:47:59 +02:00
2019-12-12 23:33:05 +01:00
/* overwrite last character - remove any utf8 extended chars */
while ( current_index > 0 & & ( outBuf [ current_index - 1 ] & 0xC0 ) = = 0x80 )
current_index - - ;
/* overwrite last character - remove first utf8 byte */
if ( current_index > 0 )
current_index - - ;
2017-06-08 20:47:59 +02:00
}
2019-12-12 23:33:05 +01:00
} else if ( ch = = L ' \003 ' ) { /* exit on Ctrl+C */
2017-03-24 20:35:52 +01:00
fatal ( " " ) ;
2017-06-08 20:47:59 +02:00
} else {
if ( flags & RPP_SEVENBIT )
ch & = 0x7f ;
2019-12-12 23:33:05 +01:00
if ( iswalpha ( ch ) ) {
2017-06-08 20:47:59 +02:00
if ( flags & RPP_FORCELOWER )
2019-12-12 23:33:05 +01:00
ch = towlower ( ch ) ;
2017-06-08 20:47:59 +02:00
if ( flags & RPP_FORCEUPPER )
2019-12-12 23:33:05 +01:00
ch = towupper ( ch ) ;
2017-06-08 20:47:59 +02:00
}
2019-12-12 23:33:05 +01:00
/* convert unicode to utf8 characters */
int utf8_char_size = sizeof ( utf8_char ) ;
if ( ( utf8_read = WideCharToMultiByte ( CP_UTF8 , 0 , & ch , 1 , utf8_char , sizeof ( utf8_char ) , NULL , NULL ) ) = = 0 )
fatal ( " character conversion failed " ) ;
/* append to output buffer if the characters fit */
if ( current_index + utf8_read > = outBufLen - 1 ) break ;
memcpy ( & outBuf [ current_index ] , utf8_char , utf8_read ) ;
current_index + = utf8_read ;
2017-06-08 20:47:59 +02:00
if ( flags & RPP_ECHO_ON )
2019-12-12 23:33:05 +01:00
wprintf_s ( L " %c " , ch ) ;
2017-03-24 20:35:52 +01:00
}
}
2017-06-08 20:47:59 +02:00
outBuf [ current_index ] = ' \0 ' ;
_cputs ( " \n " ) ;
2017-03-24 20:35:52 +01:00
2017-06-08 20:47:59 +02:00
return outBuf ;
}
2017-07-20 00:28:35 +02:00
2017-09-01 23:53:12 +02:00
void
invalid_parameter_handler ( const wchar_t * expression , const wchar_t * function , const wchar_t * file , unsigned int line , uintptr_t pReserved )
2017-07-20 00:28:35 +02:00
{
debug3 ( " Invalid parameter in function: %ls. File: %ls Line: %d. " , function , file , line ) ;
debug3 ( " Expression: %s " , expression ) ;
}
2017-09-01 23:53:12 +02:00
void
to_lower_case ( char * s )
{
for ( ; * s ; s + + )
* s = tolower ( ( u_char ) * s ) ;
}
2017-10-16 22:00:40 +02:00
2018-05-11 23:45:20 +02:00
void
to_wlower_case ( wchar_t * s )
{
for ( ; * s ; s + + )
* s = towlower ( * s ) ;
}
2017-10-16 22:00:40 +02:00
static int
get_final_mode ( int allow_mode , int deny_mode )
{
// If deny permissions are not specified then return allow permissions.
if ( ! deny_mode ) return allow_mode ;
// If allow permissions are not specified then return allow permissions (0).
if ( ! allow_mode ) return allow_mode ;
if ( deny_mode & S_IROTH )
2017-10-16 23:46:37 +02:00
allow_mode = allow_mode & ~ S_IROTH ;
2017-10-16 22:00:40 +02:00
if ( deny_mode & S_IWOTH )
2017-10-16 23:46:37 +02:00
allow_mode = allow_mode & ~ S_IWOTH ;
2017-10-16 22:00:40 +02:00
if ( deny_mode & S_IXOTH )
2017-10-16 23:46:37 +02:00
allow_mode = allow_mode & ~ S_IXOTH ;
2017-10-16 22:00:40 +02:00
return allow_mode ;
}
int
get_others_file_permissions ( wchar_t * file_name , int isReadOnlyFile )
{
PSECURITY_DESCRIPTOR pSD = NULL ;
PSID owner_sid = NULL , current_trustee_sid = NULL ;
PACL dacl = NULL ;
DWORD error_code = ERROR_SUCCESS ;
BOOL is_valid_sid = FALSE , is_valid_acl = FALSE ;
int ret = 0 , allow_mode_world = 0 , allow_mode_auth_users = 0 , deny_mode_world = 0 , deny_mode_auth_users = 0 ;
wchar_t * w_sid = NULL ;
/*Get the owner sid of the file.*/
if ( ( error_code = GetNamedSecurityInfoW ( file_name , SE_FILE_OBJECT ,
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION ,
& owner_sid , NULL , & dacl , NULL , & pSD ) ) ! = ERROR_SUCCESS ) {
debug3 ( " failed to retrieve the owner sid and dacl of file: %ls with error code: %d " , file_name , error_code ) ;
goto cleanup ;
}
2019-03-25 17:53:23 +01:00
if ( ( ( is_valid_sid = IsValidSid ( owner_sid ) ) = = FALSE ) | | dacl = = NULL | |
( ( is_valid_acl = IsValidAcl ( dacl ) ) = = FALSE ) ) {
debug3 ( " IsValidSid: %d; NULL Acl: %d; IsValidAcl: %d " , is_valid_sid , dacl = = NULL , is_valid_acl ) ;
2017-10-16 22:00:40 +02:00
goto cleanup ;
}
for ( DWORD i = 0 ; i < dacl - > AceCount ; i + + ) {
PVOID current_ace = NULL ;
PACE_HEADER current_aceHeader = NULL ;
ACCESS_MASK current_access_mask = 0 ;
int mode_tmp = 0 ;
if ( ! GetAce ( dacl , i , & current_ace ) ) {
debug3 ( " GetAce() failed " ) ;
goto cleanup ;
}
current_aceHeader = ( PACE_HEADER ) current_ace ;
/* only interested in Allow ACE */
2017-10-16 23:46:37 +02:00
if ( current_aceHeader - > AceType = = ACCESS_ALLOWED_ACE_TYPE ) {
PACCESS_ALLOWED_ACE pAllowedAce = ( PACCESS_ALLOWED_ACE ) current_ace ;
current_trustee_sid = & ( pAllowedAce - > SidStart ) ;
current_access_mask = pAllowedAce - > Mask ;
} else if ( current_aceHeader - > AceType = = ACCESS_DENIED_ACE_TYPE ) {
PACCESS_DENIED_ACE pDeniedAce = ( PACCESS_DENIED_ACE ) current_ace ;
current_trustee_sid = & ( pDeniedAce - > SidStart ) ;
current_access_mask = pDeniedAce - > Mask ;
} else continue ;
2017-10-16 22:00:40 +02:00
if ( ! ( IsWellKnownSid ( current_trustee_sid , WinWorldSid ) | |
IsWellKnownSid ( current_trustee_sid , WinAuthenticatedUserSid ) ) )
continue ;
2017-10-16 23:46:37 +02:00
2017-10-16 22:00:40 +02:00
if ( ( current_access_mask & READ_PERMISSIONS ) = = READ_PERMISSIONS )
mode_tmp | = S_IROTH ;
if ( ! isReadOnlyFile & & ( ( current_access_mask & WRITE_PERMISSIONS ) = = WRITE_PERMISSIONS ) )
mode_tmp | = S_IWOTH ;
if ( ( current_access_mask & EXECUTE_PERMISSIONS ) = = EXECUTE_PERMISSIONS )
mode_tmp | = S_IXOTH ;
if ( IsWellKnownSid ( current_trustee_sid , WinWorldSid ) ) {
if ( current_aceHeader - > AceType = = ACCESS_ALLOWED_ACE_TYPE )
allow_mode_world | = mode_tmp ;
else
deny_mode_world | = mode_tmp ;
} else if ( IsWellKnownSid ( current_trustee_sid , WinAuthenticatedUserSid ) ) {
if ( current_aceHeader - > AceType = = ACCESS_ALLOWED_ACE_TYPE )
allow_mode_auth_users | = mode_tmp ;
else
deny_mode_auth_users | = mode_tmp ;
2017-10-16 23:46:37 +02:00
}
2017-10-16 22:00:40 +02:00
}
allow_mode_world = get_final_mode ( allow_mode_world , deny_mode_world ) ;
allow_mode_auth_users = get_final_mode ( allow_mode_auth_users , deny_mode_auth_users ) ;
ret = allow_mode_world ? allow_mode_world : allow_mode_auth_users ;
cleanup :
if ( pSD )
LocalFree ( pSD ) ;
return ret ;
}
2018-01-20 00:12:43 +01:00
/* Windows absolute paths - \abc, /abc, c:\abc, c:/abc, __PROGRAMDATA__\openssh\sshd_config */
int
2018-03-29 19:54:38 +02:00
is_absolute_path ( const char * path )
2018-01-20 00:12:43 +01:00
{
int retVal = 0 ;
2018-11-05 21:22:20 +01:00
if ( * path = = ' \" ' | | * path = = ' \' ' ) /* skip double quote if path is "c:\abc" */
2018-02-12 20:49:33 +01:00
path + + ;
2019-12-09 20:31:22 +01:00
if ( * path = = ' / ' | | * path = = ' \\ ' | | ( * path ! = ' \0 ' & & __isascii ( * path ) & & isalpha ( * path ) & & path [ 1 ] = = ' : ' ) | |
2018-01-20 00:12:43 +01:00
( ( strlen ( path ) > = strlen ( PROGRAM_DATA ) ) & & ( memcmp ( path , PROGRAM_DATA , strlen ( PROGRAM_DATA ) ) = = 0 ) ) )
retVal = 1 ;
2018-02-12 20:49:33 +01:00
2018-01-20 00:12:43 +01:00
return retVal ;
}
2018-01-23 01:55:42 +01:00
/* return -1 - in case of failure, 0 - success */
int
2018-04-02 05:41:09 +02:00
create_directory_withsddl ( wchar_t * path_w , wchar_t * sddl_w )
2018-01-23 01:55:42 +01:00
{
2018-04-02 05:41:09 +02:00
if ( GetFileAttributesW ( path_w ) = = INVALID_FILE_ATTRIBUTES ) {
2018-01-23 01:55:42 +01:00
PSECURITY_DESCRIPTOR pSD = NULL ;
SECURITY_ATTRIBUTES sa ;
memset ( & sa , 0 , sizeof ( SECURITY_ATTRIBUTES ) ) ;
sa . nLength = sizeof ( SECURITY_ATTRIBUTES ) ;
sa . bInheritHandle = FALSE ;
if ( ConvertStringSecurityDescriptorToSecurityDescriptorW ( sddl_w , SDDL_REVISION , & pSD , NULL ) = = FALSE ) {
error ( " ConvertStringSecurityDescriptorToSecurityDescriptorW failed with error code %d " , GetLastError ( ) ) ;
return - 1 ;
}
if ( IsValidSecurityDescriptor ( pSD ) = = FALSE ) {
error ( " IsValidSecurityDescriptor return FALSE " ) ;
return - 1 ;
}
sa . lpSecurityDescriptor = pSD ;
if ( ! CreateDirectoryW ( path_w , & sa ) ) {
error ( " Failed to create directory:%ls error:%d " , path_w , GetLastError ( ) ) ;
return - 1 ;
}
}
return 0 ;
}
/* return -1 - in case of failure, 0 - success */
int
copy_file ( char * source , char * destination )
{
if ( ! source | | ! destination ) return 0 ;
struct stat st ;
if ( ( stat ( source , & st ) > = 0 ) & & ( stat ( destination , & st ) < 0 ) ) {
wchar_t * source_w = utf8_to_utf16 ( source ) ;
if ( ! source_w ) {
error ( " %s utf8_to_utf16() has failed to convert string:%s " , __func__ , source_w ) ;
return - 1 ;
}
wchar_t * destination_w = utf8_to_utf16 ( destination ) ;
if ( ! destination_w ) {
error ( " %s utf8_to_utf16() has failed to convert string:%s " , __func__ , destination_w ) ;
return - 1 ;
}
if ( ! CopyFileW ( source_w , destination_w , FALSE ) ) {
error ( " Failed to copy %ls to %ls, error:%d " , source_w , destination_w , GetLastError ( ) ) ;
return - 1 ;
}
}
return 0 ;
}
2018-06-05 06:10:46 +02:00
struct tm *
2018-04-03 07:30:15 +02:00
localtime_r ( const time_t * timep , struct tm * result )
{
2018-06-05 06:10:46 +02:00
return localtime_s ( result , timep ) = = 0 ? result : NULL ;
2018-04-24 11:11:43 +02:00
}
2018-05-11 23:45:20 +02:00
2018-05-30 04:00:56 +02:00
void
freezero ( void * ptr , size_t sz )
{
if ( ptr = = NULL )
return ;
explicit_bzero ( ptr , sz ) ;
free ( ptr ) ;
}
2018-10-04 23:16:02 +02:00
int
setenv ( const char * name , const char * value , int rewrite )
{
errno_t result = 0 ;
/* If rewrite is 0, then set only if the variable name doesn't already exist in environment */
if ( ! rewrite ) {
char * envValue = NULL ;
size_t len = 0 ;
_dupenv_s ( & envValue , & len , name ) ;
if ( envValue )
return result ; /* return success (as per setenv manpage) */
}
if ( ! ( result = _putenv_s ( name , value ) ) )
return 0 ;
else {
error ( " failed to set the environment variable:%s to value:%s, error:%d " , name , value , result ) ;
errno = result ;
return - 1 ;
}
}
2018-05-11 23:45:20 +02:00
int
chroot ( const char * path )
{
2019-12-17 21:40:36 +01:00
char cwd [ PATH_MAX ] ;
2018-05-11 23:45:20 +02:00
if ( strcmp ( path , " . " ) = = 0 ) {
2019-12-17 21:40:36 +01:00
if ( w32_getcwd ( cwd , PATH_MAX ) = = NULL )
2018-05-11 23:45:20 +02:00
return - 1 ;
path = ( const char * ) cwd ;
} else if ( * ( path + 1 ) ! = ' : ' ) {
errno = ENOTSUP ;
error ( " chroot only supports absolute paths " ) ;
return - 1 ;
} else {
/* TODO - ensure path exists and is a directory */
}
if ( ( chroot_path = _strdup ( path ) ) = = NULL ) {
errno = ENOMEM ;
return - 1 ;
}
to_lower_case ( chroot_path ) ;
convertToBackslash ( chroot_path ) ;
/* strip trailing \ */
if ( chroot_path [ strlen ( chroot_path ) - 1 ] = = ' \\ ' )
chroot_path [ strlen ( chroot_path ) - 1 ] = ' \0 ' ;
2018-06-05 06:10:46 +02:00
chroot_path_len = ( int ) strlen ( chroot_path ) ;
2018-05-11 23:45:20 +02:00
if ( ( chroot_pathw = utf8_to_utf16 ( chroot_path ) ) = = NULL ) {
errno = ENOMEM ;
return - 1 ;
}
/* TODO - set the env variable just in time in a posix_spawn_chroot like API */
# define POSIX_CHROOTW L"c28fc6f98a2c44abbbd89d6a3037d0d9_POSIX_CHROOT"
_wputenv_s ( POSIX_CHROOTW , chroot_pathw ) ;
return 0 ;
}
2018-05-23 06:49:58 +02:00
2018-05-23 07:24:23 +02:00
/*
* Am I running as SYSTEM ?
* a security sensitive call - fatal exits if it cannot definitively conclude
*/
int
am_system ( )
{
HANDLE proc_token = NULL ;
DWORD info_len ;
TOKEN_USER * info = NULL ;
static int running_as_system = - 1 ;
if ( running_as_system ! = - 1 )
return running_as_system ;
if ( OpenProcessToken ( GetCurrentProcess ( ) , TOKEN_QUERY , & proc_token ) = = FALSE | |
GetTokenInformation ( proc_token , TokenUser , NULL , 0 , & info_len ) = = TRUE | |
( info = ( TOKEN_USER * ) malloc ( info_len ) ) = = NULL | |
GetTokenInformation ( proc_token , TokenUser , info , info_len , & info_len ) = = FALSE )
fatal ( " unable to know if I am running as system " ) ;
if ( IsWellKnownSid ( info - > User . Sid , WinLocalSystemSid ) )
running_as_system = 1 ;
else
running_as_system = 0 ;
CloseHandle ( proc_token ) ;
free ( info ) ;
return running_as_system ;
}
2018-07-03 19:15:07 +02:00
/*
* returns SID of user / group or current user if ( user = NULL )
* caller should free ( ) return value
*/
2018-05-23 06:49:58 +02:00
PSID
2018-07-03 19:15:07 +02:00
get_sid ( const char * name )
2018-05-23 06:49:58 +02:00
{
HANDLE token = NULL ;
TOKEN_USER * info = NULL ;
DWORD info_len = 0 ;
2018-06-05 06:10:46 +02:00
PSID ret = NULL , psid = NULL ;
2018-05-23 06:49:58 +02:00
wchar_t * name_utf16 = NULL ;
if ( name ) {
DWORD sid_len = 0 ;
SID_NAME_USE n_use ;
WCHAR dom [ DNLEN + 1 ] = L " " ;
DWORD dom_len = DNLEN + 1 ;
if ( ( name_utf16 = utf8_to_utf16 ( name ) ) = = NULL )
goto cleanup ;
LookupAccountNameW ( NULL , name_utf16 , NULL , & sid_len , dom , & dom_len , & n_use ) ;
if ( sid_len = = 0 ) {
errno = errno_from_Win32LastError ( ) ;
goto cleanup ;
}
if ( ( psid = malloc ( sid_len ) ) = = NULL ) {
errno = ENOMEM ;
goto cleanup ;
}
if ( ! LookupAccountNameW ( NULL , name_utf16 , psid , & sid_len , dom , & dom_len , & n_use ) ) {
errno = errno_from_Win32LastError ( ) ;
goto cleanup ;
}
}
else {
if ( OpenProcessToken ( GetCurrentProcess ( ) , TOKEN_QUERY , & token ) = = FALSE | |
GetTokenInformation ( token , TokenUser , NULL , 0 , & info_len ) = = TRUE ) {
errno = EOTHER ;
goto cleanup ;
}
if ( ( info = ( TOKEN_USER * ) malloc ( info_len ) ) = = NULL ) {
errno = ENOMEM ;
goto cleanup ;
}
if ( GetTokenInformation ( token , TokenUser , info , info_len , & info_len ) = = FALSE ) {
errno = errno_from_Win32LastError ( ) ;
goto cleanup ;
}
if ( ( psid = malloc ( GetLengthSid ( info - > User . Sid ) ) ) = = NULL ) {
errno = ENOMEM ;
goto cleanup ;
}
if ( ! CopySid ( GetLengthSid ( info - > User . Sid ) , psid , info - > User . Sid ) ) {
errno = errno_from_Win32LastError ( ) ;
goto cleanup ;
}
}
ret = psid ;
psid = NULL ;
cleanup :
if ( token )
CloseHandle ( token ) ;
if ( name_utf16 )
free ( name_utf16 ) ;
if ( psid )
free ( psid ) ;
if ( info )
free ( info ) ;
return ret ;
}
2018-11-05 21:22:20 +01:00
/* Interpret scp and sftp executables*/
char *
build_exec_command ( const char * command )
2018-06-04 21:16:13 +02:00
{
enum cmd_type { CMD_OTHER , CMD_SFTP , CMD_SCP } command_type = CMD_OTHER ;
2018-11-05 21:22:20 +01:00
char * cmd_sp = NULL ;
int len = 0 , command_len ;
const char * command_args = NULL ;
2018-06-04 21:16:13 +02:00
2018-11-05 21:22:20 +01:00
if ( ! command )
return NULL ;
2018-06-04 21:16:13 +02:00
2018-11-05 21:22:20 +01:00
command_len = ( int ) strlen ( command ) ;
/*TODO - replace numbers below with readable compile time operators*/
if ( command_len > = 13 & & _memicmp ( command , " internal-sftp " , 13 ) = = 0 ) {
command_type = CMD_SFTP ;
command_args = command + 13 ;
}
else if ( command_len > = 11 & & _memicmp ( command , " sftp-server " , 11 ) = = 0 ) {
command_type = CMD_SFTP ;
2018-06-04 21:16:13 +02:00
2018-11-05 21:22:20 +01:00
/* account for possible .exe extension */
if ( command_len > = 15 & & _memicmp ( command + 11 , " .exe " , 4 ) = = 0 )
command_args = command + 15 ;
else
command_args = command + 11 ;
}
else if ( command_len > = 3 & & _memicmp ( command , " scp " , 3 ) = = 0 ) {
command_type = CMD_SCP ;
2018-06-04 21:16:13 +02:00
2018-11-05 21:22:20 +01:00
/* account for possible .exe extension */
if ( command_len > = 7 & & _memicmp ( command + 3 , " .exe " , 4 ) = = 0 )
command_args = command + 7 ;
else
command_args = command + 3 ;
}
2018-06-04 21:16:13 +02:00
2018-11-05 21:22:20 +01:00
len = command_len + 5 ; /* account for possible .exe addition and null term */
if ( ( cmd_sp = malloc ( len ) ) = = NULL ) {
errno = ENOMEM ;
return NULL ;
}
memset ( cmd_sp , ' \0 ' , len ) ;
if ( command_type = = CMD_SCP ) {
strcpy_s ( cmd_sp , len , " scp.exe " ) ;
strcat_s ( cmd_sp , len , command_args ) ;
}
else if ( command_type = = CMD_SFTP ) {
strcpy_s ( cmd_sp , len , " sftp-server.exe " ) ;
strcat_s ( cmd_sp , len , command_args ) ;
}
else
strcpy_s ( cmd_sp , len , command ) ;
return cmd_sp ;
}
2018-06-04 21:16:13 +02:00
2019-12-12 23:33:05 +01:00
/*
* cmd is internally decoarated with a set of ' " '
* to account for any spaces within the commandline
* the double quotes and backslash is escaped if needed
* this decoration is done only when additional arguments are passed in argv
*/
char *
build_commandline_string ( const char * cmd , char * const argv [ ] , BOOLEAN prepend_module_path )
{
char * cmdline , * t , * tmp = NULL , * path = NULL , * ret = NULL ;
char * const * t1 ;
DWORD cmdline_len = 0 , path_len = 0 ;
int add_module_path = 0 ;
if ( ! cmd ) {
error ( " %s invalid argument cmd:%s " , __func__ , cmd ) ;
return NULL ;
}
if ( ! ( path = _strdup ( cmd ) ) ) {
error ( " failed to duplicate %s " , cmd ) ;
return NULL ;
}
path_len = ( DWORD ) strlen ( path ) ;
if ( is_bash_test_env ( ) ) {
memset ( path , 0 , path_len + 1 ) ;
bash_to_win_path ( cmd , path , path_len + 1 ) ;
path_len = ( DWORD ) strlen ( path ) ;
}
if ( ! is_absolute_path ( path ) & & prepend_module_path )
add_module_path = 1 ;
/* compute total cmdline len*/
if ( add_module_path )
cmdline_len + = ( DWORD ) strlen ( __progdir ) + 1 + ( DWORD ) strlen ( path ) + 1 + 2 ;
else
cmdline_len + = ( DWORD ) strlen ( path ) + 1 + 2 ;
if ( argv ) {
t1 = argv ;
while ( * t1 ) {
char * p = * t1 + + ;
for ( int i = 0 ; i < ( int ) strlen ( p ) ; i + + ) {
if ( p [ i ] = = ' \\ ' ) {
char * b = p + i ;
int additional_backslash = 0 ;
int backslash_count = 0 ;
/*
Backslashes are interpreted literally , unless they immediately
precede a double quotation mark .
*/
while ( b ! = NULL & & * b = = ' \\ ' ) {
backslash_count + + ;
b + + ;
if ( b ! = NULL & & * b = = ' \" ' ) {
additional_backslash = 1 ;
break ;
}
}
cmdline_len + = backslash_count * ( additional_backslash + 1 ) ;
i + = backslash_count - 1 ;
}
else if ( p [ i ] = = ' \" ' )
/* backslash will be added for every double quote.*/
cmdline_len + = 2 ;
else
cmdline_len + + ;
}
cmdline_len + = 1 + 2 ; /*for "around cmd arg and traling space*/
}
}
if ( ( cmdline = malloc ( cmdline_len ) ) = = NULL ) {
errno = ENOMEM ;
goto cleanup ;
}
t = cmdline ;
* t + + = ' \" ' ;
if ( add_module_path ) {
/* add current module path to start if needed */
memcpy ( t , __progdir , strlen ( __progdir ) ) ;
t + = strlen ( __progdir ) ;
* t + + = ' \\ ' ;
}
if ( path [ 0 ] ! = ' \" ' ) {
/* If path is <executable_path> <arg> then we should add double quotes after <executable_path> i.e., "<executable_path>" <arg> should be passed to CreateProcess().
* Example - If path is C : \ cygwin64 \ bin \ bash . exe / cygdrive / e / openssh - portable - latestw_all / openssh - portable / regress / scp - ssh - wrapper . sh then
* we should pass " C: \ cygwin64 \b in \b ash.exe " / cygdrive / e / openssh - portable - latestw_all / openssh - portable / regress / scp - ssh - wrapper . sh
* to the CreateProcess ( ) otherwise CreateProcess ( ) will fail with error code 2.
*/
if ( strstr ( path , " .exe " ) & & ( tmp = strstr ( strstr ( path , " .exe " ) , " " ) ) )
{
size_t tmp_pos = tmp - path ;
memcpy ( t , path , tmp_pos ) ;
t + = tmp_pos ;
* t + + = ' \" ' ;
memcpy ( t , tmp , strlen ( path ) - tmp_pos ) ;
t + = ( strlen ( path ) - tmp_pos ) ;
}
else {
memcpy ( t , path , path_len ) ;
t + = path_len ;
* t + + = ' \" ' ;
}
}
else {
/*path already contains "*/
memcpy ( t , path + 1 , path_len - 1 ) ;
t + = path_len - 1 ;
}
* t = ' \0 ' ;
t = cmdline + strlen ( cmdline ) ;
if ( argv ) {
t1 = argv ;
while ( * t1 ) {
* t + + = ' ' ;
char * p1 = * t1 + + ;
BOOL add_quotes = FALSE ;
/* leave as is if the command is surrounded by single quotes*/
if ( p1 [ 0 ] ! = ' \' ' )
for ( int i = 0 ; i < ( int ) strlen ( p1 ) ; i + + ) {
if ( p1 [ i ] = = ' ' ) {
add_quotes = TRUE ;
break ;
}
}
if ( add_quotes )
* t + + = ' \" ' ;
for ( int i = 0 ; i < ( int ) strlen ( p1 ) ; i + + ) {
if ( p1 [ i ] = = ' \\ ' ) {
char * b = p1 + i ;
int additional_backslash = 0 ;
int backslash_count = 0 ;
/*
* Backslashes are interpreted literally , unless they immediately
* precede a double quotation mark .
*/
while ( b ! = NULL & & * b = = ' \\ ' ) {
backslash_count + + ;
b + + ;
if ( b ! = NULL & & * b = = ' \" ' ) {
additional_backslash = 1 ;
break ;
}
}
i + = backslash_count - 1 ;
int escaped_backslash_count = backslash_count * ( additional_backslash + 1 ) ;
while ( escaped_backslash_count - - )
* t + + = ' \\ ' ;
}
else if ( p1 [ i ] = = ' \" ' ) {
/* Add backslash for every double quote.*/
* t + + = ' \\ ' ;
* t + + = ' \" ' ;
}
else
* t + + = p1 [ i ] ;
}
if ( add_quotes )
* t + + = ' \" ' ;
}
}
* t = ' \0 ' ;
ret = cmdline ;
cmdline = NULL ;
cleanup :
if ( path )
free ( path ) ;
if ( cmdline )
free ( cmdline ) ;
return ret ;
}
2018-06-04 21:16:13 +02:00
2018-10-04 23:16:02 +02:00
BOOL
is_bash_test_env ( )
{
char * envValue = NULL ;
size_t len = 0 ;
BOOL retVal = FALSE ;
_dupenv_s ( & envValue , & len , " SSH_TEST_ENVIRONMENT " ) ;
if ( ( NULL ! = envValue ) & & atoi ( envValue ) )
retVal = TRUE ;
if ( envValue )
free ( envValue ) ;
return retVal ;
}
int
bash_to_win_path ( const char * in , char * out , const size_t out_len )
{
int retVal = 0 ;
const size_t cygwin_path_prefix_len = strlen ( CYGWIN_PATH_PREFIX ) ;
2018-11-05 21:22:20 +01:00
memset ( out , 0 , out_len ) ;
2018-10-04 23:16:02 +02:00
if ( _strnicmp ( in , CYGWIN_PATH_PREFIX , cygwin_path_prefix_len ) = = 0 ) {
out [ 0 ] = in [ cygwin_path_prefix_len ] ;
out [ 1 ] = ' : ' ;
strcat_s ( out , out_len , & in [ cygwin_path_prefix_len + 1 ] ) ;
retVal = 1 ;
2018-11-05 21:22:20 +01:00
} else
strcpy_s ( out , out_len , in ) ;
2018-10-04 23:16:02 +02:00
return retVal ;
}
2018-10-30 22:54:13 +01:00
int
getpeereid ( int s , uid_t * euid , gid_t * egid )
{
verbose ( " %s is not supported " , __func__ ) ;
errno = ENOTSUP ;
return - 1 ;
}
int
getrrsetbyname ( const char * hostname , unsigned int rdclass ,
unsigned int rdtype , unsigned int flags ,
struct rrsetinfo * * res )
{
verbose ( " %s is not supported " , __func__ ) ;
errno = ENOTSUP ;
return - 1 ;
}
2019-05-25 08:08:06 +02:00
int
fnmatch ( const char * pattern , const char * string , int flags )
{
int r = - 1 ;
wchar_t * pw = NULL , * sw = NULL ;
if ( flags ) {
verbose ( " %s is not supported with flags " , __func__ ) ;
goto done ;
}
pw = utf8_to_utf16 ( pattern ) ;
sw = utf8_to_utf16 ( string ) ;
if ( ! pw | | ! sw )
goto done ;
convertToBackslashW ( pw ) ;
convertToBackslashW ( sw ) ;
if ( PathMatchSpecW ( sw , pw ) )
r = 0 ;
done :
if ( pw )
free ( pw ) ;
if ( sw )
free ( sw ) ;
return r ;
}
2018-10-30 22:54:13 +01:00
void
freerrset ( struct rrsetinfo * rrset )
{
verbose ( " %s is not supported " , __func__ ) ;
return ;
}
2018-11-05 21:22:20 +01:00
void
2018-10-30 22:54:13 +01:00
debug_assert_internal ( )
{
/* debug break on non-release builds */
# ifndef NDEBUG
DebugBreak ( ) ;
# endif
}
2018-11-05 21:22:20 +01:00
char
2018-10-30 22:54:13 +01:00
* crypt ( const char * key , const char * salt )
{
verbose ( " %s is not supported " , __func__ ) ;
errno = ENOTSUP ;
return NULL ;
}
2018-11-05 21:22:20 +01:00
int
2018-10-30 22:54:13 +01:00
w32_system ( const char * command )
{
int ret = - 1 ;
2018-11-05 21:22:20 +01:00
wchar_t * command_w = NULL ;
2018-10-30 22:54:13 +01:00
if ( ! command ) {
errno = ENOTSUP ;
goto cleanup ;
}
if ( ( command_w = utf8_to_utf16 ( command ) ) = = NULL )
goto cleanup ;
ret = _wsystem ( command_w ) ;
cleanup :
if ( command_w )
free ( command_w ) ;
return ret ;
2019-05-21 22:36:24 +02:00
}
2019-12-05 22:20:04 +01:00
char *
strrstr ( const char * inStr , const char * pattern )
{
char * tmp = NULL , * last = NULL ;
tmp = ( char * ) inStr ;
while ( tmp = strstr ( tmp , pattern ) )
last = tmp + + ;
return last ;
}