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