diff options
Diffstat (limited to 'daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c')
-rw-r--r-- | daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c | 3224 |
1 files changed, 9 insertions, 3215 deletions
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c index c2d0373e6..cbf572188 100644 --- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c +++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c @@ -33,13 +33,11 @@ * Authors: * Simo Sorce <ssorce@redhat.com> * - * Copyright (C) 2005 Red Hat, Inc. + * Copyright (C) 2007-2010 Red Hat, Inc. * All rights reserved. * END COPYRIGHT BLOCK **/ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif +#include "ipapwd.h" /* * Password Modify - LDAP Extended Operation. @@ -53,31 +51,6 @@ * */ -#include <stdio.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> - -#include <prio.h> -#include <ssl.h> -#include <dirsrv/slapi-plugin.h> -#define KRB5_PRIVATE 1 -#include <krb5.h> -#include <lber.h> -#include <time.h> -#include <iconv.h> -#include <openssl/des.h> -#include <openssl/md4.h> - -/* Type of connection for this operation;*/ -#define LDAP_EXTOP_PASSMOD_CONN_SECURE - -/* Uncomment the following #undef FOR TESTING: - * allows non-SSL connections to use the password change extended op */ -/* #undef LDAP_EXTOP_PASSMOD_CONN_SECURE */ - /* ber tags for the PasswdModifyRequestValue sequence */ #define LDAP_EXTOP_PASSMOD_TAG_USERID 0x80U #define LDAP_EXTOP_PASSMOD_TAG_OLDPWD 0x81U @@ -93,164 +66,23 @@ #define KEYTAB_SET_OID "2.16.840.1.113730.3.8.3.1" #define KEYTAB_RET_OID "2.16.840.1.113730.3.8.3.2" -/* krbTicketFlags */ -#define KTF_DISALLOW_POSTDATED 0x00000001 -#define KTF_DISALLOW_FORWARDABLE 0x00000002 -#define KTF_DISALLOW_TGT_BASED 0x00000004 -#define KTF_DISALLOW_RENEWABLE 0x00000008 -#define KTF_DISALLOW_PROXIABLE 0x00000010 -#define KTF_DISALLOW_DUP_SKEY 0x00000020 -#define KTF_DISALLOW_ALL_TIX 0x00000040 -#define KTF_REQUIRES_PRE_AUTH 0x00000080 -#define KTF_REQUIRES_HW_AUTH 0x00000100 -#define KTF_REQUIRES_PWCHANGE 0x00000200 -#define KTF_DISALLOW_SVR 0x00001000 -#define KTF_PWCHANGE_SERVICE 0x00002000 - -/* These are the default enc:salt types if nothing is defined. - * TODO: retrieve the configure set of ecntypes either from the - * kfc.conf file or by synchronizing the the file content into - * the directory */ - -/* Salt types */ -#define KRB5_KDB_SALTTYPE_NORMAL 0 -#define KRB5_KDB_SALTTYPE_V4 1 -#define KRB5_KDB_SALTTYPE_NOREALM 2 -#define KRB5_KDB_SALTTYPE_ONLYREALM 3 -#define KRB5_KDB_SALTTYPE_SPECIAL 4 -#define KRB5_KDB_SALTTYPE_AFS3 5 - -#define KRB5P_SALT_SIZE 16 - -void krb5int_c_free_keyblock_contents(krb5_context context, register krb5_keyblock *key); - -/* Forward definition */ -static int ipapwd_entry_checks(Slapi_PBlock *pb, struct slapi_entry *e, - int *is_root, int *is_krb, int *is_smb, - char *attr, int access); - -static const char *ipapwd_def_encsalts[] = { - "des3-hmac-sha1:normal", -/* "arcfour-hmac:normal", - "des-hmac-sha1:normal", - "des-cbc-md5:normal", */ - "des-cbc-crc:normal", -/* "des-cbc-crc:v4", - "des-cbc-crc:afs3", */ - NULL -}; -struct ipapwd_encsalt { - krb5_int32 enc_type; - krb5_int32 salt_type; -}; /* base DN of IPA realm tree */ -static const char *ipa_realm_tree; +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; - -#define IPAPWD_PLUGIN_NAME "ipa-pwd-extop" -#define IPAPWD_FEATURE_DESC "IPA Password Manager" -#define IPAPWD_PLUGIN_DESC "IPA Password Extended Operation plugin" +const char *ipa_realm_dn; +const char *ipa_pwd_config_dn; +const char *ipa_changepw_principal_dn; -static Slapi_PluginDesc pdesc = { +Slapi_PluginDesc ipapwd_plugin_desc = { IPAPWD_FEATURE_DESC, "FreeIPA project", "FreeIPA/1.0", IPAPWD_PLUGIN_DESC }; -static void *ipapwd_plugin_id; - -#define IPA_CHANGETYPE_NORMAL 0 -#define IPA_CHANGETYPE_ADMIN 1 -#define IPA_CHANGETYPE_DSMGR 2 - -struct ipapwd_krbcfg { - krb5_context krbctx; - char *realm; - krb5_keyblock *kmkey; - int num_supp_encsalts; - struct ipapwd_encsalt *supp_encsalts; - int num_pref_encsalts; - struct ipapwd_encsalt *pref_encsalts; - char **passsync_mgrs; - int num_passsync_mgrs; -}; - -static void free_ipapwd_krbcfg(struct ipapwd_krbcfg **cfg) -{ - struct ipapwd_krbcfg *c = *cfg; - - if (!c) return; - - krb5_free_default_realm(c->krbctx, c->realm); - krb5_free_context(c->krbctx); - free(c->kmkey->contents); - free(c->kmkey); - free(c->supp_encsalts); - free(c->pref_encsalts); - slapi_ch_array_free(c->passsync_mgrs); - free(c); - *cfg = NULL; -}; - -struct ipapwd_data { - Slapi_Entry *target; - char *dn; - char *password; - time_t timeNow; - time_t lastPwChange; - time_t expireTime; - int changetype; - int pwHistoryLen; -}; - -struct ipapwd_krbkeydata { - int32_t type; - struct berval value; -}; - -struct ipapwd_krbkey { - struct ipapwd_krbkeydata *salt; - struct ipapwd_krbkeydata *ekey; - struct berval s2kparams; -}; - -struct ipapwd_keyset { - uint16_t major_vno; - uint16_t minor_vno; - uint32_t kvno; - uint32_t mkvno; - struct ipapwd_krbkey *keys; - int num_keys; -}; - -static void ipapwd_keyset_free(struct ipapwd_keyset **pkset) -{ - struct ipapwd_keyset *kset = *pkset; - int i; - - if (!kset) return; - - for (i = 0; i < kset->num_keys; i++) { - if (kset->keys[i].salt) { - free(kset->keys[i].salt->value.bv_val); - free(kset->keys[i].salt); - } - if (kset->keys[i].ekey) { - free(kset->keys[i].ekey->value.bv_val); - free(kset->keys[i].ekey); - } - free(kset->keys[i].s2kparams.bv_val); - } - free(kset->keys); - free(kset); - *pkset = NULL; -} +void *ipapwd_plugin_id; static int filter_keys(struct ipapwd_krbcfg *krbcfg, struct ipapwd_keyset *kset) { @@ -292,1563 +124,6 @@ static int filter_keys(struct ipapwd_krbcfg *krbcfg, struct ipapwd_keyset *kset) return 0; } -/* Novell key-format scheme: - - KrbKeySet ::= SEQUENCE { - attribute-major-vno [0] UInt16, - attribute-minor-vno [1] UInt16, - kvno [2] UInt32, - mkvno [3] UInt32 OPTIONAL, - keys [4] SEQUENCE OF KrbKey, - ... - } - - KrbKey ::= SEQUENCE { - salt [0] KrbSalt OPTIONAL, - key [1] EncryptionKey, - s2kparams [2] OCTET STRING OPTIONAL, - ... - } - - KrbSalt ::= SEQUENCE { - type [0] Int32, - salt [1] OCTET STRING OPTIONAL - } - - EncryptionKey ::= SEQUENCE { - keytype [0] Int32, - keyvalue [1] OCTET STRING - } - - */ - -static struct berval *encode_keys(struct ipapwd_keyset *kset) -{ - BerElement *be = NULL; - struct berval *bval = NULL; - int ret, i; - - be = ber_alloc_t(LBER_USE_DER); - - if (!be) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "memory allocation failed\n"); - return NULL; - } - - ret = ber_printf(be, "{t[i]t[i]t[i]t[i]t[{", - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), kset->major_vno, - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), kset->minor_vno, - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 2), kset->kvno, - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 3), kset->mkvno, - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 4)); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 vno info failed\n"); - goto done; - } - - for (i = 0; i < kset->num_keys; i++) { - - ret = ber_printf(be, "{"); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 EncryptionKey failed\n"); - goto done; - } - - if (kset->keys[i].salt) { - ret = ber_printf(be, "t[{t[i]", - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), - kset->keys[i].salt->type); - if ((ret != -1) && kset->keys[i].salt->value.bv_len) { - ret = ber_printf(be, "t[o]", - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), - kset->keys[i].salt->value.bv_val, - kset->keys[i].salt->value.bv_len); - } - if (ret != -1) { - ret = ber_printf(be, "}]"); - } - if (ret == -1) { - goto done; - } - } - - ret = ber_printf(be, "t[{t[i]t[o]}]", - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), - kset->keys[i].ekey->type, - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), - kset->keys[i].ekey->value.bv_val, - kset->keys[i].ekey->value.bv_len); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 EncryptionKey failed\n"); - goto done; - } - - /* FIXME: s2kparams not supported yet */ - - ret = ber_printf(be, "}"); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 EncryptionKey failed\n"); - goto done; - } - } - - ret = ber_printf(be, "}]}"); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 end of sequences failed\n"); - goto done; - } - - ret = ber_flatten(be, &bval); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "flattening asn1 failed\n"); - goto done; - } -done: - ber_free(be, 1); - - return bval; -} - -static int ipapwd_get_cur_kvno(Slapi_Entry *target) -{ - Slapi_Attr *krbPrincipalKey = NULL; - Slapi_ValueSet *svs; - Slapi_Value *sv; - BerElement *be = NULL; - const struct berval *cbval; - ber_tag_t tag, tmp; - ber_int_t tkvno; - int hint; - int kvno; - int ret; - - /* retrieve current kvno and and keys */ - ret = slapi_entry_attr_find(target, "krbPrincipalKey", &krbPrincipalKey); - if (ret != 0) { - return 0; - } - - kvno = 0; - - slapi_attr_get_valueset(krbPrincipalKey, &svs); - hint = slapi_valueset_first_value(svs, &sv); - while (hint != -1) { - cbval = slapi_value_get_berval(sv); - if (!cbval) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "Error retrieving berval from Slapi_Value\n"); - goto next; - } - be = ber_init(cbval); - if (!be) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ber_init() failed!\n"); - goto next; - } - - tag = ber_scanf(be, "{xxt[i]", &tmp, &tkvno); - if (tag == LBER_ERROR) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "Bad OLD key encoding ?!\n"); - ber_free(be, 1); - goto next; - } - - if (tkvno > kvno) { - kvno = tkvno; - } - - ber_free(be, 1); -next: - hint = slapi_valueset_next_value(svs, hint, &sv); - } - - return kvno; -} - -static inline void encode_int16(unsigned int val, unsigned char *p) -{ - p[1] = (val >> 8) & 0xff; - p[0] = (val ) & 0xff; -} - -static Slapi_Value **encrypt_encode_key(struct ipapwd_krbcfg *krbcfg, - struct ipapwd_data *data, - char **errMesg) -{ - krb5_context krbctx; - char *krbPrincipalName = NULL; - uint32_t krbMaxTicketLife; - int kvno, i; - int krbTicketFlags; - struct berval *bval = NULL; - Slapi_Value **svals = NULL; - krb5_principal princ; - krb5_error_code krberr; - krb5_data pwd; - struct ipapwd_keyset *kset = NULL; - - krbctx = krbcfg->krbctx; - - svals = (Slapi_Value **)calloc(2, sizeof(Slapi_Value *)); - if (!svals) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "memory allocation failed\n"); - return NULL; - } - - kvno = ipapwd_get_cur_kvno(data->target); - - krbPrincipalName = slapi_entry_attr_get_charptr(data->target, "krbPrincipalName"); - if (!krbPrincipalName) { - *errMesg = "no krbPrincipalName present in this entry\n"; - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", *errMesg); - return NULL; - } - - krberr = krb5_parse_name(krbctx, krbPrincipalName, &princ); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_parse_name failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - goto enc_error; - } - - 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 */ - } - - krbTicketFlags = slapi_entry_attr_get_int(data->target, "krbTicketFlags"); - - pwd.data = (char *)data->password; - pwd.length = strlen(data->password); - - kset = malloc(sizeof(struct ipapwd_keyset)); - if (!kset) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - goto enc_error; - } - - /* this encoding assumes all keys have the same kvno */ - /* major-vno = 1 and minor-vno = 1 */ - kset->major_vno = 1; - kset->minor_vno = 1; - /* increment kvno (will be 1 if this is a new entry) */ - kset->kvno = kvno + 1; - /* we also assum mkvno is 0 */ - kset->mkvno = 0; - - kset->num_keys = krbcfg->num_pref_encsalts; - kset->keys = calloc(kset->num_keys, sizeof(struct ipapwd_krbkey)); - if (!kset->keys) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - goto enc_error; - } - - for (i = 0; i < kset->num_keys; i++) { - krb5_keyblock key; - krb5_data salt; - krb5_octet *ptr; - krb5_data plain; - krb5_enc_data cipher; - size_t len; - const char *p; - - salt.data = NULL; - - switch (krbcfg->pref_encsalts[i].salt_type) { - - case KRB5_KDB_SALTTYPE_ONLYREALM: - - p = strchr(krbPrincipalName, '@'); - if (!p) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "Invalid principal name, no realm found!\n"); - goto enc_error; - } - p++; - salt.data = strdup(p); - if (!salt.data) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "memory allocation failed\n"); - goto enc_error; - } - salt.length = strlen(salt.data); /* final \0 omitted on purpose */ - break; - - case KRB5_KDB_SALTTYPE_NOREALM: - - krberr = krb5_principal2salt_norealm(krbctx, princ, &salt); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_principal2salt failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - goto enc_error; - } - break; - - case KRB5_KDB_SALTTYPE_NORMAL: - - /* If pre auth is required we can set a random salt, otherwise - * we have to use a more conservative approach and set the salt - * to be REALMprincipal (the concatenation of REALM and principal - * name without any separator) */ -#if 0 - if (krbTicketFlags & KTF_REQUIRES_PRE_AUTH) { - salt.length = KRB5P_SALT_SIZE; - salt.data = malloc(KRB5P_SALT_SIZE); - if (!salt.data) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "memory allocation failed\n"); - goto enc_error; - } - krberr = krb5_c_random_make_octets(krbctx, &salt); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_c_random_make_octets failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - goto enc_error; - } - } else { -#endif - krberr = krb5_principal2salt(krbctx, princ, &salt); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_principal2salt failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - goto enc_error; - } -#if 0 - } -#endif - break; - - case KRB5_KDB_SALTTYPE_V4: - salt.length = 0; - break; - - case KRB5_KDB_SALTTYPE_AFS3: - - p = strchr(krbPrincipalName, '@'); - if (!p) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "Invalid principal name, no realm found!\n"); - goto enc_error; - } - p++; - salt.data = strdup(p); - if (!salt.data) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "memory allocation failed\n"); - goto enc_error; - } - salt.length = SALT_TYPE_AFS_LENGTH; /* special value */ - break; - - default: - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "Invalid salt type [%d]\n", krbcfg->pref_encsalts[i].salt_type); - goto enc_error; - } - - /* need to build the key now to manage the AFS salt.length special case */ - krberr = krb5_c_string_to_key(krbctx, krbcfg->pref_encsalts[i].enc_type, &pwd, &salt, &key); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_c_string_to_key failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - krb5_free_data_contents(krbctx, &salt); - goto enc_error; - } - if (salt.length == SALT_TYPE_AFS_LENGTH) { - salt.length = strlen(salt.data); - } - - krberr = krb5_c_encrypt_length(krbctx, krbcfg->kmkey->enctype, key.length, &len); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_c_string_to_key failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - krb5int_c_free_keyblock_contents(krbctx, &key); - krb5_free_data_contents(krbctx, &salt); - goto enc_error; - } - - if ((ptr = (krb5_octet *) malloc(2 + len)) == NULL) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "memory allocation failed\n"); - krb5int_c_free_keyblock_contents(krbctx, &key); - krb5_free_data_contents(krbctx, &salt); - goto enc_error; - } - - encode_int16(key.length, ptr); - - plain.length = key.length; - plain.data = (char *)key.contents; - - cipher.ciphertext.length = len; - cipher.ciphertext.data = (char *)ptr+2; - - krberr = krb5_c_encrypt(krbctx, krbcfg->kmkey, 0, 0, &plain, &cipher); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_c_encrypt failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - krb5int_c_free_keyblock_contents(krbctx, &key); - krb5_free_data_contents(krbctx, &salt); - free(ptr); - goto enc_error; - } - - /* KrbSalt */ - kset->keys[i].salt = malloc(sizeof(struct ipapwd_krbkeydata)); - if (!kset->keys[i].salt) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - krb5int_c_free_keyblock_contents(krbctx, &key); - free(ptr); - goto enc_error; - } - - kset->keys[i].salt->type = krbcfg->pref_encsalts[i].salt_type; - - if (salt.length) { - kset->keys[i].salt->value.bv_len = salt.length; - kset->keys[i].salt->value.bv_val = salt.data; - } - - /* EncryptionKey */ - kset->keys[i].ekey = malloc(sizeof(struct ipapwd_krbkeydata)); - if (!kset->keys[i].ekey) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - krb5int_c_free_keyblock_contents(krbctx, &key); - free(ptr); - goto enc_error; - } - kset->keys[i].ekey->type = key.enctype; - kset->keys[i].ekey->value.bv_len = len+2; - kset->keys[i].ekey->value.bv_val = malloc(len+2); - if (!kset->keys[i].ekey->value.bv_val) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - krb5int_c_free_keyblock_contents(krbctx, &key); - free(ptr); - goto enc_error; - } - memcpy(kset->keys[i].ekey->value.bv_val, ptr, len+2); - - /* make sure we free the memory used now that we are done with it */ - krb5int_c_free_keyblock_contents(krbctx, &key); - free(ptr); - } - - bval = encode_keys(kset); - if (!bval) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 KrbSalt failed\n"); - goto enc_error; - } - - svals[0] = slapi_value_new_berval(bval); - if (!svals[0]) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "Converting berval to Slapi_Value\n"); - goto enc_error; - } - - ipapwd_keyset_free(&kset); - krb5_free_principal(krbctx, princ); - slapi_ch_free_string(&krbPrincipalName); - ber_bvfree(bval); - return svals; - -enc_error: - *errMesg = "key encryption/encoding failed\n"; - if (kset) ipapwd_keyset_free(&kset); - krb5_free_principal(krbctx, princ); - slapi_ch_free_string(&krbPrincipalName); - if (bval) ber_bvfree(bval); - free(svals); - return NULL; -} - -static void ipapwd_free_slapi_value_array(Slapi_Value ***svals) -{ - Slapi_Value **sv = *svals; - int i; - - if (sv) { - for (i = 0; sv[i]; i++) { - slapi_value_free(&sv[i]); - } - } - - slapi_ch_free((void **)sv); -} - - -struct ntlm_keys { - uint8_t lm[16]; - uint8_t nt[16]; -}; - -#define KTF_LM_HASH 0x01 -#define KTF_NT_HASH 0x02 -#define KTF_DOS_CHARSET "CP850" /* same default as samba */ -#define KTF_UTF8 "UTF-8" -#define KTF_UCS2 "UCS-2LE" - -static const uint8_t parity_table[128] = { - 1, 2, 4, 7, 8, 11, 13, 14, 16, 19, 21, 22, 25, 26, 28, 31, - 32, 35, 37, 38, 41, 42, 44, 47, 49, 50, 52, 55, 56, 59, 61, 62, - 64, 67, 69, 70, 73, 74, 76, 79, 81, 82, 84, 87, 88, 91, 93, 94, - 97, 98,100,103,104,107,109,110,112,115,117,118,121,122,124,127, - 128,131,133,134,137,138,140,143,145,146,148,151,152,155,157,158, - 161,162,164,167,168,171,173,174,176,179,181,182,185,186,188,191, - 193,194,196,199,200,203,205,206,208,211,213,214,217,218,220,223, - 224,227,229,230,233,234,236,239,241,242,244,247,248,251,253,254}; - -static void lm_shuffle(uint8_t *out, uint8_t *in) -{ - out[0] = parity_table[in[0]>>1]; - out[1] = parity_table[((in[0]<<6)|(in[1]>>2)) & 0x7F]; - out[2] = parity_table[((in[1]<<5)|(in[2]>>3)) & 0x7F]; - out[3] = parity_table[((in[2]<<4)|(in[3]>>4)) & 0x7F]; - out[4] = parity_table[((in[3]<<3)|(in[4]>>5)) & 0x7F]; - out[5] = parity_table[((in[4]<<2)|(in[5]>>6)) & 0x7F]; - out[6] = parity_table[((in[5]<<1)|(in[6]>>7)) & 0x7F]; - out[7] = parity_table[in[6] & 0x7F]; -} - -/* create the lm and nt hashes - newPassword: the clear text utf8 password - flags: KTF_LM_HASH | KTF_NT_HASH -*/ -static int encode_ntlm_keys(char *newPasswd, unsigned int flags, struct ntlm_keys *keys) -{ - int ret = 0; - - /* do lanman first */ - if (flags & KTF_LM_HASH) { - iconv_t cd; - size_t cs, il, ol; - char *inc, *outc; - char *upperPasswd; - char *asciiPasswd; - DES_key_schedule schedule; - DES_cblock deskey; - DES_cblock magic = "KGS!@#$%"; - - /* TODO: must store the dos charset somewhere in the directory */ - cd = iconv_open(KTF_DOS_CHARSET, KTF_UTF8); - if (cd == (iconv_t)(-1)) { - ret = -1; - goto done; - } - - /* the lanman password is upper case */ - upperPasswd = (char *)slapi_utf8StrToUpper((unsigned char *)newPasswd); - if (!upperPasswd) { - ret = -1; - goto done; - } - il = strlen(upperPasswd); - - /* an ascii string can only be smaller than or equal to an utf8 one */ - ol = il; - if (ol < 14) ol = 14; - asciiPasswd = calloc(ol+1, 1); - if (!asciiPasswd) { - slapi_ch_free_string(&upperPasswd); - ret = -1; - goto done; - } - - inc = upperPasswd; - outc = asciiPasswd; - cs = iconv(cd, &inc, &il, &outc, &ol); - if (cs == -1) { - ret = -1; - slapi_ch_free_string(&upperPasswd); - free(asciiPasswd); - iconv_close(cd); - goto done; - } - - /* done with these */ - slapi_ch_free_string(&upperPasswd); - iconv_close(cd); - - /* we are interested only in the first 14 ASCII chars for lanman */ - if (strlen(asciiPasswd) > 14) { - asciiPasswd[14] = '\0'; - } - - /* first half */ - lm_shuffle(deskey, (uint8_t *)asciiPasswd); - - DES_set_key_unchecked(&deskey, &schedule); - DES_ecb_encrypt(&magic, (DES_cblock *)keys->lm, &schedule, DES_ENCRYPT); - - /* second half */ - lm_shuffle(deskey, (uint8_t *)&asciiPasswd[7]); - - DES_set_key_unchecked(&deskey, &schedule); - DES_ecb_encrypt(&magic, (DES_cblock *)&(keys->lm[8]), &schedule, DES_ENCRYPT); - - /* done with it */ - free(asciiPasswd); - - } else { - memset(keys->lm, 0, 16); - } - - if (flags & KTF_NT_HASH) { - iconv_t cd; - size_t cs, il, ol, sl; - char *inc, *outc; - char *ucs2Passwd; - MD4_CTX md4ctx; - - /* TODO: must store the dos charset somewhere in the directory */ - cd = iconv_open(KTF_UCS2, KTF_UTF8); - if (cd == (iconv_t)(-1)) { - ret = -1; - goto done; - } - - il = strlen(newPasswd); - - /* an ucs2 string can be at most double than an utf8 one */ - sl = ol = (il+1)*2; - ucs2Passwd = calloc(ol, 1); - if (!ucs2Passwd) { - ret = -1; - goto done; - } - - inc = newPasswd; - outc = ucs2Passwd; - cs = iconv(cd, &inc, &il, &outc, &ol); - if (cs == -1) { - ret = -1; - free(ucs2Passwd); - iconv_close(cd); - goto done; - } - - /* done with it */ - iconv_close(cd); - - /* get the final ucs2 string length */ - sl -= ol; - /* we are interested only in the first 14 wchars for the nt password */ - if (sl > 28) { - sl = 28; - } - - ret = MD4_Init(&md4ctx); - if (ret == 0) { - ret = -1; - free(ucs2Passwd); - goto done; - } - ret = MD4_Update(&md4ctx, ucs2Passwd, sl); - if (ret == 0) { - ret = -1; - free(ucs2Passwd); - goto done; - } - ret = MD4_Final(keys->nt, &md4ctx); - if (ret == 0) { - ret = -1; - free(ucs2Passwd); - goto done; - } - - } else { - memset(keys->nt, 0, 16); - } - - ret = 0; - -done: - return ret; -} - -/* Easier handling for virtual attributes. You must call pwd_values_free() - * to free memory allocated here. It must be called before - * slapi_free_search_results_internal(entries) or - * slapi_pblock_destroy(pb) - */ -static int -pwd_get_values(const Slapi_Entry *ent, const char *attrname, - Slapi_ValueSet** results, char** actual_type_name, - int *buffer_flags) -{ - int flags=0; - int type_name_disposition = 0; - - int ret = slapi_vattr_values_get((Slapi_Entry *)ent, (char *)attrname, results, &type_name_disposition, actual_type_name, flags, buffer_flags); - - return ret; -} - -static void -pwd_values_free(Slapi_ValueSet** results, char** actual_type_name, int buffer_flags) -{ - slapi_vattr_values_free(results, actual_type_name, buffer_flags); -} - -/* 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 = NULL; - 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 = NULL; - int buffer_flags=0; - Slapi_ValueSet* results = NULL; - char* actual_type_name = NULL; - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_getPolicy: Searching policy for [%s]\n", dn); - - sdn = slapi_sdn_new_dn_byref(dn); - if (sdn == NULL) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_getPolicy: Out of memory on [%s]\n", dn); - ret = -1; - goto done; - } - - pwd_get_values(target, "krbPwdPolicyReference", &results, &actual_type_name, &buffer_flags); - if (results) { - Slapi_Value *sv; - slapi_valueset_first_value(results, &sv); - krbPwdPolicyReference = slapi_value_get_string(sv); - pdn = krbPwdPolicyReference; - scope = LDAP_SCOPE_BASE; - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_getPolicy: using policy reference: %s\n", pdn); - } else { - /* Find ancestor base DN */ - be = slapi_be_select(sdn); - psdn = slapi_be_getsuffix(be, 0); - if (psdn == NULL) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_getPolicy: Invalid DN [%s]\n", dn); - ret = -1; - goto done; - } - 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); - ret = -1; - goto done; - } - - /* 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 ?!"); - ret = -1; - goto done; - } - - /* 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]); - - ret = 0; - goto done; - } - - /* 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 ?!"); - ret = -1; - goto done; - } - 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 (esdn == NULL) continue; - 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) { - ret = -1; - goto done; - } - - *e = slapi_entry_dup(pe); - ret = 0; -done: - if (results) { - pwd_values_free(&results, &actual_type_name, buffer_flags); - } - if (pb) { - slapi_free_search_results_internal(pb); - slapi_pblock_destroy(pb); - } - if (sdn) slapi_sdn_free(&sdn); - return ret; -} - -#define GENERALIZED_TIME_LENGTH 15 - -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); -} - -static 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; - - old_pw = slapi_entry_attr_get_charptr(data->target, "userPassword"); - if (!old_pw) { - /* no old password to store, just return */ - return NULL; - } - - if (!gmtime_r(&(data->timeNow), &utctime)) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "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) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - return NULL; - } - - /* retrieve current history */ - ret = slapi_entry_attr_find(data->target, "passwordHistory", &passwordHistory); - if (ret == 0) { - int ret, hint, count, i; - const char *pwstr; - Slapi_Value *pw; - - hint = 0; - count = 0; - ret = slapi_attr_get_numvalues(passwordHistory, &count); - /* if we have one */ - if (count > 0 && data->pwHistoryLen > 0) { - pH = calloc(count + 2, sizeof(Slapi_Value *)); - if (!pH) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - 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) { - 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) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - 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) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - free(histr); - return NULL; - } - pc = 0; - } - - /* add new history value */ - pH[pc] = slapi_value_new_string(histr); - - free(histr); - - return pH; -} - -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]); -} - -#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 0 - -/* check password strenght and history */ -static 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; - - /* 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)) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "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) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "No password policy"); - goto no_policy; - } - - /* Retrieve Max History Len */ - data->pwHistoryLen = slapi_entry_attr_get_int(policy, "krbPwdHistoryLength"); - - if (data->changetype != IPA_CHANGETYPE_NORMAL) { - /* We must skip policy checks (Admin change) but - * force a password change on the next login. - * But not if Directory Manager */ - if (data->changetype == IPA_CHANGETYPE_ADMIN) { - 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) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - 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) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_checkPassword: 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 ? */ - } else { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "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 */ - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_checkPolicy: Ignore krbMinPwdLife Expiration, not enough info\n"); - - } else if (data->timeNow < data->lastPwChange + krbMinPwdLife) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_checkPolicy: 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; - } - } - - /* free strings or we leak them */ - slapi_ch_free_string(&krbPasswordExpiration); - slapi_ch_free_string(&krbLastPwdChange); - - /* Retrieve min length */ - tmp = slapi_entry_attr_get_int(policy, "krbPwdMinLength"); - if (tmp != 0) { - krbPwdMinLength = tmp; - } - - /* 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 > 1) - --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; - } - } - - /* Check password history */ - ret = slapi_entry_attr_find(data->target, "passwordHistory", &passwordHistory); - if (ret == 0) { - int ret, hint, count, i, j; - const char *pwstr; - Slapi_Value **pH; - Slapi_Value *pw; - - hint = 0; - count = 0; - ret = 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) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - 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) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - slapi_entry_free(policy); - free(pH); - return LDAP_OPERATIONS_ERROR; - } - - ret = slapi_pw_find_sv(pH, pw); - - for (j = 0; pH[j]; j++) { - slapi_value_free(&pH[j]); - } - slapi_value_free(&pw); - free(pH); - - if (ret == 0) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_checkPassword: 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) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_checkPassword: Password too short (%d < %d)\n", pwdCharLen, krbPwdMinLength); - 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, char **attrlist) -{ - 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, attrlist, 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_sdn_free( &sdn ); - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "<= ipapwd_getEntry: %d\n", search_result); - return search_result; -} - - -/* Construct Mods pblock and perform the modify operation - * Sets result of operation in SLAPI_PLUGIN_INTOP_RESULT - */ -static int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods) -{ - Slapi_PBlock *pb; - int ret; - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_apply_mods\n"); - - if (!mods || (slapi_mods_get_num_mods(mods) == 0)) { - return -1; - } - - 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 (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); - - return ret; -} - -/* ascii hex output of bytes in "in" - * out len is 32 (preallocated) - * in len is 16 */ -static const char hexchars[] = "0123456789ABCDEF"; -static void hexbuf(char *out, const uint8_t *in) -{ - int i; - - for (i = 0; i < 16; i++) { - out[i*2] = hexchars[in[i] >> 4]; - out[i*2+1] = hexchars[in[i] & 0x0f]; - } -} - -/* Modify the Password attributes of the entry */ -static int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg, - struct ipapwd_data *data, - int is_krb) -{ - int ret = 0; - Slapi_Mods *smods; - Slapi_Value **svals = NULL; - Slapi_Value **pwvals = NULL; - struct tm utctime; - char timestr[GENERALIZED_TIME_LENGTH+1]; - krb5_context krbctx; - krb5_error_code krberr; - char lm[33], nt[33]; - struct ntlm_keys ntlm; - int ntlm_flags = 0; - Slapi_Value *sambaSamAccount; - char *errMesg = NULL; - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_SetPassword\n"); - - smods = slapi_mods_new(); - - if (is_krb) { - /* generate kerberos keys to be put into krbPrincipalKey */ - svals = encrypt_encode_key(krbcfg, data, &errMesg); - if (!svals) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "key encryption/encoding failed\n"); - ret = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } - - slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "krbPrincipalKey", svals); - - /* change Last Password Change field with the current date */ - if (!gmtime_r(&(data->timeNow), &utctime)) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to retrieve current date (buggy gmtime_r ?)\n"); - ret = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } - strftime(timestr, GENERALIZED_TIME_LENGTH+1, "%Y%m%d%H%M%SZ", &utctime); - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbLastPwdChange", timestr); - - /* set Password Expiration date */ - if (!gmtime_r(&(data->expireTime), &utctime)) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to convert expiration date\n"); - ret = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } - strftime(timestr, GENERALIZED_TIME_LENGTH+1, "%Y%m%d%H%M%SZ", &utctime); - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbPasswordExpiration", timestr); - } - - sambaSamAccount = slapi_value_new_string("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) { - char *password = strdup(data->password); - if (encode_ntlm_keys(password, ntlm_flags, &ntlm) != 0) { - free(password); - ret = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } - if (ntlm_flags & KTF_LM_HASH) { - hexbuf(lm, ntlm.lm); - lm[32] = '\0'; - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "sambaLMPassword", lm); - } - if (ntlm_flags & KTF_NT_HASH) { - hexbuf(nt, ntlm.nt); - nt[32] = '\0'; - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "sambaNTPassword", nt); - } - free(password); - } - - /* 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 */ - 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); - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipapwd_SetPassword: %d\n", ret); - -free_and_return: - slapi_mods_free(&smods); - ipapwd_free_slapi_value_array(&svals); - ipapwd_free_slapi_value_array(&pwvals); - - return ret; -} static int ipapwd_chpwop(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) { @@ -2708,361 +983,6 @@ free_and_return: return SLAPI_PLUGIN_EXTENDED_SENT_RESULT; } -static int new_ipapwd_encsalt(krb5_context krbctx, const char * const *encsalts, - struct ipapwd_encsalt **es_types, int *num_es_types) -{ - struct ipapwd_encsalt *es; - int nes, i; - - for (i = 0; encsalts[i]; i++) /* count */ ; - es = calloc(i + 1, sizeof(struct ipapwd_encsalt)); - if (!es) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_start", "Out of memory!\n"); - return LDAP_OPERATIONS_ERROR; - } - - for (i = 0, nes = 0; encsalts[i]; i++) { - char *enc, *salt; - krb5_int32 tmpsalt; - krb5_enctype tmpenc; - krb5_boolean similar; - krb5_error_code krberr; - int j; - - enc = strdup(encsalts[i]); - if (!enc) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_start", - "Allocation error\n"); - return LDAP_OPERATIONS_ERROR; - } - salt = strchr(enc, ':'); - if (!salt) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_start", - "Invalid krb5 enc string\n"); - free(enc); - continue; - } - *salt = '\0'; /* null terminate the enc type */ - salt++; /* skip : */ - - krberr = krb5_string_to_enctype(enc, &tmpenc); - if (krberr) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_start", - "Invalid krb5 enctype\n"); - free(enc); - continue; - } - - krberr = krb5_string_to_salttype(salt, &tmpsalt); - for (j = 0; j < nes; j++) { - krb5_c_enctype_compare(krbctx, es[j].enc_type, tmpenc, &similar); - if (similar && (es[j].salt_type == tmpsalt)) { - break; - } - } - - if (j == nes) { - /* not found */ - es[j].enc_type = tmpenc; - es[j].salt_type = tmpsalt; - nes++; - } - - free(enc); - } - - *es_types = es; - *num_es_types = nes; - - return LDAP_SUCCESS; -} - -static struct ipapwd_krbcfg *ipapwd_getConfig(void) -{ - krb5_error_code krberr; - struct ipapwd_krbcfg *config = NULL; - krb5_keyblock *kmkey = NULL; - Slapi_Entry *realm_entry = NULL; - Slapi_Entry *config_entry = NULL; - Slapi_Attr *a; - Slapi_Value *v; - BerElement *be = NULL; - ber_tag_t tag, tmp; - ber_int_t ttype; - const struct berval *bval; - struct berval *mkey = NULL; - char **encsalts; - char *tmpstr; - int i, ret; - - config = calloc(1, sizeof(struct ipapwd_krbcfg)); - if (!config) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Out of memory!\n"); - goto free_and_error; - } - kmkey = calloc(1, sizeof(krb5_keyblock)); - if (!kmkey) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Out of memory!\n"); - goto free_and_error; - } - config->kmkey = kmkey; - - krberr = krb5_init_context(&config->krbctx); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "krb5_init_context failed\n"); - goto free_and_error; - } - - ret = krb5_get_default_realm(config->krbctx, &config->realm); - if (ret) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Failed to get default realm?!\n"); - goto free_and_error; - } - - /* get the Realm Container entry */ - ret = ipapwd_getEntry(ipa_realm_dn, &realm_entry, NULL); - if (ret != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "No realm Entry?\n"); - goto free_and_error; - } - - /*** get the Kerberos Master Key ***/ - - ret = slapi_entry_attr_find(realm_entry, "krbMKey", &a); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "No master key??\n"); - goto free_and_error; - } - - /* there should be only one value here */ - ret = slapi_attr_first_value(a, &v); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "No master key??\n"); - goto free_and_error; - } - - bval = slapi_value_get_berval(v); - if (!bval) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Error retrieving master key berval\n"); - goto free_and_error; - } - - be = ber_init(bval); - if (!bval) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "ber_init() failed!\n"); - goto free_and_error; - } - - tag = ber_scanf(be, "{i{iO}}", &tmp, &ttype, &mkey); - if (tag == LBER_ERROR) { - slapi_log_error(SLAPI_LOG_TRACE, "ipapwd_getConfig", - "Bad Master key encoding ?!\n"); - goto free_and_error; - } - - kmkey->magic = KV5M_KEYBLOCK; - kmkey->enctype = ttype; - kmkey->length = mkey->bv_len; - kmkey->contents = malloc(mkey->bv_len); - if (!kmkey->contents) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Out of memory!\n"); - goto free_and_error; - } - memcpy(kmkey->contents, mkey->bv_val, mkey->bv_len); - ber_bvfree(mkey); - ber_free(be, 1); - mkey = NULL; - be = NULL; - - /*** get the Supported Enc/Salt types ***/ - - encsalts = slapi_entry_attr_get_charray(realm_entry, "krbSupportedEncSaltTypes"); - if (encsalts) { - ret = new_ipapwd_encsalt(config->krbctx, - (const char * const *)encsalts, - &config->supp_encsalts, - &config->num_supp_encsalts); - slapi_ch_array_free(encsalts); - } else { - slapi_log_error(SLAPI_LOG_TRACE, "ipapwd_getConfig", - "No configured salt types use defaults\n"); - ret = new_ipapwd_encsalt(config->krbctx, - ipapwd_def_encsalts, - &config->supp_encsalts, - &config->num_supp_encsalts); - } - if (ret) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Can't get Supported EncSalt Types\n"); - goto free_and_error; - } - - /*** get the Preferred Enc/Salt types ***/ - - encsalts = slapi_entry_attr_get_charray(realm_entry, "krbDefaultEncSaltTypes"); - if (encsalts) { - ret = new_ipapwd_encsalt(config->krbctx, - (const char * const *)encsalts, - &config->pref_encsalts, - &config->num_pref_encsalts); - slapi_ch_array_free(encsalts); - } else { - slapi_log_error(SLAPI_LOG_TRACE, "ipapwd_getConfig", - "No configured salt types use defaults\n"); - ret = new_ipapwd_encsalt(config->krbctx, - ipapwd_def_encsalts, - &config->pref_encsalts, - &config->num_pref_encsalts); - } - if (ret) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Can't get Preferred EncSalt Types\n"); - goto free_and_error; - } - - slapi_entry_free(realm_entry); - - /* get the Realm Container entry */ - ret = ipapwd_getEntry(ipa_pwd_config_dn, &config_entry, NULL); - if (ret != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "No config Entry? Impossible!\n"); - goto free_and_error; - } - config->passsync_mgrs = slapi_entry_attr_get_charray(config_entry, "passSyncManagersDNs"); - /* now add Directory Manager, it is always added by default */ - tmpstr = slapi_ch_strdup("cn=Directory Manager"); - slapi_ch_array_add(&config->passsync_mgrs, tmpstr); - if (config->passsync_mgrs == NULL) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Out of memory!\n"); - goto free_and_error; - } - for (i = 0; config->passsync_mgrs[i]; i++) /* count */ ; - config->num_passsync_mgrs = i; - - return config; - -free_and_error: - if (mkey) ber_bvfree(mkey); - if (be) ber_free(be, 1); - if (kmkey) { - free(kmkey->contents); - free(kmkey); - } - if (config) { - if (config->krbctx) { - if (config->realm) - krb5_free_default_realm(config->krbctx, config->realm); - krb5_free_context(config->krbctx); - } - free(config->pref_encsalts); - free(config->supp_encsalts); - slapi_ch_array_free(config->passsync_mgrs); - free(config); - } - slapi_entry_free(config_entry); - slapi_entry_free(realm_entry); - return NULL; -} - -#define IPAPWD_CHECK_CONN_SECURE 0x00000001 -#define IPAPWD_CHECK_DN 0x00000002 - -static int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg, - struct ipapwd_krbcfg **config, - int check_flags) -{ - int ret, sasl_ssf, is_ssl; - int rc = LDAP_SUCCESS; - Slapi_Backend *be; - const Slapi_DN *psdn; - Slapi_DN *sdn; - char *dn = NULL; - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_gen_checks\n"); - -#ifdef LDAP_EXTOP_PASSMOD_CONN_SECURE - if (check_flags & IPAPWD_CHECK_CONN_SECURE) { - /* Allow password modify only for SSL/TLS established connections and - * connections using SASL privacy layers */ - if (slapi_pblock_get(pb, SLAPI_CONN_SASL_SSF, &sasl_ssf) != 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "Could not get SASL SSF from connection\n"); - *errMesg = "Operation requires a secure connection.\n"; - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - if (slapi_pblock_get(pb, SLAPI_CONN_IS_SSL_SESSION, &is_ssl) != 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "Could not get IS SSL from connection\n"); - *errMesg = "Operation requires a secure connection.\n"; - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - if ((0 == is_ssl) && (sasl_ssf <= 1)) { - *errMesg = "Operation requires a secure connection.\n"; - rc = LDAP_CONFIDENTIALITY_REQUIRED; - goto done; - } - } -#endif - - if (check_flags & IPAPWD_CHECK_DN) { - /* check we have a valid DN in the pblock or just abort */ - ret = slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn); - if (ret) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "Tried to change password for an invalid DN [%s]\n", - dn?dn:"<NULL>"); - *errMesg = "Invalid DN"; - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - sdn = slapi_sdn_new_dn_byref(dn); - if (!sdn) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "Unable to convert dn to sdn %s", dn?dn:"<NULL>"); - *errMesg = "Internal Error"; - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - be = slapi_be_select(sdn); - slapi_sdn_free(&sdn); - - psdn = slapi_be_getsuffix(be, 0); - if (!psdn) { - *errMesg = "Invalid DN"; - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - } - - /* get the kerberos context and master key */ - *config = ipapwd_getConfig(); - if (NULL == *config) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "Error Retrieving Master Key"); - *errMesg = "Fatal Internal Error"; - rc = LDAP_OPERATIONS_ERROR; - } - -done: - return rc; -} - static int ipapwd_extop(Slapi_PBlock *pb) { struct ipapwd_krbcfg *krbcfg = NULL; @@ -3114,1089 +1034,6 @@ free_and_return: return SLAPI_PLUGIN_EXTENDED_SENT_RESULT; } -/***************************************************************************** - * pre/post operations to intercept writes to userPassword - ****************************************************************************/ - -#define IPAPWD_OP_NULL 0 -#define IPAPWD_OP_ADD 1 -#define IPAPWD_OP_MOD 2 -struct ipapwd_operation { - struct ipapwd_data pwdata; - int pwd_op; - int is_krb; -}; - -/* structure with information for each extension */ -struct ipapwd_op_ext { - char *object_name; /* name of the object extended */ - int object_type; /* handle to the extended object */ - int handle; /* extension handle */ -}; - -static struct ipapwd_op_ext ipapwd_op_ext_list; - -static void *ipapwd_op_ext_constructor(void *object, void *parent) -{ - struct ipapwd_operation *ext; - - ext = (struct ipapwd_operation *)slapi_ch_calloc(1, sizeof(struct ipapwd_operation)); - return ext; -} - -static void ipapwd_op_ext_destructor(void *ext, void *object, void *parent) -{ - struct ipapwd_operation *pwdop = (struct ipapwd_operation *)ext; - if (!pwdop) - return; - if (pwdop->pwd_op != IPAPWD_OP_NULL) { - slapi_ch_free_string(&(pwdop->pwdata.dn)); - slapi_ch_free_string(&(pwdop->pwdata.password)); - } - slapi_ch_free((void **)&pwdop); -} - -static int ipapwd_entry_checks(Slapi_PBlock *pb, struct slapi_entry *e, - int *is_root, int *is_krb, int *is_smb, - char *attr, int access) -{ - Slapi_Value *sval; - int rc; - - /* Check ACIs */ - slapi_pblock_get(pb, SLAPI_REQUESTOR_ISROOT, is_root); - - if (!*is_root) { - /* verify this user is allowed to write a user password */ - rc = slapi_access_allowed(pb, e, attr, NULL, access); - if (rc != LDAP_SUCCESS) { - /* we have no business here, the operation will be denied anyway */ - rc = LDAP_SUCCESS; - goto done; - } - } - - /* Check if this is a krbPrincial and therefore needs us to generate other - * hashes */ - sval = slapi_value_new_string("krbPrincipalAux"); - if (!sval) { - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - *is_krb = slapi_entry_attr_has_syntax_value(e, SLAPI_ATTR_OBJECTCLASS, sval); - slapi_value_free(&sval); - - sval = slapi_value_new_string("sambaSamAccount"); - if (!sval) { - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - *is_smb = slapi_entry_attr_has_syntax_value(e, SLAPI_ATTR_OBJECTCLASS, sval); - slapi_value_free(&sval); - - rc = LDAP_SUCCESS; - -done: - return rc; -} - -static int ipapwd_preop_gen_hashes(struct ipapwd_krbcfg *krbcfg, - struct ipapwd_operation *pwdop, - char *userpw, - int is_krb, int is_smb, - Slapi_Value ***svals, - char **nthash, char **lmhash, - char **errMesg) -{ - int rc; - - if (is_krb) { - - pwdop->is_krb = 1; - - *svals = encrypt_encode_key(krbcfg, &pwdop->pwdata, errMesg); - - if (!*svals) { - /* errMesg should have been set in encrypt_encode_key() */ - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "key encryption/encoding failed\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - } - - if (is_smb) { - char lm[33], nt[33]; - struct ntlm_keys ntlm; - int ntlm_flags = 0; - int ret; - - /* TODO: retrieve if we want to store the LM hash or not */ - ntlm_flags = KTF_LM_HASH | KTF_NT_HASH; - - ret = encode_ntlm_keys(userpw, ntlm_flags, &ntlm); - if (ret) { - *errMesg = "Failed to generate NT/LM hashes\n"; - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - *errMesg); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - if (ntlm_flags & KTF_LM_HASH) { - hexbuf(lm, ntlm.lm); - lm[32] = '\0'; - *lmhash = slapi_ch_strdup(lm); - } - if (ntlm_flags & KTF_NT_HASH) { - hexbuf(nt, ntlm.nt); - nt[32] = '\0'; - *nthash = slapi_ch_strdup(nt); - } - } - - rc = LDAP_SUCCESS; - -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 */ - Slapi_Value *objectclass; - 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; - } - - /* we aren't interested in host principals */ - objectclass = slapi_value_new_string("ipaHost"); - if ((slapi_entry_attr_has_syntax_value(entry, SLAPI_ATTR_OBJECTCLASS, objectclass)) == 1) { - slapi_value_free(&objectclass); - goto done; - } - slapi_value_free(&objectclass); - - /* 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, 1); - 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 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. - * Check if krb or smb hashes are required by testing if the krb or smb - * objectclasses are present. - * store information for the post operation - */ -static int ipapwd_pre_add(Slapi_PBlock *pb) -{ - struct ipapwd_krbcfg *krbcfg = NULL; - char *errMesg = "Internal operations error\n"; - struct slapi_entry *e = NULL; - char *userpw = NULL; - char *dn = NULL; - struct ipapwd_operation *pwdop = NULL; - void *op; - int is_repl_op, is_root, is_krb, is_smb; - int ret; - int rc = LDAP_SUCCESS; - - slapi_log_error(SLAPI_LOG_TRACE, IPAPWD_PLUGIN_NAME, "=> ipapwd_pre_add\n"); - - ret = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl_op); - if (ret != 0) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "slapi_pblock_get failed!?\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - /* pass through if this is a replicated operation */ - if (is_repl_op) - return 0; - - /* retrieve the entry */ - slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e); - if (NULL == e) - return 0; - - /* check this is something interesting for us first */ - userpw = slapi_entry_attr_get_charptr(e, SLAPI_USERPWD_ATTR); - if (!userpw) { - /* nothing interesting here */ - return 0; - } - - /* Ok this is interesting, - * Check this is a clear text password, or refuse operation */ - if ('{' == userpw[0]) { - if (0 == strncasecmp(userpw, "{CLEAR}", strlen("{CLEAR}"))) { - char *tmp = slapi_ch_strdup(&userpw[strlen("{CLEAR}")]); - if (NULL == tmp) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "Strdup failed, Out of memory\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - slapi_ch_free_string(&userpw); - userpw = tmp; - } else if (slapi_is_encoded(userpw)) { - /* check if we have access to the unhashed user password */ - char *userpw_clear = - slapi_entry_attr_get_charptr(e, "unhashed#user#password"); - - /* 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 */ - if (!userpw_clear || (0 == strcmp(userpw, userpw_clear))) { - rc = LDAP_CONSTRAINT_VIOLATION; - slapi_ch_free_string(&userpw); - } else { - userpw = slapi_ch_strdup(userpw_clear); - } - - slapi_ch_free_string(&userpw_clear); - - if (rc != LDAP_SUCCESS) { - /* 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; - } - } - } - - rc = ipapwd_entry_checks(pb, e, - &is_root, &is_krb, &is_smb, - NULL, SLAPI_ACL_ADD); - if (rc != LDAP_SUCCESS) { - goto done; - } - - rc = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_DN); - if (rc != LDAP_SUCCESS) { - goto done; - } - - /* Get target DN */ - ret = slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn); - if (ret) { - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - /* time to get the operation handler */ - ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op); - if (ret != 0) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "slapi_pblock_get failed!?\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - pwdop = slapi_get_object_extension(ipapwd_op_ext_list.object_type, - op, ipapwd_op_ext_list.handle); - if (NULL == pwdop) { - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - pwdop->pwd_op = IPAPWD_OP_ADD; - pwdop->pwdata.password = slapi_ch_strdup(userpw); - - if (is_root) { - pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR; - } else { - char *binddn; - int i; - - pwdop->pwdata.changetype = IPA_CHANGETYPE_ADMIN; - - /* Check Bind DN */ - slapi_pblock_get(pb, SLAPI_CONN_DN, &binddn); - - /* if it is a passsync manager we also need to skip resets */ - for (i = 0; i < krbcfg->num_passsync_mgrs; i++) { - if (strcasecmp(krbcfg->passsync_mgrs[i], binddn) == 0) { - pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR; - break; - } - } - } - - pwdop->pwdata.dn = slapi_ch_strdup(dn); - pwdop->pwdata.timeNow = time(NULL); - pwdop->pwdata.target = e; - - ret = ipapwd_CheckPolicy(&pwdop->pwdata); - if (ret) { - errMesg = "Password Fails to meet minimum strength criteria"; - rc = LDAP_CONSTRAINT_VIOLATION; - goto done; - } - - if (is_krb || is_smb) { - - Slapi_Value **svals = NULL; - char *nt = NULL; - char *lm = NULL; - - rc = ipapwd_preop_gen_hashes(krbcfg, - pwdop, userpw, - is_krb, is_smb, - &svals, &nt, &lm, &errMesg); - if (rc != LDAP_SUCCESS) { - goto done; - } - - if (svals) { - /* add/replace values in existing entry */ - ret = slapi_entry_attr_replace_sv(e, "krbPrincipalKey", svals); - if (ret) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "failed to set encoded values in entry\n"); - rc = LDAP_OPERATIONS_ERROR; - ipapwd_free_slapi_value_array(&svals); - goto done; - } - - ipapwd_free_slapi_value_array(&svals); - } - - if (lm) { - /* set value */ - slapi_entry_attr_set_charptr(e, "sambaLMPassword", lm); - slapi_ch_free_string(&lm); - } - if (nt) { - /* set value */ - slapi_entry_attr_set_charptr(e, "sambaNTPassword", nt); - slapi_ch_free_string(&nt); - } - } - - rc = LDAP_SUCCESS; - -done: - if (pwdop) pwdop->pwdata.target = NULL; - free_ipapwd_krbcfg(&krbcfg); - slapi_ch_free_string(&userpw); - if (rc != LDAP_SUCCESS) { - slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL); - return -1; - } - return 0; -} - -/* PRE MOD Operation: - * Gets the clean text password (fail the operation if the password came - * pre-hashed, unless this is a replicated operation). - * Check user is authorized to add it otherwise just returns, operation will - * fail later anyway. - * Check if krb or smb hashes are required by testing if the krb or smb - * objectclasses are present. - * Run a password policy check. - * store information for the post operation - */ -static int ipapwd_pre_mod(Slapi_PBlock *pb) -{ - struct ipapwd_krbcfg *krbcfg = NULL; - char *errMesg = NULL; - LDAPMod **mods; - Slapi_Mod *smod, *tmod; - Slapi_Mods *smods = NULL; - char *userpw = NULL; - char *unhashedpw = NULL; - char *dn = NULL; - Slapi_DN *tmp_dn; - 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; - int ret, rc; - - slapi_log_error(SLAPI_LOG_TRACE, IPAPWD_PLUGIN_NAME, "=> ipapwd_pre_mod\n"); - - ret = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl_op); - if (ret != 0) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "slapi_pblock_get failed!?\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - /* pass through if this is a replicated operation */ - if (is_repl_op) { - rc = LDAP_SUCCESS; - goto done; - } - - /* grab the mods - we'll put them back later with - * our modifications appended - */ - slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); - smods = slapi_mods_new(); - slapi_mods_init_passin(smods, mods); - - /* In the first pass, - * only check there is anything we are interested in */ - is_pwd_op = 0; - tmod = slapi_mod_new(); - smod = slapi_mods_get_first_smod(smods, tmod); - while (smod) { - struct berval *bv; - const char *type; - int mop; - - type = slapi_mod_get_type(smod); - if (slapi_attr_types_equivalent(type, SLAPI_USERPWD_ATTR)) { - mop = slapi_mod_get_operation(smod); - /* check op filtering out LDAP_MOD_BVALUES */ - switch (mop & 0x0f) { - case LDAP_MOD_ADD: - case LDAP_MOD_REPLACE: - is_pwd_op = 1; - default: - break; - } - } - - /* we check for unahsehd password here so that we are sure to catch them - * early, before further checks go on, this helps checking - * LDAP_MOD_DELETE operations in some corner cases later */ - /* we keep only the last one if multiple are provided for any absurd - * reason */ - if (slapi_attr_types_equivalent(type, "unhashed#user#password")) { - bv = slapi_mod_get_first_value(smod); - if (!bv) { - slapi_mod_free(&tmod); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - slapi_ch_free_string(&unhashedpw); - unhashedpw = slapi_ch_malloc(bv->bv_len+1); - if (!unhashedpw) { - slapi_mod_free(&tmod); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - memcpy(unhashedpw, bv->bv_val, bv->bv_len); - unhashedpw[bv->bv_len] = '\0'; - } - slapi_mod_done(tmod); - smod = slapi_mods_get_next_smod(smods, tmod); - } - slapi_mod_free(&tmod); - - /* If userPassword is not modified we are done here */ - if (! is_pwd_op) { - rc = LDAP_SUCCESS; - goto done; - } - - /* OK swe have something interesting here, start checking for - * pre-requisites */ - - /* Get target DN */ - ret = slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn); - if (ret) { - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - tmp_dn = slapi_sdn_new_dn_byref(dn); - if (tmp_dn) { - /* xxxPAR: Ideally SLAPI_MODIFY_EXISTING_ENTRY should be - * available but it turns out that is only true if you are - * a dbm backend pre-op plugin - lucky dbm backend pre-op - * plugins. - * I think that is wrong since the entry is useful for filter - * tests and schema checks and this plugin shouldn't be limited - * to a single backend type, but I don't want that fight right - * now so we go get the entry here - * - slapi_pblock_get( pb, SLAPI_MODIFY_EXISTING_ENTRY, &e); - */ - ret = slapi_search_internal_get_entry(tmp_dn, 0, &e, ipapwd_plugin_id); - slapi_sdn_free(&tmp_dn); - if (ret != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Failed tpo retrieve entry?!?\n"); - rc = LDAP_NO_SUCH_OBJECT; - goto done; - } - } - - rc = ipapwd_entry_checks(pb, e, - &is_root, &is_krb, &is_smb, - SLAPI_USERPWD_ATTR, SLAPI_ACL_WRITE); - if (rc) { - goto done; - } - - rc = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_DN); - if (rc) { - goto done; - } - - /* run through the mods again and adjust flags if operations affect them */ - tmod = slapi_mod_new(); - smod = slapi_mods_get_first_smod(smods, tmod); - while (smod) { - struct berval *bv; - const char *type; - int mop; - - type = slapi_mod_get_type(smod); - if (slapi_attr_types_equivalent(type, SLAPI_USERPWD_ATTR)) { - mop = slapi_mod_get_operation(smod); - /* check op filtering out LDAP_MOD_BVALUES */ - switch (mop & 0x0f) { - case LDAP_MOD_ADD: - /* FIXME: should we try to track cases where we would end up - * with multiple userPassword entries ?? */ - case LDAP_MOD_REPLACE: - is_pwd_op = 1; - bv = slapi_mod_get_first_value(smod); - if (!bv) { - slapi_mod_free(&tmod); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - slapi_ch_free_string(&userpw); - userpw = slapi_ch_malloc(bv->bv_len+1); - if (!userpw) { - slapi_mod_free(&tmod); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - memcpy(userpw, bv->bv_val, bv->bv_len); - userpw[bv->bv_len] = '\0'; - break; - case LDAP_MOD_DELETE: - /* reset only if we are deleting all values, or the exact - * same value previously set, otherwise we are just trying to - * add a new value and delete an existing one */ - bv = slapi_mod_get_first_value(smod); - if (!bv) { - is_pwd_op = 0; - } else { - if (0 == strncmp(userpw, bv->bv_val, bv->bv_len) || - 0 == strncmp(unhashedpw, bv->bv_val, bv->bv_len)) - is_pwd_op = 0; - } - default: - break; - } - } - - if (slapi_attr_types_equivalent(type, SLAPI_ATTR_OBJECTCLASS)) { - mop = slapi_mod_get_operation(smod); - /* check op filtering out LDAP_MOD_BVALUES */ - switch (mop & 0x0f) { - case LDAP_MOD_REPLACE: - /* if objectclasses are replaced we need to start clean with - * flags, so we sero them out and see if they get set again */ - is_krb = 0; - is_smb = 0; - - case LDAP_MOD_ADD: - bv = slapi_mod_get_first_value(smod); - if (!bv) { - slapi_mod_free(&tmod); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - do { - if (0 == strncasecmp("krbPrincipalAux", bv->bv_val, bv->bv_len)) - is_krb = 1; - if (0 == strncasecmp("sambaSamAccount", bv->bv_val, bv->bv_len)) - is_smb = 1; - } while ((bv = slapi_mod_get_next_value(smod)) != NULL); - - break; - - case LDAP_MOD_DELETE: - /* can this happen for objectclasses ? */ - is_krb = 0; - is_smb = 0; - - default: - break; - } - } - - slapi_mod_done(tmod); - smod = slapi_mods_get_next_smod(smods, tmod); - } - slapi_mod_free(&tmod); - - /* It seem like we have determined that the end result will be deletion of - * the userPassword attribute, so we have no more business here */ - if (! is_pwd_op) { - rc = LDAP_SUCCESS; - goto done; - } - - /* Check this is a clear text password, or refuse operation (only if we need - * to comput other hashes */ - if (! unhashedpw) { - if ('{' == userpw[0]) { - if (0 == strncasecmp(userpw, "{CLEAR}", strlen("{CLEAR}"))) { - unhashedpw = slapi_ch_strdup(&userpw[strlen("{CLEAR}")]); - if (NULL == unhashedpw) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "Strdup failed, Out of memory\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - slapi_ch_free_string(&userpw); - - } else if (slapi_is_encoded(userpw)) { - - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Pre-Encoded passwords are not valid\n"); - errMesg = "Pre-Encoded passwords are not valid\n"; - rc = LDAP_CONSTRAINT_VIOLATION; - goto done; - } - } - } - - /* time to get the operation handler */ - ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op); - if (ret != 0) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "slapi_pblock_get failed!?\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - pwdop = slapi_get_object_extension(ipapwd_op_ext_list.object_type, - op, ipapwd_op_ext_list.handle); - if (NULL == pwdop) { - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - pwdop->pwd_op = IPAPWD_OP_MOD; - pwdop->pwdata.password = slapi_ch_strdup(unhashedpw); - pwdop->pwdata.changetype = IPA_CHANGETYPE_NORMAL; - - if (is_root) { - pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR; - } else { - char *binddn; - Slapi_DN *bdn, *tdn; - int i; - - /* Check Bind DN */ - slapi_pblock_get(pb, SLAPI_CONN_DN, &binddn); - bdn = slapi_sdn_new_dn_byref(binddn); - tdn = slapi_sdn_new_dn_byref(dn); - - /* if the change is performed by someone else, - * it is an admin change that will require a new - * password change immediately as per our IPA policy */ - if (slapi_sdn_compare(bdn, tdn)) { - pwdop->pwdata.changetype = IPA_CHANGETYPE_ADMIN; - - /* if it is a passsync manager we also need to skip resets */ - for (i = 0; i < krbcfg->num_passsync_mgrs; i++) { - if (strcasecmp(krbcfg->passsync_mgrs[i], binddn) == 0) { - pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR; - break; - } - } - - } - - slapi_sdn_free(&bdn); - slapi_sdn_free(&tdn); - - } - - pwdop->pwdata.dn = slapi_ch_strdup(dn); - pwdop->pwdata.timeNow = time(NULL); - pwdop->pwdata.target = e; - - ret = ipapwd_CheckPolicy(&pwdop->pwdata); - if (ret) { - errMesg = "Password Fails to meet minimum strength criteria"; - rc = LDAP_CONSTRAINT_VIOLATION; - goto done; - } - - if (is_krb || is_smb) { - - Slapi_Value **svals = NULL; - char *nt = NULL; - char *lm = NULL; - - rc = ipapwd_preop_gen_hashes(krbcfg, - pwdop, unhashedpw, - is_krb, is_smb, - &svals, &nt, &lm, &errMesg); - if (rc) { - goto done; - } - - if (svals) { - /* replace values */ - slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, - "krbPrincipalKey", svals); - ipapwd_free_slapi_value_array(&svals); - } - - if (lm) { - /* replace value */ - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, - "sambaLMPassword", lm); - slapi_ch_free_string(&lm); - } - if (nt) { - /* replace value */ - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, - "sambaNTPassword", nt); - slapi_ch_free_string(&nt); - } - } - - rc = LDAP_SUCCESS; - -done: - free_ipapwd_krbcfg(&krbcfg); - slapi_ch_free_string(&userpw); /* just to be sure */ - slapi_ch_free_string(&unhashedpw); /* we copied it to pwdop */ - if (e) slapi_entry_free(e); /* this is a copy in this function */ - if (pwdop) pwdop->pwdata.target = NULL; - - /* put back a, possibly modified, set of mods */ - if (smods) { - mods = slapi_mods_get_ldapmods_passout(smods); - slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods); - slapi_mods_free(&smods); - } - - if (rc != LDAP_SUCCESS) { - slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL); - return -1; - } - - return 0; -} - -static int ipapwd_post_op(Slapi_PBlock *pb) -{ - void *op; - struct ipapwd_operation *pwdop = NULL; - Slapi_Mods *smods; - Slapi_Value **pwvals; - struct tm utctime; - char timestr[GENERALIZED_TIME_LENGTH+1]; - int ret; - - slapi_log_error(SLAPI_LOG_TRACE, IPAPWD_PLUGIN_NAME, - "=> ipapwd_post_op\n"); - - /* time to get the operation handler */ - ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op); - if (ret != 0) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "slapi_pblock_get failed!?\n"); - return 0; - } - - pwdop = slapi_get_object_extension(ipapwd_op_ext_list.object_type, - op, ipapwd_op_ext_list.handle); - if (NULL == pwdop) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Internal error, couldn't find pluginextension ?!\n"); - return 0; - } - - /* not interesting */ - if (IPAPWD_OP_NULL == pwdop->pwd_op) - return 0; - - if ( ! (pwdop->is_krb)) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Not a kerberos user, ignore krb attributes\n"); - return 0; - } - - /* prepare changes that can be made only as root */ - smods = slapi_mods_new(); - - /* change Last Password Change field with the current date */ - if (!gmtime_r(&(pwdop->pwdata.timeNow), &utctime)) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "failed to parse current date (buggy gmtime_r ?)\n"); - goto done; - } - strftime(timestr, GENERALIZED_TIME_LENGTH+1, - "%Y%m%d%H%M%SZ", &utctime); - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, - "krbLastPwdChange", timestr); - - /* set Password Expiration date */ - if (!gmtime_r(&(pwdop->pwdata.expireTime), &utctime)) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "failed to parse expiration date (buggy gmtime_r ?)\n"); - goto done; - } - strftime(timestr, GENERALIZED_TIME_LENGTH+1, - "%Y%m%d%H%M%SZ", &utctime); - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, - "krbPasswordExpiration", timestr); - - /* This was a mod operation on an existing entry, make sure we also update - * the password history based on the entry we saved from the pre-op */ - if (IPAPWD_OP_MOD == pwdop->pwd_op) { - Slapi_DN *tmp_dn = slapi_sdn_new_dn_byref(pwdop->pwdata.dn); - if (tmp_dn) { - ret = slapi_search_internal_get_entry(tmp_dn, 0, - &pwdop->pwdata.target, - ipapwd_plugin_id); - slapi_sdn_free(&tmp_dn); - if (ret != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Failed tpo retrieve entry?!?\n"); - goto done; - } - } - pwvals = ipapwd_setPasswordHistory(smods, &pwdop->pwdata); - if (pwvals) { - slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, - "passwordHistory", pwvals); - } - } - - ret = ipapwd_apply_mods(pwdop->pwdata.dn, smods); - if (ret) - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Failed to set additional password attributes in the post-op!\n"); - -done: - if (pwdop && pwdop->pwdata.target) slapi_entry_free(pwdop->pwdata.target); - slapi_mods_free(&smods); - return 0; -} - /* Copied from ipamo_string2filter() * * ipapwd_string2filter() @@ -4290,22 +1127,6 @@ done: } -static int ipapwd_ext_init() -{ - int ret; - - ipapwd_op_ext_list.object_name = SLAPI_EXT_OPERATION; - - ret = slapi_register_object_extension(IPAPWD_PLUGIN_NAME, - SLAPI_EXT_OPERATION, - ipapwd_op_ext_constructor, - ipapwd_op_ext_destructor, - &ipapwd_op_ext_list.object_type, - &ipapwd_op_ext_list.handle); - - return ret; -} - static char *ipapwd_oid_list[] = { EXOP_PASSWD_OID, @@ -4320,33 +1141,6 @@ static char *ipapwd_name_list[] = { NULL }; -/* Init pre ops */ -static int ipapwd_pre_init(Slapi_PBlock *pb) -{ - int ret; - - 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); - - return ret; -} - -/* Init post ops */ -static int ipapwd_post_init(Slapi_PBlock *pb) -{ - int ret; - - 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_POST_ADD_FN, (void *)ipapwd_post_op); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *)ipapwd_post_op); - - return ret; -} - /* Initialization function */ int ipapwd_init( Slapi_PBlock *pb ) { @@ -4375,7 +1169,7 @@ int ipapwd_init( Slapi_PBlock *pb ) * plug-in */ ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, (void *)ipapwd_start); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&pdesc); + if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&ipapwd_plugin_desc); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, ipapwd_oid_list); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, ipapwd_name_list); if (!ret) slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_FN, (void *)ipapwd_extop); |