FreeRDP
Loading...
Searching...
No Matches
certificate_data.c
1
23#include <ctype.h>
24
25#include <freerdp/config.h>
26
27#include <winpr/assert.h>
28#include <winpr/path.h>
29
30#include <freerdp/settings.h>
31
32#include <freerdp/crypto/crypto.h>
33#include <freerdp/crypto/certificate_data.h>
34
35#include "certificate.h"
36#include <freerdp/log.h>
37#define TAG FREERDP_TAG("crypto.certificate_data")
38
39struct rdp_certificate_data
40{
41 char* hostname;
42 UINT16 port;
43 rdpCertificate* cert;
44
45 char cached_hash[MAX_PATH + 10];
46 char* cached_subject;
47 char* cached_issuer;
48 char* cached_fingerprint;
49 char* cached_pem;
50 char* cached_pem_chain;
51};
52
53/* ensure our hostnames (and therefore filenames) always use the same capitalization.
54 * the user might have input random case, but we always need to have a sane
55 * baseline to compare against. */
56static char* ensure_lowercase(char* str, size_t length)
57{
58 const size_t len = strnlen(str, length);
59 for (size_t x = 0; x < len; x++)
60 str[x] = (char)tolower(str[x]);
61 return str;
62}
63
64static char* ensure_valid_charset(char* str, size_t length)
65{
66 const size_t len = strnlen(str, length);
67 for (size_t x = 0; x < len; x++)
68 {
69 char cur = str[x];
70 switch (cur)
71 {
72 case ':':
73 str[x] = '.';
74 break;
75 case '/':
76 case '\\':
77 str[x] = '_';
78 break;
79 default:
80 break;
81 }
82 }
83 return str;
84}
85
86static const char* freerdp_certificate_data_hash_(const char* hostname, UINT16 port, char* name,
87 size_t length)
88{
89 (void)_snprintf(name, length, "%s_%" PRIu16 ".pem", hostname, port);
90 return ensure_lowercase(ensure_valid_charset(name, length), length);
91}
92
93static BOOL freerdp_certificate_data_load_cache(rdpCertificateData* data)
94{
95 BOOL rc = FALSE;
96
97 WINPR_ASSERT(data);
98
99 freerdp_certificate_data_hash_(data->hostname, data->port, data->cached_hash,
100 sizeof(data->cached_hash) - 1);
101 const size_t len = strnlen(data->cached_hash, sizeof(data->cached_hash));
102 if ((len == 0) || (len >= sizeof(data->cached_hash)))
103 goto fail;
104
105 data->cached_subject = freerdp_certificate_get_subject(data->cert);
106 if (!data->cached_subject)
107 data->cached_subject = calloc(1, 1);
108
109 {
110 size_t pemlen = 0;
111 data->cached_pem = freerdp_certificate_get_pem_ex(data->cert, &pemlen, FALSE);
112 }
113 if (!data->cached_pem)
114 goto fail;
115
116 {
117 size_t pemchainlen = 0;
118 data->cached_pem_chain = freerdp_certificate_get_pem_ex(data->cert, &pemchainlen, TRUE);
119 if (!data->cached_pem_chain)
120 goto fail;
121 }
122
123 data->cached_fingerprint = freerdp_certificate_get_fingerprint(data->cert);
124 if (!data->cached_fingerprint)
125 goto fail;
126
127 data->cached_issuer = freerdp_certificate_get_issuer(data->cert);
128 if (!data->cached_issuer)
129 data->cached_issuer = calloc(1, 1);
130
131 rc = TRUE;
132fail:
133 return rc;
134}
135
136static rdpCertificateData* freerdp_certificate_data_new_nocopy(const char* hostname, UINT16 port,
137 rdpCertificate* xcert)
138{
139 rdpCertificateData* certdata = NULL;
140
141 if (!hostname || !xcert)
142 goto fail;
143 if (strnlen(hostname, MAX_PATH) >= MAX_PATH)
144 {
145 WLog_ERR(TAG, "hostname exceeds length limits");
146 goto fail;
147 }
148
149 certdata = (rdpCertificateData*)calloc(1, sizeof(rdpCertificateData));
150
151 if (!certdata)
152 goto fail;
153
154 certdata->port = port;
155 certdata->hostname = _strdup(hostname);
156 if (!certdata->hostname)
157 goto fail;
158 ensure_lowercase(certdata->hostname, strlen(certdata->hostname));
159
160 certdata->cert = xcert;
161 if (!freerdp_certificate_data_load_cache(certdata))
162 {
163 certdata->cert = NULL;
164 goto fail;
165 }
166
167 return certdata;
168fail:
169 freerdp_certificate_data_free(certdata);
170 return NULL;
171}
172
173rdpCertificateData* freerdp_certificate_data_new(const char* hostname, UINT16 port,
174 const rdpCertificate* xcert)
175{
176 rdpCertificate* copy = freerdp_certificate_clone(xcert);
177 rdpCertificateData* data = freerdp_certificate_data_new_nocopy(hostname, port, copy);
178 if (!data)
179 freerdp_certificate_free(copy);
180 return data;
181}
182
183rdpCertificateData* freerdp_certificate_data_new_from_pem(const char* hostname, UINT16 port,
184 const char* pem, size_t length)
185{
186 if (!pem || (length == 0))
187 return NULL;
188
189 rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
190 rdpCertificateData* data = freerdp_certificate_data_new_nocopy(hostname, port, cert);
191 if (!data)
192 freerdp_certificate_free(cert);
193 return data;
194}
195
196rdpCertificateData* freerdp_certificate_data_new_from_file(const char* hostname, UINT16 port,
197 const char* file)
198{
199 if (!file)
200 return NULL;
201
202 rdpCertificate* cert = freerdp_certificate_new_from_file(file);
203 rdpCertificateData* data = freerdp_certificate_data_new_nocopy(hostname, port, cert);
204 if (!data)
205 freerdp_certificate_free(cert);
206 return data;
207}
208
209void freerdp_certificate_data_free(rdpCertificateData* data)
210{
211 if (data == NULL)
212 return;
213
214 free(data->hostname);
215 freerdp_certificate_free(data->cert);
216 free(data->cached_subject);
217 free(data->cached_issuer);
218 free(data->cached_fingerprint);
219 free(data->cached_pem);
220 free(data->cached_pem_chain);
221
222 free(data);
223}
224
225const char* freerdp_certificate_data_get_host(const rdpCertificateData* cert)
226{
227 if (!cert)
228 return NULL;
229 return cert->hostname;
230}
231
232UINT16 freerdp_certificate_data_get_port(const rdpCertificateData* cert)
233{
234 if (!cert)
235 return 0;
236 return cert->port;
237}
238
239const char* freerdp_certificate_data_get_pem(const rdpCertificateData* cert)
240{
241 return freerdp_certificate_data_get_pem_ex(cert, TRUE);
242}
243
244const char* freerdp_certificate_data_get_pem_ex(const rdpCertificateData* cert, BOOL withFullChain)
245{
246 if (!cert)
247 return NULL;
248 if (withFullChain)
249 return cert->cached_pem_chain;
250 return cert->cached_pem;
251}
252
253const char* freerdp_certificate_data_get_subject(const rdpCertificateData* cert)
254{
255 if (!cert)
256 return NULL;
257
258 return cert->cached_subject;
259}
260
261const char* freerdp_certificate_data_get_issuer(const rdpCertificateData* cert)
262{
263 if (!cert)
264 return NULL;
265
266 return cert->cached_issuer;
267}
268const char* freerdp_certificate_data_get_fingerprint(const rdpCertificateData* cert)
269{
270 if (!cert)
271 return NULL;
272
273 return cert->cached_fingerprint;
274}
275
276BOOL freerdp_certificate_data_equal(const rdpCertificateData* a, const rdpCertificateData* b)
277{
278 BOOL rc = FALSE;
279
280 WINPR_ASSERT(a);
281 WINPR_ASSERT(b);
282
283 if (strcmp(a->hostname, b->hostname) != 0)
284 return FALSE;
285 if (a->port != b->port)
286 return FALSE;
287
288 const char* pem1 = freerdp_certificate_data_get_fingerprint(a);
289 const char* pem2 = freerdp_certificate_data_get_fingerprint(b);
290 if (pem1 && pem2)
291 rc = strcmp(pem1, pem2) == 0;
292 else
293 rc = pem1 == pem2;
294
295 return rc;
296}
297
298const char* freerdp_certificate_data_get_hash(const rdpCertificateData* cert)
299{
300 if (!cert)
301 return NULL;
302
303 return cert->cached_hash;
304}
305
306char* freerdp_certificate_data_hash(const char* hostname, UINT16 port)
307{
308 char name[MAX_PATH + 10] = { 0 };
309 freerdp_certificate_data_hash_(hostname, port, name, sizeof(name));
310 return strndup(name, sizeof(name));
311}