mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
ipa-pwd-extop: Use common password policy code
This commit is contained in:
parent
a1637d47c0
commit
7ea0b5d56e
@ -2,7 +2,8 @@ NULL =
|
||||
|
||||
PLUGIN_COMMON_DIR=../common
|
||||
KRB5_UTIL_DIR= ../../../util
|
||||
KRB5_UTIL_SRCS = $(KRB5_UTIL_DIR)/ipa_krb5.c
|
||||
KRB5_UTIL_SRCS = $(KRB5_UTIL_DIR)/ipa_krb5.c \
|
||||
$(KRB5_UTIL_DIR)/ipa_pwd.c
|
||||
|
||||
INCLUDES = \
|
||||
-I. \
|
||||
|
@ -120,6 +120,24 @@ static int filter_keys(struct ipapwd_krbcfg *krbcfg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipapwd_to_ldap_pwpolicy_error(int ipapwderr)
|
||||
{
|
||||
switch (ipapwderr) {
|
||||
case IPAPWD_POLICY_ACCOUNT_EXPIRED:
|
||||
return LDAP_PWPOLICY_PWDMODNOTALLOWED;
|
||||
case IPAPWD_POLICY_PWD_TOO_YOUNG:
|
||||
return LDAP_PWPOLICY_PWDTOOYOUNG;
|
||||
case IPAPWD_POLICY_PWD_TOO_SHORT:
|
||||
return LDAP_PWPOLICY_PWDTOOSHORT;
|
||||
case IPAPWD_POLICY_PWD_IN_HISTORY:
|
||||
return LDAP_PWPOLICY_PWDINHISTORY;
|
||||
case IPAPWD_POLICY_PWD_COMPLEXITY:
|
||||
return LDAP_PWPOLICY_INVALIDPWDSYNTAX;
|
||||
}
|
||||
/* in case of unhandled error return access denied */
|
||||
return LDAP_PWPOLICY_PWDMODNOTALLOWED;
|
||||
}
|
||||
|
||||
|
||||
static int ipapwd_chpwop(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
{
|
||||
@ -374,12 +392,13 @@ parse_req_done:
|
||||
ret = ipapwd_CheckPolicy(&pwdata);
|
||||
if (ret) {
|
||||
errMesg = "Password Fails to meet minimum strength criteria";
|
||||
if (ret & IPAPWD_POLICY_ERROR) {
|
||||
slapi_pwpolicy_make_response_control(pb, -1, -1, ret & IPAPWD_POLICY_MASK);
|
||||
rc = LDAP_CONSTRAINT_VIOLATION;
|
||||
} else {
|
||||
if (ret == IPAPWD_POLICY_ERROR) {
|
||||
errMesg = "Internal error";
|
||||
rc = ret;
|
||||
} else {
|
||||
ret = ipapwd_to_ldap_pwpolicy_error(ret);
|
||||
slapi_pwpolicy_make_response_control(pb, -1, -1, ret);
|
||||
rc = LDAP_CONSTRAINT_VIOLATION;
|
||||
}
|
||||
goto free_and_return;
|
||||
}
|
||||
|
@ -61,6 +61,7 @@
|
||||
#include <openssl/md4.h>
|
||||
|
||||
#include "ipa_krb5.h"
|
||||
#include "ipa_pwd.h"
|
||||
|
||||
#define IPAPWD_PLUGIN_NAME "ipa-pwd-extop"
|
||||
#define IPAPWD_FEATURE_DESC "IPA Password Manager"
|
||||
@ -80,10 +81,9 @@ struct ipapwd_data {
|
||||
char *dn;
|
||||
char *password;
|
||||
time_t timeNow;
|
||||
time_t lastPwChange;
|
||||
time_t expireTime;
|
||||
int changetype;
|
||||
int pwHistoryLen;
|
||||
struct ipapwd_policy policy;
|
||||
};
|
||||
|
||||
struct ipapwd_operation {
|
||||
@ -94,11 +94,6 @@ struct ipapwd_operation {
|
||||
|
||||
#define GENERALIZED_TIME_LENGTH 15
|
||||
|
||||
#define IPAPWD_POLICY_MASK 0x0FF
|
||||
#define IPAPWD_POLICY_ERROR 0x100
|
||||
#define IPAPWD_POLICY_OK 0
|
||||
|
||||
|
||||
/* from ipapwd_common.c */
|
||||
struct ipapwd_krbcfg {
|
||||
krb5_context krbctx;
|
||||
|
@ -329,8 +329,9 @@ static int ipapwd_rdn_count(const char *dn)
|
||||
return rdnc;
|
||||
}
|
||||
|
||||
static int ipapwd_getPolicy(const char *dn,
|
||||
Slapi_Entry *target, Slapi_Entry **e)
|
||||
int ipapwd_getPolicy(const char *dn,
|
||||
Slapi_Entry *target,
|
||||
struct ipapwd_policy *policy)
|
||||
{
|
||||
const char *krbPwdPolicyReference;
|
||||
const char *pdn;
|
||||
@ -347,6 +348,7 @@ static int ipapwd_getPolicy(const char *dn,
|
||||
int buffer_flags=0;
|
||||
Slapi_ValueSet* results = NULL;
|
||||
char* actual_type_name = NULL;
|
||||
int tmpint;
|
||||
|
||||
LOG_TRACE("Searching policy for [%s]\n", dn);
|
||||
|
||||
@ -379,8 +381,6 @@ static int ipapwd_getPolicy(const char *dn,
|
||||
scope = LDAP_SCOPE_SUBTREE;
|
||||
}
|
||||
|
||||
*e = NULL;
|
||||
|
||||
pb = slapi_pblock_new();
|
||||
slapi_search_internal_set_pb(pb,
|
||||
pdn, scope,
|
||||
@ -413,10 +413,8 @@ static int ipapwd_getPolicy(const char *dn,
|
||||
|
||||
/* if there is only one, return that */
|
||||
if (i == 1) {
|
||||
*e = slapi_entry_dup(es[0]);
|
||||
|
||||
ret = 0;
|
||||
goto done;
|
||||
pe = es[0];
|
||||
goto fill;
|
||||
}
|
||||
|
||||
/* count number of RDNs in DN */
|
||||
@ -463,8 +461,27 @@ static int ipapwd_getPolicy(const char *dn,
|
||||
goto done;
|
||||
}
|
||||
|
||||
*e = slapi_entry_dup(pe);
|
||||
fill:
|
||||
policy->min_pwd_life = slapi_entry_attr_get_int(pe, "krbMinPwdLife");
|
||||
|
||||
tmpint = slapi_entry_attr_get_int(pe, "krbMaxPwdLife");
|
||||
if (tmpint != 0) {
|
||||
policy->max_pwd_life = tmpint;
|
||||
}
|
||||
|
||||
tmpint = slapi_entry_attr_get_int(pe, "krbPwdMinLength");
|
||||
if (tmpint != 0) {
|
||||
policy->min_pwd_length = tmpint;
|
||||
}
|
||||
|
||||
policy->history_length = slapi_entry_attr_get_int(pe,
|
||||
"krbPwdHistoryLength");
|
||||
|
||||
policy->min_complexity = slapi_entry_attr_get_int(pe,
|
||||
"krbPwdMinDiffChars");
|
||||
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
if (results) {
|
||||
pwd_values_free(&results, &actual_type_name, buffer_flags);
|
||||
@ -477,24 +494,6 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Slapi_Value *ipapwd_strip_pw_date(Slapi_Value *pw)
|
||||
{
|
||||
const char *pwstr;
|
||||
|
||||
pwstr = slapi_value_get_string(pw);
|
||||
return slapi_value_new_string(&pwstr[GENERALIZED_TIME_LENGTH]);
|
||||
}
|
||||
|
||||
/* searches the directory and finds the policy closest to the DN */
|
||||
/* return 0 on success, -1 on error or if no policy is found */
|
||||
static int ipapwd_sv_pw_cmp(const void *pv1, const void *pv2)
|
||||
{
|
||||
const char *pw1 = slapi_value_get_string(*((Slapi_Value **)pv1));
|
||||
const char *pw2 = slapi_value_get_string(*((Slapi_Value **)pv2));
|
||||
|
||||
return strncmp(pw1, pw2, GENERALIZED_TIME_LENGTH);
|
||||
}
|
||||
|
||||
|
||||
/*==Common-public-functions=============================================*/
|
||||
|
||||
@ -620,62 +619,19 @@ done:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 90 days default pwd max lifetime */
|
||||
#define IPAPWD_DEFAULT_PWDLIFE (90 * 24 *3600)
|
||||
#define IPAPWD_DEFAULT_MINLEN 0
|
||||
|
||||
/* check password strenght and history */
|
||||
int ipapwd_CheckPolicy(struct ipapwd_data *data)
|
||||
{
|
||||
char *krbPrincipalExpiration = NULL;
|
||||
char *krbLastPwdChange = NULL;
|
||||
char *krbPasswordExpiration = NULL;
|
||||
int krbMaxPwdLife = IPAPWD_DEFAULT_PWDLIFE;
|
||||
int krbPwdMinLength = IPAPWD_DEFAULT_MINLEN;
|
||||
int krbPwdMinDiffChars = 0;
|
||||
int krbMinPwdLife = 0;
|
||||
int pwdCharLen = 0;
|
||||
Slapi_Entry *policy = NULL;
|
||||
Slapi_Attr *passwordHistory = NULL;
|
||||
struct tm tm;
|
||||
int tmp, ret;
|
||||
char *old_pw;
|
||||
struct ipapwd_policy pol = {0};
|
||||
time_t acct_expiration;
|
||||
time_t pwd_expiration;
|
||||
time_t last_pwd_change;
|
||||
char **pwd_history;
|
||||
char *tmpstr;
|
||||
int ret;
|
||||
|
||||
/* check account is not expired. Ignore unixtime = 0 (Jan 1 1970) */
|
||||
krbPrincipalExpiration =
|
||||
slapi_entry_attr_get_charptr(data->target, "krbPrincipalExpiration");
|
||||
if (krbPrincipalExpiration &&
|
||||
(strcasecmp("19700101000000Z", krbPrincipalExpiration) != 0)) {
|
||||
/* if expiration date is set check it */
|
||||
memset(&tm, 0, sizeof(struct tm));
|
||||
ret = sscanf(krbPrincipalExpiration,
|
||||
"%04u%02u%02u%02u%02u%02u",
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
|
||||
&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
|
||||
|
||||
if (ret == 6) {
|
||||
tm.tm_year -= 1900;
|
||||
tm.tm_mon -= 1;
|
||||
|
||||
if (data->timeNow > timegm(&tm)) {
|
||||
LOG_TRACE("Account Expired");
|
||||
return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDMODNOTALLOWED;
|
||||
}
|
||||
}
|
||||
/* FIXME: else error out ? */
|
||||
}
|
||||
slapi_ch_free_string(&krbPrincipalExpiration);
|
||||
|
||||
/* find the entry with the password policy */
|
||||
ret = ipapwd_getPolicy(data->dn, data->target, &policy);
|
||||
if (ret) {
|
||||
LOG_TRACE("No password policy");
|
||||
goto no_policy;
|
||||
}
|
||||
|
||||
/* Retrieve Max History Len */
|
||||
data->pwHistoryLen =
|
||||
slapi_entry_attr_get_int(policy, "krbPwdHistoryLength");
|
||||
pol.max_pwd_life = IPAPWD_DEFAULT_PWDLIFE;
|
||||
pol.min_pwd_length = IPAPWD_DEFAULT_MINLEN;
|
||||
|
||||
if (data->changetype != IPA_CHANGETYPE_NORMAL) {
|
||||
/* We must skip policy checks (Admin change) but
|
||||
@ -685,279 +641,51 @@ int ipapwd_CheckPolicy(struct ipapwd_data *data)
|
||||
data->expireTime = data->timeNow;
|
||||
}
|
||||
|
||||
/* skip policy checks */
|
||||
slapi_entry_free(policy);
|
||||
goto no_policy;
|
||||
}
|
||||
|
||||
/* first of all check current password, if any */
|
||||
old_pw = slapi_entry_attr_get_charptr(data->target, "userPassword");
|
||||
if (old_pw) {
|
||||
Slapi_Value *cpw[2] = {NULL, NULL};
|
||||
Slapi_Value *pw;
|
||||
|
||||
cpw[0] = slapi_value_new_string(old_pw);
|
||||
pw = slapi_value_new_string(data->password);
|
||||
if (!pw) {
|
||||
LOG_OOM();
|
||||
slapi_entry_free(policy);
|
||||
slapi_ch_free_string(&old_pw);
|
||||
slapi_value_free(&cpw[0]);
|
||||
slapi_value_free(&pw);
|
||||
return LDAP_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ret = slapi_pw_find_sv(cpw, pw);
|
||||
slapi_ch_free_string(&old_pw);
|
||||
slapi_value_free(&cpw[0]);
|
||||
slapi_value_free(&pw);
|
||||
|
||||
if (ret == 0) {
|
||||
LOG_TRACE("Password in history\n");
|
||||
slapi_entry_free(policy);
|
||||
return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDINHISTORY;
|
||||
}
|
||||
}
|
||||
|
||||
krbPasswordExpiration =
|
||||
slapi_entry_attr_get_charptr(data->target, "krbPasswordExpiration");
|
||||
krbLastPwdChange =
|
||||
slapi_entry_attr_get_charptr(data->target, "krbLastPwdChange");
|
||||
/* if no previous change, it means this is probably a new account
|
||||
* or imported, log and just ignore */
|
||||
if (krbLastPwdChange) {
|
||||
|
||||
memset(&tm, 0, sizeof(struct tm));
|
||||
ret = sscanf(krbLastPwdChange,
|
||||
"%04u%02u%02u%02u%02u%02u",
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
|
||||
&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
|
||||
|
||||
if (ret == 6) {
|
||||
tm.tm_year -= 1900;
|
||||
tm.tm_mon -= 1;
|
||||
data->lastPwChange = timegm(&tm);
|
||||
}
|
||||
/* FIXME: *else* report an error ? */
|
||||
/* do not load policies */
|
||||
} else {
|
||||
LOG_TRACE("Warning: Last Password Change Time is not available\n");
|
||||
}
|
||||
|
||||
/* Check min age */
|
||||
krbMinPwdLife = slapi_entry_attr_get_int(policy, "krbMinPwdLife");
|
||||
/* if no default then treat it as no limit */
|
||||
if (krbMinPwdLife != 0) {
|
||||
|
||||
/* check for reset cases */
|
||||
if (krbLastPwdChange == NULL ||
|
||||
((krbPasswordExpiration != NULL) &&
|
||||
strcmp(krbPasswordExpiration, krbLastPwdChange) == 0)) {
|
||||
/* Expiration and last change time are the same or
|
||||
* missing this happens only when a password is reset
|
||||
* by an admin or the account is new or no expiration
|
||||
* policy is set, PASS */
|
||||
LOG_TRACE("Ignore krbMinPwdLife Expiration, not enough info\n");
|
||||
|
||||
} else if (data->timeNow < data->lastPwChange + krbMinPwdLife) {
|
||||
LOG_TRACE("Too soon to change password\n");
|
||||
slapi_entry_free(policy);
|
||||
slapi_ch_free_string(&krbPasswordExpiration);
|
||||
slapi_ch_free_string(&krbLastPwdChange);
|
||||
return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDTOOYOUNG;
|
||||
/* find the entry with the password policy */
|
||||
ret = ipapwd_getPolicy(data->dn, data->target, &pol);
|
||||
if (ret) {
|
||||
LOG_TRACE("No password policy, use defaults");
|
||||
}
|
||||
}
|
||||
|
||||
/* free strings or we leak them */
|
||||
slapi_ch_free_string(&krbPasswordExpiration);
|
||||
slapi_ch_free_string(&krbLastPwdChange);
|
||||
tmpstr = slapi_entry_attr_get_charptr(data->target,
|
||||
"krbPrincipalExpiration");
|
||||
acct_expiration = ipapwd_gentime_to_time_t(tmpstr);
|
||||
slapi_ch_free_string(&tmpstr);
|
||||
|
||||
/* Retrieve min length */
|
||||
tmp = slapi_entry_attr_get_int(policy, "krbPwdMinLength");
|
||||
if (tmp != 0) {
|
||||
krbPwdMinLength = tmp;
|
||||
}
|
||||
tmpstr = slapi_entry_attr_get_charptr(data->target,
|
||||
"krbPasswordExpiration");
|
||||
pwd_expiration = ipapwd_gentime_to_time_t(tmpstr);
|
||||
slapi_ch_free_string(&tmpstr);
|
||||
|
||||
/* check complexity */
|
||||
/* FIXME: this code is partially based on Directory Server code,
|
||||
* the plan is to merge this code later making it available
|
||||
* trough a pulic DS API for slapi plugins */
|
||||
krbPwdMinDiffChars =
|
||||
slapi_entry_attr_get_int(policy, "krbPwdMinDiffChars");
|
||||
if (krbPwdMinDiffChars != 0) {
|
||||
int num_digits = 0;
|
||||
int num_alphas = 0;
|
||||
int num_uppers = 0;
|
||||
int num_lowers = 0;
|
||||
int num_specials = 0;
|
||||
int num_8bit = 0;
|
||||
int num_repeated = 0;
|
||||
int max_repeated = 0;
|
||||
int num_categories = 0;
|
||||
char *p, *pwd;
|
||||
tmpstr = slapi_entry_attr_get_charptr(data->target,
|
||||
"krbLastPwdChange");
|
||||
last_pwd_change = ipapwd_gentime_to_time_t(tmpstr);
|
||||
slapi_ch_free_string(&tmpstr);
|
||||
|
||||
pwd = strdup(data->password);
|
||||
pwd_history = slapi_entry_attr_get_charray(data->target,
|
||||
"passwordHistory");
|
||||
|
||||
/* check character types */
|
||||
p = pwd;
|
||||
while (p && *p) {
|
||||
if (ldap_utf8isdigit(p)) {
|
||||
num_digits++;
|
||||
} else if (ldap_utf8isalpha(p)) {
|
||||
num_alphas++;
|
||||
if (slapi_utf8isLower((unsigned char *)p)) {
|
||||
num_lowers++;
|
||||
} else {
|
||||
num_uppers++;
|
||||
}
|
||||
} else {
|
||||
/* check if this is an 8-bit char */
|
||||
if (*p & 128) {
|
||||
num_8bit++;
|
||||
} else {
|
||||
num_specials++;
|
||||
}
|
||||
}
|
||||
/* check policy */
|
||||
ret = ipapwd_check_policy(&pol, data->password,
|
||||
data->timeNow,
|
||||
acct_expiration,
|
||||
pwd_expiration,
|
||||
last_pwd_change,
|
||||
pwd_history);
|
||||
|
||||
/* check for repeating characters. If this is the
|
||||
first char of the password, no need to check */
|
||||
if (pwd != p) {
|
||||
int len = ldap_utf8len(p);
|
||||
char *prev_p = ldap_utf8prev(p);
|
||||
|
||||
if (len == ldap_utf8len(prev_p)) {
|
||||
if (memcmp(p, prev_p, len) == 0) {
|
||||
num_repeated++;
|
||||
if (max_repeated < num_repeated) {
|
||||
max_repeated = num_repeated;
|
||||
}
|
||||
} else {
|
||||
num_repeated = 0;
|
||||
}
|
||||
} else {
|
||||
num_repeated = 0;
|
||||
}
|
||||
}
|
||||
|
||||
p = ldap_utf8next(p);
|
||||
}
|
||||
|
||||
free(pwd);
|
||||
p = pwd = NULL;
|
||||
|
||||
/* tally up the number of character categories */
|
||||
if (num_digits > 0) ++num_categories;
|
||||
if (num_uppers > 0) ++num_categories;
|
||||
if (num_lowers > 0) ++num_categories;
|
||||
if (num_specials > 0) ++num_categories;
|
||||
if (num_8bit > 0) ++num_categories;
|
||||
|
||||
/* FIXME: the kerberos plicy schema does not define separated
|
||||
* threshold values, so just treat anything as a category,
|
||||
* we will fix this when we merge with DS policies */
|
||||
|
||||
if (max_repeated > 1) --num_categories;
|
||||
|
||||
if (num_categories < krbPwdMinDiffChars) {
|
||||
LOG_TRACE("Password not complex enough\n");
|
||||
slapi_entry_free(policy);
|
||||
return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_INVALIDPWDSYNTAX;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check password history */
|
||||
ret = slapi_entry_attr_find(data->target,
|
||||
"passwordHistory", &passwordHistory);
|
||||
if (ret == 0) {
|
||||
int err, hint, count, i, j;
|
||||
const char *pwstr;
|
||||
Slapi_Value **pH;
|
||||
Slapi_Value *pw;
|
||||
|
||||
hint = 0;
|
||||
count = 0;
|
||||
err = slapi_attr_get_numvalues(passwordHistory, &count);
|
||||
/* check history only if we have one */
|
||||
if (count > 0 && data->pwHistoryLen > 0) {
|
||||
pH = calloc(count + 2, sizeof(Slapi_Value *));
|
||||
if (!pH) {
|
||||
LOG_OOM();
|
||||
slapi_entry_free(policy);
|
||||
return LDAP_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
hint = slapi_attr_first_value(passwordHistory, &pw);
|
||||
while (hint != -1) {
|
||||
pwstr = slapi_value_get_string(pw);
|
||||
/* if shorter than GENERALIZED_TIME_LENGTH, it
|
||||
* is garbage, we never set timeless entries */
|
||||
if (pwstr &&
|
||||
(strlen(pwstr) > GENERALIZED_TIME_LENGTH)) {
|
||||
pH[i] = pw;
|
||||
i++;
|
||||
}
|
||||
hint = slapi_attr_next_value(passwordHistory, hint, &pw);
|
||||
}
|
||||
|
||||
qsort(pH, i, sizeof(Slapi_Value *), ipapwd_sv_pw_cmp);
|
||||
|
||||
if (i > data->pwHistoryLen) {
|
||||
i = data->pwHistoryLen;
|
||||
pH[i] = NULL;
|
||||
}
|
||||
|
||||
for (j = 0; pH[j]; j++) {
|
||||
pH[j] = ipapwd_strip_pw_date(pH[j]);
|
||||
}
|
||||
|
||||
pw = slapi_value_new_string(data->password);
|
||||
if (!pw) {
|
||||
LOG_OOM();
|
||||
slapi_entry_free(policy);
|
||||
free(pH);
|
||||
return LDAP_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
err = slapi_pw_find_sv(pH, pw);
|
||||
|
||||
for (j = 0; pH[j]; j++) {
|
||||
slapi_value_free(&pH[j]);
|
||||
}
|
||||
slapi_value_free(&pw);
|
||||
free(pH);
|
||||
|
||||
if (err == 0) {
|
||||
LOG_TRACE("Password in history\n");
|
||||
slapi_entry_free(policy);
|
||||
return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDINHISTORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate max age */
|
||||
tmp = slapi_entry_attr_get_int(policy, "krbMaxPwdLife");
|
||||
if (tmp != 0) {
|
||||
krbMaxPwdLife = tmp;
|
||||
}
|
||||
|
||||
slapi_entry_free(policy);
|
||||
|
||||
no_policy:
|
||||
|
||||
/* check min lenght */
|
||||
pwdCharLen = ldap_utf8characters(data->password);
|
||||
|
||||
if (pwdCharLen < krbPwdMinLength) {
|
||||
LOG_TRACE("Password too short (%d < %d)\n",
|
||||
pwdCharLen, krbPwdMinLength);
|
||||
return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDTOOSHORT;
|
||||
}
|
||||
slapi_ch_array_free(pwd_history);
|
||||
|
||||
if (data->expireTime == 0) {
|
||||
data->expireTime = data->timeNow + krbMaxPwdLife;
|
||||
data->expireTime = data->timeNow + pol.max_pwd_life;
|
||||
}
|
||||
|
||||
return IPAPWD_POLICY_OK;
|
||||
data->policy = pol;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Searches the dn in directory,
|
||||
@ -1154,10 +882,12 @@ int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg,
|
||||
"userPassword", data->password);
|
||||
|
||||
/* set password history */
|
||||
pwvals = ipapwd_setPasswordHistory(smods, data);
|
||||
if (pwvals) {
|
||||
slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
||||
"passwordHistory", pwvals);
|
||||
if (data->policy.history_length > 0) {
|
||||
pwvals = ipapwd_setPasswordHistory(smods, data);
|
||||
if (pwvals) {
|
||||
slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
||||
"passwordHistory", pwvals);
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME:
|
||||
@ -1186,111 +916,45 @@ Slapi_Value **ipapwd_setPasswordHistory(Slapi_Mods *smods,
|
||||
struct ipapwd_data *data)
|
||||
{
|
||||
Slapi_Value **pH = NULL;
|
||||
Slapi_Attr *passwordHistory = NULL;
|
||||
char timestr[GENERALIZED_TIME_LENGTH+1];
|
||||
char *histr, *old_pw;
|
||||
struct tm utctime;
|
||||
int ret, pc;
|
||||
char **pwd_history = NULL;
|
||||
char **new_pwd_history = NULL;
|
||||
int n = 0;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
old_pw = slapi_entry_attr_get_charptr(data->target, "userPassword");
|
||||
if (!old_pw) {
|
||||
/* no old password to store, just return */
|
||||
return NULL;
|
||||
pwd_history = slapi_entry_attr_get_charray(data->target,
|
||||
"passwordHistory");
|
||||
|
||||
ret = ipapwd_generate_new_history(data->password, data->timeNow,
|
||||
data->policy.history_length,
|
||||
pwd_history, &new_pwd_history, &n);
|
||||
|
||||
if (ret) {
|
||||
LOG_FATAL("failed to generate new password history!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!gmtime_r(&(data->timeNow), &utctime)) {
|
||||
LOG_FATAL("failed to retrieve current date (buggy gmtime_r ?)\n");
|
||||
return NULL;
|
||||
}
|
||||
strftime(timestr, GENERALIZED_TIME_LENGTH+1, "%Y%m%d%H%M%SZ", &utctime);
|
||||
|
||||
histr = slapi_ch_smprintf("%s%s", timestr, old_pw);
|
||||
if (!histr) {
|
||||
pH = (Slapi_Value **)slapi_ch_calloc(n + 1, sizeof(Slapi_Value *));
|
||||
if (!pH) {
|
||||
LOG_OOM();
|
||||
return NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* retrieve current history */
|
||||
ret = slapi_entry_attr_find(data->target,
|
||||
"passwordHistory", &passwordHistory);
|
||||
if (ret == 0) {
|
||||
int err, hint, count, i, j;
|
||||
const char *pwstr;
|
||||
Slapi_Value *pw;
|
||||
|
||||
hint = 0;
|
||||
count = 0;
|
||||
err = slapi_attr_get_numvalues(passwordHistory, &count);
|
||||
/* if we have one */
|
||||
if (err == 0 && count > 0 && data->pwHistoryLen > 0) {
|
||||
pH = calloc(count + 2, sizeof(Slapi_Value *));
|
||||
if (!pH) {
|
||||
LOG_OOM();
|
||||
free(histr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
hint = slapi_attr_first_value(passwordHistory, &pw);
|
||||
while (hint != -1) {
|
||||
pwstr = slapi_value_get_string(pw);
|
||||
/* if shorter than GENERALIZED_TIME_LENGTH, it
|
||||
* is garbage, we never set timeless entries */
|
||||
if (pwstr &&
|
||||
(strlen(pwstr) > GENERALIZED_TIME_LENGTH)) {
|
||||
pH[i] = pw;
|
||||
i++;
|
||||
}
|
||||
hint = slapi_attr_next_value(passwordHistory, hint, &pw);
|
||||
}
|
||||
|
||||
qsort(pH, i, sizeof(Slapi_Value *), ipapwd_sv_pw_cmp);
|
||||
|
||||
if (i >= data->pwHistoryLen) {
|
||||
/* need to rotate out the first entry */
|
||||
for (j = 0; j < data->pwHistoryLen; j++) {
|
||||
pH[j] = pH[j + 1];
|
||||
}
|
||||
|
||||
i = data->pwHistoryLen;
|
||||
pH[i] = NULL;
|
||||
i--;
|
||||
}
|
||||
|
||||
pc = i;
|
||||
|
||||
/* copy only interesting entries */
|
||||
for (i = 0; i < pc; i++) {
|
||||
pH[i] = slapi_value_dup(pH[i]);
|
||||
if (pH[i] == NULL) {
|
||||
LOG_OOM();
|
||||
while (i) {
|
||||
i--;
|
||||
slapi_value_free(&pH[i]);
|
||||
}
|
||||
free(pH);
|
||||
free(histr);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pH == NULL) {
|
||||
pH = calloc(2, sizeof(Slapi_Value *));
|
||||
if (!pH) {
|
||||
for (i = 0; i < n; i++) {
|
||||
pH[i] = slapi_value_new_string(new_pwd_history[i]);
|
||||
if (!pH[i]) {
|
||||
ipapwd_free_slapi_value_array(&pH);
|
||||
LOG_OOM();
|
||||
free(histr);
|
||||
return NULL;
|
||||
goto done;
|
||||
}
|
||||
pc = 0;
|
||||
}
|
||||
|
||||
/* add new history value */
|
||||
pH[pc] = slapi_value_new_string(histr);
|
||||
|
||||
free(histr);
|
||||
|
||||
done:
|
||||
slapi_ch_array_free(pwd_history);
|
||||
for (i = 0; i < n; i++) {
|
||||
free(new_pwd_history[i]);
|
||||
}
|
||||
free(new_pwd_history);
|
||||
return pH;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user