Add BIND pre-op for DS->IPA password migration to ipa-pwd-extop DS plugin.

This commit is contained in:
Pavel Zuna
2009-09-04 14:51:28 +02:00
committed by Rob Crittenden
parent fd5742cc79
commit ba0e7b9c68
3 changed files with 244 additions and 15 deletions

View File

@@ -140,6 +140,9 @@ struct ipapwd_encsalt {
krb5_int32 salt_type;
};
/* base DN of IPA realm tree */
static const char *ipa_realm_tree;
/* dn of Kerberos realm entry */
static const char *ipa_realm_dn;
static const char *ipa_pwd_config_dn;
static const char *ipa_changepw_principal_dn;
@@ -1410,7 +1413,7 @@ static int ipapwd_CheckPolicy(struct ipapwd_data *data)
/* FIXME: *else* report an error ? */
} else {
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
"Warning: Last Password Change Time is not available");
"Warning: Last Password Change Time is not available\n");
}
/* Check min age */
@@ -3234,9 +3237,219 @@ done:
return rc;
}
/* PRE BIND Operation:
* Used for password migration from DS to IPA.
* Gets the clean text password, authenticates the user and generates
* a kerberos key if missing.
* Person to blame if anything blows up: Pavel Zuna <pzuna@redhat.com>
*/
static int ipapwd_pre_bind(Slapi_PBlock *pb)
{
struct ipapwd_krbcfg *krbcfg = NULL;
struct ipapwd_data pwdata;
struct berval *credentials; /* bind credentials */
Slapi_Entry *entry = NULL;
Slapi_Value **pwd_values = NULL; /* values of userPassword attribute */
Slapi_Value *value = NULL;
Slapi_Attr *attr = NULL;
struct tm expire_tm;
time_t expire_time;
char *errMesg = "Internal operations error\n"; /* error message */
char *expire = NULL; /* passwordExpirationTime attribute value */
char *dn = NULL; /* bind DN */
int method; /* authentication method */
int ret = 0;
slapi_log_error(SLAPI_LOG_TRACE, IPAPWD_PLUGIN_NAME,
"=> ipapwd_pre_bind\n");
/* get BIND parameters */
ret |= slapi_pblock_get(pb, SLAPI_BIND_TARGET, &dn);
ret |= slapi_pblock_get(pb, SLAPI_BIND_METHOD, &method);
ret |= slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &credentials);
if (ret) {
slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_pre_bind",
"slapi_pblock_get failed!?\n");
goto done;
}
/* we're only interested in simple authentication */
if (method != LDAP_AUTH_SIMPLE)
goto done;
/* list of attributes to retrieve */
const char *attrs_list[] = {SLAPI_USERPWD_ATTR, "krbprincipalkey", "uid",
"krbprincipalname", "objectclass",
"passwordexpirationtime", "passwordhistory",
NULL};
/* retrieve user entry */
ret = ipapwd_getEntry(dn, &entry, (char **) attrs_list);
if (ret) {
slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_pre_bind",
"failed to retrieve user entry: %s\n", dn);
goto done;
}
/* check the krbPrincipalName attribute is present */
ret = slapi_entry_attr_find(entry, "krbprincipalname", &attr);
if (ret) {
slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_pre_bind",
"no krbPrincipalName in user entry: %s\n", dn);
goto done;
}
/* check the krbPrincipalKey attribute is NOT present */
ret = slapi_entry_attr_find(entry, "krbprincipalkey", &attr);
if (!ret) {
slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_pre_bind",
"kerberos key already present in user entry: %s\n", dn);
goto done;
}
/* retrieve userPassword attribute */
ret = slapi_entry_attr_find(entry, SLAPI_USERPWD_ATTR, &attr);
if (ret) {
slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_pre_bind",
"no " SLAPI_USERPWD_ATTR " in user entry: %s\n", dn);
goto done;
}
/* get the number of userPassword values and allocate enough memory */
slapi_attr_get_numvalues(attr, &ret);
ret = (ret + 1) * sizeof (Slapi_Value *);
pwd_values = (Slapi_Value **) slapi_ch_malloc(ret);
if (!pwd_values) {
/* probably not required: should terminate the server anyway */
slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME,
"out of memory!?\n");
goto done;
}
/* zero-fill the allocated memory; we need the array ending with NULL */
memset(pwd_values, 0, ret);
/* retrieve userPassword values */
ret = slapi_attr_first_value(attr, &value);
while (ret != -1) {
pwd_values[ret] = value;
ret = slapi_attr_next_value(attr, ret, &value);
}
/* check if BIND password and userPassword match */
value = slapi_value_new_berval(credentials);
ret = slapi_pw_find_sv(pwd_values, value);
/* free before checking ret; we might not get a chance later */
slapi_ch_free((void **) &pwd_values);
slapi_value_free(&value);
if (ret) {
slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_pre_bind",
"invalid BIND password for user entry: %s\n", dn);
goto done;
}
/* general checks */
ret = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_DN);
if (ret) {
slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_pre_bind",
"ipapwd_gen_checks failed: %s", errMesg);
goto done;
}
/* delete userPassword - a new one will be generated later */
/* this is needed, otherwise ipapwd_CheckPolicy will think
* we're changing the password to its previous value
* and force a password change on next login */
ret = slapi_entry_attr_delete(entry, SLAPI_USERPWD_ATTR);
if (ret) {
slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_pre_bind",
"failed to delete " SLAPI_USERPWD_ATTR "\n");
goto done;
}
/* prepare data for kerberos key generation */
memset(&pwdata, 0, sizeof (pwdata));
pwdata.dn = dn;
pwdata.target = entry;
pwdata.password = credentials->bv_val;
pwdata.timeNow = time(NULL);
pwdata.changetype = IPA_CHANGETYPE_NORMAL;
/* keep password expiration time from DS, if possible */
expire = slapi_entry_attr_get_charptr(entry, "passwordexpirationtime");
if (expire) {
memset(&expire_tm, 0, sizeof (expire_tm));
if (strptime(expire, "%Y%m%d%H%M%SZ", &expire_tm))
pwdata.expireTime = mktime(&expire_tm);
}
/* check password policy */
ret = ipapwd_CheckPolicy(&pwdata);
if (ret) {
/* Password fails to meet IPA password policy,
* force user to change his password next time he logs in. */
slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_pre_bind",
"password policy check failed on user entry: %s"
" (force password change on next login)\n", dn);
pwdata.expireTime = time(NULL);
}
/* generate kerberos keys */
ret = ipapwd_SetPassword(krbcfg, &pwdata);
if (ret) {
slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_pre_bind",
"failed to set kerberos key for user entry: %s\n", dn);
goto done;
}
slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_pre_bind",
"kerberos key generated for user entry: %s\n", dn);
done:
slapi_ch_free_string(&expire);
if (entry)
slapi_entry_free(entry);
free_ipapwd_krbcfg(&krbcfg);
return 0;
}
char *ipapwd_getIpaConfigAttr(const char *attr)
{
/* check if migrtion is enabled */
Slapi_Entry *entry = NULL;
const char *attrs_list[] = {attr, 0};
char *value = NULL;
char *dn = NULL;
int ret;
dn = slapi_ch_smprintf("cn=ipaconfig,cn=etc,%s", ipa_realm_tree);
if (!dn) {
slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME,
"Out of memory ?\n");
goto done;
}
ret = ipapwd_getEntry(dn, &entry, (char **) attrs_list);
if (ret) {
slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
"failed to retrieve config entry: %s\n", dn);
goto done;
}
value = slapi_entry_attr_get_charptr(entry, attr);
done:
slapi_entry_free(entry);
slapi_ch_free_string(&dn);
return value;
}
/* PRE ADD Operation:
* Gets the clean text password (fail the operation if the password came
* pre-hashed, unless this is a replicated operation).
* pre-hashed, unless this is a replicated operation or migration mode is
* enabled).
* Check user is authorized to add it otherwise just returns, operation will
* fail later anyway.
* Run a password policy check.
@@ -3302,7 +3515,7 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
/* unhashed#user#password doesn't always contain the clear text
* password, therefore we need to check if its value isn't the same
* as userPassword, to make sure */
* as userPassword to make sure */
if (!userpw || (0 == strcmp(userpw, userpw_clear))) {
rc = LDAP_CONSTRAINT_VIOLATION;
}
@@ -3311,9 +3524,22 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
slapi_ch_free_string(&userpw_clear);
if (rc) {
/* we don't have access to the clear text password,
* let the operation continue, but don't generate keys */
return 0;
/* we don't have access to the clear text password;
* let it slide if migration is enabled, but don't
* generate kerberos keys */
char *enabled = ipapwd_getIpaConfigAttr("ipamigrationenabled");
if (NULL == enabled) {
slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
"no ipaMigrationEnabled in config;"
" assuming FALSE\n");
} else if (0 == strcmp(enabled, "TRUE")) {
return 0;
}
slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
"pre-hashed passwords are not valid\n");
errMesg = "pre-hashed passwords are not valid\n";
goto done;
}
}
}
@@ -3850,7 +4076,7 @@ static int ipapwd_post_op(Slapi_PBlock *pb)
int ret;
slapi_log_error(SLAPI_LOG_TRACE, IPAPWD_PLUGIN_NAME,
"=> ipapwd_post_add\n");
"=> ipapwd_post_op\n");
/* time to get the operation handler */
ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op);
@@ -3959,11 +4185,10 @@ Slapi_Filter *ipapwd_string2filter(char *strfilter)
/* Init data structs */
static int ipapwd_start( Slapi_PBlock *pb )
{
krb5_context krbctx;
krb5_context krbctx = NULL;
krb5_error_code krberr;
char *realm = NULL;
char *config_dn;
char *partition_dn;
Slapi_Entry *config_entry = NULL;
int ret;
@@ -3985,8 +4210,8 @@ static int ipapwd_start( Slapi_PBlock *pb )
goto done;
}
partition_dn = slapi_entry_attr_get_charptr(config_entry, "nsslapd-realmtree");
if (!partition_dn) {
ipa_realm_tree = slapi_entry_attr_get_charptr(config_entry, "nsslapd-realmtree");
if (!ipa_realm_tree) {
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Missing partition configuration entry (nsslapd-realmTree)!\n");
ret = LDAP_OPERATIONS_ERROR;
goto done;
@@ -3998,7 +4223,7 @@ static int ipapwd_start( Slapi_PBlock *pb )
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
ipa_realm_dn = slapi_ch_smprintf("cn=%s,cn=kerberos,%s", realm, partition_dn);
ipa_realm_dn = slapi_ch_smprintf("cn=%s,cn=kerberos,%s", realm, ipa_realm_tree);
if (!ipa_realm_dn) {
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Out of memory ?\n");
ret = LDAP_OPERATIONS_ERROR;
@@ -4067,6 +4292,7 @@ static int ipapwd_pre_init(Slapi_PBlock *pb)
ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&pdesc);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_BIND_FN, (void *)ipapwd_pre_bind);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, (void *)ipapwd_pre_add);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, (void *)ipapwd_pre_mod);

View File

@@ -35,11 +35,13 @@ attributetypes: ( 2.16.840.1.113730.3.8.1.11 NAME 'ipaUserObjectClasses' SYNTAX
attributetypes: ( 2.16.840.1.113730.3.8.1.12 NAME 'ipaGroupObjectClasses' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15)
attributetypes: ( 2.16.840.1.113730.3.8.1.13 NAME 'ipaDefaultEmailDomain' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15)
attributeTypes: ( 2.16.840.1.113730.3.8.3.50 NAME 'ipaObsoleteEscrowPacketLifetime' DESC 'Number of days before an obsolete escrow packet is deleted (if a newer packet for the same volume is available)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE)
attributeTypes: (2.16.840.1.113730.3.8.3.51 NAME 'ipaEscrowKeyCertificate' DESC 'Certificate for encrypting escrow packets' SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 SINGLE-VALUE)
attributeTypes: (2.16.840.1.113730.3.8.3.52 NAME 'ipaEscrowKey' DESC 'PKCS#12-formatted encrypted certificate and private key for encrypting escrow packets' SYNTAX 1.3.6.1.4.1.1466.115.121.1.5)
attributeTypes: ( 2.16.840.1.113730.3.8.3.51 NAME 'ipaEscrowKeyCertificate' DESC 'Certificate for encrypting escrow packets' SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 SINGLE-VALUE)
attributeTypes: ( 2.16.840.1.113730.3.8.3.52 NAME 'ipaEscrowKey' DESC 'PKCS#12-formatted encrypted certificate and private key for encrypting escrow packets' SYNTAX 1.3.6.1.4.1.1466.115.121.1.5)
# ipaMigrationEnabled - if TRUE allow adding user entries with pre-hashed passwords
attributeTypes: ( 2.16.840.1.113730.3.8.1.16 NAME 'ipaMigrationEnabled' DESC 'Enable adding user entries with pre-hashed passwords.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
###############################################
##
## ObjectClasses
##
## ipaGuiConfig - GUI config parameters objectclass
objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaObsoleteEscrowPacketLifetime $ ipaEscrowKeyCertificate $ ipaEscrowKey) )
objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaObsoleteEscrowPacketLifetime $ ipaEscrowKeyCertificate $ ipaEscrowKey $ ipaMigrationEnabled ) )

View File

@@ -170,6 +170,7 @@ ipaUserObjectClasses: krbticketpolicyaux
ipaUserObjectClasses: radiusprofile
ipaUserObjectClasses: ipaobject
ipaDefaultEmailDomain: $DOMAIN
ipaMigrationEnabled: FALSE
dn: cn=account inactivation,cn=accounts,$SUFFIX
changetype: add