support CRLDistributionPoints in the OpenSSL backend
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 9d70d72..7f487d3 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -28,6 +28,18 @@
 from cryptography.hazmat.primitives import hashes
 
 
+_REASONFLAGS_ENUM_MAPPING = {
+    1: x509.ReasonFlags.key_compromise,
+    2: x509.ReasonFlags.ca_compromise,
+    3: x509.ReasonFlags.affiliation_changed,
+    4: x509.ReasonFlags.superseded,
+    5: x509.ReasonFlags.cessation_of_operation,
+    6: x509.ReasonFlags.certificate_hold,
+    7: x509.ReasonFlags.privilege_withdrawn,
+    8: x509.ReasonFlags.aa_compromise
+}
+
+
 def _obj2txt(backend, obj):
     # Set to 80 on the recommendation of
     # https://www.openssl.org/docs/crypto/OBJ_nid2ln.html#return_values
@@ -56,22 +68,23 @@
     return backend._ffi.buffer(buf[0], res)[:].decode('utf8')
 
 
+def _build_x509_name_entry(backend, x509_name_entry):
+    obj = backend._lib.X509_NAME_ENTRY_get_object(x509_name_entry)
+    assert obj != backend._ffi.NULL
+    data = backend._lib.X509_NAME_ENTRY_get_data(x509_name_entry)
+    assert data != backend._ffi.NULL
+    value = _asn1_string_to_utf8(backend, data)
+    oid = _obj2txt(backend, obj)
+
+    return x509.NameAttribute(x509.ObjectIdentifier(oid), value)
+
+
 def _build_x509_name(backend, x509_name):
     count = backend._lib.X509_NAME_entry_count(x509_name)
     attributes = []
     for x in range(count):
         entry = backend._lib.X509_NAME_get_entry(x509_name, x)
-        obj = backend._lib.X509_NAME_ENTRY_get_object(entry)
-        assert obj != backend._ffi.NULL
-        data = backend._lib.X509_NAME_ENTRY_get_data(entry)
-        assert data != backend._ffi.NULL
-        value = _asn1_string_to_utf8(backend, data)
-        oid = _obj2txt(backend, obj)
-        attributes.append(
-            x509.NameAttribute(
-                x509.ObjectIdentifier(oid), value
-            )
-        )
+        attributes.append(_build_x509_name_entry(backend, entry))
 
     return x509.Name(attributes)
 
@@ -292,6 +305,8 @@
                 value = self._build_authority_information_access(ext)
             elif oid == x509.OID_CERTIFICATE_POLICIES:
                 value = self._build_certificate_policies(ext)
+            elif oid == x509.OID_CRL_DISTRIBUTION_POINTS:
+                value = self._build_crl_distribution_points(ext)
             elif critical:
                 raise x509.UnsupportedExtension(
                     "{0} is not currently supported".format(oid), oid
@@ -518,6 +533,81 @@
 
         return x509.ExtendedKeyUsage(ekus)
 
+    def _build_crl_distribution_points(self, ext):
+        cdps = self._backend._ffi.cast(
+            "Cryptography_STACK_OF_DIST_POINT *",
+            self._backend._lib.X509V3_EXT_d2i(ext)
+        )
+        assert cdps != self._backend._ffi.NULL
+        cdps = self._backend._ffi.gc(
+            cdps, self._backend._lib.sk_DIST_POINT_free)
+        num = self._backend._lib.sk_DIST_POINT_num(cdps)
+
+        dist_points = []
+        for i in range(num):
+            full_name = None
+            relative_name = None
+            crl_issuer = None
+            reasons = None
+            cdp = self._backend._lib.sk_DIST_POINT_value(cdps, i)
+            if cdp.reasons != self._backend._ffi.NULL:
+                reasons = []
+                # We will check each bit from RFC 5280
+                # ReasonFlags ::= BIT STRING {
+                #      unused                  (0),
+                #      keyCompromise           (1),
+                #      cACompromise            (2),
+                #      affiliationChanged      (3),
+                #      superseded              (4),
+                #      cessationOfOperation    (5),
+                #      certificateHold         (6),
+                #      privilegeWithdrawn      (7),
+                #      aACompromise            (8) }
+                for bit in range(1, 9):
+                    if self._backend._lib.ASN1_BIT_STRING_get_bit(
+                        cdp.reasons, bit
+                    ):
+                        reasons.append(_REASONFLAGS_ENUM_MAPPING[bit])
+
+                reasons = frozenset(reasons)
+
+            if cdp.CRLissuer != self._backend._ffi.NULL:
+                crl_issuer = _build_general_names(self._backend, cdp.CRLissuer)
+
+            # Certificates may have a crl_issuer/reasons and no distribution
+            # point so make sure it's not null.
+            if cdp.distpoint != self._backend._ffi.NULL:
+                # Type 0 is fullName, there is no #define for it in the code.
+                if cdp.distpoint.type == 0:
+                    full_name = _build_general_names(
+                        self._backend, cdp.distpoint.name.fullname
+                    )
+                # OpenSSL code doesn't test for a specific type for
+                # relativename, everything that isn't fullname is considered
+                # relativename.
+                else:
+                    rns = cdp.distpoint.name.relativename
+                    rnum = self._backend._lib.sk_X509_NAME_ENTRY_num(rns)
+                    attributes = []
+                    for i in range(rnum):
+                        rn = self._backend._lib.sk_X509_NAME_ENTRY_value(
+                            rns, i
+                        )
+                        assert rn != self._backend._ffi.NULL
+                        attributes.append(
+                            _build_x509_name_entry(self._backend, rn)
+                        )
+
+                    relative_name = x509.Name(attributes)
+
+            dist_points.append(
+                x509.DistributionPoint(
+                    full_name, relative_name, reasons, crl_issuer
+                )
+            )
+
+        return x509.CRLDistributionPoints(dist_points)
+
 
 @utils.register_interface(x509.CertificateSigningRequest)
 class _CertificateSigningRequest(object):
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 2852776..94b33ae 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -1929,3 +1929,161 @@
         assert cdp != cdp3
         assert cdp != cdp4
         assert cdp != object()
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestCRLDistributionPointsExtension(object):
+    def test_fullname_and_crl_issuer(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "PKITS_data", "certs", "ValidcRLIssuerTest28EE.crt"
+            ),
+            x509.load_der_x509_certificate,
+            backend
+        )
+
+        cdps = cert.extensions.get_extension_for_oid(
+            x509.OID_CRL_DISTRIBUTION_POINTS
+        ).value
+
+        assert cdps == x509.CRLDistributionPoints([
+            x509.DistributionPoint(
+                full_name=[x509.DirectoryName(
+                    x509.Name([
+                        x509.NameAttribute(x509.OID_COUNTRY_NAME, "US"),
+                        x509.NameAttribute(
+                            x509.OID_ORGANIZATION_NAME,
+                            "Test Certificates 2011"
+                        ),
+                        x509.NameAttribute(
+                            x509.OID_ORGANIZATIONAL_UNIT_NAME,
+                            "indirectCRL CA3 cRLIssuer"
+                        ),
+                        x509.NameAttribute(
+                            x509.OID_COMMON_NAME,
+                            "indirect CRL for indirectCRL CA3"
+                        ),
+                    ])
+                )],
+                relative_name=None,
+                reasons=None,
+                crl_issuer=[x509.DirectoryName(
+                    x509.Name([
+                        x509.NameAttribute(x509.OID_COUNTRY_NAME, "US"),
+                        x509.NameAttribute(
+                            x509.OID_ORGANIZATION_NAME,
+                            "Test Certificates 2011"
+                        ),
+                        x509.NameAttribute(
+                            x509.OID_ORGANIZATIONAL_UNIT_NAME,
+                            "indirectCRL CA3 cRLIssuer"
+                        ),
+                    ])
+                )],
+            )
+        ])
+
+    def test_relativename_and_crl_issuer(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "PKITS_data", "certs", "ValidcRLIssuerTest29EE.crt"
+            ),
+            x509.load_der_x509_certificate,
+            backend
+        )
+
+        cdps = cert.extensions.get_extension_for_oid(
+            x509.OID_CRL_DISTRIBUTION_POINTS
+        ).value
+
+        assert cdps == x509.CRLDistributionPoints([
+            x509.DistributionPoint(
+                full_name=None,
+                relative_name=x509.Name([
+                    x509.NameAttribute(
+                        x509.OID_COMMON_NAME,
+                        "indirect CRL for indirectCRL CA3"
+                    ),
+                ]),
+                reasons=None,
+                crl_issuer=[x509.DirectoryName(
+                    x509.Name([
+                        x509.NameAttribute(x509.OID_COUNTRY_NAME, "US"),
+                        x509.NameAttribute(
+                            x509.OID_ORGANIZATION_NAME,
+                            "Test Certificates 2011"
+                        ),
+                        x509.NameAttribute(
+                            x509.OID_ORGANIZATIONAL_UNIT_NAME,
+                            "indirectCRL CA3 cRLIssuer"
+                        ),
+                    ])
+                )],
+            )
+        ])
+
+    def test_fullname_crl_issuer_reasons(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "cdp_fullname_reasons_crl_issuer.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+
+        cdps = cert.extensions.get_extension_for_oid(
+            x509.OID_CRL_DISTRIBUTION_POINTS
+        ).value
+
+        assert cdps == x509.CRLDistributionPoints([
+            x509.DistributionPoint(
+                full_name=[x509.UniformResourceIdentifier(
+                    u"http://myhost.com/myca.crl"
+                )],
+                relative_name=None,
+                reasons=frozenset([
+                    x509.ReasonFlags.key_compromise,
+                    x509.ReasonFlags.ca_compromise
+                ]),
+                crl_issuer=[x509.DirectoryName(
+                    x509.Name([
+                        x509.NameAttribute(x509.OID_COUNTRY_NAME, "US"),
+                        x509.NameAttribute(
+                            x509.OID_ORGANIZATION_NAME, "PyCA"
+                        ),
+                        x509.NameAttribute(
+                            x509.OID_COMMON_NAME, "cryptography CA"
+                        ),
+                    ])
+                )],
+            )
+        ])
+
+    def test_crl_issuer_only(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "cdp_crl_issuer.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+
+        cdps = cert.extensions.get_extension_for_oid(
+            x509.OID_CRL_DISTRIBUTION_POINTS
+        ).value
+
+        assert cdps == x509.CRLDistributionPoints([
+            x509.DistributionPoint(
+                full_name=None,
+                relative_name=None,
+                reasons=None,
+                crl_issuer=[x509.DirectoryName(
+                    x509.Name([
+                        x509.NameAttribute(
+                            x509.OID_COMMON_NAME, "cryptography CA"
+                        ),
+                    ])
+                )],
+            )
+        ])