support name constraints in the openssl backend
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index cc80575..4125848 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -537,6 +537,35 @@
     )
 
 
+def _decode_name_constraints(backend, ext):
+    nc = backend._ffi.cast(
+        "NAME_CONSTRAINTS *", backend._lib.X509V3_EXT_d2i(ext)
+    )
+    assert nc != backend._ffi.NULL
+    nc = backend._ffi.gc(nc, backend._lib.NAME_CONSTRAINTS_free)
+    permitted = _decode_general_subtrees(backend, nc.permittedSubtrees)
+    excluded = _decode_general_subtrees(backend, nc.excludedSubtrees)
+    return x509.NameConstraints(
+        permitted_subtrees=permitted, excluded_subtrees=excluded
+    )
+
+
+def _decode_general_subtrees(backend, stack_subtrees):
+    if stack_subtrees == backend._ffi.NULL:
+        return None
+
+    num = backend._lib.sk_GENERAL_SUBTREE_num(stack_subtrees)
+    subtrees = []
+
+    for i in range(num):
+        obj = backend._lib.sk_GENERAL_SUBTREE_value(stack_subtrees, i)
+        assert obj != backend._ffi.NULL
+        name = _decode_general_name(backend, obj.base)
+        subtrees.append(name)
+
+    return subtrees
+
+
 def _decode_extended_key_usage(backend, ext):
     sk = backend._ffi.cast(
         "Cryptography_STACK_OF_ASN1_OBJECT *",
@@ -728,6 +757,7 @@
         x509.OID_OCSP_NO_CHECK: _decode_ocsp_no_check,
         x509.OID_INHIBIT_ANY_POLICY: _decode_inhibit_any_policy,
         x509.OID_ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name,
+        x509.OID_NAME_CONSTRAINTS: _decode_name_constraints,
     }
 )
 
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 6d91ba4..15ee118 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -2033,6 +2033,50 @@
         assert nc != object()
 
 
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestNameConstraintsExtension(object):
+    def test_permitted_excluded(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "nc_permitted_excluded_2.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        nc = cert.extensions.get_extension_for_oid(
+            x509.OID_NAME_CONSTRAINTS
+        ).value
+        assert nc == x509.NameConstraints(
+            permitted_subtrees=[
+                x509.DNSName(u"zombo.local"),
+            ],
+            excluded_subtrees=[
+                x509.DirectoryName(x509.Name([
+                    x509.NameAttribute(x509.OID_COMMON_NAME, u"zombo")
+                ]))
+            ]
+        )
+
+    def test_permitted(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "nc_permitted_2.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        nc = cert.extensions.get_extension_for_oid(
+            x509.OID_NAME_CONSTRAINTS
+        ).value
+        assert nc == x509.NameConstraints(
+            permitted_subtrees=[
+                x509.DNSName(u"zombo.local"),
+            ],
+            excluded_subtrees=None
+        )
+
+
 class TestDistributionPoint(object):
     def test_distribution_point_full_name_not_general_names(self):
         with pytest.raises(TypeError):