ipa-pwd-extop: don't check password policy for non-Kerberos account set by DM or a passsync manager

Password changes performed by cn=Directory Manager are excluded from
password policy checks according to [1]. This is correctly handled by
ipa-pwd-extop in case of a normal Kerberos principal in IPA. However,
non-kerberos accounts were not excluded from the check.

As result, password updates for PKI CA admin account in o=ipaca were
failing if a password policy does not allow a password reuse. We are
re-setting the password for PKI CA admin in ipa-replica-prepare in case
the original directory manager's password was updated since creation of
`cacert.p12`.

Do password policy check for non-Kerberos accounts only if it was set by
a regular user or admin. Changes performed by a cn=Directory Manager and
passsync managers should be excluded from the policy check.

Fixes: https://pagure.io/freeipa/issue/7181
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>

[1] https://access.redhat.com/documentation/en-us/red_hat_directory_server/10/html/administration_guide/user_account_management-managing_the_password_policy

Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Christian Heimes <cheimes@redhat.com>
This commit is contained in:
Alexander Bokovoy
2019-03-12 16:14:53 +02:00
committed by Christian Heimes
parent 527f30be76
commit d9c41df6fd
4 changed files with 66 additions and 21 deletions

View File

@@ -422,6 +422,7 @@ done:
int ipapwd_entry_checks(Slapi_PBlock *pb, struct slapi_entry *e,
int *is_root, int *is_krb, int *is_smb, int *is_ipant,
int *is_memberof,
char *attr, int acc)
{
Slapi_Value *sval;
@@ -440,6 +441,10 @@ int ipapwd_entry_checks(Slapi_PBlock *pb, struct slapi_entry *e,
}
}
/* Default to not setting memberof flag: only set it for non-Kerberos principals
* when they have krbPrincipalAux but no krbPrincipalName */
*is_memberof = 0;
/* Check if this is a krbPrincial and therefore needs us to generate other
* hashes */
sval = slapi_value_new_string("krbPrincipalAux");
@@ -450,6 +455,24 @@ int ipapwd_entry_checks(Slapi_PBlock *pb, struct slapi_entry *e,
*is_krb = slapi_entry_attr_has_syntax_value(e, SLAPI_ATTR_OBJECTCLASS, sval);
slapi_value_free(&sval);
/* If entry has krbPrincipalAux object class but lacks krbPrincipalName and
* memberOf attributes consider this not a Kerberos principal object. In
* FreeIPA krbPrincipalAux allows to store krbPwdPolicyReference attribute
* which is added by a CoS plugin configuration based on a memberOf
* attribute value.
* Note that slapi_entry_attr_find() returns 0 if attr exists, -1 for absence
*/
if (*is_krb) {
Slapi_Attr *attr_prname = NULL;
Slapi_Attr *attr_memberof = NULL;
int has_prname = slapi_entry_attr_find(e, "krbPrincipalName", &attr_prname);
int has_memberOf = slapi_entry_attr_find(e, "memberOf", &attr_memberof);
if ((has_prname == -1) && (has_memberOf == 0)) {
*is_memberof = 1;
*is_krb = 0;
}
}
sval = slapi_value_new_string("sambaSamAccount");
if (!sval) {
rc = LDAP_OPERATIONS_ERROR;

View File

@@ -222,7 +222,7 @@ static int ipapwd_chpwop(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
Slapi_Value *objectclass=NULL;
char *attrlist[] = {"*", "passwordHistory", NULL };
struct ipapwd_data pwdata;
int is_krb, is_smb, is_ipant;
int is_krb, is_smb, is_ipant, is_memberof;
char *principal = NULL;
Slapi_PBlock *chpwop_pb = NULL;
Slapi_DN *target_sdn = NULL;
@@ -348,7 +348,7 @@ parse_req_done:
/* Determine the target DN for this operation */
slapi_pblock_get(pb, SLAPI_TARGET_SDN, &target_sdn);
if (target_sdn != NULL) {
/* If there is a TARGET_DN we are consuming it */
/* If there is a TARGET_SDN we are consuming it */
slapi_pblock_set(pb, SLAPI_TARGET_SDN, NULL);
target_dn = slapi_sdn_get_ndn(target_sdn);
}
@@ -485,6 +485,7 @@ parse_req_done:
rc = ipapwd_entry_checks(pb, targetEntry,
&is_root, &is_krb, &is_smb, &is_ipant,
&is_memberof,
SLAPI_USERPWD_ATTR, SLAPI_ACL_WRITE);
if (rc) {
goto free_and_return;
@@ -580,17 +581,22 @@ parse_req_done:
/* check the policy */
ret = ipapwd_CheckPolicy(&pwdata);
if (ret) {
errMesg = ipapwd_error2string(ret);
if (ret == IPAPWD_POLICY_ERROR) {
errMesg = "Internal error";
rc = ret;
} else {
goto free_and_return;
}
/* ipapwd_CheckPolicy happily will try to apply a policy
* even if it doesn't need to be applied for Directory Manager
* or passsync managers, filter that error out */
if (pwdata.changetype != IPA_CHANGETYPE_DSMGR) {
errMesg = ipapwd_error2string(ret);
ret = ipapwd_to_ldap_pwpolicy_error(ret);
slapi_pwpolicy_make_response_control(pb, -1, -1, ret);
rc = LDAP_CONSTRAINT_VIOLATION;
}
goto free_and_return;
}
}
/* Now we're ready to set the kerberos key material */
ret = ipapwd_SetPassword(krbcfg, &pwdata, is_krb);

View File

@@ -90,6 +90,7 @@ struct ipapwd_operation {
struct ipapwd_data pwdata;
int pwd_op;
int is_krb;
int is_memberof;
int skip_keys;
int skip_history;
};
@@ -113,6 +114,7 @@ struct ipapwd_krbcfg {
int ipapwd_entry_checks(Slapi_PBlock *pb, struct slapi_entry *e,
int *is_root, int *is_krb, int *is_smb, int *is_ipant,
int *is_memberof,
char *attr, int access);
int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg,
struct ipapwd_krbcfg **config, int check_flags);

View File

@@ -80,6 +80,8 @@ struct ipapwd_op_ext {
int object_type; /* handle to the extended object */
int handle; /* extension handle */
};
/*****************************************************************************
* pre/post operations to intercept writes to userPassword
****************************************************************************/
@@ -217,7 +219,7 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
Slapi_DN *sdn = NULL;
struct ipapwd_operation *pwdop = NULL;
void *op;
int is_repl_op, is_root, is_krb, is_smb, is_ipant;
int is_repl_op, is_root, is_krb, is_smb, is_ipant, is_memberof;
int ret;
int rc = LDAP_SUCCESS;
@@ -312,6 +314,7 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
rc = ipapwd_entry_checks(pb, e,
&is_root, &is_krb, &is_smb, &is_ipant,
&is_memberof,
NULL, SLAPI_ACL_ADD);
if (rc != LDAP_SUCCESS) {
goto done;
@@ -346,6 +349,7 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
pwdop->pwd_op = IPAPWD_OP_ADD;
pwdop->pwdata.password = slapi_ch_strdup(userpw);
pwdop->is_memberof = is_memberof;
if (is_root) {
pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR;
@@ -367,12 +371,15 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
}
}
pwdop->pwdata.dn = slapi_ch_strdup(dn);
pwdop->pwdata.dn = slapi_ch_strdup(slapi_sdn_get_dn(sdn));
pwdop->pwdata.timeNow = time(NULL);
pwdop->pwdata.target = e;
ret = ipapwd_CheckPolicy(&pwdop->pwdata);
if (ret) {
/* For accounts created by cn=Directory Manager or a passsync
* managers, ignore result of a policy check */
if ((pwdop->pwdata.changetype != IPA_CHANGETYPE_DSMGR) &&
(ret != 0) ) {
errMesg = ipapwd_error2string(ret);
rc = LDAP_CONSTRAINT_VIOLATION;
goto done;
@@ -474,7 +481,7 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
struct slapi_entry *e = NULL;
struct ipapwd_operation *pwdop = NULL;
void *op;
int is_repl_op, is_pwd_op, is_root, is_krb, is_smb, is_ipant;
int is_repl_op, is_pwd_op, is_root, is_krb, is_smb, is_ipant, is_memberof;
int has_krb_keys = 0;
int has_history = 0;
int gen_krb_keys = 0;
@@ -607,6 +614,7 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
rc = ipapwd_entry_checks(pb, e,
&is_root, &is_krb, &is_smb, &is_ipant,
&is_memberof,
is_pwd_op ? SLAPI_USERPWD_ATTR : "ipaNTHash",
SLAPI_ACL_WRITE);
if (rc) {
@@ -809,6 +817,7 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
}
pwdop->is_krb = is_krb;
pwdop->is_memberof = is_memberof;
pwdop->pwd_op = IPAPWD_OP_MOD;
pwdop->pwdata.password = slapi_ch_strdup(unhashedpw);
pwdop->pwdata.changetype = IPA_CHANGETYPE_NORMAL;
@@ -853,11 +862,13 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
pwdop->pwdata.target = e;
/* if krb keys are being set by an external agent we assume password
* policies have been properly checked already, so we check them only
* if no krb keys are available */
* policies have been properly checked already. We check them only if no
* krb keys are available and raise error if the change is not done by a
* cn=Directory Manager or one of passsync managers */
if (has_krb_keys == 0) {
ret = ipapwd_CheckPolicy(&pwdop->pwdata);
if (ret) {
if ((pwdop->pwdata.changetype != IPA_CHANGETYPE_DSMGR) &&
(ret != 0)) {
errMesg = ipapwd_error2string(ret);
rc = LDAP_CONSTRAINT_VIOLATION;
goto done;
@@ -1069,7 +1080,7 @@ static int ipapwd_post_modadd(Slapi_PBlock *pb)
if (IPAPWD_OP_NULL == pwdop->pwd_op)
return 0;
if ( ! (pwdop->is_krb)) {
if ( !pwdop->is_krb || pwdop->is_memberof) {
LOG("Not a kerberos user, ignore krb attributes\n");
return 0;
}
@@ -1428,6 +1439,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
ret = ipapwd_getEntry(sdn, &entry, (char **) attrs_list);
if (ret) {
LOG("failed to retrieve user entry: %s\n", dn);
slapi_sdn_free(&sdn);
return 0;
}
@@ -1452,6 +1464,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
if (current_time > expire_time && expire_time > 0) {
LOG_FATAL("kerberos principal in %s is expired\n", dn);
slapi_entry_free(entry);
slapi_sdn_free(&sdn);
slapi_send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
"Account (Kerberos principal) is expired",
0, NULL);
@@ -1474,6 +1487,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
ret = ipapwd_authenticate(dn, entry, credentials);
if (ret) {
slapi_entry_free(entry);
slapi_sdn_free(&sdn);
return 0;
}