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