summaryrefslogtreecommitdiffstats
blob: aa41d7034496ab859866e432c33e45c1506f45b9 (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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * 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 "CAPICertificateSelector.h"

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

#include <boost/algorithm/string.hpp>

namespace Swift {

#define cert_dlg_title L"TLS Client Certificate Selection"
#define cert_dlg_prompt L"Select a certificate to use for authentication"
/////Hmm, maybe we should not exlude the "location" column
#define exclude_columns	 CRYPTUI_SELECT_LOCATION_COLUMN \
			|CRYPTUI_SELECT_INTENDEDUSE_COLUMN



static std::string getCertUri(PCCERT_CONTEXT cert, const char * cert_store_name) {
	DWORD required_size;
	char * comma;
	char * p_in;
	char * p_out;
	char * subject_name;
	std::string ret = std::string("certstore:") + cert_store_name + ":";

	required_size = CertNameToStrA(cert->dwCertEncodingType,
				&cert->pCertInfo->Subject,
				/* Discard attribute names: */
				CERT_SIMPLE_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
				NULL,
				0);

	subject_name = static_cast<char *>(malloc(required_size+1));

	if (!CertNameToStrA(cert->dwCertEncodingType,
			    &cert->pCertInfo->Subject,
			    /* Discard attribute names: */
			    CERT_SIMPLE_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
			    subject_name,
			    required_size)) {
		return "";
	}

	/* Now search for the "," (ignoring escapes)
	    and truncate the rest of the string */
	if (subject_name[0] == '"') {
		for (comma = subject_name + 1; comma[0]; comma++) {
			if (comma[0] == '"') {
				comma++;
				if (comma[0] != '"') {
					break;
				}
			}
		}
	} else {
		comma = strchr(subject_name, ',');
	}

	if (comma != NULL) {
		*comma = '\0';
	}

	/* We now need to unescape the returned RDN */
	if (subject_name[0] == '"') {
		for (p_in = subject_name + 1, p_out = subject_name; p_in[0]; p_in++, p_out++) {
			if (p_in[0] == '"') {
				p_in++;
			}

			p_out[0] = p_in[0];
		}
		p_out[0] = '\0';
	}

	ret += subject_name;
	free(subject_name);

	return ret;
}

std::string selectCAPICertificate() {

	const char * cert_store_name = "MY";
	PCCERT_CONTEXT cert;
	DWORD store_flags;
	HCERTSTORE hstore;
	HWND hwnd;

	store_flags = CERT_STORE_OPEN_EXISTING_FLAG |
		      CERT_STORE_READONLY_FLAG |
		      CERT_SYSTEM_STORE_CURRENT_USER;

	hstore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0, store_flags, cert_store_name);
	if (!hstore) {
		return "";
	}


////Does this handle need to be freed as well?
	hwnd = GetForegroundWindow();
	if (!hwnd) {
		hwnd = GetActiveWindow();
	}

	/* Call Windows dialog to select a suitable certificate */
	cert = CryptUIDlgSelectCertificateFromStore(hstore,
						  hwnd,
						  cert_dlg_title,
						  cert_dlg_prompt,
						  exclude_columns,
						  0,
						  NULL);

	if (hstore) {
		CertCloseStore(hstore, 0);
	}

	if (cert) {
		std::string ret = getCertUri(cert, cert_store_name);

		CertFreeCertificateContext(cert);

		return ret;
	} else {
		return "";
	}
}

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

}