summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipa-server/ipa-kpasswd/ipa_kpasswd.c6
-rw-r--r--ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c631
2 files changed, 533 insertions, 104 deletions
diff --git a/ipa-server/ipa-kpasswd/ipa_kpasswd.c b/ipa-server/ipa-kpasswd/ipa_kpasswd.c
index f5540b74c..fdaa8197c 100644
--- a/ipa-server/ipa-kpasswd/ipa_kpasswd.c
+++ b/ipa-server/ipa-kpasswd/ipa_kpasswd.c
@@ -399,7 +399,11 @@ int ldap_pwd_change(char *client_name, char *realm_name, krb5_data pwd)
if (ret != LDAP_SUCCESS) {
syslog(LOG_ERR, "Search for %s failed with error %d",
filter, ret);
- ret = KRB5_KPASSWD_HARDERROR;
+ if (ret == LDAP_CONSTRAINT_VIOLATION) {
+ ret = KRB5_KPASSWD_SOFTERROR;
+ } else {
+ ret = KRB5_KPASSWD_HARDERROR;
+ }
goto done;
}
free(filter);
diff --git a/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
index e4e9d4615..889f9a1fd 100644
--- a/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
+++ b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
@@ -58,6 +58,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <unistd.h>
#include <prio.h>
#include <ssl.h>
@@ -129,6 +130,7 @@ static void *ipapwd_plugin_id;
krb5_keyblock kmkey;
+char *ipa_realm;
struct krb5p_keysalt *keysalts;
int n_keysalts;
@@ -183,14 +185,22 @@ static inline void encode_int16(unsigned int val, unsigned char *p)
p[0] = (val ) & 0xff;
}
-static Slapi_Value **encrypt_encode_key(krb5_context krbctx, Slapi_Entry *e, const char *newPasswd)
+struct ipapwd_data {
+ Slapi_Entry *target;
+ const char *dn;
+ const char *password;
+ time_t timeNow;
+ time_t lastPwChange;
+ time_t expireTime;
+ int adminChange;
+};
+
+static Slapi_Value **encrypt_encode_key(krb5_context krbctx, struct ipapwd_data *data)
{
const char *krbPrincipalName;
- const char *krbLastPwdChange;
uint32_t krbMaxTicketLife;
Slapi_Attr *krbPrincipalKey = NULL;
struct kbvals *kbvals = NULL;
- time_t lastpwchange;
time_t time_now;
int kvno;
int num_versions, num_keys;
@@ -206,53 +216,31 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, Slapi_Entry *e, con
time_now = time(NULL);
- krbPrincipalName = slapi_entry_attr_get_charptr(e, "krbPrincipalName");
+ krbPrincipalName = slapi_entry_attr_get_charptr(data->target, "krbPrincipalName");
if (!krbPrincipalName) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "no krbPrincipalName present in this entry\n");
return NULL;
}
- krbMaxTicketLife = slapi_entry_attr_get_uint(e, "krbMaxTicketLife");
+ krbMaxTicketLife = slapi_entry_attr_get_uint(data->target, "krbMaxTicketLife");
if (krbMaxTicketLife == 0) {
/* FIXME: retrieve the default from config (max_life from kdc.conf) */
krbMaxTicketLife = 86400; /* just set the default 24h for now */
}
- krbLastPwdChange = slapi_entry_attr_get_charptr(e, "krbLastPwdChange");
- if (!krbLastPwdChange) {
- lastpwchange = -1;
- } else {
- struct tm tm;
-
- 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;
- lastpwchange = timegm(&tm);
- } else {
- /* FIXME: report an error ? */
- lastpwchange = -1;
- }
- }
- /* FIXME: warn if lastpwchange == -1 ? */
-
kvno = 0;
num_keys = 0;
num_versions = 1;
/* retrieve current kvno and and keys */
- ret = slapi_entry_attr_find(e, "krbPrincipalKey", &krbPrincipalKey);
+ ret = slapi_entry_attr_find(data->target, "krbPrincipalKey", &krbPrincipalKey);
if (ret == 0) {
int i, n, count, idx;
ber_int_t tkvno;
Slapi_ValueSet *svs;
Slapi_Value *sv;
ber_tag_t tag, tmp;
+ const struct berval *cbval;
slapi_attr_get_valueset(krbPrincipalKey, &svs);
count = slapi_valueset_count(svs);
@@ -271,13 +259,13 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, Slapi_Entry *e, con
"Array of stored keys shorter than expected\n");
break;
}
- bval = slapi_value_get_berval(sv);
- if (!bval) {
+ cbval = slapi_value_get_berval(sv);
+ if (!cbval) {
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
"Error retrieving berval from Slapi_Value\n");
continue;
}
- be = ber_init(bval);
+ be = ber_init(cbval);
if (!be) {
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
"ber_init() failed!\n");
@@ -293,7 +281,7 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, Slapi_Entry *e, con
}
kbvals[n].kvno = tkvno;
- kbvals[n].bval = bval;
+ kbvals[n].bval = cbval;
n++;
if (tkvno > kvno) {
@@ -305,8 +293,8 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, Slapi_Entry *e, con
num_keys = n;
/* now verify how many keys we need to keep around */
- if (num_keys > 0 && lastpwchange != -1) {
- if (time_now > lastpwchange + krbMaxTicketLife) {
+ if (num_keys) {
+ if (time_now > data->lastPwChange + krbMaxTicketLife) {
/* the last password change was long ago,
* at most only the last entry need to be kept */
num_versions = 2;
@@ -345,6 +333,8 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, Slapi_Entry *e, con
}
}
+ if (kbvals) free(kbvals);
+
krberr = krb5_parse_name(krbctx, krbPrincipalName, &princ);
if (krberr) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
@@ -353,10 +343,10 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, Slapi_Entry *e, con
goto enc_error;
}
- krbTicketFlags = slapi_entry_attr_get_int(e, "krbTicketFlags");
+ krbTicketFlags = slapi_entry_attr_get_int(data->target, "krbTicketFlags");
- pwd.data = (char *)newPasswd;
- pwd.length = strlen(newPasswd);
+ pwd.data = (char *)data->password;
+ pwd.length = strlen(data->password);
be = ber_alloc_t( LBER_USE_DER );
@@ -793,25 +783,396 @@ done:
return ret;
}
+/* 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_getPolicy(const char *dn, Slapi_Entry *target, Slapi_Entry **e)
+{
+ const char *krbPwdPolicyReference;
+ const char *pdn;
+ const Slapi_DN *psdn;
+ Slapi_Backend *be;
+ Slapi_PBlock *pb;
+ char *attrs[] = { "krbMaxPwdLife", "krbMinPwdLife",
+ "krbPwdMinDiffChars", "krbPwdMinLength",
+ "krbPwdHistoryLength", NULL};
+ Slapi_Entry **es = NULL;
+ Slapi_Entry *pe = NULL;
+ char **edn;
+ int ret, res, dist, rdnc, scope, i;
+ Slapi_DN *sdn;
+
+ sdn = slapi_sdn_new_dn_byref(dn);
+
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "ipapwd_getPolicy: Searching policy for [%s]\n", dn);
+
+ krbPwdPolicyReference = slapi_entry_attr_get_charptr(target, "krbPwdPolicyReference");
+ if (krbPwdPolicyReference) {
+ pdn = krbPwdPolicyReference;
+ scope = LDAP_SCOPE_BASE;
+ } else {
+ /* Find ancestor base DN */
+ be = slapi_be_select(sdn);
+ psdn = slapi_be_getsuffix(be, 0);
+ pdn = slapi_sdn_get_dn(psdn);
+ scope = LDAP_SCOPE_SUBTREE;
+ }
+
+ *e = NULL;
+
+ pb = slapi_pblock_new();
+ slapi_search_internal_set_pb (pb,
+ pdn, scope,
+ "(objectClass=krbPwdPolicy)",
+ attrs, 0,
+ NULL, /* Controls */
+ NULL, /* UniqueID */
+ ipapwd_plugin_id,
+ 0); /* Flags */
+
+ /* do search the tree */
+ ret = slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ if (ret == -1 || res != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "ipapwd_getPolicy: Couldn't find policy, err (%d)\n",
+ res?res:ret);
+ slapi_free_search_results_internal(pb);
+ slapi_sdn_free(&sdn);
+ return -1;
+ }
+
+ /* get entries */
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &es);
+ if (!es) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "ipapwd_getPolicy: No entries ?!");
+ slapi_free_search_results_internal(pb);
+ slapi_sdn_free(&sdn);
+ return -1;
+ }
+
+ /* count entries */
+ for (i = 0; es[i]; i++) /* count */ ;
+
+ /* if there is only one, return that */
+ if (i == 1) {
+ *e = slapi_entry_dup(es[0]);
+
+ slapi_free_search_results_internal(pb);
+ slapi_sdn_free(&sdn);
+ return 0;
+ }
+
+ /* count number of RDNs in DN */
+ edn = ldap_explode_dn(dn, 0);
+ if (!edn) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "ipapwd_getPolicy: ldap_explode_dn(dn) failed ?!");
+ slapi_free_search_results_internal(pb);
+ slapi_sdn_free(&sdn);
+ return -1;
+ }
+ for (rdnc = 0; edn[rdnc]; rdnc++) /* count */ ;
+ ldap_value_free(edn);
+
+ pe = NULL;
+ dist = -1;
+
+ /* find closest entry */
+ for (i = 0; es[i]; i++) {
+ const Slapi_DN *esdn;
+
+ esdn = slapi_entry_get_sdn_const(es[i]);
+ if (0 == slapi_sdn_compare(esdn, sdn)) {
+ pe = es[i];
+ dist = 0;
+ break;
+ }
+ if (slapi_sdn_issuffix(sdn, esdn)) {
+ const char *dn1;
+ char **e1;
+ int c1;
+
+ dn1 = slapi_sdn_get_dn(esdn);
+ if (!dn1) continue;
+ e1 = ldap_explode_dn(dn1, 0);
+ if (!e1) continue;
+ for (c1 = 0; e1[c1]; c1++) /* count */ ;
+ ldap_value_free(e1);
+ if ((dist == -1) ||
+ ((rdnc - c1) < dist)) {
+ dist = rdnc - c1;
+ pe = es[i];
+ }
+ }
+ if (dist == 0) break; /* found closest */
+ }
+
+ if (pe == NULL) {
+ slapi_free_search_results_internal(pb);
+ slapi_sdn_free(&sdn);
+ return -1;
+ }
+
+ *e = slapi_entry_dup(pe);
+
+ slapi_free_search_results_internal(pb);
+ slapi_sdn_free(&sdn);
+ return 0;
+}
+
+#define IPAPWD_POLICY_MASK 0x0FF
+#define IPAPWD_POLICY_ERROR 0x100
+#define IPAPWD_POLICY_OK 0
+
+/* 90 days default pwd max lifetime */
+#define IPAPWD_DEFAULT_PWDLIFE (90 * 24 *3600)
+#define IPAPWD_DEFAULT_MINLEN 8
+
+/* check password strenght and history */
+static int ipapwd_CheckPolicy(struct ipapwd_data *data)
+{
+ const char *krbPrincipalExpiration;
+ const char *krbLastPwdChange;
+ int krbMaxPwdLife = 0;
+ int krbPwdMinLength = 0;
+ int krbPwdMinDiffChars = 0;
+ int krbMinPwdLife = 0;
+ int pwdCharLen = 0;
+ Slapi_Entry *policy = NULL;
+ struct tm tm;
+ int ret;
+
+ /* check account is not expired */
+ krbPrincipalExpiration = slapi_entry_attr_get_charptr(data->target, "krbPrincipalExpiration");
+ if (krbPrincipalExpiration) {
+ /* if expiration date 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)) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "Account Expired");
+ return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDMODNOTALLOWED;
+ }
+ }
+ /* FIXME: else error out ? */
+ }
+
+ if (data->adminChange) {
+ /* we must skip policy checks (Admin change) but
+ * force a password change on the next login */
+
+ data->expireTime = data->timeNow;
+
+ } else {
+ 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 ? */
+ } else {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "Warning: Last Password Change Time is not available");
+ }
+ }
+
+ /* find the entry with the password policy */
+ ret = ipapwd_getPolicy(data->dn, data->target, &policy);
+ if (ret) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "No password policy");
+
+ krbMaxPwdLife = IPAPWD_DEFAULT_PWDLIFE;
+ krbPwdMinLength = IPAPWD_DEFAULT_MINLEN;
+ goto no_policy;
+ }
+
+ /* Check min age */
+ krbMinPwdLife = slapi_entry_attr_get_int(policy, "krbMinPwdLife");
+ /* if no default then treat it as no limit */
+ if (krbMinPwdLife != 0) {
+
+ if (data->timeNow < data->lastPwChange + krbMinPwdLife) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "ipapwd_checkPassword: Too soon to change password\n");
+ slapi_entry_free(policy);
+ return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDTOOYOUNG;
+ }
+ }
+
+ /* Retrieve min length */
+ krbPwdMinLength = slapi_entry_attr_get_int(policy, "krbPwdMinLength");
+ if (krbPwdMinLength == 0) {
+ /* if no default then set a minimum of 8 */
+ krbPwdMinLength = 8;
+ }
+
+ /* 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;
+
+ pwd = strdup(data->password);
+
+ /* 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 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 > 0)
+ --num_categories;
+
+ if (num_categories < krbPwdMinDiffChars) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "ipapwd_checkPassword: Password not complex enough\n");
+ slapi_entry_free(policy);
+ return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_INVALIDPWDSYNTAX;
+ }
+ }
+
+ /* TODO: Check password history */
+
+ /* Calculate max age */
+ krbMaxPwdLife = slapi_entry_attr_get_int(policy, "krbMaxPwdLife");
+ if (krbMaxPwdLife <= 0) {
+ /* set default expiration date of 90 days */
+ krbMaxPwdLife = IPAPWD_DEFAULT_PWDLIFE;
+ }
+
+ slapi_entry_free(policy);
+
+no_policy:
+
+ /* check min lenght */
+ pwdCharLen = ldap_utf8characters(data->password);
+
+ if (pwdCharLen < krbPwdMinLength) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "ipapwd_checkPassword: Password too short\n");
+ return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDTOOSHORT;
+ }
+
+ if (data->expireTime == 0) {
+ data->expireTime = data->timeNow + krbMaxPwdLife;
+ }
+
+ return IPAPWD_POLICY_OK;
+}
+
+
/* Searches the dn in directory,
* If found : fills in slapi_entry structure and returns 0
* If NOT found : returns the search result as LDAP_NO_SUCH_OBJECT
*/
-static int
-ipapwd_getEntry( const char *dn, Slapi_Entry **e2 ) {
- int search_result = 0;
- Slapi_DN *sdn;
+static int ipapwd_getEntry(const char *dn, Slapi_Entry **e2)
+{
+ Slapi_DN *sdn;
+ int search_result = 0;
+
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_getEntry\n");
sdn = slapi_sdn_new_dn_byref(dn);
if ((search_result = slapi_search_internal_get_entry( sdn, NULL, e2,
ipapwd_plugin_id)) != LDAP_SUCCESS ){
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "ipapwd_getEntry: No such entry-(%s), err (%d)\n",
- dn, search_result);
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "ipapwd_getEntry: No such entry-(%s), err (%d)\n",
+ dn, search_result);
}
slapi_sdn_free( &sdn );
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipapwd_getEntry: %d\n", search_result);
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "<= ipapwd_getEntry: %d\n", search_result);
return search_result;
}
@@ -822,35 +1183,44 @@ ipapwd_getEntry( const char *dn, Slapi_Entry **e2 ) {
static int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods)
{
Slapi_PBlock *pb;
- int ret=0;
+ int ret;
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_apply_mods\n");
- if (mods && (slapi_mods_get_num_mods(mods) > 0))
- {
- pb = slapi_pblock_new();
- slapi_modify_internal_set_pb (pb, dn,
- slapi_mods_get_ldapmods_byref(mods),
- NULL, /* Controls */
- NULL, /* UniqueID */
- ipapwd_plugin_id, /* PluginID */
- 0); /* Flags */
-
- ret = slapi_modify_internal_pb (pb);
+ if (!mods || (slapi_mods_get_num_mods(mods) == 0)) {
+ return -1;
+ }
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ pb = slapi_pblock_new();
+ slapi_modify_internal_set_pb (pb, dn,
+ slapi_mods_get_ldapmods_byref(mods),
+ NULL, /* Controls */
+ NULL, /* UniqueID */
+ ipapwd_plugin_id, /* PluginID */
+ 0); /* Flags */
- if (ret != LDAP_SUCCESS){
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "WARNING: modify error %d on entry '%s'\n",
+ ret = slapi_modify_internal_pb (pb);
+ if (ret) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "WARNING: modify error %d on entry '%s'\n",
ret, dn);
- }
+ } else {
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+
+ if (ret != LDAP_SUCCESS){
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "WARNING: modify error %d on entry '%s'\n",
+ ret, dn);
+ } else {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "<= ipapwd_apply_mods: Successful\n");
+ }
+ }
slapi_pblock_destroy(pb);
- }
-
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipapwd_apply_mods: %d\n", ret);
-
- return ret;
+
+ return ret;
}
/* ascii hex output of bytes in "in"
@@ -867,14 +1237,13 @@ static void hexbuf(char *out, const uint8_t *in)
}
}
-/* Modify the userPassword attribute field of the entry */
-static int ipapwd_userpassword(Slapi_Entry *targetEntry, const char *newPasswd)
+/* Modify the Password attributes of the entry */
+static int ipapwd_SetPassword(struct ipapwd_data *data)
{
char *dn = NULL;
int ret = 0, i = 0;
Slapi_Mods *smods;
Slapi_Value **svals;
- time_t curtime;
struct tm utctime;
char timestr[16];
krb5_context krbctx;
@@ -890,13 +1259,12 @@ static int ipapwd_userpassword(Slapi_Entry *targetEntry, const char *newPasswd)
return LDAP_OPERATIONS_ERROR;
}
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_userpassword\n");
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_SetPassword\n");
smods = slapi_mods_new();
- dn = slapi_entry_get_ndn( targetEntry );
/* generate kerberos keys to be put into krbPrincipalKey */
- svals = encrypt_encode_key(krbctx, targetEntry, newPasswd);
+ svals = encrypt_encode_key(krbctx, data);
if (!svals) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "key encryption/encoding failed\n");
krb5_free_context(krbctx);
@@ -908,32 +1276,49 @@ static int ipapwd_userpassword(Slapi_Entry *targetEntry, const char *newPasswd)
slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "krbPrincipalKey", svals);
/* change Last Password Change field with the current date */
- curtime = time(NULL);
- if (!gmtime_r(&curtime, &utctime)) {
+ if (!gmtime_r(&(data->timeNow), &utctime)) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to retrieve current date (buggy gmtime_r ?)\n");
+ free(svals);
return LDAP_OPERATIONS_ERROR;
}
if (utctime.tm_year > 8099 || utctime.tm_mon > 11 || utctime.tm_mday > 31 ||
utctime.tm_hour > 23 || utctime.tm_min > 59 || utctime.tm_sec > 59) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "retrieved a bad date (buggy gmtime_r ?)\n");
+ free(svals);
return LDAP_OPERATIONS_ERROR;
}
-
snprintf(timestr, 16, "%04d%02d%02d%02d%02d%02dZ", utctime.tm_year+1900, utctime.tm_mon+1,
utctime.tm_mday, utctime.tm_hour, utctime.tm_min, utctime.tm_sec);
-
slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbLastPwdChange", timestr);
- /* TODO: krbPasswordExpiration, (krbMaxTicketLife, krbMaxRenewableAge, krbTicketFlags ?) */
+
+ /* set Password Expiration date */
+ if (!gmtime_r(&(data->expireTime), &utctime)) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to retrieve current date (buggy gmtime_r ?)\n");
+ free(svals);
+ return LDAP_OPERATIONS_ERROR;
+ }
+ if (utctime.tm_year > 8099 || utctime.tm_mon > 11 || utctime.tm_mday > 31 ||
+ utctime.tm_hour > 23 || utctime.tm_min > 59 || utctime.tm_sec > 59) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "retrieved a bad date (buggy gmtime_r ?)\n");
+ free(svals);
+ return LDAP_OPERATIONS_ERROR;
+ }
+ snprintf(timestr, 16, "%04d%02d%02d%02d%02d%02dZ", utctime.tm_year+1900, utctime.tm_mon+1,
+ utctime.tm_mday, utctime.tm_hour, utctime.tm_min, utctime.tm_sec);
+ slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbPasswordExpiration", timestr);
sambaSamAccount = slapi_value_new_string("sambaSamAccount");
- if (slapi_entry_attr_has_syntax_value(targetEntry, "objectClass", sambaSamAccount)) {
+ if (slapi_entry_attr_has_syntax_value(data->target, "objectClass", sambaSamAccount)) {
/* TODO: retrieve if we want to store the LM hash or not */
ntlm_flags = KTF_LM_HASH | KTF_NT_HASH;
}
slapi_value_free(&sambaSamAccount);
if (ntlm_flags) {
- if (encode_ntlm_keys((char *)newPasswd, ntlm_flags, &ntlm) != 0) {
+ char *password = strdup(data->password);
+ if (encode_ntlm_keys(password, ntlm_flags, &ntlm) != 0) {
+ free(svals);
+ free(password);
return LDAP_OPERATIONS_ERROR;
}
if (ntlm_flags & KTF_LM_HASH) {
@@ -946,19 +1331,20 @@ static int ipapwd_userpassword(Slapi_Entry *targetEntry, const char *newPasswd)
nt[32] = '\0';
slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "sambaNTPassword", nt);
}
+ free(password);
}
- /* TODO !!!
+ /* FIXME:
* instead of replace we should use a delete/add so that we are
* completely sure nobody else modified the entry meanwhile and
* fail if that's the case */
/* commit changes */
- ret = ipapwd_apply_mods(dn, smods);
+ ret = ipapwd_apply_mods(data->dn, smods);
slapi_mods_free(&smods);
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipapwd_userpassword: %d\n", ret);
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipapwd_SetPassword: %d\n", ret);
for (i = 0; svals[i]; i++) {
slapi_value_free(&svals[i]);
@@ -1012,7 +1398,7 @@ static int ipapwd_generate_basic_passwd( int passlen, char **genpasswd )
/* Password Modify Extended operation plugin function */
int
-ipapwd_extop( Slapi_PBlock *pb )
+ipapwd_extop(Slapi_PBlock *pb)
{
char *oid = NULL;
char *bindDN = NULL;
@@ -1027,9 +1413,9 @@ ipapwd_extop( Slapi_PBlock *pb )
struct berval *extop_value = NULL;
BerElement *ber = NULL;
Slapi_Entry *targetEntry=NULL;
- /* Slapi_DN sdn; */
+ struct ipapwd_data pwdata;
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipa_pwd_extop\n");
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_extop\n");
/* Before going any further, we'll make sure that the right extended operation plugin
* has been called: i.e., the OID shipped whithin the extended operation request must
@@ -1259,48 +1645,83 @@ parse_req_done:
the bind operation (or used sasl or client cert auth or OS creds) */
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "oldPasswd provided, but we will ignore it");
}
-
- /* Now we're ready to make actual password change */
- ret = ipapwd_userpassword(targetEntry, newPasswd);
+ pwdata.target = targetEntry;
+ pwdata.dn = dn;
+ pwdata.password = newPasswd;
+ pwdata.timeNow = time(NULL);
+ pwdata.lastPwChange = 0;
+ pwdata.expireTime = 0;
+
+ pwdata.adminChange = 1;
+ /* if it is a regular password change */
+ if (0 == strcmp(dn, bindDN)) {
+ pwdata.adminChange = 0;
+ } else {
+ char **bindexp;
+ bindexp = ldap_explode_dn(bindDN, 0);
+ if (bindexp) {
+ if ((strncasecmp(bindexp[0], "krbprincipalname=kadmin/changepw@", 33) == 0) &&
+ (strcasecmp(&(bindexp[0][33]), ipa_realm) == 0)) {
+ pwdata.adminChange = 0;
+ }
+ ldap_value_free(bindexp);
+ }
+ }
+
+ /* check the policy */
+ ret = ipapwd_CheckPolicy(&pwdata);
+ if (ret) {
+ errMesg = "Password Fails to meet minimum strength criteria";
+ slapi_pwpolicy_make_response_control(pb, -1, -1, ret & 0x0F);
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto free_and_return;
+ }
+
+ /* Now we're ready to set the kerberos key material */
+ ret = ipapwd_SetPassword(&pwdata);
if (ret != LDAP_SUCCESS) {
/* Failed to modify the password, e.g. because insufficient access allowed */
errMesg = "Failed to update password\n";
- rc = ret;
+ if (ret > 0) {
+ rc = ret;
+ } else {
+ rc = LDAP_OPERATIONS_ERROR;
+ }
goto free_and_return;
}
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipa_pwd_extop: %d\n", rc);
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipapwd_extop: %d\n", rc);
/* Free anything that we allocated above */
- free_and_return:
+free_and_return:
slapi_ch_free_string(&oldPasswd);
slapi_ch_free_string(&newPasswd);
/* Either this is the same pointer that we allocated and set above,
* or whoever used it should have freed it and allocated a new
* value that we need to free here */
- slapi_pblock_get( pb, SLAPI_ORIGINAL_TARGET, &dn );
+ slapi_pblock_get(pb, SLAPI_ORIGINAL_TARGET, &dn);
slapi_ch_free_string(&dn);
- slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, NULL );
+ slapi_pblock_set(pb, SLAPI_ORIGINAL_TARGET, NULL);
slapi_ch_free_string(&authmethod);
- if ( targetEntry != NULL ){
- slapi_entry_free (targetEntry);
+ if (targetEntry != NULL) {
+ slapi_entry_free(targetEntry);
}
- if ( ber != NULL ){
+ if (ber != NULL) {
ber_free(ber, 1);
ber = NULL;
}
- slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
- errMesg ? errMesg : "success" );
- slapi_send_ldap_result( pb, rc, NULL, errMesg, 0, NULL );
+ slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
+ errMesg ? errMesg : "success");
+ slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL);
- return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+ return SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
-}/* ipa_pwd_extop */
+} /* ipapwd_extop */
static char *ipapwd_oid_list[] = {
@@ -1310,7 +1731,7 @@ static char *ipapwd_oid_list[] = {
static char *ipapwd_name_list[] = {
- "ipa_pwd_extop",
+ "ipapwd_extop",
NULL
};
@@ -1351,7 +1772,11 @@ int ipapwd_start( Slapi_PBlock *pb )
krberr = krb5_init_context(&krbctx);
if (krberr) {
- slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "krb5_init_context failed\n");
+ slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_start", "krb5_init_context failed\n");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ if (krb5_get_default_realm(krbctx, &ipa_realm)) {
+ krb5_free_context(krbctx);
return LDAP_OPERATIONS_ERROR;
}