Coverage Report

Created: 2026-02-26 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/crypto/x509_utils.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Cryptographic Abstraction Layer
4
 *
5
 * Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2023 Armin Novak <anovak@thincast.com>
7
 * Copyright 2023 Thincast Technologies GmbH
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *   http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
22
#include <openssl/objects.h>
23
#include <openssl/x509v3.h>
24
#include <openssl/pem.h>
25
#include <openssl/rsa.h>
26
#include <openssl/err.h>
27
28
#include <freerdp/config.h>
29
30
#include <winpr/crt.h>
31
#include <winpr/string.h>
32
#include <winpr/assert.h>
33
34
#include <freerdp/log.h>
35
36
#include "x509_utils.h"
37
38
#define TAG FREERDP_TAG("crypto")
39
40
BYTE* x509_utils_get_hash(const X509* xcert, const char* hash, size_t* length)
41
0
{
42
0
  UINT32 fp_len = EVP_MAX_MD_SIZE;
43
0
  BYTE* fp = NULL;
44
0
  const EVP_MD* md = EVP_get_digestbyname(hash);
45
0
  if (!md)
46
0
  {
47
0
    WLog_ERR(TAG, "System does not support %s hash!", hash);
48
0
    return NULL;
49
0
  }
50
0
  if (!xcert || !length)
51
0
  {
52
0
    WLog_ERR(TAG, "Invalid arguments: xcert=%p, length=%p",
53
0
             WINPR_CXX_COMPAT_CAST(const void*, xcert),
54
0
             WINPR_CXX_COMPAT_CAST(const void*, length));
55
0
    return NULL;
56
0
  }
57
58
0
  fp = calloc(fp_len + 1, sizeof(BYTE));
59
0
  if (!fp)
60
0
  {
61
0
    WLog_ERR(TAG, "could not allocate %" PRIu32 " bytes", fp_len);
62
0
    return NULL;
63
0
  }
64
65
0
  if (X509_digest(xcert, md, fp, &fp_len) != 1)
66
0
  {
67
0
    free(fp);
68
0
    WLog_ERR(TAG, "certificate does not have a %s hash!", hash);
69
0
    return NULL;
70
0
  }
71
72
0
  *length = fp_len;
73
0
  return fp;
74
0
}
75
76
static char* crypto_print_name(const X509_NAME* name)
77
0
{
78
0
  char* buffer = NULL;
79
0
  BIO* outBIO = BIO_new(BIO_s_mem());
80
81
0
  if (X509_NAME_print_ex(outBIO, name, 0, XN_FLAG_ONELINE) > 0)
82
0
  {
83
0
    UINT64 size = BIO_number_written(outBIO);
84
0
    if (size > INT_MAX)
85
0
      goto fail;
86
0
    buffer = calloc(1, (size_t)size + 1);
87
88
0
    if (!buffer)
89
0
      goto fail;
90
91
0
    ERR_clear_error();
92
0
    const int rc = BIO_read(outBIO, buffer, (int)size);
93
0
    if (rc <= 0)
94
0
    {
95
0
      free(buffer);
96
0
      buffer = NULL;
97
0
      goto fail;
98
0
    }
99
0
  }
100
101
0
fail:
102
0
  BIO_free_all(outBIO);
103
0
  return buffer;
104
0
}
105
106
char* x509_utils_get_subject(const X509* xcert)
107
0
{
108
0
  char* subject = NULL;
109
0
  if (!xcert)
110
0
  {
111
0
    WLog_ERR(TAG, "Invalid certificate NULL");
112
0
    return NULL;
113
0
  }
114
0
  subject = crypto_print_name(X509_get_subject_name(xcert));
115
0
  if (!subject)
116
0
    WLog_WARN(TAG, "certificate does not have a subject!");
117
0
  return subject;
118
0
}
119
120
/* GENERAL_NAME type labels */
121
122
static const char* general_name_type_labels[] = { "OTHERNAME", "EMAIL    ", "DNS      ",
123
                                                "X400     ", "DIRNAME  ", "EDIPARTY ",
124
                                                "URI      ", "IPADD    ", "RID      " };
125
126
static const char* general_name_type_label(int general_name_type)
127
0
{
128
0
  if ((0 <= general_name_type) &&
129
0
      ((size_t)general_name_type < ARRAYSIZE(general_name_type_labels)))
130
0
  {
131
0
    return general_name_type_labels[general_name_type];
132
0
  }
133
0
  else
134
0
  {
135
0
    static char buffer[80] = WINPR_C_ARRAY_INIT;
136
0
    (void)snprintf(buffer, sizeof(buffer), "Unknown general name type (%d)", general_name_type);
137
0
    return buffer;
138
0
  }
139
0
}
140
141
/*
142
143
map_subject_alt_name(x509,  general_name_type, mapper, data)
144
145
Call the function mapper with subjectAltNames found in the x509
146
certificate and data.  if generate_name_type is GEN_ALL,  the the
147
mapper is called for all the names,  else it's called only for names
148
of the given type.
149
150
151
We implement two extractors:
152
153
 -  a string extractor that can be used to get the subjectAltNames of
154
    the following types: GEN_URI,  GEN_DNS,  GEN_EMAIL
155
156
 - a ASN1_OBJECT filter/extractor that can be used to get the
157
   subjectAltNames of OTHERNAME type.
158
159
   Note: usually, it's a string, but some type of otherNames can be
160
   associated with different classes of objects. eg. a KPN may be a
161
   sequence of realm and principal name, instead of a single string
162
   object.
163
164
Not implemented yet: extractors for the types: GEN_X400, GEN_DIRNAME,
165
GEN_EDIPARTY, GEN_RID, GEN_IPADD (the later can contain nul-bytes).
166
167
168
mapper(name, data, index, count)
169
170
The mapper is passed:
171
 - the GENERAL_NAME selected,
172
 - the data,
173
 - the index of the general name in the subjectAltNames,
174
 - the total number of names in the subjectAltNames.
175
176
The last parameter let's the mapper allocate arrays to collect objects.
177
Note: if names are filtered,  not all the indices from 0 to count-1 are
178
passed to mapper,  only the indices selected.
179
180
When the mapper returns 0, map_subject_alt_name stops the iteration immediately.
181
182
*/
183
184
0
#define GEN_ALL (-1)
185
186
typedef int (*general_name_mapper_pr)(GENERAL_NAME* name, void* data, int index, int count);
187
188
static void map_subject_alt_name(const X509* x509, int general_name_type,
189
                                 general_name_mapper_pr mapper, void* data)
190
0
{
191
0
  int num = 0;
192
0
  STACK_OF(GENERAL_NAME)* gens = NULL;
193
0
  gens = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL);
194
195
0
  if (!gens)
196
0
  {
197
0
    return;
198
0
  }
199
200
0
  num = sk_GENERAL_NAME_num(gens);
201
202
0
  for (int i = 0; (i < num); i++)
203
0
  {
204
0
    GENERAL_NAME* name = sk_GENERAL_NAME_value(gens, i);
205
206
0
    if (name)
207
0
    {
208
0
      if ((general_name_type == GEN_ALL) || (general_name_type == name->type))
209
0
      {
210
0
        if (!mapper(name, data, i, num))
211
0
        {
212
0
          break;
213
0
        }
214
0
      }
215
0
    }
216
0
  }
217
218
0
  sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
219
0
}
220
221
/*
222
extract_string  --  string extractor
223
224
- the strings array is allocated lazily, when we first have to store a
225
  string.
226
227
- allocated contains the size of the strings array, or -1 if
228
  allocation failed.
229
230
- count contains the actual count of strings in the strings array.
231
232
- maximum limits the number of strings we can store in the strings
233
  array: beyond, the extractor returns 0 to short-cut the search.
234
235
extract_string stores in the string list OPENSSL strings,
236
that must be freed with OPENSSL_free.
237
238
*/
239
240
typedef struct string_list
241
{
242
  char** strings;
243
  size_t allocated;
244
  size_t count;
245
  size_t maximum;
246
} string_list;
247
248
static void string_list_initialize(string_list* list)
249
0
{
250
0
  list->strings = 0;
251
0
  list->allocated = 0;
252
0
  list->count = 0;
253
0
  list->maximum = INT_MAX;
254
0
}
255
256
static void string_list_allocate(string_list* list, size_t allocate_count)
257
0
{
258
0
  if (!list->strings && list->allocated == 0)
259
0
  {
260
0
    list->strings = (char**)calloc(allocate_count, sizeof(char*));
261
0
    list->allocated = list->strings ? allocate_count : 0;
262
0
    list->count = 0;
263
0
  }
264
0
}
265
266
static void string_list_free(string_list* list)
267
0
{
268
  /* Note: we don't free the contents of the strings array: this */
269
  /* is handled by the caller,  either by returning this */
270
  /* content,  or freeing it itself. */
271
0
  free((void*)list->strings);
272
0
}
273
274
static int extract_string(GENERAL_NAME* name, void* data, int index, int count)
275
0
{
276
0
  string_list* list = data;
277
0
  unsigned char* cstring = 0;
278
0
  ASN1_STRING* str = NULL;
279
280
0
  WINPR_UNUSED(index);
281
282
0
  switch (name->type)
283
0
  {
284
0
    case GEN_URI:
285
0
      str = name->d.uniformResourceIdentifier;
286
0
      break;
287
288
0
    case GEN_DNS:
289
0
      str = name->d.dNSName;
290
0
      break;
291
292
0
    case GEN_EMAIL:
293
0
      str = name->d.rfc822Name;
294
0
      break;
295
296
0
    default:
297
0
      return 1;
298
0
  }
299
300
0
  if ((ASN1_STRING_to_UTF8(&cstring, str)) < 0)
301
0
  {
302
0
    WLog_ERR(TAG, "ASN1_STRING_to_UTF8() failed for %s: %s",
303
0
             general_name_type_label(name->type), ERR_error_string(ERR_get_error(), NULL));
304
0
    return 1;
305
0
  }
306
307
0
  string_list_allocate(list, WINPR_ASSERTING_INT_CAST(WINPR_CIPHER_TYPE, count));
308
309
0
  if (list->allocated <= 0)
310
0
  {
311
0
    OPENSSL_free(cstring);
312
0
    return 0;
313
0
  }
314
315
0
  list->strings[list->count] = (char*)cstring;
316
0
  list->count++;
317
318
0
  if (list->count >= list->maximum)
319
0
  {
320
0
    return 0;
321
0
  }
322
323
0
  return 1;
324
0
}
325
326
/*
327
extract_othername_object --  object extractor.
328
329
- the objects array is allocated lazily, when we first have to store a
330
  string.
331
332
- allocated contains the size of the objects array, or -1 if
333
  allocation failed.
334
335
- count contains the actual count of objects in the objects array.
336
337
- maximum limits the number of objects we can store in the objects
338
  array: beyond, the extractor returns 0 to short-cut the search.
339
340
extract_othername_objects stores in the objects array ASN1_TYPE *
341
pointers directly obtained from the GENERAL_NAME.
342
*/
343
344
typedef struct object_list
345
{
346
  ASN1_OBJECT* type_id;
347
  char** strings;
348
  size_t allocated;
349
  size_t count;
350
  size_t maximum;
351
} object_list;
352
353
static void object_list_initialize(object_list* list)
354
0
{
355
0
  list->type_id = 0;
356
0
  list->strings = 0;
357
0
  list->allocated = 0;
358
0
  list->count = 0;
359
0
  list->maximum = INT_MAX;
360
0
}
361
362
static void object_list_allocate(object_list* list, size_t allocate_count)
363
0
{
364
0
  if (!list->strings && (list->allocated == 0) && (allocate_count > 0))
365
0
  {
366
0
    list->strings = (char**)calloc(allocate_count, sizeof(list->strings[0]));
367
0
    list->allocated = list->strings ? allocate_count : 0;
368
0
    list->count = 0;
369
0
  }
370
0
}
371
372
static char* object_string(ASN1_TYPE* object)
373
0
{
374
0
  char* result = NULL;
375
0
  unsigned char* utf8String = NULL;
376
377
  /* TODO: check that object.type is a string type. */
378
0
  const int length = ASN1_STRING_to_UTF8(&utf8String, object->value.asn1_string);
379
380
0
  if (length < 0)
381
0
  {
382
0
    return 0;
383
0
  }
384
385
0
  result = strndup((char*)utf8String, WINPR_ASSERTING_INT_CAST(size_t, length));
386
0
  OPENSSL_free(utf8String);
387
0
  return result;
388
0
}
389
390
static void object_list_free(object_list* list)
391
0
{
392
0
  WINPR_ASSERT(list);
393
0
  free((void*)list->strings);
394
0
}
395
396
static int extract_othername_object_as_string(GENERAL_NAME* name, void* data, int index, int count)
397
0
{
398
0
  object_list* list = data;
399
0
  WINPR_UNUSED(index);
400
401
0
  if (count < 0)
402
0
    return -1;
403
404
0
  if (name->type != GEN_OTHERNAME)
405
0
  {
406
0
    return 1;
407
0
  }
408
409
0
  if (0 != OBJ_cmp(name->d.otherName->type_id, list->type_id))
410
0
  {
411
0
    return 1;
412
0
  }
413
414
0
  object_list_allocate(list, WINPR_ASSERTING_INT_CAST(size_t, count));
415
416
0
  if (list->allocated <= 0)
417
0
  {
418
0
    return 0;
419
0
  }
420
421
0
  list->strings[list->count] = object_string(name->d.otherName->value);
422
423
0
  if (list->strings[list->count])
424
0
  {
425
0
    list->count++;
426
0
  }
427
428
0
  if (list->count >= list->maximum)
429
0
  {
430
0
    return 0;
431
0
  }
432
433
0
  return 1;
434
0
}
435
436
char* x509_utils_get_email(const X509* x509)
437
0
{
438
0
  char* result = 0;
439
0
  string_list list;
440
0
  string_list_initialize(&list);
441
0
  list.maximum = 1;
442
0
  map_subject_alt_name(x509, GEN_EMAIL, extract_string, &list);
443
444
0
  if (list.count == 0)
445
0
  {
446
0
    string_list_free(&list);
447
0
    return 0;
448
0
  }
449
450
0
  result = _strdup(list.strings[0]);
451
0
  OPENSSL_free(list.strings[0]);
452
0
  string_list_free(&list);
453
0
  return result;
454
0
}
455
456
char* x509_utils_get_upn(const X509* x509)
457
0
{
458
0
  char* result = 0;
459
0
  object_list list = WINPR_C_ARRAY_INIT;
460
0
  object_list_initialize(&list);
461
0
  list.type_id = OBJ_nid2obj(NID_ms_upn);
462
0
  list.maximum = 1;
463
0
  map_subject_alt_name(x509, GEN_OTHERNAME, extract_othername_object_as_string, &list);
464
465
0
  if (list.count == 0)
466
0
  {
467
0
    object_list_free(&list);
468
0
    return 0;
469
0
  }
470
471
0
  result = list.strings[0];
472
0
  object_list_free(&list);
473
0
  return result;
474
0
}
475
476
char* x509_utils_get_date(const X509* x509, BOOL startDate)
477
0
{
478
0
  WINPR_ASSERT(x509);
479
480
0
  const ASN1_TIME* date = startDate ? X509_get0_notBefore(x509) : X509_get0_notAfter(x509);
481
0
  if (!date)
482
0
    return NULL;
483
484
0
  BIO* bmem = BIO_new(BIO_s_mem());
485
0
  if (!bmem)
486
0
    return NULL;
487
488
0
  char* str = NULL;
489
0
  if (ASN1_TIME_print(bmem, date))
490
0
  {
491
0
    BUF_MEM* bptr = NULL;
492
493
0
    BIO_get_mem_ptr(bmem, &bptr);
494
0
    str = strndup(bptr->data, bptr->length);
495
0
  }
496
0
  else
497
0
  { // Log error
498
0
  }
499
0
  BIO_free_all(bmem);
500
0
  return str;
501
0
}
502
503
void x509_utils_dns_names_free(size_t count, size_t* lengths, char** dns_names)
504
0
{
505
0
  free(lengths);
506
507
0
  if (dns_names)
508
0
  {
509
0
    for (size_t i = 0; i < count; i++)
510
0
    {
511
0
      if (dns_names[i])
512
0
      {
513
0
        OPENSSL_free(dns_names[i]);
514
0
      }
515
0
    }
516
517
0
    free((void*)dns_names);
518
0
  }
519
0
}
520
521
char** x509_utils_get_dns_names(const X509* x509, size_t* count, size_t** lengths)
522
0
{
523
0
  char** result = 0;
524
0
  string_list list = WINPR_C_ARRAY_INIT;
525
0
  string_list_initialize(&list);
526
0
  map_subject_alt_name(x509, GEN_DNS, extract_string, &list);
527
0
  (*count) = list.count;
528
529
0
  if (list.count <= 0)
530
0
  {
531
0
    string_list_free(&list);
532
0
    return NULL;
533
0
  }
534
535
  /* lengths are not useful,  since we converted the
536
     strings to utf-8,  there cannot be nul-bytes in them. */
537
0
  result = (char**)calloc(list.count, sizeof(*result));
538
0
  (*lengths) = calloc(list.count, sizeof(**lengths));
539
540
0
  if (!result || !(*lengths))
541
0
  {
542
0
    string_list_free(&list);
543
0
    free((void*)result);
544
0
    free(*lengths);
545
0
    (*lengths) = 0;
546
0
    (*count) = 0;
547
0
    return NULL;
548
0
  }
549
550
0
  for (size_t i = 0; i < list.count; i++)
551
0
  {
552
0
    result[i] = list.strings[i];
553
0
    (*lengths)[i] = strlen(result[i]);
554
0
  }
555
556
0
  string_list_free(&list);
557
0
  return result;
558
0
}
559
560
char* x509_utils_get_issuer(const X509* xcert)
561
0
{
562
0
  char* issuer = NULL;
563
0
  if (!xcert)
564
0
  {
565
0
    WLog_ERR(TAG, "Invalid certificate NULL");
566
0
    return NULL;
567
0
  }
568
0
  issuer = crypto_print_name(X509_get_issuer_name(xcert));
569
0
  if (!issuer)
570
0
    WLog_WARN(TAG, "certificate does not have an issuer!");
571
0
  return issuer;
572
0
}
573
574
static int asn1_object_cmp(const ASN1_OBJECT* const* a, const ASN1_OBJECT* const* b)
575
0
{
576
0
  if (!a || !b)
577
0
    return (a == b) ? 0 : (a ? 1 : -1);
578
579
0
  if (!*a || !*b)
580
0
    return (*a == *b) ? 0 : (*a ? 1 : -1);
581
582
0
  return OBJ_cmp(*a, *b);
583
0
}
584
585
BOOL x509_utils_check_eku(const X509* xcert, int nid)
586
0
{
587
0
  BOOL ret = FALSE;
588
0
  STACK_OF(ASN1_OBJECT)* oid_stack = NULL;
589
0
  ASN1_OBJECT* oid = NULL;
590
591
0
  if (!xcert)
592
0
    return FALSE;
593
594
0
  oid = OBJ_nid2obj(nid);
595
0
  if (!oid)
596
0
    return FALSE;
597
598
0
  oid_stack = X509_get_ext_d2i(xcert, NID_ext_key_usage, NULL, NULL);
599
0
  if (!oid_stack)
600
0
    return FALSE;
601
602
0
  sk_ASN1_OBJECT_set_cmp_func(oid_stack, asn1_object_cmp);
603
0
  if (sk_ASN1_OBJECT_find(oid_stack, oid) >= 0)
604
0
    ret = TRUE;
605
606
0
  sk_ASN1_OBJECT_pop_free(oid_stack, ASN1_OBJECT_free);
607
0
  return ret;
608
0
}
609
610
void x509_utils_print_info(const X509* xcert)
611
0
{
612
0
  char* fp = NULL;
613
0
  char* issuer = NULL;
614
0
  char* subject = NULL;
615
0
  subject = x509_utils_get_subject(xcert);
616
0
  issuer = x509_utils_get_issuer(xcert);
617
0
  fp = (char*)x509_utils_get_hash(xcert, "sha256", NULL);
618
619
0
  if (!fp)
620
0
  {
621
0
    WLog_ERR(TAG, "error computing fingerprint");
622
0
    goto out_free_issuer;
623
0
  }
624
625
0
  WLog_INFO(TAG, "Certificate details:");
626
0
  WLog_INFO(TAG, "\tSubject: %s", subject);
627
0
  WLog_INFO(TAG, "\tIssuer: %s", issuer);
628
0
  WLog_INFO(TAG, "\tThumbprint: %s", fp);
629
0
  WLog_INFO(TAG,
630
0
            "The above X.509 certificate could not be verified, possibly because you do not have "
631
0
            "the CA certificate in your certificate store, or the certificate has expired. "
632
0
            "Please look at the OpenSSL documentation on how to add a private CA to the store.");
633
0
  free(fp);
634
0
out_free_issuer:
635
0
  free(issuer);
636
0
  free(subject);
637
0
}
638
639
X509* x509_utils_from_pem(const char* data, size_t len, BOOL fromFile)
640
0
{
641
0
  X509* x509 = NULL;
642
0
  BIO* bio = NULL;
643
0
  if (fromFile)
644
0
    bio = BIO_new_file(data, "rb");
645
0
  else
646
0
  {
647
0
    if (len > INT_MAX)
648
0
      return NULL;
649
650
0
    bio = BIO_new_mem_buf(data, (int)len);
651
0
  }
652
653
0
  if (!bio)
654
0
  {
655
0
    WLog_ERR(TAG, "BIO_new failed for certificate");
656
0
    return NULL;
657
0
  }
658
659
0
  x509 = PEM_read_bio_X509(bio, NULL, NULL, 0);
660
0
  BIO_free_all(bio);
661
0
  if (!x509)
662
0
    WLog_ERR(TAG, "PEM_read_bio_X509 returned NULL [input length %" PRIuz "]", len);
663
664
0
  return x509;
665
0
}
666
667
static WINPR_MD_TYPE hash_nid_to_winpr(int hash_nid)
668
0
{
669
0
  switch (hash_nid)
670
0
  {
671
0
    case NID_md2:
672
0
      return WINPR_MD_MD2;
673
0
    case NID_md4:
674
0
      return WINPR_MD_MD4;
675
0
    case NID_md5:
676
0
      return WINPR_MD_MD5;
677
0
    case NID_sha1:
678
0
      return WINPR_MD_SHA1;
679
0
    case NID_sha224:
680
0
      return WINPR_MD_SHA224;
681
0
    case NID_sha256:
682
0
      return WINPR_MD_SHA256;
683
0
    case NID_sha384:
684
0
      return WINPR_MD_SHA384;
685
0
    case NID_sha512:
686
0
      return WINPR_MD_SHA512;
687
0
    case NID_ripemd160:
688
0
      return WINPR_MD_RIPEMD160;
689
0
#if (OPENSSL_VERSION_NUMBER >= 0x1010101fL) && !defined(LIBRESSL_VERSION_NUMBER)
690
0
    case NID_sha3_224:
691
0
      return WINPR_MD_SHA3_224;
692
0
    case NID_sha3_256:
693
0
      return WINPR_MD_SHA3_256;
694
0
    case NID_sha3_384:
695
0
      return WINPR_MD_SHA3_384;
696
0
    case NID_sha3_512:
697
0
      return WINPR_MD_SHA3_512;
698
0
    case NID_shake128:
699
0
      return WINPR_MD_SHAKE128;
700
0
    case NID_shake256:
701
0
      return WINPR_MD_SHAKE256;
702
0
#endif
703
0
    case NID_undef:
704
0
    default:
705
0
      return WINPR_MD_NONE;
706
0
  }
707
0
}
708
709
static WINPR_MD_TYPE get_rsa_pss_digest(const X509_ALGOR* alg)
710
0
{
711
0
  WINPR_MD_TYPE ret = WINPR_MD_NONE;
712
0
  WINPR_MD_TYPE message_digest = WINPR_MD_NONE;
713
0
  WINPR_MD_TYPE mgf1_digest = WINPR_MD_NONE;
714
0
  int param_type = 0;
715
0
  const void* param_value = NULL;
716
0
  const ASN1_STRING* sequence = NULL;
717
0
  const unsigned char* inp = NULL;
718
0
  RSA_PSS_PARAMS* params = NULL;
719
0
  X509_ALGOR* mgf1_digest_alg = NULL;
720
721
  /* The RSA-PSS digest is encoded in a complex structure, defined in
722
  https://www.rfc-editor.org/rfc/rfc4055.html. */
723
0
  X509_ALGOR_get0(NULL, &param_type, &param_value, alg);
724
725
  /* param_type and param_value the parameter in ASN1_TYPE form, but split into two parameters. A
726
  SEQUENCE is has type V_ASN1_SEQUENCE, and the value is an ASN1_STRING with the encoded
727
  structure. */
728
0
  if (param_type != V_ASN1_SEQUENCE)
729
0
    goto end;
730
0
  sequence = param_value;
731
732
  /* Decode the structure. */
733
0
  inp = ASN1_STRING_get0_data(sequence);
734
0
  params = d2i_RSA_PSS_PARAMS(NULL, &inp, ASN1_STRING_length(sequence));
735
0
  if (params == NULL)
736
0
    goto end;
737
738
  /* RSA-PSS uses two hash algorithms, a message digest and also an MGF function which is, itself,
739
  parameterized by a hash function. Both fields default to SHA-1, so we must also check for the
740
  value being NULL. */
741
0
  message_digest = WINPR_MD_SHA1;
742
0
  if (params->hashAlgorithm != NULL)
743
0
  {
744
0
    const ASN1_OBJECT* obj = NULL;
745
0
    X509_ALGOR_get0(&obj, NULL, NULL, params->hashAlgorithm);
746
0
    message_digest = hash_nid_to_winpr(OBJ_obj2nid(obj));
747
0
    if (message_digest == WINPR_MD_NONE)
748
0
      goto end;
749
0
  }
750
751
0
  mgf1_digest = WINPR_MD_SHA1;
752
0
  if (params->maskGenAlgorithm != NULL)
753
0
  {
754
0
    const ASN1_OBJECT* obj = NULL;
755
0
    int mgf_param_type = 0;
756
0
    const void* mgf_param_value = NULL;
757
0
    const ASN1_STRING* mgf_param_sequence = NULL;
758
    /* First, check this is MGF-1, the only one ever defined. */
759
0
    X509_ALGOR_get0(&obj, &mgf_param_type, &mgf_param_value, params->maskGenAlgorithm);
760
0
    if (OBJ_obj2nid(obj) != NID_mgf1)
761
0
      goto end;
762
763
    /* MGF-1 is, itself, parameterized by a hash function, encoded as an AlgorithmIdentifier. */
764
0
    if (mgf_param_type != V_ASN1_SEQUENCE)
765
0
      goto end;
766
0
    mgf_param_sequence = mgf_param_value;
767
0
    inp = ASN1_STRING_get0_data(mgf_param_sequence);
768
0
    mgf1_digest_alg = d2i_X509_ALGOR(NULL, &inp, ASN1_STRING_length(mgf_param_sequence));
769
0
    if (mgf1_digest_alg == NULL)
770
0
      goto end;
771
772
    /* Finally, extract the digest. */
773
0
    X509_ALGOR_get0(&obj, NULL, NULL, mgf1_digest_alg);
774
0
    mgf1_digest = hash_nid_to_winpr(OBJ_obj2nid(obj));
775
0
    if (mgf1_digest == WINPR_MD_NONE)
776
0
      goto end;
777
0
  }
778
779
  /* If the two digests do not match, it is ambiguous which to return. tls-server-end-point leaves
780
  it undefined, so return none.
781
  https://www.rfc-editor.org/rfc/rfc5929.html#section-4.1 */
782
0
  if (message_digest != mgf1_digest)
783
0
    goto end;
784
0
  ret = message_digest;
785
786
0
end:
787
0
  RSA_PSS_PARAMS_free(params);
788
0
  X509_ALGOR_free(mgf1_digest_alg);
789
0
  return ret;
790
0
}
791
792
WINPR_MD_TYPE x509_utils_get_signature_alg(const X509* xcert)
793
0
{
794
0
  WINPR_ASSERT(xcert);
795
796
0
  const int nid = X509_get_signature_nid(xcert);
797
798
0
  if (nid == NID_rsassaPss)
799
0
  {
800
0
    const X509_ALGOR* alg = NULL;
801
0
    X509_get0_signature(NULL, &alg, xcert);
802
0
    return get_rsa_pss_digest(alg);
803
0
  }
804
805
0
  int hash_nid = 0;
806
0
  if (OBJ_find_sigid_algs(nid, &hash_nid, NULL) != 1)
807
0
    return WINPR_MD_NONE;
808
809
0
  return hash_nid_to_winpr(hash_nid);
810
0
}
811
812
char* x509_utils_get_common_name(const X509* xcert, size_t* plength)
813
0
{
814
0
  X509_NAME* subject_name = X509_get_subject_name(xcert);
815
0
  if (subject_name == NULL)
816
0
    return NULL;
817
818
0
  const int index = X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1);
819
0
  if (index < 0)
820
0
    return NULL;
821
822
0
  const X509_NAME_ENTRY* entry = X509_NAME_get_entry(subject_name, index);
823
0
  if (entry == NULL)
824
0
    return NULL;
825
826
0
  const ASN1_STRING* entry_data = X509_NAME_ENTRY_get_data(entry);
827
0
  if (entry_data == NULL)
828
0
    return NULL;
829
830
0
  BYTE* common_name_raw = NULL;
831
0
  const int length = ASN1_STRING_to_UTF8(&common_name_raw, entry_data);
832
0
  if (length < 0)
833
0
    return NULL;
834
835
0
  if (plength)
836
0
    *plength = (size_t)length;
837
838
0
  char* common_name = _strdup((char*)common_name_raw);
839
0
  OPENSSL_free(common_name_raw);
840
0
  return common_name;
841
0
}
842
843
static int verify_cb(int ok, X509_STORE_CTX* csc)
844
0
{
845
0
  if (ok != 1)
846
0
  {
847
0
    WINPR_ASSERT(csc);
848
0
    int err = X509_STORE_CTX_get_error(csc);
849
0
    int derr = X509_STORE_CTX_get_error_depth(csc);
850
0
    X509* where = X509_STORE_CTX_get_current_cert(csc);
851
0
    const char* what = X509_verify_cert_error_string(err);
852
0
    char* name = x509_utils_get_subject(where);
853
854
0
    WLog_WARN(TAG, "Certificate verification failure '%s (%d)' at stack position %d", what, err,
855
0
              derr);
856
0
    WLog_WARN(TAG, "%s", name);
857
858
0
    free(name);
859
0
  }
860
0
  return ok;
861
0
}
862
863
BOOL x509_utils_verify(X509* xcert, STACK_OF(X509) * chain, const char* certificate_store_path)
864
0
{
865
0
  const int purposes[3] = { X509_PURPOSE_SSL_SERVER, X509_PURPOSE_SSL_CLIENT, X509_PURPOSE_ANY };
866
0
  X509_STORE_CTX* csc = NULL;
867
0
  BOOL status = FALSE;
868
0
  X509_LOOKUP* lookup = NULL;
869
870
0
  if (!xcert)
871
0
    return FALSE;
872
873
0
  X509_STORE* cert_ctx = X509_STORE_new();
874
875
0
  if (cert_ctx == NULL)
876
0
    goto end;
877
878
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
879
  OpenSSL_add_all_algorithms();
880
#else
881
0
  OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS |
882
0
                          OPENSSL_INIT_LOAD_CONFIG,
883
0
                      NULL);
884
0
#endif
885
886
0
  if (X509_STORE_set_default_paths(cert_ctx) != 1)
887
0
    goto end;
888
889
0
  lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_hash_dir());
890
891
0
  if (lookup == NULL)
892
0
    goto end;
893
894
0
  X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
895
896
0
  if (certificate_store_path != NULL)
897
0
  {
898
0
    X509_LOOKUP_add_dir(lookup, certificate_store_path, X509_FILETYPE_PEM);
899
0
  }
900
901
0
  X509_STORE_set_flags(cert_ctx, 0);
902
903
0
  for (size_t i = 0; i < ARRAYSIZE(purposes); i++)
904
0
  {
905
0
    int err = -1;
906
0
    int rc = -1;
907
0
    int purpose = purposes[i];
908
0
    csc = X509_STORE_CTX_new();
909
910
0
    if (csc == NULL)
911
0
      goto skip;
912
0
    if (!X509_STORE_CTX_init(csc, cert_ctx, xcert, chain))
913
0
      goto skip;
914
915
0
    X509_STORE_CTX_set_purpose(csc, purpose);
916
0
    X509_STORE_CTX_set_verify_cb(csc, verify_cb);
917
918
0
    rc = X509_verify_cert(csc);
919
0
    err = X509_STORE_CTX_get_error(csc);
920
0
  skip:
921
0
    X509_STORE_CTX_free(csc);
922
0
    if (rc == 1)
923
0
    {
924
0
      status = TRUE;
925
0
      break;
926
0
    }
927
0
    else if (err != X509_V_ERR_INVALID_PURPOSE)
928
0
      break;
929
0
  }
930
931
0
  X509_STORE_free(cert_ctx);
932
0
end:
933
0
  return status;
934
0
}