Kaydet (Commit) c3d264b5 authored tarafından Markus Mohrhard's avatar Markus Mohrhard

use a more libreoffice like formatting

This formatting has been done by an astyle script. The idea is that with
a consistent formatting it is much easier to bring in changes from
Mozilla. Updating the code with the new version, running the astyle
script and then looking through the diff seems like the most sensible
option.
üst e88f080b
...@@ -16,14 +16,16 @@ ...@@ -16,14 +16,16 @@
#include "key.h" #include "key.h"
#include <stdint.h> #include <stdint.h>
typedef struct { typedef struct
enum { {
PW_NONE = 0, enum
PW_FROMFILE = 1, {
PW_PLAINTEXT = 2, PW_NONE = 0,
PW_EXTERNAL = 3 PW_FROMFILE = 1,
} source; PW_PLAINTEXT = 2,
char *data; PW_EXTERNAL = 3
} source;
char *data;
} secuPWData; } secuPWData;
#if( defined(_WINDOWS) && !defined(_WIN32_WCE)) #if( defined(_WINDOWS) && !defined(_WIN32_WCE))
......
...@@ -77,9 +77,9 @@ CryptoX_Result CryptoMac_LoadPublicKey(const unsigned char* aCertData, ...@@ -77,9 +77,9 @@ CryptoX_Result CryptoMac_LoadPublicKey(const unsigned char* aCertData,
unsigned int aDataSize, unsigned int aDataSize,
CryptoX_PublicKey* aPublicKey); CryptoX_PublicKey* aPublicKey);
CryptoX_Result CryptoMac_VerifySignature(CryptoX_SignatureHandle* aInputData, CryptoX_Result CryptoMac_VerifySignature(CryptoX_SignatureHandle* aInputData,
CryptoX_PublicKey* aPublicKey, CryptoX_PublicKey* aPublicKey,
const unsigned char* aSignature, const unsigned char* aSignature,
unsigned int aSignatureLen); unsigned int aSignatureLen);
void CryptoMac_FreeSignatureHandle(CryptoX_SignatureHandle* aInputData); void CryptoMac_FreeSignatureHandle(CryptoX_SignatureHandle* aInputData);
void CryptoMac_FreePublicKey(CryptoX_PublicKey* aPublicKey); void CryptoMac_FreePublicKey(CryptoX_PublicKey* aPublicKey);
#ifdef __cplusplus #ifdef __cplusplus
...@@ -118,9 +118,9 @@ CryptoX_Result CryptoAPI_VerifyBegin(HCRYPTPROV provider, HCRYPTHASH* hash); ...@@ -118,9 +118,9 @@ CryptoX_Result CryptoAPI_VerifyBegin(HCRYPTPROV provider, HCRYPTHASH* hash);
CryptoX_Result CryptoAPI_VerifyUpdate(HCRYPTHASH* hash, CryptoX_Result CryptoAPI_VerifyUpdate(HCRYPTHASH* hash,
BYTE *buf, DWORD len); BYTE *buf, DWORD len);
CryptoX_Result CyprtoAPI_VerifySignature(HCRYPTHASH *hash, CryptoX_Result CyprtoAPI_VerifySignature(HCRYPTHASH *hash,
HCRYPTKEY *pubKey, HCRYPTKEY *pubKey,
const BYTE *signature, const BYTE *signature,
DWORD signatureLen); DWORD signatureLen);
#define CryptoX_InvalidHandleValue ((ULONG_PTR)NULL) #define CryptoX_InvalidHandleValue ((ULONG_PTR)NULL)
#define CryptoX_ProviderHandle HCRYPTPROV #define CryptoX_ProviderHandle HCRYPTPROV
......
...@@ -9,14 +9,14 @@ ...@@ -9,14 +9,14 @@
struct CertificateCheckInfo struct CertificateCheckInfo
{ {
LPCWSTR name; LPCWSTR name;
LPCWSTR issuer; LPCWSTR issuer;
}; };
BOOL DoCertificateAttributesMatch(PCCERT_CONTEXT pCertContext, BOOL DoCertificateAttributesMatch(PCCERT_CONTEXT pCertContext,
CertificateCheckInfo &infoToMatch); CertificateCheckInfo &infoToMatch);
DWORD VerifyCertificateTrustForFile(LPCWSTR filePath); DWORD VerifyCertificateTrustForFile(LPCWSTR filePath);
DWORD CheckCertificateForPEFile(LPCWSTR filePath, DWORD CheckCertificateForPEFile(LPCWSTR filePath,
CertificateCheckInfo &infoToMatch); CertificateCheckInfo &infoToMatch);
#endif #endif
...@@ -5,6 +5,6 @@ ...@@ -5,6 +5,6 @@
void WINAPI SvcMain(DWORD dwArgc, LPWSTR *lpszArgv); void WINAPI SvcMain(DWORD dwArgc, LPWSTR *lpszArgv);
void SvcInit(DWORD dwArgc, LPWSTR *lpszArgv); void SvcInit(DWORD dwArgc, LPWSTR *lpszArgv);
void WINAPI SvcCtrlHandler(DWORD dwCtrl); void WINAPI SvcCtrlHandler(DWORD dwCtrl);
void ReportSvcStatus(DWORD dwCurrentState, void ReportSvcStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode, DWORD dwWin32ExitCode,
DWORD dwWaitHint); DWORD dwWaitHint);
...@@ -55,115 +55,127 @@ struct AutoRegKey ...@@ -55,115 +55,127 @@ struct AutoRegKey
BOOL BOOL
DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate, LPCWSTR filePath) DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate, LPCWSTR filePath)
{ {
WCHAR maintenanceServiceKey[MAX_PATH + 1]; WCHAR maintenanceServiceKey[MAX_PATH + 1];
if (!CalculateRegistryPathFromFilePath(basePathForUpdate, if (!CalculateRegistryPathFromFilePath(basePathForUpdate,
maintenanceServiceKey)) { maintenanceServiceKey))
return FALSE; {
} return FALSE;
// We use KEY_WOW64_64KEY to always force 64-bit view.
// The user may have both x86 and x64 applications installed
// which each register information. We need a consistent place
// to put those certificate attributes in and hence why we always
// force the non redirected registry under Wow6432Node.
// This flag is ignored on 32bit systems.
HKEY baseKeyRaw;
LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
maintenanceServiceKey, 0,
KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Could not open key. (%d)", retCode));
// Our tests run with a different apply directory for each test.
// We use this registry key on our test slaves to store the
// allowed name/issuers.
retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
TEST_ONLY_FALLBACK_KEY_PATH, 0,
KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Could not open fallback key. (%d)", retCode));
return FALSE;
}
}
AutoRegKey baseKey(baseKeyRaw);
// Get the number of subkeys.
DWORD subkeyCount = 0;
retCode = RegQueryInfoKeyW(baseKey.get(), nullptr, nullptr, nullptr, &subkeyCount,
nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Could not query info key. (%d)", retCode));
return FALSE;
}
// Enumerate the subkeys, each subkey represents an allowed certificate.
for (DWORD i = 0; i < subkeyCount; i++) {
WCHAR subkeyBuffer[MAX_KEY_LENGTH];
DWORD subkeyBufferCount = MAX_KEY_LENGTH;
retCode = RegEnumKeyExW(baseKey.get(), i, subkeyBuffer,
&subkeyBufferCount, nullptr,
nullptr, nullptr, nullptr);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Could not enum certs. (%d)", retCode));
return FALSE;
} }
// Open the subkey for the current certificate // We use KEY_WOW64_64KEY to always force 64-bit view.
HKEY subKeyRaw; // The user may have both x86 and x64 applications installed
retCode = RegOpenKeyExW(baseKey.get(), // which each register information. We need a consistent place
subkeyBuffer, // to put those certificate attributes in and hence why we always
0, // force the non redirected registry under Wow6432Node.
KEY_READ | KEY_WOW64_64KEY, // This flag is ignored on 32bit systems.
&subKeyRaw); HKEY baseKeyRaw;
AutoRegKey subKey(subKeyRaw); LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
if (retCode != ERROR_SUCCESS) { maintenanceServiceKey, 0,
LOG_WARN(("Could not open subkey. (%d)", retCode)); KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
continue; // Try the next subkey if (retCode != ERROR_SUCCESS)
{
LOG_WARN(("Could not open key. (%d)", retCode));
// Our tests run with a different apply directory for each test.
// We use this registry key on our test slaves to store the
// allowed name/issuers.
retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
TEST_ONLY_FALLBACK_KEY_PATH, 0,
KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
if (retCode != ERROR_SUCCESS)
{
LOG_WARN(("Could not open fallback key. (%d)", retCode));
return FALSE;
}
} }
AutoRegKey baseKey(baseKeyRaw);
const int MAX_CHAR_COUNT = 256;
DWORD valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR); // Get the number of subkeys.
WCHAR name[MAX_CHAR_COUNT] = { L'\0' }; DWORD subkeyCount = 0;
WCHAR issuer[MAX_CHAR_COUNT] = { L'\0' }; retCode = RegQueryInfoKeyW(baseKey.get(), nullptr, nullptr, nullptr, &subkeyCount,
nullptr, nullptr, nullptr, nullptr, nullptr,
// Get the name from the registry nullptr, nullptr);
retCode = RegQueryValueExW(subKey.get(), L"name", 0, nullptr, if (retCode != ERROR_SUCCESS)
(LPBYTE)name, &valueBufSize); {
if (retCode != ERROR_SUCCESS) { LOG_WARN(("Could not query info key. (%d)", retCode));
LOG_WARN(("Could not obtain name from registry. (%d)", retCode)); return FALSE;
continue; // Try the next subkey
} }
// Get the issuer from the registry // Enumerate the subkeys, each subkey represents an allowed certificate.
valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR); for (DWORD i = 0; i < subkeyCount; i++)
retCode = RegQueryValueExW(subKey.get(), L"issuer", 0, nullptr, {
(LPBYTE)issuer, &valueBufSize); WCHAR subkeyBuffer[MAX_KEY_LENGTH];
if (retCode != ERROR_SUCCESS) { DWORD subkeyBufferCount = MAX_KEY_LENGTH;
LOG_WARN(("Could not obtain issuer from registry. (%d)", retCode)); retCode = RegEnumKeyExW(baseKey.get(), i, subkeyBuffer,
continue; // Try the next subkey &subkeyBufferCount, nullptr,
} nullptr, nullptr, nullptr);
if (retCode != ERROR_SUCCESS)
{
LOG_WARN(("Could not enum certs. (%d)", retCode));
return FALSE;
}
CertificateCheckInfo allowedCertificate = { // Open the subkey for the current certificate
name, HKEY subKeyRaw;
issuer, retCode = RegOpenKeyExW(baseKey.get(),
}; subkeyBuffer,
0,
KEY_READ | KEY_WOW64_64KEY,
&subKeyRaw);
AutoRegKey subKey(subKeyRaw);
if (retCode != ERROR_SUCCESS)
{
LOG_WARN(("Could not open subkey. (%d)", retCode));
continue; // Try the next subkey
}
retCode = CheckCertificateForPEFile(filePath, allowedCertificate); const int MAX_CHAR_COUNT = 256;
if (retCode != ERROR_SUCCESS) { DWORD valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
LOG_WARN(("Error on certificate check. (%d)", retCode)); WCHAR name[MAX_CHAR_COUNT] = { L'\0' };
continue; // Try the next subkey WCHAR issuer[MAX_CHAR_COUNT] = { L'\0' };
}
retCode = VerifyCertificateTrustForFile(filePath); // Get the name from the registry
if (retCode != ERROR_SUCCESS) { retCode = RegQueryValueExW(subKey.get(), L"name", 0, nullptr,
LOG_WARN(("Error on certificate trust check. (%d)", retCode)); (LPBYTE)name, &valueBufSize);
continue; // Try the next subkey if (retCode != ERROR_SUCCESS)
} {
LOG_WARN(("Could not obtain name from registry. (%d)", retCode));
continue; // Try the next subkey
}
// Get the issuer from the registry
valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
retCode = RegQueryValueExW(subKey.get(), L"issuer", 0, nullptr,
(LPBYTE)issuer, &valueBufSize);
if (retCode != ERROR_SUCCESS)
{
LOG_WARN(("Could not obtain issuer from registry. (%d)", retCode));
continue; // Try the next subkey
}
CertificateCheckInfo allowedCertificate =
{
name,
issuer,
};
// Raise the roof, we found a match! retCode = CheckCertificateForPEFile(filePath, allowedCertificate);
return TRUE; if (retCode != ERROR_SUCCESS)
} {
LOG_WARN(("Error on certificate check. (%d)", retCode));
continue; // Try the next subkey
}
retCode = VerifyCertificateTrustForFile(filePath);
if (retCode != ERROR_SUCCESS)
{
LOG_WARN(("Error on certificate trust check. (%d)", retCode));
continue; // Try the next subkey
}
// Raise the roof, we found a match!
return TRUE;
}
// No certificates match, :'( // No certificates match, :'(
return FALSE; return FALSE;
} }
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
#define IDI_DIALOG 1003 #define IDI_DIALOG 1003
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_RESOURCE_VALUE 102
......
...@@ -18,68 +18,80 @@ ...@@ -18,68 +18,80 @@
BOOL BOOL
VerifySameFiles(LPCWSTR file1Path, LPCWSTR file2Path, BOOL &sameContent) VerifySameFiles(LPCWSTR file1Path, LPCWSTR file2Path, BOOL &sameContent)
{ {
sameContent = FALSE; sameContent = FALSE;
AutoHandle file1(CreateFileW(file1Path, GENERIC_READ, FILE_SHARE_READ, AutoHandle file1(CreateFileW(file1Path, GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, 0, nullptr)); nullptr, OPEN_EXISTING, 0, nullptr));
if (file1 == INVALID_HANDLE_VALUE) { if (file1 == INVALID_HANDLE_VALUE)
return FALSE; {
} return FALSE;
AutoHandle file2(CreateFileW(file2Path, GENERIC_READ, FILE_SHARE_READ, }
AutoHandle file2(CreateFileW(file2Path, GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, 0, nullptr)); nullptr, OPEN_EXISTING, 0, nullptr));
if (file2 == INVALID_HANDLE_VALUE) { if (file2 == INVALID_HANDLE_VALUE)
return FALSE; {
} return FALSE;
DWORD fileSize1 = GetFileSize(file1.get(), nullptr);
DWORD fileSize2 = GetFileSize(file2.get(), nullptr);
if (INVALID_FILE_SIZE == fileSize1 || INVALID_FILE_SIZE == fileSize2) {
return FALSE;
}
if (fileSize1 != fileSize2) {
// sameContent is already set to FALSE
return TRUE;
}
char buf1[COMPARE_BLOCKSIZE];
char buf2[COMPARE_BLOCKSIZE];
DWORD numBlocks = fileSize1 / COMPARE_BLOCKSIZE;
DWORD leftOver = fileSize1 % COMPARE_BLOCKSIZE;
DWORD readAmount;
for (DWORD i = 0; i < numBlocks; i++) {
if (!ReadFile(file1.get(), buf1, COMPARE_BLOCKSIZE, &readAmount, nullptr) ||
readAmount != COMPARE_BLOCKSIZE) {
return FALSE;
} }
if (!ReadFile(file2.get(), buf2, COMPARE_BLOCKSIZE, &readAmount, nullptr) || DWORD fileSize1 = GetFileSize(file1.get(), nullptr);
readAmount != COMPARE_BLOCKSIZE) { DWORD fileSize2 = GetFileSize(file2.get(), nullptr);
return FALSE; if (INVALID_FILE_SIZE == fileSize1 || INVALID_FILE_SIZE == fileSize2)
{
return FALSE;
} }
if (memcmp(buf1, buf2, COMPARE_BLOCKSIZE)) { if (fileSize1 != fileSize2)
// sameContent is already set to FALSE {
return TRUE; // sameContent is already set to FALSE
return TRUE;
} }
}
if (leftOver) { char buf1[COMPARE_BLOCKSIZE];
if (!ReadFile(file1.get(), buf1, leftOver, &readAmount, nullptr) || char buf2[COMPARE_BLOCKSIZE];
readAmount != leftOver) { DWORD numBlocks = fileSize1 / COMPARE_BLOCKSIZE;
return FALSE; DWORD leftOver = fileSize1 % COMPARE_BLOCKSIZE;
} DWORD readAmount;
for (DWORD i = 0; i < numBlocks; i++)
{
if (!ReadFile(file1.get(), buf1, COMPARE_BLOCKSIZE, &readAmount, nullptr) ||
readAmount != COMPARE_BLOCKSIZE)
{
return FALSE;
}
if (!ReadFile(file2.get(), buf2, COMPARE_BLOCKSIZE, &readAmount, nullptr) ||
readAmount != COMPARE_BLOCKSIZE)
{
return FALSE;
}
if (!ReadFile(file2.get(), buf2, leftOver, &readAmount, nullptr) || if (memcmp(buf1, buf2, COMPARE_BLOCKSIZE))
readAmount != leftOver) { {
return FALSE; // sameContent is already set to FALSE
return TRUE;
}
} }
if (memcmp(buf1, buf2, leftOver)) { if (leftOver)
// sameContent is already set to FALSE {
return TRUE; if (!ReadFile(file1.get(), buf1, leftOver, &readAmount, nullptr) ||
readAmount != leftOver)
{
return FALSE;
}
if (!ReadFile(file2.get(), buf2, leftOver, &readAmount, nullptr) ||
readAmount != leftOver)
{
return FALSE;
}
if (memcmp(buf1, buf2, leftOver))
{
// sameContent is already set to FALSE
return TRUE;
}
} }
}
sameContent = TRUE; sameContent = TRUE;
return TRUE; return TRUE;
} }
...@@ -14,7 +14,7 @@ BOOL VerifySameFiles(LPCWSTR file1Path, LPCWSTR file2Path, BOOL &sameContent); ...@@ -14,7 +14,7 @@ BOOL VerifySameFiles(LPCWSTR file1Path, LPCWSTR file2Path, BOOL &sameContent);
#define COMPARE_BLOCKSIZE 32768 #define COMPARE_BLOCKSIZE 32768
// The following string resource value is used to uniquely identify the signed // The following string resource value is used to uniquely identify the signed
// Mozilla application as an updater. Before the maintenance service will // Mozilla application as an updater. Before the maintenance service will
// execute the updater it must have this updater identity string in its string // execute the updater it must have this updater identity string in its string
// table. No other signed Mozilla product will have this string table value. // table. No other signed Mozilla product will have this string table value.
#define UPDATER_IDENTITY_STRING \ #define UPDATER_IDENTITY_STRING \
......
...@@ -11,11 +11,11 @@ BOOL SvcInstall(SvcInstallAction action); ...@@ -11,11 +11,11 @@ BOOL SvcInstall(SvcInstallAction action);
BOOL SvcUninstall(); BOOL SvcUninstall();
BOOL StopService(); BOOL StopService();
BOOL SetUserAccessServiceDACL(SC_HANDLE hService); BOOL SetUserAccessServiceDACL(SC_HANDLE hService);
DWORD SetUserAccessServiceDACL(SC_HANDLE hService, PACL &pNewAcl, DWORD SetUserAccessServiceDACL(SC_HANDLE hService, PACL &pNewAcl,
PSECURITY_DESCRIPTOR psd); PSECURITY_DESCRIPTOR psd);
struct MaintenanceServiceStringTable struct MaintenanceServiceStringTable
{ {
char serviceDescription[MAX_TEXT_LEN]; char serviceDescription[MAX_TEXT_LEN];
}; };
...@@ -20,11 +20,12 @@ static void ...@@ -20,11 +20,12 @@ static void
BinaryDataToHexString(const BYTE *hash, DWORD &hashSize, BinaryDataToHexString(const BYTE *hash, DWORD &hashSize,
LPWSTR hexString) LPWSTR hexString)
{ {
WCHAR *p = hexString; WCHAR *p = hexString;
for (DWORD i = 0; i < hashSize; ++i) { for (DWORD i = 0; i < hashSize; ++i)
wsprintfW(p, L"%.2x", hash[i]); {
p += 2; wsprintfW(p, L"%.2x", hash[i]);
} p += 2;
}
} }
/** /**
...@@ -40,52 +41,61 @@ static BOOL ...@@ -40,52 +41,61 @@ static BOOL
CalculateMD5(const char *data, DWORD dataSize, CalculateMD5(const char *data, DWORD dataSize,
BYTE **hash, DWORD &hashSize) BYTE **hash, DWORD &hashSize)
{ {
HCRYPTPROV hProv = 0; HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0; HCRYPTHASH hHash = 0;
if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_FULL, if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) { CRYPT_VERIFYCONTEXT))
if (NTE_BAD_KEYSET != GetLastError()) { {
return FALSE; if (NTE_BAD_KEYSET != GetLastError())
{
return FALSE;
}
// Maybe it doesn't exist, try to create it.
if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET))
{
return FALSE;
}
} }
// Maybe it doesn't exist, try to create it. if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_FULL, {
CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET)) { return FALSE;
return FALSE; }
if (!CryptHashData(hHash, reinterpret_cast<const BYTE*>(data),
dataSize, 0))
{
return FALSE;
}
DWORD dwCount = sizeof(DWORD);
if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&hashSize,
&dwCount, 0))
{
return FALSE;
}
*hash = new BYTE[hashSize];
ZeroMemory(*hash, hashSize);
if (!CryptGetHashParam(hHash, HP_HASHVAL, *hash, &hashSize, 0))
{
return FALSE;
}
if (hHash)
{
CryptDestroyHash(hHash);
}
if (hProv)
{
CryptReleaseContext(hProv,0);
} }
}
return TRUE;
if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
return FALSE;
}
if (!CryptHashData(hHash, reinterpret_cast<const BYTE*>(data),
dataSize, 0)) {
return FALSE;
}
DWORD dwCount = sizeof(DWORD);
if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&hashSize,
&dwCount, 0)) {
return FALSE;
}
*hash = new BYTE[hashSize];
ZeroMemory(*hash, hashSize);
if (!CryptGetHashParam(hHash, HP_HASHVAL, *hash, &hashSize, 0)) {
return FALSE;
}
if (hHash) {
CryptDestroyHash(hHash);
}
if (hProv) {
CryptReleaseContext(hProv,0);
}
return TRUE;
} }
/** /**
...@@ -100,41 +110,44 @@ BOOL ...@@ -100,41 +110,44 @@ BOOL
CalculateRegistryPathFromFilePath(const LPCWSTR filePath, CalculateRegistryPathFromFilePath(const LPCWSTR filePath,
LPWSTR registryPath) LPWSTR registryPath)
{ {
size_t filePathLen = wcslen(filePath); size_t filePathLen = wcslen(filePath);
if (!filePathLen) { if (!filePathLen)
return FALSE; {
} return FALSE;
}
// If the file path ends in a slash, ignore that character
if (filePath[filePathLen -1] == L'\\' || // If the file path ends in a slash, ignore that character
filePath[filePathLen - 1] == L'/') { if (filePath[filePathLen -1] == L'\\' ||
filePathLen--; filePath[filePathLen - 1] == L'/')
} {
filePathLen--;
// Copy in the full path into our own buffer. }
// Copying in the extra slash is OK because we calculate the hash
// based on the filePathLen which excludes the slash. // Copy in the full path into our own buffer.
// +2 to account for the possibly trailing slash and the null terminator. // Copying in the extra slash is OK because we calculate the hash
WCHAR *lowercasePath = new WCHAR[filePathLen + 2]; // based on the filePathLen which excludes the slash.
memset(lowercasePath, 0, (filePathLen + 2) * sizeof(WCHAR)); // +2 to account for the possibly trailing slash and the null terminator.
wcsncpy(lowercasePath, filePath, filePathLen + 1); WCHAR *lowercasePath = new WCHAR[filePathLen + 2];
_wcslwr(lowercasePath); memset(lowercasePath, 0, (filePathLen + 2) * sizeof(WCHAR));
wcsncpy(lowercasePath, filePath, filePathLen + 1);
BYTE *hash; _wcslwr(lowercasePath);
DWORD hashSize = 0;
if (!CalculateMD5(reinterpret_cast<const char*>(lowercasePath), BYTE *hash;
filePathLen * 2, DWORD hashSize = 0;
&hash, hashSize)) { if (!CalculateMD5(reinterpret_cast<const char*>(lowercasePath),
filePathLen * 2,
&hash, hashSize))
{
delete[] lowercasePath;
return FALSE;
}
delete[] lowercasePath; delete[] lowercasePath;
return FALSE;
} LPCWSTR baseRegPath = L"SOFTWARE\\LibreOffice\\MaintenanceService\\";
delete[] lowercasePath; wcsncpy(registryPath, baseRegPath, MAX_PATH);
BinaryDataToHexString(hash, hashSize,
LPCWSTR baseRegPath = L"SOFTWARE\\LibreOffice\\MaintenanceService\\"; registryPath + wcslen(baseRegPath));
wcsncpy(registryPath, baseRegPath, MAX_PATH); delete[] hash;
BinaryDataToHexString(hash, hashSize, return TRUE;
registryPath + wcslen(baseRegPath));
delete[] hash;
return TRUE;
} }
#endif #endif
...@@ -19,22 +19,39 @@ ...@@ -19,22 +19,39 @@
#endif #endif
// stack based FILE wrapper to ensure that fclose is called. // stack based FILE wrapper to ensure that fclose is called.
class AutoFILE { class AutoFILE
{
public: public:
explicit AutoFILE(FILE *fp) : fp_(fp) {} explicit AutoFILE(FILE *fp) : fp_(fp) {}
~AutoFILE() { if (fp_) fclose(fp_); } ~AutoFILE()
operator FILE *() { return fp_; } {
if (fp_) fclose(fp_);
}
operator FILE *()
{
return fp_;
}
private: private:
FILE *fp_; FILE *fp_;
}; };
class AutoCharArray { class AutoCharArray
{
public: public:
explicit AutoCharArray(size_t len) { ptr_ = new char[len]; } explicit AutoCharArray(size_t len)
~AutoCharArray() { delete[] ptr_; } {
operator char *() { return ptr_; } ptr_ = new char[len];
}
~AutoCharArray()
{
delete[] ptr_;
}
operator char *()
{
return ptr_;
}
private: private:
char *ptr_; char *ptr_;
}; };
static const char kNL[] = "\r\n"; static const char kNL[] = "\r\n";
...@@ -45,46 +62,55 @@ static const char kRBracket[] = "]"; ...@@ -45,46 +62,55 @@ static const char kRBracket[] = "]";
static const char* static const char*
NS_strspnp(const char *delims, const char *str) NS_strspnp(const char *delims, const char *str)
{ {
const char *d; const char *d;
do { do
for (d = delims; *d != '\0'; ++d) { {
if (*str == *d) { for (d = delims; *d != '\0'; ++d)
++str; {
break; if (*str == *d)
} {
++str;
break;
}
}
} }
} while (*d); while (*d);
return str; return str;
} }
static char* static char*
NS_strtok(const char *delims, char **str) NS_strtok(const char *delims, char **str)
{ {
if (!*str) if (!*str)
return nullptr; return nullptr;
char *ret = (char*) NS_strspnp(delims, *str); char *ret = (char*) NS_strspnp(delims, *str);
if (!*ret) { if (!*ret)
*str = ret; {
return nullptr; *str = ret;
} return nullptr;
char *i = ret;
do {
for (const char *d = delims; *d != '\0'; ++d) {
if (*i == *d) {
*i = '\0';
*str = ++i;
return ret;
}
} }
++i;
} while (*i);
*str = nullptr; char *i = ret;
return ret; do
{
for (const char *d = delims; *d != '\0'; ++d)
{
if (*i == *d)
{
*i = '\0';
*str = ++i;
return ret;
}
}
++i;
}
while (*i);
*str = nullptr;
return ret;
} }
/** /**
...@@ -94,22 +120,22 @@ NS_strtok(const char *delims, char **str) ...@@ -94,22 +120,22 @@ NS_strtok(const char *delims, char **str)
static int static int
find_key(const char *keyList, char* key) find_key(const char *keyList, char* key)
{ {
if (!keyList) if (!keyList)
return -1; return -1;
int index = 0; int index = 0;
const char *p = keyList; const char *p = keyList;
while (*p) while (*p)
{ {
if (strcmp(key, p) == 0) if (strcmp(key, p) == 0)
return index; return index;
p += strlen(p) + 1; p += strlen(p) + 1;
index++; index++;
} }
// The key was not found if we came here // The key was not found if we came here
return -1; return -1;
} }
/** /**
...@@ -129,91 +155,96 @@ ReadStrings(const NS_tchar *path, ...@@ -129,91 +155,96 @@ ReadStrings(const NS_tchar *path,
char results[][MAX_TEXT_LEN], char results[][MAX_TEXT_LEN],
const char *section) const char *section)
{ {
AutoFILE fp(NS_tfopen(path, OPEN_MODE)); AutoFILE fp(NS_tfopen(path, OPEN_MODE));
if (!fp)
return READ_ERROR;
/* get file size */
if (fseek(fp, 0, SEEK_END) != 0)
return READ_ERROR;
long len = ftell(fp);
if (len <= 0)
return READ_ERROR;
size_t flen = size_t(len);
AutoCharArray fileContents(flen + 1);
if (!fileContents)
return READ_STRINGS_MEM_ERROR;
/* read the file in one swoop */
if (fseek(fp, 0, SEEK_SET) != 0)
return READ_ERROR;
size_t rd = fread(fileContents, sizeof(char), flen, fp);
if (rd != flen)
return READ_ERROR;
fileContents[flen] = '\0';
char *buffer = fileContents;
bool inStringsSection = false;
unsigned int read = 0;
while (char *token = NS_strtok(kNL, &buffer)) {
if (token[0] == '#' || token[0] == ';') // it's a comment
continue;
token = (char*) NS_strspnp(kWhitespace, token);
if (!*token) // empty line
continue;
if (token[0] == '[') { // section header!
++token;
char const * currSection = token;
char *rb = NS_strtok(kRBracket, &token);
if (!rb || NS_strtok(kWhitespace, &token)) {
// there's either an unclosed [Section or a [Section]Moretext!
// we could frankly decide that this INI file is malformed right
// here and stop, but we won't... keep going, looking for
// a well-formed [section] to continue working with
inStringsSection = false;
}
else {
if (section)
inStringsSection = strcmp(currSection, section) == 0;
else
inStringsSection = strcmp(currSection, "Strings") == 0;
}
continue;
}
if (!inStringsSection) { if (!fp)
// If we haven't found a section header (or we found a malformed return READ_ERROR;
// section header), or this isn't the [Strings] section don't bother
// parsing this line. /* get file size */
continue; if (fseek(fp, 0, SEEK_END) != 0)
} return READ_ERROR;
long len = ftell(fp);
if (len <= 0)
return READ_ERROR;
size_t flen = size_t(len);
AutoCharArray fileContents(flen + 1);
if (!fileContents)
return READ_STRINGS_MEM_ERROR;
/* read the file in one swoop */
if (fseek(fp, 0, SEEK_SET) != 0)
return READ_ERROR;
size_t rd = fread(fileContents, sizeof(char), flen, fp);
if (rd != flen)
return READ_ERROR;
fileContents[flen] = '\0';
char *buffer = fileContents;
bool inStringsSection = false;
char *key = token; unsigned int read = 0;
char *e = NS_strtok(kEquals, &token);
if (!e)
continue;
int keyIndex = find_key(keyList, key); while (char *token = NS_strtok(kNL, &buffer))
if (keyIndex >= 0 && (unsigned int)keyIndex < numStrings)
{ {
strncpy(results[keyIndex], token, MAX_TEXT_LEN - 1); if (token[0] == '#' || token[0] == ';') // it's a comment
results[keyIndex][MAX_TEXT_LEN - 1] = '\0'; continue;
read++;
token = (char*) NS_strspnp(kWhitespace, token);
if (!*token) // empty line
continue;
if (token[0] == '[') // section header!
{
++token;
char const * currSection = token;
char *rb = NS_strtok(kRBracket, &token);
if (!rb || NS_strtok(kWhitespace, &token))
{
// there's either an unclosed [Section or a [Section]Moretext!
// we could frankly decide that this INI file is malformed right
// here and stop, but we won't... keep going, looking for
// a well-formed [section] to continue working with
inStringsSection = false;
}
else
{
if (section)
inStringsSection = strcmp(currSection, section) == 0;
else
inStringsSection = strcmp(currSection, "Strings") == 0;
}
continue;
}
if (!inStringsSection)
{
// If we haven't found a section header (or we found a malformed
// section header), or this isn't the [Strings] section don't bother
// parsing this line.
continue;
}
char *key = token;
char *e = NS_strtok(kEquals, &token);
if (!e)
continue;
int keyIndex = find_key(keyList, key);
if (keyIndex >= 0 && (unsigned int)keyIndex < numStrings)
{
strncpy(results[keyIndex], token, MAX_TEXT_LEN - 1);
results[keyIndex][MAX_TEXT_LEN - 1] = '\0';
read++;
}
} }
}
return (read == numStrings) ? OK : PARSE_ERROR; return (read == numStrings) ? OK : PARSE_ERROR;
} }
// A wrapper function to read strings for the updater. // A wrapper function to read strings for the updater.
...@@ -221,16 +252,16 @@ ReadStrings(const NS_tchar *path, ...@@ -221,16 +252,16 @@ ReadStrings(const NS_tchar *path,
int int
ReadStrings(const NS_tchar *path, StringTable *results) ReadStrings(const NS_tchar *path, StringTable *results)
{ {
const unsigned int kNumStrings = 2; const unsigned int kNumStrings = 2;
const char *kUpdaterKeys = "Title\0Info\0"; const char *kUpdaterKeys = "Title\0Info\0";
char updater_strings[kNumStrings][MAX_TEXT_LEN]; char updater_strings[kNumStrings][MAX_TEXT_LEN];
int result = ReadStrings(path, kUpdaterKeys, kNumStrings, updater_strings); int result = ReadStrings(path, kUpdaterKeys, kNumStrings, updater_strings);
strncpy(results->title, updater_strings[0], MAX_TEXT_LEN - 1); strncpy(results->title, updater_strings[0], MAX_TEXT_LEN - 1);
results->title[MAX_TEXT_LEN - 1] = '\0'; results->title[MAX_TEXT_LEN - 1] = '\0';
strncpy(results->info, updater_strings[1], MAX_TEXT_LEN - 1); strncpy(results->info, updater_strings[1], MAX_TEXT_LEN - 1);
results->info[MAX_TEXT_LEN - 1] = '\0'; results->info[MAX_TEXT_LEN - 1] = '\0';
return result; return result;
} }
...@@ -11,9 +11,9 @@ ...@@ -11,9 +11,9 @@
#ifdef _WIN32 #ifdef _WIN32
# include <windows.h> # include <windows.h>
typedef WCHAR NS_tchar; typedef WCHAR NS_tchar;
#else #else
typedef char NS_tchar; typedef char NS_tchar;
#endif #endif
#ifndef NULL #ifndef NULL
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
struct StringTable struct StringTable
{ {
char title[MAX_TEXT_LEN]; char title[MAX_TEXT_LEN];
char info[MAX_TEXT_LEN]; char info[MAX_TEXT_LEN];
}; };
/** /**
......
...@@ -11,48 +11,49 @@ ...@@ -11,48 +11,49 @@
// See the MSDN documentation with title: Privilege Constants // See the MSDN documentation with title: Privilege Constants
// At the time of this writing, this documentation is located at: // At the time of this writing, this documentation is located at:
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb530716%28v=vs.85%29.aspx // http://msdn.microsoft.com/en-us/library/windows/desktop/bb530716%28v=vs.85%29.aspx
LPCTSTR UACHelper::PrivsToDisable[] = { LPCTSTR UACHelper::PrivsToDisable[] =
SE_ASSIGNPRIMARYTOKEN_NAME, {
SE_AUDIT_NAME, SE_ASSIGNPRIMARYTOKEN_NAME,
SE_BACKUP_NAME, SE_AUDIT_NAME,
// CreateProcess will succeed but the app will fail to launch on some WinXP SE_BACKUP_NAME,
// machines if SE_CHANGE_NOTIFY_NAME is disabled. In particular this happens // CreateProcess will succeed but the app will fail to launch on some WinXP
// for limited user accounts on those machines. The define is kept here as a // machines if SE_CHANGE_NOTIFY_NAME is disabled. In particular this happens
// reminder that it should never be re-added. // for limited user accounts on those machines. The define is kept here as a
// This permission is for directory watching but also from MSDN: "This // reminder that it should never be re-added.
// privilege also causes the system to skip all traversal access checks." // This permission is for directory watching but also from MSDN: "This
// SE_CHANGE_NOTIFY_NAME, // privilege also causes the system to skip all traversal access checks."
SE_CREATE_GLOBAL_NAME, // SE_CHANGE_NOTIFY_NAME,
SE_CREATE_PAGEFILE_NAME, SE_CREATE_GLOBAL_NAME,
SE_CREATE_PERMANENT_NAME, SE_CREATE_PAGEFILE_NAME,
SE_CREATE_SYMBOLIC_LINK_NAME, SE_CREATE_PERMANENT_NAME,
SE_CREATE_TOKEN_NAME, SE_CREATE_SYMBOLIC_LINK_NAME,
SE_DEBUG_NAME, SE_CREATE_TOKEN_NAME,
SE_ENABLE_DELEGATION_NAME, SE_DEBUG_NAME,
SE_IMPERSONATE_NAME, SE_ENABLE_DELEGATION_NAME,
SE_INC_BASE_PRIORITY_NAME, SE_IMPERSONATE_NAME,
SE_INCREASE_QUOTA_NAME, SE_INC_BASE_PRIORITY_NAME,
SE_INC_WORKING_SET_NAME, SE_INCREASE_QUOTA_NAME,
SE_LOAD_DRIVER_NAME, SE_INC_WORKING_SET_NAME,
SE_LOCK_MEMORY_NAME, SE_LOAD_DRIVER_NAME,
SE_MACHINE_ACCOUNT_NAME, SE_LOCK_MEMORY_NAME,
SE_MANAGE_VOLUME_NAME, SE_MACHINE_ACCOUNT_NAME,
SE_PROF_SINGLE_PROCESS_NAME, SE_MANAGE_VOLUME_NAME,
SE_RELABEL_NAME, SE_PROF_SINGLE_PROCESS_NAME,
SE_REMOTE_SHUTDOWN_NAME, SE_RELABEL_NAME,
SE_RESTORE_NAME, SE_REMOTE_SHUTDOWN_NAME,
SE_SECURITY_NAME, SE_RESTORE_NAME,
SE_SHUTDOWN_NAME, SE_SECURITY_NAME,
SE_SYNC_AGENT_NAME, SE_SHUTDOWN_NAME,
SE_SYSTEM_ENVIRONMENT_NAME, SE_SYNC_AGENT_NAME,
SE_SYSTEM_PROFILE_NAME, SE_SYSTEM_ENVIRONMENT_NAME,
SE_SYSTEMTIME_NAME, SE_SYSTEM_PROFILE_NAME,
SE_TAKE_OWNERSHIP_NAME, SE_SYSTEMTIME_NAME,
SE_TCB_NAME, SE_TAKE_OWNERSHIP_NAME,
SE_TIME_ZONE_NAME, SE_TCB_NAME,
SE_TRUSTED_CREDMAN_ACCESS_NAME, SE_TIME_ZONE_NAME,
SE_UNDOCK_NAME, SE_TRUSTED_CREDMAN_ACCESS_NAME,
SE_UNSOLICITED_INPUT_NAME SE_UNDOCK_NAME,
SE_UNSOLICITED_INPUT_NAME
}; };
/** /**
...@@ -65,15 +66,16 @@ LPCTSTR UACHelper::PrivsToDisable[] = { ...@@ -65,15 +66,16 @@ LPCTSTR UACHelper::PrivsToDisable[] = {
HANDLE HANDLE
UACHelper::OpenUserToken(DWORD sessionID) UACHelper::OpenUserToken(DWORD sessionID)
{ {
HMODULE module = LoadLibraryW(L"wtsapi32.dll"); HMODULE module = LoadLibraryW(L"wtsapi32.dll");
HANDLE token = nullptr; HANDLE token = nullptr;
decltype(WTSQueryUserToken)* wtsQueryUserToken = decltype(WTSQueryUserToken)* wtsQueryUserToken =
(decltype(WTSQueryUserToken)*) GetProcAddress(module, "WTSQueryUserToken"); (decltype(WTSQueryUserToken)*) GetProcAddress(module, "WTSQueryUserToken");
if (wtsQueryUserToken) { if (wtsQueryUserToken)
wtsQueryUserToken(sessionID, &token); {
} wtsQueryUserToken(sessionID, &token);
FreeLibrary(module); }
return token; FreeLibrary(module);
return token;
} }
/** /**
...@@ -86,19 +88,20 @@ UACHelper::OpenUserToken(DWORD sessionID) ...@@ -86,19 +88,20 @@ UACHelper::OpenUserToken(DWORD sessionID)
HANDLE HANDLE
UACHelper::OpenLinkedToken(HANDLE token) UACHelper::OpenLinkedToken(HANDLE token)
{ {
// Magic below... // Magic below...
// UAC creates 2 tokens. One is the restricted token which we have. // UAC creates 2 tokens. One is the restricted token which we have.
// the other is the UAC elevated one. Since we are running as a service // the other is the UAC elevated one. Since we are running as a service
// as the system account we have access to both. // as the system account we have access to both.
TOKEN_LINKED_TOKEN tlt; TOKEN_LINKED_TOKEN tlt;
HANDLE hNewLinkedToken = nullptr; HANDLE hNewLinkedToken = nullptr;
DWORD len; DWORD len;
if (GetTokenInformation(token, (TOKEN_INFORMATION_CLASS)TokenLinkedToken, if (GetTokenInformation(token, (TOKEN_INFORMATION_CLASS)TokenLinkedToken,
&tlt, sizeof(TOKEN_LINKED_TOKEN), &len)) { &tlt, sizeof(TOKEN_LINKED_TOKEN), &len))
token = tlt.LinkedToken; {
hNewLinkedToken = token; token = tlt.LinkedToken;
} hNewLinkedToken = token;
return hNewLinkedToken; }
return hNewLinkedToken;
} }
...@@ -113,23 +116,25 @@ UACHelper::OpenLinkedToken(HANDLE token) ...@@ -113,23 +116,25 @@ UACHelper::OpenLinkedToken(HANDLE token)
BOOL BOOL
UACHelper::SetPrivilege(HANDLE token, LPCTSTR priv, BOOL enable) UACHelper::SetPrivilege(HANDLE token, LPCTSTR priv, BOOL enable)
{ {
LUID luidOfPriv; LUID luidOfPriv;
if (!LookupPrivilegeValue(nullptr, priv, &luidOfPriv)) { if (!LookupPrivilegeValue(nullptr, priv, &luidOfPriv))
return FALSE; {
} return FALSE;
}
TOKEN_PRIVILEGES tokenPriv;
tokenPriv.PrivilegeCount = 1; TOKEN_PRIVILEGES tokenPriv;
tokenPriv.Privileges[0].Luid = luidOfPriv; tokenPriv.PrivilegeCount = 1;
tokenPriv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; tokenPriv.Privileges[0].Luid = luidOfPriv;
tokenPriv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
SetLastError(ERROR_SUCCESS);
if (!AdjustTokenPrivileges(token, false, &tokenPriv, SetLastError(ERROR_SUCCESS);
sizeof(tokenPriv), nullptr, nullptr)) { if (!AdjustTokenPrivileges(token, false, &tokenPriv,
return FALSE; sizeof(tokenPriv), nullptr, nullptr))
} {
return FALSE;
return GetLastError() == ERROR_SUCCESS; }
return GetLastError() == ERROR_SUCCESS;
} }
/** /**
...@@ -147,34 +152,41 @@ UACHelper::DisableUnneededPrivileges(HANDLE token, ...@@ -147,34 +152,41 @@ UACHelper::DisableUnneededPrivileges(HANDLE token,
LPCTSTR *unneededPrivs, LPCTSTR *unneededPrivs,
size_t count) size_t count)
{ {
HANDLE obtainedToken = nullptr; HANDLE obtainedToken = nullptr;
if (!token) { if (!token)
// Note: This handle is a pseudo-handle and need not be closed {
HANDLE process = GetCurrentProcess(); // Note: This handle is a pseudo-handle and need not be closed
if (!OpenProcessToken(process, TOKEN_ALL_ACCESS_P, &obtainedToken)) { HANDLE process = GetCurrentProcess();
LOG_WARN(("Could not obtain token for current process, no " if (!OpenProcessToken(process, TOKEN_ALL_ACCESS_P, &obtainedToken))
"privileges changed. (%d)", GetLastError())); {
return FALSE; LOG_WARN(("Could not obtain token for current process, no "
"privileges changed. (%d)", GetLastError()));
return FALSE;
}
token = obtainedToken;
} }
token = obtainedToken;
} BOOL result = TRUE;
for (size_t i = 0; i < count; i++)
BOOL result = TRUE; {
for (size_t i = 0; i < count; i++) { if (SetPrivilege(token, unneededPrivs[i], FALSE))
if (SetPrivilege(token, unneededPrivs[i], FALSE)) { {
LOG(("Disabled unneeded token privilege: %s.", LOG(("Disabled unneeded token privilege: %s.",
unneededPrivs[i])); unneededPrivs[i]));
} else { }
LOG(("Could not disable token privilege value: %s. (%d)", else
unneededPrivs[i], GetLastError())); {
result = FALSE; LOG(("Could not disable token privilege value: %s. (%d)",
unneededPrivs[i], GetLastError()));
result = FALSE;
}
} }
}
if (obtainedToken) { if (obtainedToken)
CloseHandle(obtainedToken); {
} CloseHandle(obtainedToken);
return result; }
return result;
} }
/** /**
...@@ -190,11 +202,11 @@ UACHelper::DisableUnneededPrivileges(HANDLE token, ...@@ -190,11 +202,11 @@ UACHelper::DisableUnneededPrivileges(HANDLE token,
BOOL BOOL
UACHelper::DisablePrivileges(HANDLE token) UACHelper::DisablePrivileges(HANDLE token)
{ {
static const size_t PrivsToDisableSize = static const size_t PrivsToDisableSize =
sizeof(UACHelper::PrivsToDisable) / sizeof(UACHelper::PrivsToDisable[0]); sizeof(UACHelper::PrivsToDisable) / sizeof(UACHelper::PrivsToDisable[0]);
return DisableUnneededPrivileges(token, UACHelper::PrivsToDisable, return DisableUnneededPrivileges(token, UACHelper::PrivsToDisable,
PrivsToDisableSize); PrivsToDisableSize);
} }
/** /**
...@@ -206,19 +218,20 @@ UACHelper::DisablePrivileges(HANDLE token) ...@@ -206,19 +218,20 @@ UACHelper::DisablePrivileges(HANDLE token)
bool bool
UACHelper::CanUserElevate() UACHelper::CanUserElevate()
{ {
HANDLE token; HANDLE token;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) { if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
return false; {
} return false;
}
TOKEN_ELEVATION_TYPE elevationType;
DWORD len; TOKEN_ELEVATION_TYPE elevationType;
bool canElevate = GetTokenInformation(token, TokenElevationType, DWORD len;
&elevationType, bool canElevate = GetTokenInformation(token, TokenElevationType,
sizeof(elevationType), &len) && &elevationType,
(elevationType == TokenElevationTypeLimited); sizeof(elevationType), &len) &&
CloseHandle(token); (elevationType == TokenElevationTypeLimited);
CloseHandle(token);
return canElevate;
return canElevate;
} }
#endif #endif
...@@ -8,16 +8,16 @@ ...@@ -8,16 +8,16 @@
class UACHelper class UACHelper
{ {
public: public:
static HANDLE OpenUserToken(DWORD sessionID); static HANDLE OpenUserToken(DWORD sessionID);
static HANDLE OpenLinkedToken(HANDLE token); static HANDLE OpenLinkedToken(HANDLE token);
static BOOL DisablePrivileges(HANDLE token); static BOOL DisablePrivileges(HANDLE token);
static bool CanUserElevate(); static bool CanUserElevate();
private: private:
static BOOL SetPrivilege(HANDLE token, LPCTSTR privs, BOOL enable); static BOOL SetPrivilege(HANDLE token, LPCTSTR privs, BOOL enable);
static BOOL DisableUnneededPrivileges(HANDLE token, static BOOL DisableUnneededPrivileges(HANDLE token,
LPCTSTR *unneededPrivs, size_t count); LPCTSTR *unneededPrivs, size_t count);
static LPCTSTR PrivsToDisable[]; static LPCTSTR PrivsToDisable[];
}; };
#endif #endif
...@@ -47,13 +47,13 @@ ...@@ -47,13 +47,13 @@
static inline int mywcsprintf(WCHAR* dest, size_t count, const WCHAR* fmt, ...) static inline int mywcsprintf(WCHAR* dest, size_t count, const WCHAR* fmt, ...)
{ {
size_t _count = count - 1; size_t _count = count - 1;
va_list varargs; va_list varargs;
va_start(varargs, fmt); va_start(varargs, fmt);
int result = _vsnwprintf(dest, count - 1, fmt, varargs); int result = _vsnwprintf(dest, count - 1, fmt, varargs);
va_end(varargs); va_end(varargs);
dest[_count] = L'\0'; dest[_count] = L'\0';
return result; return result;
} }
#define NS_tsnprintf mywcsprintf #define NS_tsnprintf mywcsprintf
# define NS_taccess _waccess # define NS_taccess _waccess
......
...@@ -25,60 +25,61 @@ void UpdateLog::Init(NS_tchar* sourcePathParam, ...@@ -25,60 +25,61 @@ void UpdateLog::Init(NS_tchar* sourcePathParam,
const NS_tchar* alternateFileName, const NS_tchar* alternateFileName,
bool append) bool append)
{ {
if (logFP) if (logFP)
return; return;
sourcePath = sourcePathParam; sourcePath = sourcePathParam;
NS_tchar logFile[MAXPATHLEN]; NS_tchar logFile[MAXPATHLEN];
NS_tsnprintf(logFile, sizeof(logFile)/sizeof(logFile[0]),
NS_T("%s/%s"), sourcePathParam, fileName);
if (alternateFileName && NS_taccess(logFile, F_OK)) {
NS_tsnprintf(logFile, sizeof(logFile)/sizeof(logFile[0]), NS_tsnprintf(logFile, sizeof(logFile)/sizeof(logFile[0]),
NS_T("%s/%s"), sourcePathParam, alternateFileName); NS_T("%s/%s"), sourcePathParam, fileName);
}
if (alternateFileName && NS_taccess(logFile, F_OK))
{
NS_tsnprintf(logFile, sizeof(logFile)/sizeof(logFile[0]),
NS_T("%s/%s"), sourcePathParam, alternateFileName);
}
logFP = NS_tfopen(logFile, append ? NS_T("a") : NS_T("w")); logFP = NS_tfopen(logFile, append ? NS_T("a") : NS_T("w"));
} }
void UpdateLog::Finish() void UpdateLog::Finish()
{ {
if (!logFP) if (!logFP)
return; return;
fclose(logFP); fclose(logFP);
logFP = nullptr; logFP = nullptr;
} }
void UpdateLog::Flush() void UpdateLog::Flush()
{ {
if (!logFP) if (!logFP)
return; return;
fflush(logFP); fflush(logFP);
} }
void UpdateLog::Printf(const char *fmt, ... ) void UpdateLog::Printf(const char *fmt, ... )
{ {
if (!logFP) if (!logFP)
return; return;
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(logFP, fmt, ap); vfprintf(logFP, fmt, ap);
fprintf(logFP, "\n"); fprintf(logFP, "\n");
va_end(ap); va_end(ap);
} }
void UpdateLog::WarnPrintf(const char *fmt, ... ) void UpdateLog::WarnPrintf(const char *fmt, ... )
{ {
if (!logFP) if (!logFP)
return; return;
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
fprintf(logFP, "*** Warning: "); fprintf(logFP, "*** Warning: ");
vfprintf(logFP, fmt, ap); vfprintf(logFP, fmt, ap);
fprintf(logFP, "***\n"); fprintf(logFP, "***\n");
va_end(ap); va_end(ap);
} }
...@@ -9,16 +9,18 @@ ...@@ -9,16 +9,18 @@
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
struct DIR { struct DIR
explicit DIR(const WCHAR* path); {
~DIR(); explicit DIR(const WCHAR* path);
HANDLE findHandle; ~DIR();
WCHAR name[MAX_PATH]; HANDLE findHandle;
WCHAR name[MAX_PATH];
}; };
struct dirent { struct dirent
dirent(); {
WCHAR d_name[MAX_PATH]; dirent();
WCHAR d_name[MAX_PATH];
}; };
DIR* opendir(const WCHAR* path); DIR* opendir(const WCHAR* path);
......
const uint8_t primaryCertData[] = { const uint8_t primaryCertData[] =
{
0x30, 0x82, 0x02, 0xc3, 0x30, 0x82, 0x01, 0xab, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x05, 0x00, 0xa7, 0x67, 0xe2, 0xee, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x23, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x4c, 0x69, 0x62, 0x72, 0x65, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x20, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x30, 0x38, 0x33, 0x30, 0x30, 0x30, 0x33, 0x31, 0x30, 0x35, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x33, 0x30, 0x30, 0x30, 0x33, 0x31, 0x30, 0x35, 0x5a, 0x30, 0x23, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x4c, 0x69, 0x62, 0x72, 0x65, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x20, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb3, 0x63, 0x91, 0x44, 0xf6, 0xf1, 0xd7, 0x7f, 0xc9, 0x3d, 0xee, 0x39, 0x44, 0xba, 0xd5, 0x1b, 0x68, 0x10, 0xfd, 0x2e, 0xb3, 0xe9, 0x17, 0xd8, 0x78, 0x18, 0xff, 0xbb, 0x63, 0x6f, 0x21, 0xd9, 0xb3, 0x55, 0x83, 0xe2, 0x90, 0x18, 0xba, 0x1e, 0x3b, 0x57, 0xbb, 0x4a, 0xc7, 0x4a, 0x3b, 0x49, 0x14, 0x1b, 0xe0, 0xc5, 0x01, 0x8e, 0xb3, 0xfc, 0xe0, 0x31, 0x21, 0xea, 0x6b, 0xc6, 0x5f, 0x70, 0x3c, 0x1f, 0x40, 0x9e, 0x6f, 0xf1, 0x37, 0xa0, 0x74, 0xc5, 0x55, 0xc7, 0x4d, 0x9c, 0xdd, 0x6b, 0xb4, 0xd3, 0x17, 0x22, 0x9e, 0x27, 0xea, 0x57, 0x45, 0x58, 0x19, 0x39, 0x18, 0x42, 0x37, 0x94, 0x8d, 0x11, 0xa1, 0xa9, 0xcb, 0xdd, 0x45, 0x7e, 0x82, 0xbf, 0x93, 0x75, 0xcc, 0x8d, 0x95, 0x04, 0x74, 0xc0, 0x84, 0x2e, 0x7d, 0xbc, 0x56, 0x2d, 0xd1, 0x0e, 0x2e, 0xbf, 0x0e, 0x52, 0x22, 0x0c, 0x65, 0xb2, 0x7a, 0x12, 0x14, 0x27, 0x0b, 0xc9, 0x37, 0x30, 0x48, 0xbc, 0xf0, 0xb8, 0x6d, 0x6f, 0x38, 0xda, 0x98, 0xd0, 0x1c, 0x87, 0xfe, 0x69, 0xc4, 0xc7, 0x73, 0xed, 0x78, 0x01, 0xa5, 0xea, 0x48, 0x08, 0x28, 0xcc, 0x0e, 0x52, 0x20, 0x1f, 0x46, 0x42, 0x83, 0x2e, 0xa6, 0xfd, 0x30, 0xc6, 0x48, 0x55, 0x78, 0xff, 0xd6, 0xac, 0xdd, 0x61, 0xd3, 0xb9, 0xdb, 0x49, 0x6b, 0x93, 0x5a, 0x5b, 0x37, 0xf5, 0xcb, 0x09, 0x4a, 0x6c, 0xa3, 0x85, 0x1f, 0xeb, 0x33, 0x3f, 0xd0, 0xda, 0x55, 0xc3, 0xb2, 0x56, 0x7d, 0x13, 0x16, 0x23, 0x2b, 0x1c, 0x3f, 0xdd, 0x1a, 0xf9, 0x90, 0xf7, 0x43, 0x63, 0x80, 0xa5, 0x71, 0xce, 0x23, 0x56, 0x1b, 0xbf, 0x51, 0x3a, 0xfe, 0x6b, 0x48, 0xfd, 0x42, 0x50, 0xc0, 0x09, 0x30, 0x32, 0x27, 0x20, 0x0d, 0xda, 0x32, 0x02, 0x23, 0x92, 0x10, 0x85, 0xbf, 0xa1, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x59, 0xb2, 0x3f, 0x45, 0x2c, 0xf5, 0x75, 0xeb, 0x20, 0x2f, 0x76, 0x6f, 0x18, 0x06, 0x42, 0x20, 0x83, 0x39, 0x50, 0x64, 0x07, 0xbb, 0xb1, 0x38, 0x74, 0xbe, 0xbb, 0xf4, 0x25, 0x11, 0x72, 0xf9, 0x4a, 0xf0, 0x9a, 0x0b, 0xe7, 0x45, 0x22, 0x59, 0x04, 0x7b, 0xa4, 0xe8, 0x46, 0xe5, 0x67, 0xdb, 0x9a, 0x9e, 0x27, 0x94, 0x5e, 0x60, 0x8b, 0xf5, 0xb1, 0x3f, 0xf2, 0xab, 0x1c, 0x54, 0xc8, 0xbc, 0x2b, 0x83, 0xf9, 0xa7, 0x18, 0x02, 0xb6, 0x95, 0xfa, 0xde, 0x16, 0x49, 0xca, 0xbd, 0x2e, 0xfc, 0xb6, 0x36, 0x9a, 0x9a, 0x7a, 0x1f, 0xc8, 0x91, 0xce, 0x30, 0xe2, 0x89, 0x58, 0x05, 0xee, 0xf3, 0xd1, 0xed, 0x79, 0x45, 0x20, 0xbd, 0x84, 0x48, 0xb0, 0x56, 0x8e, 0x04, 0xc8, 0xb7, 0x7e, 0x46, 0x2a, 0x2e, 0xb3, 0xca, 0xc1, 0xb6, 0x0b, 0xd4, 0x31, 0x6e, 0x83, 0x13, 0xe9, 0xa5, 0xbd, 0x17, 0x0e, 0x47, 0x34, 0x99, 0xc9, 0x5b, 0xb2, 0x53, 0x73, 0x57, 0xeb, 0x30, 0x0d, 0x2d, 0xaa, 0x25, 0xbb, 0xab, 0xac, 0xe8, 0xda, 0xf0, 0xf1, 0xd7, 0x2d, 0x17, 0x70, 0x9e, 0x30, 0x3c, 0x38, 0x59, 0xbf, 0x40, 0x3f, 0x6e, 0xe4, 0x22, 0x84, 0x94, 0x59, 0xf6, 0x32, 0xc1, 0xcb, 0x9c, 0x56, 0x52, 0x04, 0xeb, 0xf6, 0xa3, 0x75, 0xf8, 0xcb, 0xed, 0xaf, 0x17, 0x57, 0x8f, 0x98, 0x56, 0xa4, 0x9d, 0x85, 0x16, 0xc8, 0xf7, 0xd6, 0x97, 0xed, 0xab, 0xe0, 0x4c, 0x1a, 0x44, 0x5c, 0x68, 0x30, 0x26, 0x40, 0x6b, 0xe9, 0x88, 0x6a, 0x37, 0x1e, 0xbf, 0x25, 0x38, 0x55, 0xd9, 0x84, 0x8d, 0x55, 0x08, 0xe6, 0x18, 0xc3, 0xd7, 0x96, 0xfa, 0xd0, 0x2f, 0x17, 0x9b, 0xb6, 0x40, 0xc3, 0x47, 0xb3, 0x30, 0x01, 0x59, 0xec, 0x7c, 0x8d, 0x7e, 0x0a, 0x0d, 0xeb, 0xc4, 0x3b, 0x06, 0x4e, 0x97, 0x41, 0x5e 0x30, 0x82, 0x02, 0xc3, 0x30, 0x82, 0x01, 0xab, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x05, 0x00, 0xa7, 0x67, 0xe2, 0xee, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x23, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x4c, 0x69, 0x62, 0x72, 0x65, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x20, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x30, 0x38, 0x33, 0x30, 0x30, 0x30, 0x33, 0x31, 0x30, 0x35, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x33, 0x30, 0x30, 0x30, 0x33, 0x31, 0x30, 0x35, 0x5a, 0x30, 0x23, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x4c, 0x69, 0x62, 0x72, 0x65, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x20, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb3, 0x63, 0x91, 0x44, 0xf6, 0xf1, 0xd7, 0x7f, 0xc9, 0x3d, 0xee, 0x39, 0x44, 0xba, 0xd5, 0x1b, 0x68, 0x10, 0xfd, 0x2e, 0xb3, 0xe9, 0x17, 0xd8, 0x78, 0x18, 0xff, 0xbb, 0x63, 0x6f, 0x21, 0xd9, 0xb3, 0x55, 0x83, 0xe2, 0x90, 0x18, 0xba, 0x1e, 0x3b, 0x57, 0xbb, 0x4a, 0xc7, 0x4a, 0x3b, 0x49, 0x14, 0x1b, 0xe0, 0xc5, 0x01, 0x8e, 0xb3, 0xfc, 0xe0, 0x31, 0x21, 0xea, 0x6b, 0xc6, 0x5f, 0x70, 0x3c, 0x1f, 0x40, 0x9e, 0x6f, 0xf1, 0x37, 0xa0, 0x74, 0xc5, 0x55, 0xc7, 0x4d, 0x9c, 0xdd, 0x6b, 0xb4, 0xd3, 0x17, 0x22, 0x9e, 0x27, 0xea, 0x57, 0x45, 0x58, 0x19, 0x39, 0x18, 0x42, 0x37, 0x94, 0x8d, 0x11, 0xa1, 0xa9, 0xcb, 0xdd, 0x45, 0x7e, 0x82, 0xbf, 0x93, 0x75, 0xcc, 0x8d, 0x95, 0x04, 0x74, 0xc0, 0x84, 0x2e, 0x7d, 0xbc, 0x56, 0x2d, 0xd1, 0x0e, 0x2e, 0xbf, 0x0e, 0x52, 0x22, 0x0c, 0x65, 0xb2, 0x7a, 0x12, 0x14, 0x27, 0x0b, 0xc9, 0x37, 0x30, 0x48, 0xbc, 0xf0, 0xb8, 0x6d, 0x6f, 0x38, 0xda, 0x98, 0xd0, 0x1c, 0x87, 0xfe, 0x69, 0xc4, 0xc7, 0x73, 0xed, 0x78, 0x01, 0xa5, 0xea, 0x48, 0x08, 0x28, 0xcc, 0x0e, 0x52, 0x20, 0x1f, 0x46, 0x42, 0x83, 0x2e, 0xa6, 0xfd, 0x30, 0xc6, 0x48, 0x55, 0x78, 0xff, 0xd6, 0xac, 0xdd, 0x61, 0xd3, 0xb9, 0xdb, 0x49, 0x6b, 0x93, 0x5a, 0x5b, 0x37, 0xf5, 0xcb, 0x09, 0x4a, 0x6c, 0xa3, 0x85, 0x1f, 0xeb, 0x33, 0x3f, 0xd0, 0xda, 0x55, 0xc3, 0xb2, 0x56, 0x7d, 0x13, 0x16, 0x23, 0x2b, 0x1c, 0x3f, 0xdd, 0x1a, 0xf9, 0x90, 0xf7, 0x43, 0x63, 0x80, 0xa5, 0x71, 0xce, 0x23, 0x56, 0x1b, 0xbf, 0x51, 0x3a, 0xfe, 0x6b, 0x48, 0xfd, 0x42, 0x50, 0xc0, 0x09, 0x30, 0x32, 0x27, 0x20, 0x0d, 0xda, 0x32, 0x02, 0x23, 0x92, 0x10, 0x85, 0xbf, 0xa1, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x59, 0xb2, 0x3f, 0x45, 0x2c, 0xf5, 0x75, 0xeb, 0x20, 0x2f, 0x76, 0x6f, 0x18, 0x06, 0x42, 0x20, 0x83, 0x39, 0x50, 0x64, 0x07, 0xbb, 0xb1, 0x38, 0x74, 0xbe, 0xbb, 0xf4, 0x25, 0x11, 0x72, 0xf9, 0x4a, 0xf0, 0x9a, 0x0b, 0xe7, 0x45, 0x22, 0x59, 0x04, 0x7b, 0xa4, 0xe8, 0x46, 0xe5, 0x67, 0xdb, 0x9a, 0x9e, 0x27, 0x94, 0x5e, 0x60, 0x8b, 0xf5, 0xb1, 0x3f, 0xf2, 0xab, 0x1c, 0x54, 0xc8, 0xbc, 0x2b, 0x83, 0xf9, 0xa7, 0x18, 0x02, 0xb6, 0x95, 0xfa, 0xde, 0x16, 0x49, 0xca, 0xbd, 0x2e, 0xfc, 0xb6, 0x36, 0x9a, 0x9a, 0x7a, 0x1f, 0xc8, 0x91, 0xce, 0x30, 0xe2, 0x89, 0x58, 0x05, 0xee, 0xf3, 0xd1, 0xed, 0x79, 0x45, 0x20, 0xbd, 0x84, 0x48, 0xb0, 0x56, 0x8e, 0x04, 0xc8, 0xb7, 0x7e, 0x46, 0x2a, 0x2e, 0xb3, 0xca, 0xc1, 0xb6, 0x0b, 0xd4, 0x31, 0x6e, 0x83, 0x13, 0xe9, 0xa5, 0xbd, 0x17, 0x0e, 0x47, 0x34, 0x99, 0xc9, 0x5b, 0xb2, 0x53, 0x73, 0x57, 0xeb, 0x30, 0x0d, 0x2d, 0xaa, 0x25, 0xbb, 0xab, 0xac, 0xe8, 0xda, 0xf0, 0xf1, 0xd7, 0x2d, 0x17, 0x70, 0x9e, 0x30, 0x3c, 0x38, 0x59, 0xbf, 0x40, 0x3f, 0x6e, 0xe4, 0x22, 0x84, 0x94, 0x59, 0xf6, 0x32, 0xc1, 0xcb, 0x9c, 0x56, 0x52, 0x04, 0xeb, 0xf6, 0xa3, 0x75, 0xf8, 0xcb, 0xed, 0xaf, 0x17, 0x57, 0x8f, 0x98, 0x56, 0xa4, 0x9d, 0x85, 0x16, 0xc8, 0xf7, 0xd6, 0x97, 0xed, 0xab, 0xe0, 0x4c, 0x1a, 0x44, 0x5c, 0x68, 0x30, 0x26, 0x40, 0x6b, 0xe9, 0x88, 0x6a, 0x37, 0x1e, 0xbf, 0x25, 0x38, 0x55, 0xd9, 0x84, 0x8d, 0x55, 0x08, 0xe6, 0x18, 0xc3, 0xd7, 0x96, 0xfa, 0xd0, 0x2f, 0x17, 0x9b, 0xb6, 0x40, 0xc3, 0x47, 0xb3, 0x30, 0x01, 0x59, 0xec, 0x7c, 0x8d, 0x7e, 0x0a, 0x0d, 0xeb, 0xc4, 0x3b, 0x06, 0x4e, 0x97, 0x41, 0x5e
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment