[SCM] Samba Shared Repository - branch master updated

Andrew Bartlett abartlet at samba.org
Mon Jun 10 05:33:01 UTC 2024


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 <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>
    
    Autobuild-User(master): Andrew Bartlett <abartlet at samba.org>
    Autobuild-Date(master): Mon Jun 10 05:32:54 UTC 2024 on atb-devel-224

commit 15686fec9819267f69a600cff859e52f77a64cef
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit f3528808aba9419c0895bdb709e1b0dc0bdced1e
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Mon May 27 11:51:59 2024 +1200

    WHATSNEW: Mention msDS-ExpirePasswordsOnSmartCardOnlyAccounts behaviour
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 2854ef29b82d89fb5b5c9d8414227988783120b9
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit dee3c7be5846ae7c0952b1ace94dee23241dc794
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 491b79d445d54f56ba8dfea978da322e1fc16c44
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 8afe27058b08ff30d2650bb4fec92f56fa418e6a
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 1e1c80656f7d19d1cfde118bdba75a576da978f7
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Pair-programmed-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit d03b3faeb8e6e3446c606b3f167d2e9917514fda
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 1dcd8be8f06066f8aed7765c4e92259ce1371991
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit fe61009002db897fb8632954af8203de28c24d7d
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit e178f6b0e962b0ac96d447196765c21c770ede63
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 9ba5ebf4af7c5170dc2abf2307e3c47d7d250f5f
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 09ae48b415b2b50dbf4600e9c7f9cb4ec65a6263
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 1bcc9f00157884a8870e32dda258640c2c3a0c79
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit cc3ea4ed571ca033c357cebeea4511ba6dd9fa81
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 3669479f22f2109a64250ffabd1f6453882d29f1
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 302619f66f94c981e30b1c2325e1c7c4e5ac963d
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 7c79abbab46193d8f84ac6f41b55b61228cf528f
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 044cc5386058e61cf2ec6842010dfb41e50495d1
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 68fa90754fd0cee62af636ce37c212da07cd8c46
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 48bff4b95f80c02f6701c0b4f5323e022e827a3e
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit a85f4c661b14cf3b007fa1dae2b08464055ca025
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Wed Apr 3 08:26:04 2024 +1300

    python/tests/krb5: Remove unused utf16pw variable
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 504a47ecfd6ddcca421549760caea8e2cd2448d2
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit dc6c4b215e2b191945f5ad0202133cd3c47925e6
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 4ec24a207648d9207649cd1c4364a1124db6f24d
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit b664392208c90fff63047b5faf30a33f95777ee2
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 737f2414062d694e54f60f0c6ef94b6ab4f33d19
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit e04eb9bb17000c6fa0be0e2e4f8ab17077bd4b38
Author: Andrew Bartlett <abartlet at 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 <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at 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



More information about the samba-cvs mailing list