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