The branch, master has been updated via 8579340fc54 gpo: Do not get templates list on first run via 3f3ddfa699a gpo: Decode base64 root cert before importing via 0d1ff69936f gpo: Test certificate policy without NDES from 2713023250f smbd: use dirfsp and atname in open_directory()
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 8579340fc540633c13c017d896034904a8dbd55c Author: Gabriel Nagy <gabriel.n...@canonical.com> Date: Fri Jan 19 11:36:19 2024 +0200 gpo: Do not get templates list on first run This is a visual fix and has no impact on functionality apart from cleaner log messages. The point of this is to get the list of supported templates in order to compute a diff between the current applied templates and the updated list, so we are able to unapply and reapply the policy in case there are differences. However this code path is executed on first applies as well, at which point the root CA is not yet set up. This causes the `get_supported_templates` call to fail, which is not a hard failure but still pollutes the logs. In this case it's safe to avoid executing the command as the policy will be applied regardless. Signed-off-by: Gabriel Nagy <gabriel.n...@canonical.com> Reviewed-by: David Mulder <dmul...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> Autobuild-User(master): Andreas Schneider <a...@cryptomilk.org> Autobuild-Date(master): Mon Jan 22 16:48:57 UTC 2024 on atb-devel-224 commit 3f3ddfa699a33c2c8a59f7fb9ee044bb2a6e0e06 Author: Gabriel Nagy <gabriel.n...@canonical.com> Date: Thu Jan 18 20:23:24 2024 +0200 gpo: Decode base64 root cert before importing The reasoning behind this is described in the previous commit message, but essentially this should either be wrapped in certificate blocks and imported as PEM, or converted back to binary and imported as DER. I've opted for the latter since it's how it used to work before it regressed in 157335ee93e. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15557 Signed-off-by: Gabriel Nagy <gabriel.n...@canonical.com> Reviewed-by: David Mulder <dmul...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit 0d1ff69936f18ea729fc11fbbb1569a833302572 Author: Gabriel Nagy <gabriel.n...@canonical.com> Date: Mon Jan 8 18:05:08 2024 +0200 gpo: Test certificate policy without NDES As of 8231eaf856b, the NDES feature is no longer required on Windows, as cert auto-enroll can use the certificate from the LDAP request. However, 157335ee93e changed the implementation to convert the LDAP certificate to base64 due to it failing to cleanly convert to a string. Because of insufficient test coverage I missed handling the part where NDES is disabled or not reachable and the LDAP certificate was imported. The call to load_der_x509_certificate now fails with an error because it expects binary data, yet it receives a base64 encoded string. This adds a test to confirm the issue. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15557 Signed-off-by: Gabriel Nagy <gabriel.n...@canonical.com> Reviewed-by: David Mulder <dmul...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> ----------------------------------------------------------------------- Summary of changes: python/samba/gp/gp_cert_auto_enroll_ext.py | 8 +- python/samba/tests/gpo.py | 126 +++++++++++++++++++++++++++-- 2 files changed, 125 insertions(+), 9 deletions(-) Changeset truncated at 500 lines: diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py index e2f81b3f3f8..8ba1960efe5 100644 --- a/python/samba/gp/gp_cert_auto_enroll_ext.py +++ b/python/samba/gp/gp_cert_auto_enroll_ext.py @@ -217,10 +217,11 @@ def getca(ca, url, trust_dir): ' installed or not configured.') if 'cACertificate' in ca: log.warn('Installing the server certificate only.') + der_certificate = base64.b64decode(ca['cACertificate']) try: - cert = load_der_x509_certificate(ca['cACertificate']) + cert = load_der_x509_certificate(der_certificate) except TypeError: - cert = load_der_x509_certificate(ca['cACertificate'], + cert = load_der_x509_certificate(der_certificate, default_backend()) cert_data = cert.public_bytes(Encoding.PEM) with open(root_cert, 'wb') as w: @@ -358,7 +359,8 @@ class gp_cert_auto_enroll_ext(gp_pol_ext, gp_applier): # If the policy has changed, unapply, then apply new policy old_val = self.cache_get_attribute_value(guid, attribute) old_data = json.loads(old_val) if old_val is not None else {} - templates = ['%s.%s' % (ca['name'], t.decode()) for t in get_supported_templates(ca['hostname'])] + templates = ['%s.%s' % (ca['name'], t.decode()) for t in get_supported_templates(ca['hostname'])] \ + if old_val is not None else [] new_data = { 'templates': templates, **ca } if changed(new_data, old_data) or self.cache_get_apply_state() == GPOSTATE.ENFORCE: self.unapply(guid, attribute, old_val) diff --git a/python/samba/tests/gpo.py b/python/samba/tests/gpo.py index 81bf3b8c03b..9177eef5afa 100644 --- a/python/samba/tests/gpo.py +++ b/python/samba/tests/gpo.py @@ -103,17 +103,21 @@ def dummy_certificate(): # Dummy requests structure for Certificate Auto Enrollment class dummy_requests(object): - @staticmethod - def get(url=None, params=None): + class exceptions(object): + ConnectionError = Exception + + def __init__(self, want_exception=False): + self.want_exception = want_exception + + def get(self, url=None, params=None): + if self.want_exception: + raise self.exceptions.ConnectionError + dummy = requests.Response() dummy._content = dummy_certificate() dummy.headers = {'Content-Type': 'application/x-x509-ca-cert'} return dummy - class exceptions(object): - ConnectionError = Exception -cae.requests = dummy_requests - realm = os.environ.get('REALM') policies = realm + '/POLICIES' realm = realm.lower() @@ -6906,6 +6910,114 @@ class GPOTests(tests.TestCase): # Unstage the Registry.pol file unstage_file(reg_pol) + def test_gp_cert_auto_enroll_ext_without_ndes(self): + local_path = self.lp.cache_path('gpo_cache') + guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' + reg_pol = os.path.join(local_path, policies, guid, + 'MACHINE/REGISTRY.POL') + cache_dir = self.lp.get('cache directory') + store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) + + machine_creds = Credentials() + machine_creds.guess(self.lp) + machine_creds.set_machine_account() + + # Initialize the group policy extension + cae.requests = dummy_requests(want_exception=True) + ext = cae.gp_cert_auto_enroll_ext(self.lp, machine_creds, + machine_creds.get_username(), store) + + gpos = get_gpo_list(self.server, machine_creds, self.lp, + machine_creds.get_username()) + + # Stage the Registry.pol file with test data + parser = GPPolParser() + parser.load_xml(etree.fromstring(auto_enroll_reg_pol.strip())) + ret = stage_file(reg_pol, ndr_pack(parser.pol_file)) + self.assertTrue(ret, 'Could not create the target %s' % reg_pol) + + # Write the dummy CA entry, Enrollment Services, and Templates Entries + admin_creds = Credentials() + admin_creds.set_username(os.environ.get('DC_USERNAME')) + admin_creds.set_password(os.environ.get('DC_PASSWORD')) + admin_creds.set_realm(os.environ.get('REALM')) + hostname = get_dc_hostname(machine_creds, self.lp) + url = 'ldap://%s' % hostname + ldb = Ldb(url=url, session_info=system_session(), + lp=self.lp, credentials=admin_creds) + # Write the dummy CA + confdn = 'CN=Public Key Services,CN=Services,CN=Configuration,%s' % base_dn + ca_cn = '%s-CA' % hostname.replace('.', '-') + certa_dn = 'CN=%s,CN=Certification Authorities,%s' % (ca_cn, confdn) + ldb.add({'dn': certa_dn, + 'objectClass': 'certificationAuthority', + 'authorityRevocationList': ['XXX'], + 'cACertificate': dummy_certificate(), + 'certificateRevocationList': ['XXX'], + }) + # Write the dummy pKIEnrollmentService + enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn) + ldb.add({'dn': enroll_dn, + 'objectClass': 'pKIEnrollmentService', + 'cACertificate': dummy_certificate(), + 'certificateTemplates': ['Machine'], + 'dNSHostName': hostname, + }) + # Write the dummy pKICertificateTemplate + template_dn = 'CN=Machine,CN=Certificate Templates,%s' % confdn + ldb.add({'dn': template_dn, + 'objectClass': 'pKICertificateTemplate', + }) + + with TemporaryDirectory() as dname: + try: + ext.process_group_policy([], gpos, dname, dname) + except Exception as e: + self.fail(str(e)) + + ca_crt = os.path.join(dname, '%s.crt' % ca_cn) + self.assertTrue(os.path.exists(ca_crt), + 'Root CA certificate was not requested') + machine_crt = os.path.join(dname, '%s.Machine.crt' % ca_cn) + self.assertTrue(os.path.exists(machine_crt), + 'Machine certificate was not requested') + machine_key = os.path.join(dname, '%s.Machine.key' % ca_cn) + self.assertTrue(os.path.exists(machine_key), + 'Machine key was not generated') + + # Verify RSOP does not fail + ext.rsop([g for g in gpos if g.name == guid][0]) + + # Check that a call to gpupdate --rsop also succeeds + ret = rsop(self.lp) + self.assertEqual(ret, 0, 'gpupdate --rsop failed!') + + # Remove policy + gp_db = store.get_gplog(machine_creds.get_username()) + del_gpos = get_deleted_gpos_list(gp_db, []) + ext.process_group_policy(del_gpos, [], dname) + self.assertFalse(os.path.exists(ca_crt), + 'Root CA certificate was not removed') + self.assertFalse(os.path.exists(machine_crt), + 'Machine certificate was not removed') + self.assertFalse(os.path.exists(machine_key), + 'Machine key was not removed') + out, _ = Popen(['getcert', 'list-cas'], stdout=PIPE).communicate() + self.assertNotIn(get_bytes(ca_cn), out, 'CA was not removed') + out, _ = Popen(['getcert', 'list'], stdout=PIPE).communicate() + self.assertNotIn(b'Machine', out, + 'Machine certificate not removed') + self.assertNotIn(b'Workstation', out, + 'Workstation certificate not removed') + + # Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate + ldb.delete(certa_dn) + ldb.delete(enroll_dn) + ldb.delete(template_dn) + + # Unstage the Registry.pol file + unstage_file(reg_pol) + def test_gp_cert_auto_enroll_ext(self): local_path = self.lp.cache_path('gpo_cache') guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' @@ -6919,6 +7031,7 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension + cae.requests = dummy_requests() ext = cae.gp_cert_auto_enroll_ext(self.lp, machine_creds, machine_creds.get_username(), store) @@ -7517,6 +7630,7 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension + cae.requests = dummy_requests() ext = cae.gp_cert_auto_enroll_ext(self.lp, machine_creds, machine_creds.get_username(), store) -- Samba Shared Repository