From cc3ea4ed571ca033c357cebeea4511ba6dd9fa81 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 3 Apr 2024 11:54:00 +1300 Subject: [PATCH] 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 Reviewed-by: Jo Sutton --- selftest/knownfail_heimdal_kdc | 8 +- source4/dsdb/samdb/ldb_modules/operational.c | 135 ++++++++++++++----- 2 files changed, 104 insertions(+), 39 deletions(-) diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index 41fd0587267..fd23bdd740d 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -75,14 +75,8 @@ ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_ntlm_from_pac_must_change_now ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_ntlm_from_pac_smartcard_required_must_change_now\( ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_smartcard_required_must_change_now\( -^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_smartcard_required_must_change_before_tgt_expiry ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_smartcard_required_must_change_expired -^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_smartcard_required_must_change_short_tgt -^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_smartcard_required_must_change_soon -^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_smartcard_required_must_change_before_tgt_expiry -^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_smartcard_required_must_change_expired -^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_smartcard_required_must_change_short_tgt -^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_smartcard_required_must_change_soon +^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_smartcard_required_must_change_soon\( # # Windows 2000 PK-INIT tests # diff --git a/source4/dsdb/samdb/ldb_modules/operational.c b/source4/dsdb/samdb/ldb_modules/operational.c index 867bc4fff95..f7366251c22 100644 --- a/source4/dsdb/samdb/ldb_modules/operational.c +++ b/source4/dsdb/samdb/ldb_modules/operational.c @@ -99,6 +99,26 @@ enum search_type { ACCOUNT_GROUPS }; +enum expire_uf_smartcard { + EXPIRE_UF_SMARTCARD_UNINIT = 0, + NO_EXPIRE_UF_SMARTCARD = 1, + EXPIRE_UF_SMARTCARD = 2 +}; + +struct operational_context { + struct ldb_module *module; + struct ldb_request *req; + enum ldb_scope scope; + const char * const *attrs; + struct ldb_parse_tree *tree; + struct op_controls_flags* controls_flags; + struct op_attributes_operations *list_operations; + unsigned int list_operations_size; + struct op_attributes_replace *attrs_to_replace; + unsigned int attrs_to_replace_size; + enum expire_uf_smartcard expire_passwords_onsmartcardonlyaccounts; +}; + static int get_pso_for_user(struct ldb_module *module, struct ldb_message *user_msg, struct ldb_request *parent, @@ -701,7 +721,6 @@ static int construct_msds_keyversionnumber(struct ldb_module *module, } #define _UF_NO_EXPIRY_ACCOUNTS ( \ - UF_SMARTCARD_REQUIRED | \ UF_DONT_EXPIRE_PASSWD | \ UF_TRUST_ACCOUNT_MASK \ ) @@ -737,13 +756,62 @@ static int64_t get_user_max_pwd_age(struct ldb_module *module, return samdb_search_int64(ldb, user_msg, 0, nc_root, "maxPwdAge", NULL); } +static enum expire_uf_smartcard get_expire_passwords_onsmartcardonlyaccounts(struct ldb_module *module, + struct operational_context *ac) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + if (ac->expire_passwords_onsmartcardonlyaccounts != EXPIRE_UF_SMARTCARD_UNINIT) { + return ac->expire_passwords_onsmartcardonlyaccounts; + } + + if (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2016) { + ac->expire_passwords_onsmartcardonlyaccounts + = NO_EXPIRE_UF_SMARTCARD; + } else { + const char *base_attrs[] = { "msDS-ExpirePasswordsOnSmartCardOnlyAccounts", + NULL }; + struct ldb_message *base_msg; + bool attr_in_ldb; + + int ldb_ret = dsdb_search_one(ldb, ac, + &base_msg, + ldb_get_default_basedn(ldb), + LDB_SCOPE_BASE, + base_attrs, 0, NULL); + if (ldb_ret != LDB_SUCCESS) { + DBG_WARNING("could not find own base DN in DB: %s\n", ldb_errstring(ldb)); + return EXPIRE_UF_SMARTCARD_UNINIT; + } + /* + * This attribute is to allow these passwords to + * expire, and if they expire to rotate them. TRUE + * means rotate, FALSE or absent meant never allow to + * expire. + */ + attr_in_ldb = ldb_msg_find_attr_as_bool(base_msg, + "msDS-ExpirePasswordsOnSmartCardOnlyAccounts", + false); + talloc_free(base_msg); + if (attr_in_ldb) { + ac->expire_passwords_onsmartcardonlyaccounts + = EXPIRE_UF_SMARTCARD; + } else { + ac->expire_passwords_onsmartcardonlyaccounts + = NO_EXPIRE_UF_SMARTCARD; + } + } + return ac->expire_passwords_onsmartcardonlyaccounts; +} + + /* calculate msDS-UserPasswordExpiryTimeComputed */ static NTTIME get_msds_user_password_expiry_time_computed(struct ldb_module *module, - struct ldb_message *msg, - struct ldb_request *parent, - struct ldb_dn *domain_dn) + struct operational_context *ac, + struct ldb_message *msg, + struct ldb_request *parent, + struct ldb_dn *domain_dn) { int64_t pwdLastSet, maxPwdAge; uint32_t userAccountControl; @@ -756,6 +824,15 @@ static NTTIME get_msds_user_password_expiry_time_computed(struct ldb_module *mod return INT64_MAX; } + if (userAccountControl & UF_SMARTCARD_REQUIRED) { + enum expire_uf_smartcard expire_uf_smartcard = + get_expire_passwords_onsmartcardonlyaccounts(module, ac); + + if (expire_uf_smartcard != EXPIRE_UF_SMARTCARD) { + return INT64_MAX; + } + } + pwdLastSet = ldb_msg_find_attr_as_int64(msg, "pwdLastSet", 0); if (pwdLastSet == 0) { return 0; @@ -857,12 +934,15 @@ static int construct_msds_user_account_control_computed(struct ldb_module *modul struct ldb_message *msg, enum ldb_scope scope, struct ldb_request *parent, struct ldb_reply *ares) { - uint32_t userAccountControl; uint32_t msDS_User_Account_Control_Computed = 0; struct ldb_context *ldb = ldb_module_get_ctx(module); NTTIME now; struct ldb_dn *nc_root; + NTTIME must_change_time; int ret; + struct operational_context *ac + = talloc_get_type_abort(parent->context, + struct operational_context); ret = dsdb_find_nc_root(ldb, msg, msg->dn, &nc_root); if (ret != 0) { @@ -898,19 +978,15 @@ static int construct_msds_user_account_control_computed(struct ldb_module *modul } } - userAccountControl = ldb_msg_find_attr_as_uint(msg, - "userAccountControl", - 0); - if (!(userAccountControl & _UF_NO_EXPIRY_ACCOUNTS)) { - NTTIME must_change_time - = get_msds_user_password_expiry_time_computed(module, - msg, - parent, - nc_root); - /* check for expired password */ - if (must_change_time < now) { - msDS_User_Account_Control_Computed |= UF_PASSWORD_EXPIRED; - } + must_change_time + = get_msds_user_password_expiry_time_computed(module, + ac, + msg, + parent, + nc_root); + /* check for expired password */ + if (must_change_time < now) { + msDS_User_Account_Control_Computed |= UF_PASSWORD_EXPIRED; } return samdb_msg_add_int64(ldb, @@ -927,6 +1003,9 @@ static int construct_msds_user_password_expiry_time_computed(struct ldb_module * struct ldb_request *parent, struct ldb_reply *ares) { struct ldb_context *ldb = ldb_module_get_ctx(module); + struct operational_context *ac + = talloc_get_type_abort(parent->context, + struct operational_context); struct ldb_dn *nc_root; int64_t password_expiry_time; int ret; @@ -946,7 +1025,9 @@ static int construct_msds_user_password_expiry_time_computed(struct ldb_module * } password_expiry_time - = get_msds_user_password_expiry_time_computed(module, msg, + = get_msds_user_password_expiry_time_computed(module, + ac, + msg, parent, nc_root); return samdb_msg_add_int64(ldb, @@ -1562,19 +1643,6 @@ failed: hook search operations */ -struct operational_context { - struct ldb_module *module; - struct ldb_request *req; - enum ldb_scope scope; - const char * const *attrs; - struct ldb_parse_tree *tree; - struct op_controls_flags* controls_flags; - struct op_attributes_operations *list_operations; - unsigned int list_operations_size; - struct op_attributes_replace *attrs_to_replace; - unsigned int attrs_to_replace_size; -}; - static int operational_callback(struct ldb_request *req, struct ldb_reply *ares) { struct operational_context *ac; @@ -1764,6 +1832,9 @@ static int operational_search(struct ldb_module *module, struct ldb_request *req return ldb_oom(ldb); } + ac->expire_passwords_onsmartcardonlyaccounts + = EXPIRE_UF_SMARTCARD_UNINIT; + ac->module = module; ac->req = req; ac->scope = req->op.search.scope; -- 2.47.3