12216fc83f
The OTP patches add basic support for TOTP and Radius. The 389-ds patch sets KRB5CCNAME in /etc/sysconfig/dirsrv so it can get a usable ccache.
5604 lines
181 KiB
Diff
5604 lines
181 KiB
Diff
From fe0b5a2cf772c3f85ca2c030b5be2dd0cd9c041b Mon Sep 17 00:00:00 2001
|
|
From: Nathaniel McCallum <npmccallum@redhat.com>
|
|
Date: Thu, 9 May 2013 14:43:17 -0400
|
|
Subject: [PATCH 5/6] Remove unnecessary prefixes from ipa-pwd-extop files
|
|
|
|
---
|
|
.../ipa-slapi-plugins/ipa-pwd-extop/Makefile.am | 6 +-
|
|
daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c | 1107 ++++++++++++++++
|
|
daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c | 291 +++++
|
|
daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h | 6 +-
|
|
.../ipa-pwd-extop/ipapwd_common.c | 1107 ----------------
|
|
.../ipa-pwd-extop/ipapwd_encoding.c | 291 -----
|
|
.../ipa-pwd-extop/ipapwd_prepost.c | 1349 --------------------
|
|
daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 1349 ++++++++++++++++++++
|
|
8 files changed, 2753 insertions(+), 2753 deletions(-)
|
|
create mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
|
|
create mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c
|
|
delete mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_common.c
|
|
delete mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_encoding.c
|
|
delete mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c
|
|
create mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
|
|
|
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am b/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
|
|
index ec98f95..90f940f 100644
|
|
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
|
|
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
|
|
@@ -30,9 +30,9 @@ plugin_LTLIBRARIES = \
|
|
$(NULL)
|
|
|
|
libipa_pwd_extop_la_SOURCES = \
|
|
- ipapwd_common.c \
|
|
- ipapwd_encoding.c \
|
|
- ipapwd_prepost.c \
|
|
+ common.c \
|
|
+ encoding.c \
|
|
+ prepost.c \
|
|
ipa_pwd_extop.c \
|
|
$(KRB5_UTIL_SRCS) \
|
|
$(NULL)
|
|
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
|
|
new file mode 100644
|
|
index 0000000..bb1d96a
|
|
--- /dev/null
|
|
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
|
|
@@ -0,0 +1,1107 @@
|
|
+/** BEGIN COPYRIGHT BLOCK
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
+ *
|
|
+ * Additional permission under GPLv3 section 7:
|
|
+ *
|
|
+ * In the following paragraph, "GPL" means the GNU General Public
|
|
+ * License, version 3 or any later version, and "Non-GPL Code" means
|
|
+ * code that is governed neither by the GPL nor a license
|
|
+ * compatible with the GPL.
|
|
+ *
|
|
+ * You may link the code of this Program with Non-GPL Code and convey
|
|
+ * linked combinations including the two, provided that such Non-GPL
|
|
+ * Code only links to the code of this Program through those well
|
|
+ * defined interfaces identified in the file named EXCEPTION found in
|
|
+ * the source code files (the "Approved Interfaces"). The files of
|
|
+ * Non-GPL Code may instantiate templates or use macros or inline
|
|
+ * functions from the Approved Interfaces without causing the resulting
|
|
+ * work to be covered by the GPL. Only the copyright holders of this
|
|
+ * Program may make changes or additions to the list of Approved
|
|
+ * Interfaces.
|
|
+ *
|
|
+ * Authors:
|
|
+ * Simo Sorce <ssorce@redhat.com>
|
|
+ *
|
|
+ * Copyright (C) 2007-2010 Red Hat, Inc.
|
|
+ * All rights reserved.
|
|
+ * END COPYRIGHT BLOCK **/
|
|
+
|
|
+#include "ipapwd.h"
|
|
+#include "util.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 */
|
|
+
|
|
+extern void *ipapwd_plugin_id;
|
|
+extern const char *ipa_realm_dn;
|
|
+extern const char *ipa_etc_config_dn;
|
|
+extern const char *ipa_pwd_config_dn;
|
|
+
|
|
+/* 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 file content into
|
|
+ * the directory */
|
|
+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
|
|
+};
|
|
+
|
|
+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, tvno;
|
|
+ ber_int_t ttype;
|
|
+ const struct berval *bval;
|
|
+ struct berval *mkey = NULL;
|
|
+ char **encsalts;
|
|
+ char **tmparray;
|
|
+ char *tmpstr;
|
|
+ int i, ret;
|
|
+
|
|
+ config = calloc(1, sizeof(struct ipapwd_krbcfg));
|
|
+ if (!config) {
|
|
+ LOG_OOM();
|
|
+ goto free_and_error;
|
|
+ }
|
|
+ kmkey = calloc(1, sizeof(krb5_keyblock));
|
|
+ if (!kmkey) {
|
|
+ LOG_OOM();
|
|
+ goto free_and_error;
|
|
+ }
|
|
+ config->kmkey = kmkey;
|
|
+
|
|
+ krberr = krb5_init_context(&config->krbctx);
|
|
+ if (krberr) {
|
|
+ LOG_FATAL("krb5_init_context failed\n");
|
|
+ goto free_and_error;
|
|
+ }
|
|
+
|
|
+ ret = krb5_get_default_realm(config->krbctx, &config->realm);
|
|
+ if (ret) {
|
|
+ LOG_FATAL("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) {
|
|
+ LOG_FATAL("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) {
|
|
+ LOG_FATAL("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) {
|
|
+ LOG_FATAL("No master key??\n");
|
|
+ goto free_and_error;
|
|
+ }
|
|
+
|
|
+ bval = slapi_value_get_berval(v);
|
|
+ if (!bval) {
|
|
+ LOG_FATAL("Error retrieving master key berval\n");
|
|
+ goto free_and_error;
|
|
+ }
|
|
+
|
|
+ be = ber_init(discard_const(bval));
|
|
+ if (!be) {
|
|
+ LOG_FATAL("ber_init() failed!\n");
|
|
+ goto free_and_error;
|
|
+ }
|
|
+
|
|
+ tag = ber_scanf(be, "{i{iO}}", &tvno, &ttype, &mkey);
|
|
+ if (tag == LBER_ERROR) {
|
|
+ LOG_FATAL("Bad Master key encoding ?!\n");
|
|
+ goto free_and_error;
|
|
+ }
|
|
+
|
|
+ config->mkvno = tvno;
|
|
+ kmkey->magic = KV5M_KEYBLOCK;
|
|
+ kmkey->enctype = ttype;
|
|
+ kmkey->length = mkey->bv_len;
|
|
+ kmkey->contents = malloc(mkey->bv_len);
|
|
+ if (!kmkey->contents) {
|
|
+ LOG_OOM();
|
|
+ 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) {
|
|
+ for (i = 0; encsalts[i]; i++) /* count */ ;
|
|
+ ret = parse_bval_key_salt_tuples(config->krbctx,
|
|
+ (const char * const *)encsalts, i,
|
|
+ &config->supp_encsalts,
|
|
+ &config->num_supp_encsalts);
|
|
+ slapi_ch_array_free(encsalts);
|
|
+ } else {
|
|
+ LOG("No configured salt types use defaults\n");
|
|
+ for (i = 0; ipapwd_def_encsalts[i]; i++) /* count */ ;
|
|
+ ret = parse_bval_key_salt_tuples(config->krbctx,
|
|
+ ipapwd_def_encsalts, i,
|
|
+ &config->supp_encsalts,
|
|
+ &config->num_supp_encsalts);
|
|
+ }
|
|
+ if (ret) {
|
|
+ LOG_FATAL("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) {
|
|
+ for (i = 0; encsalts[i]; i++) /* count */ ;
|
|
+ ret = parse_bval_key_salt_tuples(config->krbctx,
|
|
+ (const char * const *)encsalts, i,
|
|
+ &config->pref_encsalts,
|
|
+ &config->num_pref_encsalts);
|
|
+ slapi_ch_array_free(encsalts);
|
|
+ } else {
|
|
+ LOG("No configured salt types use defaults\n");
|
|
+ for (i = 0; ipapwd_def_encsalts[i]; i++) /* count */ ;
|
|
+ ret = parse_bval_key_salt_tuples(config->krbctx,
|
|
+ ipapwd_def_encsalts, i,
|
|
+ &config->pref_encsalts,
|
|
+ &config->num_pref_encsalts);
|
|
+ }
|
|
+ if (ret) {
|
|
+ LOG_FATAL("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) {
|
|
+ LOG_FATAL("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) {
|
|
+ LOG_OOM();
|
|
+ goto free_and_error;
|
|
+ }
|
|
+ for (i = 0; config->passsync_mgrs[i]; i++) /* count */ ;
|
|
+ config->num_passsync_mgrs = i;
|
|
+
|
|
+ slapi_entry_free(config_entry);
|
|
+
|
|
+ /* get the ipa etc/ipaConfig entry */
|
|
+ config->allow_lm_hash = false;
|
|
+ config->allow_nt_hash = false;
|
|
+ ret = ipapwd_getEntry(ipa_etc_config_dn, &config_entry, NULL);
|
|
+ if (ret != LDAP_SUCCESS) {
|
|
+ LOG_FATAL("No config Entry?\n");
|
|
+ goto free_and_error;
|
|
+ } else {
|
|
+ tmparray = slapi_entry_attr_get_charray(config_entry,
|
|
+ "ipaConfigString");
|
|
+ for (i = 0; tmparray && tmparray[i]; i++) {
|
|
+ if (strcasecmp(tmparray[i], "AllowLMhash") == 0) {
|
|
+ config->allow_lm_hash = true;
|
|
+ continue;
|
|
+ }
|
|
+ if (strcasecmp(tmparray[i], "AllowNThash") == 0) {
|
|
+ config->allow_nt_hash = true;
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ if (tmparray) slapi_ch_array_free(tmparray);
|
|
+ }
|
|
+
|
|
+ slapi_entry_free(config_entry);
|
|
+
|
|
+ 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;
|
|
+}
|
|
+
|
|
+/* 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;
|
|
+
|
|
+ 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);
|
|
+}
|
|
+
|
|
+static int ipapwd_rdn_count(const char *dn)
|
|
+{
|
|
+ int rdnc = 0;
|
|
+ LDAPDN ldn;
|
|
+ int ret;
|
|
+
|
|
+ ret = ldap_str2dn(dn, &ldn, LDAP_DN_FORMAT_LDAPV3);
|
|
+ if (ret != LDAP_SUCCESS) {
|
|
+ LOG_TRACE("ldap_str2dn(dn) failed ?!");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ for (rdnc = 0; ldn != NULL && ldn[rdnc]; rdnc++) /* count */ ;
|
|
+ ldap_dnfree(ldn);
|
|
+
|
|
+ return rdnc;
|
|
+}
|
|
+
|
|
+int ipapwd_getPolicy(const char *dn,
|
|
+ Slapi_Entry *target,
|
|
+ struct ipapwd_policy *policy)
|
|
+{
|
|
+ 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;
|
|
+ int ret, res, dist, rdnc, scope, i;
|
|
+ Slapi_DN *sdn = NULL;
|
|
+ int buffer_flags=0;
|
|
+ Slapi_ValueSet* results = NULL;
|
|
+ char* actual_type_name = NULL;
|
|
+ int tmpint;
|
|
+
|
|
+ LOG_TRACE("Searching policy for [%s]\n", dn);
|
|
+
|
|
+ sdn = slapi_sdn_new_dn_byref(dn);
|
|
+ if (sdn == NULL) {
|
|
+ LOG_OOM();
|
|
+ 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;
|
|
+ LOG_TRACE("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) {
|
|
+ LOG_FATAL("Invalid DN [%s]\n", dn);
|
|
+ ret = -1;
|
|
+ goto done;
|
|
+ }
|
|
+ pdn = slapi_sdn_get_dn(psdn);
|
|
+ scope = LDAP_SCOPE_SUBTREE;
|
|
+ }
|
|
+
|
|
+ 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) {
|
|
+ LOG_FATAL("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) {
|
|
+ LOG_TRACE("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) {
|
|
+ pe = es[0];
|
|
+ goto fill;
|
|
+ }
|
|
+
|
|
+ /* count number of RDNs in DN */
|
|
+ rdnc = ipapwd_rdn_count(dn);
|
|
+ if (rdnc == -1) {
|
|
+ LOG_TRACE("ipapwd_rdn_count(dn) failed");
|
|
+ ret = -1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+ int c1;
|
|
+
|
|
+ dn1 = slapi_sdn_get_dn(esdn);
|
|
+ if (!dn1) continue;
|
|
+ c1 = ipapwd_rdn_count(dn1);
|
|
+ if (c1 == -1) continue;
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+fill:
|
|
+ policy->min_pwd_life = slapi_entry_attr_get_int(pe, "krbMinPwdLife");
|
|
+
|
|
+ tmpint = slapi_entry_attr_get_int(pe, "krbMaxPwdLife");
|
|
+ if (tmpint != 0) {
|
|
+ policy->max_pwd_life = tmpint;
|
|
+ }
|
|
+
|
|
+ tmpint = slapi_entry_attr_get_int(pe, "krbPwdMinLength");
|
|
+ if (tmpint != 0) {
|
|
+ policy->min_pwd_length = tmpint;
|
|
+ }
|
|
+
|
|
+ policy->history_length = slapi_entry_attr_get_int(pe,
|
|
+ "krbPwdHistoryLength");
|
|
+
|
|
+ policy->min_complexity = slapi_entry_attr_get_int(pe,
|
|
+ "krbPwdMinDiffChars");
|
|
+
|
|
+ ret = 0;
|
|
+
|
|
+done:
|
|
+ if (results) {
|
|
+ pwd_values_free(&results, &actual_type_name, buffer_flags);
|
|
+ }
|
|
+ if (pb) {
|
|
+ slapi_free_search_results_internal(pb);
|
|
+ slapi_pblock_destroy(pb);
|
|
+ }
|
|
+ if (sdn) slapi_sdn_free(&sdn);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+/*==Common-public-functions=============================================*/
|
|
+
|
|
+int ipapwd_entry_checks(Slapi_PBlock *pb, struct slapi_entry *e,
|
|
+ int *is_root, int *is_krb, int *is_smb, int *is_ipant,
|
|
+ char *attr, int acc)
|
|
+{
|
|
+ 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, acc);
|
|
+ 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);
|
|
+
|
|
+ sval = slapi_value_new_string("ipaNTUserAttrs");
|
|
+ if (!sval) {
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+ *is_ipant = slapi_entry_attr_has_syntax_value(e, SLAPI_ATTR_OBJECTCLASS,
|
|
+ sval);
|
|
+ slapi_value_free(&sval);
|
|
+
|
|
+ rc = LDAP_SUCCESS;
|
|
+
|
|
+done:
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg,
|
|
+ struct ipapwd_krbcfg **config, int check_flags)
|
|
+{
|
|
+ int ret, ssf;
|
|
+ int rc = LDAP_SUCCESS;
|
|
+ Slapi_Backend *be;
|
|
+ const Slapi_DN *psdn;
|
|
+ Slapi_DN *sdn;
|
|
+ char *dn = NULL;
|
|
+
|
|
+ LOG_TRACE("=>\n");
|
|
+
|
|
+#ifdef LDAP_EXTOP_PASSMOD_CONN_SECURE
|
|
+ if (check_flags & IPAPWD_CHECK_CONN_SECURE) {
|
|
+ /* Allow password modify on all connections with a Security Strength
|
|
+ * Factor (SSF) higher than 1 */
|
|
+ if (slapi_pblock_get(pb, SLAPI_OPERATION_SSF, &ssf) != 0) {
|
|
+ LOG("Could not get SSF from connection\n");
|
|
+ *errMesg = "Operation requires a secure connection.\n";
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (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) {
|
|
+ LOG("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) {
|
|
+ LOG_FATAL("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) {
|
|
+ LOG_FATAL("Error Retrieving Master Key");
|
|
+ *errMesg = "Fatal Internal Error";
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ }
|
|
+
|
|
+done:
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* check password strenght and history */
|
|
+int ipapwd_CheckPolicy(struct ipapwd_data *data)
|
|
+{
|
|
+ struct ipapwd_policy pol = {0};
|
|
+ time_t acct_expiration;
|
|
+ time_t pwd_expiration;
|
|
+ time_t last_pwd_change;
|
|
+ char **pwd_history;
|
|
+ char *tmpstr;
|
|
+ int ret;
|
|
+
|
|
+ pol.max_pwd_life = IPAPWD_DEFAULT_PWDLIFE;
|
|
+ pol.min_pwd_length = IPAPWD_DEFAULT_MINLEN;
|
|
+
|
|
+ if (data->changetype != IPA_CHANGETYPE_NORMAL) {
|
|
+ /* We must skip policy checks (Admin change) but
|
|
+ * force a password change on the next login.
|
|
+ * But not if Directory Manager */
|
|
+ if (data->changetype == IPA_CHANGETYPE_ADMIN) {
|
|
+ /* The expiration date needs to be older than the current time
|
|
+ * otherwise the KDC may not immediately register the password
|
|
+ * as expired. The last password change needs to match the
|
|
+ * password expiration otherwise minlife issues will arise.
|
|
+ */
|
|
+ data->timeNow -= 1;
|
|
+ data->expireTime = data->timeNow;
|
|
+ }
|
|
+
|
|
+ /* do not load policies */
|
|
+ } else {
|
|
+
|
|
+ /* find the entry with the password policy */
|
|
+ ret = ipapwd_getPolicy(data->dn, data->target, &pol);
|
|
+ if (ret) {
|
|
+ LOG_TRACE("No password policy, use defaults");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ tmpstr = slapi_entry_attr_get_charptr(data->target,
|
|
+ "krbPrincipalExpiration");
|
|
+ acct_expiration = ipapwd_gentime_to_time_t(tmpstr);
|
|
+ slapi_ch_free_string(&tmpstr);
|
|
+
|
|
+ tmpstr = slapi_entry_attr_get_charptr(data->target,
|
|
+ "krbPasswordExpiration");
|
|
+ pwd_expiration = ipapwd_gentime_to_time_t(tmpstr);
|
|
+ slapi_ch_free_string(&tmpstr);
|
|
+
|
|
+ tmpstr = slapi_entry_attr_get_charptr(data->target,
|
|
+ "krbLastPwdChange");
|
|
+ last_pwd_change = ipapwd_gentime_to_time_t(tmpstr);
|
|
+ slapi_ch_free_string(&tmpstr);
|
|
+
|
|
+ pwd_history = slapi_entry_attr_get_charray(data->target,
|
|
+ "passwordHistory");
|
|
+
|
|
+ /* check policy */
|
|
+ ret = ipapwd_check_policy(&pol, data->password,
|
|
+ data->timeNow,
|
|
+ acct_expiration,
|
|
+ pwd_expiration,
|
|
+ last_pwd_change,
|
|
+ pwd_history);
|
|
+
|
|
+ slapi_ch_array_free(pwd_history);
|
|
+
|
|
+ if (data->expireTime == 0) {
|
|
+ data->expireTime = data->timeNow + pol.max_pwd_life;
|
|
+ }
|
|
+
|
|
+ data->policy = pol;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* 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
|
|
+ */
|
|
+int ipapwd_getEntry(const char *dn, Slapi_Entry **e2, char **attrlist)
|
|
+{
|
|
+ Slapi_DN *sdn;
|
|
+ int search_result = 0;
|
|
+
|
|
+ LOG_TRACE("=>\n");
|
|
+
|
|
+ sdn = slapi_sdn_new_dn_byref(dn);
|
|
+ search_result = slapi_search_internal_get_entry(sdn, attrlist, e2,
|
|
+ ipapwd_plugin_id);
|
|
+ if (search_result != LDAP_SUCCESS) {
|
|
+ LOG_TRACE("No such entry-(%s), err (%d)\n", dn, search_result);
|
|
+ }
|
|
+
|
|
+ slapi_sdn_free(&sdn);
|
|
+ LOG_TRACE("<= result: %d\n", search_result);
|
|
+ return search_result;
|
|
+}
|
|
+
|
|
+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) {
|
|
+ LOG_TRACE("Error retrieving berval from Slapi_Value\n");
|
|
+ goto next;
|
|
+ }
|
|
+ be = ber_init(discard_const(cbval));
|
|
+ if (!be) {
|
|
+ LOG_TRACE("ber_init() failed!\n");
|
|
+ goto next;
|
|
+ }
|
|
+
|
|
+ tag = ber_scanf(be, "{xxt[i]", &tmp, &tkvno);
|
|
+ if (tag == LBER_ERROR) {
|
|
+ LOG_TRACE("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;
|
|
+}
|
|
+
|
|
+/* Modify the Password attributes of the entry */
|
|
+int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg,
|
|
+ struct ipapwd_data *data, int is_krb)
|
|
+{
|
|
+ int ret = 0;
|
|
+ Slapi_Mods *smods = NULL;
|
|
+ Slapi_Value **svals = NULL;
|
|
+ Slapi_Value **ntvals = NULL;
|
|
+ Slapi_Value **pwvals = NULL;
|
|
+ struct tm utctime;
|
|
+ char timestr[GENERALIZED_TIME_LENGTH+1];
|
|
+ char *lm = NULL;
|
|
+ char *nt = NULL;
|
|
+ int is_smb = 0;
|
|
+ int is_ipant = 0;
|
|
+ int is_host = 0;
|
|
+ Slapi_Value *sambaSamAccount;
|
|
+ Slapi_Value *ipaNTUserAttrs;
|
|
+ Slapi_Value *ipaHost;
|
|
+ char *errMesg = NULL;
|
|
+ char *modtime = NULL;
|
|
+
|
|
+ LOG_TRACE("=>\n");
|
|
+
|
|
+ sambaSamAccount = slapi_value_new_string("sambaSamAccount");
|
|
+ if (slapi_entry_attr_has_syntax_value(data->target,
|
|
+ "objectClass", sambaSamAccount)) {
|
|
+ is_smb = 1;
|
|
+ }
|
|
+ slapi_value_free(&sambaSamAccount);
|
|
+
|
|
+ ipaNTUserAttrs = slapi_value_new_string("ipaNTUserAttrs");
|
|
+ if (slapi_entry_attr_has_syntax_value(data->target,
|
|
+ "objectClass", ipaNTUserAttrs)) {
|
|
+ is_ipant = 1;
|
|
+ }
|
|
+ slapi_value_free(&ipaNTUserAttrs);
|
|
+
|
|
+ ipaHost = slapi_value_new_string("ipaHost");
|
|
+ if (slapi_entry_attr_has_syntax_value(data->target,
|
|
+ "objectClass", ipaHost)) {
|
|
+ is_host = 1;
|
|
+ }
|
|
+ slapi_value_free(&ipaHost);
|
|
+
|
|
+ ret = ipapwd_gen_hashes(krbcfg, data,
|
|
+ data->password,
|
|
+ is_krb, is_smb, is_ipant,
|
|
+ &svals, &nt, &lm, &ntvals, &errMesg);
|
|
+ if (ret) {
|
|
+ goto free_and_return;
|
|
+ }
|
|
+
|
|
+ smods = slapi_mods_new();
|
|
+
|
|
+ if (svals) {
|
|
+ slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
|
+ "krbPrincipalKey", svals);
|
|
+
|
|
+ /* krbLastPwdChange is used to tell whether a host entry has a
|
|
+ * keytab so don't set it on hosts.
|
|
+ */
|
|
+ if (!is_host) {
|
|
+ /* change Last Password Change field with the current date */
|
|
+ if (!gmtime_r(&(data->timeNow), &utctime)) {
|
|
+ LOG_FATAL("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)) {
|
|
+ LOG_FATAL("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);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (lm && is_smb) {
|
|
+ slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
+ "sambaLMPassword", lm);
|
|
+ }
|
|
+
|
|
+ if (nt && is_smb) {
|
|
+ slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
+ "sambaNTPassword", nt);
|
|
+ }
|
|
+
|
|
+ if (ntvals && is_ipant) {
|
|
+ slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
|
+ "ipaNTHash", ntvals);
|
|
+ }
|
|
+
|
|
+ if (is_smb) {
|
|
+ /* with samba integration we need to also set sambaPwdLastSet or
|
|
+ * samba will decide the user has to change the password again */
|
|
+ if (data->changetype == IPA_CHANGETYPE_ADMIN) {
|
|
+ /* if it is an admin change instead we need to let know to
|
|
+ * samba as well that the use rmust change its password */
|
|
+ modtime = slapi_ch_smprintf("0");
|
|
+ } else {
|
|
+ modtime = slapi_ch_smprintf("%ld", (long)data->timeNow);
|
|
+ }
|
|
+ if (!modtime) {
|
|
+ LOG_FATAL("failed to smprintf string!\n");
|
|
+ ret = LDAP_OPERATIONS_ERROR;
|
|
+ goto free_and_return;
|
|
+ }
|
|
+ slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
+ "sambaPwdLastset", modtime);
|
|
+ }
|
|
+ if (is_krb) {
|
|
+ if (data->changetype == IPA_CHANGETYPE_ADMIN) {
|
|
+ slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
+ "krbLoginFailedCount", "0");
|
|
+ }
|
|
+ }
|
|
+ /* let DS encode the password itself, this allows also other plugins to
|
|
+ * intercept it to perform operations like synchronization with Active
|
|
+ * Directory domains through the replication plugin */
|
|
+ slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
+ "userPassword", data->password);
|
|
+
|
|
+ /* set password history */
|
|
+ if (data->policy.history_length > 0) {
|
|
+ pwvals = ipapwd_setPasswordHistory(smods, data);
|
|
+ if (pwvals) {
|
|
+ slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
|
+ "passwordHistory", pwvals);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* FIXME:
|
|
+ * instead of replace we should use a delete/add so that we are
|
|
+ * completely sure nobody else modified the entry meanwhile and
|
|
+ * fail if that's the case */
|
|
+
|
|
+ /* commit changes */
|
|
+ ret = ipapwd_apply_mods(data->dn, smods);
|
|
+
|
|
+ LOG_TRACE("<= result: %d\n", ret);
|
|
+
|
|
+free_and_return:
|
|
+ if (lm) slapi_ch_free((void **)&lm);
|
|
+ if (nt) slapi_ch_free((void **)&nt);
|
|
+ if (modtime) slapi_ch_free((void **)&modtime);
|
|
+ slapi_mods_free(&smods);
|
|
+ ipapwd_free_slapi_value_array(&svals);
|
|
+ ipapwd_free_slapi_value_array(&ntvals);
|
|
+ ipapwd_free_slapi_value_array(&pwvals);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+Slapi_Value **ipapwd_setPasswordHistory(Slapi_Mods *smods,
|
|
+ struct ipapwd_data *data)
|
|
+{
|
|
+ Slapi_Value **pH = NULL;
|
|
+ char **pwd_history = NULL;
|
|
+ char **new_pwd_history = NULL;
|
|
+ int n = 0;
|
|
+ int ret;
|
|
+ int i;
|
|
+
|
|
+ pwd_history = slapi_entry_attr_get_charray(data->target,
|
|
+ "passwordHistory");
|
|
+
|
|
+ ret = ipapwd_generate_new_history(data->password, data->timeNow,
|
|
+ data->policy.history_length,
|
|
+ pwd_history, &new_pwd_history, &n);
|
|
+
|
|
+ if (ret && data->policy.history_length) {
|
|
+ LOG_FATAL("failed to generate new password history!\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ pH = (Slapi_Value **)slapi_ch_calloc(n + 1, sizeof(Slapi_Value *));
|
|
+ if (!pH) {
|
|
+ LOG_OOM();
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < n; i++) {
|
|
+ pH[i] = slapi_value_new_string(new_pwd_history[i]);
|
|
+ if (!pH[i]) {
|
|
+ ipapwd_free_slapi_value_array(&pH);
|
|
+ LOG_OOM();
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+done:
|
|
+ slapi_ch_array_free(pwd_history);
|
|
+ for (i = 0; i < n; i++) {
|
|
+ free(new_pwd_history[i]);
|
|
+ }
|
|
+ free(new_pwd_history);
|
|
+ return pH;
|
|
+}
|
|
+
|
|
+/* Construct Mods pblock and perform the modify operation
|
|
+ * Sets result of operation in SLAPI_PLUGIN_INTOP_RESULT
|
|
+ */
|
|
+int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods)
|
|
+{
|
|
+ Slapi_PBlock *pb;
|
|
+ int ret;
|
|
+
|
|
+ LOG_TRACE("=>\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) {
|
|
+ LOG_TRACE("WARNING: modify error %d on entry '%s'\n", ret, dn);
|
|
+ } else {
|
|
+
|
|
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
|
|
+
|
|
+ if (ret != LDAP_SUCCESS){
|
|
+ LOG_TRACE("WARNING: modify error %d on entry '%s'\n", ret, dn);
|
|
+ } else {
|
|
+ LOG_TRACE("<= Successful\n");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ slapi_pblock_destroy(pb);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int ipapwd_set_extradata(const char *dn,
|
|
+ const char *principal,
|
|
+ time_t unixtime)
|
|
+{
|
|
+ Slapi_Mods *smods;
|
|
+ Slapi_Value *va[2] = { NULL };
|
|
+ struct berval bv;
|
|
+ char *xdata;
|
|
+ int xd_len;
|
|
+ int p_len;
|
|
+ int ret;
|
|
+
|
|
+ p_len = strlen(principal);
|
|
+ xd_len = 2 + 4 + p_len + 1;
|
|
+ xdata = malloc(xd_len);
|
|
+ if (!xdata) {
|
|
+ return LDAP_OPERATIONS_ERROR;
|
|
+ }
|
|
+
|
|
+ smods = slapi_mods_new();
|
|
+
|
|
+ /* data type id */
|
|
+ xdata[0] = 0x00;
|
|
+ xdata[1] = 0x02;
|
|
+
|
|
+ /* unix timestamp in Little Endian */
|
|
+ xdata[2] = unixtime & 0xff;
|
|
+ xdata[3] = (unixtime & 0xff00) >> 8;
|
|
+ xdata[4] = (unixtime & 0xff0000) >> 16;
|
|
+ xdata[5] = (unixtime & 0xff000000) >> 24;
|
|
+
|
|
+ /* append the principal name */
|
|
+ strncpy(&xdata[6], principal, p_len);
|
|
+
|
|
+ xdata[xd_len -1] = 0;
|
|
+
|
|
+ bv.bv_val = xdata;
|
|
+ bv.bv_len = xd_len;
|
|
+ va[0] = slapi_value_new_berval(&bv);
|
|
+
|
|
+ slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "krbExtraData", va);
|
|
+
|
|
+ ret = ipapwd_apply_mods(dn, smods);
|
|
+
|
|
+ slapi_value_free(&va[0]);
|
|
+ slapi_mods_free(&smods);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+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);
|
|
+}
|
|
+
|
|
+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;
|
|
+};
|
|
+
|
|
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c
|
|
new file mode 100644
|
|
index 0000000..a92eaf0
|
|
--- /dev/null
|
|
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c
|
|
@@ -0,0 +1,291 @@
|
|
+/** BEGIN COPYRIGHT BLOCK
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
+ *
|
|
+ * Additional permission under GPLv3 section 7:
|
|
+ *
|
|
+ * In the following paragraph, "GPL" means the GNU General Public
|
|
+ * License, version 3 or any later version, and "Non-GPL Code" means
|
|
+ * code that is governed neither by the GPL nor a license
|
|
+ * compatible with the GPL.
|
|
+ *
|
|
+ * You may link the code of this Program with Non-GPL Code and convey
|
|
+ * linked combinations including the two, provided that such Non-GPL
|
|
+ * Code only links to the code of this Program through those well
|
|
+ * defined interfaces identified in the file named EXCEPTION found in
|
|
+ * the source code files (the "Approved Interfaces"). The files of
|
|
+ * Non-GPL Code may instantiate templates or use macros or inline
|
|
+ * functions from the Approved Interfaces without causing the resulting
|
|
+ * work to be covered by the GPL. Only the copyright holders of this
|
|
+ * Program may make changes or additions to the list of Approved
|
|
+ * Interfaces.
|
|
+ *
|
|
+ * Authors:
|
|
+ * Simo Sorce <ssorce@redhat.com>
|
|
+ *
|
|
+ * Copyright (C) 2007-2010 Red Hat, Inc.
|
|
+ * All rights reserved.
|
|
+ * END COPYRIGHT BLOCK **/
|
|
+
|
|
+#ifdef HAVE_CONFIG_H
|
|
+# include <config.h>
|
|
+#endif
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include <dirsrv/slapi-plugin.h>
|
|
+#include <lber.h>
|
|
+#include <time.h>
|
|
+
|
|
+#include <endian.h>
|
|
+
|
|
+#include "ipapwd.h"
|
|
+#include "util.h"
|
|
+#include "ipa_krb5.h"
|
|
+
|
|
+/* 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
|
|
+
|
|
+/* 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];
|
|
+ }
|
|
+}
|
|
+
|
|
+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++) {
|
|
+ free(kset->keys[i].key_data_contents[0]);
|
|
+ free(kset->keys[i].key_data_contents[1]);
|
|
+ }
|
|
+ free(kset->keys);
|
|
+ free(kset);
|
|
+ *pkset = NULL;
|
|
+}
|
|
+
|
|
+static Slapi_Value **encrypt_encode_key(struct ipapwd_krbcfg *krbcfg,
|
|
+ struct ipapwd_data *data,
|
|
+ char **errMesg)
|
|
+{
|
|
+ krb5_context krbctx;
|
|
+ char *krbPrincipalName = NULL;
|
|
+ int kvno;
|
|
+ struct berval *bval = NULL;
|
|
+ Slapi_Value **svals = NULL;
|
|
+ krb5_principal princ = NULL;
|
|
+ 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) {
|
|
+ LOG_OOM();
|
|
+ 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";
|
|
+ LOG_FATAL("%s", *errMesg);
|
|
+ goto enc_error;
|
|
+ }
|
|
+
|
|
+ krberr = krb5_parse_name(krbctx, krbPrincipalName, &princ);
|
|
+ if (krberr) {
|
|
+ LOG_FATAL("krb5_parse_name failed [%s]\n",
|
|
+ krb5_get_error_message(krbctx, krberr));
|
|
+ goto enc_error;
|
|
+ }
|
|
+
|
|
+ pwd.data = (char *)data->password;
|
|
+ pwd.length = strlen(data->password);
|
|
+
|
|
+ kset = malloc(sizeof(struct ipapwd_keyset));
|
|
+ if (!kset) {
|
|
+ LOG_OOM();
|
|
+ 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) */
|
|
+ kvno += 1;
|
|
+ kset->mkvno = krbcfg->mkvno;
|
|
+
|
|
+ krberr = ipa_krb5_generate_key_data(krbctx, princ,
|
|
+ pwd, kvno, krbcfg->kmkey,
|
|
+ krbcfg->num_pref_encsalts,
|
|
+ krbcfg->pref_encsalts,
|
|
+ &kset->num_keys, &kset->keys);
|
|
+ if (krberr != 0) {
|
|
+ LOG_FATAL("generating kerberos keys failed [%s]\n",
|
|
+ krb5_get_error_message(krbctx, krberr));
|
|
+ goto enc_error;
|
|
+ }
|
|
+
|
|
+ krberr = ber_encode_krb5_key_data(kset->keys, kset->num_keys,
|
|
+ kset->mkvno, &bval);
|
|
+ if (krberr != 0) {
|
|
+ LOG_FATAL("encoding krb5_key_data failed\n");
|
|
+ goto enc_error;
|
|
+ }
|
|
+
|
|
+ svals[0] = slapi_value_new_berval(bval);
|
|
+ if (!svals[0]) {
|
|
+ LOG_FATAL("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;
|
|
+}
|
|
+
|
|
+int ipapwd_gen_hashes(struct ipapwd_krbcfg *krbcfg,
|
|
+ struct ipapwd_data *data, char *userpw,
|
|
+ int is_krb, int is_smb, int is_ipant, Slapi_Value ***svals,
|
|
+ char **nthash, char **lmhash, Slapi_Value ***ntvals,
|
|
+ char **errMesg)
|
|
+{
|
|
+ int rc;
|
|
+ char *userpw_uc = NULL;
|
|
+
|
|
+ *svals = NULL;
|
|
+ *nthash = NULL;
|
|
+ *lmhash = NULL;
|
|
+ *errMesg = NULL;
|
|
+
|
|
+ if (is_krb) {
|
|
+
|
|
+ *svals = encrypt_encode_key(krbcfg, data, errMesg);
|
|
+
|
|
+ if (!*svals) {
|
|
+ /* errMesg should have been set in encrypt_encode_key() */
|
|
+ LOG_FATAL("key encryption/encoding failed\n");
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (is_smb || is_ipant) {
|
|
+ char lm[33], nt[33];
|
|
+ struct ntlm_keys ntlm;
|
|
+ int ret;
|
|
+
|
|
+ userpw_uc = (char *) slapi_utf8StrToUpper((unsigned char *) userpw);
|
|
+ if (!userpw_uc) {
|
|
+ *errMesg = "Failed to generate upper case password\n";
|
|
+ LOG_FATAL("%s", *errMesg);
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = encode_ntlm_keys(userpw,
|
|
+ userpw_uc,
|
|
+ krbcfg->allow_lm_hash,
|
|
+ krbcfg->allow_nt_hash,
|
|
+ &ntlm);
|
|
+ memset(userpw_uc, 0, strlen(userpw_uc));
|
|
+ slapi_ch_free_string(&userpw_uc);
|
|
+ if (ret) {
|
|
+ *errMesg = "Failed to generate NT/LM hashes\n";
|
|
+ LOG_FATAL("%s", *errMesg);
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+ if (krbcfg->allow_lm_hash) {
|
|
+ hexbuf(lm, ntlm.lm);
|
|
+ lm[32] = '\0';
|
|
+ *lmhash = slapi_ch_strdup(lm);
|
|
+ }
|
|
+ if (krbcfg->allow_nt_hash) {
|
|
+ hexbuf(nt, ntlm.nt);
|
|
+ nt[32] = '\0';
|
|
+ *nthash = slapi_ch_strdup(nt);
|
|
+ }
|
|
+
|
|
+ if (is_ipant) {
|
|
+ *ntvals = (Slapi_Value **)calloc(2, sizeof(Slapi_Value *));
|
|
+ if (!*ntvals) {
|
|
+ LOG_OOM();
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+ (*ntvals)[0] = slapi_value_new();
|
|
+ if (slapi_value_set((*ntvals)[0], ntlm.nt, 16) == NULL) {
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ rc = LDAP_SUCCESS;
|
|
+
|
|
+done:
|
|
+
|
|
+ /* when error, free possibly allocated output parameters */
|
|
+ if (rc) {
|
|
+ ipapwd_free_slapi_value_array(svals);
|
|
+ ipapwd_free_slapi_value_array(ntvals);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
|
|
index 3689783..372441d 100644
|
|
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
|
|
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
|
|
@@ -96,7 +96,7 @@ struct ipapwd_operation {
|
|
|
|
#define GENERALIZED_TIME_LENGTH 15
|
|
|
|
-/* from ipapwd_common.c */
|
|
+/* from common.c */
|
|
struct ipapwd_krbcfg {
|
|
krb5_context krbctx;
|
|
char *realm;
|
|
@@ -131,7 +131,7 @@ int ipapwd_set_extradata(const char *dn,
|
|
void ipapwd_free_slapi_value_array(Slapi_Value ***svals);
|
|
void free_ipapwd_krbcfg(struct ipapwd_krbcfg **cfg);
|
|
|
|
-/* from ipapwd_encoding.c */
|
|
+/* from encoding.c */
|
|
struct ipapwd_keyset {
|
|
uint16_t major_vno;
|
|
uint16_t minor_vno;
|
|
@@ -148,7 +148,7 @@ int ipapwd_gen_hashes(struct ipapwd_krbcfg *krbcfg,
|
|
Slapi_Value ***svals, char **nthash, char **lmhash,
|
|
Slapi_Value ***ntvals, char **errMesg);
|
|
|
|
-/* from ipapwd_prepost.c */
|
|
+/* from prepost.c */
|
|
int ipapwd_ext_init(void);
|
|
int ipapwd_pre_init(Slapi_PBlock *pb);
|
|
int ipapwd_post_init(Slapi_PBlock *pb);
|
|
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_common.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_common.c
|
|
deleted file mode 100644
|
|
index bb1d96a..0000000
|
|
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_common.c
|
|
+++ /dev/null
|
|
@@ -1,1107 +0,0 @@
|
|
-/** BEGIN COPYRIGHT BLOCK
|
|
- * This program is free software; you can redistribute it and/or modify
|
|
- * it under the terms of the GNU General Public License as published by
|
|
- * the Free Software Foundation, either version 3 of the License, or
|
|
- * (at your option) any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * You should have received a copy of the GNU General Public License
|
|
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
- *
|
|
- * Additional permission under GPLv3 section 7:
|
|
- *
|
|
- * In the following paragraph, "GPL" means the GNU General Public
|
|
- * License, version 3 or any later version, and "Non-GPL Code" means
|
|
- * code that is governed neither by the GPL nor a license
|
|
- * compatible with the GPL.
|
|
- *
|
|
- * You may link the code of this Program with Non-GPL Code and convey
|
|
- * linked combinations including the two, provided that such Non-GPL
|
|
- * Code only links to the code of this Program through those well
|
|
- * defined interfaces identified in the file named EXCEPTION found in
|
|
- * the source code files (the "Approved Interfaces"). The files of
|
|
- * Non-GPL Code may instantiate templates or use macros or inline
|
|
- * functions from the Approved Interfaces without causing the resulting
|
|
- * work to be covered by the GPL. Only the copyright holders of this
|
|
- * Program may make changes or additions to the list of Approved
|
|
- * Interfaces.
|
|
- *
|
|
- * Authors:
|
|
- * Simo Sorce <ssorce@redhat.com>
|
|
- *
|
|
- * Copyright (C) 2007-2010 Red Hat, Inc.
|
|
- * All rights reserved.
|
|
- * END COPYRIGHT BLOCK **/
|
|
-
|
|
-#include "ipapwd.h"
|
|
-#include "util.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 */
|
|
-
|
|
-extern void *ipapwd_plugin_id;
|
|
-extern const char *ipa_realm_dn;
|
|
-extern const char *ipa_etc_config_dn;
|
|
-extern const char *ipa_pwd_config_dn;
|
|
-
|
|
-/* 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 file content into
|
|
- * the directory */
|
|
-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
|
|
-};
|
|
-
|
|
-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, tvno;
|
|
- ber_int_t ttype;
|
|
- const struct berval *bval;
|
|
- struct berval *mkey = NULL;
|
|
- char **encsalts;
|
|
- char **tmparray;
|
|
- char *tmpstr;
|
|
- int i, ret;
|
|
-
|
|
- config = calloc(1, sizeof(struct ipapwd_krbcfg));
|
|
- if (!config) {
|
|
- LOG_OOM();
|
|
- goto free_and_error;
|
|
- }
|
|
- kmkey = calloc(1, sizeof(krb5_keyblock));
|
|
- if (!kmkey) {
|
|
- LOG_OOM();
|
|
- goto free_and_error;
|
|
- }
|
|
- config->kmkey = kmkey;
|
|
-
|
|
- krberr = krb5_init_context(&config->krbctx);
|
|
- if (krberr) {
|
|
- LOG_FATAL("krb5_init_context failed\n");
|
|
- goto free_and_error;
|
|
- }
|
|
-
|
|
- ret = krb5_get_default_realm(config->krbctx, &config->realm);
|
|
- if (ret) {
|
|
- LOG_FATAL("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) {
|
|
- LOG_FATAL("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) {
|
|
- LOG_FATAL("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) {
|
|
- LOG_FATAL("No master key??\n");
|
|
- goto free_and_error;
|
|
- }
|
|
-
|
|
- bval = slapi_value_get_berval(v);
|
|
- if (!bval) {
|
|
- LOG_FATAL("Error retrieving master key berval\n");
|
|
- goto free_and_error;
|
|
- }
|
|
-
|
|
- be = ber_init(discard_const(bval));
|
|
- if (!be) {
|
|
- LOG_FATAL("ber_init() failed!\n");
|
|
- goto free_and_error;
|
|
- }
|
|
-
|
|
- tag = ber_scanf(be, "{i{iO}}", &tvno, &ttype, &mkey);
|
|
- if (tag == LBER_ERROR) {
|
|
- LOG_FATAL("Bad Master key encoding ?!\n");
|
|
- goto free_and_error;
|
|
- }
|
|
-
|
|
- config->mkvno = tvno;
|
|
- kmkey->magic = KV5M_KEYBLOCK;
|
|
- kmkey->enctype = ttype;
|
|
- kmkey->length = mkey->bv_len;
|
|
- kmkey->contents = malloc(mkey->bv_len);
|
|
- if (!kmkey->contents) {
|
|
- LOG_OOM();
|
|
- 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) {
|
|
- for (i = 0; encsalts[i]; i++) /* count */ ;
|
|
- ret = parse_bval_key_salt_tuples(config->krbctx,
|
|
- (const char * const *)encsalts, i,
|
|
- &config->supp_encsalts,
|
|
- &config->num_supp_encsalts);
|
|
- slapi_ch_array_free(encsalts);
|
|
- } else {
|
|
- LOG("No configured salt types use defaults\n");
|
|
- for (i = 0; ipapwd_def_encsalts[i]; i++) /* count */ ;
|
|
- ret = parse_bval_key_salt_tuples(config->krbctx,
|
|
- ipapwd_def_encsalts, i,
|
|
- &config->supp_encsalts,
|
|
- &config->num_supp_encsalts);
|
|
- }
|
|
- if (ret) {
|
|
- LOG_FATAL("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) {
|
|
- for (i = 0; encsalts[i]; i++) /* count */ ;
|
|
- ret = parse_bval_key_salt_tuples(config->krbctx,
|
|
- (const char * const *)encsalts, i,
|
|
- &config->pref_encsalts,
|
|
- &config->num_pref_encsalts);
|
|
- slapi_ch_array_free(encsalts);
|
|
- } else {
|
|
- LOG("No configured salt types use defaults\n");
|
|
- for (i = 0; ipapwd_def_encsalts[i]; i++) /* count */ ;
|
|
- ret = parse_bval_key_salt_tuples(config->krbctx,
|
|
- ipapwd_def_encsalts, i,
|
|
- &config->pref_encsalts,
|
|
- &config->num_pref_encsalts);
|
|
- }
|
|
- if (ret) {
|
|
- LOG_FATAL("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) {
|
|
- LOG_FATAL("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) {
|
|
- LOG_OOM();
|
|
- goto free_and_error;
|
|
- }
|
|
- for (i = 0; config->passsync_mgrs[i]; i++) /* count */ ;
|
|
- config->num_passsync_mgrs = i;
|
|
-
|
|
- slapi_entry_free(config_entry);
|
|
-
|
|
- /* get the ipa etc/ipaConfig entry */
|
|
- config->allow_lm_hash = false;
|
|
- config->allow_nt_hash = false;
|
|
- ret = ipapwd_getEntry(ipa_etc_config_dn, &config_entry, NULL);
|
|
- if (ret != LDAP_SUCCESS) {
|
|
- LOG_FATAL("No config Entry?\n");
|
|
- goto free_and_error;
|
|
- } else {
|
|
- tmparray = slapi_entry_attr_get_charray(config_entry,
|
|
- "ipaConfigString");
|
|
- for (i = 0; tmparray && tmparray[i]; i++) {
|
|
- if (strcasecmp(tmparray[i], "AllowLMhash") == 0) {
|
|
- config->allow_lm_hash = true;
|
|
- continue;
|
|
- }
|
|
- if (strcasecmp(tmparray[i], "AllowNThash") == 0) {
|
|
- config->allow_nt_hash = true;
|
|
- continue;
|
|
- }
|
|
- }
|
|
- if (tmparray) slapi_ch_array_free(tmparray);
|
|
- }
|
|
-
|
|
- slapi_entry_free(config_entry);
|
|
-
|
|
- 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;
|
|
-}
|
|
-
|
|
-/* 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;
|
|
-
|
|
- 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);
|
|
-}
|
|
-
|
|
-static int ipapwd_rdn_count(const char *dn)
|
|
-{
|
|
- int rdnc = 0;
|
|
- LDAPDN ldn;
|
|
- int ret;
|
|
-
|
|
- ret = ldap_str2dn(dn, &ldn, LDAP_DN_FORMAT_LDAPV3);
|
|
- if (ret != LDAP_SUCCESS) {
|
|
- LOG_TRACE("ldap_str2dn(dn) failed ?!");
|
|
- return -1;
|
|
- }
|
|
-
|
|
- for (rdnc = 0; ldn != NULL && ldn[rdnc]; rdnc++) /* count */ ;
|
|
- ldap_dnfree(ldn);
|
|
-
|
|
- return rdnc;
|
|
-}
|
|
-
|
|
-int ipapwd_getPolicy(const char *dn,
|
|
- Slapi_Entry *target,
|
|
- struct ipapwd_policy *policy)
|
|
-{
|
|
- 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;
|
|
- int ret, res, dist, rdnc, scope, i;
|
|
- Slapi_DN *sdn = NULL;
|
|
- int buffer_flags=0;
|
|
- Slapi_ValueSet* results = NULL;
|
|
- char* actual_type_name = NULL;
|
|
- int tmpint;
|
|
-
|
|
- LOG_TRACE("Searching policy for [%s]\n", dn);
|
|
-
|
|
- sdn = slapi_sdn_new_dn_byref(dn);
|
|
- if (sdn == NULL) {
|
|
- LOG_OOM();
|
|
- 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;
|
|
- LOG_TRACE("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) {
|
|
- LOG_FATAL("Invalid DN [%s]\n", dn);
|
|
- ret = -1;
|
|
- goto done;
|
|
- }
|
|
- pdn = slapi_sdn_get_dn(psdn);
|
|
- scope = LDAP_SCOPE_SUBTREE;
|
|
- }
|
|
-
|
|
- 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) {
|
|
- LOG_FATAL("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) {
|
|
- LOG_TRACE("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) {
|
|
- pe = es[0];
|
|
- goto fill;
|
|
- }
|
|
-
|
|
- /* count number of RDNs in DN */
|
|
- rdnc = ipapwd_rdn_count(dn);
|
|
- if (rdnc == -1) {
|
|
- LOG_TRACE("ipapwd_rdn_count(dn) failed");
|
|
- ret = -1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- 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;
|
|
- int c1;
|
|
-
|
|
- dn1 = slapi_sdn_get_dn(esdn);
|
|
- if (!dn1) continue;
|
|
- c1 = ipapwd_rdn_count(dn1);
|
|
- if (c1 == -1) continue;
|
|
- 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;
|
|
- }
|
|
-
|
|
-fill:
|
|
- policy->min_pwd_life = slapi_entry_attr_get_int(pe, "krbMinPwdLife");
|
|
-
|
|
- tmpint = slapi_entry_attr_get_int(pe, "krbMaxPwdLife");
|
|
- if (tmpint != 0) {
|
|
- policy->max_pwd_life = tmpint;
|
|
- }
|
|
-
|
|
- tmpint = slapi_entry_attr_get_int(pe, "krbPwdMinLength");
|
|
- if (tmpint != 0) {
|
|
- policy->min_pwd_length = tmpint;
|
|
- }
|
|
-
|
|
- policy->history_length = slapi_entry_attr_get_int(pe,
|
|
- "krbPwdHistoryLength");
|
|
-
|
|
- policy->min_complexity = slapi_entry_attr_get_int(pe,
|
|
- "krbPwdMinDiffChars");
|
|
-
|
|
- ret = 0;
|
|
-
|
|
-done:
|
|
- if (results) {
|
|
- pwd_values_free(&results, &actual_type_name, buffer_flags);
|
|
- }
|
|
- if (pb) {
|
|
- slapi_free_search_results_internal(pb);
|
|
- slapi_pblock_destroy(pb);
|
|
- }
|
|
- if (sdn) slapi_sdn_free(&sdn);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-
|
|
-/*==Common-public-functions=============================================*/
|
|
-
|
|
-int ipapwd_entry_checks(Slapi_PBlock *pb, struct slapi_entry *e,
|
|
- int *is_root, int *is_krb, int *is_smb, int *is_ipant,
|
|
- char *attr, int acc)
|
|
-{
|
|
- 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, acc);
|
|
- 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);
|
|
-
|
|
- sval = slapi_value_new_string("ipaNTUserAttrs");
|
|
- if (!sval) {
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
- *is_ipant = slapi_entry_attr_has_syntax_value(e, SLAPI_ATTR_OBJECTCLASS,
|
|
- sval);
|
|
- slapi_value_free(&sval);
|
|
-
|
|
- rc = LDAP_SUCCESS;
|
|
-
|
|
-done:
|
|
- return rc;
|
|
-}
|
|
-
|
|
-int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg,
|
|
- struct ipapwd_krbcfg **config, int check_flags)
|
|
-{
|
|
- int ret, ssf;
|
|
- int rc = LDAP_SUCCESS;
|
|
- Slapi_Backend *be;
|
|
- const Slapi_DN *psdn;
|
|
- Slapi_DN *sdn;
|
|
- char *dn = NULL;
|
|
-
|
|
- LOG_TRACE("=>\n");
|
|
-
|
|
-#ifdef LDAP_EXTOP_PASSMOD_CONN_SECURE
|
|
- if (check_flags & IPAPWD_CHECK_CONN_SECURE) {
|
|
- /* Allow password modify on all connections with a Security Strength
|
|
- * Factor (SSF) higher than 1 */
|
|
- if (slapi_pblock_get(pb, SLAPI_OPERATION_SSF, &ssf) != 0) {
|
|
- LOG("Could not get SSF from connection\n");
|
|
- *errMesg = "Operation requires a secure connection.\n";
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- if (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) {
|
|
- LOG("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) {
|
|
- LOG_FATAL("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) {
|
|
- LOG_FATAL("Error Retrieving Master Key");
|
|
- *errMesg = "Fatal Internal Error";
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- }
|
|
-
|
|
-done:
|
|
- return rc;
|
|
-}
|
|
-
|
|
-/* check password strenght and history */
|
|
-int ipapwd_CheckPolicy(struct ipapwd_data *data)
|
|
-{
|
|
- struct ipapwd_policy pol = {0};
|
|
- time_t acct_expiration;
|
|
- time_t pwd_expiration;
|
|
- time_t last_pwd_change;
|
|
- char **pwd_history;
|
|
- char *tmpstr;
|
|
- int ret;
|
|
-
|
|
- pol.max_pwd_life = IPAPWD_DEFAULT_PWDLIFE;
|
|
- pol.min_pwd_length = IPAPWD_DEFAULT_MINLEN;
|
|
-
|
|
- if (data->changetype != IPA_CHANGETYPE_NORMAL) {
|
|
- /* We must skip policy checks (Admin change) but
|
|
- * force a password change on the next login.
|
|
- * But not if Directory Manager */
|
|
- if (data->changetype == IPA_CHANGETYPE_ADMIN) {
|
|
- /* The expiration date needs to be older than the current time
|
|
- * otherwise the KDC may not immediately register the password
|
|
- * as expired. The last password change needs to match the
|
|
- * password expiration otherwise minlife issues will arise.
|
|
- */
|
|
- data->timeNow -= 1;
|
|
- data->expireTime = data->timeNow;
|
|
- }
|
|
-
|
|
- /* do not load policies */
|
|
- } else {
|
|
-
|
|
- /* find the entry with the password policy */
|
|
- ret = ipapwd_getPolicy(data->dn, data->target, &pol);
|
|
- if (ret) {
|
|
- LOG_TRACE("No password policy, use defaults");
|
|
- }
|
|
- }
|
|
-
|
|
- tmpstr = slapi_entry_attr_get_charptr(data->target,
|
|
- "krbPrincipalExpiration");
|
|
- acct_expiration = ipapwd_gentime_to_time_t(tmpstr);
|
|
- slapi_ch_free_string(&tmpstr);
|
|
-
|
|
- tmpstr = slapi_entry_attr_get_charptr(data->target,
|
|
- "krbPasswordExpiration");
|
|
- pwd_expiration = ipapwd_gentime_to_time_t(tmpstr);
|
|
- slapi_ch_free_string(&tmpstr);
|
|
-
|
|
- tmpstr = slapi_entry_attr_get_charptr(data->target,
|
|
- "krbLastPwdChange");
|
|
- last_pwd_change = ipapwd_gentime_to_time_t(tmpstr);
|
|
- slapi_ch_free_string(&tmpstr);
|
|
-
|
|
- pwd_history = slapi_entry_attr_get_charray(data->target,
|
|
- "passwordHistory");
|
|
-
|
|
- /* check policy */
|
|
- ret = ipapwd_check_policy(&pol, data->password,
|
|
- data->timeNow,
|
|
- acct_expiration,
|
|
- pwd_expiration,
|
|
- last_pwd_change,
|
|
- pwd_history);
|
|
-
|
|
- slapi_ch_array_free(pwd_history);
|
|
-
|
|
- if (data->expireTime == 0) {
|
|
- data->expireTime = data->timeNow + pol.max_pwd_life;
|
|
- }
|
|
-
|
|
- data->policy = pol;
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-/* 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
|
|
- */
|
|
-int ipapwd_getEntry(const char *dn, Slapi_Entry **e2, char **attrlist)
|
|
-{
|
|
- Slapi_DN *sdn;
|
|
- int search_result = 0;
|
|
-
|
|
- LOG_TRACE("=>\n");
|
|
-
|
|
- sdn = slapi_sdn_new_dn_byref(dn);
|
|
- search_result = slapi_search_internal_get_entry(sdn, attrlist, e2,
|
|
- ipapwd_plugin_id);
|
|
- if (search_result != LDAP_SUCCESS) {
|
|
- LOG_TRACE("No such entry-(%s), err (%d)\n", dn, search_result);
|
|
- }
|
|
-
|
|
- slapi_sdn_free(&sdn);
|
|
- LOG_TRACE("<= result: %d\n", search_result);
|
|
- return search_result;
|
|
-}
|
|
-
|
|
-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) {
|
|
- LOG_TRACE("Error retrieving berval from Slapi_Value\n");
|
|
- goto next;
|
|
- }
|
|
- be = ber_init(discard_const(cbval));
|
|
- if (!be) {
|
|
- LOG_TRACE("ber_init() failed!\n");
|
|
- goto next;
|
|
- }
|
|
-
|
|
- tag = ber_scanf(be, "{xxt[i]", &tmp, &tkvno);
|
|
- if (tag == LBER_ERROR) {
|
|
- LOG_TRACE("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;
|
|
-}
|
|
-
|
|
-/* Modify the Password attributes of the entry */
|
|
-int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg,
|
|
- struct ipapwd_data *data, int is_krb)
|
|
-{
|
|
- int ret = 0;
|
|
- Slapi_Mods *smods = NULL;
|
|
- Slapi_Value **svals = NULL;
|
|
- Slapi_Value **ntvals = NULL;
|
|
- Slapi_Value **pwvals = NULL;
|
|
- struct tm utctime;
|
|
- char timestr[GENERALIZED_TIME_LENGTH+1];
|
|
- char *lm = NULL;
|
|
- char *nt = NULL;
|
|
- int is_smb = 0;
|
|
- int is_ipant = 0;
|
|
- int is_host = 0;
|
|
- Slapi_Value *sambaSamAccount;
|
|
- Slapi_Value *ipaNTUserAttrs;
|
|
- Slapi_Value *ipaHost;
|
|
- char *errMesg = NULL;
|
|
- char *modtime = NULL;
|
|
-
|
|
- LOG_TRACE("=>\n");
|
|
-
|
|
- sambaSamAccount = slapi_value_new_string("sambaSamAccount");
|
|
- if (slapi_entry_attr_has_syntax_value(data->target,
|
|
- "objectClass", sambaSamAccount)) {
|
|
- is_smb = 1;
|
|
- }
|
|
- slapi_value_free(&sambaSamAccount);
|
|
-
|
|
- ipaNTUserAttrs = slapi_value_new_string("ipaNTUserAttrs");
|
|
- if (slapi_entry_attr_has_syntax_value(data->target,
|
|
- "objectClass", ipaNTUserAttrs)) {
|
|
- is_ipant = 1;
|
|
- }
|
|
- slapi_value_free(&ipaNTUserAttrs);
|
|
-
|
|
- ipaHost = slapi_value_new_string("ipaHost");
|
|
- if (slapi_entry_attr_has_syntax_value(data->target,
|
|
- "objectClass", ipaHost)) {
|
|
- is_host = 1;
|
|
- }
|
|
- slapi_value_free(&ipaHost);
|
|
-
|
|
- ret = ipapwd_gen_hashes(krbcfg, data,
|
|
- data->password,
|
|
- is_krb, is_smb, is_ipant,
|
|
- &svals, &nt, &lm, &ntvals, &errMesg);
|
|
- if (ret) {
|
|
- goto free_and_return;
|
|
- }
|
|
-
|
|
- smods = slapi_mods_new();
|
|
-
|
|
- if (svals) {
|
|
- slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
|
- "krbPrincipalKey", svals);
|
|
-
|
|
- /* krbLastPwdChange is used to tell whether a host entry has a
|
|
- * keytab so don't set it on hosts.
|
|
- */
|
|
- if (!is_host) {
|
|
- /* change Last Password Change field with the current date */
|
|
- if (!gmtime_r(&(data->timeNow), &utctime)) {
|
|
- LOG_FATAL("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)) {
|
|
- LOG_FATAL("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);
|
|
- }
|
|
- }
|
|
-
|
|
- if (lm && is_smb) {
|
|
- slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
- "sambaLMPassword", lm);
|
|
- }
|
|
-
|
|
- if (nt && is_smb) {
|
|
- slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
- "sambaNTPassword", nt);
|
|
- }
|
|
-
|
|
- if (ntvals && is_ipant) {
|
|
- slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
|
- "ipaNTHash", ntvals);
|
|
- }
|
|
-
|
|
- if (is_smb) {
|
|
- /* with samba integration we need to also set sambaPwdLastSet or
|
|
- * samba will decide the user has to change the password again */
|
|
- if (data->changetype == IPA_CHANGETYPE_ADMIN) {
|
|
- /* if it is an admin change instead we need to let know to
|
|
- * samba as well that the use rmust change its password */
|
|
- modtime = slapi_ch_smprintf("0");
|
|
- } else {
|
|
- modtime = slapi_ch_smprintf("%ld", (long)data->timeNow);
|
|
- }
|
|
- if (!modtime) {
|
|
- LOG_FATAL("failed to smprintf string!\n");
|
|
- ret = LDAP_OPERATIONS_ERROR;
|
|
- goto free_and_return;
|
|
- }
|
|
- slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
- "sambaPwdLastset", modtime);
|
|
- }
|
|
- if (is_krb) {
|
|
- if (data->changetype == IPA_CHANGETYPE_ADMIN) {
|
|
- slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
- "krbLoginFailedCount", "0");
|
|
- }
|
|
- }
|
|
- /* let DS encode the password itself, this allows also other plugins to
|
|
- * intercept it to perform operations like synchronization with Active
|
|
- * Directory domains through the replication plugin */
|
|
- slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
- "userPassword", data->password);
|
|
-
|
|
- /* set password history */
|
|
- if (data->policy.history_length > 0) {
|
|
- pwvals = ipapwd_setPasswordHistory(smods, data);
|
|
- if (pwvals) {
|
|
- slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
|
- "passwordHistory", pwvals);
|
|
- }
|
|
- }
|
|
-
|
|
- /* FIXME:
|
|
- * instead of replace we should use a delete/add so that we are
|
|
- * completely sure nobody else modified the entry meanwhile and
|
|
- * fail if that's the case */
|
|
-
|
|
- /* commit changes */
|
|
- ret = ipapwd_apply_mods(data->dn, smods);
|
|
-
|
|
- LOG_TRACE("<= result: %d\n", ret);
|
|
-
|
|
-free_and_return:
|
|
- if (lm) slapi_ch_free((void **)&lm);
|
|
- if (nt) slapi_ch_free((void **)&nt);
|
|
- if (modtime) slapi_ch_free((void **)&modtime);
|
|
- slapi_mods_free(&smods);
|
|
- ipapwd_free_slapi_value_array(&svals);
|
|
- ipapwd_free_slapi_value_array(&ntvals);
|
|
- ipapwd_free_slapi_value_array(&pwvals);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-
|
|
-Slapi_Value **ipapwd_setPasswordHistory(Slapi_Mods *smods,
|
|
- struct ipapwd_data *data)
|
|
-{
|
|
- Slapi_Value **pH = NULL;
|
|
- char **pwd_history = NULL;
|
|
- char **new_pwd_history = NULL;
|
|
- int n = 0;
|
|
- int ret;
|
|
- int i;
|
|
-
|
|
- pwd_history = slapi_entry_attr_get_charray(data->target,
|
|
- "passwordHistory");
|
|
-
|
|
- ret = ipapwd_generate_new_history(data->password, data->timeNow,
|
|
- data->policy.history_length,
|
|
- pwd_history, &new_pwd_history, &n);
|
|
-
|
|
- if (ret && data->policy.history_length) {
|
|
- LOG_FATAL("failed to generate new password history!\n");
|
|
- goto done;
|
|
- }
|
|
-
|
|
- pH = (Slapi_Value **)slapi_ch_calloc(n + 1, sizeof(Slapi_Value *));
|
|
- if (!pH) {
|
|
- LOG_OOM();
|
|
- goto done;
|
|
- }
|
|
-
|
|
- for (i = 0; i < n; i++) {
|
|
- pH[i] = slapi_value_new_string(new_pwd_history[i]);
|
|
- if (!pH[i]) {
|
|
- ipapwd_free_slapi_value_array(&pH);
|
|
- LOG_OOM();
|
|
- goto done;
|
|
- }
|
|
- }
|
|
-
|
|
-done:
|
|
- slapi_ch_array_free(pwd_history);
|
|
- for (i = 0; i < n; i++) {
|
|
- free(new_pwd_history[i]);
|
|
- }
|
|
- free(new_pwd_history);
|
|
- return pH;
|
|
-}
|
|
-
|
|
-/* Construct Mods pblock and perform the modify operation
|
|
- * Sets result of operation in SLAPI_PLUGIN_INTOP_RESULT
|
|
- */
|
|
-int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods)
|
|
-{
|
|
- Slapi_PBlock *pb;
|
|
- int ret;
|
|
-
|
|
- LOG_TRACE("=>\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) {
|
|
- LOG_TRACE("WARNING: modify error %d on entry '%s'\n", ret, dn);
|
|
- } else {
|
|
-
|
|
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
|
|
-
|
|
- if (ret != LDAP_SUCCESS){
|
|
- LOG_TRACE("WARNING: modify error %d on entry '%s'\n", ret, dn);
|
|
- } else {
|
|
- LOG_TRACE("<= Successful\n");
|
|
- }
|
|
- }
|
|
-
|
|
- slapi_pblock_destroy(pb);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-int ipapwd_set_extradata(const char *dn,
|
|
- const char *principal,
|
|
- time_t unixtime)
|
|
-{
|
|
- Slapi_Mods *smods;
|
|
- Slapi_Value *va[2] = { NULL };
|
|
- struct berval bv;
|
|
- char *xdata;
|
|
- int xd_len;
|
|
- int p_len;
|
|
- int ret;
|
|
-
|
|
- p_len = strlen(principal);
|
|
- xd_len = 2 + 4 + p_len + 1;
|
|
- xdata = malloc(xd_len);
|
|
- if (!xdata) {
|
|
- return LDAP_OPERATIONS_ERROR;
|
|
- }
|
|
-
|
|
- smods = slapi_mods_new();
|
|
-
|
|
- /* data type id */
|
|
- xdata[0] = 0x00;
|
|
- xdata[1] = 0x02;
|
|
-
|
|
- /* unix timestamp in Little Endian */
|
|
- xdata[2] = unixtime & 0xff;
|
|
- xdata[3] = (unixtime & 0xff00) >> 8;
|
|
- xdata[4] = (unixtime & 0xff0000) >> 16;
|
|
- xdata[5] = (unixtime & 0xff000000) >> 24;
|
|
-
|
|
- /* append the principal name */
|
|
- strncpy(&xdata[6], principal, p_len);
|
|
-
|
|
- xdata[xd_len -1] = 0;
|
|
-
|
|
- bv.bv_val = xdata;
|
|
- bv.bv_len = xd_len;
|
|
- va[0] = slapi_value_new_berval(&bv);
|
|
-
|
|
- slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "krbExtraData", va);
|
|
-
|
|
- ret = ipapwd_apply_mods(dn, smods);
|
|
-
|
|
- slapi_value_free(&va[0]);
|
|
- slapi_mods_free(&smods);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-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);
|
|
-}
|
|
-
|
|
-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;
|
|
-};
|
|
-
|
|
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_encoding.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_encoding.c
|
|
deleted file mode 100644
|
|
index a92eaf0..0000000
|
|
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_encoding.c
|
|
+++ /dev/null
|
|
@@ -1,291 +0,0 @@
|
|
-/** BEGIN COPYRIGHT BLOCK
|
|
- * This program is free software; you can redistribute it and/or modify
|
|
- * it under the terms of the GNU General Public License as published by
|
|
- * the Free Software Foundation, either version 3 of the License, or
|
|
- * (at your option) any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * You should have received a copy of the GNU General Public License
|
|
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
- *
|
|
- * Additional permission under GPLv3 section 7:
|
|
- *
|
|
- * In the following paragraph, "GPL" means the GNU General Public
|
|
- * License, version 3 or any later version, and "Non-GPL Code" means
|
|
- * code that is governed neither by the GPL nor a license
|
|
- * compatible with the GPL.
|
|
- *
|
|
- * You may link the code of this Program with Non-GPL Code and convey
|
|
- * linked combinations including the two, provided that such Non-GPL
|
|
- * Code only links to the code of this Program through those well
|
|
- * defined interfaces identified in the file named EXCEPTION found in
|
|
- * the source code files (the "Approved Interfaces"). The files of
|
|
- * Non-GPL Code may instantiate templates or use macros or inline
|
|
- * functions from the Approved Interfaces without causing the resulting
|
|
- * work to be covered by the GPL. Only the copyright holders of this
|
|
- * Program may make changes or additions to the list of Approved
|
|
- * Interfaces.
|
|
- *
|
|
- * Authors:
|
|
- * Simo Sorce <ssorce@redhat.com>
|
|
- *
|
|
- * Copyright (C) 2007-2010 Red Hat, Inc.
|
|
- * All rights reserved.
|
|
- * END COPYRIGHT BLOCK **/
|
|
-
|
|
-#ifdef HAVE_CONFIG_H
|
|
-# include <config.h>
|
|
-#endif
|
|
-
|
|
-#include <stdio.h>
|
|
-#include <string.h>
|
|
-#include <sys/types.h>
|
|
-#include <sys/stat.h>
|
|
-#include <fcntl.h>
|
|
-#include <unistd.h>
|
|
-
|
|
-#include <dirsrv/slapi-plugin.h>
|
|
-#include <lber.h>
|
|
-#include <time.h>
|
|
-
|
|
-#include <endian.h>
|
|
-
|
|
-#include "ipapwd.h"
|
|
-#include "util.h"
|
|
-#include "ipa_krb5.h"
|
|
-
|
|
-/* 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
|
|
-
|
|
-/* 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];
|
|
- }
|
|
-}
|
|
-
|
|
-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++) {
|
|
- free(kset->keys[i].key_data_contents[0]);
|
|
- free(kset->keys[i].key_data_contents[1]);
|
|
- }
|
|
- free(kset->keys);
|
|
- free(kset);
|
|
- *pkset = NULL;
|
|
-}
|
|
-
|
|
-static Slapi_Value **encrypt_encode_key(struct ipapwd_krbcfg *krbcfg,
|
|
- struct ipapwd_data *data,
|
|
- char **errMesg)
|
|
-{
|
|
- krb5_context krbctx;
|
|
- char *krbPrincipalName = NULL;
|
|
- int kvno;
|
|
- struct berval *bval = NULL;
|
|
- Slapi_Value **svals = NULL;
|
|
- krb5_principal princ = NULL;
|
|
- 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) {
|
|
- LOG_OOM();
|
|
- 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";
|
|
- LOG_FATAL("%s", *errMesg);
|
|
- goto enc_error;
|
|
- }
|
|
-
|
|
- krberr = krb5_parse_name(krbctx, krbPrincipalName, &princ);
|
|
- if (krberr) {
|
|
- LOG_FATAL("krb5_parse_name failed [%s]\n",
|
|
- krb5_get_error_message(krbctx, krberr));
|
|
- goto enc_error;
|
|
- }
|
|
-
|
|
- pwd.data = (char *)data->password;
|
|
- pwd.length = strlen(data->password);
|
|
-
|
|
- kset = malloc(sizeof(struct ipapwd_keyset));
|
|
- if (!kset) {
|
|
- LOG_OOM();
|
|
- 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) */
|
|
- kvno += 1;
|
|
- kset->mkvno = krbcfg->mkvno;
|
|
-
|
|
- krberr = ipa_krb5_generate_key_data(krbctx, princ,
|
|
- pwd, kvno, krbcfg->kmkey,
|
|
- krbcfg->num_pref_encsalts,
|
|
- krbcfg->pref_encsalts,
|
|
- &kset->num_keys, &kset->keys);
|
|
- if (krberr != 0) {
|
|
- LOG_FATAL("generating kerberos keys failed [%s]\n",
|
|
- krb5_get_error_message(krbctx, krberr));
|
|
- goto enc_error;
|
|
- }
|
|
-
|
|
- krberr = ber_encode_krb5_key_data(kset->keys, kset->num_keys,
|
|
- kset->mkvno, &bval);
|
|
- if (krberr != 0) {
|
|
- LOG_FATAL("encoding krb5_key_data failed\n");
|
|
- goto enc_error;
|
|
- }
|
|
-
|
|
- svals[0] = slapi_value_new_berval(bval);
|
|
- if (!svals[0]) {
|
|
- LOG_FATAL("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;
|
|
-}
|
|
-
|
|
-int ipapwd_gen_hashes(struct ipapwd_krbcfg *krbcfg,
|
|
- struct ipapwd_data *data, char *userpw,
|
|
- int is_krb, int is_smb, int is_ipant, Slapi_Value ***svals,
|
|
- char **nthash, char **lmhash, Slapi_Value ***ntvals,
|
|
- char **errMesg)
|
|
-{
|
|
- int rc;
|
|
- char *userpw_uc = NULL;
|
|
-
|
|
- *svals = NULL;
|
|
- *nthash = NULL;
|
|
- *lmhash = NULL;
|
|
- *errMesg = NULL;
|
|
-
|
|
- if (is_krb) {
|
|
-
|
|
- *svals = encrypt_encode_key(krbcfg, data, errMesg);
|
|
-
|
|
- if (!*svals) {
|
|
- /* errMesg should have been set in encrypt_encode_key() */
|
|
- LOG_FATAL("key encryption/encoding failed\n");
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
- }
|
|
-
|
|
- if (is_smb || is_ipant) {
|
|
- char lm[33], nt[33];
|
|
- struct ntlm_keys ntlm;
|
|
- int ret;
|
|
-
|
|
- userpw_uc = (char *) slapi_utf8StrToUpper((unsigned char *) userpw);
|
|
- if (!userpw_uc) {
|
|
- *errMesg = "Failed to generate upper case password\n";
|
|
- LOG_FATAL("%s", *errMesg);
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- ret = encode_ntlm_keys(userpw,
|
|
- userpw_uc,
|
|
- krbcfg->allow_lm_hash,
|
|
- krbcfg->allow_nt_hash,
|
|
- &ntlm);
|
|
- memset(userpw_uc, 0, strlen(userpw_uc));
|
|
- slapi_ch_free_string(&userpw_uc);
|
|
- if (ret) {
|
|
- *errMesg = "Failed to generate NT/LM hashes\n";
|
|
- LOG_FATAL("%s", *errMesg);
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
- if (krbcfg->allow_lm_hash) {
|
|
- hexbuf(lm, ntlm.lm);
|
|
- lm[32] = '\0';
|
|
- *lmhash = slapi_ch_strdup(lm);
|
|
- }
|
|
- if (krbcfg->allow_nt_hash) {
|
|
- hexbuf(nt, ntlm.nt);
|
|
- nt[32] = '\0';
|
|
- *nthash = slapi_ch_strdup(nt);
|
|
- }
|
|
-
|
|
- if (is_ipant) {
|
|
- *ntvals = (Slapi_Value **)calloc(2, sizeof(Slapi_Value *));
|
|
- if (!*ntvals) {
|
|
- LOG_OOM();
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
- (*ntvals)[0] = slapi_value_new();
|
|
- if (slapi_value_set((*ntvals)[0], ntlm.nt, 16) == NULL) {
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- rc = LDAP_SUCCESS;
|
|
-
|
|
-done:
|
|
-
|
|
- /* when error, free possibly allocated output parameters */
|
|
- if (rc) {
|
|
- ipapwd_free_slapi_value_array(svals);
|
|
- ipapwd_free_slapi_value_array(ntvals);
|
|
- }
|
|
-
|
|
- return rc;
|
|
-}
|
|
-
|
|
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c
|
|
deleted file mode 100644
|
|
index 0318cec..0000000
|
|
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c
|
|
+++ /dev/null
|
|
@@ -1,1349 +0,0 @@
|
|
-/** BEGIN COPYRIGHT BLOCK
|
|
- * This program is free software; you can redistribute it and/or modify
|
|
- * it under the terms of the GNU General Public License as published by
|
|
- * the Free Software Foundation, either version 3 of the License, or
|
|
- * (at your option) any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * You should have received a copy of the GNU General Public License
|
|
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
- *
|
|
- * Additional permission under GPLv3 section 7:
|
|
- *
|
|
- * In the following paragraph, "GPL" means the GNU General Public
|
|
- * License, version 3 or any later version, and "Non-GPL Code" means
|
|
- * code that is governed neither by the GPL nor a license
|
|
- * compatible with the GPL.
|
|
- *
|
|
- * You may link the code of this Program with Non-GPL Code and convey
|
|
- * linked combinations including the two, provided that such Non-GPL
|
|
- * Code only links to the code of this Program through those well
|
|
- * defined interfaces identified in the file named EXCEPTION found in
|
|
- * the source code files (the "Approved Interfaces"). The files of
|
|
- * Non-GPL Code may instantiate templates or use macros or inline
|
|
- * functions from the Approved Interfaces without causing the resulting
|
|
- * work to be covered by the GPL. Only the copyright holders of this
|
|
- * Program may make changes or additions to the list of Approved
|
|
- * Interfaces.
|
|
- *
|
|
- * Authors:
|
|
- * Simo Sorce <ssorce@redhat.com>
|
|
- *
|
|
- * Copyright (C) 2007-2010 Red Hat, Inc.
|
|
- * All rights reserved.
|
|
- * END COPYRIGHT BLOCK **/
|
|
-
|
|
-#ifdef HAVE_CONFIG_H
|
|
-# include <config.h>
|
|
-#endif
|
|
-
|
|
-/* strptime needs _XOPEN_SOURCE and endian.h needs __USE_BSD
|
|
- * _GNU_SOURCE imply both, and we use it elsewhere, so use this */
|
|
-#ifndef _GNU_SOURCE
|
|
-#define _GNU_SOURCE 1
|
|
-#endif
|
|
-
|
|
-#include <stdio.h>
|
|
-#include <string.h>
|
|
-#include <strings.h>
|
|
-#include <sys/types.h>
|
|
-#include <sys/stat.h>
|
|
-#include <fcntl.h>
|
|
-#include <unistd.h>
|
|
-
|
|
-#include <dirsrv/slapi-plugin.h>
|
|
-#include <lber.h>
|
|
-#include <time.h>
|
|
-#include <endian.h>
|
|
-
|
|
-#include "ipapwd.h"
|
|
-#include "util.h"
|
|
-
|
|
-#define IPAPWD_OP_NULL 0
|
|
-#define IPAPWD_OP_ADD 1
|
|
-#define IPAPWD_OP_MOD 2
|
|
-
|
|
-extern Slapi_PluginDesc ipapwd_plugin_desc;
|
|
-extern void *ipapwd_plugin_id;
|
|
-extern const char *ipa_realm_tree;
|
|
-
|
|
-/* 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 */
|
|
-};
|
|
-/*****************************************************************************
|
|
- * pre/post operations to intercept writes to userPassword
|
|
- ****************************************************************************/
|
|
-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);
|
|
-}
|
|
-
|
|
-int ipapwd_ext_init(void)
|
|
-{
|
|
- 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_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) {
|
|
- LOG_OOM();
|
|
- goto done;
|
|
- }
|
|
-
|
|
- ret = ipapwd_getEntry(dn, &entry, (char **) attrs_list);
|
|
- if (ret) {
|
|
- LOG("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, is_ipant;
|
|
- int ret;
|
|
- int rc = LDAP_SUCCESS;
|
|
-
|
|
- LOG_TRACE("=>\n");
|
|
-
|
|
- ret = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl_op);
|
|
- if (ret != 0) {
|
|
- LOG_FATAL("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) {
|
|
- LOG_OOM();
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
- slapi_ch_free_string(&userpw);
|
|
- userpw = tmp;
|
|
- } else if (slapi_is_encoded(userpw)) {
|
|
- const char *userpw_clear = NULL;
|
|
- Slapi_Value **pwvals = NULL;
|
|
-
|
|
- /* Try to get clear password from an entry extension.
|
|
- * This function does not return a copy of the values,
|
|
- * no need to free them. */
|
|
- rc = slapi_pw_get_entry_ext(e, &pwvals);
|
|
- if (LDAP_SUCCESS == rc) {
|
|
- userpw_clear = slapi_value_get_string(pwvals[0]);
|
|
- }
|
|
-
|
|
- /* Fail if we did not get a real clear text password from
|
|
- * the extension. This will happen if the password is hashed. */
|
|
- if (!userpw_clear || (0 == strcmp(userpw, userpw_clear))) {
|
|
- rc = LDAP_CONSTRAINT_VIOLATION;
|
|
- slapi_ch_free_string(&userpw);
|
|
- } else {
|
|
- userpw = slapi_ch_strdup(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) {
|
|
- LOG("no ipaMigrationEnabled in config, assuming FALSE\n");
|
|
- } else if (0 == strcmp(enabled, "TRUE")) {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- LOG("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, &is_ipant,
|
|
- 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) {
|
|
- LOG_FATAL("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 = ipapwd_error2string(ret);
|
|
- rc = LDAP_CONSTRAINT_VIOLATION;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- if (is_krb || is_smb || is_ipant) {
|
|
-
|
|
- Slapi_Value **svals = NULL;
|
|
- Slapi_Value **ntvals = NULL;
|
|
- char *nt = NULL;
|
|
- char *lm = NULL;
|
|
-
|
|
- pwdop->is_krb = is_krb;
|
|
-
|
|
- rc = ipapwd_gen_hashes(krbcfg, &pwdop->pwdata,
|
|
- userpw, is_krb, is_smb, is_ipant,
|
|
- &svals, &nt, &lm, &ntvals, &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) {
|
|
- LOG_FATAL("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 && is_smb) {
|
|
- /* set value */
|
|
- slapi_entry_attr_set_charptr(e, "sambaLMPassword", lm);
|
|
- slapi_ch_free_string(&lm);
|
|
- }
|
|
- if (nt && is_smb) {
|
|
- /* set value */
|
|
- slapi_entry_attr_set_charptr(e, "sambaNTPassword", nt);
|
|
- slapi_ch_free_string(&nt);
|
|
- }
|
|
-
|
|
- if (ntvals && is_ipant) {
|
|
- slapi_entry_attr_replace_sv(e, "ipaNTHash", ntvals);
|
|
- ipapwd_free_slapi_value_array(&ntvals);
|
|
- }
|
|
-
|
|
- if (is_smb) {
|
|
- /* with samba integration we need to also set sambaPwdLastSet or
|
|
- * samba will decide the user has to change the password again */
|
|
- if (pwdop->pwdata.changetype == IPA_CHANGETYPE_ADMIN) {
|
|
- /* if it is an admin change instead we need to let know to
|
|
- * samba as well that the use rmust change its password */
|
|
- slapi_entry_attr_set_long(e, "sambaPwdLastset", 0L);
|
|
- } else {
|
|
- slapi_entry_attr_set_long(e, "sambaPwdLastset",
|
|
- (long)pwdop->pwdata.timeNow);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- 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;
|
|
-}
|
|
-
|
|
-#define NTHASH_REGEN_VAL "MagicRegen"
|
|
-#define NTHASH_REGEN_LEN sizeof(NTHASH_REGEN_VAL)
|
|
-static int ipapwd_regen_nthash(Slapi_PBlock *pb, Slapi_Mods *smods,
|
|
- char *dn, struct slapi_entry *entry,
|
|
- struct ipapwd_krbcfg *krbcfg);
|
|
-
|
|
-/* 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;
|
|
- LDAPMod *lmod;
|
|
- 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, is_ipant;
|
|
- int has_krb_keys = 0;
|
|
- int has_history = 0;
|
|
- int gen_krb_keys = 0;
|
|
- int is_magic_regen = 0;
|
|
- int ret, rc;
|
|
-
|
|
- LOG_TRACE( "=>\n");
|
|
-
|
|
- ret = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl_op);
|
|
- if (ret != 0) {
|
|
- LOG_FATAL("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;
|
|
- lmod = slapi_mods_get_first_mod(smods);
|
|
- while (lmod) {
|
|
- struct berval *bv;
|
|
-
|
|
- if (slapi_attr_types_equivalent(lmod->mod_type, SLAPI_USERPWD_ATTR)) {
|
|
- /* check op filtering out LDAP_MOD_BVALUES */
|
|
- switch (lmod->mod_op & 0x0f) {
|
|
- case LDAP_MOD_ADD:
|
|
- case LDAP_MOD_REPLACE:
|
|
- is_pwd_op = 1;
|
|
- default:
|
|
- break;
|
|
- }
|
|
- } else if (slapi_attr_types_equivalent(lmod->mod_type, "ipaNTHash")) {
|
|
- /* check op filtering out LDAP_MOD_BVALUES */
|
|
- switch (lmod->mod_op & 0x0f) {
|
|
- case LDAP_MOD_ADD:
|
|
- if (!lmod->mod_bvalues ||
|
|
- !lmod->mod_bvalues[0]) {
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
- bv = lmod->mod_bvalues[0];
|
|
- if ((bv->bv_len >= NTHASH_REGEN_LEN -1) &&
|
|
- (bv->bv_len <= NTHASH_REGEN_LEN) &&
|
|
- (strncmp(NTHASH_REGEN_VAL,
|
|
- bv->bv_val, bv->bv_len) == 0)) {
|
|
- is_magic_regen = 1;
|
|
- /* make sure the database will later ignore this mod */
|
|
- slapi_mods_remove(smods);
|
|
- }
|
|
- default:
|
|
- break;
|
|
- }
|
|
- } else if (slapi_attr_types_equivalent(lmod->mod_type,
|
|
- "unhashed#user#password")) {
|
|
- /* 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
|
|
- * reason */
|
|
- if (!lmod->mod_bvalues ||
|
|
- !lmod->mod_bvalues[0]) {
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
- bv = lmod->mod_bvalues[0];
|
|
- slapi_ch_free_string(&unhashedpw);
|
|
- unhashedpw = slapi_ch_malloc(bv->bv_len+1);
|
|
- if (!unhashedpw) {
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
- memcpy(unhashedpw, bv->bv_val, bv->bv_len);
|
|
- unhashedpw[bv->bv_len] = '\0';
|
|
- }
|
|
- lmod = slapi_mods_get_next_mod(smods);
|
|
- }
|
|
-
|
|
- /* If userPassword is not modified check if this is a request to generate
|
|
- * NT hashes otherwise we are done here */
|
|
- if (!is_pwd_op && !is_magic_regen) {
|
|
- rc = LDAP_SUCCESS;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- /* OK we 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) {
|
|
- LOG("Failed to retrieve entry?!\n");
|
|
- rc = LDAP_NO_SUCH_OBJECT;
|
|
- goto done;
|
|
- }
|
|
- }
|
|
-
|
|
- rc = ipapwd_entry_checks(pb, e,
|
|
- &is_root, &is_krb, &is_smb, &is_ipant,
|
|
- SLAPI_USERPWD_ATTR, SLAPI_ACL_WRITE);
|
|
- if (rc) {
|
|
- goto done;
|
|
- }
|
|
-
|
|
- rc = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_DN);
|
|
- if (rc) {
|
|
- goto done;
|
|
- }
|
|
-
|
|
- if (!is_pwd_op) {
|
|
- /* This may be a magic op to ask us to generate the NT hashes */
|
|
- if (is_magic_regen) {
|
|
- /* Make sense to call only if this entry has krb keys to source
|
|
- * the nthash from */
|
|
- if (is_krb) {
|
|
- rc = ipapwd_regen_nthash(pb, smods, dn, e, krbcfg);
|
|
- } else {
|
|
- rc = LDAP_UNWILLING_TO_PERFORM;
|
|
- }
|
|
- } else {
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- }
|
|
- goto done;
|
|
- }
|
|
-
|
|
- /* run through the mods again and adjust flags if operations affect them */
|
|
- lmod = slapi_mods_get_first_mod(smods);
|
|
- while (lmod) {
|
|
- struct berval *bv;
|
|
-
|
|
- if (slapi_attr_types_equivalent(lmod->mod_type, SLAPI_USERPWD_ATTR)) {
|
|
- /* check op filtering out LDAP_MOD_BVALUES */
|
|
- switch (lmod->mod_op & 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;
|
|
- if (!lmod->mod_bvalues ||
|
|
- !lmod->mod_bvalues[0]) {
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
- bv = lmod->mod_bvalues[0];
|
|
- slapi_ch_free_string(&userpw);
|
|
- userpw = slapi_ch_malloc(bv->bv_len+1);
|
|
- if (!userpw) {
|
|
- 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 */
|
|
- if (!lmod->mod_bvalues ||
|
|
- !lmod->mod_bvalues[0]) {
|
|
- is_pwd_op = 0;
|
|
- } else {
|
|
- bv = lmod->mod_bvalues[0];
|
|
- if ((userpw &&
|
|
- strncmp(userpw, bv->bv_val, bv->bv_len) == 0) ||
|
|
- (unhashedpw &&
|
|
- strncmp(unhashedpw, bv->bv_val, bv->bv_len) == 0)) {
|
|
- is_pwd_op = 0;
|
|
- }
|
|
- }
|
|
- default:
|
|
- break;
|
|
- }
|
|
-
|
|
- } else if (slapi_attr_types_equivalent(lmod->mod_type,
|
|
- SLAPI_ATTR_OBJECTCLASS)) {
|
|
- int i;
|
|
- /* check op filtering out LDAP_MOD_BVALUES */
|
|
- switch (lmod->mod_op & 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;
|
|
- is_ipant = 0;
|
|
-
|
|
- case LDAP_MOD_ADD:
|
|
- if (!lmod->mod_bvalues ||
|
|
- !lmod->mod_bvalues[0]) {
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
- for (i = 0; (bv = lmod->mod_bvalues[i]) != NULL; i++) {
|
|
- if (strncasecmp("krbPrincipalAux",
|
|
- bv->bv_val, bv->bv_len) == 0) {
|
|
- is_krb = 1;
|
|
- } else if (strncasecmp("sambaSamAccount",
|
|
- bv->bv_val, bv->bv_len) == 0) {
|
|
- is_smb = 1;
|
|
- } else if (strncasecmp("ipaNTUserAttrs",
|
|
- bv->bv_val, bv->bv_len) == 0) {
|
|
- is_ipant = 1;
|
|
- }
|
|
- }
|
|
-
|
|
- break;
|
|
-
|
|
- case LDAP_MOD_DELETE:
|
|
- /* can this happen for objectclasses ? */
|
|
- is_krb = 0;
|
|
- is_smb = 0;
|
|
- is_ipant = 0;
|
|
-
|
|
- default:
|
|
- break;
|
|
- }
|
|
-
|
|
- } else if (slapi_attr_types_equivalent(lmod->mod_type,
|
|
- "krbPrincipalKey")) {
|
|
-
|
|
- /* if we are getting a krbPrincipalKey, also avoid regenerating
|
|
- * the keys, it means kadmin has alredy done the job and is simply
|
|
- * keeping userPassword and sambaXXPAssword in sync */
|
|
-
|
|
- /* we also check we have enough authority */
|
|
- if (is_root) {
|
|
- has_krb_keys = 1;
|
|
- }
|
|
-
|
|
- } else if (slapi_attr_types_equivalent(lmod->mod_type,
|
|
- "passwordHistory")) {
|
|
-
|
|
- /* if we are getting a passwordHistory, also avoid regenerating
|
|
- * the hashes, it means kadmin has alredy done the job and is
|
|
- * simply keeping userPassword and sambaXXPAssword in sync */
|
|
-
|
|
- /* we also check we have enough authority */
|
|
- if (is_root) {
|
|
- has_history = 1;
|
|
- }
|
|
- }
|
|
-
|
|
- lmod = slapi_mods_get_next_mod(smods);
|
|
- }
|
|
-
|
|
- if (is_krb) {
|
|
- if (has_krb_keys) {
|
|
- gen_krb_keys = 0;
|
|
- } else {
|
|
- gen_krb_keys = 1;
|
|
- }
|
|
- }
|
|
-
|
|
- /* 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 && (gen_krb_keys || is_smb || is_ipant)) {
|
|
- if ('{' == userpw[0]) {
|
|
- if (0 == strncasecmp(userpw, "{CLEAR}", strlen("{CLEAR}"))) {
|
|
- unhashedpw = slapi_ch_strdup(&userpw[strlen("{CLEAR}")]);
|
|
- if (NULL == unhashedpw) {
|
|
- LOG_OOM();
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- goto done;
|
|
- }
|
|
- slapi_ch_free_string(&userpw);
|
|
-
|
|
- } else if (slapi_is_encoded(userpw)) {
|
|
-
|
|
- LOG("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) {
|
|
- LOG_FATAL("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->is_krb = is_krb;
|
|
- pwdop->pwd_op = IPAPWD_OP_MOD;
|
|
- pwdop->pwdata.password = slapi_ch_strdup(unhashedpw);
|
|
- pwdop->pwdata.changetype = IPA_CHANGETYPE_NORMAL;
|
|
- pwdop->skip_history = has_history;
|
|
- pwdop->skip_keys = has_krb_keys;
|
|
-
|
|
- 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;
|
|
-
|
|
- /* if krb keys are being set by an external agent we assume password
|
|
- * policies have been properly checked already, so we check them only
|
|
- * if no krb keys are available */
|
|
- if (has_krb_keys == 0) {
|
|
- ret = ipapwd_CheckPolicy(&pwdop->pwdata);
|
|
- if (ret) {
|
|
- errMesg = ipapwd_error2string(ret);
|
|
- rc = LDAP_CONSTRAINT_VIOLATION;
|
|
- goto done;
|
|
- }
|
|
- }
|
|
-
|
|
- if (gen_krb_keys || is_smb || is_ipant) {
|
|
-
|
|
- Slapi_Value **svals = NULL;
|
|
- Slapi_Value **ntvals = NULL;
|
|
- char *nt = NULL;
|
|
- char *lm = NULL;
|
|
-
|
|
- rc = ipapwd_gen_hashes(krbcfg, &pwdop->pwdata, unhashedpw,
|
|
- gen_krb_keys, is_smb, is_ipant,
|
|
- &svals, &nt, &lm, &ntvals, &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 && is_smb) {
|
|
- /* replace value */
|
|
- slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
- "sambaLMPassword", lm);
|
|
- slapi_ch_free_string(&lm);
|
|
- }
|
|
- if (nt && is_smb) {
|
|
- /* replace value */
|
|
- slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
- "sambaNTPassword", nt);
|
|
- slapi_ch_free_string(&nt);
|
|
- }
|
|
-
|
|
- if (ntvals && is_ipant) {
|
|
- slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
|
- "ipaNTHash", ntvals);
|
|
- ipapwd_free_slapi_value_array(&ntvals);
|
|
- }
|
|
-
|
|
- if (is_smb) {
|
|
- /* with samba integration we need to also set sambaPwdLastSet or
|
|
- * samba will decide the user has to change the password again */
|
|
- if (pwdop->pwdata.changetype == IPA_CHANGETYPE_ADMIN) {
|
|
- /* if it is an admin change instead we need to let know to
|
|
- * samba as well that the use rmust change its password */
|
|
- slapi_entry_attr_set_long(e, "sambaPwdLastset", 0L);
|
|
- } else {
|
|
- slapi_entry_attr_set_long(e, "sambaPwdLastset",
|
|
- (long)pwdop->pwdata.timeNow);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- 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);
|
|
- if (slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods)) {
|
|
- LOG_FATAL("slapi_pblock_set failed!\n");
|
|
- rc = LDAP_OPERATIONS_ERROR;
|
|
- }
|
|
- 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_regen_nthash(Slapi_PBlock *pb, Slapi_Mods *smods,
|
|
- char *dn, struct slapi_entry *entry,
|
|
- struct ipapwd_krbcfg *krbcfg)
|
|
-{
|
|
- Slapi_Attr *attr;
|
|
- Slapi_Value *value;
|
|
- const struct berval *val;
|
|
- struct berval *ntvals[2] = { NULL, NULL };
|
|
- struct berval bval;
|
|
- krb5_key_data *keys;
|
|
- int num_keys;
|
|
- int mkvno;
|
|
- int ret;
|
|
- int i;
|
|
-
|
|
- ret = slapi_entry_attr_find(entry, "ipaNTHash", &attr);
|
|
- if (ret == 0) {
|
|
- /* We refuse to regen if there is already a value */
|
|
- return LDAP_CONSTRAINT_VIOLATION;
|
|
- }
|
|
-
|
|
- /* ok let's see if we can find the RC4 hash in the keys */
|
|
- ret = slapi_entry_attr_find(entry, "krbPrincipalKey", &attr);
|
|
- if (ret) {
|
|
- return LDAP_UNWILLING_TO_PERFORM;
|
|
- }
|
|
-
|
|
- ret = slapi_attr_first_value(attr, &value);
|
|
- if (ret) {
|
|
- return LDAP_OPERATIONS_ERROR;
|
|
- }
|
|
-
|
|
- val = slapi_value_get_berval(value);
|
|
- if (!val) {
|
|
- return LDAP_OPERATIONS_ERROR;
|
|
- }
|
|
-
|
|
- ret = ber_decode_krb5_key_data((struct berval *)val,
|
|
- &mkvno, &num_keys, &keys);
|
|
- if (ret) {
|
|
- return LDAP_OPERATIONS_ERROR;
|
|
- }
|
|
-
|
|
- ret = LDAP_UNWILLING_TO_PERFORM;
|
|
-
|
|
- for (i = 0; i < num_keys; i++) {
|
|
- char nthash[16];
|
|
- krb5_enc_data cipher;
|
|
- krb5_data plain;
|
|
- krb5_int16 t;
|
|
-
|
|
- if (keys[i].key_data_type[0] != ENCTYPE_ARCFOUR_HMAC) {
|
|
- continue;
|
|
- }
|
|
-
|
|
- memcpy(&t, keys[i].key_data_contents[0], 2);
|
|
- plain.length = le16toh(t);
|
|
- if (plain.length != 16) {
|
|
- continue;
|
|
- }
|
|
- plain.data = nthash;
|
|
-
|
|
- memset(&cipher, 0, sizeof(krb5_enc_data));
|
|
- cipher.enctype = krbcfg->kmkey->enctype;
|
|
- cipher.ciphertext.length = keys[i].key_data_length[0] - 2;
|
|
- cipher.ciphertext.data = ((char *)keys[i].key_data_contents[0]) + 2;
|
|
-
|
|
- ret = krb5_c_decrypt(krbcfg->krbctx, krbcfg->kmkey,
|
|
- 0, NULL, &cipher, &plain);
|
|
- if (ret) {
|
|
- ret = LDAP_OPERATIONS_ERROR;
|
|
- break;
|
|
- }
|
|
-
|
|
- bval.bv_val = nthash;
|
|
- bval.bv_len = 16;
|
|
- ntvals[0] = &bval;
|
|
-
|
|
- slapi_mods_add_modbvps(smods, LDAP_MOD_ADD, "ipaNTHash", ntvals);
|
|
-
|
|
- ret = LDAP_SUCCESS;
|
|
- break;
|
|
- }
|
|
-
|
|
- ipa_krb5_free_key_data(keys, num_keys);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-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;
|
|
- char *errMsg = "Internal operations error\n";
|
|
- struct ipapwd_krbcfg *krbcfg = NULL;
|
|
- char *principal = NULL;
|
|
- Slapi_Value *ipahost;
|
|
-
|
|
- LOG_TRACE("=>\n");
|
|
-
|
|
- /* time to get the operation handler */
|
|
- ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op);
|
|
- if (ret != 0) {
|
|
- LOG_FATAL("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) {
|
|
- LOG_FATAL("Internal error, couldn't find pluginextension ?!\n");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- /* not interesting */
|
|
- if (IPAPWD_OP_NULL == pwdop->pwd_op)
|
|
- return 0;
|
|
-
|
|
- if ( ! (pwdop->is_krb)) {
|
|
- LOG("Not a kerberos user, ignore krb attributes\n");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- if (pwdop->skip_keys && pwdop->skip_history) {
|
|
- /* nothing to do, caller already set all interesting attributes */
|
|
- return 0;
|
|
- }
|
|
-
|
|
- ret = ipapwd_gen_checks(pb, &errMsg, &krbcfg, 0);
|
|
- if (ret != 0) {
|
|
- LOG_FATAL("ipapwd_gen_checks failed!?\n");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- /* prepare changes that can be made only as root */
|
|
- smods = slapi_mods_new();
|
|
-
|
|
- /* 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 && !pwdop->skip_history) {
|
|
- 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) {
|
|
- LOG("Failed to retrieve entry?!\n");
|
|
- goto done;
|
|
- }
|
|
- }
|
|
- pwvals = ipapwd_setPasswordHistory(smods, &pwdop->pwdata);
|
|
- if (pwvals) {
|
|
- slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
|
- "passwordHistory", pwvals);
|
|
- }
|
|
- }
|
|
-
|
|
- /* we assume that krb attributes are properly updated too if keys were
|
|
- * passed in */
|
|
- if (!pwdop->skip_keys) {
|
|
- /* Don't set a last password change or expiration on host passwords.
|
|
- * krbLastPwdChange is used to tell whether we have a valid keytab.
|
|
- * If we set it on userPassword it confuses enrollment.
|
|
- * If krbPasswordExpiration is set on a host entry then the keytab
|
|
- * will appear to be expired.
|
|
- *
|
|
- * When a host is issued a keytab these attributes get set properly by
|
|
- * ipapwd_setkeytab().
|
|
- */
|
|
- ipahost = slapi_value_new_string("ipaHost");
|
|
- if (!pwdop->pwdata.target ||
|
|
- (slapi_entry_attr_has_syntax_value(pwdop->pwdata.target,
|
|
- SLAPI_ATTR_OBJECTCLASS, ipahost)) == 0) {
|
|
- /* set Password Expiration date */
|
|
- if (!gmtime_r(&(pwdop->pwdata.expireTime), &utctime)) {
|
|
- LOG_FATAL("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);
|
|
-
|
|
- /* change Last Password Change field with the current date */
|
|
- if (!gmtime_r(&(pwdop->pwdata.timeNow), &utctime)) {
|
|
- LOG_FATAL("failed to parse current date (buggy gmtime_r ?)\n");
|
|
- slapi_value_free(&ipahost);
|
|
- 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);
|
|
- }
|
|
- slapi_value_free(&ipahost);
|
|
- }
|
|
-
|
|
- ret = ipapwd_apply_mods(pwdop->pwdata.dn, smods);
|
|
- if (ret)
|
|
- LOG("Failed to set additional password attributes in the post-op!\n");
|
|
-
|
|
- if (!pwdop->skip_keys) {
|
|
- if (pwdop->pwdata.changetype == IPA_CHANGETYPE_NORMAL) {
|
|
- principal = slapi_entry_attr_get_charptr(pwdop->pwdata.target,
|
|
- "krbPrincipalName");
|
|
- } else {
|
|
- principal = slapi_ch_smprintf("root/admin@%s", krbcfg->realm);
|
|
- }
|
|
- ipapwd_set_extradata(pwdop->pwdata.dn, principal, pwdop->pwdata.timeNow);
|
|
- }
|
|
-
|
|
-done:
|
|
- if (pwdop && pwdop->pwdata.target) slapi_entry_free(pwdop->pwdata.target);
|
|
- slapi_mods_free(&smods);
|
|
- slapi_ch_free_string(&principal);
|
|
- free_ipapwd_krbcfg(&krbcfg);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-/* 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;
|
|
- 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;
|
|
- char *principal = NULL;
|
|
-
|
|
- LOG_TRACE("=>\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) {
|
|
- LOG_FATAL("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) {
|
|
- LOG("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) {
|
|
- LOG("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) {
|
|
- LOG("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) {
|
|
- LOG("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 */
|
|
- LOG_OOM();
|
|
- 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) {
|
|
- LOG("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) {
|
|
- LOG_FATAL("Generic 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) {
|
|
- LOG_FATAL("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. */
|
|
- LOG("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) {
|
|
- LOG("failed to set kerberos key for user entry: %s\n", dn);
|
|
- goto done;
|
|
- }
|
|
-
|
|
- /* we need to make sure the ExtraData is set, otherwise kadmin
|
|
- * will not like the object */
|
|
- principal = slapi_entry_attr_get_charptr(entry, "krbPrincipalName");
|
|
- if (!principal) {
|
|
- LOG_OOM();
|
|
- goto done;
|
|
- }
|
|
- ipapwd_set_extradata(pwdata.dn, principal, pwdata.timeNow);
|
|
-
|
|
- LOG("kerberos key generated for user entry: %s\n", dn);
|
|
-
|
|
-done:
|
|
- slapi_ch_free_string(&principal);
|
|
- slapi_ch_free_string(&expire);
|
|
- if (entry)
|
|
- slapi_entry_free(entry);
|
|
- free_ipapwd_krbcfg(&krbcfg);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-
|
|
-
|
|
-/* Init pre ops */
|
|
-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 *)&ipapwd_plugin_desc);
|
|
- 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;
|
|
-}
|
|
-
|
|
-int ipapwd_pre_init_betxn(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 *)&ipapwd_plugin_desc);
|
|
- if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_PRE_ADD_FN, (void *)ipapwd_pre_add);
|
|
- if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN, (void *)ipapwd_pre_mod);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-/* Init post ops */
|
|
-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 *)&ipapwd_plugin_desc);
|
|
- 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;
|
|
-}
|
|
-
|
|
-int ipapwd_post_init_betxn(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 *)&ipapwd_plugin_desc);
|
|
- if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_POST_ADD_FN, (void *)ipapwd_post_op);
|
|
- if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN, (void *)ipapwd_post_op);
|
|
-
|
|
- return ret;
|
|
-}
|
|
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
|
new file mode 100644
|
|
index 0000000..0318cec
|
|
--- /dev/null
|
|
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
|
@@ -0,0 +1,1349 @@
|
|
+/** BEGIN COPYRIGHT BLOCK
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
+ *
|
|
+ * Additional permission under GPLv3 section 7:
|
|
+ *
|
|
+ * In the following paragraph, "GPL" means the GNU General Public
|
|
+ * License, version 3 or any later version, and "Non-GPL Code" means
|
|
+ * code that is governed neither by the GPL nor a license
|
|
+ * compatible with the GPL.
|
|
+ *
|
|
+ * You may link the code of this Program with Non-GPL Code and convey
|
|
+ * linked combinations including the two, provided that such Non-GPL
|
|
+ * Code only links to the code of this Program through those well
|
|
+ * defined interfaces identified in the file named EXCEPTION found in
|
|
+ * the source code files (the "Approved Interfaces"). The files of
|
|
+ * Non-GPL Code may instantiate templates or use macros or inline
|
|
+ * functions from the Approved Interfaces without causing the resulting
|
|
+ * work to be covered by the GPL. Only the copyright holders of this
|
|
+ * Program may make changes or additions to the list of Approved
|
|
+ * Interfaces.
|
|
+ *
|
|
+ * Authors:
|
|
+ * Simo Sorce <ssorce@redhat.com>
|
|
+ *
|
|
+ * Copyright (C) 2007-2010 Red Hat, Inc.
|
|
+ * All rights reserved.
|
|
+ * END COPYRIGHT BLOCK **/
|
|
+
|
|
+#ifdef HAVE_CONFIG_H
|
|
+# include <config.h>
|
|
+#endif
|
|
+
|
|
+/* strptime needs _XOPEN_SOURCE and endian.h needs __USE_BSD
|
|
+ * _GNU_SOURCE imply both, and we use it elsewhere, so use this */
|
|
+#ifndef _GNU_SOURCE
|
|
+#define _GNU_SOURCE 1
|
|
+#endif
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <strings.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include <dirsrv/slapi-plugin.h>
|
|
+#include <lber.h>
|
|
+#include <time.h>
|
|
+#include <endian.h>
|
|
+
|
|
+#include "ipapwd.h"
|
|
+#include "util.h"
|
|
+
|
|
+#define IPAPWD_OP_NULL 0
|
|
+#define IPAPWD_OP_ADD 1
|
|
+#define IPAPWD_OP_MOD 2
|
|
+
|
|
+extern Slapi_PluginDesc ipapwd_plugin_desc;
|
|
+extern void *ipapwd_plugin_id;
|
|
+extern const char *ipa_realm_tree;
|
|
+
|
|
+/* 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 */
|
|
+};
|
|
+/*****************************************************************************
|
|
+ * pre/post operations to intercept writes to userPassword
|
|
+ ****************************************************************************/
|
|
+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);
|
|
+}
|
|
+
|
|
+int ipapwd_ext_init(void)
|
|
+{
|
|
+ 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_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) {
|
|
+ LOG_OOM();
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = ipapwd_getEntry(dn, &entry, (char **) attrs_list);
|
|
+ if (ret) {
|
|
+ LOG("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, is_ipant;
|
|
+ int ret;
|
|
+ int rc = LDAP_SUCCESS;
|
|
+
|
|
+ LOG_TRACE("=>\n");
|
|
+
|
|
+ ret = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl_op);
|
|
+ if (ret != 0) {
|
|
+ LOG_FATAL("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) {
|
|
+ LOG_OOM();
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+ slapi_ch_free_string(&userpw);
|
|
+ userpw = tmp;
|
|
+ } else if (slapi_is_encoded(userpw)) {
|
|
+ const char *userpw_clear = NULL;
|
|
+ Slapi_Value **pwvals = NULL;
|
|
+
|
|
+ /* Try to get clear password from an entry extension.
|
|
+ * This function does not return a copy of the values,
|
|
+ * no need to free them. */
|
|
+ rc = slapi_pw_get_entry_ext(e, &pwvals);
|
|
+ if (LDAP_SUCCESS == rc) {
|
|
+ userpw_clear = slapi_value_get_string(pwvals[0]);
|
|
+ }
|
|
+
|
|
+ /* Fail if we did not get a real clear text password from
|
|
+ * the extension. This will happen if the password is hashed. */
|
|
+ if (!userpw_clear || (0 == strcmp(userpw, userpw_clear))) {
|
|
+ rc = LDAP_CONSTRAINT_VIOLATION;
|
|
+ slapi_ch_free_string(&userpw);
|
|
+ } else {
|
|
+ userpw = slapi_ch_strdup(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) {
|
|
+ LOG("no ipaMigrationEnabled in config, assuming FALSE\n");
|
|
+ } else if (0 == strcmp(enabled, "TRUE")) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ LOG("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, &is_ipant,
|
|
+ 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) {
|
|
+ LOG_FATAL("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 = ipapwd_error2string(ret);
|
|
+ rc = LDAP_CONSTRAINT_VIOLATION;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (is_krb || is_smb || is_ipant) {
|
|
+
|
|
+ Slapi_Value **svals = NULL;
|
|
+ Slapi_Value **ntvals = NULL;
|
|
+ char *nt = NULL;
|
|
+ char *lm = NULL;
|
|
+
|
|
+ pwdop->is_krb = is_krb;
|
|
+
|
|
+ rc = ipapwd_gen_hashes(krbcfg, &pwdop->pwdata,
|
|
+ userpw, is_krb, is_smb, is_ipant,
|
|
+ &svals, &nt, &lm, &ntvals, &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) {
|
|
+ LOG_FATAL("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 && is_smb) {
|
|
+ /* set value */
|
|
+ slapi_entry_attr_set_charptr(e, "sambaLMPassword", lm);
|
|
+ slapi_ch_free_string(&lm);
|
|
+ }
|
|
+ if (nt && is_smb) {
|
|
+ /* set value */
|
|
+ slapi_entry_attr_set_charptr(e, "sambaNTPassword", nt);
|
|
+ slapi_ch_free_string(&nt);
|
|
+ }
|
|
+
|
|
+ if (ntvals && is_ipant) {
|
|
+ slapi_entry_attr_replace_sv(e, "ipaNTHash", ntvals);
|
|
+ ipapwd_free_slapi_value_array(&ntvals);
|
|
+ }
|
|
+
|
|
+ if (is_smb) {
|
|
+ /* with samba integration we need to also set sambaPwdLastSet or
|
|
+ * samba will decide the user has to change the password again */
|
|
+ if (pwdop->pwdata.changetype == IPA_CHANGETYPE_ADMIN) {
|
|
+ /* if it is an admin change instead we need to let know to
|
|
+ * samba as well that the use rmust change its password */
|
|
+ slapi_entry_attr_set_long(e, "sambaPwdLastset", 0L);
|
|
+ } else {
|
|
+ slapi_entry_attr_set_long(e, "sambaPwdLastset",
|
|
+ (long)pwdop->pwdata.timeNow);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+}
|
|
+
|
|
+#define NTHASH_REGEN_VAL "MagicRegen"
|
|
+#define NTHASH_REGEN_LEN sizeof(NTHASH_REGEN_VAL)
|
|
+static int ipapwd_regen_nthash(Slapi_PBlock *pb, Slapi_Mods *smods,
|
|
+ char *dn, struct slapi_entry *entry,
|
|
+ struct ipapwd_krbcfg *krbcfg);
|
|
+
|
|
+/* 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;
|
|
+ LDAPMod *lmod;
|
|
+ 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, is_ipant;
|
|
+ int has_krb_keys = 0;
|
|
+ int has_history = 0;
|
|
+ int gen_krb_keys = 0;
|
|
+ int is_magic_regen = 0;
|
|
+ int ret, rc;
|
|
+
|
|
+ LOG_TRACE( "=>\n");
|
|
+
|
|
+ ret = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl_op);
|
|
+ if (ret != 0) {
|
|
+ LOG_FATAL("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;
|
|
+ lmod = slapi_mods_get_first_mod(smods);
|
|
+ while (lmod) {
|
|
+ struct berval *bv;
|
|
+
|
|
+ if (slapi_attr_types_equivalent(lmod->mod_type, SLAPI_USERPWD_ATTR)) {
|
|
+ /* check op filtering out LDAP_MOD_BVALUES */
|
|
+ switch (lmod->mod_op & 0x0f) {
|
|
+ case LDAP_MOD_ADD:
|
|
+ case LDAP_MOD_REPLACE:
|
|
+ is_pwd_op = 1;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ } else if (slapi_attr_types_equivalent(lmod->mod_type, "ipaNTHash")) {
|
|
+ /* check op filtering out LDAP_MOD_BVALUES */
|
|
+ switch (lmod->mod_op & 0x0f) {
|
|
+ case LDAP_MOD_ADD:
|
|
+ if (!lmod->mod_bvalues ||
|
|
+ !lmod->mod_bvalues[0]) {
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+ bv = lmod->mod_bvalues[0];
|
|
+ if ((bv->bv_len >= NTHASH_REGEN_LEN -1) &&
|
|
+ (bv->bv_len <= NTHASH_REGEN_LEN) &&
|
|
+ (strncmp(NTHASH_REGEN_VAL,
|
|
+ bv->bv_val, bv->bv_len) == 0)) {
|
|
+ is_magic_regen = 1;
|
|
+ /* make sure the database will later ignore this mod */
|
|
+ slapi_mods_remove(smods);
|
|
+ }
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ } else if (slapi_attr_types_equivalent(lmod->mod_type,
|
|
+ "unhashed#user#password")) {
|
|
+ /* 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
|
|
+ * reason */
|
|
+ if (!lmod->mod_bvalues ||
|
|
+ !lmod->mod_bvalues[0]) {
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+ bv = lmod->mod_bvalues[0];
|
|
+ slapi_ch_free_string(&unhashedpw);
|
|
+ unhashedpw = slapi_ch_malloc(bv->bv_len+1);
|
|
+ if (!unhashedpw) {
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+ memcpy(unhashedpw, bv->bv_val, bv->bv_len);
|
|
+ unhashedpw[bv->bv_len] = '\0';
|
|
+ }
|
|
+ lmod = slapi_mods_get_next_mod(smods);
|
|
+ }
|
|
+
|
|
+ /* If userPassword is not modified check if this is a request to generate
|
|
+ * NT hashes otherwise we are done here */
|
|
+ if (!is_pwd_op && !is_magic_regen) {
|
|
+ rc = LDAP_SUCCESS;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* OK we 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) {
|
|
+ LOG("Failed to retrieve entry?!\n");
|
|
+ rc = LDAP_NO_SUCH_OBJECT;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ rc = ipapwd_entry_checks(pb, e,
|
|
+ &is_root, &is_krb, &is_smb, &is_ipant,
|
|
+ SLAPI_USERPWD_ATTR, SLAPI_ACL_WRITE);
|
|
+ if (rc) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ rc = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_DN);
|
|
+ if (rc) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (!is_pwd_op) {
|
|
+ /* This may be a magic op to ask us to generate the NT hashes */
|
|
+ if (is_magic_regen) {
|
|
+ /* Make sense to call only if this entry has krb keys to source
|
|
+ * the nthash from */
|
|
+ if (is_krb) {
|
|
+ rc = ipapwd_regen_nthash(pb, smods, dn, e, krbcfg);
|
|
+ } else {
|
|
+ rc = LDAP_UNWILLING_TO_PERFORM;
|
|
+ }
|
|
+ } else {
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ }
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* run through the mods again and adjust flags if operations affect them */
|
|
+ lmod = slapi_mods_get_first_mod(smods);
|
|
+ while (lmod) {
|
|
+ struct berval *bv;
|
|
+
|
|
+ if (slapi_attr_types_equivalent(lmod->mod_type, SLAPI_USERPWD_ATTR)) {
|
|
+ /* check op filtering out LDAP_MOD_BVALUES */
|
|
+ switch (lmod->mod_op & 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;
|
|
+ if (!lmod->mod_bvalues ||
|
|
+ !lmod->mod_bvalues[0]) {
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+ bv = lmod->mod_bvalues[0];
|
|
+ slapi_ch_free_string(&userpw);
|
|
+ userpw = slapi_ch_malloc(bv->bv_len+1);
|
|
+ if (!userpw) {
|
|
+ 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 */
|
|
+ if (!lmod->mod_bvalues ||
|
|
+ !lmod->mod_bvalues[0]) {
|
|
+ is_pwd_op = 0;
|
|
+ } else {
|
|
+ bv = lmod->mod_bvalues[0];
|
|
+ if ((userpw &&
|
|
+ strncmp(userpw, bv->bv_val, bv->bv_len) == 0) ||
|
|
+ (unhashedpw &&
|
|
+ strncmp(unhashedpw, bv->bv_val, bv->bv_len) == 0)) {
|
|
+ is_pwd_op = 0;
|
|
+ }
|
|
+ }
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ } else if (slapi_attr_types_equivalent(lmod->mod_type,
|
|
+ SLAPI_ATTR_OBJECTCLASS)) {
|
|
+ int i;
|
|
+ /* check op filtering out LDAP_MOD_BVALUES */
|
|
+ switch (lmod->mod_op & 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;
|
|
+ is_ipant = 0;
|
|
+
|
|
+ case LDAP_MOD_ADD:
|
|
+ if (!lmod->mod_bvalues ||
|
|
+ !lmod->mod_bvalues[0]) {
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+ for (i = 0; (bv = lmod->mod_bvalues[i]) != NULL; i++) {
|
|
+ if (strncasecmp("krbPrincipalAux",
|
|
+ bv->bv_val, bv->bv_len) == 0) {
|
|
+ is_krb = 1;
|
|
+ } else if (strncasecmp("sambaSamAccount",
|
|
+ bv->bv_val, bv->bv_len) == 0) {
|
|
+ is_smb = 1;
|
|
+ } else if (strncasecmp("ipaNTUserAttrs",
|
|
+ bv->bv_val, bv->bv_len) == 0) {
|
|
+ is_ipant = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ break;
|
|
+
|
|
+ case LDAP_MOD_DELETE:
|
|
+ /* can this happen for objectclasses ? */
|
|
+ is_krb = 0;
|
|
+ is_smb = 0;
|
|
+ is_ipant = 0;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ } else if (slapi_attr_types_equivalent(lmod->mod_type,
|
|
+ "krbPrincipalKey")) {
|
|
+
|
|
+ /* if we are getting a krbPrincipalKey, also avoid regenerating
|
|
+ * the keys, it means kadmin has alredy done the job and is simply
|
|
+ * keeping userPassword and sambaXXPAssword in sync */
|
|
+
|
|
+ /* we also check we have enough authority */
|
|
+ if (is_root) {
|
|
+ has_krb_keys = 1;
|
|
+ }
|
|
+
|
|
+ } else if (slapi_attr_types_equivalent(lmod->mod_type,
|
|
+ "passwordHistory")) {
|
|
+
|
|
+ /* if we are getting a passwordHistory, also avoid regenerating
|
|
+ * the hashes, it means kadmin has alredy done the job and is
|
|
+ * simply keeping userPassword and sambaXXPAssword in sync */
|
|
+
|
|
+ /* we also check we have enough authority */
|
|
+ if (is_root) {
|
|
+ has_history = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ lmod = slapi_mods_get_next_mod(smods);
|
|
+ }
|
|
+
|
|
+ if (is_krb) {
|
|
+ if (has_krb_keys) {
|
|
+ gen_krb_keys = 0;
|
|
+ } else {
|
|
+ gen_krb_keys = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* 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 && (gen_krb_keys || is_smb || is_ipant)) {
|
|
+ if ('{' == userpw[0]) {
|
|
+ if (0 == strncasecmp(userpw, "{CLEAR}", strlen("{CLEAR}"))) {
|
|
+ unhashedpw = slapi_ch_strdup(&userpw[strlen("{CLEAR}")]);
|
|
+ if (NULL == unhashedpw) {
|
|
+ LOG_OOM();
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ goto done;
|
|
+ }
|
|
+ slapi_ch_free_string(&userpw);
|
|
+
|
|
+ } else if (slapi_is_encoded(userpw)) {
|
|
+
|
|
+ LOG("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) {
|
|
+ LOG_FATAL("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->is_krb = is_krb;
|
|
+ pwdop->pwd_op = IPAPWD_OP_MOD;
|
|
+ pwdop->pwdata.password = slapi_ch_strdup(unhashedpw);
|
|
+ pwdop->pwdata.changetype = IPA_CHANGETYPE_NORMAL;
|
|
+ pwdop->skip_history = has_history;
|
|
+ pwdop->skip_keys = has_krb_keys;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ /* if krb keys are being set by an external agent we assume password
|
|
+ * policies have been properly checked already, so we check them only
|
|
+ * if no krb keys are available */
|
|
+ if (has_krb_keys == 0) {
|
|
+ ret = ipapwd_CheckPolicy(&pwdop->pwdata);
|
|
+ if (ret) {
|
|
+ errMesg = ipapwd_error2string(ret);
|
|
+ rc = LDAP_CONSTRAINT_VIOLATION;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (gen_krb_keys || is_smb || is_ipant) {
|
|
+
|
|
+ Slapi_Value **svals = NULL;
|
|
+ Slapi_Value **ntvals = NULL;
|
|
+ char *nt = NULL;
|
|
+ char *lm = NULL;
|
|
+
|
|
+ rc = ipapwd_gen_hashes(krbcfg, &pwdop->pwdata, unhashedpw,
|
|
+ gen_krb_keys, is_smb, is_ipant,
|
|
+ &svals, &nt, &lm, &ntvals, &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 && is_smb) {
|
|
+ /* replace value */
|
|
+ slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
+ "sambaLMPassword", lm);
|
|
+ slapi_ch_free_string(&lm);
|
|
+ }
|
|
+ if (nt && is_smb) {
|
|
+ /* replace value */
|
|
+ slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
|
+ "sambaNTPassword", nt);
|
|
+ slapi_ch_free_string(&nt);
|
|
+ }
|
|
+
|
|
+ if (ntvals && is_ipant) {
|
|
+ slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
|
+ "ipaNTHash", ntvals);
|
|
+ ipapwd_free_slapi_value_array(&ntvals);
|
|
+ }
|
|
+
|
|
+ if (is_smb) {
|
|
+ /* with samba integration we need to also set sambaPwdLastSet or
|
|
+ * samba will decide the user has to change the password again */
|
|
+ if (pwdop->pwdata.changetype == IPA_CHANGETYPE_ADMIN) {
|
|
+ /* if it is an admin change instead we need to let know to
|
|
+ * samba as well that the use rmust change its password */
|
|
+ slapi_entry_attr_set_long(e, "sambaPwdLastset", 0L);
|
|
+ } else {
|
|
+ slapi_entry_attr_set_long(e, "sambaPwdLastset",
|
|
+ (long)pwdop->pwdata.timeNow);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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);
|
|
+ if (slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods)) {
|
|
+ LOG_FATAL("slapi_pblock_set failed!\n");
|
|
+ rc = LDAP_OPERATIONS_ERROR;
|
|
+ }
|
|
+ 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_regen_nthash(Slapi_PBlock *pb, Slapi_Mods *smods,
|
|
+ char *dn, struct slapi_entry *entry,
|
|
+ struct ipapwd_krbcfg *krbcfg)
|
|
+{
|
|
+ Slapi_Attr *attr;
|
|
+ Slapi_Value *value;
|
|
+ const struct berval *val;
|
|
+ struct berval *ntvals[2] = { NULL, NULL };
|
|
+ struct berval bval;
|
|
+ krb5_key_data *keys;
|
|
+ int num_keys;
|
|
+ int mkvno;
|
|
+ int ret;
|
|
+ int i;
|
|
+
|
|
+ ret = slapi_entry_attr_find(entry, "ipaNTHash", &attr);
|
|
+ if (ret == 0) {
|
|
+ /* We refuse to regen if there is already a value */
|
|
+ return LDAP_CONSTRAINT_VIOLATION;
|
|
+ }
|
|
+
|
|
+ /* ok let's see if we can find the RC4 hash in the keys */
|
|
+ ret = slapi_entry_attr_find(entry, "krbPrincipalKey", &attr);
|
|
+ if (ret) {
|
|
+ return LDAP_UNWILLING_TO_PERFORM;
|
|
+ }
|
|
+
|
|
+ ret = slapi_attr_first_value(attr, &value);
|
|
+ if (ret) {
|
|
+ return LDAP_OPERATIONS_ERROR;
|
|
+ }
|
|
+
|
|
+ val = slapi_value_get_berval(value);
|
|
+ if (!val) {
|
|
+ return LDAP_OPERATIONS_ERROR;
|
|
+ }
|
|
+
|
|
+ ret = ber_decode_krb5_key_data((struct berval *)val,
|
|
+ &mkvno, &num_keys, &keys);
|
|
+ if (ret) {
|
|
+ return LDAP_OPERATIONS_ERROR;
|
|
+ }
|
|
+
|
|
+ ret = LDAP_UNWILLING_TO_PERFORM;
|
|
+
|
|
+ for (i = 0; i < num_keys; i++) {
|
|
+ char nthash[16];
|
|
+ krb5_enc_data cipher;
|
|
+ krb5_data plain;
|
|
+ krb5_int16 t;
|
|
+
|
|
+ if (keys[i].key_data_type[0] != ENCTYPE_ARCFOUR_HMAC) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ memcpy(&t, keys[i].key_data_contents[0], 2);
|
|
+ plain.length = le16toh(t);
|
|
+ if (plain.length != 16) {
|
|
+ continue;
|
|
+ }
|
|
+ plain.data = nthash;
|
|
+
|
|
+ memset(&cipher, 0, sizeof(krb5_enc_data));
|
|
+ cipher.enctype = krbcfg->kmkey->enctype;
|
|
+ cipher.ciphertext.length = keys[i].key_data_length[0] - 2;
|
|
+ cipher.ciphertext.data = ((char *)keys[i].key_data_contents[0]) + 2;
|
|
+
|
|
+ ret = krb5_c_decrypt(krbcfg->krbctx, krbcfg->kmkey,
|
|
+ 0, NULL, &cipher, &plain);
|
|
+ if (ret) {
|
|
+ ret = LDAP_OPERATIONS_ERROR;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ bval.bv_val = nthash;
|
|
+ bval.bv_len = 16;
|
|
+ ntvals[0] = &bval;
|
|
+
|
|
+ slapi_mods_add_modbvps(smods, LDAP_MOD_ADD, "ipaNTHash", ntvals);
|
|
+
|
|
+ ret = LDAP_SUCCESS;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ipa_krb5_free_key_data(keys, num_keys);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+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;
|
|
+ char *errMsg = "Internal operations error\n";
|
|
+ struct ipapwd_krbcfg *krbcfg = NULL;
|
|
+ char *principal = NULL;
|
|
+ Slapi_Value *ipahost;
|
|
+
|
|
+ LOG_TRACE("=>\n");
|
|
+
|
|
+ /* time to get the operation handler */
|
|
+ ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op);
|
|
+ if (ret != 0) {
|
|
+ LOG_FATAL("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) {
|
|
+ LOG_FATAL("Internal error, couldn't find pluginextension ?!\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* not interesting */
|
|
+ if (IPAPWD_OP_NULL == pwdop->pwd_op)
|
|
+ return 0;
|
|
+
|
|
+ if ( ! (pwdop->is_krb)) {
|
|
+ LOG("Not a kerberos user, ignore krb attributes\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (pwdop->skip_keys && pwdop->skip_history) {
|
|
+ /* nothing to do, caller already set all interesting attributes */
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ret = ipapwd_gen_checks(pb, &errMsg, &krbcfg, 0);
|
|
+ if (ret != 0) {
|
|
+ LOG_FATAL("ipapwd_gen_checks failed!?\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* prepare changes that can be made only as root */
|
|
+ smods = slapi_mods_new();
|
|
+
|
|
+ /* 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 && !pwdop->skip_history) {
|
|
+ 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) {
|
|
+ LOG("Failed to retrieve entry?!\n");
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+ pwvals = ipapwd_setPasswordHistory(smods, &pwdop->pwdata);
|
|
+ if (pwvals) {
|
|
+ slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
|
+ "passwordHistory", pwvals);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* we assume that krb attributes are properly updated too if keys were
|
|
+ * passed in */
|
|
+ if (!pwdop->skip_keys) {
|
|
+ /* Don't set a last password change or expiration on host passwords.
|
|
+ * krbLastPwdChange is used to tell whether we have a valid keytab.
|
|
+ * If we set it on userPassword it confuses enrollment.
|
|
+ * If krbPasswordExpiration is set on a host entry then the keytab
|
|
+ * will appear to be expired.
|
|
+ *
|
|
+ * When a host is issued a keytab these attributes get set properly by
|
|
+ * ipapwd_setkeytab().
|
|
+ */
|
|
+ ipahost = slapi_value_new_string("ipaHost");
|
|
+ if (!pwdop->pwdata.target ||
|
|
+ (slapi_entry_attr_has_syntax_value(pwdop->pwdata.target,
|
|
+ SLAPI_ATTR_OBJECTCLASS, ipahost)) == 0) {
|
|
+ /* set Password Expiration date */
|
|
+ if (!gmtime_r(&(pwdop->pwdata.expireTime), &utctime)) {
|
|
+ LOG_FATAL("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);
|
|
+
|
|
+ /* change Last Password Change field with the current date */
|
|
+ if (!gmtime_r(&(pwdop->pwdata.timeNow), &utctime)) {
|
|
+ LOG_FATAL("failed to parse current date (buggy gmtime_r ?)\n");
|
|
+ slapi_value_free(&ipahost);
|
|
+ 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);
|
|
+ }
|
|
+ slapi_value_free(&ipahost);
|
|
+ }
|
|
+
|
|
+ ret = ipapwd_apply_mods(pwdop->pwdata.dn, smods);
|
|
+ if (ret)
|
|
+ LOG("Failed to set additional password attributes in the post-op!\n");
|
|
+
|
|
+ if (!pwdop->skip_keys) {
|
|
+ if (pwdop->pwdata.changetype == IPA_CHANGETYPE_NORMAL) {
|
|
+ principal = slapi_entry_attr_get_charptr(pwdop->pwdata.target,
|
|
+ "krbPrincipalName");
|
|
+ } else {
|
|
+ principal = slapi_ch_smprintf("root/admin@%s", krbcfg->realm);
|
|
+ }
|
|
+ ipapwd_set_extradata(pwdop->pwdata.dn, principal, pwdop->pwdata.timeNow);
|
|
+ }
|
|
+
|
|
+done:
|
|
+ if (pwdop && pwdop->pwdata.target) slapi_entry_free(pwdop->pwdata.target);
|
|
+ slapi_mods_free(&smods);
|
|
+ slapi_ch_free_string(&principal);
|
|
+ free_ipapwd_krbcfg(&krbcfg);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* 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;
|
|
+ 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;
|
|
+ char *principal = NULL;
|
|
+
|
|
+ LOG_TRACE("=>\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) {
|
|
+ LOG_FATAL("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) {
|
|
+ LOG("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) {
|
|
+ LOG("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) {
|
|
+ LOG("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) {
|
|
+ LOG("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 */
|
|
+ LOG_OOM();
|
|
+ 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) {
|
|
+ LOG("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) {
|
|
+ LOG_FATAL("Generic 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) {
|
|
+ LOG_FATAL("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. */
|
|
+ LOG("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) {
|
|
+ LOG("failed to set kerberos key for user entry: %s\n", dn);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* we need to make sure the ExtraData is set, otherwise kadmin
|
|
+ * will not like the object */
|
|
+ principal = slapi_entry_attr_get_charptr(entry, "krbPrincipalName");
|
|
+ if (!principal) {
|
|
+ LOG_OOM();
|
|
+ goto done;
|
|
+ }
|
|
+ ipapwd_set_extradata(pwdata.dn, principal, pwdata.timeNow);
|
|
+
|
|
+ LOG("kerberos key generated for user entry: %s\n", dn);
|
|
+
|
|
+done:
|
|
+ slapi_ch_free_string(&principal);
|
|
+ slapi_ch_free_string(&expire);
|
|
+ if (entry)
|
|
+ slapi_entry_free(entry);
|
|
+ free_ipapwd_krbcfg(&krbcfg);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+/* Init pre ops */
|
|
+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 *)&ipapwd_plugin_desc);
|
|
+ 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;
|
|
+}
|
|
+
|
|
+int ipapwd_pre_init_betxn(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 *)&ipapwd_plugin_desc);
|
|
+ if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_PRE_ADD_FN, (void *)ipapwd_pre_add);
|
|
+ if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN, (void *)ipapwd_pre_mod);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* Init post ops */
|
|
+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 *)&ipapwd_plugin_desc);
|
|
+ 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;
|
|
+}
|
|
+
|
|
+int ipapwd_post_init_betxn(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 *)&ipapwd_plugin_desc);
|
|
+ if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_POST_ADD_FN, (void *)ipapwd_post_op);
|
|
+ if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN, (void *)ipapwd_post_op);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
--
|
|
1.8.2.1
|
|
|