summaryrefslogtreecommitdiffstats
blob: e47121b53256a16021ec963e6e1b10c52eeb2ece (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/*
 * Copyright (c) 2012 Isode Limited, London, England.
 * Licensed under the simplified BSD license.
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 */

#include <string>

#include <Swift/QtUI/CAPICertificateSelector.h>

#define SECURITY_WIN32
#include <Windows.h>
#include <WinCrypt.h>
#include <cryptuiapi.h>


#include <Swiften/StringCodecs/Hexify.h>
#include <boost/algorithm/string.hpp>
#include <Swift/Controllers/Intl.h>
#include <Swift/QtUI/QtSwiftUtil.h>

#include <Swiften/Base/Log.h>
#include <Swiften/TLS/Schannel/SchannelUtil.h>

namespace Swift {

/////Hmm, maybe we should not exlude the "location" column
#define exclude_columns     CRYPTUI_SELECT_LOCATION_COLUMN | CRYPTUI_SELECT_INTENDEDUSE_COLUMN

#define SHA1_HASH_LENGTH 20

static std::string getCertUri(PCCERT_CONTEXT cert, const char * cert_store_name) {
    DWORD cbHash = SHA1_HASH_LENGTH;
    BYTE aHash[SHA1_HASH_LENGTH];
    std::string result("certstore:");

    result += cert_store_name;
    result += ":sha1:";

    if (CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID, aHash, &cbHash) == FALSE ) {
        return "";
    }

    ByteArray byteArray = createByteArray((char *)(&aHash[0]), cbHash);
    result += Hexify::hexify(byteArray);

    return result;
}

std::string selectCAPICertificate() {
    const char* certStoreName = "MY";

    DWORD storeFlags = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER;

    HCERTSTORE hstore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0, storeFlags, certStoreName);
    if (!hstore) {
        return "";
    }

    HWND hwnd = GetForegroundWindow();
    if (!hwnd) {
        hwnd = GetActiveWindow();
    }

    std::string certificateDialogTitle = QT_TRANSLATE_NOOP("", "TLS Client Certificate Selection");
    std::string certificateDialogPrompt = QT_TRANSLATE_NOOP("", "Select a certificate to use for authentication");

    int titleLength = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, certificateDialogTitle.c_str(), -1, NULL, 0);
    int promptLength = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, certificateDialogPrompt.c_str(), -1, NULL, 0);

    wchar_t* titleChars = new wchar_t[titleLength];
    wchar_t* promptChars = new wchar_t[promptLength];

    //titleChars[titleLength] = '\0';
    //promptChars[promptLength] = '\0';

    titleLength = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, certificateDialogTitle.c_str(), -1, titleChars, titleLength);
    promptLength = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, certificateDialogPrompt.c_str(), -1, promptChars, promptLength);

    if (titleLength == 0 || promptLength == 0) {
        int error = GetLastError();
        switch (error) {
            case ERROR_INSUFFICIENT_BUFFER: SWIFT_LOG(error) << "Insufficient buffer for rendering cert dialog" << std::endl;break;
            case ERROR_INVALID_FLAGS: SWIFT_LOG(error) << "Invalid flags for rendering cert dialog" << std::endl;break;
            case ERROR_INVALID_PARAMETER: SWIFT_LOG(error) << "Invalid parameter for rendering cert dialog" << std::endl;break;
            case ERROR_NO_UNICODE_TRANSLATION: SWIFT_LOG(error) << "Invalid unicode for rendering cert dialog" << std::endl;break;
            default: SWIFT_LOG(error) << "Unexpected multibyte conversion errorcode" << std::endl;

        }
    }

    std::string result;
    /* Call Windows dialog to select a suitable certificate */
    {
        ScopedCertContext cert(CryptUIDlgSelectCertificateFromStore(hstore, hwnd, titleChars, promptChars, exclude_columns, 0, NULL));
        if (cert) {
            result = getCertUri(cert, certStoreName);
        }
    }

    delete[] titleChars;
    delete[] promptChars;

    if (hstore) {
        if (CertCloseStore(hstore, 0) == FALSE) {
            SWIFT_LOG(debug) << "Failed to close the certificate store handle." << std::endl;
        }
    }

    return result;
}

bool isCAPIURI(std::string uri) {
    return (boost::iequals(uri.substr(0, 10), "certstore:"));
}

}