int ipapwd_setdate(Slapi_Entry *source, Slapi_Mods *smods, const char *attr, time_t date, bool remove) { char timestr[GENERALIZED_TIME_LENGTH+1]; struct tm utctime; Slapi_Attr *t; bool exists; exists = (slapi_entry_attr_find(source, attr, &t) == 0); if (remove) { if (exists) { slapi_mods_add_mod_values(smods, LDAP_MOD_DELETE, attr, NULL); } return LDAP_SUCCESS; } if (!gmtime_r(&date, &utctime)) { LOG_FATAL("failed to convert %s date\n", attr); return LDAP_OPERATIONS_ERROR; } strftime(timestr, GENERALIZED_TIME_LENGTH + 1, "%Y%m%d%H%M%SZ", &utctime); slapi_mods_add_string(smods, exists ? LDAP_MOD_REPLACE : LDAP_MOD_ADD, attr, timestr); return LDAP_SUCCESS; }
static int set_retry_cnt_and_time ( Slapi_PBlock *pb, int count, time_t cur_time ) { const char *dn = NULL; Slapi_DN *sdn = NULL; Slapi_Mods smods; time_t reset_time; char *timestr; passwdPolicy *pwpolicy = NULL; int rc = 0; slapi_pblock_get( pb, SLAPI_TARGET_SDN, &sdn ); dn = slapi_sdn_get_dn(sdn); pwpolicy = new_passwdPolicy(pb, dn); slapi_mods_init(&smods, 0); reset_time = time_plus_sec ( cur_time, pwpolicy->pw_resetfailurecount ); timestr = format_genTime ( reset_time ); slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "retryCountResetTime", timestr); slapi_ch_free((void **)×tr); rc = set_retry_cnt_mods(pb, &smods, count); pw_apply_mods(sdn, &smods); slapi_mods_done(&smods); return rc; }
int set_retry_cnt_mods(Slapi_PBlock *pb, Slapi_Mods *smods, int count) { char *timestr; time_t unlock_time; char retry_cnt[8]; /* 1-65535 */ const char *dn = NULL; Slapi_DN *sdn = NULL; passwdPolicy *pwpolicy = NULL; int rc = 0; slapi_pblock_get( pb, SLAPI_TARGET_SDN, &sdn ); dn = slapi_sdn_get_dn(sdn); pwpolicy = new_passwdPolicy(pb, dn); if (smods) { sprintf ( retry_cnt, "%d", count ); slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "passwordRetryCount", retry_cnt); /* lock account if reache retry limit */ if ( count >= pwpolicy->pw_maxfailure ) { /* Remove lock_account function to perform all mods at once */ /* lock_account ( pb ); */ /* reach the retry limit, lock the account */ if ( pwpolicy->pw_unlock == 0 ) { /* lock until admin reset password */ unlock_time = NO_TIME; } else { unlock_time = time_plus_sec ( current_time(), pwpolicy->pw_lockduration ); } timestr= format_genTime ( unlock_time ); slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "accountUnlockTime", timestr); slapi_ch_free((void **)×tr); rc = LDAP_CONSTRAINT_VIOLATION; } } return rc; }
/* Modify the Password attributes of the entry */ int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg, struct ipapwd_data *data, int is_krb) { int ret = 0; Slapi_Mods *smods = NULL; Slapi_Value **svals = NULL; Slapi_Value **ntvals = NULL; Slapi_Value **pwvals = NULL; char *nt = NULL; int is_smb = 0; int is_ipant = 0; int is_host = 0; Slapi_Value *sambaSamAccount; Slapi_Value *ipaNTUserAttrs; Slapi_Value *ipaHost; char *errMesg = NULL; char *modtime = NULL; LOG_TRACE("=>\n"); sambaSamAccount = slapi_value_new_string("sambaSamAccount"); if (slapi_entry_attr_has_syntax_value(data->target, "objectClass", sambaSamAccount)) { is_smb = 1; } slapi_value_free(&sambaSamAccount); ipaNTUserAttrs = slapi_value_new_string("ipaNTUserAttrs"); if (slapi_entry_attr_has_syntax_value(data->target, "objectClass", ipaNTUserAttrs)) { is_ipant = 1; } slapi_value_free(&ipaNTUserAttrs); ipaHost = slapi_value_new_string("ipaHost"); if (slapi_entry_attr_has_syntax_value(data->target, "objectClass", ipaHost)) { is_host = 1; } slapi_value_free(&ipaHost); ret = ipapwd_gen_hashes(krbcfg, data, data->password, is_krb, is_smb, is_ipant, &svals, &nt, &ntvals, &errMesg); if (ret) { goto free_and_return; } smods = slapi_mods_new(); if (svals) { slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "krbPrincipalKey", svals); /* krbLastPwdChange is used to tell whether a host entry has a * keytab so don't set it on hosts. */ if (!is_host) { /* change Last Password Change field with the current date */ ret = ipapwd_setdate(data->target, smods, "krbLastPwdChange", data->timeNow, false); if (ret != LDAP_SUCCESS) goto free_and_return; /* set Password Expiration date */ ret = ipapwd_setdate(data->target, smods, "krbPasswordExpiration", data->expireTime, (data->expireTime == 0)); if (ret != LDAP_SUCCESS) goto free_and_return; } } if (nt && is_smb) { slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "sambaNTPassword", nt); } if (ntvals && is_ipant) { slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "ipaNTHash", ntvals); } if (is_smb) { /* with samba integration we need to also set sambaPwdLastSet or * samba will decide the user has to change the password again */ if (data->changetype == IPA_CHANGETYPE_ADMIN) { /* if it is an admin change instead we need to let know to * samba as well that the use rmust change its password */ modtime = slapi_ch_smprintf("0"); } else { modtime = slapi_ch_smprintf("%ld", (long)data->timeNow); } if (!modtime) { LOG_FATAL("failed to smprintf string!\n"); ret = LDAP_OPERATIONS_ERROR; goto free_and_return; } slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "sambaPwdLastset", modtime); } if (is_krb) { if (data->changetype == IPA_CHANGETYPE_ADMIN) { slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbLoginFailedCount", "0"); } } /* let DS encode the password itself, this allows also other plugins to * intercept it to perform operations like synchronization with Active * Directory domains through the replication plugin */ slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "userPassword", data->password); /* set password history */ 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: * 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(data->dn, smods); LOG_TRACE("<= result: %d\n", ret); free_and_return: if (nt) slapi_ch_free((void **)&nt); if (modtime) slapi_ch_free((void **)&modtime); slapi_mods_free(&smods); ipapwd_free_slapi_value_array(&svals); ipapwd_free_slapi_value_array(&ntvals); ipapwd_free_slapi_value_array(&pwvals); return ret; }
/* * update multiple attribute values per _do_modify */ static int _update_all_per_mod(Slapi_DN *entrySDN, /* DN of the searched entry */ Slapi_Attr *attr, /* referred attribute */ char *attrName, Slapi_DN *origDN, /* original DN that was modified */ char *newRDN, /* new RDN from modrdn */ const char *newsuperior, /* new superior from modrdn */ Slapi_PBlock *mod_pb) { Slapi_Mods *smods = NULL; char *newDN = NULL; char **dnParts = NULL; char *sval = NULL; char *newvalue = NULL; char *p = NULL; size_t dnlen = 0; int rc = 0; int nval = 0; slapi_attr_get_numvalues(attr, &nval); if (NULL == newRDN && NULL == newsuperior) { /* in delete mode */ LDAPMod *mods[2]; char *values_del[2]; LDAPMod attribute1; /* delete old dn so set that up */ values_del[0] = (char *)slapi_sdn_get_dn(origDN); values_del[1] = NULL; attribute1.mod_type = attrName; attribute1.mod_op = LDAP_MOD_DELETE; attribute1.mod_values = values_del; mods[0] = &attribute1; /* terminate list of mods. */ mods[1] = NULL; rc = _do_modify(mod_pb, entrySDN, mods); if (rc) { slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM, "_update_all_per_mod: entry %s: deleting \"%s: %s\" failed (%d)" "\n", slapi_sdn_get_dn(entrySDN), attrName, slapi_sdn_get_dn(origDN), rc); } } else { /* in modrdn mode */ const char *superior = NULL; int nval = 0; Slapi_Value *v = NULL; if (NULL == origDN) { slapi_log_error(SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM, "_update_all_per_mod: NULL dn was passed\n"); goto bail; } /* need to put together rdn into a dn */ dnParts = slapi_ldap_explode_dn( slapi_sdn_get_dn(origDN), 0 ); if (NULL == dnParts) { slapi_log_error(SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM, "_update_all_per_mod: failed to explode dn %s\n", slapi_sdn_get_dn(origDN)); goto bail; } if (NULL == newRDN) { newRDN = dnParts[0]; } if (newsuperior) { superior = newsuperior; } else { /* do not free superior */ superior = slapi_dn_find_parent(slapi_sdn_get_dn(origDN)); } /* newRDN and superior are already normalized. */ newDN = slapi_ch_smprintf("%s,%s", newRDN, superior); slapi_dn_ignore_case(newDN); /* * Compare the modified dn with the value of * the target attribute of referint to find out * the modified dn is the ancestor (case 2) or * the value itself (case 1). * * E.g., * (case 1) * modrdn: uid=A,ou=B,o=C --> uid=A',ou=B',o=C * (origDN) (newDN) * member: uid=A,ou=B,ou=C --> uid=A',ou=B',ou=C * (sval) (newDN) * * (case 2) * modrdn: ou=B,o=C --> ou=B',o=C * (origDN) (newDN) * member: uid=A,ou=B,ou=C --> uid=A,ou=B',ou=C * (sval) (sval' + newDN) */ slapi_attr_get_numvalues(attr, &nval); smods = slapi_mods_new(); slapi_mods_init(smods, 2 * nval + 1); for (nval = slapi_attr_first_value(attr, &v); nval != -1; nval = slapi_attr_next_value(attr, nval, &v)) { p = NULL; dnlen = 0; /* DN syntax, which should be a string */ sval = slapi_ch_strdup(slapi_value_get_string(v)); rc = slapi_dn_normalize_case_ext(sval, 0, &p, &dnlen); if (rc == 0) { /* sval is passed in; not terminated */ *(p + dnlen) = '\0'; sval = p; } else if (rc > 0) { slapi_ch_free_string(&sval); sval = p; } /* else: (rc < 0) Ignore the DN normalization error for now. */ p = PL_strstr(sval, slapi_sdn_get_ndn(origDN)); if (p == sval) { /* (case 1) */ slapi_mods_add_string(smods, LDAP_MOD_DELETE, attrName, sval); slapi_mods_add_string(smods, LDAP_MOD_ADD, attrName, newDN); } else if (p) { /* (case 2) */ slapi_mods_add_string(smods, LDAP_MOD_DELETE, attrName, sval); *p = '\0'; newvalue = slapi_ch_smprintf("%s%s", sval, newDN); slapi_mods_add_string(smods, LDAP_MOD_ADD, attrName, newvalue); slapi_ch_free_string(&newvalue); } /* else: value does not include the modified DN. Ignore it. */ slapi_ch_free_string(&sval); } rc = _do_modify(mod_pb, entrySDN, slapi_mods_get_ldapmods_byref(smods)); if (rc) { slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM, "_update_all_per_mod: entry %s failed (%d)\n", slapi_sdn_get_dn(entrySDN), rc); } /* cleanup memory allocated for dnParts and newDN */ if (dnParts){ slapi_ldap_value_free(dnParts); dnParts = NULL; } slapi_ch_free_string(&newDN); slapi_mods_free(&smods); } bail: return rc; }
/** Apply the mods to the ec entry. Check for syntax, schema problems. Check for abandon. Return code: -1 - error - result code and message are set appropriately 0 - successfully applied and checked 1 - not an error - no mods to apply or op abandoned */ static int modify_apply_check_expand( Slapi_PBlock *pb, Slapi_Operation *operation, LDAPMod **mods, /* list of mods to apply */ struct backentry *e, /* original "before" entry */ struct backentry *ec, /* "after" entry with mods applied */ Slapi_Entry **postentry, int *ldap_result_code, char **ldap_result_message ) { int rc = 0; int i; int repl_op; int change_entry = 0; Slapi_Mods smods = {0}; CSN *csn = operation_get_csn(operation); slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op); slapi_mods_init_byref( &smods, mods ); if ( (change_entry = mods_have_effect (ec->ep_entry, &smods)) ) { *ldap_result_code = entry_apply_mods_wsi(ec->ep_entry, &smods, csn, operation_is_flag_set(operation, OP_FLAG_REPLICATED)); /* * XXXmcs: it would be nice to get back an error message from * the above call so we could pass it along to the client, e.g., * "duplicate value for attribute givenName." */ } else { Slapi_Entry *epostop = NULL; /* If the entry was not actually changed, we still need to * set the SLAPI_ENTRY_POST_OP field in the pblock (post-op * plugins expect that field to be present for all modify * operations that return LDAP_SUCCESS). */ slapi_pblock_get ( pb, SLAPI_ENTRY_POST_OP, &epostop ); slapi_entry_free ( epostop ); /* free existing one, if any */ slapi_pblock_set ( pb, SLAPI_ENTRY_POST_OP, slapi_entry_dup( e->ep_entry ) ); *postentry = NULL; /* to avoid free in main error cleanup code */ } if ( !change_entry || *ldap_result_code != 0 ) { /* change_entry == 0 is not an error just a no-op */ rc = change_entry ? -1 : 1; goto done; } /* * If the objectClass attribute type was modified in any way, expand * the objectClass values to reflect the inheritance hierarchy. */ for ( i = 0; mods && mods[i]; ++i ) { if ( 0 == strcasecmp( SLAPI_ATTR_OBJECTCLASS, mods[i]->mod_type )) { slapi_schema_expand_objectclasses( ec->ep_entry ); break; } } /* * We are about to pass the last abandon test, so from now on we are * committed to finish this operation. Set status to "will complete" * before we make our last abandon check to avoid race conditions in * the code that processes abandon operations. */ operation->o_status = SLAPI_OP_STATUS_WILL_COMPLETE; if ( slapi_op_abandoned( pb ) ) { rc = 1; goto done; } /* multimaster replication can result in a schema violation, * although the individual operations on each master were valid * It is too late to resolve this. But we can check schema and * add a replication conflict attribute. */ /* check that the entry still obeys the schema */ if ((operation_is_flag_set(operation,OP_FLAG_ACTION_SCHEMA_CHECK)) && slapi_entry_schema_check_ext( pb, ec->ep_entry, 1 ) != 0 ) { if(repl_op){ Slapi_Attr *attr; Slapi_Mods smods; LDAPMod **lmods; if (slapi_entry_attr_find (ec->ep_entry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0) { /* add value */ Slapi_Value *val = slapi_value_new_string("Schema violation"); slapi_attr_add_value(attr,val); slapi_value_free(&val); } else { /* Add new attribute */ slapi_entry_add_string (ec->ep_entry, ATTR_NSDS5_REPLCONFLICT, "Schema violation"); } /* the replconflict attribute is indexed and the index is built from the mods, * so we need to extend the mods */ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &lmods); slapi_mods_init_passin(&smods, lmods); slapi_mods_add_string (&smods, LDAP_MOD_ADD, ATTR_NSDS5_REPLCONFLICT, "Schema violation"); lmods = slapi_mods_get_ldapmods_passout(&smods); slapi_pblock_set(pb, SLAPI_MODIFY_MODS, lmods); slapi_mods_done(&smods); } else { *ldap_result_code = LDAP_OBJECT_CLASS_VIOLATION; slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, ldap_result_message); rc = -1; goto done; } } if(!repl_op){ /* check attribute syntax for the new values */ if (slapi_mods_syntax_check(pb, mods, 0) != 0) { *ldap_result_code = LDAP_INVALID_SYNTAX; slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, ldap_result_message); rc = -1; goto done; } /* * make sure the entry contains all values in the RDN. * if not, the modification must have removed them. */ if ( ! slapi_entry_rdn_values_present( ec->ep_entry ) ) { *ldap_result_code= LDAP_NOT_ALLOWED_ON_RDN; rc = -1; goto done; } } done: slapi_mods_done( &smods ); return rc; }