Coverage Report

Created: 2024-09-08 06:16

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