Lines 39-45
Link Here
|
39 |
#include <string.h> |
39 |
#include <string.h> |
40 |
|
40 |
|
41 |
#include <openssl/ssl.h> |
41 |
#include <openssl/ssl.h> |
42 |
#include <openssl/x509.h> |
42 |
#include <openssl/x509v3.h> |
43 |
#include <openssl/err.h> |
43 |
#include <openssl/err.h> |
44 |
#include <openssl/rand.h> |
44 |
#include <openssl/rand.h> |
45 |
|
45 |
|
Lines 486-494
Link Here
|
486 |
ssl_check_certificate (int fd, const char *host) |
486 |
ssl_check_certificate (int fd, const char *host) |
487 |
{ |
487 |
{ |
488 |
X509 *cert; |
488 |
X509 *cert; |
|
|
489 |
GENERAL_NAMES *subjectAltNames; |
489 |
char common_name[256]; |
490 |
char common_name[256]; |
490 |
long vresult; |
491 |
long vresult; |
491 |
bool success = true; |
492 |
bool success = true; |
|
|
493 |
bool alt_name_checked = false; |
492 |
|
494 |
|
493 |
/* If the user has specified --no-check-cert, we still want to warn |
495 |
/* If the user has specified --no-check-cert, we still want to warn |
494 |
him about problems with the server's certificate. */ |
496 |
him about problems with the server's certificate. */ |
Lines 536-542
Link Here
|
536 |
break; |
538 |
break; |
537 |
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: |
539 |
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: |
538 |
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: |
540 |
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: |
539 |
logprintf (LOG_NOTQUIET, _(" Self-signed certificate encountered.\n")); |
541 |
logprintf (LOG_NOTQUIET, |
|
|
542 |
_(" Self-signed certificate encountered.\n")); |
540 |
break; |
543 |
break; |
541 |
case X509_V_ERR_CERT_NOT_YET_VALID: |
544 |
case X509_V_ERR_CERT_NOT_YET_VALID: |
542 |
logprintf (LOG_NOTQUIET, _(" Issued certificate not yet valid.\n")); |
545 |
logprintf (LOG_NOTQUIET, _(" Issued certificate not yet valid.\n")); |
Lines 558-567
Link Here
|
558 |
/* Check that HOST matches the common name in the certificate. |
561 |
/* Check that HOST matches the common name in the certificate. |
559 |
#### The following remains to be done: |
562 |
#### The following remains to be done: |
560 |
|
563 |
|
561 |
- It should use dNSName/ipAddress subjectAltName extensions if |
|
|
562 |
available; according to rfc2818: "If a subjectAltName extension |
563 |
of type dNSName is present, that MUST be used as the identity." |
564 |
|
565 |
- When matching against common names, it should loop over all |
564 |
- When matching against common names, it should loop over all |
566 |
common names and choose the most specific one, i.e. the last |
565 |
common names and choose the most specific one, i.e. the last |
567 |
one, not the first one, which the current code picks. |
566 |
one, not the first one, which the current code picks. |
Lines 569-618
Link Here
|
569 |
- Ensure that ASN1 strings from the certificate are encoded as |
568 |
- Ensure that ASN1 strings from the certificate are encoded as |
570 |
UTF-8 which can be meaningfully compared to HOST. */ |
569 |
UTF-8 which can be meaningfully compared to HOST. */ |
571 |
|
570 |
|
572 |
X509_NAME *xname = X509_get_subject_name(cert); |
571 |
subjectAltNames = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL); |
573 |
common_name[0] = '\0'; |
|
|
574 |
X509_NAME_get_text_by_NID (xname, NID_commonName, common_name, |
575 |
sizeof (common_name)); |
576 |
|
572 |
|
577 |
if (!pattern_match (common_name, host)) |
573 |
if (subjectAltNames) |
578 |
{ |
574 |
{ |
579 |
logprintf (LOG_NOTQUIET, _("\ |
575 |
/* Test subject alternative names */ |
580 |
%s: certificate common name %s doesn't match requested host name %s.\n"), |
576 |
|
581 |
severity, quote_n (0, common_name), quote_n (1, host)); |
577 |
/* Do we want to check for dNSNAmes or ipAddresses (see RFC 2818)? |
582 |
success = false; |
578 |
* Signal it by host_in_octet_string. */ |
|
|
579 |
ASN1_OCTET_STRING *host_in_octet_string = NULL; |
580 |
host_in_octet_string = a2i_IPADDRESS (host); |
581 |
|
582 |
int numaltnames = sk_GENERAL_NAME_num (subjectAltNames); |
583 |
int i; |
584 |
for (i=0; i < numaltnames; i++) |
585 |
{ |
586 |
const GENERAL_NAME *name = |
587 |
sk_GENERAL_NAME_value (subjectAltNames, i); |
588 |
if (name) |
589 |
{ |
590 |
if (host_in_octet_string) |
591 |
{ |
592 |
if (name->type == GEN_IPADD) |
593 |
{ |
594 |
/* Check for ipAddress */ |
595 |
/* TODO: Should we convert between IPv4-mapped IPv6 |
596 |
* addresses and IPv4 addresses? */ |
597 |
alt_name_checked = true; |
598 |
if (!ASN1_STRING_cmp (host_in_octet_string, |
599 |
name->d.iPAddress)) |
600 |
break; |
601 |
} |
602 |
} |
603 |
else if (name->type == GEN_DNS) |
604 |
{ |
605 |
/* Check for dNSName */ |
606 |
alt_name_checked = true; |
607 |
/* dNSName should be IA5String (i.e. ASCII), however who |
608 |
* does trust CA? Convert it into UTF-8 for sure. */ |
609 |
unsigned char *name_in_utf8 = NULL; |
610 |
if (0 <= ASN1_STRING_to_UTF8 (&name_in_utf8, name->d.dNSName)) |
611 |
{ |
612 |
/* Compare and check for NULL attack in ASN1_STRING */ |
613 |
if (pattern_match ((char *)name_in_utf8, host) && |
614 |
(strlen ((char *)name_in_utf8) == |
615 |
ASN1_STRING_length (name->d.dNSName))) |
616 |
{ |
617 |
OPENSSL_free (name_in_utf8); |
618 |
break; |
619 |
} |
620 |
OPENSSL_free (name_in_utf8); |
621 |
} |
622 |
} |
623 |
} |
624 |
} |
625 |
sk_GENERAL_NAME_free (subjectAltNames); |
626 |
if (host_in_octet_string) |
627 |
ASN1_OCTET_STRING_free(host_in_octet_string); |
628 |
|
629 |
if (alt_name_checked == true && i >= numaltnames) |
630 |
{ |
631 |
logprintf (LOG_NOTQUIET, |
632 |
_("%s: no certificate subject alternative name matches\n" |
633 |
"\trequested host name %s.\n"), |
634 |
severity, quote_n (1, host)); |
635 |
success = false; |
636 |
} |
583 |
} |
637 |
} |
584 |
else |
638 |
|
|
|
639 |
if (alt_name_checked == false) |
585 |
{ |
640 |
{ |
586 |
/* We now determine the length of the ASN1 string. If it differs from |
641 |
/* Test commomName */ |
587 |
* common_name's length, then there is a \0 before the string terminates. |
642 |
X509_NAME *xname = X509_get_subject_name(cert); |
588 |
* This can be an instance of a null-prefix attack. |
643 |
common_name[0] = '\0'; |
589 |
* |
644 |
X509_NAME_get_text_by_NID (xname, NID_commonName, common_name, |
590 |
* https://www.blackhat.com/html/bh-usa-09/bh-usa-09-archives.html#Marlinspike |
645 |
sizeof (common_name)); |
591 |
* */ |
646 |
|
592 |
|
647 |
if (!pattern_match (common_name, host)) |
593 |
int i = -1, j; |
648 |
{ |
594 |
X509_NAME_ENTRY *xentry; |
649 |
logprintf (LOG_NOTQUIET, _("\ |
595 |
ASN1_STRING *sdata; |
650 |
%s: certificate common name %s doesn't match requested host name %s.\n"), |
596 |
|
651 |
severity, quote_n (0, common_name), quote_n (1, host)); |
597 |
if (xname) { |
652 |
success = false; |
598 |
for (;;) |
653 |
} |
599 |
{ |
654 |
else |
600 |
j = X509_NAME_get_index_by_NID (xname, NID_commonName, i); |
655 |
{ |
601 |
if (j == -1) break; |
656 |
/* We now determine the length of the ASN1 string. If it |
602 |
i = j; |
657 |
* differs from common_name's length, then there is a \0 |
|
|
658 |
* before the string terminates. This can be an instance of a |
659 |
* null-prefix attack. |
660 |
* |
661 |
* https://www.blackhat.com/html/bh-usa-09/bh-usa-09-archives.html#Marlinspike |
662 |
* */ |
663 |
|
664 |
int i = -1, j; |
665 |
X509_NAME_ENTRY *xentry; |
666 |
ASN1_STRING *sdata; |
667 |
|
668 |
if (xname) { |
669 |
for (;;) |
670 |
{ |
671 |
j = X509_NAME_get_index_by_NID (xname, NID_commonName, i); |
672 |
if (j == -1) break; |
673 |
i = j; |
674 |
} |
603 |
} |
675 |
} |
604 |
} |
|
|
605 |
|
676 |
|
606 |
xentry = X509_NAME_get_entry(xname,i); |
677 |
xentry = X509_NAME_get_entry(xname,i); |
607 |
sdata = X509_NAME_ENTRY_get_data(xentry); |
678 |
sdata = X509_NAME_ENTRY_get_data(xentry); |
608 |
if (strlen (common_name) != ASN1_STRING_length (sdata)) |
679 |
if (strlen (common_name) != ASN1_STRING_length (sdata)) |
609 |
{ |
680 |
{ |
610 |
logprintf (LOG_NOTQUIET, _("\ |
681 |
logprintf (LOG_NOTQUIET, _("\ |
611 |
%s: certificate common name is invalid (contains a NUL character).\n\ |
682 |
%s: certificate common name is invalid (contains a NUL character).\n\ |
612 |
This may be an indication that the host is not who it claims to be\n\ |
683 |
This may be an indication that the host is not who it claims to be\n\ |
613 |
(that is, it is not the real %s).\n"), |
684 |
(that is, it is not the real %s).\n"), |
614 |
severity, quote (host)); |
685 |
severity, quote (host)); |
615 |
success = false; |
686 |
success = false; |
|
|
687 |
} |
616 |
} |
688 |
} |
617 |
} |
689 |
} |
618 |
|
690 |
|
Lines 631-633
Link Here
|
631 |
/* Allow --no-check-cert to disable certificate checking. */ |
703 |
/* Allow --no-check-cert to disable certificate checking. */ |
632 |
return opt.check_cert ? success : true; |
704 |
return opt.check_cert ? success : true; |
633 |
} |
705 |
} |
|
|
706 |
|
707 |
/* |
708 |
* vim: tabstop=2 shiftwidth=2 softtabstop=2 |
709 |
*/ |