Coverage Report

Created: 2026-01-09 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/crypto/certificate_data.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Certificate Handling
4
 *
5
 * Copyright 2011 Jiten Pathy
6
 * Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
7
 * Copyright 2023 Armin Novak <anovak@thincast.com>
8
 * Copyright 2023 Thincast Technologies GmbH
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 *     http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22
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
39
struct 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. */
56
static char* ensure_lowercase(char* str, size_t length)
57
0
{
58
0
  const size_t len = strnlen(str, length);
59
0
  for (size_t x = 0; x < len; x++)
60
0
    str[x] = (char)tolower(str[x]);
61
0
  return str;
62
0
}
63
64
static char* ensure_valid_charset(char* str, size_t length)
65
0
{
66
0
  const size_t len = strnlen(str, length);
67
0
  for (size_t x = 0; x < len; x++)
68
0
  {
69
0
    char cur = str[x];
70
0
    switch (cur)
71
0
    {
72
0
      case ':':
73
0
        str[x] = '.';
74
0
        break;
75
0
      case '/':
76
0
      case '\\':
77
0
        str[x] = '_';
78
0
        break;
79
0
      default:
80
0
        break;
81
0
    }
82
0
  }
83
0
  return str;
84
0
}
85
86
static const char* freerdp_certificate_data_hash_(const char* hostname, UINT16 port, char* name,
87
                                                  size_t length)
88
0
{
89
0
  (void)_snprintf(name, length, "%s_%" PRIu16 ".pem", hostname, port);
90
0
  return ensure_lowercase(ensure_valid_charset(name, length), length);
91
0
}
92
93
static BOOL freerdp_certificate_data_load_cache(rdpCertificateData* data)
94
0
{
95
0
  BOOL rc = FALSE;
96
97
0
  WINPR_ASSERT(data);
98
99
0
  freerdp_certificate_data_hash_(data->hostname, data->port, data->cached_hash,
100
0
                                 sizeof(data->cached_hash) - 1);
101
0
  const size_t len = strnlen(data->cached_hash, sizeof(data->cached_hash));
102
0
  if ((len == 0) || (len >= sizeof(data->cached_hash)))
103
0
    goto fail;
104
105
0
  data->cached_subject = freerdp_certificate_get_subject(data->cert);
106
0
  if (!data->cached_subject)
107
0
    data->cached_subject = calloc(1, 1);
108
109
0
  {
110
0
    size_t pemlen = 0;
111
0
    data->cached_pem = freerdp_certificate_get_pem_ex(data->cert, &pemlen, FALSE);
112
0
  }
113
0
  if (!data->cached_pem)
114
0
    goto fail;
115
116
0
  {
117
0
    size_t pemchainlen = 0;
118
0
    data->cached_pem_chain = freerdp_certificate_get_pem_ex(data->cert, &pemchainlen, TRUE);
119
0
    if (!data->cached_pem_chain)
120
0
      goto fail;
121
0
  }
122
123
0
  data->cached_fingerprint = freerdp_certificate_get_fingerprint(data->cert);
124
0
  if (!data->cached_fingerprint)
125
0
    goto fail;
126
127
0
  data->cached_issuer = freerdp_certificate_get_issuer(data->cert);
128
0
  if (!data->cached_issuer)
129
0
    data->cached_issuer = calloc(1, 1);
130
131
0
  rc = TRUE;
132
0
fail:
133
0
  return rc;
134
0
}
135
136
static rdpCertificateData* freerdp_certificate_data_new_nocopy(const char* hostname, UINT16 port,
137
                                                               rdpCertificate* xcert)
138
0
{
139
0
  rdpCertificateData* certdata = NULL;
140
141
0
  if (!hostname || !xcert)
142
0
    goto fail;
143
0
  if (strnlen(hostname, MAX_PATH) >= MAX_PATH)
144
0
  {
145
0
    WLog_ERR(TAG, "hostname exceeds length limits");
146
0
    goto fail;
147
0
  }
148
149
0
  certdata = (rdpCertificateData*)calloc(1, sizeof(rdpCertificateData));
150
151
0
  if (!certdata)
152
0
    goto fail;
153
154
0
  certdata->port = port;
155
0
  certdata->hostname = _strdup(hostname);
156
0
  if (!certdata->hostname)
157
0
    goto fail;
158
0
  ensure_lowercase(certdata->hostname, strlen(certdata->hostname));
159
160
0
  certdata->cert = xcert;
161
0
  if (!freerdp_certificate_data_load_cache(certdata))
162
0
  {
163
0
    certdata->cert = NULL;
164
0
    goto fail;
165
0
  }
166
167
0
  return certdata;
168
0
fail:
169
0
  freerdp_certificate_data_free(certdata);
170
0
  return NULL;
171
0
}
172
173
rdpCertificateData* freerdp_certificate_data_new(const char* hostname, UINT16 port,
174
                                                 const rdpCertificate* xcert)
175
0
{
176
0
  rdpCertificate* copy = freerdp_certificate_clone(xcert);
177
0
  rdpCertificateData* data = freerdp_certificate_data_new_nocopy(hostname, port, copy);
178
0
  if (!data)
179
0
    freerdp_certificate_free(copy);
180
0
  return data;
181
0
}
182
183
rdpCertificateData* freerdp_certificate_data_new_from_pem(const char* hostname, UINT16 port,
184
                                                          const char* pem, size_t length)
185
0
{
186
0
  if (!pem || (length == 0))
187
0
    return NULL;
188
189
0
  rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
190
0
  rdpCertificateData* data = freerdp_certificate_data_new_nocopy(hostname, port, cert);
191
0
  if (!data)
192
0
    freerdp_certificate_free(cert);
193
0
  return data;
194
0
}
195
196
rdpCertificateData* freerdp_certificate_data_new_from_file(const char* hostname, UINT16 port,
197
                                                           const char* file)
198
0
{
199
0
  if (!file)
200
0
    return NULL;
201
202
0
  rdpCertificate* cert = freerdp_certificate_new_from_file(file);
203
0
  rdpCertificateData* data = freerdp_certificate_data_new_nocopy(hostname, port, cert);
204
0
  if (!data)
205
0
    freerdp_certificate_free(cert);
206
0
  return data;
207
0
}
208
209
void freerdp_certificate_data_free(rdpCertificateData* data)
210
0
{
211
0
  if (data == NULL)
212
0
    return;
213
214
0
  free(data->hostname);
215
0
  freerdp_certificate_free(data->cert);
216
0
  free(data->cached_subject);
217
0
  free(data->cached_issuer);
218
0
  free(data->cached_fingerprint);
219
0
  free(data->cached_pem);
220
0
  free(data->cached_pem_chain);
221
222
0
  free(data);
223
0
}
224
225
const char* freerdp_certificate_data_get_host(const rdpCertificateData* cert)
226
0
{
227
0
  if (!cert)
228
0
    return NULL;
229
0
  return cert->hostname;
230
0
}
231
232
UINT16 freerdp_certificate_data_get_port(const rdpCertificateData* cert)
233
0
{
234
0
  if (!cert)
235
0
    return 0;
236
0
  return cert->port;
237
0
}
238
239
const char* freerdp_certificate_data_get_pem(const rdpCertificateData* cert)
240
0
{
241
0
  return freerdp_certificate_data_get_pem_ex(cert, TRUE);
242
0
}
243
244
const char* freerdp_certificate_data_get_pem_ex(const rdpCertificateData* cert, BOOL withFullChain)
245
0
{
246
0
  if (!cert)
247
0
    return NULL;
248
0
  if (withFullChain)
249
0
    return cert->cached_pem_chain;
250
0
  return cert->cached_pem;
251
0
}
252
253
const char* freerdp_certificate_data_get_subject(const rdpCertificateData* cert)
254
0
{
255
0
  if (!cert)
256
0
    return NULL;
257
258
0
  return cert->cached_subject;
259
0
}
260
261
const char* freerdp_certificate_data_get_issuer(const rdpCertificateData* cert)
262
0
{
263
0
  if (!cert)
264
0
    return NULL;
265
266
0
  return cert->cached_issuer;
267
0
}
268
const char* freerdp_certificate_data_get_fingerprint(const rdpCertificateData* cert)
269
0
{
270
0
  if (!cert)
271
0
    return NULL;
272
273
0
  return cert->cached_fingerprint;
274
0
}
275
276
BOOL freerdp_certificate_data_equal(const rdpCertificateData* a, const rdpCertificateData* b)
277
0
{
278
0
  BOOL rc = FALSE;
279
280
0
  WINPR_ASSERT(a);
281
0
  WINPR_ASSERT(b);
282
283
0
  if (strcmp(a->hostname, b->hostname) != 0)
284
0
    return FALSE;
285
0
  if (a->port != b->port)
286
0
    return FALSE;
287
288
0
  const char* pem1 = freerdp_certificate_data_get_fingerprint(a);
289
0
  const char* pem2 = freerdp_certificate_data_get_fingerprint(b);
290
0
  if (pem1 && pem2)
291
0
    rc = strcmp(pem1, pem2) == 0;
292
0
  else
293
0
    rc = pem1 == pem2;
294
295
0
  return rc;
296
0
}
297
298
const char* freerdp_certificate_data_get_hash(const rdpCertificateData* cert)
299
0
{
300
0
  if (!cert)
301
0
    return NULL;
302
303
0
  return cert->cached_hash;
304
0
}
305
306
char* freerdp_certificate_data_hash(const char* hostname, UINT16 port)
307
0
{
308
0
  char name[MAX_PATH + 10] = { 0 };
309
0
  freerdp_certificate_data_hash_(hostname, port, name, sizeof(name));
310
0
  return strndup(name, sizeof(name));
311
0
}