The branch, master has been updated via 469b22b849a python/samba/tests/krb5: Allow PkInitTests.test_pkinit_ntlm_from_pac_must_change_now to pass on Samba/Heimdal via 15686fec981 python/samba/tests/krb5: Expand test without UF_SMARTCARD_REQUIRED to show rotation is not done via f3528808aba WHATSNEW: Mention msDS-ExpirePasswordsOnSmartCardOnlyAccounts behaviour via 2854ef29b82 provision: Match Windows 2022 and set msDS-ExpirePasswordsOnSmartCardOnlyAccounts by default via dee3c7be584 selftest: Add test that msDS-ExpirePasswordsOnSmartCardOnlyAccounts=TRUE is set via 491b79d445d kdc: Rotate smart-card only underlying password in 2nd half of lifetime via 8afe27058b0 kdc: Track the pwdLastSet of expired UF_SMARTCARD_REQUIRED accounts via 1e1c80656f7 kdc: Detect (about to) expire UF_SMARTCARD_REQUIRED accounts and rotate passwords via d03b3faeb8e s4-auth: Use consistant externally-supplied time in auth stack via 1dcd8be8f06 kdc: Use a consistent, stable time throughout the Heimdal KDC via fe61009002d kdc: Mark KDC sam.ldb as not to use ldb_wrap cache via e178f6b0e96 ldb_wrap: Provide a way to avoid Samba using ldb_wrap() via 9ba5ebf4af7 kdc: Remove confusing duplicate open of sam.ldb to find RODC status via 09ae48b415b dsdb: Prepare to handle smartcard password rollover via 1bcc9f00157 dsdb: Use dsdb_gmsa_current_time() in construct_msds_user_account_control_computed via cc3ea4ed571 dsdb: UF_SMARTCARD_REQUIRED can have a password expiry, if configured! via 3669479f22f dsdb: Reduce minimum maxPwdAge from 1 day to nil via 302619f66f9 dsdb: Change the magic smartcard_reset to set AES keys like the krbtgt mode via 7c79abbab46 python/samba/tests/krb5: PKINIT tests of passwords that are naturally expired via 044cc538605 python/test/krb5: Use assertAlmostEqual in check_ticket_times() via 68fa90754fd python/tests/krb5: Move check_ticket_times() to kdc_base_test.py via 48bff4b95f8 python/samba/krb5: Add test for password rotation on UF_SMARCARD_REQUIRED accounts via a85f4c661b1 python/tests/krb5: Remove unused utf16pw variable via 504a47ecfd6 python/tests/krb5: Expect AES keys for UF_SMARTCARD_REQUIRED via dc6c4b215e2 python/samba/tests/krb5: Extend PKINIT tests to show kpasswd still works via 4ec24a20764 python/samba/tests/krb5: Move get_kpasswd_sname() into raw_testcase() to allow broader use via b664392208c s4-auth: Use msDS-User-Account-Control-Computed for PW expiry check via 737f2414062 s4-auth: Update comment to mention 60mins in the NTLM grace period via e04eb9bb170 dsdb: Make argument order of dsdb_update_gmsa_{entry_,}keys() consistant with other uses from a9b3522f53a smbd: Ensure we grant owner sid in check_parent_access_fsp()
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 469b22b849aa6a76739dc21d8a2d80907cdf8d73 Author: Andrew Bartlett <abart...@samba.org> Date: Tue Jun 4 13:26:18 2024 +1200 python/samba/tests/krb5: Allow PkInitTests.test_pkinit_ntlm_from_pac_must_change_now to pass on Samba/Heimdal This flexiblity in the tests avoids requiring Samba/Heimdal to omit an NTSTATUS error return and just be consistent between the different authentication paths. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> Autobuild-User(master): Andrew Bartlett <abart...@samba.org> Autobuild-Date(master): Mon Jun 10 05:32:54 UTC 2024 on atb-devel-224 commit 15686fec9819267f69a600cff859e52f77a64cef Author: Andrew Bartlett <abart...@samba.org> Date: Tue Jun 4 11:36:53 2024 +1200 python/samba/tests/krb5: Expand test without UF_SMARTCARD_REQUIRED to show rotation is not done This makes sense as otherwise the user would suddenly not know their password for use when they do not use their smartcard. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit f3528808aba9419c0895bdb709e1b0dc0bdced1e Author: Andrew Bartlett <abart...@samba.org> Date: Mon May 27 11:51:59 2024 +1200 WHATSNEW: Mention msDS-ExpirePasswordsOnSmartCardOnlyAccounts behaviour Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 2854ef29b82d89fb5b5c9d8414227988783120b9 Author: Andrew Bartlett <abart...@samba.org> Date: Mon May 27 11:30:29 2024 +1200 provision: Match Windows 2022 and set msDS-ExpirePasswordsOnSmartCardOnlyAccounts by default We do this by telling the Domain Functional Level upgrade code that this is a new install. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit dee3c7be5846ae7c0952b1ace94dee23241dc794 Author: Andrew Bartlett <abart...@samba.org> Date: Mon May 27 11:53:15 2024 +1200 selftest: Add test that msDS-ExpirePasswordsOnSmartCardOnlyAccounts=TRUE is set This assures us that the new provision sets the value by default. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 491b79d445d54f56ba8dfea978da322e1fc16c44 Author: Andrew Bartlett <abart...@samba.org> Date: Fri May 17 17:34:36 2024 +1200 kdc: Rotate smart-card only underlying password in 2nd half of lifetime This is a measure to avoid multiple servers rotating the password but means that the maximum password age really must be set to twice the TGT lifetime, eg a default of 20 hours. The internet suggestions of 1 day for this feature should work fine. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 8afe27058b08ff30d2650bb4fec92f56fa418e6a Author: Andrew Bartlett <abart...@samba.org> Date: Thu May 9 16:24:31 2024 +1200 kdc: Track the pwdLastSet of expired UF_SMARTCARD_REQUIRED accounts This is to gracefully deal with races and to avoid additional password rollover in situations where the TGT lifetime is longer than the maximum password lifetime. This is not a sensible combination, so we just avoid the extra DB write, and update it only once per AS-REQ in this case. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 1e1c80656f7d19d1cfde118bdba75a576da978f7 Author: Andrew Bartlett <abart...@samba.org> Date: Tue May 21 11:14:50 2024 +1200 kdc: Detect (about to) expire UF_SMARTCARD_REQUIRED accounts and rotate passwords This ensures that before the KDC starts to process the entry we check if it is expired and rotate it. As an account with UF_SMARTCARD_REQUIRED simply can not expire unless msDS-ExpirePasswordsOnSmartCardOnlyAccounts is set and the Domain Functional Level is >= 2016 we do not need to do configuration checks here. Signed-off-by: Andrew Bartlett <abart...@samba.org> Signed-off-by: Jo Sutton <josut...@catalyst.net.nz> Pair-programmed-by: Jo Sutton <josut...@catalyst.net.nz> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit d03b3faeb8e6e3446c606b3f167d2e9917514fda Author: Andrew Bartlett <abart...@samba.org> Date: Wed May 29 14:51:01 2024 +1200 s4-auth: Use consistant externally-supplied time in auth stack This makes the time during authentication stay consistent in the KDC and follows the fake time when we are testing gMSA accounts. By having the account expiry follow exactly the same clock as the password expiry we can hope for less supprises. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 1dcd8be8f06066f8aed7765c4e92259ce1371991 Author: Andrew Bartlett <abart...@samba.org> Date: Tue May 28 12:53:19 2024 +1200 kdc: Use a consistent, stable time throughout the Heimdal KDC The MIT KDC has a fallback to a consistent time per fetch call, and both implementations then follow the time in each 'struct samba_kdc_entry'. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit fe61009002db897fb8632954af8203de28c24d7d Author: Andrew Bartlett <abart...@samba.org> Date: Thu May 30 11:43:04 2024 +1200 kdc: Mark KDC sam.ldb as not to use ldb_wrap cache This will ensure that the time which will be is passed in an opaque is not used by other parts of Samba Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit e178f6b0e962b0ac96d447196765c21c770ede63 Author: Andrew Bartlett <abart...@samba.org> Date: Thu May 30 11:23:01 2024 +1200 ldb_wrap: Provide a way to avoid Samba using ldb_wrap() ldb_wrap is a caching mechansim, and it should probably be removed but for now provide a way to avoid it in specific cases where we know it is harmful. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 9ba5ebf4af7c5170dc2abf2307e3c47d7d250f5f Author: Andrew Bartlett <abart...@samba.org> Date: Thu May 30 11:40:16 2024 +1200 kdc: Remove confusing duplicate open of sam.ldb to find RODC status Instead, make this query after we open the DB in common with the MIT code. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 09ae48b415b2b50dbf4600e9c7f9cb4ec65a6263 Author: Andrew Bartlett <abart...@samba.org> Date: Mon May 20 13:51:23 2024 +1200 dsdb: Prepare to handle smartcard password rollover We do this by allowing the password change control to indicate that the password is to be randomised, bypassing the quality checks (as true random passwords often fail these) and re-randomising with the same code as is used for the KDC. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 1bcc9f00157884a8870e32dda258640c2c3a0c79 Author: Andrew Bartlett <abart...@samba.org> Date: Fri May 17 15:10:18 2024 +1200 dsdb: Use dsdb_gmsa_current_time() in construct_msds_user_account_control_computed This both allows the time to be overriden in some future unit tests (which is incredibly helpful in testing) and gets a full NTTIME rather than just a time_t based time, so we do not need to wait an extra second for the NTTIME to change. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit cc3ea4ed571ca033c357cebeea4511ba6dd9fa81 Author: Andrew Bartlett <abart...@samba.org> Date: Wed Apr 3 11:54:00 2024 +1300 dsdb: UF_SMARTCARD_REQUIRED can have a password expiry, if configured! While the passwords are random and rolled on the server, we can tell about the expiry by setting pwdLastSet to 0. Samba now honours the password expiry. This is only enabled for domain functional level 2016 and when msDS-ExpirePasswordsOnSmartCardOnlyAccounts is set to TRUE. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 3669479f22f2109a64250ffabd1f6453882d29f1 Author: Andrew Bartlett <abart...@samba.org> Date: Fri May 17 14:19:31 2024 +1200 dsdb: Reduce minimum maxPwdAge from 1 day to nil This allows us to have tests, which pass on Windows, that use a very short maxPwdAge. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 302619f66f94c981e30b1c2325e1c7c4e5ac963d Author: Andrew Bartlett <abart...@samba.org> Date: Mon May 20 17:13:53 2024 +1200 dsdb: Change the magic smartcard_reset to set AES keys like the krbtgt mode This is because the smartcard reset now generates all the keys on Windows, so we want to match Windows 2022 as at April 2024 behaviour. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 7c79abbab46193d8f84ac6f41b55b61228cf528f Author: Andrew Bartlett <abart...@samba.org> Date: Fri May 10 16:51:27 2024 +1200 python/samba/tests/krb5: PKINIT tests of passwords that are naturally expired The tests of passwords that will expire in the TGT lifetime fail against windows, we do not see the rotation in that case. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 044cc5386058e61cf2ec6842010dfb41e50495d1 Author: Andrew Bartlett <abart...@samba.org> Date: Fri May 17 12:17:40 2024 +1200 python/test/krb5: Use assertAlmostEqual in check_ticket_times() This allows Windows behaviour with clock skew to be allowed for. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 68fa90754fd0cee62af636ce37c212da07cd8c46 Author: Andrew Bartlett <abart...@samba.org> Date: Fri May 17 12:25:17 2024 +1200 python/tests/krb5: Move check_ticket_times() to kdc_base_test.py This will allow other parts of the testsuite to use this helpful function. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 48bff4b95f80c02f6701c0b4f5323e022e827a3e Author: Andrew Bartlett <abart...@samba.org> Date: Wed Apr 3 10:53:11 2024 +1300 python/samba/krb5: Add test for password rotation on UF_SMARCARD_REQUIRED accounts This demonstrates behaviour against a server presumed to be in FL 2016 what the impact of the msDS-ExpirePasswordsOnSmartCardOnlyAccounts attribute is. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit a85f4c661b14cf3b007fa1dae2b08464055ca025 Author: Andrew Bartlett <abart...@samba.org> Date: Wed Apr 3 08:26:04 2024 +1300 python/tests/krb5: Remove unused utf16pw variable Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 504a47ecfd6ddcca421549760caea8e2cd2448d2 Author: Andrew Bartlett <abart...@samba.org> Date: Thu May 2 16:02:58 2024 +1200 python/tests/krb5: Expect AES keys for UF_SMARTCARD_REQUIRED Windows 2022 at April 2024 has change and now includes the AES keys for accounts with UF_SMARTCARD_REQUIRED, so revert part of the change in b2fe1ea1c6aba116b31a1c803b4e0d36ac1a32ee. (This is an improvement to Windows security). Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit dc6c4b215e2b191945f5ad0202133cd3c47925e6 Author: Andrew Bartlett <abart...@samba.org> Date: Mon May 27 18:53:42 2024 +1200 python/samba/tests/krb5: Extend PKINIT tests to show kpasswd still works We have had confirmed from MS that this behaviour is both deliberate and required. Possession of the credential is (by the returned PAC containing the NT hash) possession of the password, and it must be possible to change the password to a known value otherwise DPAPI (local keychain) secured by this value can fail on the client. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15045 Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 4ec24a207648d9207649cd1c4364a1124db6f24d Author: Andrew Bartlett <abart...@samba.org> Date: Mon May 27 18:46:49 2024 +1200 python/samba/tests/krb5: Move get_kpasswd_sname() into raw_testcase() to allow broader use Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit b664392208c90fff63047b5faf30a33f95777ee2 Author: Andrew Bartlett <abart...@samba.org> Date: Mon May 20 14:07:46 2024 +1200 s4-auth: Use msDS-User-Account-Control-Computed for PW expiry check This centralises the check rather than checking the time in multiple spots. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit 737f2414062d694e54f60f0c6ef94b6ab4f33d19 Author: Andrew Bartlett <abart...@samba.org> Date: Wed Apr 3 11:52:28 2024 +1300 s4-auth: Update comment to mention 60mins in the NTLM grace period Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> commit e04eb9bb17000c6fa0be0e2e4f8ab17077bd4b38 Author: Andrew Bartlett <abart...@samba.org> Date: Tue Apr 23 16:17:04 2024 +1200 dsdb: Make argument order of dsdb_update_gmsa_{entry_,}keys() consistant with other uses Other functions in this file are TALLOC_CTX, struct ldb_context *, not the other way around. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Jo Sutton <josut...@catalyst.net.nz> ----------------------------------------------------------------------- Summary of changes: WHATSNEW.txt | 26 +- lib/ldb/include/ldb.h | 6 + python/samba/domain_update.py | 23 +- python/samba/provision/__init__.py | 4 +- python/samba/tests/dsdb_quiet_provision_tests.py | 10 + python/samba/tests/krb5/authn_policy_tests.py | 30 -- python/samba/tests/krb5/kdc_base_test.py | 34 +- python/samba/tests/krb5/kpasswd_tests.py | 4 - python/samba/tests/krb5/pkinit_tests.py | 630 +++++++++++++++++++++-- python/samba/tests/krb5/raw_testcase.py | 12 +- selftest/knownfail_heimdal_kdc | 2 - selftest/knownfail_mit_kdc_1_20 | 6 + source4/auth/auth.h | 1 + source4/auth/ntlm/auth_sam.c | 33 +- source4/auth/sam.c | 5 +- source4/dsdb/common/util.c | 12 +- source4/dsdb/gmsa/util.c | 23 +- source4/dsdb/gmsa/util.h | 8 +- source4/dsdb/samdb/ldb_modules/operational.c | 156 ++++-- source4/dsdb/samdb/ldb_modules/password_hash.c | 116 +++-- source4/dsdb/samdb/samdb.c | 7 +- source4/dsdb/samdb/samdb.h | 17 +- source4/kdc/db-glue.c | 293 ++++++++++- source4/kdc/hdb-samba4.c | 13 +- source4/kdc/kdc-glue.h | 3 +- source4/kdc/kdc-heimdal.c | 47 +- source4/kdc/kdc-proxy.c | 8 +- source4/kdc/kdc-server.h | 2 +- source4/kdc/kdc-service-mit.c | 13 - source4/kdc/mit_kdc_irpc.c | 15 + source4/kdc/mit_samba.c | 54 ++ source4/kdc/pac-glue.c | 1 + source4/kdc/samba_kdc.h | 13 + source4/kdc/wscript_build | 2 +- source4/ldap_server/ldap_backend.c | 5 +- source4/libcli/ldap/ldap_controls.c | 1 + source4/libnet/libnet_export_keytab.c | 32 ++ source4/setup/schema_samba4.ldif | 2 +- 38 files changed, 1382 insertions(+), 287 deletions(-) Changeset truncated at 500 lines: diff --git a/WHATSNEW.txt b/WHATSNEW.txt index 6d1368c42b1..be93dd5ae61 100644 --- a/WHATSNEW.txt +++ b/WHATSNEW.txt @@ -139,6 +139,31 @@ authentication and DNS functions. This is not supported in samba-tool yet. +Samba AD will rotate expired passwords on smartcard-required accounts +--------------------------------------------------------------------- + +Traditionally in AD, accounts set to be "smart card require for logon" +will have a password for NTLM fallback and local profile encryption +(Windows DPAPI). This password previously would not expire. + +Matching Windows behaviour, when the DC in a FL 2016 domain and the +msDS-ExpirePasswordsOnSmartCardOnlyAccounts attribute on the domain +root is set to TRUE, Samba will now expire these passwords and rotate +them shortly before they expire. + +Note that the password expiry time must be set to twice the TGT lifetime for +smooth operation, e.g. daily expiry given a default 10 hour TGT +lifetime, as the password is only rotated in the second half of its +life. Again, this matches the Windows behaviour. + +Provided the default 2016 schema is used, new Samba domains +provisioned with Samba 4.21 will have this enabled once the domain +functional level is set to 2016. + +NOTE: Domains upgraded from older Samba versions will not have this +set, even after the functional level preparation, matching the +behaviour of upgraded Windows AD domains. + REMOVED FEATURES ================ @@ -181,4 +206,3 @@ database (https://bugzilla.samba.org/). == Our Code, Our Bugs, Our Responsibility. == The Samba Team ====================================================================== - diff --git a/lib/ldb/include/ldb.h b/lib/ldb/include/ldb.h index 0a93b560180..f29392ad4ea 100644 --- a/lib/ldb/include/ldb.h +++ b/lib/ldb/include/ldb.h @@ -273,6 +273,12 @@ enum ldb_debug_level {LDB_DEBUG_FATAL, LDB_DEBUG_ERROR, */ #define LDB_FLG_DONT_CREATE_DB 64 +/** + * Allow DB create time flags that have meaning only to our + * calling application or modules. These must be in this range: + */ +#define LDB_FLG_PRIVATE_MASK 0xff000000 + /* structures for ldb_parse_tree handling code */ diff --git a/python/samba/domain_update.py b/python/samba/domain_update.py index e91bdf40dbb..2277cc10c18 100644 --- a/python/samba/domain_update.py +++ b/python/samba/domain_update.py @@ -92,15 +92,18 @@ class DomainUpdate(object): """Check and update a SAM database for domain updates""" def __init__(self, samdb, fix=False, + new_install=False, add_update_container=True): """ :param samdb: LDB database :param fix: Apply the update if the container is missing + :param new_install: Apply the update as per a new install (see op 88) :param add_update_container: Add the container at the end of the change :raise DomainUpdateException: """ self.samdb = samdb self.fix = fix + self.new_install = new_install self.add_update_container = add_update_container # TODO: In future we should check for inconsistencies when it claims it has been done self.check_update_applied = False @@ -521,19 +524,29 @@ otherWellKnownObjects: B:32:683A24E2E8164BD3AF86AC3C2CF3F981:%s ## Operation 88: {434bb40d-dbc9-4fe7-81d4-d57229f7b080} ## ## Add "msDS-ExpirePasswordsOnSmartCardOnlyAccounts" on the domain NC object - ## and set default value to FALSE + ## and set default value to FALSE (upgrades) or TRUE (new installs) + ## + ## See + ## https://learn.microsoft.com/en-us/windows-server/get-started/whats-new-in-windows-server-2016#rolling-public-key-only-users-ntlm-secrets + ## for justification of the observed behaviour that new installs + ## have this set to TRUE ## def operation_88(self, op): if self.update_exists(op): return self.raise_if_not_fix(op) - ldif = """ -dn: %s + if self.new_install: + expire_value = "TRUE" + else: + expire_value = "FALSE" + + ldif = f""" +dn: {self.domain_dn} changetype: modify add: msDS-ExpirePasswordsOnSmartCardOnlyAccounts -msDS-ExpirePasswordsOnSmartCardOnlyAccounts: FALSE -""" % str(self.domain_dn) +msDS-ExpirePasswordsOnSmartCardOnlyAccounts: {expire_value} +""" self.samdb.modify_ldif(ldif) diff --git a/python/samba/provision/__init__.py b/python/samba/provision/__init__.py index 80684c47522..dea50aa364e 100644 --- a/python/samba/provision/__init__.py +++ b/python/samba/provision/__init__.py @@ -2392,7 +2392,9 @@ def provision(logger, session_info, smbconf=None, try: from samba.domain_update import DomainUpdate - DomainUpdate(samdb, fix=True).check_updates_functional_level( + DomainUpdate(samdb, + new_install=True, + fix=True).check_updates_functional_level( adprep_level, DS_DOMAIN_FUNCTION_2008, update_revision=True, diff --git a/python/samba/tests/dsdb_quiet_provision_tests.py b/python/samba/tests/dsdb_quiet_provision_tests.py index 81ef3ceb74f..a7c9fbec83e 100644 --- a/python/samba/tests/dsdb_quiet_provision_tests.py +++ b/python/samba/tests/dsdb_quiet_provision_tests.py @@ -67,3 +67,13 @@ class DsdbQuietProvisionTests(TestCase): expression=f"(&(objectClass = msKds-ProvRootKey)(msKds-UseStartTime<={min_use_start_time}))") self.assertGreater(len(res), 0) + + def test_dsdb_smartcard_expire_set(self): + """In provision we set msDS-ExpirePasswordsOnSmartCardOnlyAccounts: TRUE for a new 2016 provision + """ + dn = self.samdb.get_default_basedn() + res = self.samdb.search(dn, + scope=ldb.SCOPE_BASE, + expression="(msDS-ExpirePasswordsOnSmartCardOnlyAccounts=TRUE)") + + self.assertEqual(len(res), 1) diff --git a/python/samba/tests/krb5/authn_policy_tests.py b/python/samba/tests/krb5/authn_policy_tests.py index 455ad36d479..a1a38a19449 100755 --- a/python/samba/tests/krb5/authn_policy_tests.py +++ b/python/samba/tests/krb5/authn_policy_tests.py @@ -1067,36 +1067,6 @@ class AuthnPolicyBaseTests(AuthLogTestBase, KdcTgsBaseTests): audit_event=server_policy_event, reason=server_policy_reason) - def check_ticket_times(self, - ticket_creds, - expected_life=None, - expected_renew_life=None): - ticket = ticket_creds.ticket_private - - authtime = ticket['authtime'] - starttime = ticket.get('starttime', authtime) - endtime = ticket['endtime'] - renew_till = ticket.get('renew-till', None) - - starttime = self.get_EpochFromKerberosTime(starttime) - - if expected_life is not None: - actual_end = self.get_EpochFromKerberosTime( - endtime.decode('ascii')) - actual_lifetime = actual_end - starttime - - self.assertEqual(expected_life, actual_lifetime) - - if renew_till is None: - self.assertIsNone(expected_renew_life) - else: - if expected_renew_life is not None: - actual_renew_till = self.get_EpochFromKerberosTime( - renew_till.decode('ascii')) - actual_renew_life = actual_renew_till - starttime - - self.assertEqual(expected_renew_life, actual_renew_life) - def _get_tgt(self, creds, *, armor_tgt=None, till=None, diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index feb576b8458..eb3497c554e 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -894,7 +894,6 @@ class KDCBaseTest(TestCaseInTempDir, RawKerberosTest): domain = samdb.domain_netbios_name().upper() password = generate_random_password(32, 32) - utf16pw = ('"%s"' % password).encode('utf-16-le') try: net_ctx.set_password(newpassword=password, @@ -2167,7 +2166,7 @@ class KDCBaseTest(TestCaseInTempDir, RawKerberosTest): # # The NT hash is different, as it is returned to the client in # the PAC so is visible in the network behaviour. - if force_nt4_hash or smartcard_required: + if force_nt4_hash: expected_etypes = {kcrypto.Enctype.RC4} keys = self.get_keys(creds, expected_etypes=expected_etypes) self.creds_set_keys(creds, keys) @@ -3786,3 +3785,34 @@ class KDCBaseTest(TestCaseInTempDir, RawKerberosTest): self.assertEqual(0, flags) return validation + + def check_ticket_times(self, + ticket_creds, + expected_life=None, + expected_renew_life=None, + delta=0): + ticket = ticket_creds.ticket_private + + authtime = ticket['authtime'] + starttime = ticket.get('starttime', authtime) + endtime = ticket['endtime'] + renew_till = ticket.get('renew-till', None) + + starttime = self.get_EpochFromKerberosTime(starttime) + + if expected_life is not None: + actual_end = self.get_EpochFromKerberosTime( + endtime.decode('ascii')) + actual_lifetime = actual_end - starttime + + self.assertAlmostEqual(expected_life, actual_lifetime, delta=delta) + + if renew_till is None: + self.assertIsNone(expected_renew_life) + else: + if expected_renew_life is not None: + actual_renew_till = self.get_EpochFromKerberosTime( + renew_till.decode('ascii')) + actual_renew_life = actual_renew_till - starttime + + self.assertAlmostEqual(expected_renew_life, actual_renew_life, delta=delta) diff --git a/python/samba/tests/krb5/kpasswd_tests.py b/python/samba/tests/krb5/kpasswd_tests.py index 0f1fe656f49..6f0b36a25f6 100755 --- a/python/samba/tests/krb5/kpasswd_tests.py +++ b/python/samba/tests/krb5/kpasswd_tests.py @@ -90,10 +90,6 @@ class KpasswdTests(KDCBaseTest): return creds - def get_kpasswd_sname(self): - return self.PrincipalName_create(name_type=NT_PRINCIPAL, - names=['kadmin', 'changepw']) - def get_ticket_lifetime(self, ticket): enc_part = ticket.ticket_private diff --git a/python/samba/tests/krb5/pkinit_tests.py b/python/samba/tests/krb5/pkinit_tests.py index c1f85c6c9cd..0c92801cbce 100755 --- a/python/samba/tests/krb5/pkinit_tests.py +++ b/python/samba/tests/krb5/pkinit_tests.py @@ -24,6 +24,7 @@ sys.path.insert(0, 'bin/python') os.environ['PYTHONUNBUFFERED'] = '1' from datetime import datetime, timedelta +import time from pyasn1.type import univ @@ -36,8 +37,12 @@ from cryptography.x509.oid import NameOID import ldb import samba.tests -from samba import credentials, ntstatus +from samba import credentials, generate_random_password, ntstatus +from samba.nt_time import (nt_time_delta_from_timedelta, + nt_now, string_from_nt_time) from samba.dcerpc import security, netlogon +from samba.dsdb import UF_PASSWORD_EXPIRED, UF_DONT_EXPIRE_PASSWD +from samba.tests.pso import PasswordSettings from samba.tests.krb5 import kcrypto from samba.tests.krb5.kdc_base_test import KDCBaseTest from samba.tests.krb5.raw_testcase import PkInit, RawKerberosTest @@ -51,6 +56,7 @@ from samba.tests.krb5.rfc4120_constants import ( KDC_ERR_PREAUTH_EXPIRED, KDC_ERR_PREAUTH_FAILED, KDC_ERR_PREAUTH_REQUIRED, + KPASSWD_SUCCESS, KU_PA_ENC_TIMESTAMP, NT_PRINCIPAL, NT_SRV_INST, @@ -66,6 +72,23 @@ SidType = RawKerberosTest.SidType global_asn1_print = False global_hexdump = False +def set_ExpirePasswordsOnSmartCardOnlyAccounts(samdb, val): + msg = ldb.Message() + msg.dn = samdb.get_default_basedn() + + # Allow val to be True, False, strings or message elements + if val is True: + val = "TRUE" + elif val is False: + val = "FALSE" + elif val is None: + val = [] + + msg["msDS-ExpirePasswordsOnSmartCardOnlyAccounts"] \ + = ldb.MessageElement(val, + ldb.FLAG_MOD_REPLACE, + "msDS-ExpirePasswordsOnSmartCardOnlyAccounts") + samdb.modify(msg) class PkInitTests(KDCBaseTest): @classmethod @@ -77,16 +100,23 @@ class PkInitTests(KDCBaseTest): self.do_asn1_print = global_asn1_print self.do_hexdump = global_hexdump - def _get_creds(self, account_type=KDCBaseTest.AccountType.USER, use_cache=False, smartcard_required=False): + def _get_creds(self, + account_type=KDCBaseTest.AccountType.USER, + use_cache=False, + smartcard_required=False, + assigned_policy=None): """Return credentials with an account having a UPN for performing PK-INIT.""" samdb = self.get_samdb() realm = samdb.domain_dns_name().upper() + opts={'upn': f'{{account}}.{realm}@{realm}', + 'smartcard_required': smartcard_required} + if assigned_policy is not None: + opts['assigned_policy'] = str(assigned_policy.dn) return self.get_cached_creds( account_type=account_type, - opts={'upn': f'{{account}}.{realm}@{realm}', - 'smartcard_required': smartcard_required}, + opts=opts, use_cache=use_cache) def test_pkinit_no_des3(self): @@ -588,6 +618,59 @@ class PkInitTests(KDCBaseTest): logon_type=netlogon.NetlogonNetworkInformation, expect_error=ntstatus.NT_STATUS_WRONG_PASSWORD) + def _test_samlogon_smartcard_required_expired(self, smartcard_pw_expire): + """Test SamLogon with an account set to smartcard login required. No actual PK-INIT in this test.""" + samdb = self.get_samdb() + msgs = samdb.search(base=samdb.get_default_basedn(), + scope=ldb.SCOPE_BASE, + attrs=["msDS-ExpirePasswordsOnSmartCardOnlyAccounts"]) + msg = msgs[0] + + try: + old_ExpirePasswordsOnSmartCardOnlyAccounts = msg["msDS-ExpirePasswordsOnSmartCardOnlyAccounts"] + except KeyError: + old_ExpirePasswordsOnSmartCardOnlyAccounts = None + + self.addCleanup(set_ExpirePasswordsOnSmartCardOnlyAccounts, + samdb, old_ExpirePasswordsOnSmartCardOnlyAccounts) + + # Enable auto-rotation for this test + set_ExpirePasswordsOnSmartCardOnlyAccounts(samdb, smartcard_pw_expire) + + client_creds = self._get_creds(smartcard_required=True) + + client_creds.set_kerberos_state(credentials.AUTO_USE_KERBEROS) + + msg = ldb.Message() + msg.dn = client_creds.get_dn() + + # Ideally we would set this to a time just long enough for the + # password to expire, but we are unable to do that. + # + # 0 means "must change on first login" + msg["pwdLastSet"] = \ + ldb.MessageElement(str(0), + ldb.FLAG_MOD_REPLACE, + "pwdLastSet") + samdb.modify(msg) + + # This shows that the magic rotation behaviour is not + # triggered in SamLogon + self._test_samlogon( + creds=client_creds, + logon_type=netlogon.NetlogonInteractiveInformation, + expect_error=ntstatus.NT_STATUS_SMARTCARD_LOGON_REQUIRED) + + self._test_samlogon(creds=client_creds, + logon_type=netlogon.NetlogonNetworkInformation, + expect_error=ntstatus.NT_STATUS_WRONG_PASSWORD) + + def test_samlogon_smartcard_required_expired(self): + self._test_samlogon_smartcard_required_expired(True) + + def test_samlogon_smartcard_required_expired_disabled(self): + self._test_samlogon_smartcard_required_expired(False) + def test_pkinit_ntlm_from_pac(self): """Test public-key PK-INIT to get an NT hash and confirm NTLM authentication is possible with it.""" @@ -629,8 +712,11 @@ class PkInitTests(KDCBaseTest): freshness_token = self.create_freshness_token() + # The hash will not match as UF_SMARTCARD_REQUIRED at creation + # time make the password random kdc_exchange_dict = self._pkinit_req(client_creds, krbtgt_creds, - freshness_token=freshness_token) + freshness_token=freshness_token, + expect_matching_nt_hash_in_pac=False) nt_hash_from_pac = kdc_exchange_dict['nt_hash_from_pac'] client_creds.set_nt_hash(nt_hash_from_pac, @@ -655,34 +741,58 @@ class PkInitTests(KDCBaseTest): self._test_samlogon(creds=client_creds, logon_type=netlogon.NetlogonNetworkInformation) - def test_pkinit_ntlm_from_pac_must_change_now(self): - """Test public-key PK-INIT to get an NT hash and confirm NTLM - authentication is possible with it.""" + def _test_pkinit_ntlm_from_pac_must_change_now(self, smartcard_pw_expire): + """Test public-key PK-INIT on an account set to 'must change now'. + This shows that PKINIT is not available for these accounts and no + auto-rollover happens because UF_SMARTCARD_REQUIRED is not set""" + samdb = self.get_samdb() + + msgs = samdb.search(base=samdb.get_default_basedn(), + scope=ldb.SCOPE_BASE, + attrs=["msDS-ExpirePasswordsOnSmartCardOnlyAccounts"]) + msg = msgs[0] + + try: + old_ExpirePasswordsOnSmartCardOnlyAccounts = msg["msDS-ExpirePasswordsOnSmartCardOnlyAccounts"] + except KeyError: + old_ExpirePasswordsOnSmartCardOnlyAccounts = None + + self.addCleanup(set_ExpirePasswordsOnSmartCardOnlyAccounts, + samdb, old_ExpirePasswordsOnSmartCardOnlyAccounts) + + # Enable auto-rotation for this test + set_ExpirePasswordsOnSmartCardOnlyAccounts(samdb, smartcard_pw_expire) + client_creds = self._get_creds() client_creds.set_kerberos_state(credentials.AUTO_USE_KERBEROS) - msg = ldb.Message() - msg.dn = client_creds.get_dn() + mod_msg = ldb.Message() + mod_msg.dn = client_creds.get_dn() # Ideally we would set this to a time just long enough for the - # password to expire, but we are unable to do that. + # password to expire, but this is good enough # # 0 means "must change on first login" - msg["pwdLastSet"] = \ + mod_msg["pwdLastSet"] = \ ldb.MessageElement(str(0), ldb.FLAG_MOD_REPLACE, "pwdLastSet") - samdb = self.get_samdb() - samdb.modify(msg) + samdb.modify(mod_msg) krbtgt_creds = self.get_krbtgt_creds() freshness_token = self.create_freshness_token() + # Windows does not send an NTSTATUS in this case for an + # expired password against PKINIT, but will for ENC-TS, + # However Samba on Heimdal is consistent between both, so we + # must set expect_status=None to allow the test to pass + # against both. self._pkinit_req(client_creds, krbtgt_creds, freshness_token=freshness_token, expect_error=KDC_ERR_KEY_EXPIRED, - expect_edata=True + expect_edata=True, + expected_status=ntstatus.NT_STATUS_PASSWORD_MUST_CHANGE, ) # AS-REQ will not succeed, password is still expired @@ -706,9 +816,35 @@ class PkInitTests(KDCBaseTest): logon_type=netlogon.NetlogonNetworkInformation, expect_error=ntstatus.NT_STATUS_PASSWORD_MUST_CHANGE) - def test_pkinit_ntlm_from_pac_smartcard_required_must_change_now(self): + def test_pkinit_ntlm_from_pac_must_change_now(self): + self._test_pkinit_ntlm_from_pac_must_change_now(smartcard_pw_expire=True) + + def test_pkinit_ntlm_from_pac_must_change_now_rotate_disabled(self): + self._test_pkinit_ntlm_from_pac_must_change_now(smartcard_pw_expire=False) + + def _test_pkinit_ntlm_from_pac_smartcard_required_must_change_now(self, smartcard_pw_expire): -- Samba Shared Repository