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 size_t pemlen = 0;
110 data->cached_pem = freerdp_certificate_get_pem_ex(data->cert, &pemlen, FALSE);
111 if (!data->cached_pem)
112 goto fail;
113
114 size_t pemchainlen = 0;
115 data->cached_pem_chain = freerdp_certificate_get_pem_ex(data->cert, &pemchainlen, TRUE);
116 if (!data->cached_pem_chain)
117 goto fail;
118
119 data->cached_fingerprint = freerdp_certificate_get_fingerprint(data->cert);
120 if (!data->cached_fingerprint)
121 goto fail;
122
123 data->cached_issuer = freerdp_certificate_get_issuer(data->cert);
124 if (!data->cached_issuer)
125 data->cached_issuer = calloc(1, 1);
126
127 rc = TRUE;
128fail:
129 return rc;
130}
131
132static rdpCertificateData* freerdp_certificate_data_new_nocopy(const char* hostname, UINT16 port,
133 rdpCertificate* xcert)
134{
135 rdpCertificateData* certdata = NULL;
136
137 if (!hostname || !xcert)
138 goto fail;
139 if (strnlen(hostname, MAX_PATH) >= MAX_PATH)
140 {
141 WLog_ERR(TAG, "hostname exceeds length limits");
142 goto fail;
143 }
144
145 certdata = (rdpCertificateData*)calloc(1, sizeof(rdpCertificateData));
146
147 if (!certdata)
148 goto fail;
149
150 certdata->port = port;
151 certdata->hostname = _strdup(hostname);
152 if (!certdata->hostname)
153 goto fail;
154 ensure_lowercase(certdata->hostname, strlen(certdata->hostname));
155
156 certdata->cert = xcert;
157 if (!freerdp_certificate_data_load_cache(certdata))
158 {
159 certdata->cert = NULL;
160 goto fail;
161 }
162
163 return certdata;
164fail:
165 freerdp_certificate_data_free(certdata);
166 return NULL;
167}
168
169rdpCertificateData* freerdp_certificate_data_new(const char* hostname, UINT16 port,
170 const rdpCertificate* xcert)
171{
172 rdpCertificate* copy = freerdp_certificate_clone(xcert);
173 rdpCertificateData* data = freerdp_certificate_data_new_nocopy(hostname, port, copy);
174 if (!data)
175 freerdp_certificate_free(copy);
176 return data;
177}
178
179rdpCertificateData* freerdp_certificate_data_new_from_pem(const char* hostname, UINT16 port,
180 const char* pem, size_t length)
181{
182 if (!pem || (length == 0))
183 return NULL;
184
185 rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
186 rdpCertificateData* data = freerdp_certificate_data_new_nocopy(hostname, port, cert);
187 if (!data)
188 freerdp_certificate_free(cert);
189 return data;
190}
191
192rdpCertificateData* freerdp_certificate_data_new_from_file(const char* hostname, UINT16 port,
193 const char* file)
194{
195 if (!file)
196 return NULL;
197
198 rdpCertificate* cert = freerdp_certificate_new_from_file(file);
199 rdpCertificateData* data = freerdp_certificate_data_new_nocopy(hostname, port, cert);
200 if (!data)
201 freerdp_certificate_free(cert);
202 return data;
203}
204
205void freerdp_certificate_data_free(rdpCertificateData* data)
206{
207 if (data == NULL)
208 return;
209
210 free(data->hostname);
211 freerdp_certificate_free(data->cert);
212 free(data->cached_subject);
213 free(data->cached_issuer);
214 free(data->cached_fingerprint);
215 free(data->cached_pem);
216 free(data->cached_pem_chain);
217
218 free(data);
219}
220
221const char* freerdp_certificate_data_get_host(const rdpCertificateData* cert)
222{
223 if (!cert)
224 return NULL;
225 return cert->hostname;
226}
227
228UINT16 freerdp_certificate_data_get_port(const rdpCertificateData* cert)
229{
230 if (!cert)
231 return 0;
232 return cert->port;
233}
234
235const char* freerdp_certificate_data_get_pem(const rdpCertificateData* cert)
236{
237 return freerdp_certificate_data_get_pem_ex(cert, TRUE);
238}
239
240const char* freerdp_certificate_data_get_pem_ex(const rdpCertificateData* cert, BOOL withFullChain)
241{
242 if (!cert)
243 return NULL;
244 if (withFullChain)
245 return cert->cached_pem_chain;
246 return cert->cached_pem;
247}
248
249const char* freerdp_certificate_data_get_subject(const rdpCertificateData* cert)
250{
251 if (!cert)
252 return NULL;
253
254 return cert->cached_subject;
255}
256
257const char* freerdp_certificate_data_get_issuer(const rdpCertificateData* cert)
258{
259 if (!cert)
260 return NULL;
261
262 return cert->cached_issuer;
263}
264const char* freerdp_certificate_data_get_fingerprint(const rdpCertificateData* cert)
265{
266 if (!cert)
267 return NULL;
268
269 return cert->cached_fingerprint;
270}
271
272BOOL freerdp_certificate_data_equal(const rdpCertificateData* a, const rdpCertificateData* b)
273{
274 BOOL rc = FALSE;
275
276 WINPR_ASSERT(a);
277 WINPR_ASSERT(b);
278
279 if (strcmp(a->hostname, b->hostname) != 0)
280 return FALSE;
281 if (a->port != b->port)
282 return FALSE;
283
284 const char* pem1 = freerdp_certificate_data_get_fingerprint(a);
285 const char* pem2 = freerdp_certificate_data_get_fingerprint(b);
286 if (pem1 && pem2)
287 rc = strcmp(pem1, pem2) == 0;
288 else
289 rc = pem1 == pem2;
290
291 return rc;
292}
293
294const char* freerdp_certificate_data_get_hash(const rdpCertificateData* cert)
295{
296 if (!cert)
297 return NULL;
298
299 return cert->cached_hash;
300}
301
302char* freerdp_certificate_data_hash(const char* hostname, UINT16 port)
303{
304 char name[MAX_PATH + 10] = { 0 };
305 freerdp_certificate_data_hash_(hostname, port, name, sizeof(name));
306 return strndup(name, sizeof(name));
307}