diff --git a/Fix-LDAP-policy-enforcement-of-pw_expiration.patch b/Fix-LDAP-policy-enforcement-of-pw_expiration.patch new file mode 100644 index 0000000..45b0484 --- /dev/null +++ b/Fix-LDAP-policy-enforcement-of-pw_expiration.patch @@ -0,0 +1,302 @@ +From d62cb044abe57eda1216f9ab97f50bd178f1d495 Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Tue, 17 Dec 2019 17:37:41 -0500 +Subject: [PATCH] Fix LDAP policy enforcement of pw_expiration + +In the LDAP backend, the change mask is used to determine what LDAP +attributes to update. As a result, password expiration was not set +from policy when running during addprinc, among other issues. +However, when the mask did not contain KADM5_PRINCIPAL, pw_expiration +would be applied regardless, which meant that (for instance) changing +the password would cause the password application to be applied. + +Remove the check for KADM5_PRINCIPAL, and fix the mask to contain +KADM5_PW_EXPIRATION where appropriate. Add a regression test to +t_kdb.py. + +[ghudson@mit.edu: also set KADM5_ATTRIBUTES for randkey and setkey +since they both unset KRB5_KDB_REQUIRES_PWCHANGE; edited comments and +commit message] + +ticket: 8861 (new) +tags: pullup +target_version: 1.17-next + +(cherry picked from commit 6b004dd5739bded71be4290c11e7ac3a816c7e09) +--- + src/lib/kadm5/srv/svr_principal.c | 92 +++++++++---------- + .../kdb/ldap/libkdb_ldap/ldap_principal2.c | 13 --- + src/tests/t_kdb.py | 17 ++++ + 3 files changed, 60 insertions(+), 62 deletions(-) + +diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c +index a1ecdbfc4..35bbf1218 100644 +--- a/src/lib/kadm5/srv/svr_principal.c ++++ b/src/lib/kadm5/srv/svr_principal.c +@@ -356,6 +356,11 @@ kadm5_create_principal_3(void *server_handle, + kdb = calloc(1, sizeof(*kdb)); + if (kdb == NULL) + return ENOMEM; ++ ++ /* In all cases the principal entry is new and key data is set; let the ++ * database provider know. */ ++ kdb->mask = mask | KADM5_KEY_DATA | KADM5_PRINCIPAL; ++ + memset(&adb, 0, sizeof(osa_princ_ent_rec)); + + /* +@@ -405,14 +410,12 @@ kadm5_create_principal_3(void *server_handle, + kdb->expiration = handle->params.expiration; + + kdb->pw_expiration = 0; +- if (have_polent) { +- if(polent.pw_max_life) +- kdb->pw_expiration = ts_incr(now, polent.pw_max_life); +- else +- kdb->pw_expiration = 0; +- } +- if ((mask & KADM5_PW_EXPIRATION)) ++ if (mask & KADM5_PW_EXPIRATION) { + kdb->pw_expiration = entry->pw_expiration; ++ } else if (have_polent && polent.pw_max_life) { ++ kdb->mask |= KADM5_PW_EXPIRATION; ++ kdb->pw_expiration = ts_incr(now, polent.pw_max_life); ++ } + + kdb->last_success = 0; + kdb->last_failed = 0; +@@ -503,9 +506,6 @@ kadm5_create_principal_3(void *server_handle, + adb.policy = entry->policy; + } + +- /* In all cases key and the principal data is set, let the database provider know */ +- kdb->mask = mask | KADM5_KEY_DATA | KADM5_PRINCIPAL ; +- + /* store the new db entry */ + ret = kdb_put_entry(handle, kdb, &adb); + +@@ -601,6 +601,9 @@ kadm5_modify_principal(void *server_handle, + if (ret) + return(ret); + ++ /* Let the mask propagate to the database provider. */ ++ kdb->mask = mask; ++ + /* + * This is pretty much the same as create ... + */ +@@ -616,11 +619,15 @@ kadm5_modify_principal(void *server_handle, + free(adb.policy); + adb.policy = strdup(entry->policy); + } +- if (have_pol) { ++ ++ if (mask & KADM5_PW_EXPIRATION) { ++ kdb->pw_expiration = entry->pw_expiration; ++ } else if (have_pol) { + /* set pw_max_life based on new policy */ ++ kdb->mask |= KADM5_PW_EXPIRATION; + if (pol.pw_max_life) { + ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb, +- &(kdb->pw_expiration)); ++ &kdb->pw_expiration); + if (ret) + goto done; + kdb->pw_expiration = ts_incr(kdb->pw_expiration, pol.pw_max_life); +@@ -642,8 +649,6 @@ kadm5_modify_principal(void *server_handle, + kdb->max_life = entry->max_life; + if ((mask & KADM5_PRINC_EXPIRE_TIME)) + kdb->expiration = entry->princ_expire_time; +- if (mask & KADM5_PW_EXPIRATION) +- kdb->pw_expiration = entry->pw_expiration; + if (mask & KADM5_MAX_RLIFE) + kdb->max_renewable_life = entry->max_renewable_life; + +@@ -682,9 +687,6 @@ kadm5_modify_principal(void *server_handle, + kdb->fail_auth_count = 0; + } + +- /* let the mask propagate to the database provider */ +- kdb->mask = mask; +- + ret = k5_kadm5_hook_modify(handle->context, handle->hook_handles, + KADM5_HOOK_STAGE_PRECOMMIT, entry, mask); + if (ret) +@@ -1362,6 +1364,11 @@ kadm5_chpass_principal_3(void *server_handle, + if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) + return(ret); + ++ /* We will always be changing the key data, attributes, auth failure count, ++ * and password expiration time. */ ++ kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_FAIL_AUTH_COUNT | ++ KADM5_PW_EXPIRATION; ++ + ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple, + &new_n_ks_tuple, &new_ks_tuple); + if (ret) +@@ -1407,6 +1414,7 @@ kadm5_chpass_principal_3(void *server_handle, + if (ret) + goto done; + ++ kdb->pw_expiration = 0; + if ((adb.aux_attributes & KADM5_POLICY)) { + /* the policy was loaded before */ + +@@ -1439,10 +1447,6 @@ kadm5_chpass_principal_3(void *server_handle, + + if (pol.pw_max_life) + kdb->pw_expiration = ts_incr(now, pol.pw_max_life); +- else +- kdb->pw_expiration = 0; +- } else { +- kdb->pw_expiration = 0; + } + + #ifdef USE_PASSWORD_SERVER +@@ -1481,11 +1485,6 @@ kadm5_chpass_principal_3(void *server_handle, + /* unlock principal on this KDC */ + kdb->fail_auth_count = 0; + +- /* key data and attributes changed, let the database provider know */ +- kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | +- KADM5_FAIL_AUTH_COUNT; +- /* | KADM5_CPW_FUNCTION */ +- + if (hist_added) + kdb->mask |= KADM5_KEY_HIST; + +@@ -1560,6 +1559,11 @@ kadm5_randkey_principal_3(void *server_handle, + if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) + return(ret); + ++ /* We will always be changing the key data, attributes, auth failure count, ++ * and password expiration time. */ ++ kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_FAIL_AUTH_COUNT | ++ KADM5_PW_EXPIRATION; ++ + ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple, + &new_n_ks_tuple, &new_ks_tuple); + if (ret) +@@ -1599,14 +1603,10 @@ kadm5_randkey_principal_3(void *server_handle, + if (ret) + goto done; + } +- if (have_pol) { +- if (pol.pw_max_life) +- kdb->pw_expiration = ts_incr(now, pol.pw_max_life); +- else +- kdb->pw_expiration = 0; +- } else { +- kdb->pw_expiration = 0; +- } ++ ++ kdb->pw_expiration = 0; ++ if (have_pol && pol.pw_max_life) ++ kdb->pw_expiration = ts_incr(now, pol.pw_max_life); + + ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now); + if (ret) +@@ -1624,10 +1624,6 @@ kadm5_randkey_principal_3(void *server_handle, + goto done; + } + +- /* key data changed, let the database provider know */ +- kdb->mask = KADM5_KEY_DATA | KADM5_FAIL_AUTH_COUNT; +- /* | KADM5_RANDKEY_USED */; +- + ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles, + KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold, + new_n_ks_tuple, new_ks_tuple, NULL); +@@ -1763,6 +1759,11 @@ kadm5_setkey_principal_4(void *server_handle, krb5_principal principal, + if (ret) + return ret; + ++ /* We will always be changing the key data, attributes, auth failure count, ++ * and password expiration time. */ ++ kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_FAIL_AUTH_COUNT | ++ KADM5_PW_EXPIRATION; ++ + if (kvno == 0) { + /* Pick the next kvno. */ + for (i = 0; i < kdb->n_key_data; i++) { +@@ -1864,14 +1865,10 @@ kadm5_setkey_principal_4(void *server_handle, krb5_principal principal, + if (ret) + goto done; + } +- if (have_pol) { +- if (pol.pw_max_life) +- kdb->pw_expiration = ts_incr(now, pol.pw_max_life); +- else +- kdb->pw_expiration = 0; +- } else { +- kdb->pw_expiration = 0; +- } ++ ++ kdb->pw_expiration = 0; ++ if (have_pol && pol.pw_max_life) ++ kdb->pw_expiration = ts_incr(now, pol.pw_max_life); + + ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now); + if (ret) +@@ -1880,9 +1877,6 @@ kadm5_setkey_principal_4(void *server_handle, krb5_principal principal, + /* Unlock principal on this KDC. */ + kdb->fail_auth_count = 0; + +- /* key data changed, let the database provider know */ +- kdb->mask = KADM5_KEY_DATA | KADM5_FAIL_AUTH_COUNT; +- + ret = kdb_put_entry(handle, kdb, &adb); + if (ret) + goto done; +diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c +index ee9c02814..fa0a2c683 100644 +--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c ++++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c +@@ -1233,19 +1233,6 @@ krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry, + goto cleanup; + } + +- if (!(entry->mask & KADM5_PRINCIPAL)) { +- memset(strval, 0, sizeof(strval)); +- if ((strval[0]=getstringtime(entry->pw_expiration)) == NULL) +- goto cleanup; +- if ((st=krb5_add_str_mem_ldap_mod(&mods, +- "krbpasswordexpiration", +- LDAP_MOD_REPLACE, strval)) != 0) { +- free (strval[0]); +- goto cleanup; +- } +- free (strval[0]); +- } +- + /* Update last password change whenever a new key is set */ + { + krb5_timestamp last_pw_changed; +diff --git a/src/tests/t_kdb.py b/src/tests/t_kdb.py +index 7271fcbbd..d18f672c1 100755 +--- a/src/tests/t_kdb.py ++++ b/src/tests/t_kdb.py +@@ -494,6 +494,23 @@ else: + realm.run([kadminl, 'modprinc', '-pwexpire', '2040-02-03', 'user']) + realm.run([kadminl, 'getprinc', 'user'], expected_msg=' 2040\n') + ++# Regression test for #8861 (pw_expiration policy enforcement). ++mark('pw_expiration propogation') ++# Create a policy with a max life and verify its application. ++realm.run([kadminl, 'addpol', '-maxlife', '1s', 'pw_e']) ++realm.run([kadminl, 'addprinc', '-policy', 'pw_e', '-pw', 'password', ++ 'pwuser']) ++out = realm.run([kadminl, 'getprinc', 'pwuser'], ++ expected_msg='Password expiration date: ') ++if 'Password expiration date: [never]' in out: ++ fail('pw_expiration not applied at principal creation') ++# Unset the policy max life and verify its application during password ++# change. ++realm.run([kadminl, 'modpol', '-maxlife', '0', 'pw_e']) ++realm.run([kadminl, 'cpw', '-pw', 'password_', 'pwuser']) ++realm.run([kadminl, 'getprinc', 'pwuser'], ++ expected_msg='Password expiration date: [never]') ++ + realm.stop() + + # Briefly test dump and load. diff --git a/Fix-handling-of-invalid-CAMMAC-service-verifier.patch b/Fix-handling-of-invalid-CAMMAC-service-verifier.patch new file mode 100644 index 0000000..bc285c2 --- /dev/null +++ b/Fix-handling-of-invalid-CAMMAC-service-verifier.patch @@ -0,0 +1,30 @@ +From 87d0a1364b9ddb4b9ed8dfaee3022172bfb879ba Mon Sep 17 00:00:00 2001 +From: Jeffrey Arbuckle +Date: Sat, 21 Dec 2019 22:59:20 -0500 +Subject: [PATCH] Fix handling of invalid CAMMAC service verifier + +In extract_cammacs(), avoid a null dereference if the CAMMAC service +verifier is invalid or the CAMMAC is empty. + +ticket: 8856 +tags: pullup +target_version: 1.17-next + +(cherry picked from commit 8451ff6ed57361de585a35f35a39c54dc48172c7) +--- + src/lib/krb5/krb/authdata.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/lib/krb5/krb/authdata.c b/src/lib/krb5/krb/authdata.c +index 3e7dfbe49..d3096e5a2 100644 +--- a/src/lib/krb5/krb/authdata.c ++++ b/src/lib/krb5/krb/authdata.c +@@ -557,6 +557,8 @@ extract_cammacs(krb5_context kcontext, krb5_authdata **cammacs, + if (ret && ret != KRB5KRB_AP_ERR_BAD_INTEGRITY) + goto cleanup; + ret = 0; ++ if (elements == NULL) ++ continue; + + /* Add the verified elements to list and free the container array. */ + for (n_elements = 0; elements[n_elements] != NULL; n_elements++); diff --git a/krb5.spec b/krb5.spec index 9ed8d49..c7f4735 100644 --- a/krb5.spec +++ b/krb5.spec @@ -18,7 +18,7 @@ Summary: The Kerberos network authentication system Name: krb5 Version: 1.17.1 # for prerelease, should be e.g., 0.% {prerelease}.1% { ?dist } (without spaces) -Release: 4%{?dist} +Release: 5%{?dist} # rharwood has trust path to signing key and verifies on check-in Source0: https://web.mit.edu/kerberos/dist/krb5/1.17/krb5-%{version}%{prerelease}.tar.gz @@ -119,6 +119,8 @@ Patch171: Don-t-warn-in-kadmin-when-no-policy-is-specified.patch Patch172: Allow-client-canonicalization-in-non-krbtgt-AS-REP.patch Patch173: Do-not-always-canonicalize-enterprise-principals.patch Patch174: Fix-xdr_bytes-strict-aliasing-violations.patch +Patch175: Fix-handling-of-invalid-CAMMAC-service-verifier.patch +Patch176: Fix-LDAP-policy-enforcement-of-pw_expiration.patch License: MIT URL: https://web.mit.edu/kerberos/www/ @@ -692,6 +694,10 @@ exit 0 %{_libdir}/libkadm5srv_mit.so.* %changelog +* Wed Jan 08 2020 Robbie Harwood - 1.17.1-5 +- Fix LDAP policy enforcement of pw_expiration +- Fix handling of invalid CAMMAC service verifier + * Mon Jan 06 2020 Robbie Harwood - 1.17.1-4 - Fix xdr_bytes() strict-aliasing violations