GateKeeper.cpp
author Volker Birk <vb@pep-project.org>
Wed, 22 Jun 2016 13:01:31 +0200
changeset 119 7b1eac29288f
parent 118 397dc2da7805
child 120 5adccc3e3d3c
permissions -rw-r--r--
safer
     1 #include "stdafx.h"
     2 
     3 #include "GateKeeper.h"
     4 #include "pEpCOMServerAdapter.h"
     5 
     6 using namespace std;
     7 
     8 namespace pEp {
     9 
    10     const LPCTSTR GateKeeper::plugin_reg_path = _T("Software\\Microsoft\\Office\\Outlook\\Addins\\pEp");
    11     const LPCTSTR GateKeeper::plugin_reg_value_name = _T("LoadBehavior");
    12     const LPCTSTR GateKeeper::updater_reg_path = _T("Software\\pEp\\Updater";)
    13 
    14     const time_t GateKeeper::cycle = 7200;   // 7200 sec is 2 h
    15     const DWORD GateKeeper::waiting = 10000; // 10000 ms is 10 sec
    16 
    17     GateKeeper::GateKeeper(CpEpCOMServerAdapterModule * self)
    18         : _self(self), now(time(NULL)), next(now + time_diff()), hkUpdater(NULL), internet(NULL), hAES(NULL), hRSA(NULL)
    19     {
    20         LONG lResult = RegOpenCurrentUser(KEY_READ, &cu);
    21         assert(lResult == ERROR_SUCCESS);
    22         if (lResult == ERROR_SUCCESS)
    23             cu_open = true;
    24         else
    25             cu_open = false;
    26 
    27         if (cu_open) {
    28             LONG lResult = RegOpenKeyEx(cu, updater_reg_path, 0, KEY_READ, &hkUpdater);
    29             assert(lResult == ERROR_SUCCESS);
    30             if (lResult != ERROR_SUCCESS)
    31                 return;
    32         }
    33     }
    34     
    35     GateKeeper::~GateKeeper()
    36     {
    37         if (cu_open) {
    38             if (hkUpdater)
    39                 RegCloseKey(hkUpdater);
    40             RegCloseKey(cu);
    41         }
    42     }
    43 
    44     time_t GateKeeper::time_diff()
    45     {
    46         try {
    47             static random_device rd;
    48             static mt19937 gen(rd());
    49 
    50             uniform_int_distribution<time_t> dist(0, cycle);
    51 
    52             return dist(gen);
    53         }
    54         catch (exception&) {
    55             assert(0);
    56             return 0;
    57         }
    58     }
    59 
    60     void GateKeeper::keep()
    61     {
    62         if (!cu_open)
    63             return;
    64 
    65         while (1) {
    66             keep_plugin();
    67 
    68             now = time(NULL);
    69             assert(now != -1);
    70 
    71             if (now > next) {
    72                 next = now + GateKeeper::cycle;
    73                 keep_updated();
    74             }
    75 
    76             Sleep(waiting);
    77         }
    78     }
    79 
    80     void GateKeeper::keep_plugin()
    81     {
    82         while (!_self->m_bComInitialized)
    83             Sleep(1);
    84 
    85         MessageBox(NULL, _T("test"), _T("keep_plugin"), MB_ICONINFORMATION | MB_TOPMOST);
    86 
    87         DWORD value;
    88         DWORD size;
    89 
    90         LONG lResult = RegGetValue(cu, plugin_reg_path, plugin_reg_value_name, RRF_RT_REG_DWORD, NULL, &value, &size);
    91         if (lResult != ERROR_SUCCESS)
    92             return;
    93 
    94         if (value != 3) {
    95             lResult = RegSetValue(cu, plugin_reg_path, RRF_RT_REG_DWORD, plugin_reg_value_name, 3);
    96             assert(lResult == ERROR_SUCCESS);
    97         }
    98     }
    99 
   100     string GateKeeper::update_key()
   101     {
   102         static string key;
   103 
   104         if (key.length() == 0) {
   105             HRSRC res = FindResource(_self->hModule(), MAKEINTRESOURCE(IRD_UPDATEKEY), RT_RCDATA);
   106             assert(res);
   107             if (!res)
   108                 throw runtime_error("FindResource: IRD_UPDATEKEY");
   109 
   110             HGLOBAL hRes = LoadResource(_self->hModule(), res);
   111             assert(hRes);
   112             if (!hRes)
   113                 throw runtime_error("LoadResource: IRD_UPDATEKEY");
   114 
   115             key = string((char *)LockResource(hRes), SizeofResource(_self->hModule(), res));
   116             UnlockResource(hRes);
   117         }
   118 
   119         return key;
   120     }
   121 
   122     BCRYPT_KEY_HANDLE GateKeeper::delivery_key()
   123     {
   124         aeskey_t key;
   125 
   126         static random_device rd;
   127         static mt19937 gen(rd());
   128 
   129         uniform_int_distribution<time_t> dist(0, UINT64_MAX);
   130 
   131         key.qw_key[0] = dist(gen);
   132         key.qw_key[1] = dist(gen);
   133 
   134         BCRYPT_KEY_HANDLE hKey;
   135         NTSTATUS status = BCryptGenerateSymmetricKey(hAES, &hKey, NULL, 0, (PUCHAR) &key, (ULONG) sizeof(aeskey_t), 0);
   136         assert(status == 0);
   137         if (status)
   138             throw runtime_error("BCryptGenerateSymmetricKey");
   139 
   140         return hKey;
   141     }
   142 
   143     string GateKeeper::wrapped_delivery_key(BCRYPT_KEY_HANDLE hKey)
   144     {
   145         string result;
   146 
   147         BCRYPT_KEY_HANDLE hUpdateKey;
   148         string _update_key = update_key();
   149 
   150         NTSTATUS status = BCryptImportKeyPair(hRSA, NULL, BCRYPT_RSAPUBLIC_BLOB, &hUpdateKey,
   151                 (PUCHAR) _update_key.data(), _update_key.size(), 0);
   152         if (status)
   153             throw runtime_error("BCryptImportKeyPair: update_key");
   154 
   155         static random_device rd;
   156         static mt19937 gen(rd());
   157         uniform_int_distribution<time_t> dist(0, UINT64_MAX);
   158         uint64_t r[32];
   159         for (int i = 0; i < 32; i++)
   160             r[i] = dist(gen);
   161 
   162         BCRYPT_OAEP_PADDING_INFO pi;
   163         memset(&pi, 0, sizeof(BCRYPT_OAEP_PADDING_INFO));
   164         pi.pszAlgId = BCRYPT_SHA256_ALGORITHM;
   165         pi.pbLabel = (PUCHAR) r;
   166         pi.cbLabel = sizeof(r);
   167 
   168         ULONG result_size;
   169         PUCHAR _result = NULL;
   170         status = BCryptEncrypt(hUpdateKey, (PUCHAR) _update_key.data(), _update_key.size(), &pi, NULL, 0, NULL, 0, &result_size, BCRYPT_PAD_OAEP);
   171         if (status) {
   172             BCryptDestroyKey(hUpdateKey);
   173             throw runtime_error("BCryptEncrypt: calculating result size");
   174         }
   175 
   176         _result = new UCHAR[result_size];
   177         ULONG copied;
   178         status = BCryptEncrypt(hUpdateKey, (PUCHAR) _update_key.data(), _update_key.size(), &pi, NULL, 0, _result, result_size, &copied, BCRYPT_PAD_OAEP);
   179         if (status) {
   180             BCryptDestroyKey(hUpdateKey);
   181             delete[] _result;
   182             throw runtime_error("BCryptEncrypt: encrypting using update_key");
   183         }
   184 
   185         stringstream s;
   186         s << hex << setw(2) << setfill('0');
   187         for (ULONG i = 0; i < copied; i++)
   188             s << (int) _result[i];
   189         delete[] _result;
   190         s >> result;
   191 
   192         BCryptDestroyKey(hUpdateKey);
   193         return result;
   194     }
   195 
   196     GateKeeper::product_list& GateKeeper::registered_products()
   197     {
   198         static product_list products;
   199 
   200         // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724872(v=vs.85).aspx
   201         static TCHAR value_name[16384];
   202         DWORD value_name_size;
   203         static TCHAR value[L_MAX_URL_LENGTH + 1];
   204         DWORD value_size;
   205 
   206         products.empty();
   207 
   208         LONG lResult = ERROR_SUCCESS;
   209         for (DWORD i = 0; lResult == ERROR_SUCCESS; i++) {
   210             value_size = L_MAX_URL_LENGTH + 1;
   211             lResult = RegEnumValue(hkUpdater, 0, value_name, &value_name_size, NULL, NULL, (LPBYTE) value, &value_size);
   212             if (lResult == ERROR_SUCCESS)
   213                 products.push_back({ value_name, value });
   214         }
   215 
   216         return products;
   217     }
   218 
   219     void GateKeeper::update_product(product p, DWORD context)
   220     {
   221         HINTERNET hUrl = InternetOpenUrl(internet, p.second.c_str(), NULL, 0,
   222                 INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_NO_UI | INTERNET_FLAG_SECURE, context);
   223         if (hUrl == NULL)
   224             return;
   225 
   226         // update
   227         PCTSTR rgpszAcceptTypes[] = { _T("text/plain"), NULL };
   228         HINTERNET hRequest = HttpOpenRequest(hUrl, NULL, _T("challenge"), NULL, NULL, rgpszAcceptTypes, INTERNET_FLAG_NO_UI | INTERNET_FLAG_SECURE, context);
   229 
   230 
   231         InternetCloseHandle(hUrl);
   232     }
   233 
   234     void GateKeeper::keep_updated()
   235     {
   236         return; // disabled for now
   237 
   238         NTSTATUS status = BCryptOpenAlgorithmProvider(&hAES, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
   239         assert(status == 0);
   240         if (status)
   241             goto closing;
   242 
   243         status = BCryptOpenAlgorithmProvider(&hRSA, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
   244         assert(status == 0);
   245         if (status)
   246             goto closing;
   247 
   248         internet = InternetOpen(_T("pEp"), INTERNET_OPEN_TYPE_PROXY, NULL, NULL, 0);
   249         if (!internet)
   250             goto closing;
   251 
   252         product_list& products = registered_products();
   253         DWORD context = 0;
   254         for (auto i = products.begin(); i != products.end(); i++) {
   255             update_product(*i, context++);
   256         }
   257 
   258     closing:
   259         if (internet)
   260             InternetCloseHandle(internet);
   261         if (hAES)
   262             BCryptCloseAlgorithmProvider(hAES, 0);
   263         if (hRSA)
   264             BCryptCloseAlgorithmProvider(hRSA, 0);
   265         internet = NULL;
   266         hAES = NULL;
   267         hRSA = NULL;
   268     }
   269 
   270 } // namespace pEp