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