GateKeeper.cpp
author Volker Birk <vb@pep.foundation>
Fri, 03 Jul 2020 10:55:44 +0200
branchCOM-115
changeset 434 bf1832802558
parent 432 9ed5917614f6
child 447 18a75cc8b45f
permissions -rw-r--r--
use string table
vb@432
     1
#include "stdafx.h"
vb@88
     2
vb@88
     3
#include "GateKeeper.h"
vb@110
     4
#include "pEpCOMServerAdapter.h"
vb@432
     5
#include "CMainWindow.h"
vb@88
     6
vb@88
     7
using namespace std;
vb@88
     8
vb@432
     9
extern CMainWindow mainWindow;
vb@434
    10
auto r = CMainWindow::r;
vb@432
    11
vb@130
    12
// from https://msdn.microsoft.com/en-us/library/windows/desktop/dd388945(v=vs.85).aspx
vb@123
    13
vb@123
    14
struct PUBLIC_KEY_VALUES {
vb@123
    15
    BLOBHEADER blobheader;
vb@123
    16
    RSAPUBKEY rsapubkey;
vb@123
    17
    BYTE modulus[4096];
vb@123
    18
};
vb@123
    19
vb@123
    20
static void ReverseMemCopy(
vb@123
    21
    _Out_ BYTE       *pbDest,
vb@123
    22
    _In_  BYTE const *pbSource,
vb@123
    23
    _In_  DWORD       cb
markus@269
    24
)
vb@123
    25
{
vb@123
    26
    for (DWORD i = 0; i < cb; i++) {
vb@123
    27
        pbDest[cb - 1 - i] = pbSource[i];
vb@123
    28
    }
vb@123
    29
}
vb@123
    30
vb@123
    31
static NTSTATUS ImportRsaPublicKey(
vb@123
    32
    _In_ BCRYPT_ALG_HANDLE  hAlg,    // CNG provider
vb@123
    33
    _In_ PUBLIC_KEY_VALUES *pKey,    // Pointer to the RSAPUBKEY blob.
vb@123
    34
    _In_ BCRYPT_KEY_HANDLE *phKey    // Receives a handle the imported public key.
markus@269
    35
)
vb@123
    36
{
vb@123
    37
    NTSTATUS hr = 0;
vb@123
    38
vb@123
    39
    BYTE *pbPublicKey = NULL;
vb@123
    40
    DWORD cbKey = 0;
vb@123
    41
vb@123
    42
    // Layout of the RSA public key blob:
vb@123
    43
vb@123
    44
    //  +----------------------------------------------------------------+
vb@123
    45
    //  |     BCRYPT_RSAKEY_BLOB    | BE( dwExp ) |   BE( Modulus )      |
vb@123
    46
    //  +----------------------------------------------------------------+
vb@123
    47
    //
vb@123
    48
    //  sizeof(BCRYPT_RSAKEY_BLOB)       cbExp           cbModulus 
vb@123
    49
    //  <--------------------------><------------><---------------------->
vb@123
    50
    //
vb@123
    51
    //   BE = Big Endian Format                                                     
vb@123
    52
vb@123
    53
    DWORD cbModulus = (pKey->rsapubkey.bitlen + 7) / 8;
vb@123
    54
    DWORD dwExp = pKey->rsapubkey.pubexp;
vb@123
    55
    DWORD cbExp = (dwExp & 0xFF000000) ? 4 :
vb@123
    56
        (dwExp & 0x00FF0000) ? 3 :
vb@123
    57
        (dwExp & 0x0000FF00) ? 2 : 1;
vb@123
    58
vb@123
    59
    BCRYPT_RSAKEY_BLOB *pRsaBlob;
vb@123
    60
    PBYTE pbCurrent;
vb@123
    61
vb@123
    62
    if (!SUCCEEDED(hr = DWordAdd(cbModulus, sizeof(BCRYPT_RSAKEY_BLOB), &cbKey))) {
vb@123
    63
        goto cleanup;
vb@123
    64
    }
vb@123
    65
vb@123
    66
    cbKey += cbExp;
vb@123
    67
markus@269
    68
    pbPublicKey = (PBYTE)CoTaskMemAlloc(cbKey);
vb@123
    69
    if (pbPublicKey == NULL) {
vb@123
    70
        hr = E_OUTOFMEMORY;
vb@123
    71
        goto cleanup;
vb@123
    72
    }
vb@123
    73
vb@123
    74
    ZeroMemory(pbPublicKey, cbKey);
markus@269
    75
    pRsaBlob = (BCRYPT_RSAKEY_BLOB *)(pbPublicKey);
vb@123
    76
vb@123
    77
    //
vb@123
    78
    // Make the Public Key Blob Header
vb@123
    79
    //
vb@123
    80
vb@123
    81
    pRsaBlob->Magic = BCRYPT_RSAPUBLIC_MAGIC;
vb@123
    82
    pRsaBlob->BitLength = pKey->rsapubkey.bitlen;
vb@123
    83
    pRsaBlob->cbPublicExp = cbExp;
vb@123
    84
    pRsaBlob->cbModulus = cbModulus;
vb@123
    85
    pRsaBlob->cbPrime1 = 0;
vb@123
    86
    pRsaBlob->cbPrime2 = 0;
vb@123
    87
markus@269
    88
    pbCurrent = (PBYTE)(pRsaBlob + 1);
vb@123
    89
vb@123
    90
    //
vb@123
    91
    // Copy pubExp Big Endian 
vb@123
    92
    //
vb@123
    93
markus@269
    94
    ReverseMemCopy(pbCurrent, (PBYTE)&dwExp, cbExp);
vb@123
    95
    pbCurrent += cbExp;
vb@123
    96
vb@123
    97
    //
vb@123
    98
    // Copy Modulus Big Endian 
vb@123
    99
    //
vb@123
   100
vb@123
   101
    ReverseMemCopy(pbCurrent, pKey->modulus, cbModulus);
vb@123
   102
vb@123
   103
    //
vb@123
   104
    // Import the public key
vb@123
   105
    //
vb@123
   106
markus@269
   107
    hr = BCryptImportKeyPair(hAlg, NULL, BCRYPT_RSAPUBLIC_BLOB, phKey, (PUCHAR)pbPublicKey, cbKey, 0);
vb@123
   108
vb@123
   109
cleanup:
vb@123
   110
    CoTaskMemFree(pbPublicKey);
vb@123
   111
    return hr;
vb@123
   112
}
vb@123
   113
vb@96
   114
namespace pEp {
vb@96
   115
    const LPCTSTR GateKeeper::plugin_reg_path = _T("Software\\Microsoft\\Office\\Outlook\\Addins\\pEp");
vb@96
   116
    const LPCTSTR GateKeeper::plugin_reg_value_name = _T("LoadBehavior");
vb@135
   117
    const LPCTSTR GateKeeper::updater_reg_path = _T("Software\\pEp\\Updater");
vb@88
   118
vb@96
   119
    const time_t GateKeeper::cycle = 7200;   // 7200 sec is 2 h
vb@137
   120
    const time_t GateKeeper::fraction = 10;  // first update is at 10% of cycle
Thomas@380
   121
    const DWORD GateKeeper::waiting = 10000; //  10 sec
vb@88
   122
vb@117
   123
    GateKeeper::GateKeeper(CpEpCOMServerAdapterModule * self)
vb@228
   124
        : _self(self), now(time(NULL)), next(now /*+ time_diff()*/), hkUpdater(NULL),
markus@269
   125
        internet(NULL), hAES(NULL), hRSA(NULL)
vb@96
   126
    {
Thomas@370
   127
        if (the_gatekeeper)
Thomas@370
   128
            throw runtime_error("second instance of GateKeeper was initialized");
vb@366
   129
markus@269
   130
        DeleteFile(get_lockFile().c_str());
vb@208
   131
vb@96
   132
        LONG lResult = RegOpenCurrentUser(KEY_READ, &cu);
vb@96
   133
        assert(lResult == ERROR_SUCCESS);
vb@96
   134
        if (lResult == ERROR_SUCCESS)
vb@96
   135
            cu_open = true;
vb@96
   136
        else
vb@96
   137
            cu_open = false;
vb@88
   138
vb@96
   139
        if (cu_open) {
vb@96
   140
            LONG lResult = RegOpenKeyEx(cu, updater_reg_path, 0, KEY_READ, &hkUpdater);
markus@269
   141
            if (lResult != ERROR_SUCCESS)
markus@269
   142
                RegCreateKeyEx(cu, updater_reg_path, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hkUpdater, NULL);
markus@269
   143
        }
vb@366
   144
Thomas@370
   145
        the_gatekeeper = this;
vb@88
   146
    }
markus@269
   147
vb@96
   148
    GateKeeper::~GateKeeper()
vb@96
   149
    {
Thomas@370
   150
        the_gatekeeper = nullptr;
vb@366
   151
vb@96
   152
        if (cu_open) {
vb@96
   153
            if (hkUpdater)
vb@96
   154
                RegCloseKey(hkUpdater);
vb@96
   155
            RegCloseKey(cu);
vb@96
   156
        }
vb@88
   157
    }
vb@88
   158
vb@96
   159
    time_t GateKeeper::time_diff()
vb@96
   160
    {
vb@96
   161
        try {
vb@96
   162
            static random_device rd;
vb@96
   163
            static mt19937 gen(rd());
vb@88
   164
markus@269
   165
            uniform_int_distribution<time_t> dist(0, cycle / fraction);
vb@88
   166
vb@96
   167
            return dist(gen);
vb@96
   168
        }
vb@96
   169
        catch (exception&) {
vb@96
   170
            assert(0);
vb@96
   171
            return 0;
vb@96
   172
        }
vb@96
   173
    }
vb@96
   174
vb@96
   175
    void GateKeeper::keep()
vb@96
   176
    {
vb@96
   177
        if (!cu_open)
vb@96
   178
            return;
vb@96
   179
vb@96
   180
        while (1) {
vb@96
   181
            keep_plugin();
vb@96
   182
vb@96
   183
            now = time(NULL);
vb@96
   184
            assert(now != -1);
vb@96
   185
vb@366
   186
            if (now > next) {
vb@96
   187
                next = now + GateKeeper::cycle;
vb@428
   188
                if (update_enabled())
vb@429
   189
                    update_now();
vb@134
   190
            }
Thomas@370
   191
Thomas@370
   192
            Sleep(waiting);
markus@284
   193
        }
markus@284
   194
    }
vb@96
   195
vb@96
   196
    void GateKeeper::keep_plugin()
vb@96
   197
    {
markus@269
   198
        HKEY hkPluginStart = NULL;
vb@141
   199
vb@141
   200
        LONG lResult = RegOpenKeyEx(cu, plugin_reg_path, 0, KEY_WRITE, &hkPluginStart);
vb@96
   201
        if (lResult != ERROR_SUCCESS)
vb@96
   202
            return;
vb@96
   203
vb@141
   204
        DWORD v = 3;
markus@269
   205
        lResult = RegSetValueEx(hkPluginStart, plugin_reg_value_name, 0, REG_DWORD, (const BYTE *)&v, sizeof(DWORD));
vb@141
   206
        assert(lResult == ERROR_SUCCESS);
vb@141
   207
vb@141
   208
        RegCloseKey(hkPluginStart);
vb@96
   209
    }
vb@96
   210
vb@111
   211
    string GateKeeper::update_key()
vb@110
   212
    {
vb@110
   213
        static string key;
vb@110
   214
vb@110
   215
        if (key.length() == 0) {
vb@110
   216
            HRSRC res = FindResource(_self->hModule(), MAKEINTRESOURCE(IRD_UPDATEKEY), RT_RCDATA);
vb@110
   217
            assert(res);
vb@110
   218
            if (!res)
vb@110
   219
                throw runtime_error("FindResource: IRD_UPDATEKEY");
vb@110
   220
vb@110
   221
            HGLOBAL hRes = LoadResource(_self->hModule(), res);
vb@110
   222
            assert(hRes);
vb@110
   223
            if (!hRes)
vb@110
   224
                throw runtime_error("LoadResource: IRD_UPDATEKEY");
vb@110
   225
vb@110
   226
            key = string((char *)LockResource(hRes), SizeofResource(_self->hModule(), res));
vb@110
   227
            UnlockResource(hRes);
vb@110
   228
        }
vb@110
   229
vb@110
   230
        return key;
vb@110
   231
    }
vb@110
   232
vb@116
   233
    BCRYPT_KEY_HANDLE GateKeeper::delivery_key()
vb@112
   234
    {
vb@112
   235
        aeskey_t key;
vb@112
   236
vb@112
   237
        static random_device rd;
vb@112
   238
        static mt19937 gen(rd());
vb@112
   239
vb@123
   240
        uniform_int_distribution<int64_t> dist(0, UINT32_MAX);
vb@112
   241
vb@123
   242
        for (int i = 0; i < 8; i++)
markus@269
   243
            key.dw_key[i] = (uint32_t)dist(gen);
vb@113
   244
vb@116
   245
        BCRYPT_KEY_HANDLE hKey;
vb@142
   246
markus@269
   247
        NTSTATUS status = BCryptGenerateSymmetricKey(hAES, &hKey, NULL, 0, (PUCHAR)&key, (ULONG) sizeof(aeskey_t), 0);
vb@116
   248
        assert(status == 0);
vb@116
   249
        if (status)
vb@116
   250
            throw runtime_error("BCryptGenerateSymmetricKey");
vb@116
   251
vb@142
   252
#ifndef NDEBUG
vb@131
   253
        DWORD keylength = 0;
vb@131
   254
        ULONG copied = 0;
markus@269
   255
        status = BCryptGetProperty(hKey, BCRYPT_KEY_LENGTH, (PUCHAR)&keylength, sizeof(DWORD), &copied, 0);
vb@131
   256
        assert(keylength == 256);
vb@142
   257
#endif
vb@131
   258
vb@116
   259
        return hKey;
vb@113
   260
    }
vb@113
   261
vb@121
   262
    string GateKeeper::wrapped_delivery_key(BCRYPT_KEY_HANDLE hDeliveryKey)
vb@113
   263
    {
vb@113
   264
        string result;
vb@113
   265
vb@299
   266
        BCRYPT_KEY_HANDLE hUpdateKey = NULL;
vb@117
   267
        string _update_key = update_key();
vb@113
   268
vb@299
   269
        PCERT_PUBLIC_KEY_INFO uk = NULL;
vb@299
   270
        DWORD uk_size = 0;
vb@142
   271
vb@123
   272
        BOOL bResult = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
markus@269
   273
            (const BYTE *)_update_key.data(), _update_key.size(), CRYPT_DECODE_ALLOC_FLAG, NULL, &uk, &uk_size);
vb@123
   274
        if (!bResult)
vb@123
   275
            throw runtime_error("CryptDecodeObjectEx: X509_PUBLIC_KEY_INFO");
vb@123
   276
vb@299
   277
        PUBLIC_KEY_VALUES *_uk = NULL;
vb@299
   278
        DWORD _uk_size = 0;
vb@142
   279
vb@123
   280
        bResult = CryptDecodeObjectEx(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB,
vb@123
   281
            uk->PublicKey.pbData, uk->PublicKey.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &_uk, &_uk_size);
vb@123
   282
        LocalFree(uk);
vb@123
   283
        if (!bResult)
vb@123
   284
            throw runtime_error("CryptDecodeObjectEx: X509_PUBLIC_KEY_INFO");
vb@123
   285
vb@123
   286
        HRESULT hResult = ImportRsaPublicKey(hRSA, _uk, &hUpdateKey);
vb@123
   287
        LocalFree(_uk);
vb@302
   288
        if (!hUpdateKey)
vb@123
   289
            throw runtime_error("ImportRsaPublicKey");
vb@117
   290
vb@129
   291
        ULONG psize;
vb@129
   292
        NTSTATUS status = BCryptGetProperty(hUpdateKey, BCRYPT_ALGORITHM_NAME, NULL, 0, &psize, 0);
vb@129
   293
        char *prop = new char[psize];
markus@269
   294
        TCHAR *_prop = (TCHAR *)prop;
vb@299
   295
        status = BCryptGetProperty(hUpdateKey, BCRYPT_ALGORITHM_NAME, (PUCHAR)prop, psize, &psize, 0);
vb@302
   296
        if (status)
vb@302
   297
            throw runtime_error("BCryptGetProperty: BCRYPT_ALGORITHM_NAME");
vb@129
   298
vb@129
   299
        ULONG export_size;
vb@129
   300
        status = BCryptExportKey(hDeliveryKey, NULL, BCRYPT_KEY_DATA_BLOB, NULL, NULL,
vb@129
   301
            &export_size, 0);
vb@129
   302
        if (status)
vb@129
   303
            throw runtime_error("BCryptExportKey: measuring export size");
vb@129
   304
vb@129
   305
        PUCHAR _delivery_key = new UCHAR[export_size];
vb@120
   306
        ULONG copied;
vb@129
   307
        status = BCryptExportKey(hDeliveryKey, NULL, BCRYPT_KEY_DATA_BLOB, _delivery_key, export_size,
markus@269
   308
            &copied, 0);
vb@129
   309
        if (status) {
vb@129
   310
            delete[] _delivery_key;
vb@120
   311
            throw runtime_error("BCryptExportKey: delivery_key");
vb@129
   312
        }
vb@120
   313
vb@117
   314
        BCRYPT_OAEP_PADDING_INFO pi;
vb@119
   315
        memset(&pi, 0, sizeof(BCRYPT_OAEP_PADDING_INFO));
vb@117
   316
        pi.pszAlgId = BCRYPT_SHA256_ALGORITHM;
vb@117
   317
vb@301
   318
        ULONG result_size = 0;
vb@118
   319
        PUCHAR _result = NULL;
vb@129
   320
        ULONG blob_size = export_size - sizeof(BCRYPT_KEY_DATA_BLOB_HEADER);
vb@129
   321
        PUCHAR blob = _delivery_key + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER);
vb@129
   322
        status = BCryptEncrypt(hUpdateKey, blob, blob_size, &pi, NULL, 0, NULL, 0, &result_size, BCRYPT_PAD_OAEP);
vb@119
   323
        if (status) {
vb@129
   324
            delete[] _delivery_key;
vb@119
   325
            BCryptDestroyKey(hUpdateKey);
vb@117
   326
            throw runtime_error("BCryptEncrypt: calculating result size");
vb@119
   327
        }
vb@117
   328
vb@301
   329
        _result = new UCHAR[result_size + 1];
vb@129
   330
        status = BCryptEncrypt(hUpdateKey, blob, blob_size, &pi, NULL, 0, _result, result_size, &copied, BCRYPT_PAD_OAEP);
vb@129
   331
        delete[] _delivery_key;
vb@119
   332
        if (status) {
vb@119
   333
            BCryptDestroyKey(hUpdateKey);
vb@119
   334
            delete[] _result;
vb@118
   335
            throw runtime_error("BCryptEncrypt: encrypting using update_key");
vb@119
   336
        }
vb@117
   337
vb@120
   338
        BCryptDestroyKey(hUpdateKey);
vb@120
   339
vb@117
   340
        stringstream s;
vb@129
   341
        for (ULONG i = 0; i < copied; i++) {
vb@129
   342
            s << hex << setw(2) << setfill('0');
markus@269
   343
            s << (int)_result[i];
vb@129
   344
        }
vb@117
   345
        delete[] _result;
vb@117
   346
        s >> result;
vb@117
   347
vb@113
   348
        return result;
vb@112
   349
    }
vb@112
   350
vb@428
   351
    void GateKeeper::enable_update()
vb@428
   352
    {
vb@428
   353
        LONG lResult = RegOpenKeyEx(cu, updater_reg_path, 0, KEY_WRITE, &hkUpdater);
vb@428
   354
        if (lResult != ERROR_SUCCESS)
vb@428
   355
            return;
vb@428
   356
vb@428
   357
        lResult = RegSetValueExW(hkUpdater, NULL, 0, REG_SZ, (const BYTE *) _T("1"), sizeof(TCHAR)*2);
vb@428
   358
    }
vb@428
   359
vb@428
   360
    void GateKeeper::disable_update()
vb@428
   361
    {
vb@428
   362
        LONG lResult = RegOpenKeyEx(cu, updater_reg_path, 0, KEY_WRITE, &hkUpdater);
vb@428
   363
        if (lResult != ERROR_SUCCESS)
vb@428
   364
            return;
vb@428
   365
vb@428
   366
        lResult = RegSetValueEx(hkUpdater, NULL, 0, REG_SZ, (const BYTE *) _T("0"), sizeof(TCHAR) * 2);
vb@428
   367
    }
vb@428
   368
vb@428
   369
    bool GateKeeper::update_enabled()
vb@428
   370
    {
vb@428
   371
        bool enabled = true;
vb@428
   372
vb@428
   373
        DWORD esize;
vb@428
   374
        RegGetValue(cu, updater_reg_path, NULL, RRF_RT_REG_SZ, NULL, NULL, &esize);
vb@428
   375
        if (esize) {
vb@428
   376
            TCHAR* edata = new TCHAR[esize];
vb@428
   377
            RegGetValue(cu, updater_reg_path, NULL, RRF_RT_REG_SZ, NULL, edata, &esize);
vb@428
   378
            if (tstring(edata) == _T("0"))
vb@428
   379
                enabled = false;
vb@428
   380
            delete[] edata;
vb@428
   381
        }
vb@428
   382
vb@428
   383
        return enabled;
vb@428
   384
    }
vb@428
   385
vb@214
   386
    GateKeeper::product_list GateKeeper::registered_products()
vb@96
   387
    {
vb@214
   388
        product_list products;
vb@96
   389
vb@96
   390
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724872(v=vs.85).aspx
vb@300
   391
        static TCHAR value_name[16384];
vb@96
   392
        DWORD value_name_size;
vb@300
   393
        static TCHAR value[L_MAX_URL_LENGTH + 1];
vb@96
   394
        DWORD value_size;
vb@96
   395
vb@96
   396
        LONG lResult = ERROR_SUCCESS;
vb@96
   397
        for (DWORD i = 0; lResult == ERROR_SUCCESS; i++) {
vb@138
   398
            value_name_size = 16383;
vb@96
   399
            value_size = L_MAX_URL_LENGTH + 1;
markus@269
   400
            lResult = RegEnumValue(hkUpdater, i, value_name, &value_name_size, NULL, NULL, (LPBYTE)value, &value_size);
vb@139
   401
            if (lResult == ERROR_SUCCESS) {
vb@96
   402
                products.push_back({ value_name, value });
vb@139
   403
            }
vb@88
   404
        }
vb@88
   405
vb@96
   406
        return products;
vb@88
   407
    }
vb@88
   408
Thomas@360
   409
    void GateKeeper::execute_file(tstring filename)
vb@126
   410
    {
markus@269
   411
        HANDLE hMutex = CreateMutex(NULL, TRUE, _T("PEPINSTALLERMUTEX"));
markus@269
   412
        if (hMutex) {
markus@269
   413
            CloseHandle(hMutex);
markus@269
   414
            ShellExecute(NULL, _T("open"), filename.c_str(), NULL, NULL, SW_SHOW);
markus@269
   415
        }
vb@126
   416
    }
vb@126
   417
markus@269
   418
    tstring GateKeeper::get_lockFile()
markus@269
   419
    {
markus@269
   420
        static const tstring _fileName = _T("\\pEpSetup.lck");
markus@269
   421
        static tstring fileName;
vb@207
   422
markus@269
   423
        if (fileName.length() == 0) {
markus@269
   424
            unique_ptr < TCHAR[] > _pathName(new TCHAR[MAX_PATH + 1]);
markus@269
   425
            DWORD size = GetTempPath(MAX_PATH, _pathName.get());
markus@269
   426
            if (size > MAX_PATH - _fileName.size())
markus@269
   427
                throw runtime_error("TEMP path too long");
vb@207
   428
markus@269
   429
            fileName = _pathName.get();
markus@269
   430
            fileName += _fileName;
markus@269
   431
        }
vb@207
   432
markus@269
   433
        return fileName;
markus@269
   434
    }
vb@208
   435
Thomas@364
   436
    // Retrieving Headers Using HTTP_QUERY_CUSTOM
Thomas@364
   437
    static tstring httpQueryCustom(HINTERNET hHttp, tstring header)
Thomas@364
   438
    {
Thomas@364
   439
        DWORD dwResult = 0;
Thomas@364
   440
        LPTSTR lpOutBuffer = StrDup(header.c_str());
Thomas@361
   441
Thomas@364
   442
    retry:
Thomas@361
   443
Thomas@364
   444
        if (!HttpQueryInfo(hHttp, HTTP_QUERY_CUSTOM, (LPVOID)lpOutBuffer, &dwResult, NULL))
Thomas@364
   445
        {
Thomas@364
   446
            if (GetLastError() == ERROR_HTTP_HEADER_NOT_FOUND)
Thomas@364
   447
            {
Thomas@364
   448
                // Code to handle the case where the header isn't available.
Thomas@364
   449
                LocalFree(lpOutBuffer);
Thomas@364
   450
                throw(runtime_error("ERROR_HTTP_HEADER_NOT_FOUND"));
Thomas@364
   451
            }
Thomas@364
   452
            else
Thomas@364
   453
            {
Thomas@364
   454
                // Check for an insufficient buffer.
Thomas@364
   455
                if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
Thomas@364
   456
                {
Thomas@364
   457
                    // Allocate the necessary buffer.
Thomas@364
   458
                    LocalFree(lpOutBuffer);
Thomas@364
   459
                    lpOutBuffer = (LPTSTR)LocalAlloc(LMEM_FIXED, dwResult + 1);
Thomas@361
   460
Thomas@364
   461
                    // Rewrite the header name in the buffer.
Thomas@364
   462
                    StringCchPrintf(lpOutBuffer, dwResult, header.c_str());
Thomas@361
   463
Thomas@364
   464
                    // Retry the call.
Thomas@364
   465
                    goto retry;
Thomas@364
   466
                }
Thomas@364
   467
                else
Thomas@364
   468
                {
Thomas@364
   469
                    // Error handling code.
Thomas@364
   470
                    LocalFree(lpOutBuffer);
Thomas@364
   471
                    // FIXME: Add GetLastError()
Thomas@364
   472
                    throw(runtime_error("Unknown"));
Thomas@364
   473
                }
Thomas@364
   474
            }
Thomas@364
   475
        }
Thomas@361
   476
Thomas@364
   477
        tstring result(lpOutBuffer);
Thomas@364
   478
        LocalFree(lpOutBuffer);
Thomas@361
   479
Thomas@364
   480
        return result;
Thomas@364
   481
    }
Thomas@361
   482
vb@363
   483
    bool GateKeeper::update_product(product p, DWORD context)
vb@208
   484
    {
markus@269
   485
        {
markus@269
   486
            HANDLE file = CreateFile(get_lockFile().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
markus@269
   487
            if (file == INVALID_HANDLE_VALUE) {
vb@363
   488
                return false;
markus@269
   489
            }
markus@269
   490
            else {
markus@269
   491
                CloseHandle(file);
markus@269
   492
                DeleteFile(get_lockFile().c_str());
markus@269
   493
            }
markus@269
   494
        }
vb@199
   495
vb@127
   496
        BCRYPT_KEY_HANDLE dk = delivery_key();
vb@126
   497
#ifdef UNICODE
vb@127
   498
        tstring delivery = utility::utf16_string(wrapped_delivery_key(dk));
vb@126
   499
#else
vb@126
   500
        tstring delivery = wrapped_delivery_key(delivery_key());
vb@126
   501
#endif
vb@124
   502
        tstring url = p.second;
vb@124
   503
        url += _T("&challenge=");
vb@124
   504
        url += delivery;
vb@124
   505
        tstring headers;
vb@124
   506
        HINTERNET hUrl = InternetOpenUrl(internet, url.c_str(), headers.c_str(), headers.length(),
markus@269
   507
            INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_NO_UI | INTERNET_FLAG_SECURE, context);
vb@97
   508
        if (hUrl == NULL)
vb@363
   509
            return false;
vb@94
   510
vb@127
   511
        string crypted;
vb@127
   512
        string unencrypted;
vb@133
   513
        UCHAR iv[12];
vb@133
   514
        UCHAR nonce[sizeof(iv)];
vb@133
   515
        UCHAR tag[16];
vb@138
   516
        tstring filename;
vb@138
   517
        HANDLE hFile = NULL;
vb@138
   518
        char *unencrypted_buffer = NULL;
Thomas@364
   519
        bool result = false;
vb@97
   520
vb@130
   521
        try {
Thomas@361
   522
vb@133
   523
            DWORD reading;
vb@133
   524
            InternetReadFile(hUrl, iv, sizeof(iv), &reading);
vb@133
   525
vb@432
   526
            if (reading)
vb@434
   527
                mainWindow.ShowNotificationInfo(r(IDS_DOWNLOADTITLE), r(IDS_DOWNLOADTEXT));
vb@432
   528
vb@133
   529
            if (reading) do {
markus@269
   530
                static char buffer[1024 * 1024];
markus@269
   531
                BOOL bResult = InternetReadFile(hUrl, buffer, 1024 * 1024, &reading);
vb@130
   532
                if (!bResult || !reading)
vb@130
   533
                    break;
vb@130
   534
                crypted += string(buffer, reading);
vb@130
   535
            } while (1);
vb@279
   536
Thomas@364
   537
            tstring contentDisposition = httpQueryCustom(hUrl, _T("Content-Disposition"));
Thomas@361
   538
Thomas@364
   539
            tregex filenameRegex(_T("filename=.([^\"]*)"), regex::extended); //FIXME: case insensitive
Thomas@364
   540
            tsmatch match;
Thomas@361
   541
Thomas@364
   542
            if (regex_search(contentDisposition, match, filenameRegex)) {
Thomas@364
   543
                filename = match[1];
Thomas@364
   544
            }
Thomas@361
   545
vb@279
   546
            InternetCloseHandle(hUrl);
vb@279
   547
            hUrl = NULL;
vb@279
   548
vb@279
   549
            memcpy(nonce, iv, sizeof(iv));
vb@279
   550
vb@279
   551
            BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
vb@279
   552
            BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
vb@279
   553
            authInfo.pbNonce = nonce;
vb@279
   554
            authInfo.cbNonce = sizeof(nonce);
vb@279
   555
            authInfo.pbTag = tag;
vb@279
   556
            authInfo.cbTag = sizeof(tag);
vb@279
   557
vb@279
   558
            ULONG unencrypted_size;
vb@279
   559
            NTSTATUS status = BCryptDecrypt(dk, (PUCHAR)crypted.data(), crypted.size(),
vb@279
   560
                &authInfo, iv, sizeof(iv), NULL, 0, &unencrypted_size, 0);
vb@279
   561
            if (status)
vb@279
   562
                goto closing;
vb@279
   563
vb@279
   564
            unencrypted_buffer = new char[unencrypted_size];
vb@279
   565
vb@279
   566
            PUCHAR crypted_data = (PUCHAR)crypted.data();
vb@279
   567
            ULONG crypted_size = (ULONG)crypted.size() - sizeof(tag);
vb@279
   568
            memcpy(tag, crypted_data + crypted_size, sizeof(tag));
vb@279
   569
vb@279
   570
            status = BCryptDecrypt(dk, crypted_data, crypted_size,
vb@279
   571
                &authInfo, iv, sizeof(iv), (PUCHAR)unencrypted_buffer, unencrypted_size, &unencrypted_size, 0);
vb@279
   572
            if (status)
vb@279
   573
                goto closing;
vb@279
   574
vb@279
   575
            BCryptDestroyKey(dk);
vb@279
   576
vb@431
   577
            TCHAR download_path[MAX_PATH + 1];
vb@431
   578
            PWSTR _downloads;
vb@431
   579
            SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &_downloads);
vb@431
   580
            StringCchCopy(download_path, MAX_PATH, _downloads);
vb@431
   581
            CoTaskMemFree(_downloads);
vb@431
   582
vb@431
   583
            GetTempPath(MAX_PATH, download_path);
Thomas@361
   584
Thomas@364
   585
            if (filename == _T("")) {
vb@431
   586
                filename = download_path;
Thomas@364
   587
                filename += _T("\\pEp_");
Thomas@364
   588
                filename += delivery.substr(0, 32);
Thomas@364
   589
                filename += _T(".msi");
Thomas@364
   590
            }
Thomas@364
   591
            else {
vb@431
   592
                filename = tstring(download_path) + _T("\\") + filename;
Thomas@364
   593
            }
vb@279
   594
vb@279
   595
            hFile = CreateFile(filename.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
vb@279
   596
            if (!hFile)
vb@279
   597
                goto closing;
vb@279
   598
            DWORD writing;
vb@279
   599
            WriteFile(hFile, unencrypted_buffer, unencrypted_size, &writing, NULL);
vb@279
   600
            CloseHandle(hFile);
Thomas@364
   601
            hFile = NULL;
vb@279
   602
            delete[] unencrypted_buffer;
vb@279
   603
            unencrypted_buffer = nullptr;
vb@130
   604
        }
vb@138
   605
        catch (exception&) {
vb@138
   606
            goto closing;
vb@130
   607
        }
vb@133
   608
Thomas@360
   609
        execute_file(filename);
Thomas@364
   610
        result = true;
vb@127
   611
vb@126
   612
    closing:
vb@127
   613
        if (unencrypted_buffer)
vb@127
   614
            delete[] unencrypted_buffer;
vb@126
   615
        if (hFile)
vb@126
   616
            CloseHandle(hFile);
vb@126
   617
        if (hUrl)
vb@126
   618
            InternetCloseHandle(hUrl);
vb@127
   619
        BCryptDestroyKey(dk);
vb@363
   620
Thomas@364
   621
        return result;
vb@96
   622
    }
vb@88
   623
vb@429
   624
    void GateKeeper::update_now()
vb@96
   625
    {
vb@116
   626
        NTSTATUS status = BCryptOpenAlgorithmProvider(&hAES, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
vb@116
   627
        assert(status == 0);
vb@116
   628
        if (status)
vb@116
   629
            goto closing;
markus@269
   630
        status = BCryptSetProperty(hAES, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
vb@128
   631
        if (status)
vb@128
   632
            goto closing;
vb@116
   633
vb@116
   634
        status = BCryptOpenAlgorithmProvider(&hRSA, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
vb@116
   635
        assert(status == 0);
vb@116
   636
        if (status)
vb@116
   637
            goto closing;
vb@116
   638
vb@124
   639
        internet = InternetOpen(_T("pEp"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
vb@124
   640
        if (!internet)
vb@124
   641
            goto closing;
vb@97
   642
markus@269
   643
        {
markus@269
   644
            product_list products = registered_products();
markus@269
   645
            DWORD context = 0;
vb@138
   646
markus@269
   647
            for (auto i = products.begin(); i != products.end(); i++) {
markus@269
   648
                try {
markus@269
   649
                    update_product(*i, context++);
markus@269
   650
                }
markus@269
   651
                catch (exception&) {
vb@214
   652
markus@269
   653
                }
markus@269
   654
            }
markus@269
   655
        }
vb@97
   656
vb@116
   657
    closing:
vb@116
   658
        if (internet)
vb@116
   659
            InternetCloseHandle(internet);
vb@116
   660
        if (hAES)
vb@116
   661
            BCryptCloseAlgorithmProvider(hAES, 0);
vb@116
   662
        if (hRSA)
vb@116
   663
            BCryptCloseAlgorithmProvider(hRSA, 0);
vb@97
   664
        internet = NULL;
vb@116
   665
        hAES = NULL;
vb@116
   666
        hRSA = NULL;
vb@96
   667
    }
vb@88
   668
Thomas@370
   669
    GateKeeper *GateKeeper::the_gatekeeper = nullptr;
vb@366
   670
vb@96
   671
} // namespace pEp