Files
archived-pecl-caching-wincache/wincache_utils.c
2024-06-29 02:00:00 +02:00

1436 lines
41 KiB
C

/*
+----------------------------------------------------------------------------------------------+
| Windows Cache for PHP |
+----------------------------------------------------------------------------------------------+
| Copyright (c) 2009, Microsoft Corporation. All rights reserved. |
| Copyright (c) 2024, Hao Lu. All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without modification, are |
| permitted provided that the following conditions are met: |
| - Redistributions of source code must retain the above copyright notice, this list of |
| conditions and the following disclaimer. |
| - 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. |
| - Neither the name of the Microsoft Corporation nor the names of its contributors may be |
| used to endorse or promote products derived from this software without specific prior written|
| permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 |
| COPYRIGHT HOLDER OR CONTRIBUTORS 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. |
+----------------------------------------------------------------------------------------------+
| Module: wincache_utils.c |
+----------------------------------------------------------------------------------------------+
| Author: Kanwaljeet Singla <ksingla@microsoft.com> |
| Updated: Eric Stenson <ericsten@microsoft.com> |
| Hao Lu <hao.lu@ife.uni-stuttgart.de> |
+----------------------------------------------------------------------------------------------+
*/
#include "precomp.h"
#include <Sddl.h>
#include <Aclapi.h>
#define DWORD_MAX 0xFFFFFFFF
/* Temp files are in the format of "wincache_" + suffix + ".tmp" */
#define TEMP_FILE_PREFIX_CHARS 13
/* IIS App Pool defines */
#define SC_APP_POOL_DOMAIN_NAME "IIS APPPOOL"
#define SC_APP_POOL_DOMAIN_NAME_LEN 11
/* 1st param == app pool, 2nd param == current user */
static const char g_sddlTemplate[] = "D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;%s)(A;OICI;GA;;;%s)";
static const size_t g_cchSddlTemplate = ARRAYSIZE(g_sddlTemplate);
/* IIS App Pool Prototypes */
int
GetAppPoolSid(
PCSTR szAppPoolName,
PSID * ppSid
);
int
GetUserSid(
PSID *ppSid
);
int
SetFileDacl(
PSTR pszSddl,
PSTR pszFilePath
);
/* CRC 32 functions */
static uint32_t crc32_generate(int n);
static uint32_t utils_crc32(const char * str, size_t strlen);
/* CRC table generated by crc32_generate() */
static uint32_t crc32_table[] = {
/* 0 */ 0x00000000, 0x3b83984b, 0x77073096, 0x4c84a8dd,
/* 4 */ 0xee0e612c, 0xd58df967, 0x990951ba, 0xa28ac9f1,
/* 8 */ 0x076dc419, 0x3cee5c52, 0x706af48f, 0x4be96cc4,
/* 12 */ 0xe963a535, 0xd2e03d7e, 0x9e6495a3, 0xa5e70de8,
/* 16 */ 0x0edb8832, 0x35581079, 0x79dcb8a4, 0x425f20ef,
/* 20 */ 0xe0d5e91e, 0xdb567155, 0x97d2d988, 0xac5141c3,
/* 24 */ 0x09b64c2b, 0x3235d460, 0x7eb17cbd, 0x4532e4f6,
/* 28 */ 0xe7b82d07, 0xdc3bb54c, 0x90bf1d91, 0xab3c85da,
/* 32 */ 0x1db71064, 0x2634882f, 0x6ab020f2, 0x5133b8b9,
/* 36 */ 0xf3b97148, 0xc83ae903, 0x84be41de, 0xbf3dd995,
/* 40 */ 0x1adad47d, 0x21594c36, 0x6ddde4eb, 0x565e7ca0,
/* 44 */ 0xf4d4b551, 0xcf572d1a, 0x83d385c7, 0xb8501d8c,
/* 48 */ 0x136c9856, 0x28ef001d, 0x646ba8c0, 0x5fe8308b,
/* 52 */ 0xfd62f97a, 0xc6e16131, 0x8a65c9ec, 0xb1e651a7,
/* 56 */ 0x14015c4f, 0x2f82c404, 0x63066cd9, 0x5885f492,
/* 60 */ 0xfa0f3d63, 0xc18ca528, 0x8d080df5, 0xb68b95be,
/* 64 */ 0x3b6e20c8, 0x00edb883, 0x4c69105e, 0x77ea8815,
/* 68 */ 0xd56041e4, 0xeee3d9af, 0xa2677172, 0x99e4e939,
/* 72 */ 0x3c03e4d1, 0x07807c9a, 0x4b04d447, 0x70874c0c,
/* 76 */ 0xd20d85fd, 0xe98e1db6, 0xa50ab56b, 0x9e892d20,
/* 80 */ 0x35b5a8fa, 0x0e3630b1, 0x42b2986c, 0x79310027,
/* 84 */ 0xdbbbc9d6, 0xe038519d, 0xacbcf940, 0x973f610b,
/* 88 */ 0x32d86ce3, 0x095bf4a8, 0x45df5c75, 0x7e5cc43e,
/* 92 */ 0xdcd60dcf, 0xe7559584, 0xabd13d59, 0x9052a512,
/* 96 */ 0x26d930ac, 0x1d5aa8e7, 0x51de003a, 0x6a5d9871,
/* 100 */ 0xc8d75180, 0xf354c9cb, 0xbfd06116, 0x8453f95d,
/* 104 */ 0x21b4f4b5, 0x1a376cfe, 0x56b3c423, 0x6d305c68,
/* 108 */ 0xcfba9599, 0xf4390dd2, 0xb8bda50f, 0x833e3d44,
/* 112 */ 0x2802b89e, 0x138120d5, 0x5f058808, 0x64861043,
/* 116 */ 0xc60cd9b2, 0xfd8f41f9, 0xb10be924, 0x8a88716f,
/* 120 */ 0x2f6f7c87, 0x14ece4cc, 0x58684c11, 0x63ebd45a,
/* 124 */ 0xc1611dab, 0xfae285e0, 0xb6662d3d, 0x8de5b576,
/* 128 */ 0x76dc4190, 0x4d5fd9db, 0x01db7106, 0x3a58e94d,
/* 132 */ 0x98d220bc, 0xa351b8f7, 0xefd5102a, 0xd4568861,
/* 136 */ 0x71b18589, 0x4a321dc2, 0x06b6b51f, 0x3d352d54,
/* 140 */ 0x9fbfe4a5, 0xa43c7cee, 0xe8b8d433, 0xd33b4c78,
/* 144 */ 0x7807c9a2, 0x438451e9, 0x0f00f934, 0x3483617f,
/* 148 */ 0x9609a88e, 0xad8a30c5, 0xe10e9818, 0xda8d0053,
/* 152 */ 0x7f6a0dbb, 0x44e995f0, 0x086d3d2d, 0x33eea566,
/* 156 */ 0x91646c97, 0xaae7f4dc, 0xe6635c01, 0xdde0c44a,
/* 160 */ 0x6b6b51f4, 0x50e8c9bf, 0x1c6c6162, 0x27eff929,
/* 164 */ 0x856530d8, 0xbee6a893, 0xf262004e, 0xc9e19805,
/* 168 */ 0x6c0695ed, 0x57850da6, 0x1b01a57b, 0x20823d30,
/* 172 */ 0x8208f4c1, 0xb98b6c8a, 0xf50fc457, 0xce8c5c1c,
/* 176 */ 0x65b0d9c6, 0x5e33418d, 0x12b7e950, 0x2934711b,
/* 180 */ 0x8bbeb8ea, 0xb03d20a1, 0xfcb9887c, 0xc73a1037,
/* 184 */ 0x62dd1ddf, 0x595e8594, 0x15da2d49, 0x2e59b502,
/* 188 */ 0x8cd37cf3, 0xb750e4b8, 0xfbd44c65, 0xc057d42e,
/* 192 */ 0x4db26158, 0x7631f913, 0x3ab551ce, 0x0136c985,
/* 196 */ 0xa3bc0074, 0x983f983f, 0xd4bb30e2, 0xef38a8a9,
/* 200 */ 0x4adfa541, 0x715c3d0a, 0x3dd895d7, 0x065b0d9c,
/* 204 */ 0xa4d1c46d, 0x9f525c26, 0xd3d6f4fb, 0xe8556cb0,
/* 208 */ 0x4369e96a, 0x78ea7121, 0x346ed9fc, 0x0fed41b7,
/* 212 */ 0xad678846, 0x96e4100d, 0xda60b8d0, 0xe1e3209b,
/* 216 */ 0x44042d73, 0x7f87b538, 0x33031de5, 0x088085ae,
/* 220 */ 0xaa0a4c5f, 0x9189d414, 0xdd0d7cc9, 0xe68ee482,
/* 224 */ 0x5005713c, 0x6b86e977, 0x270241aa, 0x1c81d9e1,
/* 228 */ 0xbe0b1010, 0x8588885b, 0xc90c2086, 0xf28fb8cd,
/* 232 */ 0x5768b525, 0x6ceb2d6e, 0x206f85b3, 0x1bec1df8,
/* 236 */ 0xb966d409, 0x82e54c42, 0xce61e49f, 0xf5e27cd4,
/* 240 */ 0x5edef90e, 0x655d6145, 0x29d9c998, 0x125a51d3,
/* 244 */ 0xb0d09822, 0x8b530069, 0xc7d7a8b4, 0xfc5430ff,
/* 248 */ 0x59b33d17, 0x6230a55c, 0x2eb40d81, 0x153795ca,
/* 252 */ 0xb7bd5c3b, 0x8c3ec470, 0xc0ba6cad, 0xfb39f4e6,
};
static uint32_t crc32_generate(int n)
{
int i = 0;
uint32_t crc = 0;
crc = n;
for(i = 8; i >= 0; i--)
{
if(crc & 1)
{
crc = (crc >> 1) ^ 0xEDB88320;
}
else
{
crc >>= 1;
}
}
return crc;
}
static uint32_t utils_crc32(const char * str, size_t strlen)
{
size_t index = 0;
uint32_t table_index = 0;
uint32_t crcvalue = 0xFFFFFFFF;
char chvalue = 0;
char toldiff = 'a' - 'A';
dprintdecorate("start utils_crc32");
for(index = 0; index < strlen; index++)
{
chvalue = str[index];
if(chvalue >= 'A' && chvalue <= 'Z')
{
chvalue = chvalue + toldiff;
}
table_index = (crcvalue ^ chvalue) & 0x000000FF;
crcvalue = ((crcvalue >> 8) & 0x00FFFFFF) ^ crc32_table[table_index];
}
dprintdecorate("end utils_crc32");
return ~crcvalue;
}
uint32_t utils_hashcalc(const char * str, size_t strlen)
{
return utils_crc32(str, strlen);
}
uint32_t utils_getindex(const char * filename, unsigned int numfiles)
{
uint32_t hash = 0;
size_t length = 0;
dprintdecorate("start utils_getindex");
_ASSERT(filename != NULL);
_ASSERT(numfiles != 0);
length = strlen(filename);
_ASSERT(length != 0);
if (length > MAX_PATH)
{
goto Finished;
}
hash = utils_hashcalc(filename, length);
hash = hash % numfiles;
Finished:
dprintdecorate("end utils_getindex");
return hash;
}
const char * utils_filepath(zend_file_handle * file_handle)
{
const char * pchar = NULL;
dprintverbose("start utils_filepath");
_ASSERT(file_handle != NULL);
/* Use filename if opened_path is null */
if(file_handle->opened_path != NULL)
{
pchar = ZSTR_VAL(file_handle->opened_path);
}
else if(file_handle->filename != NULL)
{
#if ZEND_MODULE_API_NO >= 20210902 /* PHP 8.1 release */
pchar = ZSTR_VAL(file_handle->filename);
#else
pchar = file_handle->filename;
#endif
}
dprintverbose("end utils_filepath");
return pchar;
}
char * utils_fullpath(const char * filename, size_t filename_len)
{
char * filepath = NULL;
unsigned int fplength = 0;
unsigned int index = 0;
dprintverbose("start utils_fullpath");
_ASSERT(filename != NULL);
/* If the filename is too big, just bail out. */
if (filename_len >= MAX_PATH || filename_len < 0)
{
goto Finished;
}
/* Get fullpath in a standardized format */
filepath = alloc_emalloc(MAX_PATH);
if(filepath == NULL)
{
goto Finished;
}
ZeroMemory(filepath, MAX_PATH);
fplength = GetFullPathName(filename, MAX_PATH, filepath, NULL);
if(!fplength)
{
/* Return the filename which was passed */
strcpy(filepath, filename);
}
for(index = 0; index < fplength; index++)
{
if(filepath[index] == '/')
{
filepath[index] = '\\';
}
}
Finished:
dprintverbose("end utils_fullpath");
return filepath;
}
int utils_cwdcexec(char * buffer, size_t length)
{
int result = NONFATAL;
unsigned int cwdlen = 0;
const char * execname = NULL;
size_t execlen = 0;
dprintverbose("start utils_cwdcxec");
_ASSERT(buffer != NULL);
_ASSERT(length > MAX_PATH);
ZeroMemory(buffer, length);
cwdlen = GetCurrentDirectory((DWORD)length, buffer);
*(buffer + cwdlen) = '|';
if(zend_is_executing())
{
execname = zend_get_executed_filename();
execlen = strlen(execname);
if((length - cwdlen - 2) < execlen)
{
result = FATAL_NEED_MORE_MEMORY;
goto Finished;
}
strncpy(buffer + cwdlen + 1, execname, execlen);
}
Finished:
if(FAILED(result))
{
dprintimportant("failure %d in utils_cwdcxec", result);
}
dprintverbose("end utils_cwdcexec");
return result;
}
int utils_filefolder(const char * filepath, size_t flength, char * pbuffer, size_t length)
{
int result = NONFATAL;
char * pbslash = NULL;
size_t folderlen = 0;
_ASSERT(filepath != NULL);
_ASSERT(pbuffer != NULL);
_ASSERT(IS_ABSOLUTE_PATH(filepath, flength));
if(length < flength)
{
result = FATAL_NEED_MORE_MEMORY;
goto Finished;
}
ZeroMemory(pbuffer, length);
/* NOTE: By the time we get here, filepath must not contain '/' as the */
/* directory separator char */
pbslash = strrchr(filepath, '\\');
if (pbslash == NULL)
{
result = FATAL_INVALID_ARGUMENT;
goto Finished;
}
/* length does not include backslash */
folderlen = pbslash - filepath;
if (folderlen > (size_t)length)
{
result = FATAL_INVALID_ARGUMENT;
goto Finished;
}
memcpy_s(pbuffer, folderlen, filepath, folderlen);
Finished:
if(FAILED(result))
{
dprintimportant("failure %d in utils_filefolder", result);
}
return result;
}
int utils_apoolpid()
{
int retval = -1;
char * buffer = NULL;
unsigned int buflen = 0;
dprintverbose("start utils_apoolpid");
if(WCG(apppoolid) != NULL)
{
dprintverbose("using apppoolid");
retval = utils_hashcalc(WCG(apppoolid), strlen(WCG(apppoolid)));
} else {
buflen = GetEnvironmentVariable("APP_POOL_ID", NULL, 0);
if(buflen == 0)
{
goto Finished;
}
buffer = (char *)alloc_pemalloc(buflen);
if(buffer == NULL)
{
goto Finished;
}
buflen = GetEnvironmentVariable("APP_POOL_ID", buffer, buflen);
if(buflen == 0)
{
goto Finished;
}
/* Keeping number between 99999 and 999999 to not confuse with regular pids */
/* 999999 - 100000 = 899999. Code dependent on assumption that apoolpid > 99999 */
/* If hashcalc returned a -ve value, make it +ve by subtracting from 0 */
retval = utils_hashcalc(buffer, buflen);
}
retval %= 899999;
retval = ((retval < 0) ? (0 - retval) : retval);
retval += 100000;
Finished:
if(buffer != NULL)
{
alloc_pefree(buffer);
buffer = NULL;
}
dprintverbose("end utils_apoolpid");
return retval;
}
unsigned int utils_ticksdiff(unsigned int present, unsigned int past)
{
unsigned int ticksdiff = 0;
_ASSERT(past != 0);
/* If present is 0, get current tickcount */
if(present == 0)
{
present = GetTickCount();
}
/* Take care of rollover while calculating difference */
if(present >= past)
{
ticksdiff = present - past;
}
else
{
ticksdiff = (DWORD_MAX - past) + present;
}
return ticksdiff;
}
/* Copy of php_resolve_path from PHP 5.3 branch for use in PHP 5.2 */
char * utils_resolve_path(const char *filename, size_t filename_length, const char *path)
{
char resolved_path[MAXPATHLEN];
char trypath[MAXPATHLEN];
const char *ptr, *end, *p;
char *actual_path;
php_stream_wrapper *wrapper;
if (!filename) {
return NULL;
}
/* Don't resolve paths which contain protocol (except of file://) */
for (p = filename; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
if ((*p == ':') && (p - filename > 1) && (p[1] == '/') && (p[2] == '/')) {
wrapper = php_stream_locate_url_wrapper(filename, &actual_path, STREAM_OPEN_FOR_INCLUDE);
if (wrapper == &php_plain_files_wrapper) {
if (tsrm_realpath(actual_path, resolved_path)) {
return alloc_estrdup(resolved_path);
}
}
return NULL;
}
/*
* if filename starts with: "." or "..\"
* --OR--
* if filename starts with: "X:" or "\\"
* --OR--
* if filename starts with: "\" (root on current drive)
* --OR--
* path is NULL or empty
* --THEN--
* this is an absolute path.
*/
if ((*filename == '.' &&
(IS_SLASH(filename[1]) ||
((filename[1] == '.') && IS_SLASH(filename[2])))) ||
(IS_ABSOLUTE_PATH(filename, filename_length) || IS_SLASH(filename[0])) ||
!path ||
!*path) {
if (tsrm_realpath(filename, resolved_path)) {
return alloc_estrdup(resolved_path);
} else {
return NULL;
}
}
ptr = path;
while (ptr && *ptr) {
/* Check for stream wrapper */
int is_stream_wrapper = 0;
for (p = ptr; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
if ((*p == ':') && (p - ptr > 1) && (p[1] == '/') && (p[2] == '/')) {
/* .:// or ..:// is not a stream wrapper */
if (p[-1] != '.' || p[-2] != '.' || p - 2 != ptr) {
p += 3;
is_stream_wrapper = 1;
}
}
end = strchr(p, DEFAULT_DIR_SEPARATOR);
if (end) {
if ((end-ptr) + 1 + filename_length + 1 >= MAXPATHLEN) {
ptr = end + 1;
continue;
}
memcpy(trypath, ptr, end-ptr);
trypath[end-ptr] = '/';
memcpy(trypath+(end-ptr)+1, filename, filename_length+1);
ptr = end+1;
} else {
size_t len = strlen(ptr);
if (len + 1 + filename_length + 1 >= MAXPATHLEN) {
break;
}
memcpy(trypath, ptr, len);
trypath[len] = '/';
memcpy(trypath+len+1, filename, filename_length+1);
ptr = NULL;
}
actual_path = trypath;
if (is_stream_wrapper) {
wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE);
if (!wrapper) {
continue;
} else if (wrapper != &php_plain_files_wrapper) {
if (wrapper->wops->url_stat) {
php_stream_statbuf ssb;
if (SUCCESS == wrapper->wops->url_stat(wrapper, trypath, 0, &ssb, NULL)) {
return alloc_estrdup(trypath);
}
}
continue;
}
}
if (tsrm_realpath(actual_path, resolved_path)) {
return alloc_estrdup(resolved_path);
}
} /* end provided path */
/* check in calling scripts' current working directory as a fall back case
*/
if (zend_is_executing()) {
const char *exec_fname = zend_get_executed_filename();
size_t exec_fname_length = strlen(exec_fname);
while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
if (exec_fname && exec_fname[0] != '[' &&
exec_fname_length > 0 &&
exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) {
memcpy(trypath, exec_fname, exec_fname_length + 1);
memcpy(trypath+exec_fname_length + 1, filename, filename_length+1);
actual_path = trypath;
/* Check for stream wrapper */
for (p = trypath; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
if ((*p == ':') && (p - trypath > 1) && (p[1] == '/') && (p[2] == '/')) {
wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE);
if (!wrapper) {
return NULL;
} else if (wrapper != &php_plain_files_wrapper) {
if (wrapper->wops->url_stat) {
php_stream_statbuf ssb;
if (SUCCESS == wrapper->wops->url_stat(wrapper, trypath, 0, &ssb, NULL)) {
return alloc_estrdup(trypath);
}
}
return NULL;
}
}
if (tsrm_realpath(actual_path, resolved_path)) {
return alloc_estrdup(resolved_path);
}
}
}
return NULL;
}
/*
* Caller must free returned string using alloc_pefree( ptr ).
*/
char * utils_build_temp_filename(char * suffix)
{
char * temp_file = NULL;
const char * temp_dir = WCG(filemapdir);
size_t filename_len;
if (!temp_dir)
{
goto Finished;
}
filename_len = strlen(temp_dir);
// TODO: Make sure trailing char is not IS_SLASH()
filename_len += strlen(suffix);
filename_len++; /* DEFAULT_SLASH separator */
filename_len += TEMP_FILE_PREFIX_CHARS;
filename_len++; /* terminating NULL */
temp_file = alloc_pemalloc(filename_len);
if (!temp_file)
{
error_setlasterror();
goto Finished;
}
_snprintf_s(temp_file, filename_len, filename_len -1, "%s\\wincache_%s.tmp", temp_dir, suffix);
Finished:
return temp_file;
}
/*
* If this succeeds, then caller must ReleaseMutex on the returned pinit_event
* handle.
*/
int utils_create_init_event(
char * prefix,
char * name,
HANDLE *pinit_event,
unsigned char *pisfirst
)
{
int result = NONFATAL;
int len = 0;
char evtname[ MAX_PATH];
DWORD ret = 0;
HANDLE hinitdone = NULL;
unsigned char isfirst = 1;
*pinit_event = NULL;
len = _snprintf_s(evtname, MAX_PATH, MAX_PATH - 1, "%s%s", prefix, name);
if (len == -1)
{
result = FATAL_ALLOC_INIT_EVENT;
goto Finished;
}
hinitdone = CreateMutex(NULL, TRUE, evtname);
if (hinitdone == NULL)
{
error_setlasterror();
dprintcritical("Failed to create event %s", evtname);
result = FATAL_ALLOC_INIT_EVENT;
goto Finished;
}
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
isfirst = 0;
/* Wait for other process to initialize completely */
ret = WaitForSingleObject(hinitdone, MAX_INIT_EVENT_WAIT);
if (ret == WAIT_TIMEOUT)
{
dprintcritical("Timed out waiting for other process to release %s", evtname);
EventWriteInitMutexErrorEvent(name, hinitdone);
result = FATAL_ALLOC_INIT_EVENT;
goto Finished;
}
if (ret == WAIT_FAILED)
{
dprintcritical("Failed waiting for other process to release %s: (%d)", evtname, error_setlasterror());
EventWriteInitMutexErrorEvent(name, hinitdone);
result = FATAL_ALLOC_INIT_EVENT;
goto Finished;
}
}
*pinit_event = hinitdone;
*pisfirst = isfirst;
Finished:
if (FAILED(result))
{
if (hinitdone)
{
CloseHandle(hinitdone);
hinitdone = NULL;
}
}
return result;
}
#if (defined(_MSC_VER) && (_MSC_VER < 1500))
int wincache_php_snprintf_s(char *buf, size_t len, size_t len2, const char *format,...)
{
va_list arglist;
int retval = 0;
va_start(arglist, format);
retval = ap_php_snprintf(buf, len, format, arglist);
va_end(arglist);
return retval;
}
#endif
/*
* IIS App Pool utils
*/
/* Caller must NOT free the returned string */
const char *
utils_get_apppool_name()
{
unsigned int buflen;
PSTR szAppPoolName = NULL;
int ret = 0;
if ( WCG(apppoolid) )
{
return WCG(apppoolid);
}
/* Get app pool ID (as unicode) */
buflen = GetEnvironmentVariableA( "APP_POOL_ID", NULL, 0 );
if (buflen == 0)
{
/* No app pool. Don't do anything */
goto Finished;
}
szAppPoolName = (PSTR)alloc_pemalloc(buflen + 1);
if (szAppPoolName == NULL)
{
ret = FATAL_OUT_OF_MEMORY;
goto Finished;
}
buflen = GetEnvironmentVariable( "APP_POOL_ID", szAppPoolName, buflen + 1 );
if (buflen == 0)
{
ret = FATAL_UNEXPECTED_DATA;
goto Finished;
}
WCG(apppoolid) = szAppPoolName;
Finished:
if ( FAILED( ret ) )
{
if ( szAppPoolName )
{
alloc_pefree( szAppPoolName );
szAppPoolName = NULL;
}
}
return szAppPoolName;
}
/* IIS App Pool Functions */
int
utils_set_apppool_acl(
char * filename
)
{
int ret = 0;
PCSTR szAppPoolName = NULL;
PSTR szNewSddlAcl = NULL;
PSID pAppPoolSid = NULL;
PSID pUserSid = NULL;
PSTR szAppPoolSid = NULL;
PSTR szUserSid = NULL;
size_t sddlLen = 0;
/* Get app pool name */
szAppPoolName = utils_get_apppool_name();
if ( !szAppPoolName )
{
/* No app pool. Don't do anything */
goto Finished;
}
/* Get app pool SID */
ret = GetAppPoolSid( szAppPoolName, &pAppPoolSid );
if ( FAILED( ret ) )
{
goto Finished;
}
/* Convert SID to String */
if ( !ConvertSidToStringSidA( pAppPoolSid, &szAppPoolSid ) )
{
error_setlasterror();
ret = FATAL_ACL_FAILED;
goto Finished;
}
/*
* Get Token for the current user.
* Free the token user with the FreeTokenUserSid API when done.
*/
ret = GetUserSid(&pUserSid);
if ( FAILED( ret ) )
{
goto Finished;
}
/* Convert SID to String */
if ( !ConvertSidToStringSidA( pUserSid, &szUserSid ) )
{
error_setlasterror();
ret = FATAL_ACL_FAILED;
goto Finished;
}
/* Construct SDDL string for file ACL */
/* ACL: SYSTEM:Full, ADMINISTRATORS:Full, AppPool:Full */
sddlLen = g_cchSddlTemplate; /* includes terminating NULL */
sddlLen += strlen( szAppPoolSid );
sddlLen += strlen( szUserSid );
szNewSddlAcl = (PSTR)alloc_pemalloc(sddlLen);
if ( !szNewSddlAcl )
{
ret = FATAL_OUT_OF_MEMORY;
goto Finished;
}
sprintf_s( szNewSddlAcl, sddlLen, g_sddlTemplate, szAppPoolSid, szUserSid );
/* set on filename */
ret = SetFileDacl( szNewSddlAcl, filename );
Finished:
if ( szNewSddlAcl )
{
alloc_pefree( szNewSddlAcl );
szNewSddlAcl = NULL;
}
if ( szAppPoolSid )
{
LocalFree( szAppPoolSid );
szAppPoolSid = NULL;
}
if ( szUserSid )
{
LocalFree( szUserSid );
szUserSid = NULL;
}
if ( pAppPoolSid )
{
alloc_pefree( pAppPoolSid );
pAppPoolSid = NULL;
}
if ( pUserSid )
{
alloc_pefree( pUserSid );
pUserSid = NULL;
}
return ret;
}
/*****************************************************************************
Routine Description:
Sets the DACL on the specified file.
Arguments:
pszSddl - SDDL representation of DACL
pszFilePath - complete file path
Returns:
NONFATAL - Success
FATAL_ACL_FAILED - Could not set the ACL on the file.
*****************************************************************************/
int
SetFileDacl(
PSTR pszSddl,
PSTR pszFilePath
)
{
int ret = NONFATAL;
SECURITY_ATTRIBUTES secAttr;
BOOL fDaclPresent = FALSE;
BOOL fDaclDefaulted = FALSE;
PACL pAcl = NULL;
DWORD dwStatus = 0;
WCHAR pwszSddl[1024];
SecureZeroMemory( &secAttr, sizeof(secAttr) );
ret = MultiByteToWideChar( CP_UTF8,
MB_ERR_INVALID_CHARS,
pszSddl,
-1,
pwszSddl,
1024 );
if (0 == ret)
{
error_setlasterror();
ret = FATAL_ACL_FAILED;
goto Finished;
}
ret = NONFATAL;
if ( ! ConvertStringSecurityDescriptorToSecurityDescriptorW(
pwszSddl,
SDDL_REVISION_1,
&(secAttr.lpSecurityDescriptor),
NULL ) )
{
error_setlasterror();
ret = FATAL_ACL_FAILED;
goto Finished;
}
secAttr.nLength = sizeof(secAttr);
secAttr.bInheritHandle = FALSE;
if ( ! GetSecurityDescriptorDacl( secAttr.lpSecurityDescriptor,
&fDaclPresent,
&pAcl,
&fDaclDefaulted ) )
{
error_setlasterror();
ret = FATAL_ACL_FAILED;
goto Finished;
}
if ( fDaclPresent )
{
dwStatus = SetNamedSecurityInfoA( pszFilePath,
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
NULL,
NULL,
pAcl,
NULL);
if ( dwStatus != ERROR_SUCCESS )
{
error_setlasterror();
ret = FATAL_ACL_FAILED;
goto Finished;
}
}
Finished:
if ( secAttr.lpSecurityDescriptor != NULL )
{
LocalFree( secAttr.lpSecurityDescriptor );
secAttr.lpSecurityDescriptor = NULL;
}
return ret;
}
/* callers must free returned PSID with alloc_pefree() */
int
GetAppPoolSid(
PCSTR szAppPoolName,
PSID *ppSid
)
{
int ret = 0;
char * szAccountName = NULL;
char * szDomainName = NULL;
size_t cchAccountName = 0;
DWORD sidLength = 0;
DWORD domainNameLength = 0;
SID_NAME_USE sidNameUse;
PSID pSid = NULL;
/* build the app pool account name */
/* "IIS APPPOOL\<AppPoolName>" */
cchAccountName = strlen(szAppPoolName) + 1 + SC_APP_POOL_DOMAIN_NAME_LEN;
szAccountName = alloc_pemalloc(cchAccountName + 1);
if (!szAccountName)
{
ret = FATAL_ACL_FAILED;
goto Finished;
}
sprintf_s( szAccountName, cchAccountName + 1, "%s\\%s", SC_APP_POOL_DOMAIN_NAME, szAppPoolName );
if ( !LookupAccountName(
NULL, // lpSystemName
szAccountName, // lpAccountName
NULL, // Sid
&sidLength, // cbSid
NULL, // ReferencedDomainName
&domainNameLength, // cchReferencedDomainName
&sidNameUse // peUse
))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
error_setlasterror();
ret = WARNING_APPPOOL_NAME_NOT_FOUND;
goto Finished;
}
pSid = (PSID)alloc_pemalloc(sidLength);
if (!pSid)
{
ret = FATAL_OUT_OF_MEMORY;
goto Finished;
}
szDomainName = (char *)alloc_pemalloc(domainNameLength);
if (!szDomainName)
{
ret = FATAL_OUT_OF_MEMORY;
goto Finished;
}
if ( !LookupAccountName(
NULL, // lpSystemName
szAccountName, // lpAccountName
pSid, // Sid
&sidLength, // cbSid
szDomainName, // ReferencedDomainName
&domainNameLength, // cchReferencedDomainName
&sidNameUse // peUse
))
{
error_setlasterror();
ret = FATAL_ACL_FAILED;
goto Finished;
}
}
*ppSid = pSid;
Finished:
if (szAccountName)
{
alloc_pefree(szAccountName);
szAccountName = NULL;
}
if (szDomainName)
{
alloc_pefree(szDomainName);
szDomainName = NULL;
}
return ret;
}
/* callers must free returned PSID with alloc_pefree() */
int
GetUserSid(
PSID *ppSid
)
{
int ret = SUCCESS;
BOOL fRet = FALSE;
HANDLE hProcessToken = NULL;
TOKEN_USER * pTokenUser = NULL;
PSID pSid = NULL;
DWORD cbSid = 0;
DWORD cbSize = 0;
/*
* Get Current User SID
*/
if (!OpenProcessToken(GetCurrentProcess(), GENERIC_READ, &hProcessToken))
{
error_setlasterror();
ret = FATAL_ACCESS_DENIED;
goto Finished;
}
/*
* Get Token for the current user.
* Free the token user with the FreeTokenUserSid API when done.
*/
fRet = GetTokenInformation(hProcessToken,
TokenUser,
NULL,
0,
&cbSize);
if (FALSE == fRet)
{
if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
{
error_setlasterror();
ret = FATAL_ACL_FAILED;
goto Finished;
}
}
pTokenUser = (TOKEN_USER*)alloc_pemalloc(cbSize);
if (NULL == pTokenUser)
{
ret = FATAL_OUT_OF_MEMORY;
goto Finished;
}
fRet = GetTokenInformation(hProcessToken,
TokenUser,
pTokenUser,
cbSize,
&cbSize);
if (FALSE == fRet)
{
error_setlasterror();
ret = FATAL_ACL_FAILED;
goto Finished;
}
cbSid = GetLengthSid(pTokenUser->User.Sid);
pSid = (PSID)alloc_pemalloc(cbSid);
if (!pSid)
{
ret = FATAL_OUT_OF_MEMORY;
goto Finished;
}
if (!CopySid(cbSid, pSid, pTokenUser->User.Sid))
{
error_setlasterror();
ret = FATAL_ACL_FAILED;
goto Finished;
}
/* Transfer ownership of memory to caller */
*ppSid = pSid;
pSid = NULL;
Finished:
if (pTokenUser)
{
alloc_pefree(pTokenUser);
pTokenUser = NULL;
}
if (pSid)
{
alloc_pefree(pSid);
pSid = NULL;
}
return ret;
}
/*
* Impersonation utils
*/
int utils_revert_if_necessary(HANDLE *phOriginalToken)
{
int result = NONFATAL;
HANDLE hOriginalToken = NULL;
/*
* RevertToSelf if needed
*/
if( OpenThreadToken( GetCurrentThread(), /* thread handle whose access token is opened */
TOKEN_IMPERSONATE, /* desired access */
TRUE, /* open as self (access check made against process) */
&hOriginalToken ) ) /* out token handle */
{
_ASSERT( hOriginalToken != NULL );
if( !RevertToSelf() )
{
error_setlasterror();
result = FATAL_FILEMAP_REVERT_FAILED;
goto Finished;
}
}
else
{
error_setlasterror();
if( ERROR_NO_TOKEN == error_getlasterror() )
{
hOriginalToken = NULL;
}
else
{
result = FATAL_FILEMAP_REVERT_FAILED;
goto Finished;
}
}
Finished:
*phOriginalToken = hOriginalToken;
return result;
}
void utils_reimpersonate_if_necessary(HANDLE hOriginalToken)
{
if ( hOriginalToken != NULL )
{
if( !SetThreadToken( NULL, hOriginalToken ) )
{
error_setlasterror();
}
CloseHandle( hOriginalToken );
}
}
void utils_get_filename_and_line(
const char **filename,
uint32_t *linenumber
)
{
const char *error_filename;
uint32_t error_lineno = 0;
if (zend_is_compiling()) {
error_filename = ZSTR_VAL(zend_get_compiled_filename());
error_lineno = zend_get_compiled_lineno();
} else if (EG(current_execute_data) &&
EG(current_execute_data)->func &&
EG(current_execute_data)->func->type == ZEND_USER_FUNCTION) {
error_filename = ZSTR_VAL(EG(current_execute_data)->func->op_array.filename);
error_lineno = (EG(current_execute_data)->opline ? EG(current_execute_data)->opline->lineno : 0);
} else {
error_filename = NULL;
}
if (!error_filename) {
error_filename = "Unknown";
}
*filename = error_filename;
*linenumber = error_lineno;
}
const char * utils_get_typename(zend_uchar type)
{
const char *valuetype;
switch(type)
{
case IS_UNDEF:
valuetype = "undef";
break;
case IS_NULL:
valuetype = "null";
break;
case IS_TRUE:
case IS_FALSE:
valuetype = "bool";
break;
case IS_LONG:
valuetype = "long";
break;
case IS_DOUBLE:
valuetype = "double";
break;
case IS_STRING:
valuetype = "string";
break;
case IS_ARRAY:
valuetype = "array";
break;
case IS_OBJECT:
valuetype = "object";
break;
case IS_REFERENCE:
valuetype = "reference";
break;
#if PHP_API_VERSION < 20180731
case IS_CONSTANT:
#endif
case IS_CONSTANT_AST:
valuetype = "constant";
break;
#if ZEND_MODULE_API_NO >= 20160303
case IS_VOID:
valuetype = "void";
break;
#endif /* ZEND_MODULE_API_NO */
default:
valuetype = "unknown";
break;
}
return valuetype;
}
void utils_wait_for_listener(const char * respath, unsigned int timeout)
{
int result = NONFATAL;
unsigned int sticks = 0;
unsigned char lexists = 0;
if (WCG(lfcache)->pnotify != NULL)
{
sticks = GetTickCount();
while(1)
{
lexists = 1;
result = fcnotify_listenerexists(WCG(lfcache)->pnotify, respath, &lexists);
if(FAILED(result))
{
goto Finished;
}
if (lexists)
{
/* If listener still exists then wait until it is cleared out by file change notification thread */
dprintverbose("utils_wait_for_listener: Waiting for file change listener to close");
Sleep(50);
}
else
{
/* If listener does not exist for this directory then stop waiting. */
break;
}
/* If it takes more than 1 second then exit to prevent process hangs. */
if(utils_ticksdiff(0, sticks) >= timeout)
{
dprintimportant("utils_wait_for_listener: Timed out while waiting for file change listener to close");
break;
}
}
}
Finished:
return;
}
/*
static array of primes. Each prime number is at least twice as large
as the previous.
*/
static unsigned int g_pdwPrimes[] =
{
11, 23, 47, 97,
197, 397, 797, 1597,
3203, 6421, 12853, 25717,
51437, 102877, 205759, 411527,
823117, 1646237, 3292489, 6584983,
13169977, 26339969, 52679969, 105359939,
210719881, 421439783, 842879579, 1685759167,
3371518343
};
#define PRIMES_COUNT (sizeof( g_pdwPrimes ) / sizeof( unsigned int ) )
/* Binary search to find appropriate prime that is less than value */
unsigned int utils_get_prime_less_than(size_t value)
{
unsigned int ret;
int low, high, current, delta;
int done = 0;
low = 0;
high = PRIMES_COUNT;
current = high / 2;
while (!done)
{
if (value == g_pdwPrimes[current]) /* perfect! */
{
ret = g_pdwPrimes[current];
done = 1;
}
else if (value < g_pdwPrimes[current]) /* lower */
{
high = current;
delta = -((high - low) / 2);
}
else /* higher */
{
low = current;
delta = ((high - low) / 2);
}
if (delta == 0)
{
ret = g_pdwPrimes[low];
done = 1;
}
current += delta;
}
return ret;
}