]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
CVE-2026-3012: do not fetch certificate over http
authorDouglas Bagnall <douglas.bagnall@catalyst.net.nz>
Sun, 22 Feb 2026 22:01:57 +0000 (11:01 +1300)
committerStefan Metzmacher <metze@samba.org>
Tue, 26 May 2026 12:51:32 +0000 (12:51 +0000)
In the case where a certificate was found via HTTP, it was trusted
without verification and put in the global CA store.

There is no means to check the certificate other than by comparing it
to certificates we may have gathered via LDAP, but in that case there
is no advantage over just using the LDAP-derived certificates.

Using the LDAP certificates was already the fallback case if HTTP
failed, so we just make it the default.

The HTTP fetch depends on the NDES service, which is a variant of
Simple Certificate Enrolment Protocol (SCEP, RFC8894), but in fact
Samba implements none of that protocol other than the HTTP fetch. SCEP
is for clients that are not true domain members. Domain members can
access to certificates over LDAP. This patch is not reducing SCEP
client support because Samba never had it.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=16003

Reported-by: Arad Inbar, DREAM Security Research Team
Reported-by: Nir Somech, DREAM Security Research Team
Reported-by: Ben Grinberg, DREAM Security Research Team
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Jennifer Sutton <jennifersutton@catalyst.net.nz>
python/samba/gp/gp_cert_auto_enroll_ext.py
selftest/knownfail.d/gpo-auto-enrol [new file with mode: 0644]

index 877659b043edfedf64d7853e0a97cab38171ae60..815436e11e9cf4154875964e06f1ac1e142a2837 100644 (file)
@@ -16,7 +16,6 @@
 
 import os
 import operator
-import requests
 from samba.gp.gpclass import gp_pol_ext, gp_applier, GPOSTATE
 from samba import Ldb
 from samba.dcerpc import misc
@@ -195,58 +194,24 @@ def get_supported_templates(server):
     return out.strip().split()
 
 
-def getca(ca, url, trust_dir):
-    """Fetch Certificate Chain from the CA."""
+def getca(ca, trust_dir):
+    """Fetch a certificate from LDAP."""
     root_cert = os.path.join(trust_dir, '%s.crt' % ca['name'])
     root_certs = []
-
-    try:
-        r = requests.get(url=url, params={'operation': 'GetCACert',
-                                          'message': 'CAIdentifier'})
-    except requests.exceptions.ConnectionError:
-        log.warn('Could not connect to Network Device Enrollment Service.')
-        r = None
-    if r is None or r.content == b'' or r.headers['Content-Type'] == 'text/html':
-        log.warn('Unable to fetch root certificates (requires NDES).')
-        if 'cACertificate' in ca:
-            log.warn('Installing the server certificate only.')
-            der_certificate = base64.b64decode(ca['cACertificate'])
-            try:
-                cert = load_der_x509_certificate(der_certificate)
-            except TypeError:
-                cert = load_der_x509_certificate(der_certificate,
-                                                 default_backend())
-            cert_data = cert.public_bytes(Encoding.PEM)
-            with open(root_cert, 'wb') as w:
-                w.write(cert_data)
-            root_certs.append(root_cert)
-        return root_certs
-
-    if r.headers['Content-Type'] == 'application/x-x509-ca-cert':
-        # Older versions of load_der_x509_certificate require a backend param
+    if 'cACertificate' in ca:
+        log.warn('Installing the server certificate only.')
+        der_certificate = base64.b64decode(ca['cACertificate'])
         try:
-            cert = load_der_x509_certificate(r.content)
+            cert = load_der_x509_certificate(der_certificate)
         except TypeError:
-            cert = load_der_x509_certificate(r.content, default_backend())
+            cert = load_der_x509_certificate(der_certificate,
+                                             default_backend())
         cert_data = cert.public_bytes(Encoding.PEM)
         with open(root_cert, 'wb') as w:
             w.write(cert_data)
         root_certs.append(root_cert)
-    elif r.headers['Content-Type'] == 'application/x-x509-ca-ra-cert':
-        certs = load_der_pkcs7_certificates(r.content)
-        for i in range(0, len(certs)):
-            cert = certs[i].public_bytes(Encoding.PEM)
-            filename, extension = root_cert.rsplit('.', 1)
-            dest = '%s.%d.%s' % (filename, i, extension)
-            with open(dest, 'wb') as w:
-                w.write(cert)
-            root_certs.append(dest)
-    else:
-        log.warn('getca: Wrong (or missing) MIME content type')
-
     return root_certs
 
-
 def find_global_trust_dir():
     """Return the global trust dir using known paths from various Linux distros."""
     for trust_dir in global_trust_dirs:
@@ -266,11 +231,10 @@ def changed(new_data, old_data):
 def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
     """Install the root certificate chain."""
     data = dict({'files': [], 'templates': []}, **ca)
-    url = 'http://%s/CertSrv/mscep/mscep.dll/pkiclient.exe?' % ca['hostname']
 
     log.info("Try to get root or server certificates")
 
-    root_certs = getca(ca, url, trust_dir)
+    root_certs = getca(ca, trust_dir)
     data['files'].extend(root_certs)
     global_trust_dir = find_global_trust_dir()
     for src in root_certs:
diff --git a/selftest/knownfail.d/gpo-auto-enrol b/selftest/knownfail.d/gpo-auto-enrol
new file mode 100644 (file)
index 0000000..4bf4b8e
--- /dev/null
@@ -0,0 +1,2 @@
+^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_advanced_gp_cert_auto_enroll_ext\(ad_dc:local\)
+^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_gp_cert_auto_enroll_ext\(ad_dc:local\)