def test_integration_dns_id_fail(self): """ Raise VerificationError if no certificate id matches the supplied service ids. """ i = DNS_ID(u"wrong.host") with pytest.raises(VerificationError) as e: verify_service_identity( DNS_IDS, obligatory_ids=[i], optional_ids=[] ) assert [DNSMismatch(mismatched_id=i)] == e.value.errors
def _verify_hostname(certificate, hostname): """ Verify whether *certificate* has a valid certificate chain for *hostname*. """ # Using private APIs here because service_identity doesn't *quite* have the # right public API. verify_service_identity( cert_patterns=extract_ids(certificate), obligatory_ids=[DNS_ID(hostname)], optional_ids=[], )
def test_integration_dns_id_fail(self): """ Raise VerificationError if no certificate id matches the supplied service ids. """ i = DNS_ID(u"wrong.host") with pytest.raises(VerificationError) as e: verify_service_identity(DNS_IDS, obligatory_ids=[i], optional_ids=[]) assert [DNSMismatch(mismatched_id=i)] == e.value.errors
def test_obligatory_mismatch(self): """ Raise if one of the obligatory IDs doesn't match. """ i = DNS_ID(u"example.net") with pytest.raises(VerificationError) as e: verify_service_identity( [SRVPattern(b"_mail.example.net"), DNSPattern(b"example.com")], obligatory_ids=[SRV_ID(u"_mail.example.net"), i], optional_ids=[], ) assert [DNSMismatch(mismatched_id=i)] == e.value.errors
def test_optional_mismatch(self): """ Raise VerificationError if an ID from optional_ids does not match a pattern of respective type even if obligatory IDs match. """ i = SRV_ID(u"_xmpp.example.com") with pytest.raises(VerificationError) as e: verify_service_identity( [DNSPattern(b"example.net"), SRVPattern(b"_mail.example.com")], obligatory_ids=[DNS_ID(u"example.net")], optional_ids=[i], ) assert [SRVMismatch(mismatched_id=i)] == e.value.errors
def match(self, value): # This is somewhat terrible. Probably can be better after # pyca/service_identity#14 is resolved. target_ids = [ DNSPattern(target_name.encode('utf-8')) for target_name in (value.extensions.get_extension_for_oid( ExtensionOID.SUBJECT_ALTERNATIVE_NAME).value. get_values_for_type(x509.DNSName)) ] ids = [DNS_ID(self.name)] try: verify_service_identity(cert_patterns=target_ids, obligatory_ids=ids, optional_ids=[]) except VerificationError: return Mismatch('{!r} is not valid for {!r}'.format( value, self.name))
def match(self, value): # This is somewhat terrible. Probably can be better after # pyca/service_identity#14 is resolved. target_ids = [ DNSPattern(target_name.encode('utf-8')) for target_name in ( value.extensions .get_extension_for_oid( ExtensionOID.SUBJECT_ALTERNATIVE_NAME) .value .get_values_for_type(x509.DNSName) )] ids = [DNS_ID(self.name)] try: verify_service_identity( cert_patterns=target_ids, obligatory_ids=ids, optional_ids=[]) except VerificationError: return Mismatch( '{!r} is not valid for {!r}'.format(value, self.name))
def test_optional_missing(self): """ Optional IDs may miss as long as they don't conflict with an existing pattern. """ p = DNSPattern(b"mail.foo.com") i = DNS_ID(u"mail.foo.com") rv = verify_service_identity( [p], obligatory_ids=[i], optional_ids=[SRV_ID(u"_mail.foo.com")] ) assert [ServiceMatch(cert_pattern=p, service_id=i)] == rv
def test_dns_id_success(self): """ Return pairs of certificate ids and service ids on matches. """ rv = verify_service_identity(DNS_IDS, [DNS_ID(u"twistedmatrix.com")], []) assert [ ServiceMatch(cert_pattern=DNSPattern(b"twistedmatrix.com"), service_id=DNS_ID(u"twistedmatrix.com"),), ] == rv
def test_optional_missing(self): """ Optional IDs may miss as long as they don't conflict with an existing pattern. """ p = DNSPattern(b"mail.foo.com") i = DNS_ID(u"mail.foo.com") rv = verify_service_identity([p], obligatory_ids=[i], optional_ids=[SRV_ID(u"_mail.foo.com")]) assert [ServiceMatch(cert_pattern=p, service_id=i)] == rv
def test_dns_id_success(self): """ Return pairs of certificate ids and service ids on matches. """ rv = verify_service_identity(extract_ids(CERT_DNS_ONLY), [DNS_ID(u"twistedmatrix.com")], []) assert [ ServiceMatch(cert_pattern=DNSPattern(b"twistedmatrix.com"), service_id=DNS_ID(u"twistedmatrix.com"),), ] == rv
def test_vsi_dns_id_success(self): """ Return pairs of certificate ids and service ids on matches. """ rv = verify_service_identity(extract_ids(CERT_DNS_ONLY), [DNS_ID(u("twistedmatrix.com"))]) self.assertEqual( [ (DNSPattern(b"twistedmatrix.com"), DNS_ID(u("twistedmatrix.com")),), ], rv )
def test_vsi_contains_srvs_and_matches(self): """ If a matching SRV-ID is found, return the tuple within the returned list and don't raise an error. """ p = SRVPattern(b"_mail.example.net") i = SRV_ID(u("_mail.example.net")) rv = verify_service_identity( [DNSPattern(b"example.net"), p], [DNS_ID(u("example.net")), i], ) self.assertEqual((p, i), rv[1])
def test_vsi_contains_uris_and_matches(self): """ If a matching URI-ID is found, return the tuple within the returned list and don't raise an error. """ p = URIPattern(b"sip:example.net") uri_id = URI_ID(u("sip:example.net")) rv = verify_service_identity( [DNSPattern(b"example.net"), p], [DNS_ID(u("example.net")), uri_id], ) self.assertEqual((p, uri_id), rv[1])
def test_contains_optional_and_matches(self): """ If an optional ID is found, return the match within the returned list and don't raise an error. """ p = SRVPattern(b"_mail.example.net") i = SRV_ID(u"_mail.example.net") rv = verify_service_identity( [DNSPattern(b"example.net"), p], obligatory_ids=[DNS_ID(u"example.net")], optional_ids=[i], ) assert ServiceMatch(cert_pattern=p, service_id=i) == rv[1]
def test_ip_address_success(self): """ IP addresses patterns are matched against IP address IDs. """ ip4 = ipaddress.ip_address(u"2.2.2.2") ip6 = ipaddress.ip_address(u"2a00:1c38::53") id4 = IPAddress_ID(six.text_type(ip4)) id6 = IPAddress_ID(six.text_type(ip6)) rv = verify_service_identity(extract_ids(CERT_EVERYTHING), [id4, id6], []) assert [ ServiceMatch(id4, IPAddressPattern(ip4)), ServiceMatch(id6, IPAddressPattern(ip6)), ] == rv
def test_ip_address_success(self): """ IP addresses patterns are matched against IP address IDs. """ ip4 = ipaddress.ip_address(u"2.2.2.2") ip6 = ipaddress.ip_address(u"2a00:1c38::53") id4 = IPAddress_ID(six.text_type(ip4)) id6 = IPAddress_ID(six.text_type(ip6)) rv = verify_service_identity( extract_ids(CERT_EVERYTHING), [id4, id6], [] ) assert [ ServiceMatch(id4, IPAddressPattern(ip4)), ServiceMatch(id6, IPAddressPattern(ip6)), ] == rv
def verifyHostname(connection, hostname): if _is_ip_address(hostname): hostname_id = IPADDRESS_ID(hostname) else: hostname_id = DNS_ID(hostname) cert = connection.get_peer_certificate() ids = [] # Basically the same as service_identity.pyopenssl.extract_ids, but with # subjectAltName support for iPAddress IDs and IP Addresses stuck in dNSName ids for i in range(cert.get_extension_count()): ext = cert.get_extension(i) if ext.get_short_name() == b"subjectAltName": names, _ = decode(ext.get_data(), asn1Spec=GeneralNames()) for n in names: name_string = n.getName() if name_string == "iPAddress": ids.append(IPADDRESSPattern(n.getComponent().asOctets())) elif name_string == "dNSName" and _is_ip_address( n.getComponent().asOctets().strip()): # If an IP Address is passed as a dNSName, it will # cause service_identity to throw an exception, as # it doesn't consider this valid. From reading the # RFCs, i think it's a gray area, so i'm going to # allow it, since we've seen it happen with a customer. try: ip_string = n.getComponent().asOctets().strip() if ":" in ip_string: value = inet_pton(AF_INET6, ip_string) else: value = inet_pton(AF_INET, ip_string) ids.append(IPADDRESSPattern(value)) except CertificateError as e: log.warning( "Ignoring invalid dNSName record in subjectAltName: %s", e) # Normal behavior below: elif name_string == "dNSName": ids.append(DNSPattern(n.getComponent().asOctets())) elif name_string == "uniformResourceIdentifier": ids.append(URIPattern(n.getComponent().asOctets())) elif name_string == "otherName": comp = n.getComponent() oid = comp.getComponentByPosition(0) if oid == ID_ON_DNS_SRV: srv, _ = decode(comp.getComponentByPosition(1)) if isinstance(srv, IA5String): ids.append(SRVPattern(srv.asOctets())) else: # pragma: nocover raise CertificateError( "Unexpected certificate content.") if not ids: # http://tools.ietf.org/search/rfc6125#section-6.4.4 # A client MUST NOT seek a match for a reference identifier of CN-ID if # the presented identifiers include a DNS-ID, SRV-ID, URI-ID, or any # application-specific identifier types supported by the client. warnings.warn( "Certificate has no `subjectAltName`, falling back to check for a " "`commonName` for now. This feature is being removed by major " "browsers and deprecated by RFC 2818.", SubjectAltNameWarning) ids = [ DNSPattern(c[1]) for c in cert.get_subject().get_components() if c[0] == b"CN" ] verify_service_identity( cert_patterns=ids, obligatory_ids=[hostname_id], optional_ids=[], )
def verifyHostname(connection, hostname): if _is_ip_address(hostname): hostname_id = IPADDRESS_ID(hostname) else: hostname_id = DNS_ID(hostname) cert = connection.get_peer_certificate() ids = [] # Basically the same as service_identity.pyopenssl.extract_ids, but with # subjectAltName support for iPAddress IDs and IP Addresses stuck in dNSName ids for i in range(cert.get_extension_count()): ext = cert.get_extension(i) if ext.get_short_name() == b"subjectAltName": names, _ = decode(ext.get_data(), asn1Spec=GeneralNames()) for n in names: name_string = n.getName() if name_string == "iPAddress": ids.append(IPADDRESSPattern(n.getComponent().asOctets())) elif name_string == "dNSName" and _is_ip_address(n.getComponent().asOctets().strip()): # If an IP Address is passed as a dNSName, it will # cause service_identity to throw an exception, as # it doesn't consider this valid. From reading the # RFCs, i think it's a gray area, so i'm going to # allow it, since we've seen it happen with a customer. try: ip_string = n.getComponent().asOctets().strip() if ":" in ip_string: value = inet_pton(AF_INET6, ip_string) else: value = inet_pton(AF_INET, ip_string) ids.append(IPADDRESSPattern(value)) except CertificateError as e: log.warning( "Ignoring invalid dNSName record in subjectAltName: %s", e) # Normal behavior below: elif name_string == "dNSName": ids.append(DNSPattern(n.getComponent().asOctets())) elif name_string == "uniformResourceIdentifier": ids.append(URIPattern(n.getComponent().asOctets())) elif name_string == "otherName": comp = n.getComponent() oid = comp.getComponentByPosition(0) if oid == ID_ON_DNS_SRV: srv, _ = decode(comp.getComponentByPosition(1)) if isinstance(srv, IA5String): ids.append(SRVPattern(srv.asOctets())) else: # pragma: nocover raise CertificateError( "Unexpected certificate content.") if not ids: # http://tools.ietf.org/search/rfc6125#section-6.4.4 # A client MUST NOT seek a match for a reference identifier of CN-ID if # the presented identifiers include a DNS-ID, SRV-ID, URI-ID, or any # application-specific identifier types supported by the client. warnings.warn( "Certificate has no `subjectAltName`, falling back to check for a " "`commonName` for now. This feature is being removed by major " "browsers and deprecated by RFC 2818.", SubjectAltNameWarning ) ids = [DNSPattern(c[1]) for c in cert.get_subject().get_components() if c[0] == b"CN"] verify_service_identity( cert_patterns=ids, obligatory_ids=[hostname_id], optional_ids=[], )