diff options
author | Rob Crittenden <rcritten@redhat.com> | 2009-01-29 16:26:07 -0500 |
---|---|---|
committer | Rob Crittenden <rcritten@redhat.com> | 2009-02-03 15:27:14 -0500 |
commit | e30cd6ba42c256d2016db45146d616f329455e86 (patch) | |
tree | d4c5291095c80c92bc4803fe7f20fc2838124ffa /ipa-server/ipa-slapi-plugins | |
parent | c4ed025001895bfc65c613cabbbfcb27c19cc29f (diff) | |
download | freeipa-e30cd6ba42c256d2016db45146d616f329455e86.tar.gz freeipa-e30cd6ba42c256d2016db45146d616f329455e86.tar.xz freeipa-e30cd6ba42c256d2016db45146d616f329455e86.zip |
Mass tree reorganization for IPAv2. To view previous history of files use:
% git log --follow -- <file>
renamed: ipa-server/autogen.sh -> autogen.sh
renamed: ipa-server/ipa-kpasswd/Makefile.am -> daemons/ipa-kpasswd/Makefile.am
renamed: ipa-server/ipa-kpasswd/README -> daemons/ipa-kpasswd/README
renamed: ipa-server/ipa-kpasswd/ipa_kpasswd.c -> daemons/ipa-kpasswd/ipa_kpasswd.c
renamed: ipa-server/ipa-kpasswd/ipa_kpasswd.init -> daemons/ipa-kpasswd/ipa_kpasswd.init
renamed: ipa-server/ipa-slapi-plugins/Makefile.am -> daemons/ipa-slapi-plugins/Makefile.am
renamed: ipa-server/ipa-slapi-plugins/README -> daemons/ipa-slapi-plugins/README
renamed: ipa-server/ipa-slapi-plugins/dna/Makefile.am -> daemons/ipa-slapi-plugins/dna/Makefile.am
renamed: ipa-server/ipa-slapi-plugins/dna/dna-conf.ldif -> daemons/ipa-slapi-plugins/dna/dna-conf.ldif
renamed: ipa-server/ipa-slapi-plugins/dna/dna.c -> daemons/ipa-slapi-plugins/dna/dna.c
renamed: ipa-server/ipa-slapi-plugins/ipa-memberof/Makefile.am -> daemons/ipa-slapi-plugins/ipa-memberof/Makefile.am
renamed: ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.c -> daemons/ipa-slapi-plugins/ipa-memberof/ipa-memberof.c
renamed: ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.h -> daemons/ipa-slapi-plugins/ipa-memberof/ipa-memberof.h
renamed: ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof_config.c -> daemons/ipa-slapi-plugins/ipa-memberof/ipa-memberof_config.c
renamed: ipa-server/ipa-slapi-plugins/ipa-memberof/memberof-conf.ldif -> daemons/ipa-slapi-plugins/ipa-memberof/memberof-conf.ldif
renamed: ipa-server/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am -> daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
renamed: ipa-server/ipa-slapi-plugins/ipa-pwd-extop/README -> daemons/ipa-slapi-plugins/ipa-pwd-extop/README
renamed: ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c -> daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
renamed: ipa-server/ipa-slapi-plugins/ipa-pwd-extop/pwd-extop-conf.ldif -> daemons/ipa-slapi-plugins/ipa-pwd-extop/pwd-extop-conf.ldif
renamed: ipa-server/ipa-slapi-plugins/ipa-winsync/Makefile.am -> daemons/ipa-slapi-plugins/ipa-winsync/Makefile.am
renamed: ipa-server/ipa-slapi-plugins/ipa-winsync/README -> daemons/ipa-slapi-plugins/ipa-winsync/README
renamed: ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync-conf.ldif -> daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync-conf.ldif
renamed: ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync-config.c -> daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync-config.c
renamed: ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync.c -> daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync.c
renamed: ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync.h -> daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync.h
renamed: ipa-server/xmlrpc-server/ipa-rewrite.conf -> install/conf/ipa-rewrite.conf
renamed: ipa-server/xmlrpc-server/ipa.conf -> install/conf/ipa.conf
renamed: ipa-server/xmlrpc-server/ssbrowser.html -> install/html/ssbrowser.html
renamed: ipa-server/xmlrpc-server/unauthorized.html -> install/html/unauthorized.html
renamed: ipa-server/ipa-install/share/60ipaconfig.ldif -> install/share/60ipaconfig.ldif
renamed: ipa-server/ipa-install/share/60kerberos.ldif -> install/share/60kerberos.ldif
renamed: ipa-server/ipa-install/share/60radius.ldif -> install/share/60radius.ldif
renamed: ipa-server/ipa-install/share/60samba.ldif -> install/share/60samba.ldif
renamed: ipa-server/ipa-install/share/Makefile.am -> install/share/Makefile.am
renamed: ipa-server/ipa-install/share/bind.named.conf.template -> install/share/bind.named.conf.template
renamed: ipa-server/ipa-install/share/bind.zone.db.template -> install/share/bind.zone.db.template
renamed: ipa-server/ipa-install/share/bootstrap-template.ldif -> install/share/bootstrap-template.ldif
renamed: ipa-server/ipa-install/share/certmap.conf.template -> install/share/certmap.conf.template
renamed: ipa-server/ipa-install/share/default-aci.ldif -> install/share/default-aci.ldif
renamed: ipa-server/ipa-install/share/default-keytypes.ldif -> install/share/default-keytypes.ldif
renamed: ipa-server/ipa-install/share/dna-posix.ldif -> install/share/dna-posix.ldif
renamed: ipa-server/ipa-install/share/encrypted_attribute.ldif -> install/share/encrypted_attribute.ldif
renamed: ipa-server/ipa-install/share/fedora-ds.init.patch -> install/share/fedora-ds.init.patch
renamed: ipa-server/ipa-install/share/indices.ldif -> install/share/indices.ldif
renamed: ipa-server/ipa-install/share/kdc.conf.template -> install/share/kdc.conf.template
renamed: ipa-server/ipa-install/share/kerberos.ldif -> install/share/kerberos.ldif
renamed: ipa-server/ipa-install/share/krb.con.template -> install/share/krb.con.template
renamed: ipa-server/ipa-install/share/krb5.conf.template -> install/share/krb5.conf.template
renamed: ipa-server/ipa-install/share/krb5.ini.template -> install/share/krb5.ini.template
renamed: ipa-server/ipa-install/share/krbrealm.con.template -> install/share/krbrealm.con.template
renamed: ipa-server/ipa-install/share/master-entry.ldif -> install/share/master-entry.ldif
renamed: ipa-server/ipa-install/share/memberof-task.ldif -> install/share/memberof-task.ldif
renamed: ipa-server/ipa-install/share/ntp.conf.server.template -> install/share/ntp.conf.server.template
renamed: ipa-server/ipa-install/share/ntpd.sysconfig.template -> install/share/ntpd.sysconfig.template
renamed: ipa-server/ipa-install/share/preferences.html.template -> install/share/preferences.html.template
renamed: ipa-server/ipa-install/share/referint-conf.ldif -> install/share/referint-conf.ldif
renamed: ipa-server/ipa-install/share/schema_compat.uldif -> install/share/schema_compat.uldif
renamed: ipa-server/ipa-install/share/unique-attributes.ldif -> install/share/unique-attributes.ldif
renamed: ipa-server/ipa-install/Makefile.am -> install/tools/Makefile.am
renamed: ipa-server/ipa-install/README -> install/tools/README
renamed: ipa-server/ipa-compat-manage -> install/tools/ipa-compat-manage
renamed: ipa-server/ipa-fix-CVE-2008-3274 -> install/tools/ipa-fix-CVE-2008-3274
renamed: ipa-server/ipa-ldap-updater -> install/tools/ipa-ldap-updater
renamed: ipa-server/ipa-install/ipa-replica-install -> install/tools/ipa-replica-install
renamed: ipa-server/ipa-install/ipa-replica-manage -> install/tools/ipa-replica-manage
renamed: ipa-server/ipa-install/ipa-replica-prepare -> install/tools/ipa-replica-prepare
renamed: ipa-server/ipa-install/ipa-server-certinstall -> install/tools/ipa-server-certinstall
renamed: ipa-server/ipa-install/ipa-server-install -> install/tools/ipa-server-install
renamed: ipa-server/ipa-upgradeconfig -> install/tools/ipa-upgradeconfig
renamed: ipa-server/ipa-install/ipactl -> install/tools/ipactl
renamed: ipa-server/man/Makefile.am -> install/tools/man/Makefile.am
renamed: ipa-server/man/ipa-compat-manage.1 -> install/tools/man/ipa-compat-manage.1
renamed: ipa-server/man/ipa-ldap-updater.1 -> install/tools/man/ipa-ldap-updater.1
renamed: ipa-server/man/ipa-replica-install.1 -> install/tools/man/ipa-replica-install.1
renamed: ipa-server/man/ipa-replica-manage.1 -> install/tools/man/ipa-replica-manage.1
renamed: ipa-server/man/ipa-replica-prepare.1 -> install/tools/man/ipa-replica-prepare.1
renamed: ipa-server/man/ipa-server-certinstall.1 -> install/tools/man/ipa-server-certinstall.1
renamed: ipa-server/man/ipa-server-install.1 -> install/tools/man/ipa-server-install.1
renamed: ipa-server/man/ipa_kpasswd.8 -> install/tools/man/ipa_kpasswd.8
renamed: ipa-server/man/ipa_webgui.8 -> install/tools/man/ipa_webgui.8
renamed: ipa-server/man/ipactl.8 -> install/tools/man/ipactl.8
renamed: ipa-server/ipa-install/updates/Makefile.am -> install/updates/Makefile.am
renamed: ipa-server/ipa-install/updates/RFC2307bis.update -> install/updates/RFC2307bis.update
renamed: ipa-server/ipa-install/updates/RFC4876.update -> install/updates/RFC4876.update
renamed: ipa-server/ipa-install/updates/indices.update -> install/updates/indices.update
renamed: ipa-server/ipa-install/updates/nss_ldap.update -> install/updates/nss_ldap.update
renamed: ipa-server/ipa-install/updates/replication.update -> install/updates/replication.update
renamed: ipa-server/ipa-install/updates/winsync_index.update -> install/updates/winsync_index.update
renamed: ipa-server/ipaserver/Makefile.am -> ipaserver/install/Makefile.am
renamed: ipa-server/ipaserver/__init__.py -> ipaserver/install/__init__.py
renamed: ipa-server/ipaserver/bindinstance.py -> ipaserver/install/bindinstance.py
renamed: ipa-server/ipaserver/certs.py -> ipaserver/install/certs.py
renamed: ipa-server/ipaserver/dsinstance.py -> ipaserver/install/dsinstance.py
renamed: ipa-server/ipaserver/httpinstance.py -> ipaserver/install/httpinstance.py
renamed: ipa-server/ipaserver/installutils.py -> ipaserver/install/installutils.py
renamed: ipa-server/ipaserver/ipaldap.py -> ipaserver/install/ipaldap.py
renamed: ipa-server/ipaserver/krbinstance.py -> ipaserver/install/krbinstance.py
renamed: ipa-server/ipaserver/ldapupdate.py -> ipaserver/install/ldapupdate.py
renamed: ipa-server/ipaserver/ntpinstance.py -> ipaserver/install/ntpinstance.py
renamed: ipa-server/ipaserver/replication.py -> ipaserver/install/replication.py
renamed: ipa-server/ipaserver/service.py -> ipaserver/install/service.py
renamed: ipa-server/selinux/Makefile -> selinux/Makefile
renamed: ipa-server/selinux/ipa-server-selinux.spec.in -> selinux/ipa-server-selinux.spec.in
renamed: ipa-server/selinux/ipa_kpasswd/ipa_kpasswd.fc -> selinux/ipa_kpasswd/ipa_kpasswd.fc
renamed: ipa-server/selinux/ipa_kpasswd/ipa_kpasswd.te -> selinux/ipa_kpasswd/ipa_kpasswd.te
renamed: ipa-server/selinux/ipa_webgui/ipa_webgui.fc -> selinux/ipa_webgui/ipa_webgui.fc
renamed: ipa-server/selinux/ipa_webgui/ipa_webgui.te -> selinux/ipa_webgui/ipa_webgui.te
renamed: ipa-server/version.m4.in -> version.m4.in
Diffstat (limited to 'ipa-server/ipa-slapi-plugins')
20 files changed, 0 insertions, 10749 deletions
diff --git a/ipa-server/ipa-slapi-plugins/Makefile.am b/ipa-server/ipa-slapi-plugins/Makefile.am deleted file mode 100644 index f316371c3..000000000 --- a/ipa-server/ipa-slapi-plugins/Makefile.am +++ /dev/null @@ -1,16 +0,0 @@ -NULL = - -SUBDIRS = \ - ipa-pwd-extop \ - ipa-memberof \ - dna \ - ipa-winsync \ - $(NULL) - -EXTRA_DIST = \ - README \ - $(NULL) - -MAINTAINERCLEANFILES = \ - *~ \ - Makefile.in diff --git a/ipa-server/ipa-slapi-plugins/README b/ipa-server/ipa-slapi-plugins/README deleted file mode 100644 index e69de29bb..000000000 --- a/ipa-server/ipa-slapi-plugins/README +++ /dev/null diff --git a/ipa-server/ipa-slapi-plugins/dna/Makefile.am b/ipa-server/ipa-slapi-plugins/dna/Makefile.am deleted file mode 100644 index 4a54b8d5d..000000000 --- a/ipa-server/ipa-slapi-plugins/dna/Makefile.am +++ /dev/null @@ -1,42 +0,0 @@ -NULL = - -INCLUDES = \ - -I. \ - -I$(srcdir) \ - -DPREFIX=\""$(prefix)"\" \ - -DBINDIR=\""$(bindir)"\" \ - -DLIBDIR=\""$(libdir)"\" \ - -DLIBEXECDIR=\""$(libexecdir)"\" \ - -DDATADIR=\""$(datadir)"\" \ - $(MOZLDAP_CFLAGS) \ - $(KRB5_CFLAGS) \ - $(WARN_CFLAGS) \ - $(NULL) - -plugindir = $(libdir)/dirsrv/plugins -plugin_LTLIBRARIES = \ - libipa-dna-plugin.la \ - $(NULL) - -libipa_dna_plugin_la_SOURCES = \ - dna.c \ - $(NULL) - -libipa_dna_plugin_la_LDFLAGS = -avoid-version - -libipa_dna_plugin_la_LIBADD = \ - $(MOZLDAP_LIBS) \ - $(NULL) - -appdir = $(IPA_DATA_DIR) -app_DATA = \ - dna-conf.ldif \ - $(NULL) - -EXTRA_DIST = \ - $(app_DATA) \ - $(NULL) - -MAINTAINERCLEANFILES = \ - *~ \ - Makefile.in diff --git a/ipa-server/ipa-slapi-plugins/dna/dna-conf.ldif b/ipa-server/ipa-slapi-plugins/dna/dna-conf.ldif deleted file mode 100644 index 02532b4e4..000000000 --- a/ipa-server/ipa-slapi-plugins/dna/dna-conf.ldif +++ /dev/null @@ -1,14 +0,0 @@ -dn: cn=ipa-dna,cn=plugins,cn=config -changetype: add -objectclass: top -objectclass: nsSlapdPlugin -objectclass: extensibleObject -cn: ipa-dna -nsslapd-pluginpath: libipa-dna-plugin -nsslapd-plugininitfunc: ipa_dna_init -nsslapd-plugintype: preoperation -nsslapd-pluginenabled: on -nsslapd-pluginid: ipa-dna -nsslapd-pluginversion: 1.0 -nsslapd-pluginvendor: Red Hat -nsslapd-plugindescription: IPA Distributed numeric assignment plugin diff --git a/ipa-server/ipa-slapi-plugins/dna/dna.c b/ipa-server/ipa-slapi-plugins/dna/dna.c deleted file mode 100644 index cb6a0629c..000000000 --- a/ipa-server/ipa-slapi-plugins/dna/dna.c +++ /dev/null @@ -1,1462 +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; version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., 59 Temple - * Place, Suite 330, Boston, MA 02111-1307 USA. - * - * In addition, as a special exception, Red Hat, Inc. gives You the additional - * right to link the code of this Program with code not covered under the GNU - * General Public License ("Non-GPL Code") and to distribute linked combinations - * including the two, subject to the limitations in this paragraph. Non-GPL Code - * permitted under this exception must only link 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 GNU General Public License. Only Red Hat, Inc. may make changes or - * additions to the list of Approved Interfaces. You must obey the GNU General - * Public License in all respects for all of the Program code and other code used - * in conjunction with the Program except the Non-GPL Code covered by this - * exception. If you modify this file, you may extend this exception to your - * version of the file, but you are not obligated to do so. If you do not wish to - * provide this exception without modification, you must delete this exception - * statement from your version and license this file solely under the GPL without - * exception. - * - * - * Author: Pete Rowley - * - * Copyright (C) 2007 Red Hat, Inc. - * All rights reserved. - * END COPYRIGHT BLOCK **/ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - - -/** - * Distributed Numeric Assignment plug-in - */ - -#include <dirsrv/slapi-plugin.h> - -#include <stdio.h> -#include <ctype.h> -#include <string.h> -#include <errno.h> -/*#include "portable.h"*/ -#include "nspr.h" -/*#include "slapi-private.h"*/ -/*#include "dirlite_strings.h"*/ -/*#include "dirver.h"*/ - -#include "prclist.h" -#include "ldif.h" - -/* get file mode flags for unix */ -#ifndef _WIN32 -#include <sys/stat.h> -#endif - -#define DNA_PLUGIN_SUBSYSTEM "ipa-dna-plugin" -#define DNA_PLUGIN_VERSION 0x00020000 - -/* temporary */ -#define DNA_DN "cn=ipa-dna,cn=plugins,cn=config" - -#define DNA_SUCCESS 0 -#define DNA_FAILURE -1 - -/** - * DNA config types - */ -#define DNA_TYPE "dnaType" -#define DNA_PREFIX "dnaPrefix" -#define DNA_NEXTVAL "dnaNextValue" -#define DNA_INTERVAL "dnaInterval" -#define DNA_GENERATE "dnaMagicRegen" -#define DNA_FILTER "dnaFilter" -#define DNA_SCOPE "dnaScope" - -/* since v2 */ -#define DNA_MAXVAL "dnaMaxValue" -#define DNA_SHARED_CFG_DN "dnaSharedCfgDN" - -/* Shared Config */ -#define DNA_GLOBAL_RANGE "dnaGlobalRange" -#define DNA_RANGE "dnaRange" -#define DNA_MAX_RANGE_SIZE "dnaMaxRangeSize" -#define DNA_CHUNK_SIZE "dnaChunkSize" - - - -#define FEATURE_DESC "IPA Distributed Numeric Assignment" -#define PLUGIN_DESC "IPA Distributed Numeric Assignment plugin" -#define PLUGIN_DESC_INT_PREOP PLUGIN_DESC " preop internal" -#define PLUGIN_DESC_POSTOP PLUGIN_DESC " postop" -#define PLUGIN_DESC_INT_POSTOP PLUGIN_DESC " postop internal" - -static Slapi_PluginDesc pdesc = { FEATURE_DESC, - "FreeIPA project", "FreeIPA/1.0", - PLUGIN_DESC -}; - - -/** - * linked list of config entries - */ - -struct configEntry { - PRCList list; - char *dn; - char *type; - char *prefix; - PRUint64 nextval; - PRUint64 interval; - PRUint64 maxval; - char *filter; - struct slapi_filter *slapi_filter; - char *generate; - char *scope; -}; - -static PRCList *dna_global_config = NULL; -static PRRWLock *g_dna_cache_lock; - -static void *_PluginID = NULL; -static char *_PluginDN = NULL; - -static int g_plugin_started = 0; - - -/* - * new value lock - */ -static Slapi_Mutex *g_new_value_lock; - -/** - * - * DNA plug-in management functions - * - */ -int ipa_dna_init(Slapi_PBlock * pb); -static int dna_start(Slapi_PBlock * pb); -static int dna_close(Slapi_PBlock * pb); -static int dna_internal_preop_init(Slapi_PBlock *pb); -static int dna_postop_init(Slapi_PBlock * pb); - -/** - * - * Local operation functions - * - */ -static int loadPluginConfig(); -static int parseConfigEntry(Slapi_Entry * e); -static void deleteConfig(); -static void freeConfigEntry(struct configEntry ** entry); - -/** - * - * helpers - * - */ -static char *dna_get_dn(Slapi_PBlock * pb); -static int dna_dn_is_config(char *dn); -static int dna_get_next_value(struct configEntry * config_entry, - char **next_value_ret); - -/** - * - * the ops (where the real work is done) - * - */ -static int dna_config_check_post_op(Slapi_PBlock * pb); -static int dna_pre_op(Slapi_PBlock * pb, int modtype); -static int dna_mod_pre_op(Slapi_PBlock * pb); -static int dna_add_pre_op(Slapi_PBlock * pb); - -/** - * debug functions - global, for the debugger - */ -void dnaDumpConfig(); -void dnaDumpConfigEntry(struct configEntry *); - -/** - * set the debug level - */ -#ifdef _WIN32 -int *module_ldap_debug = 0; - -void plugin_init_debug_level(int *level_ptr) -{ - module_ldap_debug = level_ptr; -} -#endif - -/** - * - * Deal with cache locking - * - */ -void dna_read_lock() -{ - PR_RWLock_Rlock(g_dna_cache_lock); -} - -void dna_write_lock() -{ - PR_RWLock_Wlock(g_dna_cache_lock); -} - -void dna_unlock() -{ - PR_RWLock_Unlock(g_dna_cache_lock); -} - -/** - * - * Get the dna plug-in version - * - */ -int dna_version() -{ - return DNA_PLUGIN_VERSION; -} - -/** - * Plugin identity mgmt - */ -void setPluginID(void *pluginID) -{ - _PluginID = pluginID; -} - -void *getPluginID() -{ - return _PluginID; -} - -void setPluginDN(char *pluginDN) -{ - _PluginDN = pluginDN; -} - -char *getPluginDN() -{ - return _PluginDN; -} - -/* - dna_init - ------------- - adds our callbacks to the list -*/ -int ipa_dna_init(Slapi_PBlock * pb) -{ - int status = DNA_SUCCESS; - char *plugin_identity = NULL; - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "--> ipa_dna_init\n"); - - /** - * Store the plugin identity for later use. - * Used for internal operations - */ - - slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity); - PR_ASSERT(plugin_identity); - setPluginID(plugin_identity); - - if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, - SLAPI_PLUGIN_VERSION_01) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, - (void *) dna_start) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, - (void *) dna_close) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, - (void *) &pdesc) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, - (void *) dna_mod_pre_op) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, - (void *) dna_add_pre_op) != 0 || - /* internal preoperation */ - slapi_register_plugin("internalpreoperation", /* op type */ - 1, /* Enabled */ - "dna_internal_preop_init", /* this function desc */ - dna_internal_preop_init, /* init func */ - PLUGIN_DESC_INT_PREOP, /* plugin desc */ - NULL, /* ? */ - plugin_identity /* access control */ - ) || - /* the config change checking post op */ - slapi_register_plugin("postoperation", /* op type */ - 1, /* Enabled */ - "dna_postop_init", /* this function desc */ - dna_postop_init, /* init func for post op */ - PLUGIN_DESC_POSTOP, /* plugin desc */ - NULL, /* ? */ - plugin_identity /* access control */ - ) - ) { - slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM, - "ipa_dna_init: failed to register plugin\n"); - status = DNA_FAILURE; - } - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "<-- ipa_dna_init\n"); - return status; -} - - -static int -dna_internal_preop_init(Slapi_PBlock *pb) -{ - int status = DNA_SUCCESS; - - if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, - SLAPI_PLUGIN_VERSION_01) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, - (void *) &pdesc) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN, - (void *) dna_mod_pre_op) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN, - (void *) dna_add_pre_op) != 0) { - status = DNA_FAILURE; - } - - return status; -} - - -static int dna_postop_init(Slapi_PBlock * pb) -{ - int status = DNA_SUCCESS; - - if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, - SLAPI_PLUGIN_VERSION_01) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, - (void *) &pdesc) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, - (void *) dna_config_check_post_op) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN, - (void *) dna_config_check_post_op) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, - (void *) dna_config_check_post_op) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, - (void *) dna_config_check_post_op) != 0) { - slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM, - "dna_postop_init: failed to register plugin\n"); - status = DNA_FAILURE; - } - - return status; -} - -/* - dna_start - -------------- - Kicks off the config cache. - It is called after dna_init. -*/ -static int dna_start(Slapi_PBlock * pb) -{ - char *plugindn = NULL; - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "--> dna_start\n"); - - /* Check if we're already started */ - if (g_plugin_started) { - goto done; - } - - g_dna_cache_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "dna"); - g_new_value_lock = slapi_new_mutex(); - - if (!g_dna_cache_lock || !g_new_value_lock) { - slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM, - "dna_start: lock creation failed\n"); - - return DNA_FAILURE; - } - - /** - * Get the plug-in target dn from the system - * and store it for future use. This should avoid - * hardcoding of DN's in the code. - */ - slapi_pblock_get(pb, SLAPI_TARGET_DN, &plugindn); - if (NULL == plugindn || 0 == strlen(plugindn)) { - slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM, - "dna_start: had to use hard coded config dn\n"); - plugindn = DNA_DN; - } else { - slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM, - "dna_start: config at %s\n", plugindn); - - } - - setPluginDN(plugindn); - - /** - * Load the config for our plug-in - */ - dna_global_config = (PRCList *) - slapi_ch_calloc(1, sizeof(struct configEntry)); - PR_INIT_CLIST(dna_global_config); - - if (loadPluginConfig() != DNA_SUCCESS) { - slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM, - "dna_start: unable to load plug-in configuration\n"); - return DNA_FAILURE; - } - - g_plugin_started = 1; - slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM, - "dna: ready for service\n"); - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "<-- dna_start\n"); - -done: - return DNA_SUCCESS; -} - -/* - dna_close - -------------- - closes down the cache -*/ -static int dna_close(Slapi_PBlock * pb) -{ - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "--> dna_close\n"); - - deleteConfig(); - - slapi_ch_free((void **)&dna_global_config); - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "<-- dna_close\n"); - - return DNA_SUCCESS; -} - -/* - * config looks like this - * - cn=myplugin - * --- cn=posix - * ------ cn=accounts - * ------ cn=groups - * --- cn=samba - * --- cn=etc - * ------ cn=etc etc - */ -static int loadPluginConfig() -{ - int status = DNA_SUCCESS; - int result; - int i; - Slapi_PBlock *search_pb; - Slapi_Entry **entries = NULL; - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "--> loadPluginConfig\n"); - - dna_write_lock(); - deleteConfig(); - - search_pb = slapi_pblock_new(); - - slapi_search_internal_set_pb(search_pb, getPluginDN(), - LDAP_SCOPE_SUBTREE, "objectclass=*", - NULL, 0, NULL, NULL, getPluginID(), 0); - slapi_search_internal_pb(search_pb); - slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result); - - if (LDAP_SUCCESS != result) { - status = DNA_FAILURE; - goto cleanup; - } - - slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, - &entries); - if (NULL == entries || NULL == entries[0]) { - status = DNA_SUCCESS; - goto cleanup; - } - - for (i = 0; (entries[i] != NULL); i++) { - status = parseConfigEntry(entries[i]); - if (DNA_SUCCESS != status) - break; - } - - cleanup: - slapi_free_search_results_internal(search_pb); - slapi_pblock_destroy(search_pb); - dna_unlock(); - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "<-- loadPluginConfig\n"); - - return status; -} - -static int parseConfigEntry(Slapi_Entry * e) -{ - char *value; - struct configEntry *entry; - struct configEntry *config_entry; - PRCList *list; - int entry_added = 0; - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "--> parseConfigEntry\n"); - - entry = (struct configEntry *) - slapi_ch_calloc(1, sizeof(struct configEntry)); - if (NULL == entry) - goto bail; - - value = slapi_entry_get_ndn(e); - if (value) { - entry->dn = strdup(value); - } - - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "----------> dn [%s]\n", entry->dn, 0, 0); - - value = slapi_entry_attr_get_charptr(e, DNA_TYPE); - if (value) { - entry->type = value; - } else - goto bail; - - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "----------> dnaType [%s]\n", entry->type, 0, 0); - - /* FIXME: check the attribute type, it must suport matching rules and be - * indexed, these are requirements and failure to meet them should result in - * the configuration to be disarded and an ERROR logged prominently */ - - value = slapi_entry_attr_get_charptr(e, DNA_NEXTVAL); - if (value) { - entry->nextval = strtoul(value, 0, 0); - slapi_ch_free_string(&value); - } else - goto bail; - - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "----------> dnaNextValue [%d]\n", entry->nextval, 0, - 0); - - value = slapi_entry_attr_get_charptr(e, DNA_PREFIX); - if (value && value[0]) { - entry->prefix = value; - } - - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "----------> dnaPrefix [%s]\n", entry->prefix, 0, 0); - - value = slapi_entry_attr_get_charptr(e, DNA_INTERVAL); - if (value) { - entry->interval = strtoul(value, 0, 0); - } else - goto bail; - - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "----------> dnaInterval [%s]\n", value, 0, 0); - - slapi_ch_free_string(&value); - - value = slapi_entry_attr_get_charptr(e, DNA_GENERATE); - if (value) { - entry->generate = value; - } - - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "----------> dnaMagicRegen [%s]\n", entry->generate, - 0, 0); - - value = slapi_entry_attr_get_charptr(e, DNA_FILTER); - if (value) { - entry->filter = value; - entry->slapi_filter = slapi_str2filter(value); - } else - goto bail; - - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "----------> dnaFilter [%s]\n", value, 0, 0); - - value = slapi_entry_attr_get_charptr(e, DNA_SCOPE); - if (value) { - entry->scope = slapi_dn_normalize(value); - } - - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "----------> dnaScope [%s]\n", entry->scope, 0, 0); - - /* optional, if not specified set -1 which is converted to the max unisgnee - * value */ - value = slapi_entry_attr_get_charptr(e, DNA_MAXVAL); - if (value) { - entry->maxval = strtoul(value, 0, 0); - - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "----------> dnaMaxValue [%ld]\n", value, 0, 0); - - slapi_ch_free_string(&value); - } else - entry->maxval = -1; - - - /** - * Finally add the entry to the list - * we group by type then by filter - * and finally sort by dn length with longer dn's - * first - this allows the scope checking - * code to be simple and quick and - * cunningly linear - */ - if (!PR_CLIST_IS_EMPTY(dna_global_config)) { - list = PR_LIST_HEAD(dna_global_config); - while (list != dna_global_config) { - config_entry = (struct configEntry *) list; - - if (slapi_attr_type_cmp(config_entry->type, entry->type, 1)) - goto next; - - if (slapi_filter_compare(config_entry->slapi_filter, - entry->slapi_filter)) - goto next; - - if (slapi_dn_issuffix(entry->scope, config_entry->scope)) { - PR_INSERT_BEFORE(&(entry->list), list); - slapi_log_error(SLAPI_LOG_CONFIG, - DNA_PLUGIN_SUBSYSTEM, - "store [%s] before [%s] \n", entry->scope, - config_entry->scope, 0); - entry_added = 1; - break; - } - - next: - list = PR_NEXT_LINK(list); - - if (dna_global_config == list) { - /* add to tail */ - PR_INSERT_BEFORE(&(entry->list), list); - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "store [%s] at tail\n", entry->scope, 0, - 0); - entry_added = 1; - break; - } - } - } else { - /* first entry */ - PR_INSERT_LINK(&(entry->list), dna_global_config); - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "store [%s] at head \n", entry->scope, 0, 0); - entry_added = 1; - } - - bail: - if (0 == entry_added) { - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "config entry [%s] skipped\n", entry->dn, 0, 0); - freeConfigEntry(&entry); - } - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "<-- parseConfigEntry\n"); - - return DNA_SUCCESS; -} - -static void freeConfigEntry(struct configEntry ** entry) -{ - struct configEntry *e = *entry; - - if (e->dn) { - slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "freeing config entry [%s]\n", e->dn, 0, 0); - slapi_ch_free_string(&e->dn); - } - - if (e->type) - slapi_ch_free_string(&e->type); - - if (e->prefix) - slapi_ch_free_string(&e->prefix); - - if (e->filter) - slapi_ch_free_string(&e->filter); - - if (e->slapi_filter) - slapi_filter_free(e->slapi_filter, 1); - - if (e->generate) - slapi_ch_free_string(&e->generate); - - if (e->scope) - slapi_ch_free_string(&e->scope); - - slapi_ch_free((void **) entry); -} - -static void deleteConfigEntry(PRCList * entry) -{ - PR_REMOVE_LINK(entry); - freeConfigEntry((struct configEntry **) & entry); -} - -static void deleteConfig() -{ - PRCList *list; - - while (!PR_CLIST_IS_EMPTY(dna_global_config)) { - list = PR_LIST_HEAD(dna_global_config); - deleteConfigEntry(list); - } - - return; -} - -/**************************************************** - Distributed ranges Helpers -****************************************************/ - -static int dna_fix_maxval(Slapi_DN *dn, PRUint64 *cur, PRUint64 *max) -{ - /* TODO: check the main partition to see if another range - * is available, and set the new local configuration - * accordingly. - * If a new range is not available run the retrieval task - * and simply return error - */ - - return LDAP_OPERATIONS_ERROR; -} - -static void dna_notice_allocation(Slapi_DN *dn, PRUint64 new) -{ - /* TODO: check if we passed a new chunk threshold and update - * the shared configuration on the public partition. - */ - - return; -} - -/**************************************************** - Helpers -****************************************************/ - -static char *dna_get_dn(Slapi_PBlock * pb) -{ - char *dn = 0; - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "--> dna_get_dn\n"); - - if (slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn)) { - slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM, - "dna_get_dn: failed to get dn of changed entry"); - goto bail; - } - -/* slapi_dn_normalize( dn ); -*/ - bail: - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "<-- dna_get_dn\n"); - - return dn; -} - -/* config check - matching config dn or a descendent reloads config -*/ -static int dna_dn_is_config(char *dn) -{ - int ret = 0; - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "--> dna_is_config\n"); - - if (slapi_dn_issuffix(dn, getPluginDN())) { - ret = 1; - } - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "<-- dna_is_config\n"); - - return ret; -} - -#define DNA_LDAP_TAG_SK_REVERSE 0x81L - -static LDAPControl *dna_build_sort_control(const char *attr) -{ - LDAPControl *ctrl; - BerElement *ber; - int rc; - - ber = ber_alloc(); - if (NULL == ber) - return NULL; - - rc = ber_printf(ber, "{{stb}}", attr, DNA_LDAP_TAG_SK_REVERSE, 1); - if (-1 == rc) { - ber_free(ber, 1); - return NULL; - } - - rc = slapi_build_control(LDAP_CONTROL_SORTREQUEST, ber, 1, &ctrl); - - ber_free(ber, 1); - - if (LDAP_SUCCESS != rc) - return NULL; - - return ctrl; -} - -/**************************************************** - Functions that actually do things other - than config and startup -****************************************************/ - -/* we do search all values between newval and maxval asking the - * server to sort them, then we check the first free spot and - * use it as newval */ -static int dna_first_free_value(struct configEntry *config_entry, - PRUint64 *newval, - PRUint64 maxval, - PRUint64 increment) -{ - Slapi_Entry **entries = NULL; - Slapi_PBlock *pb = NULL; - LDAPControl **ctrls; - char *attrs[2]; - char *filter; - char *prefix; - char *type; - int preflen; - int result, status; - PRUint64 tmpval, sval, i; - char *strval = NULL; - - prefix = config_entry->prefix; - type = config_entry->type; - tmpval = *newval; - - attrs[0] = type; - attrs[1] = NULL; - - ctrls = (LDAPControl **)slapi_ch_calloc(2, sizeof(LDAPControl)); - if (NULL == ctrls) - return LDAP_OPERATIONS_ERROR; - - ctrls[0] = dna_build_sort_control(config_entry->type); - if (NULL == ctrls[0]) { - slapi_ch_free((void **)&ctrls); - return LDAP_OPERATIONS_ERROR; - } - - filter = slapi_ch_smprintf("(&%s(&(%s>=%s%llu)(%s<=%s%llu)))", - config_entry->filter, - type, prefix?prefix:"", tmpval, - type, prefix?prefix:"", maxval); - if (NULL == filter) { - ldap_control_free(ctrls[0]); - slapi_ch_free((void **)&ctrls); - return LDAP_OPERATIONS_ERROR; - } - - pb = slapi_pblock_new(); - if (NULL == pb) { - ldap_control_free(ctrls[0]); - slapi_ch_free((void **)&ctrls); - slapi_ch_free_string(&filter); - return LDAP_OPERATIONS_ERROR; - } - - slapi_search_internal_set_pb(pb, config_entry->scope, - LDAP_SCOPE_SUBTREE, filter, - attrs, 0, ctrls, - NULL, getPluginID(), 0); - slapi_search_internal_pb(pb); -/* - ldap_control_free(ctrls[0]); -*/ - slapi_ch_free_string(&filter); - - slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result); - if (LDAP_SUCCESS != result) { - status = LDAP_OPERATIONS_ERROR; - goto cleanup; - } - - slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, - &entries); - - if (NULL == entries || NULL == entries[0]) { - /* no values means we already have a good value */ - status = LDAP_SUCCESS; - goto cleanup; - } - - /* entries are sorted and filtered for value >= tval therefore if the - * first one does not match tval it means that the value is free, - * otherwise we need to cycle through values until we find a mismatch, - * the first mismatch is the first free pit */ - - preflen = prefix?strlen(prefix):0; - sval = 0; - for (i = 0; NULL != entries[i]; i++) { - strval = slapi_entry_attr_get_charptr(entries[i], type); - if (preflen) { - if (strlen(strval) <= preflen) { - /* something very wrong here ... */ - status = LDAP_OPERATIONS_ERROR; - goto cleanup; - } - strval = &strval[preflen-1]; - } - - errno = 0; - sval = strtoul(strval, 0, 0); - if (errno) { - /* something very wrong here ... */ - status = LDAP_OPERATIONS_ERROR; - goto cleanup; - } - slapi_ch_free_string(&strval); - - if (tmpval != sval) - break; - - if (maxval < sval) - break; - - tmpval += increment; - } - - *newval = tmpval; - status = LDAP_SUCCESS; - -cleanup: - slapi_ch_free_string(&strval); - slapi_free_search_results_internal(pb); - slapi_pblock_destroy(pb); - - return status; -} - -/* - * Perform ldap operationally atomic increment - * Return the next value to be assigned - * Method: - * 1. retrieve entry - * 2. do increment operations - * 3. remove current value, add new value in one operation - * 4. if failed, and less than 3 times, goto 1 - */ -static int dna_get_next_value(struct configEntry *config_entry, - char **next_value_ret) -{ - Slapi_PBlock *pb = NULL; - char *old_value = NULL; - Slapi_Entry *e = NULL; - Slapi_DN *dn = NULL; - char *attrlist[4]; - int attempts; - int ret; - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "--> dna_get_next_value\n"); - - /* get pre-requisites to search */ - dn = slapi_sdn_new_dn_byref(config_entry->dn); - attrlist[0] = DNA_NEXTVAL; - attrlist[1] = DNA_MAXVAL; - attrlist[2] = DNA_INTERVAL; - attrlist[3] = NULL; - - - /* the operation is constructed such that race conditions - * to increment the value are detected and avoided - one wins, - * one loses - however, there is no need for the server to compete - * with itself so we lock here - */ - - slapi_lock_mutex(g_new_value_lock); - - for (attempts = 0; attempts < 3; attempts++) { - - LDAPMod mod_add; - LDAPMod mod_delete; - LDAPMod *mods[3]; - char *delete_val[2]; - char *add_val[2]; - char new_value[16]; - char *interval; - char *max_value; - PRUint64 increment = 1; /* default increment */ - PRUint64 setval = 0; - PRUint64 newval = 0; - PRUint64 maxval = -1; - - /* do update */ - ret = slapi_search_internal_get_entry(dn, attrlist, &e, - getPluginID()); - if (LDAP_SUCCESS != ret) { - ret = LDAP_OPERATIONS_ERROR; - goto done; - } - - old_value = slapi_entry_attr_get_charptr(e, DNA_NEXTVAL); - if (NULL == old_value) { - ret = LDAP_OPERATIONS_ERROR; - goto done; - } - - setval = strtoul(old_value, 0, 0); - - max_value = slapi_entry_attr_get_charptr(e, DNA_MAXVAL); - if (max_value) { - maxval = strtoul(max_value, 0, 0); - slapi_ch_free_string(&max_value); - } - - /* if not present the default is 1 */ - interval = slapi_entry_attr_get_charptr(e, DNA_INTERVAL); - if (NULL != interval) { - increment = strtoul(interval, 0, 0); - } - - slapi_entry_free(e); - e = NULL; - - /* check the value is actually in range */ - - /* verify the new value is actually free and get the first - * one free if not*/ - ret = dna_first_free_value(config_entry, &setval, maxval, increment); - if (LDAP_SUCCESS != ret) - goto done; - - /* try for a new range or fail */ - if (setval > maxval) { - ret = dna_fix_maxval(dn, &setval, &maxval); - if (LDAP_SUCCESS != ret) { - slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM, - "dna_get_next_value: no more IDs available!!\n"); - goto done; - } - - /* verify the new value is actually free and get the first - * one free if not */ - ret = dna_first_free_value(config_entry, &setval, maxval, increment); - if (LDAP_SUCCESS != ret) - goto done; - } - - if (setval > maxval) { - ret = LDAP_OPERATIONS_ERROR; - goto done; - } - - newval = setval + increment; - - /* try for a new range or fail */ - if (newval > maxval) { - ret = dna_fix_maxval(dn, &newval, &maxval); - if (LDAP_SUCCESS != ret) { - slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM, - "dna_get_next_value: no more IDs available!!\n"); - goto done; - } - } - - /* try to set the new value */ - - sprintf(new_value, "%llu", newval); - - delete_val[0] = old_value; - delete_val[1] = 0; - - mod_delete.mod_op = LDAP_MOD_DELETE; - mod_delete.mod_type = DNA_NEXTVAL; - mod_delete.mod_values = delete_val; - - add_val[0] = new_value; - add_val[1] = 0; - - mod_add.mod_op = LDAP_MOD_ADD; - mod_add.mod_type = DNA_NEXTVAL; - mod_add.mod_values = add_val; - - mods[0] = &mod_delete; - mods[1] = &mod_add; - mods[2] = 0; - - pb = slapi_pblock_new(); - if (NULL == pb) { - ret = LDAP_OPERATIONS_ERROR; - goto done; - } - - slapi_modify_internal_set_pb(pb, config_entry->dn, - mods, 0, 0, getPluginID(), 0); - - slapi_modify_internal_pb(pb); - - slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret); - - slapi_pblock_destroy(pb); - pb = NULL; - slapi_ch_free_string(&interval); - slapi_ch_free_string(&old_value); - - if (LDAP_SUCCESS == ret) { - *next_value_ret = slapi_ch_smprintf("%llu", setval); - if (NULL == *next_value_ret) { - ret = LDAP_OPERATIONS_ERROR; - goto done; - } - - dna_notice_allocation(dn, newval); - goto done; - } - - if (LDAP_NO_SUCH_ATTRIBUTE != ret) { - /* not the result of a race - to change the value - */ - goto done; - } - } - - done: - - slapi_unlock_mutex(g_new_value_lock); - - if (LDAP_SUCCESS != ret) - slapi_ch_free_string(&old_value); - - if (dn) - slapi_sdn_free(&dn); - - if (e) - slapi_entry_free(e); - - if (pb) - slapi_pblock_destroy(pb); - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "<-- dna_get_next_value\n"); - - return ret; -} - -/* for mods and adds: - where dn's are supplied, the closest in scope - is used as long as the type and filter - are identical - otherwise all matches count -*/ - -static int dna_pre_op(Slapi_PBlock * pb, int modtype) -{ - char *dn = 0; - PRCList *list = 0; - struct configEntry *config_entry = 0; - struct slapi_entry *e = 0; - char *last_type = 0; - char *value = 0; - int generate = 0; - Slapi_Mods *smods = 0; - Slapi_Mod *smod = 0; - LDAPMod **mods; - int free_entry = 0; - char *errstr = NULL; - int ret = 0; - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "--> dna_pre_op\n"); - - /* Just bail if we aren't ready to service requests yet. */ - if (!g_plugin_started) - goto bail; - - if (0 == (dn = dna_get_dn(pb))) - goto bail; - - if (dna_dn_is_config(dn)) - goto bail; - - if (LDAP_CHANGETYPE_ADD == modtype) { - slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e); - } else { - /* 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); - */ - Slapi_DN *tmp_dn = slapi_sdn_new_dn_byref(dn); - if (tmp_dn) { - slapi_search_internal_get_entry(tmp_dn, 0, &e, getPluginID()); - slapi_sdn_free(&tmp_dn); - free_entry = 1; - } - - /* 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); - } - - if (0 == e) - goto bailmod; - - dna_read_lock(); - - if (!PR_CLIST_IS_EMPTY(dna_global_config)) { - list = PR_LIST_HEAD(dna_global_config); - - while (list != dna_global_config && LDAP_SUCCESS == ret) { - config_entry = (struct configEntry *) list; - - /* did we already service this type? */ - if (last_type) { - if (!slapi_attr_type_cmp(config_entry->type, last_type, 1)) - goto next; - } - - /* is the entry in scope? */ - if (config_entry->scope) { - if (!slapi_dn_issuffix(dn, config_entry->scope)) - goto next; - } - - /* does the entry match the filter? */ - if (config_entry->slapi_filter) { - if (LDAP_SUCCESS != slapi_vattr_filter_test(pb, - e, - config_entry-> - slapi_filter, 0)) - goto next; - } - - - if (LDAP_CHANGETYPE_ADD == modtype) { - /* does attribute contain the magic value - or is the type not there? - */ - value = - slapi_entry_attr_get_charptr(e, config_entry->type); - if ((value - && !slapi_UTF8CASECMP(config_entry->generate, value)) - || 0 == value) { - generate = 1; - } - } else { - /* check mods for magic value */ - Slapi_Mod *next_mod = slapi_mod_new(); - smod = slapi_mods_get_first_smod(smods, next_mod); - while (smod) { - char *type = (char *) - slapi_mod_get_type(smod); - - if (slapi_attr_types_equivalent(type, - config_entry->type)) { - struct berval *bv = - slapi_mod_get_first_value(smod); - int len = strlen(config_entry->generate); - - - if (len == bv->bv_len) { - if (!slapi_UTF8NCASECMP(bv->bv_val, - config_entry->generate, - len)) - - generate = 1; - break; - } - } - - slapi_mod_done(next_mod); - smod = slapi_mods_get_next_smod(smods, next_mod); - } - - slapi_mod_free(&next_mod); - } - - if (generate) { - char *new_value; - int len; - - /* create the value to add */ - ret = dna_get_next_value(config_entry, &value); - if (DNA_SUCCESS != ret) { - errstr = slapi_ch_smprintf("Allocation of a new value for" - " %s failed! Unable to proceed.", - config_entry->type); - break; - } - - len = strlen(value) + 1; - if (config_entry->prefix) { - len += strlen(config_entry->prefix); - } - - new_value = slapi_ch_malloc(len); - - if (config_entry->prefix) { - strcpy(new_value, config_entry->prefix); - strcat(new_value, value); - } else - strcpy(new_value, value); - - /* do the mod */ - if (LDAP_CHANGETYPE_ADD == modtype) { - /* add - add to entry */ - slapi_entry_attr_set_charptr(e, - config_entry->type, - new_value); - } else { - /* mod - add to mods */ - slapi_mods_add_string(smods, - LDAP_MOD_REPLACE, - config_entry->type, new_value); - } - - /* free up */ - slapi_ch_free_string(&value); - slapi_ch_free_string(&new_value); - - /* make sure we don't generate for this - * type again - */ - if (LDAP_SUCCESS == ret) { - last_type = config_entry->type; - } - - generate = 0; - } - next: - list = PR_NEXT_LINK(list); - } - } - - dna_unlock(); - - bailmod: - if (LDAP_CHANGETYPE_MODIFY == modtype) { - /* these are the mods you made, really, - * I didn't change them, honest, just had a quick look - */ - mods = slapi_mods_get_ldapmods_passout(smods); - slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods); - slapi_mods_free(&smods); - } - - bail: - - if (free_entry && e) - slapi_entry_free(e); - - if (ret) { - slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM, - "dna_pre_op: operation failure [%d]\n", ret); - slapi_send_ldap_result(pb, ret, NULL, errstr, 0, NULL); - slapi_ch_free((void **)&errstr); - ret = DNA_FAILURE; - } - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "<-- dna_pre_op\n"); - - return ret; -} - -static int dna_add_pre_op(Slapi_PBlock * pb) -{ - return dna_pre_op(pb, LDAP_CHANGETYPE_ADD); -} - -static int dna_mod_pre_op(Slapi_PBlock * pb) -{ - return dna_pre_op(pb, LDAP_CHANGETYPE_MODIFY); -} - -static int dna_config_check_post_op(Slapi_PBlock * pb) -{ - char *dn; - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "--> dna_config_check_post_op\n"); - - if ((dn = dna_get_dn(pb))) { - if (dna_dn_is_config(dn)) - loadPluginConfig(); - } - - slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, - "<-- dna_config_check_post_op\n"); - - return 0; -} - -/**************************************************** - End of - Functions that actually do things other - than config and startup -****************************************************/ - -/** - * debug functions to print config - */ -void dnaDumpConfig() -{ - PRCList *list; - - dna_read_lock(); - - if (!PR_CLIST_IS_EMPTY(dna_global_config)) { - list = PR_LIST_HEAD(dna_global_config); - while (list != dna_global_config) { - dnaDumpConfigEntry((struct configEntry *) list); - list = PR_NEXT_LINK(list); - } - } - - dna_unlock(); -} - - -void dnaDumpConfigEntry(struct configEntry * entry) -{ - printf("<- type --------------> %s\n", entry->type); - printf("<---- prefix ---------> %s\n", entry->prefix); - printf("<---- next value -----> %lu\n", entry->nextval); - printf("<---- interval -------> %lu\n", entry->interval); - printf("<---- generate flag --> %s\n", entry->generate); -} diff --git a/ipa-server/ipa-slapi-plugins/ipa-memberof/Makefile.am b/ipa-server/ipa-slapi-plugins/ipa-memberof/Makefile.am deleted file mode 100644 index d0ac7f935..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-memberof/Makefile.am +++ /dev/null @@ -1,43 +0,0 @@ -NULL = - -INCLUDES = \ - -I. \ - -I$(srcdir) \ - -DPREFIX=\""$(prefix)"\" \ - -DBINDIR=\""$(bindir)"\" \ - -DLIBDIR=\""$(libdir)"\" \ - -DLIBEXECDIR=\""$(libexecdir)"\" \ - -DDATADIR=\""$(datadir)"\" \ - $(MOZLDAP_CFLAGS) \ - $(KRB5_CFLAGS) \ - $(WARN_CFLAGS) \ - $(NULL) - -plugindir = $(libdir)/dirsrv/plugins -plugin_LTLIBRARIES = \ - libipa-memberof-plugin.la \ - $(NULL) - -libipa_memberof_plugin_la_SOURCES = \ - ipa-memberof.c \ - ipa-memberof_config.c \ - $(NULL) - -libipa_memberof_plugin_la_LDFLAGS = -avoid-version - -libipa_memberof_plugin_la_LIBADD = \ - $(MOZLDAP_LIBS) \ - $(NULL) - -appdir = $(IPA_DATA_DIR) -app_DATA = \ - memberof-conf.ldif \ - $(NULL) - -EXTRA_DIST = \ - $(app_DATA) \ - $(NULL) - -MAINTAINERCLEANFILES = \ - *~ \ - Makefile.in diff --git a/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.c b/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.c deleted file mode 100644 index 3baf2f6cf..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.c +++ /dev/null @@ -1,2244 +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; version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., 59 Temple - * Place, Suite 330, Boston, MA 02111-1307 USA. - * - * In addition, as a special exception, Red Hat, Inc. gives You the additional - * right to link the code of this Program with code not covered under the GNU - * General Public License ("Non-GPL Code") and to distribute linked combinations - * including the two, subject to the limitations in this paragraph. Non-GPL Code - * permitted under this exception must only link 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 GNU General Public License. Only Red Hat, Inc. may make changes or - * additions to the list of Approved Interfaces. You must obey the GNU General - * Public License in all respects for all of the Program code and other code used - * in conjunction with the Program except the Non-GPL Code covered by this - * exception. If you modify this file, you may extend this exception to your - * version of the file, but you are not obligated to do so. If you do not wish to - * provide this exception without modification, you must delete this exception - * statement from your version and license this file solely under the GPL without - * exception. - * - * Authors: - * Pete Rowley <prowley@redhat.com> - * - * Copyright (C) 2007 Red Hat, Inc. - * All rights reserved. - * END COPYRIGHT BLOCK - **/ - -/* The memberof plugin updates the memberof attribute of entries - * based on modifications performed on groupofuniquenames entries - * - * In addition the plugin provides a DS task that may be started - * administrative clients and that creates the initial memberof - * list for imported entries and/or fixes the memberof list of - * existing entries that have inconsistent state (for example, - * if the memberof attribute was incorrectly edited directly) - * - * To start the memberof task add an entry like: - * - * dn: cn=mytask, cn=memberof task, cn=tasks, cn=config - * objectClass: top - * objectClass: extensibleObject - * cn: mytask - * basedn: dc=example, dc=com - * filter: (uid=test4) - * - * where "basedn" is required and refers to the top most node to perform the - * task on, and where "filter" is an optional attribute that provides a filter - * describing the entries to be worked on - */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <dirsrv/slapi-plugin.h> - -#include "string.h" -#include "nspr.h" - -#include "ipa-memberof.h" - -static Slapi_PluginDesc pdesc = { "ipamo", "FreeIPA project", "FreeIPA/1.0", - "IPA memberof plugin" }; - -static void* _PluginID = NULL; -static Slapi_Mutex *memberof_operation_lock = 0; -MemberOfConfig *qsortConfig = 0; - -typedef struct _memberofstringll -{ - const char *dn; - void *next; -} memberofstringll; - -typedef struct _memberof_get_groups_data -{ - MemberOfConfig *config; - Slapi_Value *memberdn_val; - Slapi_ValueSet **groupvals; -} memberof_get_groups_data; - -/****** secrets *********/ -#ifndef SLAPI_TASK_PUBLIC -/*from FDS slap.h - * until we get a proper api for access - */ -#define TASK_RUNNING_AS_TASK 0x0 - -/****************************************************************************** - * Online tasks interface (to support import, export, etc) - * After some cleanup, we could consider making these public. - */ -struct _slapi_task { - struct _slapi_task *next; - char *task_dn; - int task_exitcode; /* for the end user */ - int task_state; /* (see above) */ - int task_progress; /* number between 0 and task_work */ - int task_work; /* "units" of work to be done */ - int task_flags; /* (see above) */ - - /* it is the task's responsibility to allocate this memory & free it: */ - char *task_status; /* transient status info */ - char *task_log; /* appended warnings, etc */ - - void *task_private; /* for use by backends */ - TaskCallbackFn cancel; /* task has been cancelled by user */ - TaskCallbackFn destructor; /* task entry is being destroyed */ - int task_refcount; -}; - -static void slapi_task_set_data(Slapi_Task *task, void *data) -{ - if (task) { - task->task_private = data; - } -} - -/* - * Retrieve some opaque task specific data from the task. - */ -static void * slapi_task_get_data(Slapi_Task *task) -{ - if (task) { - return task->task_private; - } -} - -static void slapi_task_begin(Slapi_Task *task, int total_work) -{ - if (task) { - task->task_work = total_work; - task->task_progress = 0; - task->task_state = SLAPI_TASK_RUNNING; - slapi_task_status_changed(task); - } -} - -static void slapi_task_inc_progress(Slapi_Task *task) -{ - if (task) { - task->task_progress++; - slapi_task_status_changed(task); - } -} - -static void slapi_task_finish(Slapi_Task *task, int rc) -{ - if (task) { - task->task_exitcode = rc; - task->task_state = SLAPI_TASK_FINISHED; - slapi_task_status_changed(task); - } -} - -static void slapi_task_set_destructor_fn(Slapi_Task *task, TaskCallbackFn func) -{ - if (task) { - task->destructor = func; - } -} - -#endif /* !SLAPI_TASK_PUBLIC */ -/****** secrets ********/ - -/*** function prototypes ***/ - -/* exported functions */ -int ipamo_postop_init(Slapi_PBlock *pb ); - -/* plugin callbacks */ -static int memberof_postop_del(Slapi_PBlock *pb ); -static int memberof_postop_modrdn(Slapi_PBlock *pb ); -static int memberof_postop_modify(Slapi_PBlock *pb ); -static int memberof_postop_add(Slapi_PBlock *pb ); -static int memberof_postop_start(Slapi_PBlock *pb); -static int memberof_postop_close(Slapi_PBlock *pb); - -/* supporting cast */ -static int memberof_oktodo(Slapi_PBlock *pb); -static char *memberof_getdn(Slapi_PBlock *pb); -static int memberof_modop_one(Slapi_PBlock *pb, MemberOfConfig *config, int mod_op, - char *op_this, char *op_to); -static int memberof_modop_one_r(Slapi_PBlock *pb, MemberOfConfig *config, int mod_op, - char *group_dn, char *op_this, char *op_to, memberofstringll *stack); -static int memberof_add_one(Slapi_PBlock *pb, MemberOfConfig *config, char *addthis, - char *addto); -static int memberof_del_one(Slapi_PBlock *pb, MemberOfConfig *config, char *delthis, - char *delfrom); -static int memberof_mod_smod_list(Slapi_PBlock *pb, MemberOfConfig *config, int mod, - char *groupdn, Slapi_Mod *smod); -static int memberof_add_smod_list(Slapi_PBlock *pb, MemberOfConfig *config, - char *groupdn, Slapi_Mod *smod); -static int memberof_del_smod_list(Slapi_PBlock *pb, MemberOfConfig *config, - char *groupdn, Slapi_Mod *smod); -static int memberof_mod_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, int mod, - char *groupdn, Slapi_Attr *attr); -static int memberof_mod_attr_list_r(Slapi_PBlock *pb, MemberOfConfig *config, - int mod, char *group_dn, char *op_this, Slapi_Attr *attr, memberofstringll *stack); -static int memberof_add_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, - char *groupdn, Slapi_Attr *attr); -static int memberof_del_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, - char *groupdn, Slapi_Attr *attr); -static int memberof_moddn_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, - char *pre_dn, char *post_dn, Slapi_Attr *attr); -static int memberof_replace_list(Slapi_PBlock *pb, MemberOfConfig *config, char *group_dn); -static void memberof_set_plugin_id(void * plugin_id); -static void *memberof_get_plugin_id(); -static int memberof_compare(MemberOfConfig *config, const void *a, const void *b); -static int memberof_qsort_compare(const void *a, const void *b); -static void memberof_load_array(Slapi_Value **array, Slapi_Attr *attr); -static int memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, char *dn); -static int memberof_call_foreach_dn(Slapi_PBlock *pb, char *dn, - char *type, plugin_search_entry_callback callback, void *callback_data); -static int memberof_is_direct_member(MemberOfConfig *config, Slapi_Value *groupdn, - Slapi_Value *memberdn); -static Slapi_ValueSet *memberof_get_groups(MemberOfConfig *config, char *memberdn); -static int memberof_get_groups_r(MemberOfConfig *config, char *memberdn, - memberof_get_groups_data *data); -static int memberof_get_groups_callback(Slapi_Entry *e, void *callback_data); -static int memberof_test_membership(Slapi_PBlock *pb, MemberOfConfig *config, - char *group_dn); -static int memberof_test_membership_callback(Slapi_Entry *e, void *callback_data); -static int memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data); -static int memberof_replace_dn_type_callback(Slapi_Entry *e, void *callback_data); -static int memberof_replace_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, - char *pre_dn, char *post_dn); -static int memberof_modop_one_replace_r(Slapi_PBlock *pb, MemberOfConfig *config, - int mod_op, char *group_dn, char *op_this, char *replace_with, char *op_to, - memberofstringll *stack); -static int memberof_task_add(Slapi_PBlock *pb, Slapi_Entry *e, - Slapi_Entry *eAfter, int *returncode, char *returntext, - void *arg); -static void memberof_task_destructor(Slapi_Task *task); -static const char *fetch_attr(Slapi_Entry *e, const char *attrname, - const char *default_val); -static void memberof_fixup_task_thread(void *arg); -static int memberof_fix_memberof(MemberOfConfig *config, char *dn, char *filter_str); -static int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data); - - -/*** implementation ***/ - - -/*** exported functions ***/ - -/* - * ipamo_postop_init() - * - * Register plugin call backs - * - */ -int -ipamo_postop_init(Slapi_PBlock *pb) -{ - int ret = 0; - char *memberof_plugin_identity = 0; - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "--> ipamo_postop_init\n" ); - /* - * Get plugin identity and stored it for later use - * Used for internal operations - */ - - slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &memberof_plugin_identity); - PR_ASSERT (memberof_plugin_identity); - memberof_set_plugin_id(memberof_plugin_identity); - - if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, - SLAPI_PLUGIN_VERSION_01 ) != 0 || - slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, - (void *)&pdesc ) != 0 || - slapi_pblock_set( pb, SLAPI_PLUGIN_POST_DELETE_FN, - (void *) memberof_postop_del ) != 0 || - slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODRDN_FN, - (void *) memberof_postop_modrdn ) != 0 || - slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODIFY_FN, - (void *) memberof_postop_modify ) != 0 || - slapi_pblock_set( pb, SLAPI_PLUGIN_POST_ADD_FN, - (void *) memberof_postop_add ) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, - (void *) memberof_postop_start ) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, - (void *) memberof_postop_close ) != 0) - { - slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, - "ipamo_postop_init failed\n" ); - ret = -1; - } - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "<-- ipamo_postop_init\n" ); - return ret; -} - -/* - * memberof_postop_start() - * - * Do plugin start up stuff - * - */ -int memberof_postop_start(Slapi_PBlock *pb) -{ - int rc = 0; - Slapi_Entry *config_e = NULL; /* entry containing plugin config */ - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "--> memberof_postop_start\n" ); - - memberof_operation_lock = slapi_new_mutex(); - if(0 == memberof_operation_lock) - { - rc = -1; - goto bail; - } - - if ( slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &config_e ) != 0 ) { - slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, - "missing config entry\n" ); - rc = -1; - goto bail; - } - - if (( rc = memberof_config( config_e )) != LDAP_SUCCESS ) { - slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, - "configuration failed (%s)\n", ldap_err2string( rc )); - return( -1 ); - } - - rc = slapi_task_register_handler("memberof task", memberof_task_add); - if(rc) - { - goto bail; - } - - /* - * TODO: start up operation actor thread - * need to get to a point where server failure - * or shutdown doesn't hose our operations - * so we should create a task entry that contains - * all required information to complete the operation - * then the tasks can be restarted safely if - * interrupted - */ - -bail: - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "<-- memberof_postop_start\n" ); - - return rc; -} - -/* - * memberof_postop_close() - * - * Do plugin shut down stuff - * - */ -int memberof_postop_close(Slapi_PBlock *pb) -{ - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "--> memberof_postop_close\n" ); - - - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "<-- memberof_postop_close\n" ); - return 0; -} - -/* - * memberof_postop_del() - * - * All entries with a memberOf attribute that contains the group DN get retrieved - * and have the their memberOf attribute regenerated (it is far too complex and - * error prone to attempt to change only those dn values involved in this case - - * mainly because the deleted group may itself be a member of other groups which - * may be members of other groups etc. in a big recursive mess involving dependency - * chains that must be created and traversed in order to decide if an entry should - * really have those groups removed too) - */ -int memberof_postop_del(Slapi_PBlock *pb) -{ - int ret = 0; - MemberOfConfig configCopy = {0, 0, 0, 0}; - char *dn; - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "--> memberof_postop_del\n" ); - - if(memberof_oktodo(pb) && (dn = memberof_getdn(pb))) - { - struct slapi_entry *e = NULL; - - slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &e ); - - /* We need to get the config lock first. Trying to get the - * config lock after we already hold the op lock can cause - * a deadlock. */ - memberof_rlock_config(); - /* copy config so it doesn't change out from under us */ - memberof_copy_config(&configCopy, memberof_get_config()); - memberof_unlock_config(); - - /* get the memberOf operation lock */ - memberof_lock(); - - /* remove this group DN from the - * membership lists of groups - */ - memberof_del_dn_from_groups(pb, &configCopy, dn); - - /* is the entry of interest as a group? */ - if(e && !slapi_filter_test_simple(e, configCopy.group_filter)) - { - Slapi_Attr *attr = 0; - - if(0 == slapi_entry_attr_find(e, configCopy.groupattr, &attr)) - { - memberof_del_attr_list(pb, &configCopy, dn, attr); - } - } - - memberof_unlock(); - - memberof_free_config(&configCopy); - } - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "<-- memberof_postop_del\n" ); - return ret; -} - -typedef struct _memberof_del_dn_data -{ - char *dn; - char *type; -} memberof_del_dn_data; - -int memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, char *dn) -{ - memberof_del_dn_data data = {dn, config->groupattr}; - - return memberof_call_foreach_dn(pb, dn, - config->groupattr, memberof_del_dn_type_callback, &data); -} - -int memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data) -{ - int rc = 0; - LDAPMod mod; - LDAPMod *mods[2]; - char *val[2]; - Slapi_PBlock *mod_pb = 0; - - mod_pb = slapi_pblock_new(); - - mods[0] = &mod; - mods[1] = 0; - - val[0] = ((memberof_del_dn_data *)callback_data)->dn; - val[1] = 0; - - mod.mod_op = LDAP_MOD_DELETE; - mod.mod_type = ((memberof_del_dn_data *)callback_data)->type; - mod.mod_values = val; - - slapi_modify_internal_set_pb( - mod_pb, slapi_entry_get_dn(e), - mods, 0, 0, - memberof_get_plugin_id(), 0); - - slapi_modify_internal_pb(mod_pb); - - slapi_pblock_get(mod_pb, - SLAPI_PLUGIN_INTOP_RESULT, - &rc); - - slapi_pblock_destroy(mod_pb); - - return rc; -} - -/* - * Does a callback search of "type=dn" under the db suffix that "dn" is in. - * If "dn" is a user, you'd want "type" to be "member". If "dn" is a group, - * you could want type to be either "member" or "memberOf" depending on the - * case. - */ -int memberof_call_foreach_dn(Slapi_PBlock *pb, char *dn, - char *type, plugin_search_entry_callback callback, void *callback_data) -{ - int rc = 0; - Slapi_PBlock *search_pb = slapi_pblock_new(); - Slapi_Backend *be = 0; - Slapi_DN *sdn = 0; - Slapi_DN *base_sdn = 0; - char *filter_str = 0; - - /* get the base dn for the backend we are in - (we don't support having members and groups in - different backends - issues with offline / read only backends) - */ - sdn = slapi_sdn_new_dn_byref(dn); - be = slapi_be_select(sdn); - if(be) - { - base_sdn = (Slapi_DN*)slapi_be_getsuffix(be,0); - } - - if(base_sdn) - { - filter_str = slapi_ch_smprintf("(%s=%s)", type, dn); - } - - if(filter_str) - { - slapi_search_internal_set_pb(search_pb, slapi_sdn_get_dn(base_sdn), - LDAP_SCOPE_SUBTREE, filter_str, 0, 0, - 0, 0, - memberof_get_plugin_id(), - 0); - - slapi_search_internal_callback_pb(search_pb, - callback_data, - 0, callback, - 0); - } - - slapi_sdn_free(&sdn); - slapi_pblock_destroy(search_pb); - slapi_ch_free_string(&filter_str); - return rc; -} - -/* - * memberof_postop_modrdn() - * - * All entries with a memberOf attribute that contains the old group DN get retrieved - * and have the old group DN deleted and the new group DN added to their memberOf attribute - */ -int memberof_postop_modrdn(Slapi_PBlock *pb) -{ - int ret = 0; - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "--> memberof_postop_modrdn\n" ); - - if(memberof_oktodo(pb)) - { - MemberOfConfig *mainConfig = 0; - MemberOfConfig configCopy = {0, 0, 0, 0}; - struct slapi_entry *pre_e = NULL; - struct slapi_entry *post_e = NULL; - char *pre_dn = 0; - char *post_dn = 0; - int interested = 0; - - slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &pre_e ); - slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &post_e ); - - if(pre_e && post_e) - { - pre_dn = slapi_entry_get_ndn(pre_e); - post_dn = slapi_entry_get_ndn(post_e); - } - - /* is the entry of interest? */ - memberof_rlock_config(); - mainConfig = memberof_get_config(); - if(pre_dn && post_dn && - !slapi_filter_test_simple(post_e, mainConfig->group_filter)) - { - interested = 1; - /* copy config so it doesn't change out from under us */ - memberof_copy_config(&configCopy, mainConfig); - } - memberof_unlock_config(); - - if(interested) - { - Slapi_Attr *attr = 0; - - memberof_lock(); - - /* get a list of member attributes present in the group - * entry that is being renamed. */ - if(0 == slapi_entry_attr_find(post_e, configCopy.groupattr, &attr)) - { - memberof_moddn_attr_list(pb, &configCopy, pre_dn, post_dn, attr); - } - - /* modrdn must change the dns in groups that have - * this group as a member. - */ - memberof_replace_dn_from_groups(pb, &configCopy, pre_dn, post_dn); - - memberof_unlock(); - - memberof_free_config(&configCopy); - } - } - - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "<-- memberof_postop_modrdn\n" ); - return ret; -} - -typedef struct _replace_dn_data -{ - char *pre_dn; - char *post_dn; - char *type; -} replace_dn_data; - -int memberof_replace_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, - char *pre_dn, char *post_dn) -{ - replace_dn_data data = {pre_dn, post_dn, config->groupattr}; - - return memberof_call_foreach_dn(pb, pre_dn, config->groupattr, - memberof_replace_dn_type_callback, &data); -} - - -int memberof_replace_dn_type_callback(Slapi_Entry *e, void *callback_data) -{ - int rc = 0; - LDAPMod delmod; - LDAPMod addmod; - LDAPMod *mods[3]; - char *delval[2]; - char *addval[2]; - Slapi_PBlock *mod_pb = 0; - - mod_pb = slapi_pblock_new(); - - mods[0] = &delmod; - mods[1] = &addmod; - mods[2] = 0; - - delval[0] = ((replace_dn_data *)callback_data)->pre_dn; - delval[1] = 0; - - delmod.mod_op = LDAP_MOD_DELETE; - delmod.mod_type = ((replace_dn_data *)callback_data)->type; - delmod.mod_values = delval; - - addval[0] = ((replace_dn_data *)callback_data)->post_dn; - addval[1] = 0; - - addmod.mod_op = LDAP_MOD_ADD; - addmod.mod_type = ((replace_dn_data *)callback_data)->type; - addmod.mod_values = addval; - - slapi_modify_internal_set_pb( - mod_pb, slapi_entry_get_dn(e), - mods, 0, 0, - memberof_get_plugin_id(), 0); - - slapi_modify_internal_pb(mod_pb); - - slapi_pblock_get(mod_pb, - SLAPI_PLUGIN_INTOP_RESULT, - &rc); - - slapi_pblock_destroy(mod_pb); - - return rc; -} - -/* - * memberof_postop_modify() - * - * Added members are retrieved and have the group DN added to their memberOf attribute - * Deleted members are retrieved and have the group DN deleted from their memberOf attribute - * On replace of the membership attribute values: - * 1. Sort old and new values - * 2. Iterate through both lists at same time - * 3. Any value not in old list but in new list - add group DN to memberOf attribute - * 4. Any value in old list but not in new list - remove group DN from memberOf attribute - * - * Note: this will suck for large groups but nonetheless is optimal (it's linear) given - * current restrictions i.e. originally adding members in sorted order would allow - * us to sort one list only (the new one) but that is under server control, not this plugin - */ -int memberof_postop_modify(Slapi_PBlock *pb) -{ - int ret = 0; - char *dn = 0; - Slapi_Mods *smods = 0; - Slapi_Mod *smod = 0; - LDAPMod **mods; - Slapi_Mod *next_mod = 0; - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "--> memberof_postop_modify\n" ); - - if(memberof_oktodo(pb) && - (dn = memberof_getdn(pb))) - { - int config_copied = 0; - MemberOfConfig *mainConfig = 0; - MemberOfConfig configCopy = {0, 0, 0, 0}; - - /* get the mod set */ - slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); - smods = slapi_mods_new(); - slapi_mods_init_byref(smods, mods); - - next_mod = slapi_mod_new(); - smod = slapi_mods_get_first_smod(smods, next_mod); - while(smod) - { - int interested = 0; - char *type = (char *)slapi_mod_get_type(smod); - - /* We only want to copy the config if we encounter an - * operation that we need to act on. We also want to - * only copy the config the first time it's needed so - * it remains the same for all mods in the operation, - * despite any config changes that may be made. */ - if (!config_copied) - { - memberof_rlock_config(); - mainConfig = memberof_get_config(); - - if(slapi_attr_types_equivalent(type, mainConfig->groupattr)) - { - interested = 1; - /* copy config so it doesn't change out from under us */ - memberof_copy_config(&configCopy, mainConfig); - config_copied = 1; - } - - memberof_unlock_config(); - } else { - if(slapi_attr_types_equivalent(type, configCopy.groupattr)) - { - interested = 1; - } - } - - if(interested) - { - int op = slapi_mod_get_operation(smod); - - memberof_lock(); - - /* the modify op decides the function */ - switch(op & ~LDAP_MOD_BVALUES) - { - case LDAP_MOD_ADD: - { - /* add group DN to targets */ - memberof_add_smod_list(pb, &configCopy, dn, smod); - break; - } - - case LDAP_MOD_DELETE: - { - /* If there are no values in the smod, we should - * just do a replace instead. The user is just - * trying to delete all members from this group - * entry, which the replace code deals with. */ - if (slapi_mod_get_num_values(smod) == 0) - { - memberof_replace_list(pb, &configCopy, dn); - } - else - { - /* remove group DN from target values in smod*/ - memberof_del_smod_list(pb, &configCopy, dn, smod); - } - break; - } - - case LDAP_MOD_REPLACE: - { - /* replace current values */ - memberof_replace_list(pb, &configCopy, dn); - break; - } - - default: - { - slapi_log_error( - SLAPI_LOG_PLUGIN, - MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_postop_modify: unknown mod type\n" ); - break; - } - } - - memberof_unlock(); - } - - slapi_mod_done(next_mod); - smod = slapi_mods_get_next_smod(smods, next_mod); - } - - if (config_copied) - { - memberof_free_config(&configCopy); - } - - slapi_mod_free(&next_mod); - slapi_mods_free(&smods); - } - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "<-- memberof_postop_modify\n" ); - return ret; -} - - -/* - * memberof_postop_add() - * - * All members in the membership attribute of the new entry get retrieved - * and have the group DN added to their memberOf attribute - */ -int memberof_postop_add(Slapi_PBlock *pb) -{ - int ret = 0; - int interested = 0; - char *dn = 0; - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "--> memberof_postop_add\n" ); - - if(memberof_oktodo(pb) && (dn = memberof_getdn(pb))) - { - MemberOfConfig *mainConfig = 0; - MemberOfConfig configCopy = {0, 0, 0, 0}; - struct slapi_entry *e = NULL; - - slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &e ); - - - /* is the entry of interest? */ - memberof_rlock_config(); - mainConfig = memberof_get_config(); - if(e && !slapi_filter_test_simple(e, mainConfig->group_filter)) - { - interested = 1; - /* copy config so it doesn't change out from under us */ - memberof_copy_config(&configCopy, mainConfig); - } - memberof_unlock_config(); - - if(interested) - { - Slapi_Attr *attr = 0; - - memberof_lock(); - - if(0 == slapi_entry_attr_find(e, configCopy.groupattr, &attr)) - { - memberof_add_attr_list(pb, &configCopy, dn, attr); - } - - memberof_unlock(); - - memberof_free_config(&configCopy); - } - } - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "<-- memberof_postop_add\n" ); - return ret; -} - -/*** Support functions ***/ - -/* - * memberof_oktodo() - * - * Check that the op succeeded - * Note: we also respond to replicated ops so we don't test for that - * this does require that the memberOf attribute not be replicated - * and this means that memberof is consistent with local state - * not the network system state - * - */ -int memberof_oktodo(Slapi_PBlock *pb) -{ - int ret = 1; - int oprc = 0; - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "--> memberof_postop_oktodo\n" ); - - if(slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0) - { - slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_postop_oktodo: could not get parameters\n" ); - ret = -1; - } - - /* this plugin should only execute if the operation succeeded - */ - if(oprc != 0) - { - ret = 0; - } - - slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM, - "<-- memberof_postop_oktodo\n" ); - - return ret; -} - -/* - * memberof_getdn() - * - * Get dn of target entry - * - */ -char *memberof_getdn(Slapi_PBlock *pb) -{ - char *dn = 0; - - slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn); - - return dn; -} - -/* - * memberof_modop_one() - * - * Perform op on memberof attribute of op_to using op_this as the value - * However, if op_to happens to be a group, we must arrange for the group - * members to have the mod performed on them instead, and we must take - * care to not recurse when we have visted a group before - * - * Also, we must not delete entries that are a member of the group - */ -int memberof_modop_one(Slapi_PBlock *pb, MemberOfConfig *config, int mod_op, - char *op_this, char *op_to) -{ - return memberof_modop_one_r(pb, config, mod_op, op_this, op_this, op_to, 0); -} - -/* memberof_modop_one_r() - * - * recursive function to perform above (most things don't need the replace arg) - */ - -int memberof_modop_one_r(Slapi_PBlock *pb, MemberOfConfig *config, int mod_op, - char *group_dn, char *op_this, char *op_to, memberofstringll *stack) -{ - return memberof_modop_one_replace_r( - pb, config, mod_op, group_dn, op_this, 0, op_to, stack); -} - -/* memberof_modop_one_replace_r() - * - * recursive function to perform above (with added replace arg) - */ -int memberof_modop_one_replace_r(Slapi_PBlock *pb, MemberOfConfig *config, - int mod_op, char *group_dn, char *op_this, char *replace_with, - char *op_to, memberofstringll *stack) -{ - int rc = 0; - LDAPMod mod; - LDAPMod replace_mod; - LDAPMod *mods[3]; - char *val[2]; - char *replace_val[2]; - Slapi_PBlock *mod_pb = 0; - char *attrlist[2] = {config->groupattr,0}; - Slapi_DN *op_to_sdn = 0; - Slapi_Entry *e = 0; - memberofstringll *ll = 0; - char *op_str = 0; - Slapi_Value *to_dn_val = slapi_value_new_string(op_to); - Slapi_Value *this_dn_val = slapi_value_new_string(op_this); - - /* determine if this is a group op or single entry */ - op_to_sdn = slapi_sdn_new_dn_byref(op_to); - slapi_search_internal_get_entry( op_to_sdn, attrlist, - &e, memberof_get_plugin_id()); - if(!e) - { - /* In the case of a delete, we need to worry about the - * missing entry being a nested group. There's a small - * window where another thread may have deleted a nested - * group that our group_dn entry refers to. This has the - * potential of us missing some indirect member entries - * that need to be updated. */ - if(LDAP_MOD_DELETE == mod_op) - { - Slapi_PBlock *search_pb = slapi_pblock_new(); - Slapi_DN *base_sdn = 0; - Slapi_Backend *be = 0; - char *filter_str = 0; - int n_entries = 0; - - /* We can't tell for sure if the op_to entry is a - * user or a group since the entry doesn't exist - * anymore. We can safely ignore the missing entry - * if no other entries have a memberOf attribute that - * points to the missing entry. */ - be = slapi_be_select(op_to_sdn); - if(be) - { - base_sdn = (Slapi_DN*)slapi_be_getsuffix(be,0); - } - - if(base_sdn) - { - filter_str = slapi_ch_smprintf("(%s=%s)", - config->memberof_attr, op_to); - } - - if(filter_str) - { - slapi_search_internal_set_pb(search_pb, slapi_sdn_get_dn(base_sdn), - LDAP_SCOPE_SUBTREE, filter_str, 0, 0, 0, 0, - memberof_get_plugin_id(), 0); - - if (slapi_search_internal_pb(search_pb)) - { - /* get result and log an error */ - int res = 0; - slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res); - slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_modop_one_replace_r: error searching for members: " - "%d", res); - } else { - slapi_pblock_get(search_pb, SLAPI_NENTRIES, &n_entries); - - if(n_entries > 0) - { - /* We want to fixup the membership for the - * entries that referred to the missing group - * entry. This will fix the references to - * the missing group as well as the group - * represented by op_this. */ - memberof_test_membership(pb, config, op_to); - } - } - - slapi_free_search_results_internal(search_pb); - slapi_ch_free_string(&filter_str); - } - - slapi_pblock_destroy(search_pb); - } - - goto bail; - } - - if(LDAP_MOD_DELETE == mod_op) - { - op_str = "DELETE"; - } - else if(LDAP_MOD_ADD == mod_op) - { - op_str = "ADD"; - } - else if(LDAP_MOD_REPLACE == mod_op) - { - op_str = "REPLACE"; - } - else - { - op_str = "UNKNOWN"; - } - - slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_modop_one_replace_r: %s %s in %s\n" - ,op_str, op_this, op_to); - - if(!slapi_filter_test_simple(e, config->group_filter)) - { - /* group */ - Slapi_Value *ll_dn_val = 0; - Slapi_Attr *members = 0; - - ll = stack; - - /* have we been here before? */ - while(ll) - { - ll_dn_val = slapi_value_new_string(ll->dn); - - if(0 == memberof_compare(config, &ll_dn_val, &to_dn_val)) - { - slapi_value_free(&ll_dn_val); - - /* someone set up infinitely - recursive groups - bail out */ - slapi_log_error( SLAPI_LOG_PLUGIN, - MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_modop_one_replace_r: group recursion" - " detected in %s\n" - ,op_to); - goto bail; - } - - slapi_value_free(&ll_dn_val); - ll = ll->next; - } - - /* do op on group */ - slapi_log_error( SLAPI_LOG_PLUGIN, - MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_modop_one_replace_r: descending into group %s\n", - op_to); - /* Add the nested group's DN to the stack so we can detect loops later. */ - ll = (memberofstringll*)slapi_ch_malloc(sizeof(memberofstringll)); - ll->dn = op_to; - ll->next = stack; - - slapi_entry_attr_find( e, config->groupattr, &members ); - if(members) - { - memberof_mod_attr_list_r(pb, config, mod_op, group_dn, op_this, members, ll); - } - - { - /* crazyness follows: - * strict-aliasing doesn't like the required cast - * to void for slapi_ch_free so we are made to - * juggle to get a normal thing done - */ - void *pll = ll; - slapi_ch_free(&pll); - ll = 0; - } - } - /* continue with operation */ - { - /* We want to avoid listing a group as a memberOf itself - * in case someone set up a circular grouping. - */ - if (0 == memberof_compare(config, &this_dn_val, &to_dn_val)) - { - slapi_log_error( SLAPI_LOG_PLUGIN, - MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_modop_one_replace_r: not processing memberOf " - "operations on self entry: %s\n", this_dn_val); - goto bail; - } - - /* For add and del modify operations, we just regenerate the - * memberOf attribute. */ - if(LDAP_MOD_DELETE == mod_op || LDAP_MOD_ADD == mod_op) - { - /* find parent groups and replace our member attr */ - memberof_fix_memberof_callback(e, config); - } else { - /* single entry - do mod */ - mod_pb = slapi_pblock_new(); - - mods[0] = &mod; - if(LDAP_MOD_REPLACE == mod_op) - { - mods[1] = &replace_mod; - mods[2] = 0; - } - else - { - mods[1] = 0; - } - - val[0] = op_this; - val[1] = 0; - mod.mod_op = LDAP_MOD_REPLACE == mod_op?LDAP_MOD_DELETE:mod_op; - mod.mod_type = config->memberof_attr; - mod.mod_values = val; - - if(LDAP_MOD_REPLACE == mod_op) - { - replace_val[0] = replace_with; - replace_val[1] = 0; - - replace_mod.mod_op = LDAP_MOD_ADD; - replace_mod.mod_type = config->memberof_attr; - replace_mod.mod_values = replace_val; - } - - slapi_modify_internal_set_pb( - mod_pb, op_to, - mods, 0, 0, - memberof_get_plugin_id(), 0); - - slapi_modify_internal_pb(mod_pb); - - slapi_pblock_get(mod_pb, - SLAPI_PLUGIN_INTOP_RESULT, - &rc); - - slapi_pblock_destroy(mod_pb); - } - } - -bail: - slapi_sdn_free(&op_to_sdn); - slapi_value_free(&to_dn_val); - slapi_value_free(&this_dn_val); - slapi_entry_free(e); - return rc; -} - - -/* - * memberof_add_one() - * - * Add addthis DN to the memberof attribute of addto - * - */ -int memberof_add_one(Slapi_PBlock *pb, MemberOfConfig *config, char *addthis, char *addto) -{ - return memberof_modop_one(pb, config, LDAP_MOD_ADD, addthis, addto); -} - -/* - * memberof_del_one() - * - * Delete delthis DN from the memberof attribute of delfrom - * - */ -int memberof_del_one(Slapi_PBlock *pb, MemberOfConfig *config, char *delthis, char *delfrom) -{ - return memberof_modop_one(pb, config, LDAP_MOD_DELETE, delthis, delfrom); -} - -/* - * memberof_mod_smod_list() - * - * Perform mod for group DN to the memberof attribute of the list of targets - * - */ -int memberof_mod_smod_list(Slapi_PBlock *pb, MemberOfConfig *config, int mod, - char *group_dn, Slapi_Mod *smod) -{ - int rc = 0; - struct berval *bv = slapi_mod_get_first_value(smod); - int last_size = 0; - char *last_str = 0; - - while(bv) - { - char *dn_str = 0; - - if(last_size > bv->bv_len) - { - dn_str = last_str; - } - else - { - int the_size = (bv->bv_len * 2) + 1; - - if(last_str) - slapi_ch_free_string(&last_str); - - dn_str = (char*)slapi_ch_malloc(the_size); - - last_str = dn_str; - last_size = the_size; - } - - memset(dn_str, 0, last_size); - - strncpy(dn_str, bv->bv_val, (size_t)bv->bv_len); - - memberof_modop_one(pb, config, mod, group_dn, dn_str); - - bv = slapi_mod_get_next_value(smod); - } - - if(last_str) - slapi_ch_free_string(&last_str); - - return rc; -} - -/* - * memberof_add_smod_list() - * - * Add group DN to the memberof attribute of the list of targets - * - */ -int memberof_add_smod_list(Slapi_PBlock *pb, MemberOfConfig *config, - char *groupdn, Slapi_Mod *smod) -{ - return memberof_mod_smod_list(pb, config, LDAP_MOD_ADD, groupdn, smod); -} - - -/* - * memberof_del_smod_list() - * - * Remove group DN from the memberof attribute of the list of targets - * - */ -int memberof_del_smod_list(Slapi_PBlock *pb, MemberOfConfig *config, - char *groupdn, Slapi_Mod *smod) -{ - return memberof_mod_smod_list(pb, config, LDAP_MOD_DELETE, groupdn, smod); -} - -/** - * Plugin identity mgmt - */ -void memberof_set_plugin_id(void * plugin_id) -{ - _PluginID=plugin_id; -} - -void * memberof_get_plugin_id() -{ - return _PluginID; -} - - -/* - * memberof_mod_attr_list() - * - * Perform mod for group DN to the memberof attribute of the list of targets - * - */ -int memberof_mod_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, int mod, - char *group_dn, Slapi_Attr *attr) -{ - return memberof_mod_attr_list_r(pb, config, mod, group_dn, group_dn, attr, 0); -} - -int memberof_mod_attr_list_r(Slapi_PBlock *pb, MemberOfConfig *config, int mod, - char *group_dn, char *op_this, Slapi_Attr *attr, memberofstringll *stack) -{ - int rc = 0; - Slapi_Value *val = 0; - Slapi_Value *op_this_val = 0; - int last_size = 0; - char *last_str = 0; - int hint = slapi_attr_first_value(attr, &val); - - op_this_val = slapi_value_new_string(op_this); - - while(val) - { - char *dn_str = 0; - struct berval *bv = 0; - - /* We don't want to process a memberOf operation on ourselves. */ - if(0 != memberof_compare(config, &val, &op_this_val)) - { - bv = (struct berval *)slapi_value_get_berval(val); - - if(last_size > bv->bv_len) - { - dn_str = last_str; - } - else - { - int the_size = (bv->bv_len * 2) + 1; - - if(last_str) - slapi_ch_free_string(&last_str); - - dn_str = (char*)slapi_ch_malloc(the_size); - - last_str = dn_str; - last_size = the_size; - } - - memset(dn_str, 0, last_size); - - strncpy(dn_str, bv->bv_val, (size_t)bv->bv_len); - - /* If we're doing a replace (as we would in the MODRDN case), we need - * to specify the new group DN value */ - if(mod == LDAP_MOD_REPLACE) - { - memberof_modop_one_replace_r(pb, config, mod, group_dn, op_this, - group_dn, dn_str, stack); - } - else - { - memberof_modop_one_r(pb, config, mod, group_dn, op_this, dn_str, stack); - } - } - - hint = slapi_attr_next_value(attr, hint, &val); - } - - slapi_value_free(&op_this_val); - - if(last_str) - slapi_ch_free_string(&last_str); - - return rc; -} - -/* - * memberof_add_attr_list() - * - * Add group DN to the memberof attribute of the list of targets - * - */ -int memberof_add_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, char *groupdn, - Slapi_Attr *attr) -{ - return memberof_mod_attr_list(pb, config, LDAP_MOD_ADD, groupdn, attr); -} - -/* - * memberof_del_attr_list() - * - * Remove group DN from the memberof attribute of the list of targets - * - */ -int memberof_del_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, char *groupdn, - Slapi_Attr *attr) -{ - return memberof_mod_attr_list(pb, config, LDAP_MOD_DELETE, groupdn, attr); -} - -/* - * memberof_moddn_attr_list() - * - * Perform mod for group DN to the memberof attribute of the list of targets - * - */ -int memberof_moddn_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, - char *pre_dn, char *post_dn, Slapi_Attr *attr) -{ - int rc = 0; - Slapi_Value *val = 0; - int last_size = 0; - char *last_str = 0; - int hint = slapi_attr_first_value(attr, &val); - - while(val) - { - char *dn_str = 0; - struct berval *bv = (struct berval *)slapi_value_get_berval(val); - - if(last_size > bv->bv_len) - { - dn_str = last_str; - } - else - { - int the_size = (bv->bv_len * 2) + 1; - - if(last_str) - slapi_ch_free_string(&last_str); - - dn_str = (char*)slapi_ch_malloc(the_size); - - last_str = dn_str; - last_size = the_size; - } - - memset(dn_str, 0, last_size); - - strncpy(dn_str, bv->bv_val, (size_t)bv->bv_len); - - memberof_modop_one_replace_r(pb, config, LDAP_MOD_REPLACE, - post_dn, pre_dn, post_dn, dn_str, 0); - - hint = slapi_attr_next_value(attr, hint, &val); - } - - if(last_str) - slapi_ch_free_string(&last_str); - - return rc; -} - -/* memberof_get_groups() - * - * Gets a list of all groups that an entry is a member of. - * This is done by looking only at member attribute values. - * A Slapi_ValueSet* is returned. It is up to the caller to - * free it. - */ -Slapi_ValueSet *memberof_get_groups(MemberOfConfig *config, char *memberdn) -{ - Slapi_Value *memberdn_val = slapi_value_new_string(memberdn); - Slapi_ValueSet *groupvals = slapi_valueset_new(); - memberof_get_groups_data data = {config, memberdn_val, &groupvals}; - - memberof_get_groups_r(config, memberdn, &data); - - slapi_value_free(&memberdn_val); - - return groupvals; -} - -int memberof_get_groups_r(MemberOfConfig *config, char *memberdn, memberof_get_groups_data *data) -{ - /* Search for member=<memberdn> - * For each match, add it to the list, recurse and do same search */ - return memberof_call_foreach_dn(NULL, memberdn, config->groupattr, - memberof_get_groups_callback, data); -} - -/* memberof_get_groups_callback() - * - * Callback to perform work of memberof_get_groups() - */ -int memberof_get_groups_callback(Slapi_Entry *e, void *callback_data) -{ - char *group_dn = slapi_entry_get_dn(e); - Slapi_Value *group_dn_val = 0; - Slapi_ValueSet *groupvals = *((memberof_get_groups_data*)callback_data)->groupvals; - - /* get the DN of the group */ - group_dn_val = slapi_value_new_string(group_dn); - - /* check if e is the same as our original member entry */ - if (0 == memberof_compare(((memberof_get_groups_data*)callback_data)->config, - &((memberof_get_groups_data*)callback_data)->memberdn_val, &group_dn_val)) - { - /* A recursive group caused us to find our original - * entry we passed to memberof_get_groups(). We just - * skip processing this entry. */ - slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_get_groups_callback: group recursion" - " detected in %s\n" ,group_dn); - slapi_value_free(&group_dn_val); - goto bail; - - } - - /* have we been here before? */ - if (groupvals && - slapi_valueset_find(((memberof_get_groups_data*)callback_data)->config->group_slapiattr, - groupvals, group_dn_val)) - { - /* we either hit a recursive grouping, or an entry is - * a member of a group through multiple paths. Either - * way, we can just skip processing this entry since we've - * already gone through this part of the grouping hierarchy. */ - slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, - "memberof_get_groups_callback: possible group recursion" - " detected in %s\n" ,group_dn); - slapi_value_free(&group_dn_val); - goto bail; - } - - /* Push group_dn_val into the valueset. This memory is now owned - * by the valueset. */ - slapi_valueset_add_value_ext(groupvals, group_dn_val, SLAPI_VALUE_FLAG_PASSIN); - - /* now recurse to find parent groups of e */ - memberof_get_groups_r(((memberof_get_groups_data*)callback_data)->config, - group_dn, callback_data); - - bail: - return 0; -} - -/* memberof_is_direct_member() - * - * tests for direct membership of memberdn in group groupdn - * returns non-zero when true, zero otherwise - */ -int memberof_is_direct_member(MemberOfConfig *config, Slapi_Value *groupdn, - Slapi_Value *memberdn) -{ - int rc = 0; - Slapi_DN *sdn = 0; - char *attrlist[2] = {config->groupattr,0}; - Slapi_Entry *group_e = 0; - Slapi_Attr *attr = 0; - - sdn = slapi_sdn_new_dn_byref(slapi_value_get_string(groupdn)); - - slapi_search_internal_get_entry(sdn, attrlist, - &group_e, memberof_get_plugin_id()); - - if(group_e) - { - slapi_entry_attr_find(group_e, config->groupattr, &attr ); - if(attr) - { - rc = 0 == slapi_attr_value_find( - attr, slapi_value_get_berval(memberdn)); - } - slapi_entry_free(group_e); - } - - slapi_sdn_free(&sdn); - return rc; -} - -/* memberof_test_membership() - * - * Finds all entries who are a "memberOf" the group - * represented by "group_dn". For each matching entry, we - * call memberof_test_membership_callback(). - * - * for each attribute in the memberof attribute - * determine if the entry is still a member. - * - * test each for direct membership - * move groups entry is memberof to member group - * test remaining groups for membership in member groups - * iterate until a pass fails to move a group over to member groups - * remaining groups should be deleted - */ -int memberof_test_membership(Slapi_PBlock *pb, MemberOfConfig *config, char *group_dn) -{ - return memberof_call_foreach_dn(pb, group_dn, config->memberof_attr, - memberof_test_membership_callback , config); -} - -/* - * memberof_test_membership_callback() - * - * A callback function to do the work of memberof_test_membership(). - * Note that this not only tests membership, but updates the memberOf - * attributes in the entry to be correct. - */ -int memberof_test_membership_callback(Slapi_Entry *e, void *callback_data) -{ - int rc = 0; - Slapi_Attr *attr = 0; - int total = 0; - Slapi_Value **member_array = 0; - Slapi_Value **candidate_array = 0; - Slapi_Value *entry_dn = 0; - MemberOfConfig *config = (MemberOfConfig *)callback_data; - - entry_dn = slapi_value_new_string(slapi_entry_get_dn(e)); - - if(0 == entry_dn) - { - goto bail; - } - - /* divide groups into member and non-member lists */ - slapi_entry_attr_find(e, config->memberof_attr, &attr ); - if(attr) - { - slapi_attr_get_numvalues( attr, &total); - if(total) - { - Slapi_Value *val = 0; - int hint = 0; - int c_index = 0; - int m_index = 0; - int member_found = 1; - int outer_index = 0; - - candidate_array = - (Slapi_Value**) - slapi_ch_malloc(sizeof(Slapi_Value*)*total); - memset(candidate_array, 0, sizeof(Slapi_Value*)*total); - member_array = - (Slapi_Value**) - slapi_ch_malloc(sizeof(Slapi_Value*)*total); - memset(member_array, 0, sizeof(Slapi_Value*)*total); - - hint = slapi_attr_first_value(attr, &val); - - while(val) - { - /* test for direct membership */ - if(memberof_is_direct_member(config, val, entry_dn)) - { - /* it is a member */ - member_array[m_index] = val; - m_index++; - } - else - { - /* not a member, still a candidate */ - candidate_array[c_index] = val; - c_index++; - } - - hint = slapi_attr_next_value(attr, hint, &val); - } - - /* now iterate over members testing for membership - in candidate groups and moving candidates to members - when successful, quit when a full iteration adds no - new members - */ - while(member_found) - { - member_found = 0; - - /* For each group that this entry is a verified member of, see if - * any of the candidate groups are members. If they are, add them - * to the list of verified groups that this entry is a member of. - */ - while(outer_index < m_index) - { - int inner_index = 0; - - while(inner_index < c_index) - { - /* Check for a special value in this position - * that indicates that the candidate was moved - * to the member array. */ - if((void*)1 == - candidate_array[inner_index]) - { - /* was moved, skip */ - inner_index++; - continue; - } - - if(memberof_is_direct_member( - config, - candidate_array[inner_index], - member_array[outer_index])) - { - member_array[m_index] = - candidate_array - [inner_index]; - m_index++; - - candidate_array[inner_index] = - (void*)1; - - member_found = 1; - } - - inner_index++; - } - - outer_index++; - } - } - - /* here we are left only with values to delete - from the memberof attribute in the candidate list - */ - outer_index = 0; - while(outer_index < c_index) - { - /* Check for a special value in this position - * that indicates that the candidate was moved - * to the member array. */ - if((void*)1 == candidate_array[outer_index]) - { - /* item moved, skip */ - outer_index++; - continue; - } - - memberof_del_one( - 0, config, - (char*)slapi_value_get_string( - candidate_array[outer_index]), - (char*)slapi_value_get_string(entry_dn)); - - outer_index++; - } - { - /* crazyness follows: - * strict-aliasing doesn't like the required cast - * to void for slapi_ch_free so we are made to - * juggle to get a normal thing done - */ - void *pmember_array = member_array; - void *pcandidate_array = candidate_array; - slapi_ch_free(&pcandidate_array); - slapi_ch_free(&pmember_array); - candidate_array = 0; - member_array = 0; - } - } - } - -bail: - slapi_value_free(&entry_dn); - - return rc; -} - -/* - * memberof_replace_list() - * - * Perform replace the group DN list in the memberof attribute of the list of targets - * - */ -int memberof_replace_list(Slapi_PBlock *pb, MemberOfConfig *config, char *group_dn) -{ - struct slapi_entry *pre_e = NULL; - struct slapi_entry *post_e = NULL; - Slapi_Attr *pre_attr = 0; - Slapi_Attr *post_attr = 0; - - slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &pre_e ); - slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &post_e ); - - if(pre_e && post_e) - { - slapi_entry_attr_find( pre_e, config->groupattr, &pre_attr ); - slapi_entry_attr_find( post_e, config->groupattr, &post_attr ); - } - - if(pre_attr || post_attr) - { - int pre_total = 0; - int post_total = 0; - Slapi_Value **pre_array = 0; - Slapi_Value **post_array = 0; - int pre_index = 0; - int post_index = 0; - - /* create arrays of values */ - if(pre_attr) - { - slapi_attr_get_numvalues( pre_attr, &pre_total); - } - - if(post_attr) - { - slapi_attr_get_numvalues( post_attr, &post_total); - } - - /* Stash a plugin global pointer here and have memberof_qsort_compare - * use it. We have to do this because we use memberof_qsort_compare - * as the comparator function for qsort, which requires the function - * to only take two void* args. This is thread-safe since we only - * store and use the pointer while holding the memberOf operation - * lock. */ - qsortConfig = config; - - if(pre_total) - { - pre_array = - (Slapi_Value**) - slapi_ch_malloc(sizeof(Slapi_Value*)*pre_total); - memberof_load_array(pre_array, pre_attr); - qsort( - pre_array, - pre_total, - sizeof(Slapi_Value*), - memberof_qsort_compare); - } - - if(post_total) - { - post_array = - (Slapi_Value**) - slapi_ch_malloc(sizeof(Slapi_Value*)*post_total); - memberof_load_array(post_array, post_attr); - qsort( - post_array, - post_total, - sizeof(Slapi_Value*), - memberof_qsort_compare); - } - - qsortConfig = 0; - - - /* work through arrays, following these rules: - in pre, in post, do nothing - in pre, not in post, delete from entry - not in pre, in post, add to entry - */ - while(pre_index < pre_total || post_index < post_total) - { - if(pre_index == pre_total) - { - /* add the rest of post */ - memberof_add_one( - pb, config, - group_dn, - (char*)slapi_value_get_string( - post_array[post_index])); - - post_index++; - } - else if(post_index == post_total) - { - /* delete the rest of pre */ - memberof_del_one( - pb, config, - group_dn, - (char*)slapi_value_get_string( - pre_array[pre_index])); - - pre_index++; - } - else - { - /* decide what to do */ - int cmp = memberof_compare( - config, - &(pre_array[pre_index]), - &(post_array[post_index])); - - if(cmp < 0) - { - /* delete pre array */ - memberof_del_one( - pb, config, - group_dn, - (char*)slapi_value_get_string( - pre_array[pre_index])); - - pre_index++; - } - else if(cmp > 0) - { - /* add post array */ - memberof_add_one( - pb, config, - group_dn, - (char*)slapi_value_get_string( - post_array[post_index])); - - post_index++; - } - else - { - /* do nothing, advance */ - pre_index++; - post_index++; - } - } - } - slapi_ch_free((void **)&pre_array); - slapi_ch_free((void **)&post_array); - } - - return 0; -} - -/* memberof_load_array() - * - * put attribute values in array structure - */ -void memberof_load_array(Slapi_Value **array, Slapi_Attr *attr) -{ - Slapi_Value *val = 0; - int hint = slapi_attr_first_value(attr, &val); - - while(val) - { - *array = val; - array++; - hint = slapi_attr_next_value(attr, hint, &val); - } -} - -/* memberof_compare() - * - * compare two attr values - */ -int memberof_compare(MemberOfConfig *config, const void *a, const void *b) -{ - Slapi_Value *val1 = *((Slapi_Value **)a); - Slapi_Value *val2 = *((Slapi_Value **)b); - - return slapi_attr_value_cmp( - config->group_slapiattr, - slapi_value_get_berval(val1), - slapi_value_get_berval(val2)); -} - -/* memberof_qsort_compare() - * - * This is a version of memberof_compare that uses a plugin - * global copy of the config. We'd prefer to pass in a copy - * of config that is local to the running thread, but we can't - * do this since qsort is using us as a comparator function. - * We should only use this function when using qsort, and only - * when the memberOf lock is acquired. - */ -int memberof_qsort_compare(const void *a, const void *b) -{ - Slapi_Value *val1 = *((Slapi_Value **)a); - Slapi_Value *val2 = *((Slapi_Value **)b); - - return slapi_attr_value_cmp( - qsortConfig->group_slapiattr, - slapi_value_get_berval(val1), - slapi_value_get_berval(val2)); -} - -void memberof_lock() -{ - slapi_lock_mutex(memberof_operation_lock); -} - -void memberof_unlock() -{ - slapi_unlock_mutex(memberof_operation_lock); -} - -typedef struct _task_data -{ - char *dn; - char *filter_str; -} task_data; - -void memberof_fixup_task_thread(void *arg) -{ - MemberOfConfig configCopy = {0, 0, 0, 0}; - Slapi_Task *task = (Slapi_Task *)arg; - task_data *td = NULL; - int rc = 0; - - /* Fetch our task data from the task */ - td = (task_data *)slapi_task_get_data(task); - - slapi_task_begin(task, 1); - slapi_task_log_notice(task, "Memberof task starts (arg: %s) ...\n", - td->filter_str); - - /* We need to get the config lock first. Trying to get the - * config lock after we already hold the op lock can cause - * a deadlock. */ - memberof_rlock_config(); - /* copy config so it doesn't change out from under us */ - memberof_copy_config(&configCopy, memberof_get_config()); - memberof_unlock_config(); - - /* get the memberOf operation lock */ - memberof_lock(); - - /* do real work */ - rc = memberof_fix_memberof(&configCopy, td->dn, td->filter_str); - - /* release the memberOf operation lock */ - memberof_unlock(); - - memberof_free_config(&configCopy); - - slapi_task_log_notice(task, "Memberof task finished."); - slapi_task_log_status(task, "Memberof task finished."); - slapi_task_inc_progress(task); - - /* this will queue the destruction of the task */ - slapi_task_finish(task, rc); -} - -/* extract a single value from the entry (as a string) -- if it's not in the - * entry, the default will be returned (which can be NULL). - * you do not need to free anything returned by this. - */ -const char *fetch_attr(Slapi_Entry *e, const char *attrname, - const char *default_val) -{ - Slapi_Attr *attr; - Slapi_Value *val = NULL; - - if (slapi_entry_attr_find(e, attrname, &attr) != 0) - return default_val; - slapi_attr_first_value(attr, &val); - return slapi_value_get_string(val); -} - -int memberof_task_add(Slapi_PBlock *pb, Slapi_Entry *e, - Slapi_Entry *eAfter, int *returncode, char *returntext, - void *arg) -{ - PRThread *thread = NULL; - int rv = SLAPI_DSE_CALLBACK_OK; - task_data *mytaskdata = NULL; - Slapi_Task *task = NULL; - const char *filter; - const char *dn = 0; - - *returncode = LDAP_SUCCESS; - /* get arg(s) */ - if ((dn = fetch_attr(e, "basedn", 0)) == NULL) - { - *returncode = LDAP_OBJECT_CLASS_VIOLATION; - rv = SLAPI_DSE_CALLBACK_ERROR; - goto out; - } - - if ((filter = fetch_attr(e, "filter", "(objectclass=inetuser)")) == NULL) - { - *returncode = LDAP_OBJECT_CLASS_VIOLATION; - rv = SLAPI_DSE_CALLBACK_ERROR; - goto out; - } - - /* setup our task data */ - mytaskdata = (task_data*)slapi_ch_malloc(sizeof(task_data)); - if (mytaskdata == NULL) - { - *returncode = LDAP_OPERATIONS_ERROR; - rv = SLAPI_DSE_CALLBACK_ERROR; - goto out; - } - mytaskdata->dn = slapi_ch_strdup(dn); - mytaskdata->filter_str = slapi_ch_strdup(filter); - - /* allocate new task now */ - task = slapi_new_task(slapi_entry_get_ndn(e)); - - /* register our destructor for cleaning up our private data */ - slapi_task_set_destructor_fn(task, memberof_task_destructor); - - /* Stash a pointer to our data in the task */ - slapi_task_set_data(task, mytaskdata); - - /* start the sample task as a separate thread */ - thread = PR_CreateThread(PR_USER_THREAD, memberof_fixup_task_thread, - (void *)task, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, - PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE); - if (thread == NULL) - { - slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, - "unable to create task thread!\n"); - *returncode = LDAP_OPERATIONS_ERROR; - rv = SLAPI_DSE_CALLBACK_ERROR; - slapi_task_finish(task, *returncode); - } else { - rv = SLAPI_DSE_CALLBACK_OK; - } - -out: - return rv; -} - -void -memberof_task_destructor(Slapi_Task *task) -{ - if (task) { - task_data *mydata = (task_data *)slapi_task_get_data(task); - if (mydata) { - slapi_ch_free_string(&mydata->dn); - slapi_ch_free_string(&mydata->filter_str); - /* Need to cast to avoid a compiler warning */ - slapi_ch_free((void **)&mydata); - } - } -} - -int memberof_fix_memberof(MemberOfConfig *config, char *dn, char *filter_str) -{ - int rc = 0; - Slapi_PBlock *search_pb = slapi_pblock_new(); - - slapi_search_internal_set_pb(search_pb, dn, - LDAP_SCOPE_SUBTREE, filter_str, 0, 0, - 0, 0, - memberof_get_plugin_id(), - 0); - - rc = slapi_search_internal_callback_pb(search_pb, - config, - 0, memberof_fix_memberof_callback, - 0); - - slapi_pblock_destroy(search_pb); - - return rc; -} - -/* memberof_fix_memberof_callback() - * Add initial and/or fix up broken group list in entry - * - * 1. Remove all present memberOf values - * 2. Add direct group membership memberOf values - * 3. Add indirect group membership memberOf values - */ -int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data) -{ - int rc = 0; - char *dn = slapi_entry_get_dn(e); - MemberOfConfig *config = (MemberOfConfig *)callback_data; - memberof_del_dn_data del_data = {0, config->memberof_attr}; - Slapi_ValueSet *groups = 0; - - /* get a list of all of the groups this user belongs to */ - groups = memberof_get_groups(config, dn); - - /* If we found some groups, replace the existing memberOf attribute - * with the found values. */ - if (groups && slapi_valueset_count(groups)) - { - Slapi_PBlock *mod_pb = slapi_pblock_new(); - Slapi_Value *val = 0; - Slapi_Mod *smod; - LDAPMod **mods = (LDAPMod **) slapi_ch_malloc(2 * sizeof(LDAPMod *)); - int hint = 0; - - /* NGK - need to allocate the smod */ - smod = slapi_mod_new(); - slapi_mod_init(smod, 0); - slapi_mod_set_operation(smod, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES); - slapi_mod_set_type(smod, config->memberof_attr); - - /* Loop through all of our values and add them to smod */ - hint = slapi_valueset_first_value(groups, &val); - while (val) - { - /* this makes a copy of the berval */ - slapi_mod_add_value(smod, slapi_value_get_berval(val)); - hint = slapi_valueset_next_value(groups, hint, &val); - } - - mods[0] = slapi_mod_get_ldapmod_passout(smod); - mods[1] = 0; - - slapi_modify_internal_set_pb( - mod_pb, dn, mods, 0, 0, - memberof_get_plugin_id(), 0); - - slapi_modify_internal_pb(mod_pb); - - slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc); - - ldap_mods_free(mods, 1); - slapi_mod_free(&smod); - /* NGK - need to free the smod */ - slapi_pblock_destroy(mod_pb); - } else { - /* No groups were found, so remove the memberOf attribute - * from this entry. */ - memberof_del_dn_type_callback(e, &del_data); - } - - slapi_valueset_free(groups); - - return rc; -} - diff --git a/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.h b/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.h deleted file mode 100644 index 3e7b5cf4b..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof.h +++ /dev/null @@ -1,100 +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; version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., 59 Temple - * Place, Suite 330, Boston, MA 02111-1307 USA. - * - * In addition, as a special exception, Red Hat, Inc. gives You the additional - * right to link the code of this Program with code not covered under the GNU - * General Public License ("Non-GPL Code") and to distribute linked combinations - * including the two, subject to the limitations in this paragraph. Non-GPL Code - * permitted under this exception must only link 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 GNU General Public License. Only Red Hat, Inc. may make changes or - * additions to the list of Approved Interfaces. You must obey the GNU General - * Public License in all respects for all of the Program code and other code used - * in conjunction with the Program except the Non-GPL Code covered by this - * exception. If you modify this file, you may extend this exception to your - * version of the file, but you are not obligated to do so. If you do not wish to - * provide this exception without modification, you must delete this exception - * statement from your version and license this file solely under the GPL without - * exception. - * - * - * Copyright (C) 2008 Red Hat, Inc. - * All rights reserved. - * END COPYRIGHT BLOCK **/ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -/* - * ipa-memberof.h - memberOf shared definitions - * - */ - -#ifndef _MEMBEROF_H_ -#define _MEMBEROF_H_ - -#include <stdio.h> -#include <string.h> -#include <time.h> -#include <sys/types.h> -#include <dirsrv/slapi-plugin.h> -#include <nspr.h> - -/****** secrets *********/ -/*from FDS slapi-private.h - * until we get a proper api for access - */ -#define SLAPI_DSE_CALLBACK_OK (1) -#define SLAPI_DSE_CALLBACK_ERROR (-1) -#define SLAPI_DSE_CALLBACK_DO_NOT_APPLY (0) -#define SLAPI_DSE_RETURNTEXT_SIZE 512 -#define DSE_FLAG_PREOP 0x0002 -/*********** end secrets **********/ -/* - * macros - */ -#define MEMBEROF_PLUGIN_SUBSYSTEM "ipa-memberof-plugin" /* used for logging */ -#define MEMBEROF_GROUP_ATTR "member" -#define MEMBEROF_ATTR "memberOf" - - -/* - * structs - */ -typedef struct memberofconfig { - char *groupattr; - char *memberof_attr; - Slapi_Filter *group_filter; - Slapi_Attr *group_slapiattr; -} MemberOfConfig; - - -/* - * functions - */ -int memberof_config(Slapi_Entry *config_e); -void memberof_copy_config(MemberOfConfig *dest, MemberOfConfig *src); -void memberof_free_config(MemberOfConfig *config); -MemberOfConfig *memberof_get_config(); -void memberof_lock(); -void memberof_unlock(); -void memberof_rlock_config(); -void memberof_wlock_config(); -void memberof_unlock_config(); - - -#endif /* _MEMBEROF_H_ */ diff --git a/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof_config.c b/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof_config.c deleted file mode 100644 index b2bd374ad..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-memberof/ipa-memberof_config.c +++ /dev/null @@ -1,312 +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; version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., 59 Temple - * Place, Suite 330, Boston, MA 02111-1307 USA. - * - * In addition, as a special exception, Red Hat, Inc. gives You the additional - * right to link the code of this Program with code not covered under the GNU - * General Public License ("Non-GPL Code") and to distribute linked combinations - * including the two, subject to the limitations in this paragraph. Non-GPL Code - * permitted under this exception must only link 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 GNU General Public License. Only Red Hat, Inc. may make changes or - * additions to the list of Approved Interfaces. You must obey the GNU General - * Public License in all respects for all of the Program code and other code used - * in conjunction with the Program except the Non-GPL Code covered by this - * exception. If you modify this file, you may extend this exception to your - * version of the file, but you are not obligated to do so. If you do not wish to - * provide this exception without modification, you must delete this exception - * statement from your version and license this file solely under the GPL without - * exception. - * - * - * Copyright (C) 2008 Red Hat, Inc. - * All rights reserved. - * END COPYRIGHT BLOCK **/ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -/* - * memberof_config.c - configuration-related code for memberOf plug-in - * - */ - -#include <plstr.h> - -#include "ipa-memberof.h" - -#define MEMBEROF_CONFIG_FILTER "(objectclass=*)" - -/* - * The configuration attributes are contained in the plugin entry e.g. - * cn=MemberOf Plugin,cn=plugins,cn=config - * - * Configuration is a two step process. The first pass is a validation step which - * occurs pre-op - check inputs and error out if bad. The second pass actually - * applies the changes to the run time config. - */ - - -/* - * function prototypes - */ -static int memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, - int *returncode, char *returntext, void *arg); -static int memberof_search (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, - int *returncode, char *returntext, void *arg) -{ - return SLAPI_DSE_CALLBACK_OK; -} - -/* - * static variables - */ -/* This is the main configuration which is updated from dse.ldif. The - * config will be copied when it is used by the plug-in to prevent it - * being changed out from under a running memberOf operation. */ -static MemberOfConfig theConfig; -static PRRWLock *memberof_config_lock = 0; -static int inited = 0; - - -static int dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, - int *returncode, char *returntext, void *arg) -{ - *returncode = LDAP_UNWILLING_TO_PERFORM; - return SLAPI_DSE_CALLBACK_ERROR; -} - -/* - * memberof_config() - * - * Read configuration and create a configuration data structure. - * This is called after the server has configured itself so we can - * perform checks with regards to suffixes if it ever becomes - * necessary. - * Returns an LDAP error code (LDAP_SUCCESS if all goes well). - */ -int -memberof_config(Slapi_Entry *config_e) -{ - int returncode = LDAP_SUCCESS; - char returntext[SLAPI_DSE_RETURNTEXT_SIZE]; - - if ( inited ) { - slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, - "only one memberOf plugin instance can be used\n" ); - return( LDAP_PARAM_ERROR ); - } - - /* initialize the RW lock to protect the main config */ - memberof_config_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "memberof_config_lock"); - - /* initialize fields */ - memberof_apply_config(NULL, NULL, config_e, - &returncode, returntext, NULL); - - /* config DSE must be initialized before we get here */ - if (returncode == LDAP_SUCCESS) { - const char *config_dn = slapi_entry_get_dn_const(config_e); - slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, - config_dn, LDAP_SCOPE_BASE, MEMBEROF_CONFIG_FILTER, - dont_allow_that,NULL); - slapi_config_register_callback(SLAPI_OPERATION_MODRDN, DSE_FLAG_PREOP, - config_dn, LDAP_SCOPE_BASE, MEMBEROF_CONFIG_FILTER, - dont_allow_that, NULL); - slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, - config_dn, LDAP_SCOPE_BASE, MEMBEROF_CONFIG_FILTER, - dont_allow_that, NULL); - slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, - config_dn, LDAP_SCOPE_BASE, MEMBEROF_CONFIG_FILTER, - memberof_search,NULL); - } - - inited = 1; - - if (returncode != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, - "Error %d: %s\n", returncode, returntext); - } - - return returncode; -} - - -/* - * memberof_apply_config() - * - * Just use hardcoded config values. - */ -static int -memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, - int *returncode, char *returntext, void *arg) -{ - char *groupattr = NULL; - char *memberof_attr = NULL; - char *filter_str = NULL; - - *returncode = LDAP_SUCCESS; - - groupattr = slapi_ch_strdup(MEMBEROF_GROUP_ATTR); - memberof_attr = slapi_ch_strdup(MEMBEROF_ATTR); - - /* We want to be sure we don't change the config in the middle of - * a memberOf operation, so we obtain an exclusive lock here */ - memberof_wlock_config(); - - if (!theConfig.groupattr || - (groupattr && PL_strcmp(theConfig.groupattr, groupattr))) { - slapi_ch_free_string(&theConfig.groupattr); - theConfig.groupattr = groupattr; - groupattr = NULL; /* config now owns memory */ - - /* We allocate a Slapi_Attr using the groupattr for - * convenience in our memberOf comparison functions */ - slapi_attr_free(&theConfig.group_slapiattr); - theConfig.group_slapiattr = slapi_attr_new(); - slapi_attr_init(theConfig.group_slapiattr, theConfig.groupattr); - - /* The filter is based off of the groupattr, so we - * update it here too. */ - slapi_filter_free(theConfig.group_filter, 1); - filter_str = slapi_ch_smprintf("(%s=*)", theConfig.groupattr); - theConfig.group_filter = slapi_str2filter(filter_str); - slapi_ch_free_string(&filter_str); - } - - if (!theConfig.memberof_attr || - (memberof_attr && PL_strcmp(theConfig.memberof_attr, memberof_attr))) { - slapi_ch_free_string(&theConfig.memberof_attr); - theConfig.memberof_attr = memberof_attr; - memberof_attr = NULL; /* config now owns memory */ - } - - /* release the lock */ - memberof_unlock_config(); - - slapi_ch_free_string(&groupattr); - slapi_ch_free_string(&memberof_attr); - - if (*returncode != LDAP_SUCCESS) - { - return SLAPI_DSE_CALLBACK_ERROR; - } - else - { - return SLAPI_DSE_CALLBACK_OK; - } -} - -/* - * memberof_copy_config() - * - * Makes a copy of the config in src. This function will free the - * elements of dest if they already exist. This should only be called - * if you hold the memberof config lock if src was obtained with - * memberof_get_config(). - */ -void -memberof_copy_config(MemberOfConfig *dest, MemberOfConfig *src) -{ - if (dest && src) - { - /* Check if the copy is already up to date */ - if (!dest->groupattr || (src->groupattr - && PL_strcmp(dest->groupattr, src->groupattr))) - { - slapi_ch_free_string(&dest->groupattr); - dest->groupattr = slapi_ch_strdup(src->groupattr); - slapi_filter_free(dest->group_filter, 1); - dest->group_filter = slapi_filter_dup(src->group_filter); - slapi_attr_free(&dest->group_slapiattr); - dest->group_slapiattr = slapi_attr_dup(src->group_slapiattr); - } - - if (!dest->memberof_attr || (src->memberof_attr - && PL_strcmp(dest->memberof_attr, src->memberof_attr))) - { - slapi_ch_free_string(&dest->memberof_attr); - dest->memberof_attr = slapi_ch_strdup(src->memberof_attr); - } - } -} - -/* - * memberof_free_config() - * - * Free's the contents of a config structure. - */ -void -memberof_free_config(MemberOfConfig *config) -{ - if (config) - { - slapi_ch_free_string(&config->groupattr); - slapi_filter_free(config->group_filter, 1); - slapi_attr_free(&config->group_slapiattr); - slapi_ch_free_string(&config->memberof_attr); - } -} - -/* - * memberof_get_config() - * - * Returns a pointer to the main config. You should call - * memberof_rlock_config() first so the main config doesn't - * get modified out from under you. - */ -MemberOfConfig * -memberof_get_config() -{ - return &theConfig; -} - -/* - * memberof_rlock_config() - * - * Gets a non-exclusive lock on the main config. This will - * prevent the config from being changed out from under you - * while you read it, but it will still allow other threads - * to read the config at the same time. - */ -void -memberof_rlock_config() -{ - PR_RWLock_Rlock(memberof_config_lock); -} - -/* - * memberof_wlock_config() - * - * Gets an exclusive lock on the main config. This should - * be called if you need to write to the main config. - */ -void -memberof_wlock_config() -{ - PR_RWLock_Wlock(memberof_config_lock); -} - -/* - * memberof_unlock_config() - * - * Unlocks the main config. - */ -void -memberof_unlock_config() -{ - PR_RWLock_Unlock(memberof_config_lock); -} diff --git a/ipa-server/ipa-slapi-plugins/ipa-memberof/memberof-conf.ldif b/ipa-server/ipa-slapi-plugins/ipa-memberof/memberof-conf.ldif deleted file mode 100644 index 1441afeae..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-memberof/memberof-conf.ldif +++ /dev/null @@ -1,14 +0,0 @@ -dn: cn=ipa-memberof,cn=plugins,cn=config -changetype: add -objectclass: top -objectclass: nsSlapdPlugin -objectclass: extensibleObject -cn: ipa-memberof -nsslapd-pluginpath: libipa-memberof-plugin -nsslapd-plugininitfunc: ipamo_postop_init -nsslapd-plugintype: postoperation -nsslapd-pluginenabled: on -nsslapd-pluginid: memberof -nsslapd-pluginversion: 1.0 -nsslapd-pluginvendor: Red Hat -nsslapd-plugindescription: Memberof plugin diff --git a/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am deleted file mode 100644 index 540646f06..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am +++ /dev/null @@ -1,46 +0,0 @@ -NULL = - -INCLUDES = \ - -I. \ - -I$(srcdir) \ - -DPREFIX=\""$(prefix)"\" \ - -DBINDIR=\""$(bindir)"\" \ - -DLIBDIR=\""$(libdir)"\" \ - -DLIBEXECDIR=\""$(libexecdir)"\" \ - -DDATADIR=\""$(datadir)"\" \ - $(MOZLDAP_CFLAGS) \ - $(KRB5_CFLAGS) \ - $(SSL_CFLAGS) \ - $(WARN_CFLAGS) \ - $(NULL) - -plugindir = $(libdir)/dirsrv/plugins -plugin_LTLIBRARIES = \ - libipa_pwd_extop.la \ - $(NULL) - -libipa_pwd_extop_la_SOURCES = \ - ipa_pwd_extop.c \ - $(NULL) - -libipa_pwd_extop_la_LDFLAGS = -avoid-version - -libipa_pwd_extop_la_LIBADD = \ - $(KRB5_LIBS) \ - $(SSL_LIBS) \ - $(MOZLDAP_LIBS) \ - $(NULL) - -appdir = $(IPA_DATA_DIR) -app_DATA = \ - pwd-extop-conf.ldif \ - $(NULL) - -EXTRA_DIST = \ - README \ - $(app_DATA) \ - $(NULL) - -MAINTAINERCLEANFILES = \ - *~ \ - Makefile.in diff --git a/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/README b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/README deleted file mode 100644 index e69de29bb..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/README +++ /dev/null diff --git a/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c deleted file mode 100644 index 24acc8875..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c +++ /dev/null @@ -1,4058 +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; version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., 59 Temple - * Place, Suite 330, Boston, MA 02111-1307 USA. - * - * In addition, as a special exception, Red Hat, Inc. gives You the additional - * right to link the code of this Program with code not covered under the GNU - * General Public License ("Non-GPL Code") and to distribute linked combinations - * including the two, subject to the limitations in this paragraph. Non-GPL Code - * permitted under this exception must only link 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 GNU General Public License. Only Red Hat, Inc. may make changes or - * additions to the list of Approved Interfaces. You must obey the GNU General - * Public License in all respects for all of the Program code and other code - * used in conjunction with the Program except the Non-GPL Code covered by this - * exception. If you modify this file, you may extend this exception to your - * version of the file, but you are not obligated to do so. If you do not wish - * to provide this exception without modification, you must delete this - * exception statement from your version and license this file solely under the - * GPL without exception. - * - * Authors: - * Simo Sorce <ssorce@redhat.com> - * - * Copyright (C) 2005 Red Hat, Inc. - * All rights reserved. - * END COPYRIGHT BLOCK **/ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -/* - * Password Modify - LDAP Extended Operation. - * RFC 3062 - * - * - * This plugin implements the "Password Modify - LDAP3" - * extended operation for LDAP. The plugin function is called by - * the server if an LDAP client request contains the OID: - * "1.3.6.1.4.1.4203.1.11.1". - * - */ - -#include <stdio.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> - -#include <prio.h> -#include <ssl.h> -#include <dirsrv/slapi-plugin.h> -#define KRB5_PRIVATE 1 -#include <krb5.h> -#include <lber.h> -#include <time.h> -#include <iconv.h> -#include <openssl/des.h> -#include <openssl/md4.h> - -/* Type of connection for this operation;*/ -#define LDAP_EXTOP_PASSMOD_CONN_SECURE - -/* Uncomment the following #undef FOR TESTING: - * allows non-SSL connections to use the password change extended op */ -/* #undef LDAP_EXTOP_PASSMOD_CONN_SECURE */ - -/* ber tags for the PasswdModifyRequestValue sequence */ -#define LDAP_EXTOP_PASSMOD_TAG_USERID 0x80U -#define LDAP_EXTOP_PASSMOD_TAG_OLDPWD 0x81U -#define LDAP_EXTOP_PASSMOD_TAG_NEWPWD 0x82U - -/* ber tags for the PasswdModifyResponseValue sequence */ -#define LDAP_EXTOP_PASSMOD_TAG_GENPWD 0x80U - -/* OID of the extended operation handled by this plug-in */ -#define EXOP_PASSWD_OID "1.3.6.1.4.1.4203.1.11.1" - -/* OID to retrieve keytabs */ -#define KEYTAB_SET_OID "2.16.840.1.113730.3.8.3.1" -#define KEYTAB_RET_OID "2.16.840.1.113730.3.8.3.2" - -/* krbTicketFlags */ -#define KTF_DISALLOW_POSTDATED 0x00000001 -#define KTF_DISALLOW_FORWARDABLE 0x00000002 -#define KTF_DISALLOW_TGT_BASED 0x00000004 -#define KTF_DISALLOW_RENEWABLE 0x00000008 -#define KTF_DISALLOW_PROXIABLE 0x00000010 -#define KTF_DISALLOW_DUP_SKEY 0x00000020 -#define KTF_DISALLOW_ALL_TIX 0x00000040 -#define KTF_REQUIRES_PRE_AUTH 0x00000080 -#define KTF_REQUIRES_HW_AUTH 0x00000100 -#define KTF_REQUIRES_PWCHANGE 0x00000200 -#define KTF_DISALLOW_SVR 0x00001000 -#define KTF_PWCHANGE_SERVICE 0x00002000 - -/* These are the default enc:salt types if nothing is defined. - * TODO: retrieve the configure set of ecntypes either from the - * kfc.conf file or by synchronizing the the file content into - * the directory */ - -/* Salt types */ -#define KRB5_KDB_SALTTYPE_NORMAL 0 -#define KRB5_KDB_SALTTYPE_V4 1 -#define KRB5_KDB_SALTTYPE_NOREALM 2 -#define KRB5_KDB_SALTTYPE_ONLYREALM 3 -#define KRB5_KDB_SALTTYPE_SPECIAL 4 -#define KRB5_KDB_SALTTYPE_AFS3 5 - -#define KRB5P_SALT_SIZE 16 - -void krb5int_c_free_keyblock_contents(krb5_context context, register krb5_keyblock *key); - -static const char *ipapwd_def_encsalts[] = { - "des3-hmac-sha1:normal", -/* "arcfour-hmac:normal", - "des-hmac-sha1:normal", - "des-cbc-md5:normal", */ - "des-cbc-crc:normal", -/* "des-cbc-crc:v4", - "des-cbc-crc:afs3", */ - NULL -}; - -struct ipapwd_encsalt { - krb5_int32 enc_type; - krb5_int32 salt_type; -}; - -static const char *ipa_realm_dn; -static const char *ipa_pwd_config_dn; -static const char *ipa_changepw_principal_dn; - -#define IPAPWD_PLUGIN_NAME "ipa-pwd-extop" -#define IPAPWD_FEATURE_DESC "IPA Password Manager" -#define IPAPWD_PLUGIN_DESC "IPA Password Extended Operation plugin" - -static Slapi_PluginDesc pdesc = { - IPAPWD_FEATURE_DESC, - "FreeIPA project", - "FreeIPA/1.0", - IPAPWD_PLUGIN_DESC -}; - -static void *ipapwd_plugin_id; - -#define IPA_CHANGETYPE_NORMAL 0 -#define IPA_CHANGETYPE_ADMIN 1 -#define IPA_CHANGETYPE_DSMGR 2 - -struct ipapwd_krbcfg { - krb5_context krbctx; - char *realm; - krb5_keyblock *kmkey; - int num_supp_encsalts; - struct ipapwd_encsalt *supp_encsalts; - int num_pref_encsalts; - struct ipapwd_encsalt *pref_encsalts; - char **passsync_mgrs; - int num_passsync_mgrs; -}; - -static void free_ipapwd_krbcfg(struct ipapwd_krbcfg **cfg) -{ - struct ipapwd_krbcfg *c = *cfg; - - if (!c) return; - - krb5_free_default_realm(c->krbctx, c->realm); - krb5_free_context(c->krbctx); - free(c->kmkey->contents); - free(c->kmkey); - free(c->supp_encsalts); - free(c->pref_encsalts); - slapi_ch_array_free(c->passsync_mgrs); - free(c); - *cfg = NULL; -}; - -struct ipapwd_data { - Slapi_Entry *target; - char *dn; - char *password; - time_t timeNow; - time_t lastPwChange; - time_t expireTime; - int changetype; - int pwHistoryLen; -}; - -struct ipapwd_krbkeydata { - int32_t type; - struct berval value; -}; - -struct ipapwd_krbkey { - struct ipapwd_krbkeydata *salt; - struct ipapwd_krbkeydata *ekey; - struct berval s2kparams; -}; - -struct ipapwd_keyset { - uint16_t major_vno; - uint16_t minor_vno; - uint32_t kvno; - uint32_t mkvno; - struct ipapwd_krbkey *keys; - int num_keys; -}; - -static void ipapwd_keyset_free(struct ipapwd_keyset **pkset) -{ - struct ipapwd_keyset *kset = *pkset; - int i; - - if (!kset) return; - - for (i = 0; i < kset->num_keys; i++) { - if (kset->keys[i].salt) { - free(kset->keys[i].salt->value.bv_val); - free(kset->keys[i].salt); - } - if (kset->keys[i].ekey) { - free(kset->keys[i].ekey->value.bv_val); - free(kset->keys[i].ekey); - } - free(kset->keys[i].s2kparams.bv_val); - } - free(kset->keys); - free(kset); - *pkset = NULL; -} - -static int filter_keys(struct ipapwd_krbcfg *krbcfg, struct ipapwd_keyset *kset) -{ - int i, j; - - for (i = 0; i < kset->num_keys; i++) { - for (j = 0; j < krbcfg->num_supp_encsalts; j++) { - if (kset->keys[i].ekey->type == - krbcfg->supp_encsalts[j].enc_type) { - break; - } - } - if (j == krbcfg->num_supp_encsalts) { /* not valid */ - - /* free key */ - if (kset->keys[i].ekey) { - free(kset->keys[i].ekey->value.bv_val); - free(kset->keys[i].ekey); - } - if (kset->keys[i].salt) { - free(kset->keys[i].salt->value.bv_val); - free(kset->keys[i].salt); - } - free(kset->keys[i].s2kparams.bv_val); - - /* move all remaining keys up by one */ - kset->num_keys -= 1; - - for (j = i; j < kset->num_keys; j++) { - kset->keys[j] = kset->keys[j + 1]; - } - - /* new key has been moved to this position, make sure - * we do not skip it, by neutralizing next increment */ - i--; - } - } - - return 0; -} - -/* Novell key-format scheme: - - KrbKeySet ::= SEQUENCE { - attribute-major-vno [0] UInt16, - attribute-minor-vno [1] UInt16, - kvno [2] UInt32, - mkvno [3] UInt32 OPTIONAL, - keys [4] SEQUENCE OF KrbKey, - ... - } - - KrbKey ::= SEQUENCE { - salt [0] KrbSalt OPTIONAL, - key [1] EncryptionKey, - s2kparams [2] OCTET STRING OPTIONAL, - ... - } - - KrbSalt ::= SEQUENCE { - type [0] Int32, - salt [1] OCTET STRING OPTIONAL - } - - EncryptionKey ::= SEQUENCE { - keytype [0] Int32, - keyvalue [1] OCTET STRING - } - - */ - -static struct berval *encode_keys(struct ipapwd_keyset *kset) -{ - BerElement *be = NULL; - struct berval *bval = NULL; - int ret, i; - - be = ber_alloc_t(LBER_USE_DER); - - if (!be) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "memory allocation failed\n"); - return NULL; - } - - ret = ber_printf(be, "{t[i]t[i]t[i]t[i]t[{", - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), kset->major_vno, - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), kset->minor_vno, - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 2), kset->kvno, - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 3), kset->mkvno, - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 4)); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 vno info failed\n"); - goto done; - } - - for (i = 0; i < kset->num_keys; i++) { - - ret = ber_printf(be, "{"); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 EncryptionKey failed\n"); - goto done; - } - - if (kset->keys[i].salt) { - ret = ber_printf(be, "t[{t[i]", - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), - kset->keys[i].salt->type); - if ((ret != -1) && kset->keys[i].salt->value.bv_len) { - ret = ber_printf(be, "t[o]", - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), - kset->keys[i].salt->value.bv_val, - kset->keys[i].salt->value.bv_len); - } - if (ret != -1) { - ret = ber_printf(be, "}]"); - } - if (ret == -1) { - goto done; - } - } - - ret = ber_printf(be, "t[{t[i]t[o]}]", - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), - kset->keys[i].ekey->type, - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), - kset->keys[i].ekey->value.bv_val, - kset->keys[i].ekey->value.bv_len); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 EncryptionKey failed\n"); - goto done; - } - - /* FIXME: s2kparams not supported yet */ - - ret = ber_printf(be, "}"); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 EncryptionKey failed\n"); - goto done; - } - } - - ret = ber_printf(be, "}]}"); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 end of sequences failed\n"); - goto done; - } - - ret = ber_flatten(be, &bval); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "flattening asn1 failed\n"); - goto done; - } -done: - ber_free(be, 1); - - return bval; -} - -static int ipapwd_get_cur_kvno(Slapi_Entry *target) -{ - Slapi_Attr *krbPrincipalKey = NULL; - Slapi_ValueSet *svs; - Slapi_Value *sv; - BerElement *be = NULL; - const struct berval *cbval; - ber_tag_t tag, tmp; - ber_int_t tkvno; - int hint; - int kvno; - int ret; - - /* retrieve current kvno and and keys */ - ret = slapi_entry_attr_find(target, "krbPrincipalKey", &krbPrincipalKey); - if (ret != 0) { - return 0; - } - - kvno = 0; - - slapi_attr_get_valueset(krbPrincipalKey, &svs); - hint = slapi_valueset_first_value(svs, &sv); - while (hint != -1) { - cbval = slapi_value_get_berval(sv); - if (!cbval) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "Error retrieving berval from Slapi_Value\n"); - goto next; - } - be = ber_init(cbval); - if (!be) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ber_init() failed!\n"); - goto next; - } - - tag = ber_scanf(be, "{xxt[i]", &tmp, &tkvno); - if (tag == LBER_ERROR) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "Bad OLD key encoding ?!\n"); - ber_free(be, 1); - goto next; - } - - if (tkvno > kvno) { - kvno = tkvno; - } - - ber_free(be, 1); -next: - hint = slapi_valueset_next_value(svs, hint, &sv); - } - - return kvno; -} - -static inline void encode_int16(unsigned int val, unsigned char *p) -{ - p[1] = (val >> 8) & 0xff; - p[0] = (val ) & 0xff; -} - -static Slapi_Value **encrypt_encode_key(struct ipapwd_krbcfg *krbcfg, - struct ipapwd_data *data) -{ - krb5_context krbctx; - char *krbPrincipalName = NULL; - uint32_t krbMaxTicketLife; - int kvno, i; - int krbTicketFlags; - struct berval *bval = NULL; - Slapi_Value **svals = NULL; - krb5_principal princ; - krb5_error_code krberr; - krb5_data pwd; - struct ipapwd_keyset *kset = NULL; - - krbctx = krbcfg->krbctx; - - svals = (Slapi_Value **)calloc(2, sizeof(Slapi_Value *)); - if (!svals) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "memory allocation failed\n"); - return NULL; - } - - kvno = ipapwd_get_cur_kvno(data->target); - - krbPrincipalName = slapi_entry_attr_get_charptr(data->target, "krbPrincipalName"); - if (!krbPrincipalName) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "no krbPrincipalName present in this entry\n"); - return NULL; - } - - krberr = krb5_parse_name(krbctx, krbPrincipalName, &princ); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_parse_name failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - goto enc_error; - } - - krbMaxTicketLife = slapi_entry_attr_get_uint(data->target, "krbMaxTicketLife"); - if (krbMaxTicketLife == 0) { - /* FIXME: retrieve the default from config (max_life from kdc.conf) */ - krbMaxTicketLife = 86400; /* just set the default 24h for now */ - } - - krbTicketFlags = slapi_entry_attr_get_int(data->target, "krbTicketFlags"); - - pwd.data = (char *)data->password; - pwd.length = strlen(data->password); - - kset = malloc(sizeof(struct ipapwd_keyset)); - if (!kset) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - goto enc_error; - } - - /* this encoding assumes all keys have the same kvno */ - /* major-vno = 1 and minor-vno = 1 */ - kset->major_vno = 1; - kset->minor_vno = 1; - /* increment kvno (will be 1 if this is a new entry) */ - kset->kvno = kvno + 1; - /* we also assum mkvno is 0 */ - kset->mkvno = 0; - - kset->num_keys = krbcfg->num_pref_encsalts; - kset->keys = calloc(kset->num_keys, sizeof(struct ipapwd_krbkey)); - if (!kset->keys) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - goto enc_error; - } - - for (i = 0; i < kset->num_keys; i++) { - krb5_keyblock key; - krb5_data salt; - krb5_octet *ptr; - krb5_data plain; - krb5_enc_data cipher; - size_t len; - const char *p; - - salt.data = NULL; - - switch (krbcfg->pref_encsalts[i].salt_type) { - - case KRB5_KDB_SALTTYPE_ONLYREALM: - - p = strchr(krbPrincipalName, '@'); - if (!p) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "Invalid principal name, no realm found!\n"); - goto enc_error; - } - p++; - salt.data = strdup(p); - if (!salt.data) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "memory allocation failed\n"); - goto enc_error; - } - salt.length = strlen(salt.data); /* final \0 omitted on purpose */ - break; - - case KRB5_KDB_SALTTYPE_NOREALM: - - krberr = krb5_principal2salt_norealm(krbctx, princ, &salt); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_principal2salt failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - goto enc_error; - } - break; - - case KRB5_KDB_SALTTYPE_NORMAL: - - /* If pre auth is required we can set a random salt, otherwise - * we have to use a more conservative approach and set the salt - * to be REALMprincipal (the concatenation of REALM and principal - * name without any separator) */ -#if 0 - if (krbTicketFlags & KTF_REQUIRES_PRE_AUTH) { - salt.length = KRB5P_SALT_SIZE; - salt.data = malloc(KRB5P_SALT_SIZE); - if (!salt.data) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "memory allocation failed\n"); - goto enc_error; - } - krberr = krb5_c_random_make_octets(krbctx, &salt); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_c_random_make_octets failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - goto enc_error; - } - } else { -#endif - krberr = krb5_principal2salt(krbctx, princ, &salt); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_principal2salt failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - goto enc_error; - } -#if 0 - } -#endif - break; - - case KRB5_KDB_SALTTYPE_V4: - salt.length = 0; - break; - - case KRB5_KDB_SALTTYPE_AFS3: - - p = strchr(krbPrincipalName, '@'); - if (!p) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "Invalid principal name, no realm found!\n"); - goto enc_error; - } - p++; - salt.data = strdup(p); - if (!salt.data) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "memory allocation failed\n"); - goto enc_error; - } - salt.length = SALT_TYPE_AFS_LENGTH; /* special value */ - break; - - default: - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "Invalid salt type [%d]\n", krbcfg->pref_encsalts[i].salt_type); - goto enc_error; - } - - /* need to build the key now to manage the AFS salt.length special case */ - krberr = krb5_c_string_to_key(krbctx, krbcfg->pref_encsalts[i].enc_type, &pwd, &salt, &key); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_c_string_to_key failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - krb5_free_data_contents(krbctx, &salt); - goto enc_error; - } - if (salt.length == SALT_TYPE_AFS_LENGTH) { - salt.length = strlen(salt.data); - } - - krberr = krb5_c_encrypt_length(krbctx, krbcfg->kmkey->enctype, key.length, &len); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_c_string_to_key failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - krb5int_c_free_keyblock_contents(krbctx, &key); - krb5_free_data_contents(krbctx, &salt); - goto enc_error; - } - - if ((ptr = (krb5_octet *) malloc(2 + len)) == NULL) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "memory allocation failed\n"); - krb5int_c_free_keyblock_contents(krbctx, &key); - krb5_free_data_contents(krbctx, &salt); - goto enc_error; - } - - encode_int16(key.length, ptr); - - plain.length = key.length; - plain.data = (char *)key.contents; - - cipher.ciphertext.length = len; - cipher.ciphertext.data = (char *)ptr+2; - - krberr = krb5_c_encrypt(krbctx, krbcfg->kmkey, 0, 0, &plain, &cipher); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_c_encrypt failed [%s]\n", - krb5_get_error_message(krbctx, krberr)); - krb5int_c_free_keyblock_contents(krbctx, &key); - krb5_free_data_contents(krbctx, &salt); - free(ptr); - goto enc_error; - } - - /* KrbSalt */ - kset->keys[i].salt = malloc(sizeof(struct ipapwd_krbkeydata)); - if (!kset->keys[i].salt) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - krb5int_c_free_keyblock_contents(krbctx, &key); - free(ptr); - goto enc_error; - } - - kset->keys[i].salt->type = krbcfg->pref_encsalts[i].salt_type; - - if (salt.length) { - kset->keys[i].salt->value.bv_len = salt.length; - kset->keys[i].salt->value.bv_val = salt.data; - } - - /* EncryptionKey */ - kset->keys[i].ekey = malloc(sizeof(struct ipapwd_krbkeydata)); - if (!kset->keys[i].ekey) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - krb5int_c_free_keyblock_contents(krbctx, &key); - free(ptr); - goto enc_error; - } - kset->keys[i].ekey->type = key.enctype; - kset->keys[i].ekey->value.bv_len = len+2; - kset->keys[i].ekey->value.bv_val = malloc(len+2); - if (!kset->keys[i].ekey->value.bv_val) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - krb5int_c_free_keyblock_contents(krbctx, &key); - free(ptr); - goto enc_error; - } - memcpy(kset->keys[i].ekey->value.bv_val, ptr, len+2); - - /* make sure we free the memory used now that we are done with it */ - krb5int_c_free_keyblock_contents(krbctx, &key); - free(ptr); - } - - bval = encode_keys(kset); - if (!bval) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 KrbSalt failed\n"); - goto enc_error; - } - - svals[0] = slapi_value_new_berval(bval); - if (!svals[0]) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "Converting berval to Slapi_Value\n"); - goto enc_error; - } - - ipapwd_keyset_free(&kset); - krb5_free_principal(krbctx, princ); - slapi_ch_free_string(&krbPrincipalName); - ber_bvfree(bval); - return svals; - -enc_error: - if (kset) ipapwd_keyset_free(&kset); - krb5_free_principal(krbctx, princ); - slapi_ch_free_string(&krbPrincipalName); - if (bval) ber_bvfree(bval); - free(svals); - return NULL; -} - -static void ipapwd_free_slapi_value_array(Slapi_Value ***svals) -{ - Slapi_Value **sv = *svals; - int i; - - if (sv) { - for (i = 0; sv[i]; i++) { - slapi_value_free(&sv[i]); - } - } - - slapi_ch_free((void **)sv); -} - - -struct ntlm_keys { - uint8_t lm[16]; - uint8_t nt[16]; -}; - -#define KTF_LM_HASH 0x01 -#define KTF_NT_HASH 0x02 -#define KTF_DOS_CHARSET "CP850" /* same default as samba */ -#define KTF_UTF8 "UTF-8" -#define KTF_UCS2 "UCS-2LE" - -static const uint8_t parity_table[128] = { - 1, 2, 4, 7, 8, 11, 13, 14, 16, 19, 21, 22, 25, 26, 28, 31, - 32, 35, 37, 38, 41, 42, 44, 47, 49, 50, 52, 55, 56, 59, 61, 62, - 64, 67, 69, 70, 73, 74, 76, 79, 81, 82, 84, 87, 88, 91, 93, 94, - 97, 98,100,103,104,107,109,110,112,115,117,118,121,122,124,127, - 128,131,133,134,137,138,140,143,145,146,148,151,152,155,157,158, - 161,162,164,167,168,171,173,174,176,179,181,182,185,186,188,191, - 193,194,196,199,200,203,205,206,208,211,213,214,217,218,220,223, - 224,227,229,230,233,234,236,239,241,242,244,247,248,251,253,254}; - -static void lm_shuffle(uint8_t *out, uint8_t *in) -{ - out[0] = parity_table[in[0]>>1]; - out[1] = parity_table[((in[0]<<6)|(in[1]>>2)) & 0x7F]; - out[2] = parity_table[((in[1]<<5)|(in[2]>>3)) & 0x7F]; - out[3] = parity_table[((in[2]<<4)|(in[3]>>4)) & 0x7F]; - out[4] = parity_table[((in[3]<<3)|(in[4]>>5)) & 0x7F]; - out[5] = parity_table[((in[4]<<2)|(in[5]>>6)) & 0x7F]; - out[6] = parity_table[((in[5]<<1)|(in[6]>>7)) & 0x7F]; - out[7] = parity_table[in[6] & 0x7F]; -} - -/* create the lm and nt hashes - newPassword: the clear text utf8 password - flags: KTF_LM_HASH | KTF_NT_HASH -*/ -static int encode_ntlm_keys(char *newPasswd, unsigned int flags, struct ntlm_keys *keys) -{ - int ret = 0; - - /* do lanman first */ - if (flags & KTF_LM_HASH) { - iconv_t cd; - size_t cs, il, ol; - char *inc, *outc; - char *upperPasswd; - char *asciiPasswd; - DES_key_schedule schedule; - DES_cblock deskey; - DES_cblock magic = "KGS!@#$%"; - - /* TODO: must store the dos charset somewhere in the directory */ - cd = iconv_open(KTF_DOS_CHARSET, KTF_UTF8); - if (cd == (iconv_t)(-1)) { - ret = -1; - goto done; - } - - /* the lanman password is upper case */ - upperPasswd = (char *)slapi_utf8StrToUpper((unsigned char *)newPasswd); - if (!upperPasswd) { - ret = -1; - goto done; - } - il = strlen(upperPasswd); - - /* an ascii string can only be smaller than or equal to an utf8 one */ - ol = il; - if (ol < 14) ol = 14; - asciiPasswd = calloc(ol+1, 1); - if (!asciiPasswd) { - slapi_ch_free_string(&upperPasswd); - ret = -1; - goto done; - } - - inc = upperPasswd; - outc = asciiPasswd; - cs = iconv(cd, &inc, &il, &outc, &ol); - if (cs == -1) { - ret = -1; - slapi_ch_free_string(&upperPasswd); - free(asciiPasswd); - iconv_close(cd); - goto done; - } - - /* done with these */ - slapi_ch_free_string(&upperPasswd); - iconv_close(cd); - - /* we are interested only in the first 14 ASCII chars for lanman */ - if (strlen(asciiPasswd) > 14) { - asciiPasswd[14] = '\0'; - } - - /* first half */ - lm_shuffle(deskey, (uint8_t *)asciiPasswd); - - DES_set_key_unchecked(&deskey, &schedule); - DES_ecb_encrypt(&magic, (DES_cblock *)keys->lm, &schedule, DES_ENCRYPT); - - /* second half */ - lm_shuffle(deskey, (uint8_t *)&asciiPasswd[7]); - - DES_set_key_unchecked(&deskey, &schedule); - DES_ecb_encrypt(&magic, (DES_cblock *)&(keys->lm[8]), &schedule, DES_ENCRYPT); - - /* done with it */ - free(asciiPasswd); - - } else { - memset(keys->lm, 0, 16); - } - - if (flags & KTF_NT_HASH) { - iconv_t cd; - size_t cs, il, ol, sl; - char *inc, *outc; - char *ucs2Passwd; - MD4_CTX md4ctx; - - /* TODO: must store the dos charset somewhere in the directory */ - cd = iconv_open(KTF_UCS2, KTF_UTF8); - if (cd == (iconv_t)(-1)) { - ret = -1; - goto done; - } - - il = strlen(newPasswd); - - /* an ucs2 string can be at most double than an utf8 one */ - sl = ol = (il+1)*2; - ucs2Passwd = calloc(ol, 1); - if (!ucs2Passwd) { - ret = -1; - goto done; - } - - inc = newPasswd; - outc = ucs2Passwd; - cs = iconv(cd, &inc, &il, &outc, &ol); - if (cs == -1) { - ret = -1; - free(ucs2Passwd); - iconv_close(cd); - goto done; - } - - /* done with it */ - iconv_close(cd); - - /* get the final ucs2 string length */ - sl -= ol; - /* we are interested only in the first 14 wchars for the nt password */ - if (sl > 28) { - sl = 28; - } - - ret = MD4_Init(&md4ctx); - if (ret == 0) { - ret = -1; - free(ucs2Passwd); - goto done; - } - ret = MD4_Update(&md4ctx, ucs2Passwd, sl); - if (ret == 0) { - ret = -1; - free(ucs2Passwd); - goto done; - } - ret = MD4_Final(keys->nt, &md4ctx); - if (ret == 0) { - ret = -1; - free(ucs2Passwd); - goto done; - } - - } else { - memset(keys->nt, 0, 16); - } - - ret = 0; - -done: - return ret; -} - -/* searches the directory and finds the policy closest to the DN */ -/* return 0 on success, -1 on error or if no policy is found */ -static int ipapwd_getPolicy(const char *dn, Slapi_Entry *target, Slapi_Entry **e) -{ - const char *krbPwdPolicyReference; - const char *pdn; - const Slapi_DN *psdn; - Slapi_Backend *be; - Slapi_PBlock *pb = NULL; - char *attrs[] = { "krbMaxPwdLife", "krbMinPwdLife", - "krbPwdMinDiffChars", "krbPwdMinLength", - "krbPwdHistoryLength", NULL}; - Slapi_Entry **es = NULL; - Slapi_Entry *pe = NULL; - char **edn; - int ret, res, dist, rdnc, scope, i; - Slapi_DN *sdn = NULL; - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_getPolicy: Searching policy for [%s]\n", dn); - - sdn = slapi_sdn_new_dn_byref(dn); - if (sdn == NULL) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_getPolicy: Out of memory on [%s]\n", dn); - ret = -1; - goto done; - } - - krbPwdPolicyReference = slapi_entry_attr_get_charptr(target, "krbPwdPolicyReference"); - if (krbPwdPolicyReference) { - pdn = krbPwdPolicyReference; - scope = LDAP_SCOPE_BASE; - } else { - /* Find ancestor base DN */ - be = slapi_be_select(sdn); - psdn = slapi_be_getsuffix(be, 0); - if (psdn == NULL) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_getPolicy: Invalid DN [%s]\n", dn); - ret = -1; - goto done; - } - pdn = slapi_sdn_get_dn(psdn); - scope = LDAP_SCOPE_SUBTREE; - } - - *e = NULL; - - pb = slapi_pblock_new(); - slapi_search_internal_set_pb (pb, - pdn, scope, - "(objectClass=krbPwdPolicy)", - attrs, 0, - NULL, /* Controls */ - NULL, /* UniqueID */ - ipapwd_plugin_id, - 0); /* Flags */ - - /* do search the tree */ - ret = slapi_search_internal_pb(pb); - slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res); - if (ret == -1 || res != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_getPolicy: Couldn't find policy, err (%d)\n", - res?res:ret); - ret = -1; - goto done; - } - - /* get entries */ - slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &es); - if (!es) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_getPolicy: No entries ?!"); - ret = -1; - goto done; - } - - /* count entries */ - for (i = 0; es[i]; i++) /* count */ ; - - /* if there is only one, return that */ - if (i == 1) { - *e = slapi_entry_dup(es[0]); - - ret = 0; - goto done; - } - - /* count number of RDNs in DN */ - edn = ldap_explode_dn(dn, 0); - if (!edn) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_getPolicy: ldap_explode_dn(dn) failed ?!"); - ret = -1; - goto done; - } - for (rdnc = 0; edn[rdnc]; rdnc++) /* count */ ; - ldap_value_free(edn); - - pe = NULL; - dist = -1; - - /* find closest entry */ - for (i = 0; es[i]; i++) { - const Slapi_DN *esdn; - - esdn = slapi_entry_get_sdn_const(es[i]); - if (esdn == NULL) continue; - if (0 == slapi_sdn_compare(esdn, sdn)) { - pe = es[i]; - dist = 0; - break; - } - if (slapi_sdn_issuffix(sdn, esdn)) { - const char *dn1; - char **e1; - int c1; - - dn1 = slapi_sdn_get_dn(esdn); - if (!dn1) continue; - e1 = ldap_explode_dn(dn1, 0); - if (!e1) continue; - for (c1 = 0; e1[c1]; c1++) /* count */ ; - ldap_value_free(e1); - if ((dist == -1) || - ((rdnc - c1) < dist)) { - dist = rdnc - c1; - pe = es[i]; - } - } - if (dist == 0) break; /* found closest */ - } - - if (pe == NULL) { - ret = -1; - goto done; - } - - *e = slapi_entry_dup(pe); - ret = 0; -done: - if (pb) { - slapi_free_search_results_internal(pb); - slapi_pblock_destroy(pb); - } - if (sdn) slapi_sdn_free(&sdn); - return ret; -} - -#define GENERALIZED_TIME_LENGTH 15 - -static int ipapwd_sv_pw_cmp(const void *pv1, const void *pv2) -{ - const char *pw1 = slapi_value_get_string(*((Slapi_Value **)pv1)); - const char *pw2 = slapi_value_get_string(*((Slapi_Value **)pv2)); - - return strncmp(pw1, pw2, GENERALIZED_TIME_LENGTH); -} - -static Slapi_Value **ipapwd_setPasswordHistory(Slapi_Mods *smods, struct ipapwd_data *data) -{ - Slapi_Value **pH = NULL; - Slapi_Attr *passwordHistory = NULL; - char timestr[GENERALIZED_TIME_LENGTH+1]; - char *histr, *old_pw; - struct tm utctime; - int ret, pc; - - old_pw = slapi_entry_attr_get_charptr(data->target, "userPassword"); - if (!old_pw) { - /* no old password to store, just return */ - return NULL; - } - - if (!gmtime_r(&(data->timeNow), &utctime)) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to retrieve current date (buggy gmtime_r ?)\n"); - return NULL; - } - strftime(timestr, GENERALIZED_TIME_LENGTH+1, "%Y%m%d%H%M%SZ", &utctime); - - histr = slapi_ch_smprintf("%s%s", timestr, old_pw); - if (!histr) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - return NULL; - } - - /* retrieve current history */ - ret = slapi_entry_attr_find(data->target, "passwordHistory", &passwordHistory); - if (ret == 0) { - int ret, hint, count, i; - const char *pwstr; - Slapi_Value *pw; - - hint = 0; - count = 0; - ret = slapi_attr_get_numvalues(passwordHistory, &count); - /* if we have one */ - if (count > 0 && data->pwHistoryLen > 0) { - pH = calloc(count + 2, sizeof(Slapi_Value *)); - if (!pH) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - free(histr); - return NULL; - } - - i = 0; - hint = slapi_attr_first_value(passwordHistory, &pw); - while (hint != -1) { - pwstr = slapi_value_get_string(pw); - /* if shorter than GENERALIZED_TIME_LENGTH, it - * is garbage, we never set timeless entries */ - if (pwstr && - (strlen(pwstr) > GENERALIZED_TIME_LENGTH)) { - pH[i] = pw; - i++; - } - hint = slapi_attr_next_value(passwordHistory, hint, &pw); - } - - qsort(pH, i, sizeof(Slapi_Value *), ipapwd_sv_pw_cmp); - - if (i >= data->pwHistoryLen) { - i = data->pwHistoryLen; - pH[i] = NULL; - i--; - } - - pc = i; - - /* copy only interesting entries */ - for (i = 0; i < pc; i++) { - pH[i] = slapi_value_dup(pH[i]); - if (pH[i] == NULL) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - while (i) { - i--; - slapi_value_free(&pH[i]); - } - free(pH); - free(histr); - return NULL; - } - } - } - } - - if (pH == NULL) { - pH = calloc(2, sizeof(Slapi_Value *)); - if (!pH) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - free(histr); - return NULL; - } - pc = 0; - } - - /* add new history value */ - pH[pc] = slapi_value_new_string(histr); - - free(histr); - - return pH; -} - -static Slapi_Value *ipapwd_strip_pw_date(Slapi_Value *pw) -{ - const char *pwstr; - - pwstr = slapi_value_get_string(pw); - return slapi_value_new_string(&pwstr[GENERALIZED_TIME_LENGTH]); -} - -#define IPAPWD_POLICY_MASK 0x0FF -#define IPAPWD_POLICY_ERROR 0x100 -#define IPAPWD_POLICY_OK 0 - -/* 90 days default pwd max lifetime */ -#define IPAPWD_DEFAULT_PWDLIFE (90 * 24 *3600) -#define IPAPWD_DEFAULT_MINLEN 0 - -/* check password strenght and history */ -static int ipapwd_CheckPolicy(struct ipapwd_data *data) -{ - char *krbPrincipalExpiration = NULL; - char *krbLastPwdChange = NULL; - char *krbPasswordExpiration = NULL; - int krbMaxPwdLife = IPAPWD_DEFAULT_PWDLIFE; - int krbPwdMinLength = IPAPWD_DEFAULT_MINLEN; - int krbPwdMinDiffChars = 0; - int krbMinPwdLife = 0; - int pwdCharLen = 0; - Slapi_Entry *policy = NULL; - Slapi_Attr *passwordHistory = NULL; - struct tm tm; - int tmp, ret; - char *old_pw; - - /* check account is not expired. Ignore unixtime = 0 (Jan 1 1970) */ - krbPrincipalExpiration = slapi_entry_attr_get_charptr(data->target, "krbPrincipalExpiration"); - if (krbPrincipalExpiration && - (strcasecmp("19700101000000Z", krbPrincipalExpiration) != 0)) { - /* if expiration date is set check it */ - memset(&tm, 0, sizeof(struct tm)); - ret = sscanf(krbPrincipalExpiration, - "%04u%02u%02u%02u%02u%02u", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, - &tm.tm_hour, &tm.tm_min, &tm.tm_sec); - - if (ret == 6) { - tm.tm_year -= 1900; - tm.tm_mon -= 1; - - if (data->timeNow > timegm(&tm)) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "Account Expired"); - return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDMODNOTALLOWED; - } - } - /* FIXME: else error out ? */ - } - slapi_ch_free_string(&krbPrincipalExpiration); - - /* find the entry with the password policy */ - ret = ipapwd_getPolicy(data->dn, data->target, &policy); - if (ret) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "No password policy"); - goto no_policy; - } - - /* Retrieve Max History Len */ - data->pwHistoryLen = slapi_entry_attr_get_int(policy, "krbPwdHistoryLength"); - - if (data->changetype != IPA_CHANGETYPE_NORMAL) { - /* We must skip policy checks (Admin change) but - * force a password change on the next login. - * But not if Directory Manager */ - if (data->changetype == IPA_CHANGETYPE_ADMIN) { - data->expireTime = data->timeNow; - } - - /* skip policy checks */ - slapi_entry_free(policy); - goto no_policy; - } - - /* first of all check current password, if any */ - old_pw = slapi_entry_attr_get_charptr(data->target, "userPassword"); - if (old_pw) { - Slapi_Value *cpw[2] = {NULL, NULL}; - Slapi_Value *pw; - - cpw[0] = slapi_value_new_string(old_pw); - pw = slapi_value_new_string(data->password); - if (!pw) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - slapi_entry_free(policy); - slapi_ch_free_string(&old_pw); - slapi_value_free(&cpw[0]); - slapi_value_free(&pw); - return LDAP_OPERATIONS_ERROR; - } - - ret = slapi_pw_find_sv(cpw, pw); - slapi_ch_free_string(&old_pw); - slapi_value_free(&cpw[0]); - slapi_value_free(&pw); - - if (ret == 0) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_checkPassword: Password in history\n"); - slapi_entry_free(policy); - return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDINHISTORY; - } - } - - krbPasswordExpiration = slapi_entry_attr_get_charptr(data->target, "krbPasswordExpiration"); - krbLastPwdChange = slapi_entry_attr_get_charptr(data->target, "krbLastPwdChange"); - /* if no previous change, it means this is probably a new account - * or imported, log and just ignore */ - if (krbLastPwdChange) { - - memset(&tm, 0, sizeof(struct tm)); - ret = sscanf(krbLastPwdChange, - "%04u%02u%02u%02u%02u%02u", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, - &tm.tm_hour, &tm.tm_min, &tm.tm_sec); - - if (ret == 6) { - tm.tm_year -= 1900; - tm.tm_mon -= 1; - data->lastPwChange = timegm(&tm); - } - /* FIXME: *else* report an error ? */ - } else { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "Warning: Last Password Change Time is not available"); - } - - /* Check min age */ - krbMinPwdLife = slapi_entry_attr_get_int(policy, "krbMinPwdLife"); - /* if no default then treat it as no limit */ - if (krbMinPwdLife != 0) { - - /* check for reset cases */ - if (krbLastPwdChange == NULL || - ((krbPasswordExpiration != NULL) && - strcmp(krbPasswordExpiration, krbLastPwdChange) == 0)) { - /* Expiration and last change time are the same or - * missing this happens only when a password is reset - * by an admin or the account is new or no expiration - * policy is set, PASS */ - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_checkPolicy: Ignore krbMinPwdLife Expiration, not enough info\n"); - - } else if (data->timeNow < data->lastPwChange + krbMinPwdLife) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_checkPolicy: Too soon to change password\n"); - slapi_entry_free(policy); - slapi_ch_free_string(&krbPasswordExpiration); - slapi_ch_free_string(&krbLastPwdChange); - return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDTOOYOUNG; - } - } - - /* free strings or we leak them */ - slapi_ch_free_string(&krbPasswordExpiration); - slapi_ch_free_string(&krbLastPwdChange); - - /* Retrieve min length */ - tmp = slapi_entry_attr_get_int(policy, "krbPwdMinLength"); - if (tmp != 0) { - krbPwdMinLength = tmp; - } - - /* check complexity */ - /* FIXME: this code is partially based on Directory Server code, - * the plan is to merge this code later making it available - * trough a pulic DS API for slapi plugins */ - krbPwdMinDiffChars = slapi_entry_attr_get_int(policy, "krbPwdMinDiffChars"); - if (krbPwdMinDiffChars != 0) { - int num_digits = 0; - int num_alphas = 0; - int num_uppers = 0; - int num_lowers = 0; - int num_specials = 0; - int num_8bit = 0; - int num_repeated = 0; - int max_repeated = 0; - int num_categories = 0; - char *p, *pwd; - - pwd = strdup(data->password); - - /* check character types */ - p = pwd; - while ( p && *p ) - { - if ( ldap_utf8isdigit( p ) ) { - num_digits++; - } else if ( ldap_utf8isalpha( p ) ) { - num_alphas++; - if ( slapi_utf8isLower( (unsigned char *)p ) ) { - num_lowers++; - } else { - num_uppers++; - } - } else { - /* check if this is an 8-bit char */ - if ( *p & 128 ) { - num_8bit++; - } else { - num_specials++; - } - } - - /* check for repeating characters. If this is the - first char of the password, no need to check */ - if ( pwd != p ) { - int len = ldap_utf8len( p ); - char *prev_p = ldap_utf8prev( p ); - - if ( len == ldap_utf8len( prev_p ) ) - { - if ( memcmp( p, prev_p, len ) == 0 ) - { - num_repeated++; - if ( max_repeated < num_repeated ) { - max_repeated = num_repeated; - } - } else { - num_repeated = 0; - } - } else { - num_repeated = 0; - } - } - - p = ldap_utf8next( p ); - } - - free(pwd); - p = pwd = NULL; - - /* tally up the number of character categories */ - if ( num_digits > 0 ) - ++num_categories; - if ( num_uppers > 0 ) - ++num_categories; - if ( num_lowers > 0 ) - ++num_categories; - if ( num_specials > 0 ) - ++num_categories; - if ( num_8bit > 0 ) - ++num_categories; - - /* FIXME: the kerberos plicy schema does not define separated threshold values, - * so just treat anything as a category, we will fix this when we merge - * with DS policies */ - - if (max_repeated > 1) - --num_categories; - - if (num_categories < krbPwdMinDiffChars) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_checkPassword: Password not complex enough\n"); - slapi_entry_free(policy); - return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_INVALIDPWDSYNTAX; - } - } - - /* Check password history */ - ret = slapi_entry_attr_find(data->target, "passwordHistory", &passwordHistory); - if (ret == 0) { - int ret, hint, count, i, j; - const char *pwstr; - Slapi_Value **pH; - Slapi_Value *pw; - - hint = 0; - count = 0; - ret = slapi_attr_get_numvalues(passwordHistory, &count); - /* check history only if we have one */ - if (count > 0 && data->pwHistoryLen > 0) { - pH = calloc(count + 2, sizeof(Slapi_Value *)); - if (!pH) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - slapi_entry_free(policy); - return LDAP_OPERATIONS_ERROR; - } - - i = 0; - hint = slapi_attr_first_value(passwordHistory, &pw); - while (hint != -1) { - pwstr = slapi_value_get_string(pw); - /* if shorter than GENERALIZED_TIME_LENGTH, it - * is garbage, we never set timeless entries */ - if (pwstr && - (strlen(pwstr) > GENERALIZED_TIME_LENGTH)) { - pH[i] = pw; - i++; - } - hint = slapi_attr_next_value(passwordHistory, hint, &pw); - } - - qsort(pH, i, sizeof(Slapi_Value *), ipapwd_sv_pw_cmp); - - if (i > data->pwHistoryLen) { - i = data->pwHistoryLen; - pH[i] = NULL; - } - - for (j = 0; pH[j]; j++) { - pH[j] = ipapwd_strip_pw_date(pH[j]); - } - - pw = slapi_value_new_string(data->password); - if (!pw) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "ipapwd_checkPassword: Out of Memory\n"); - slapi_entry_free(policy); - free(pH); - return LDAP_OPERATIONS_ERROR; - } - - ret = slapi_pw_find_sv(pH, pw); - - for (j = 0; pH[j]; j++) { - slapi_value_free(&pH[j]); - } - slapi_value_free(&pw); - free(pH); - - if (ret == 0) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_checkPassword: Password in history\n"); - slapi_entry_free(policy); - return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDINHISTORY; - } - } - } - - /* Calculate max age */ - tmp = slapi_entry_attr_get_int(policy, "krbMaxPwdLife"); - if (tmp != 0) { - krbMaxPwdLife = tmp; - } - - slapi_entry_free(policy); - -no_policy: - - /* check min lenght */ - pwdCharLen = ldap_utf8characters(data->password); - - if (pwdCharLen < krbPwdMinLength) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_checkPassword: Password too short\n"); - return IPAPWD_POLICY_ERROR | LDAP_PWPOLICY_PWDTOOSHORT; - } - - if (data->expireTime == 0) { - data->expireTime = data->timeNow + krbMaxPwdLife; - } - - return IPAPWD_POLICY_OK; -} - - -/* Searches the dn in directory, - * If found : fills in slapi_entry structure and returns 0 - * If NOT found : returns the search result as LDAP_NO_SUCH_OBJECT - */ -static int ipapwd_getEntry(const char *dn, Slapi_Entry **e2, char **attrlist) -{ - Slapi_DN *sdn; - int search_result = 0; - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_getEntry\n"); - - sdn = slapi_sdn_new_dn_byref(dn); - if ((search_result = slapi_search_internal_get_entry( sdn, attrlist, e2, - ipapwd_plugin_id)) != LDAP_SUCCESS ){ - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "ipapwd_getEntry: No such entry-(%s), err (%d)\n", - dn, search_result); - } - - slapi_sdn_free( &sdn ); - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "<= ipapwd_getEntry: %d\n", search_result); - return search_result; -} - - -/* Construct Mods pblock and perform the modify operation - * Sets result of operation in SLAPI_PLUGIN_INTOP_RESULT - */ -static int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods) -{ - Slapi_PBlock *pb; - int ret; - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_apply_mods\n"); - - if (!mods || (slapi_mods_get_num_mods(mods) == 0)) { - return -1; - } - - pb = slapi_pblock_new(); - slapi_modify_internal_set_pb (pb, dn, - slapi_mods_get_ldapmods_byref(mods), - NULL, /* Controls */ - NULL, /* UniqueID */ - ipapwd_plugin_id, /* PluginID */ - 0); /* Flags */ - - ret = slapi_modify_internal_pb (pb); - if (ret) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "WARNING: modify error %d on entry '%s'\n", - ret, dn); - } else { - - slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret); - - if (ret != LDAP_SUCCESS){ - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "WARNING: modify error %d on entry '%s'\n", - ret, dn); - } else { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "<= ipapwd_apply_mods: Successful\n"); - } - } - - slapi_pblock_destroy(pb); - - return ret; -} - -/* ascii hex output of bytes in "in" - * out len is 32 (preallocated) - * in len is 16 */ -static const char hexchars[] = "0123456789ABCDEF"; -static void hexbuf(char *out, const uint8_t *in) -{ - int i; - - for (i = 0; i < 16; i++) { - out[i*2] = hexchars[in[i] >> 4]; - out[i*2+1] = hexchars[in[i] & 0x0f]; - } -} - -/* Modify the Password attributes of the entry */ -static int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg, - struct ipapwd_data *data) -{ - int ret = 0, i = 0; - Slapi_Mods *smods; - Slapi_Value **svals = NULL; - Slapi_Value **pwvals = NULL; - struct tm utctime; - char timestr[GENERALIZED_TIME_LENGTH+1]; - krb5_context krbctx; - krb5_error_code krberr; - char lm[33], nt[33]; - struct ntlm_keys ntlm; - int ntlm_flags = 0; - Slapi_Value *sambaSamAccount; - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_SetPassword\n"); - - smods = slapi_mods_new(); - - /* generate kerberos keys to be put into krbPrincipalKey */ - svals = encrypt_encode_key(krbcfg, data); - if (!svals) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "key encryption/encoding failed\n"); - ret = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } - - slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "krbPrincipalKey", svals); - - /* change Last Password Change field with the current date */ - if (!gmtime_r(&(data->timeNow), &utctime)) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to retrieve current date (buggy gmtime_r ?)\n"); - ret = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } - strftime(timestr, GENERALIZED_TIME_LENGTH+1, "%Y%m%d%H%M%SZ", &utctime); - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbLastPwdChange", timestr); - - /* set Password Expiration date */ - if (!gmtime_r(&(data->expireTime), &utctime)) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to convert expiration date\n"); - ret = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } - strftime(timestr, GENERALIZED_TIME_LENGTH+1, "%Y%m%d%H%M%SZ", &utctime); - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbPasswordExpiration", timestr); - - sambaSamAccount = slapi_value_new_string("sambaSamAccount"); - if (slapi_entry_attr_has_syntax_value(data->target, "objectClass", sambaSamAccount)) { - /* TODO: retrieve if we want to store the LM hash or not */ - ntlm_flags = KTF_LM_HASH | KTF_NT_HASH; - } - slapi_value_free(&sambaSamAccount); - - if (ntlm_flags) { - char *password = strdup(data->password); - if (encode_ntlm_keys(password, ntlm_flags, &ntlm) != 0) { - free(password); - ret = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } - if (ntlm_flags & KTF_LM_HASH) { - hexbuf(lm, ntlm.lm); - lm[32] = '\0'; - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "sambaLMPassword", lm); - } - if (ntlm_flags & KTF_NT_HASH) { - hexbuf(nt, ntlm.nt); - nt[32] = '\0'; - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "sambaNTPassword", nt); - } - free(password); - } - - /* let DS encode the password itself, this allows also other plugins to - * intercept it to perform operations like synchronization with Active - * Directory domains through the replication plugin */ - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "userPassword", data->password); - - /* set password history */ - pwvals = ipapwd_setPasswordHistory(smods, data); - if (pwvals) { - slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "passwordHistory", pwvals); - } - - /* FIXME: - * instead of replace we should use a delete/add so that we are - * completely sure nobody else modified the entry meanwhile and - * fail if that's the case */ - - /* commit changes */ - ret = ipapwd_apply_mods(data->dn, smods); - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipapwd_SetPassword: %d\n", ret); - -free_and_return: - slapi_mods_free(&smods); - ipapwd_free_slapi_value_array(&svals); - ipapwd_free_slapi_value_array(&pwvals); - - return ret; -} - -static int ipapwd_chpwop(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) -{ - char *bindDN = NULL; - char *authmethod = NULL; - char *dn = NULL; - char *oldPasswd = NULL; - char *newPasswd = NULL; - char *errMesg = NULL; - int ret=0, rc=0, is_root=0; - ber_tag_t tag=0; - ber_len_t len=-1; - struct berval *extop_value = NULL; - BerElement *ber = NULL; - Slapi_Entry *targetEntry=NULL; - char *attrlist[] = {"*", "passwordHistory", NULL }; - struct ipapwd_data pwdata; - - /* Get the ber value of the extended operation */ - slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value); - - if ((ber = ber_init(extop_value)) == NULL) - { - errMesg = "PasswdModify Request decode failed.\n"; - rc = LDAP_PROTOCOL_ERROR; - goto free_and_return; - } - - /* Format of request to parse - * - * PasswdModifyRequestValue ::= SEQUENCE { - * userIdentity [0] OCTET STRING OPTIONAL - * oldPasswd [1] OCTET STRING OPTIONAL - * newPasswd [2] OCTET STRING OPTIONAL } - * - * The request value field is optional. If it is - * provided, at least one field must be filled in. - */ - - /* ber parse code */ - if ( ber_scanf( ber, "{") == LBER_ERROR ) - { - /* The request field wasn't provided. We'll - * now try to determine the userid and verify - * knowledge of the old password via other - * means. - */ - goto parse_req_done; - } else { - tag = ber_peek_tag( ber, &len); - } - - /* identify userID field by tags */ - if (tag == LDAP_EXTOP_PASSMOD_TAG_USERID ) - { - if (ber_scanf(ber, "a", &dn) == LBER_ERROR) { - slapi_ch_free_string(&dn); - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "ber_scanf failed\n"); - errMesg = "ber_scanf failed at userID parse.\n"; - rc = LDAP_PROTOCOL_ERROR; - goto free_and_return; - } - - tag = ber_peek_tag(ber, &len); - } - - /* identify oldPasswd field by tags */ - if (tag == LDAP_EXTOP_PASSMOD_TAG_OLDPWD ) - { - if (ber_scanf(ber, "a", &oldPasswd) == LBER_ERROR) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "ber_scanf failed\n"); - errMesg = "ber_scanf failed at oldPasswd parse.\n"; - rc = LDAP_PROTOCOL_ERROR; - goto free_and_return; - } - tag = ber_peek_tag(ber, &len); - } - - /* identify newPasswd field by tags */ - if (tag == LDAP_EXTOP_PASSMOD_TAG_NEWPWD ) - { - if (ber_scanf(ber, "a", &newPasswd) == LBER_ERROR) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "ber_scanf failed\n"); - errMesg = "ber_scanf failed at newPasswd parse.\n"; - rc = LDAP_PROTOCOL_ERROR; - goto free_and_return; - } - } - -parse_req_done: - /* Uncomment for debugging, otherwise we don't want to leak the - * password values into the log... */ - /* LDAPDebug( LDAP_DEBUG_ARGS, "passwd: dn (%s), oldPasswd (%s), - * newPasswd (%s)\n", dn, oldPasswd, newPasswd); */ - - - /* Get Bind DN */ - slapi_pblock_get(pb, SLAPI_CONN_DN, &bindDN); - - /* If the connection is bound anonymously, we must refuse - * to process this operation. */ - if (bindDN == NULL || *bindDN == '\0') { - /* Refuse the operation because they're bound anonymously */ - errMesg = "Anonymous Binds are not allowed.\n"; - rc = LDAP_INSUFFICIENT_ACCESS; - goto free_and_return; - } - - /* A new password was not supplied in the request, and we do not support - * password generation yet. - */ - if (newPasswd == NULL || *newPasswd == '\0') { - errMesg = "Password generation not implemented.\n"; - rc = LDAP_UNWILLING_TO_PERFORM; - goto free_and_return; - } - - if (oldPasswd == NULL || *oldPasswd == '\0') { - /* If user is authenticated, they already gave their password during - the bind operation (or used sasl or client cert auth or OS creds) */ - slapi_pblock_get(pb, SLAPI_CONN_AUTHMETHOD, &authmethod); - if (!authmethod || !strcmp(authmethod, SLAPD_AUTH_NONE)) { - errMesg = "User must be authenticated to the directory server.\n"; - rc = LDAP_INSUFFICIENT_ACCESS; - goto free_and_return; - } - } - - /* Determine the target DN for this operation */ - /* Did they give us a DN ? */ - if (dn == NULL || *dn == '\0') { - /* Get the DN from the bind identity on this connection */ - dn = slapi_ch_strdup(bindDN); - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "Missing userIdentity in request, using the bind DN instead.\n"); - } - - slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, dn ); - - /* Now we have the DN, look for the entry */ - ret = ipapwd_getEntry(dn, &targetEntry, attrlist); - /* If we can't find the entry, then that's an error */ - if (ret) { - /* Couldn't find the entry, fail */ - errMesg = "No such Entry exists.\n" ; - rc = LDAP_NO_SUCH_OBJECT; - goto free_and_return; - } - - /* First thing to do is to ask access control if the bound identity has - * rights to modify the userpassword attribute on this entry. If not, - * then we fail immediately with insufficient access. This means that - * we don't leak any useful information to the client such as current - * password wrong, etc. - */ - - is_root = slapi_dn_isroot(bindDN); - slapi_pblock_set(pb, SLAPI_REQUESTOR_ISROOT, &is_root); - - /* In order to perform the access control check, we need to select a - * backend (even though we don't actually need it otherwise). - */ - { - Slapi_Backend *be = NULL; - - be = slapi_be_select(slapi_entry_get_sdn(targetEntry)); - if (NULL == be) { - errMesg = "Failed to find backend for target entry"; - rc = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } - slapi_pblock_set(pb, SLAPI_BACKEND, be); - } - - ret = slapi_access_allowed( pb, targetEntry, "krbPrincipalKey", NULL, SLAPI_ACL_WRITE ); - if ( ret != LDAP_SUCCESS ) { - errMesg = "Insufficient access rights\n"; - rc = LDAP_INSUFFICIENT_ACCESS; - goto free_and_return; - } - - /* Now we have the entry which we want to modify - * They gave us a password (old), check it against the target entry - * Is the old password valid ? - */ - if (oldPasswd && *oldPasswd) { - /* If user is authenticated, they already gave their password - * during the bind operation (or used sasl or client cert auth - * or OS creds) */ - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "oldPasswd provided, but we will ignore it"); - } - - memset(&pwdata, 0, sizeof(pwdata)); - pwdata.target = targetEntry; - pwdata.dn = dn; - pwdata.password = newPasswd; - pwdata.timeNow = time(NULL); - pwdata.changetype = IPA_CHANGETYPE_NORMAL; - - /* - * (technically strcasecmp to compare DNs is not absolutely correct, - * but it should work for the cases we care about here) - */ - - /* determine type of password change */ - /* special cases */ - if ((strcasecmp(dn, bindDN) != 0) && - (strcasecmp(ipa_changepw_principal_dn, bindDN) != 0)) { - int i; - - pwdata.changetype = IPA_CHANGETYPE_ADMIN; - - for (i = 0; i < krbcfg->num_passsync_mgrs; i++) { - if (strcasecmp(krbcfg->passsync_mgrs[i], bindDN) == 0) { - pwdata.changetype = IPA_CHANGETYPE_DSMGR; - break; - } - } - } - - /* check the policy */ - ret = ipapwd_CheckPolicy(&pwdata); - if (ret) { - errMesg = "Password Fails to meet minimum strength criteria"; - if (ret & IPAPWD_POLICY_ERROR) { - slapi_pwpolicy_make_response_control(pb, -1, -1, ret & IPAPWD_POLICY_MASK); - rc = LDAP_CONSTRAINT_VIOLATION; - } else { - errMesg = "Internal error"; - rc = ret; - } - goto free_and_return; - } - - /* Now we're ready to set the kerberos key material */ - ret = ipapwd_SetPassword(krbcfg, &pwdata); - if (ret != LDAP_SUCCESS) { - /* Failed to modify the password, - * e.g. because insufficient access allowed */ - errMesg = "Failed to update password"; - if (ret > 0) { - rc = ret; - } else { - rc = LDAP_OPERATIONS_ERROR; - } - goto free_and_return; - } - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipapwd_extop: %d\n", rc); - - /* Free anything that we allocated above */ -free_and_return: - slapi_ch_free_string(&oldPasswd); - slapi_ch_free_string(&newPasswd); - /* Either this is the same pointer that we allocated and set above, - * or whoever used it should have freed it and allocated a new - * value that we need to free here */ - slapi_pblock_get(pb, SLAPI_ORIGINAL_TARGET, &dn); - slapi_ch_free_string(&dn); - slapi_pblock_set(pb, SLAPI_ORIGINAL_TARGET, NULL); - slapi_ch_free_string(&authmethod); - - if (targetEntry) slapi_entry_free(targetEntry); - if (ber) ber_free(ber, 1); - - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", errMesg ? errMesg : "success"); - slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL); - - return SLAPI_PLUGIN_EXTENDED_SENT_RESULT; - -} - -/* Password Modify Extended operation plugin function */ -static int ipapwd_setkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) -{ - char *bindDN = NULL; - char *serviceName = NULL; - char *errMesg = NULL; - int ret=0, rc=0, is_root=0; - struct berval *extop_value = NULL; - BerElement *ber = NULL; - Slapi_PBlock *pbte = NULL; - Slapi_Entry *targetEntry=NULL; - struct berval *bval = NULL; - Slapi_Value **svals = NULL; - const char *bdn; - const Slapi_DN *bsdn; - Slapi_DN *sdn; - Slapi_Backend *be; - Slapi_Entry **es = NULL; - int scope, res; - char *filter; - char *attrlist[] = {"krbPrincipalKey", "krbLastPwdChange", NULL }; - krb5_context krbctx = NULL; - krb5_principal krbname = NULL; - krb5_error_code krberr; - int i, kvno; - Slapi_Mods *smods; - ber_tag_t rtag, ttmp; - ber_int_t tint; - ber_len_t tlen; - struct ipapwd_keyset *kset = NULL; - struct tm utctime; - char timestr[GENERALIZED_TIME_LENGTH+1]; - time_t time_now = time(NULL); - - svals = (Slapi_Value **)calloc(2, sizeof(Slapi_Value *)); - if (!svals) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "memory allocation failed\n"); - rc = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } - - krberr = krb5_init_context(&krbctx); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_init_context failed\n"); - rc = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } - - /* Get Bind DN */ - slapi_pblock_get(pb, SLAPI_CONN_DN, &bindDN); - - /* If the connection is bound anonymously, we must refuse to process - * this operation. */ - if (bindDN == NULL || *bindDN == '\0') { - /* Refuse the operation because they're bound anonymously */ - errMesg = "Anonymous Binds are not allowed.\n"; - rc = LDAP_INSUFFICIENT_ACCESS; - goto free_and_return; - } - - /* Get the ber value of the extended operation */ - slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value); - - if ((ber = ber_init(extop_value)) == NULL) - { - errMesg = "KeytabGet Request decode failed.\n"; - rc = LDAP_PROTOCOL_ERROR; - goto free_and_return; - } - - /* Format of request to parse - * - * KeytabGetRequest ::= SEQUENCE { - * serviceIdentity OCTET STRING - * keys SEQUENCE OF KrbKey, - * ... - * } - * - * KrbKey ::= SEQUENCE { - * key [0] EncryptionKey, - * salt [1] KrbSalt OPTIONAL, - * s2kparams [2] OCTET STRING OPTIONAL, - * ... - * } - * - * EncryptionKey ::= SEQUENCE { - * keytype [0] Int32, - * keyvalue [1] OCTET STRING - * } - * - * KrbSalt ::= SEQUENCE { - * type [0] Int32, - * salt [1] OCTET STRING OPTIONAL - * } - */ - - /* ber parse code */ - rtag = ber_scanf(ber, "{a{", &serviceName); - if (rtag == LBER_ERROR) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "ber_scanf failed\n"); - errMesg = "Invalid payload, failed to decode.\n"; - rc = LDAP_PROTOCOL_ERROR; - goto free_and_return; - } - - /* make sure it is a valid name */ - krberr = krb5_parse_name(krbctx, serviceName, &krbname); - if (krberr) { - slapi_ch_free_string(&serviceName); - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_parse_name failed\n"); - rc = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } else { - /* invert so that we get the canonical form - * (add REALM if not present for example) */ - char *canonname; - krberr = krb5_unparse_name(krbctx, krbname, &canonname); - if (krberr) { - slapi_ch_free_string(&serviceName); - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "krb5_unparse_name failed\n"); - rc = LDAP_OPERATIONS_ERROR; - goto free_and_return; - } - slapi_ch_free_string(&serviceName); - serviceName = canonname; - } - - /* check entry before doing any other decoding */ - - /* Find ancestor base DN */ - sdn = slapi_sdn_new_dn_byval(ipa_realm_dn); - be = slapi_be_select(sdn); - slapi_sdn_free(&sdn); - bsdn = slapi_be_getsuffix(be, 0); - if (bsdn == NULL) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "Search for Base DN failed\n"); - errMesg = "PrincipalName not found.\n"; - rc = LDAP_NO_SUCH_OBJECT; - goto free_and_return; - } - bdn = slapi_sdn_get_dn(bsdn); - scope = LDAP_SCOPE_SUBTREE; - - /* get Entry by krbPrincipalName */ - filter = slapi_ch_smprintf("(krbPrincipalName=%s)", serviceName); - - pbte = slapi_pblock_new(); - slapi_search_internal_set_pb(pbte, - bdn, scope, filter, attrlist, 0, - NULL, /* Controls */ - NULL, /* UniqueID */ - ipapwd_plugin_id, - 0); /* Flags */ - - /* do search the tree */ - ret = slapi_search_internal_pb(pbte); - slapi_pblock_get(pbte, SLAPI_PLUGIN_INTOP_RESULT, &res); - if (ret == -1 || res != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "Search for Principal failed, err (%d)\n", - res?res:ret); - errMesg = "PrincipalName not found.\n"; - rc = LDAP_NO_SUCH_OBJECT; - goto free_and_return; - } - - /* get entries */ - slapi_pblock_get(pbte, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &es); - if (!es) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "No entries ?!"); - errMesg = "PrincipalName not found.\n"; - rc = LDAP_NO_SUCH_OBJECT; - goto free_and_return; - } - - /* count entries */ - for (i = 0; es[i]; i++) /* count */ ; - - /* if there is none or more than one, freak out */ - if (i != 1) { - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", - "Too many entries, or entry no found (%d)", i); - errMesg = "PrincipalName not found.\n"; - rc = LDAP_NO_SUCH_OBJECT; - goto free_and_return; - } - targetEntry = es[0]; - - /* First thing to do is to ask access control if the bound identity has - * rights to modify the userpassword attribute on this entry. If not, - * then we fail immediately with insufficient access. This means that - * we don't leak any useful information to the client such as current - * password wrong, etc. - */ - - is_root = slapi_dn_isroot(bindDN); - slapi_pblock_set(pb, SLAPI_REQUESTOR_ISROOT, &is_root); - - /* In order to perform the access control check, - * we need to select a backend (even though - * we don't actually need it otherwise). - */ - slapi_pblock_set(pb, SLAPI_BACKEND, be); - - /* Access Strategy: - * If the user has WRITE-ONLY access, a new keytab is set on the entry. - */ - - ret = slapi_access_allowed(pb, targetEntry, "krbPrincipalKey", NULL, SLAPI_ACL_WRITE); - if (ret != LDAP_SUCCESS) { - errMesg = "Insufficient access rights\n"; - rc = LDAP_INSUFFICIENT_ACCESS; - goto free_and_return; - } - - /* increment kvno (will be 1 if this is a new entry) */ - kvno = ipapwd_get_cur_kvno(targetEntry) + 1; - - /* ok access allowed, init kset and continue to parse ber buffer */ - - errMesg = "Unable to set key\n"; - rc = LDAP_OPERATIONS_ERROR; - - kset = malloc(sizeof(struct ipapwd_keyset)); - if (!kset) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - goto free_and_return; - } - - /* this encoding assumes all keys have the same kvno */ - /* major-vno = 1 and minor-vno = 1 */ - kset->major_vno = 1; - kset->minor_vno = 1; - kset->kvno = kvno; - /* we also assum mkvno is 0 */ - kset->mkvno = 0; - - kset->keys = NULL; - kset->num_keys = 0; - - rtag = ber_peek_tag(ber, &tlen); - while (rtag == LBER_SEQUENCE) { - krb5_data plain; - krb5_enc_data cipher; - struct berval tval; - krb5_octet *kdata; - size_t klen; - - i = kset->num_keys; - - if (kset->keys) { - struct ipapwd_krbkey *newset; - - newset = realloc(kset->keys, sizeof(struct ipapwd_krbkey) * (i + 1)); - if (!newset) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - goto free_and_return; - } - kset->keys = newset; - } else { - kset->keys = malloc(sizeof(struct ipapwd_krbkey)); - if (!kset->keys) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - goto free_and_return; - } - } - kset->num_keys += 1; - - kset->keys[i].salt = NULL; - kset->keys[i].ekey = NULL; - kset->keys[i].s2kparams.bv_len = 0; - kset->keys[i].s2kparams.bv_val = NULL; - - /* EncryptionKey */ - rtag = ber_scanf(ber, "{t[{t[i]t[o]}]", &ttmp, &ttmp, &tint, &ttmp, &tval); - if (rtag == LBER_ERROR) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "ber_scanf failed\n"); - errMesg = "Invalid payload, failed to decode.\n"; - rc = LDAP_PROTOCOL_ERROR; - goto free_and_return; - } - - kset->keys[i].ekey = calloc(1, sizeof(struct ipapwd_krbkeydata)); - if (!kset->keys[i].ekey) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - goto free_and_return; - } - - kset->keys[i].ekey->type = tint; - - plain.length = tval.bv_len; - plain.data = tval.bv_val; - - krberr = krb5_c_encrypt_length(krbctx, krbcfg->kmkey->enctype, plain.length, &klen); - if (krberr) { - free(tval.bv_val); - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "krb encryption failed!\n"); - goto free_and_return; - } - - kdata = malloc(2 + klen); - if (!kdata) { - free(tval.bv_val); - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - goto free_and_return; - } - encode_int16(plain.length, kdata); - - kset->keys[i].ekey->value.bv_len = 2 + klen; - kset->keys[i].ekey->value.bv_val = (char *)kdata; - - cipher.ciphertext.length = klen; - cipher.ciphertext.data = (char *)kdata + 2; - - krberr = krb5_c_encrypt(krbctx, krbcfg->kmkey, 0, 0, &plain, &cipher); - if (krberr) { - free(tval.bv_val); - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "krb encryption failed!\n"); - goto free_and_return; - } - - free(tval.bv_val); - - rtag = ber_peek_tag(ber, &tlen); - - /* KrbSalt */ - if (rtag == (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1)) { - - rtag = ber_scanf(ber, "t[{t[i]", &ttmp, &ttmp, &tint); - if (rtag == LBER_ERROR) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "ber_scanf failed\n"); - errMesg = "Invalid payload, failed to decode.\n"; - rc = LDAP_PROTOCOL_ERROR; - goto free_and_return; - } - - kset->keys[i].salt = calloc(1, sizeof(struct ipapwd_krbkeydata)); - if (!kset->keys[i].salt) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n"); - goto free_and_return; - } - - kset->keys[i].salt->type = tint; - - rtag = ber_peek_tag(ber, &tlen); - if (rtag == (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1)) { - - rtag = ber_scanf(ber, "t[o]}]", &ttmp, &tval); - if (rtag == LBER_ERROR) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "ber_scanf failed\n"); - errMesg = "Invalid payload, failed to decode.\n"; - rc = LDAP_PROTOCOL_ERROR; - goto free_and_return; - } - - kset->keys[i].salt->value = tval; - - rtag = ber_peek_tag(ber, &tlen); - } - } - - /* FIXME: s2kparams - NOT implemented yet */ - if (rtag == (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 2)) { - rtag = ber_scanf(ber, "t[x]}", &ttmp); - } else { - rtag = ber_scanf(ber, "}", &ttmp); - } - if (rtag == LBER_ERROR) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "ber_scanf failed\n"); - errMesg = "Invalid payload, failed to decode.\n"; - rc = LDAP_PROTOCOL_ERROR; - goto free_and_return; - } - - rtag = ber_peek_tag(ber, &tlen); - } - - ber_free(ber, 1); - ber = NULL; - - /* filter un-supported encodings */ - ret = filter_keys(krbcfg, kset); - if (ret) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "keyset filtering failed\n"); - goto free_and_return; - } - - /* check if we have any left */ - if (kset->num_keys == 0) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "keyset filtering rejected all proposed keys\n"); - errMesg = "All enctypes provided are unsupported"; - rc = LDAP_UNWILLING_TO_PERFORM; - goto free_and_return; - } - - smods = slapi_mods_new(); - - /* change Last Password Change field with the current date */ - if (!gmtime_r(&(time_now), &utctime)) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "failed to retrieve current date (buggy gmtime_r ?)\n"); - slapi_mods_free(&smods); - 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); - - /* FIXME: set Password Expiration date ? */ -#if 0 - if (!gmtime_r(&(data->expireTime), &utctime)) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "failed to convert expiration date\n"); - slapi_ch_free_string(&randPasswd); - slapi_mods_free(&smods); - rc = 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); -#endif - - bval = encode_keys(kset); - if (!bval) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "encoding asn1 KrbSalt failed\n"); - slapi_mods_free(&smods); - goto free_and_return; - } - - svals[0] = slapi_value_new_berval(bval); - if (!svals[0]) { - slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", - "Converting berval to Slapi_Value\n"); - slapi_mods_free(&smods); - goto free_and_return; - } - - slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "krbPrincipalKey", svals); - - /* commit changes */ - ret = ipapwd_apply_mods(slapi_entry_get_dn_const(targetEntry), smods); - - if (ret != LDAP_SUCCESS) { - slapi_mods_free(&smods); - goto free_and_return; - - } - slapi_mods_free(&smods); - - /* Format of response - * - * KeytabGetRequest ::= SEQUENCE { - * new_kvno Int32 - * SEQUENCE OF KeyTypes - * } - * - * * List of accepted enctypes * - * KeyTypes ::= SEQUENCE { - * enctype Int32 - * } - */ - - errMesg = "Internal Error\n"; - rc = LDAP_OPERATIONS_ERROR; - - ber = ber_alloc(); - if (!ber) { - goto free_and_return; - } - - ret = ber_printf(ber, "{i{", (ber_int_t)kvno); - if (ret == -1) { - goto free_and_return; - } - - for (i = 0; i < kset->num_keys; i++) { - ret = ber_printf(ber, "{i}", (ber_int_t)kset->keys[i].ekey->type); - if (ret == -1) { - goto free_and_return; - } - } - ret = ber_printf(ber, "}}"); - if (ret == -1) { - goto free_and_return; - } - - if (ret != -1) { - struct berval *bvp; - LDAPControl new_ctrl = {0}; - - ret = ber_flatten(ber, &bvp); - if (ret == -1) { - goto free_and_return; - } - - new_ctrl.ldctl_oid = KEYTAB_RET_OID; - new_ctrl.ldctl_value = *bvp; - new_ctrl.ldctl_iscritical = 0; - rc= slapi_pblock_set(pb, SLAPI_ADD_RESCONTROL, &new_ctrl); - ber_bvfree(bvp); - } - - /* Free anything that we allocated above */ -free_and_return: - free(serviceName); - if (kset) ipapwd_keyset_free(&kset); - - if (bval) ber_bvfree(bval); - if (ber) ber_free(ber, 1); - - if (pbte) { - slapi_free_search_results_internal(pbte); - slapi_pblock_destroy(pbte); - } - if (svals) { - for (i = 0; svals[i]; i++) { - slapi_value_free(&svals[i]); - } - free(svals); - } - - if (krbname) krb5_free_principal(krbctx, krbname); - if (krbctx) krb5_free_context(krbctx); - - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", errMesg ? errMesg : "success"); - slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL); - - return SLAPI_PLUGIN_EXTENDED_SENT_RESULT; -} - -static int new_ipapwd_encsalt(krb5_context krbctx, const char * const *encsalts, - struct ipapwd_encsalt **es_types, int *num_es_types) -{ - struct ipapwd_encsalt *es; - int nes, i; - - for (i = 0; encsalts[i]; i++) /* count */ ; - es = calloc(i + 1, sizeof(struct ipapwd_encsalt)); - if (!es) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_start", "Out of memory!\n"); - return LDAP_OPERATIONS_ERROR; - } - - for (i = 0, nes = 0; encsalts[i]; i++) { - char *enc, *salt; - krb5_int32 tmpsalt; - krb5_enctype tmpenc; - krb5_boolean similar; - krb5_error_code krberr; - int j; - - enc = strdup(encsalts[i]); - if (!enc) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_start", - "Allocation error\n"); - return LDAP_OPERATIONS_ERROR; - } - salt = strchr(enc, ':'); - if (!salt) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_start", - "Invalid krb5 enc string\n"); - free(enc); - continue; - } - *salt = '\0'; /* null terminate the enc type */ - salt++; /* skip : */ - - krberr = krb5_string_to_enctype(enc, &tmpenc); - if (krberr) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_start", - "Invalid krb5 enctype\n"); - free(enc); - continue; - } - - krberr = krb5_string_to_salttype(salt, &tmpsalt); - for (j = 0; j < nes; j++) { - krb5_c_enctype_compare(krbctx, es[j].enc_type, tmpenc, &similar); - if (similar && (es[j].salt_type == tmpsalt)) { - break; - } - } - - if (j == nes) { - /* not found */ - es[j].enc_type = tmpenc; - es[j].salt_type = tmpsalt; - nes++; - } - - free(enc); - } - - *es_types = es; - *num_es_types = nes; - - return LDAP_SUCCESS; -} - -static struct ipapwd_krbcfg *ipapwd_getConfig(void) -{ - krb5_error_code krberr; - struct ipapwd_krbcfg *config = NULL; - krb5_keyblock *kmkey = NULL; - Slapi_Entry *realm_entry = NULL; - Slapi_Entry *config_entry = NULL; - Slapi_Attr *a; - Slapi_Value *v; - BerElement *be = NULL; - ber_tag_t tag, tmp; - ber_int_t ttype; - const struct berval *bval; - struct berval *mkey = NULL; - char **encsalts; - char *tmpstr; - int i, ret; - - config = calloc(1, sizeof(struct ipapwd_krbcfg)); - if (!config) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Out of memory!\n"); - goto free_and_error; - } - kmkey = calloc(1, sizeof(krb5_keyblock)); - if (!kmkey) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Out of memory!\n"); - goto free_and_error; - } - config->kmkey = kmkey; - - krberr = krb5_init_context(&config->krbctx); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "krb5_init_context failed\n"); - goto free_and_error; - } - - ret = krb5_get_default_realm(config->krbctx, &config->realm); - if (ret) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Failed to get default realm?!\n"); - goto free_and_error; - } - - /* get the Realm Container entry */ - ret = ipapwd_getEntry(ipa_realm_dn, &realm_entry, NULL); - if (ret != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "No realm Entry?\n"); - goto free_and_error; - } - - /*** get the Kerberos Master Key ***/ - - ret = slapi_entry_attr_find(realm_entry, "krbMKey", &a); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "No master key??\n"); - goto free_and_error; - } - - /* there should be only one value here */ - ret = slapi_attr_first_value(a, &v); - if (ret == -1) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "No master key??\n"); - goto free_and_error; - } - - bval = slapi_value_get_berval(v); - if (!bval) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Error retrieving master key berval\n"); - goto free_and_error; - } - - be = ber_init(bval); - if (!bval) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "ber_init() failed!\n"); - goto free_and_error; - } - - tag = ber_scanf(be, "{i{iO}}", &tmp, &ttype, &mkey); - if (tag == LBER_ERROR) { - slapi_log_error(SLAPI_LOG_TRACE, "ipapwd_getConfig", - "Bad Master key encoding ?!\n"); - goto free_and_error; - } - - kmkey->magic = KV5M_KEYBLOCK; - kmkey->enctype = ttype; - kmkey->length = mkey->bv_len; - kmkey->contents = malloc(mkey->bv_len); - if (!kmkey->contents) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Out of memory!\n"); - goto free_and_error; - } - memcpy(kmkey->contents, mkey->bv_val, mkey->bv_len); - ber_bvfree(mkey); - ber_free(be, 1); - mkey = NULL; - be = NULL; - - /*** get the Supported Enc/Salt types ***/ - - encsalts = slapi_entry_attr_get_charray(realm_entry, "krbSupportedEncSaltTypes"); - if (encsalts) { - ret = new_ipapwd_encsalt(config->krbctx, - (const char * const *)encsalts, - &config->supp_encsalts, - &config->num_supp_encsalts); - slapi_ch_array_free(encsalts); - } else { - slapi_log_error(SLAPI_LOG_TRACE, "ipapwd_getConfig", - "No configured salt types use defaults\n"); - ret = new_ipapwd_encsalt(config->krbctx, - ipapwd_def_encsalts, - &config->supp_encsalts, - &config->num_supp_encsalts); - } - if (ret) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Can't get Supported EncSalt Types\n"); - goto free_and_error; - } - - /*** get the Preferred Enc/Salt types ***/ - - encsalts = slapi_entry_attr_get_charray(realm_entry, "krbDefaultEncSaltTypes"); - if (encsalts) { - ret = new_ipapwd_encsalt(config->krbctx, - (const char * const *)encsalts, - &config->pref_encsalts, - &config->num_pref_encsalts); - slapi_ch_array_free(encsalts); - } else { - slapi_log_error(SLAPI_LOG_TRACE, "ipapwd_getConfig", - "No configured salt types use defaults\n"); - ret = new_ipapwd_encsalt(config->krbctx, - ipapwd_def_encsalts, - &config->pref_encsalts, - &config->num_pref_encsalts); - } - if (ret) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Can't get Preferred EncSalt Types\n"); - goto free_and_error; - } - - slapi_entry_free(realm_entry); - - /* get the Realm Container entry */ - ret = ipapwd_getEntry(ipa_pwd_config_dn, &config_entry, NULL); - if (ret != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "No config Entry? Impossible!\n"); - goto free_and_error; - } - config->passsync_mgrs = slapi_entry_attr_get_charray(config_entry, "passSyncManagersDNs"); - /* now add Directory Manager, it is always added by default */ - tmpstr = slapi_ch_strdup("cn=Directory Manager"); - slapi_ch_array_add(&config->passsync_mgrs, tmpstr); - if (config->passsync_mgrs == NULL) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_getConfig", - "Out of memory!\n"); - goto free_and_error; - } - for (i = 0; config->passsync_mgrs[i]; i++) /* count */ ; - config->num_passsync_mgrs = i; - - return config; - -free_and_error: - if (mkey) ber_bvfree(mkey); - if (be) ber_free(be, 1); - if (kmkey) { - free(kmkey->contents); - free(kmkey); - } - if (config) { - if (config->krbctx) { - if (config->realm) - krb5_free_default_realm(config->krbctx, config->realm); - krb5_free_context(config->krbctx); - } - free(config->pref_encsalts); - free(config->supp_encsalts); - slapi_ch_array_free(config->passsync_mgrs); - free(config); - } - slapi_entry_free(config_entry); - slapi_entry_free(realm_entry); - return NULL; -} - -#define IPAPWD_CHECK_CONN_SECURE 0x00000001 -#define IPAPWD_CHECK_DN 0x00000002 - -static int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg, - struct ipapwd_krbcfg **config, - int check_flags) -{ - int ret, sasl_ssf, is_ssl; - int rc = LDAP_SUCCESS; - Slapi_Backend *be; - const Slapi_DN *psdn; - Slapi_DN *sdn; - char *dn = NULL; - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_gen_checks\n"); - -#ifdef LDAP_EXTOP_PASSMOD_CONN_SECURE - if (check_flags & IPAPWD_CHECK_CONN_SECURE) { - /* Allow password modify only for SSL/TLS established connections and - * connections using SASL privacy layers */ - if (slapi_pblock_get(pb, SLAPI_CONN_SASL_SSF, &sasl_ssf) != 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "Could not get SASL SSF from connection\n"); - *errMesg = "Operation requires a secure connection.\n"; - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - if (slapi_pblock_get(pb, SLAPI_CONN_IS_SSL_SESSION, &is_ssl) != 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "Could not get IS SSL from connection\n"); - *errMesg = "Operation requires a secure connection.\n"; - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - if ((0 == is_ssl) && (sasl_ssf <= 1)) { - *errMesg = "Operation requires a secure connection.\n"; - rc = LDAP_CONFIDENTIALITY_REQUIRED; - goto done; - } - } -#endif - - if (check_flags & IPAPWD_CHECK_DN) { - /* check we have a valid DN in the pblock or just abort */ - ret = slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn); - if (ret) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "Tried to change password for an invalid DN [%s]\n", - dn?dn:"<NULL>"); - *errMesg = "Invalid DN"; - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - sdn = slapi_sdn_new_dn_byref(dn); - if (!sdn) { - *errMesg = "Internal Error"; - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - be = slapi_be_select(sdn); - slapi_sdn_free(&sdn); - - psdn = slapi_be_getsuffix(be, 0); - if (!psdn) { - *errMesg = "Invalid DN"; - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - } - - /* get the kerberos context and master key */ - *config = ipapwd_getConfig(); - if (NULL == *config) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "Error Retrieving Master Key"); - *errMesg = "Fatal Internal Error"; - rc = LDAP_OPERATIONS_ERROR; - } - -done: - return rc; -} - -static int ipapwd_extop(Slapi_PBlock *pb) -{ - struct ipapwd_krbcfg *krbcfg = NULL; - char *errMesg = NULL; - char *oid = NULL; - int rc, ret; - - slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_extop\n"); - - rc = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_CONN_SECURE); - if (rc) { - goto free_and_return; - } - - /* Before going any further, we'll make sure that the right extended - * operation plugin has been called: i.e., the OID shipped whithin the - * extended operation request must match this very plugin's OIDs: - * EXOP_PASSWD_OID or KEYTAB_SET_OID. */ - if (slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_OID, &oid) != 0) { - errMesg = "Could not get OID value from request.\n"; - rc = LDAP_OPERATIONS_ERROR; - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", errMesg); - goto free_and_return; - } else { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", - "Received extended operation request with OID %s\n", oid); - } - - if (strcasecmp(oid, EXOP_PASSWD_OID) == 0) { - ret = ipapwd_chpwop(pb, krbcfg); - free_ipapwd_krbcfg(&krbcfg); - return ret; - } - if (strcasecmp(oid, KEYTAB_SET_OID) == 0) { - ret = ipapwd_setkeytab(pb, krbcfg); - free_ipapwd_krbcfg(&krbcfg); - return ret; - } - - errMesg = "Request OID does not match supported OIDs.\n"; - rc = LDAP_OPERATIONS_ERROR; - -free_and_return: - if (krbcfg) free_ipapwd_krbcfg(&krbcfg); - - slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", errMesg); - slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL); - - return SLAPI_PLUGIN_EXTENDED_SENT_RESULT; -} - -/***************************************************************************** - * pre/post operations to intercept writes to userPassword - ****************************************************************************/ - -#define IPAPWD_OP_NULL 0 -#define IPAPWD_OP_ADD 1 -#define IPAPWD_OP_MOD 2 -struct ipapwd_operation { - struct ipapwd_data pwdata; - int pwd_op; - int is_krb; -}; - -/* structure with information for each extension */ -struct ipapwd_op_ext { - char *object_name; /* name of the object extended */ - int object_type; /* handle to the extended object */ - int handle; /* extension handle */ -}; - -static struct ipapwd_op_ext ipapwd_op_ext_list; - -static void *ipapwd_op_ext_constructor(void *object, void *parent) -{ - struct ipapwd_operation *ext; - - ext = (struct ipapwd_operation *)slapi_ch_calloc(1, sizeof(struct ipapwd_operation)); - return ext; -} - -static void ipapwd_op_ext_destructor(void *ext, void *object, void *parent) -{ - struct ipapwd_operation *pwdop = (struct ipapwd_operation *)ext; - if (!pwdop) - return; - if (pwdop->pwd_op != IPAPWD_OP_NULL) { - slapi_ch_free_string(&(pwdop->pwdata.dn)); - slapi_ch_free_string(&(pwdop->pwdata.password)); - } - slapi_ch_free((void **)&pwdop); -} - -static int ipapwd_entry_checks(Slapi_PBlock *pb, struct slapi_entry *e, - int *is_root, int *is_krb, int *is_smb, - char *attr, int access) -{ - Slapi_Value *sval; - int rc; - - /* Check ACIs */ - slapi_pblock_get(pb, SLAPI_REQUESTOR_ISROOT, is_root); - - if (!*is_root) { - /* verify this user is allowed to write a user password */ - rc = slapi_access_allowed(pb, e, attr, NULL, access); - if (rc != LDAP_SUCCESS) { - /* we have no business here, the operation will be denied anyway */ - rc = LDAP_SUCCESS; - goto done; - } - } - - /* Check if this is a krbPrincial and therefore needs us to generate other - * hashes */ - sval = slapi_value_new_string("krbPrincipalAux"); - if (!sval) { - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - *is_krb = slapi_entry_attr_has_syntax_value(e, SLAPI_ATTR_OBJECTCLASS, sval); - slapi_value_free(&sval); - - sval = slapi_value_new_string("sambaSamAccount"); - if (!sval) { - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - *is_smb = slapi_entry_attr_has_syntax_value(e, SLAPI_ATTR_OBJECTCLASS, sval); - slapi_value_free(&sval); - - rc = LDAP_SUCCESS; - -done: - return rc; -} - -static int ipapwd_preop_gen_hashes(struct ipapwd_krbcfg *krbcfg, - struct ipapwd_operation *pwdop, - char *userpw, - int is_krb, int is_smb, - Slapi_Value ***svals, - char **nthash, char **lmhash) -{ - int rc; - - if (is_krb) { - - pwdop->is_krb = 1; - - *svals = encrypt_encode_key(krbcfg, &pwdop->pwdata); - - if (!*svals) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "key encryption/encoding failed\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - } - - if (is_smb) { - char lm[33], nt[33]; - struct ntlm_keys ntlm; - int ntlm_flags = 0; - int ret; - - /* TODO: retrieve if we want to store the LM hash or not */ - ntlm_flags = KTF_LM_HASH | KTF_NT_HASH; - - ret = encode_ntlm_keys(userpw, ntlm_flags, &ntlm); - if (ret) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "Failed to generate NT/LM hashes\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - if (ntlm_flags & KTF_LM_HASH) { - hexbuf(lm, ntlm.lm); - lm[32] = '\0'; - *lmhash = slapi_ch_strdup(lm); - } - if (ntlm_flags & KTF_NT_HASH) { - hexbuf(nt, ntlm.nt); - nt[32] = '\0'; - *nthash = slapi_ch_strdup(nt); - } - } - - rc = LDAP_SUCCESS; - -done: - - return rc; -} - -/* PRE ADD 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. - * Run a password policy check. - * Check if krb or smb hashes are required by testing if the krb or smb - * objectclasses are present. - * store information for the post operation - */ -static int ipapwd_pre_add(Slapi_PBlock *pb) -{ - struct ipapwd_krbcfg *krbcfg = NULL; - char *errMesg = "Internal operations error\n"; - struct slapi_entry *e = NULL; - char *userpw = NULL; - char *dn = NULL; - struct ipapwd_operation *pwdop = NULL; - void *op; - int is_repl_op, is_root, is_krb, is_smb; - int ret, rc; - - slapi_log_error(SLAPI_LOG_TRACE, IPAPWD_PLUGIN_NAME, "=> ipapwd_pre_add\n"); - - ret = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl_op); - if (ret != 0) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "slapi_pblock_get failed!?\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - /* pass through if this is a replicated operation */ - if (is_repl_op) - return 0; - - /* retrieve the entry */ - slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e); - if (NULL == e) - return 0; - - /* check this is something interesting for us first */ - userpw = slapi_entry_attr_get_charptr(e, SLAPI_USERPWD_ATTR); - if (!userpw) { - /* nothing interesting here */ - return 0; - } - - /* Ok this is interesting, - * Check this is a clear text password, or refuse operation */ - if ('{' == userpw[0]) { - if (0 == strncasecmp(userpw, "{CLEAR}", strlen("{CLEAR}"))) { - char *tmp = slapi_ch_strdup(&userpw[strlen("{CLEAR}")]); - if (NULL == tmp) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "Strdup failed, Out of memory\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - slapi_ch_free_string(&userpw); - userpw = tmp; - } else if (slapi_is_encoded(userpw)) { - - slapi_ch_free_string(&userpw); - - /* check if we have access to the unhashed user password */ - userpw = slapi_entry_attr_get_charptr(e, "unhashed#user#password"); - if (!userpw) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Pre-Encoded passwords are not valid\n"); - errMesg = "Pre-Encoded passwords are not valid\n"; - rc = LDAP_CONSTRAINT_VIOLATION; - goto done; - } - } - } - - rc = ipapwd_entry_checks(pb, e, - &is_root, &is_krb, &is_smb, - NULL, SLAPI_ACL_ADD); - if (rc) { - goto done; - } - - rc = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_DN); - if (rc) { - goto done; - } - - /* Get target DN */ - ret = slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn); - if (ret) { - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - /* time to get the operation handler */ - ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op); - if (ret != 0) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "slapi_pblock_get failed!?\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - pwdop = slapi_get_object_extension(ipapwd_op_ext_list.object_type, - op, ipapwd_op_ext_list.handle); - if (NULL == pwdop) { - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - pwdop->pwd_op = IPAPWD_OP_ADD; - pwdop->pwdata.password = slapi_ch_strdup(userpw); - - if (is_root) { - pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR; - } else { - char *binddn; - int i; - - pwdop->pwdata.changetype = IPA_CHANGETYPE_ADMIN; - - /* Check Bind DN */ - slapi_pblock_get(pb, SLAPI_CONN_DN, &binddn); - - /* if it is a passsync manager we also need to skip resets */ - for (i = 0; i < krbcfg->num_passsync_mgrs; i++) { - if (strcasecmp(krbcfg->passsync_mgrs[i], binddn) == 0) { - pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR; - break; - } - } - } - - pwdop->pwdata.dn = slapi_ch_strdup(dn); - pwdop->pwdata.timeNow = time(NULL); - pwdop->pwdata.target = e; - - ret = ipapwd_CheckPolicy(&pwdop->pwdata); - if (ret) { - errMesg = "Password Fails to meet minimum strength criteria"; - rc = LDAP_CONSTRAINT_VIOLATION; - goto done; - } - - if (is_krb || is_smb) { - - Slapi_Value **svals = NULL; - char *nt = NULL; - char *lm = NULL; - - rc = ipapwd_preop_gen_hashes(krbcfg, - pwdop, userpw, - is_krb, is_smb, - &svals, &nt, &lm); - if (rc) { - goto done; - } - - if (svals) { - /* add/replace values in existing entry */ - ret = slapi_entry_attr_replace_sv(e, "krbPrincipalKey", svals); - if (ret) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "failed to set encoded values in entry\n"); - rc = LDAP_OPERATIONS_ERROR; - ipapwd_free_slapi_value_array(&svals); - goto done; - } - - ipapwd_free_slapi_value_array(&svals); - } - - if (lm) { - /* set value */ - slapi_entry_attr_set_charptr(e, "sambaLMPassword", lm); - slapi_ch_free_string(&lm); - } - if (nt) { - /* set value */ - slapi_entry_attr_set_charptr(e, "sambaNTPassword", nt); - slapi_ch_free_string(&nt); - } - } - - rc = LDAP_SUCCESS; - -done: - if (pwdop) pwdop->pwdata.target = NULL; - free_ipapwd_krbcfg(&krbcfg); - slapi_ch_free_string(&userpw); - if (rc != LDAP_SUCCESS) { - slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL); - return -1; - } - return 0; -} - -/* PRE MOD Operation: - * Gets the clean text password (fail the operation if the password came - * pre-hashed, unless this is a replicated operation). - * Check user is authorized to add it otherwise just returns, operation will - * fail later anyway. - * Check if krb or smb hashes are required by testing if the krb or smb - * objectclasses are present. - * Run a password policy check. - * store information for the post operation - */ -static int ipapwd_pre_mod(Slapi_PBlock *pb) -{ - struct ipapwd_krbcfg *krbcfg = NULL; - char *errMesg = NULL; - LDAPMod **mods; - Slapi_Mod *smod, *tmod; - Slapi_Mods *smods = NULL; - char *userpw = NULL; - char *unhashedpw = NULL; - char *dn = NULL; - Slapi_DN *tmp_dn; - struct slapi_entry *e = NULL; - struct ipapwd_operation *pwdop = NULL; - void *op; - int is_repl_op, is_pwd_op, is_root, is_krb, is_smb; - int ret, rc; - - slapi_log_error(SLAPI_LOG_TRACE, IPAPWD_PLUGIN_NAME, "=> ipapwd_pre_mod\n"); - - ret = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl_op); - if (ret != 0) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "slapi_pblock_get failed!?\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - /* pass through if this is a replicated operation */ - if (is_repl_op) { - rc = LDAP_SUCCESS; - goto done; - } - - /* grab the mods - we'll put them back later with - * our modifications appended - */ - slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); - smods = slapi_mods_new(); - slapi_mods_init_passin(smods, mods); - - /* In the first pass, - * only check there is anything we are interested in */ - is_pwd_op = 0; - tmod = slapi_mod_new(); - smod = slapi_mods_get_first_smod(smods, tmod); - while (smod) { - struct berval *bv; - const char *type; - int mop; - - type = slapi_mod_get_type(smod); - if (slapi_attr_types_equivalent(type, SLAPI_USERPWD_ATTR)) { - mop = slapi_mod_get_operation(smod); - /* check op filtering out LDAP_MOD_BVALUES */ - switch (mop & 0x0f) { - case LDAP_MOD_ADD: - case LDAP_MOD_REPLACE: - is_pwd_op = 1; - default: - break; - } - } - - /* we check for unahsehd password here so that we are sure to catch them - * early, before further checks go on, this helps checking - * LDAP_MOD_DELETE operations in some corner cases later */ - /* we keep only the last one if multiple are provided for any absurd - * reason */ - if (slapi_attr_types_equivalent(type, "unhashed#user#password")) { - bv = slapi_mod_get_first_value(smod); - if (!bv) { - slapi_mod_free(&tmod); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - slapi_ch_free_string(&unhashedpw); - unhashedpw = slapi_ch_malloc(bv->bv_len+1); - if (!unhashedpw) { - slapi_mod_free(&tmod); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - memcpy(unhashedpw, bv->bv_val, bv->bv_len); - unhashedpw[bv->bv_len] = '\0'; - } - slapi_mod_done(tmod); - smod = slapi_mods_get_next_smod(smods, tmod); - } - slapi_mod_free(&tmod); - - /* If userPassword is not modified we are done here */ - if (! is_pwd_op) { - rc = LDAP_SUCCESS; - goto done; - } - - /* OK swe have something interesting here, start checking for - * pre-requisites */ - - /* Get target DN */ - ret = slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn); - if (ret) { - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - tmp_dn = slapi_sdn_new_dn_byref(dn); - if (tmp_dn) { - /* xxxPAR: Ideally SLAPI_MODIFY_EXISTING_ENTRY should be - * available but it turns out that is only true if you are - * a dbm backend pre-op plugin - lucky dbm backend pre-op - * plugins. - * I think that is wrong since the entry is useful for filter - * tests and schema checks and this plugin shouldn't be limited - * to a single backend type, but I don't want that fight right - * now so we go get the entry here - * - slapi_pblock_get( pb, SLAPI_MODIFY_EXISTING_ENTRY, &e); - */ - ret = slapi_search_internal_get_entry(tmp_dn, 0, &e, ipapwd_plugin_id); - slapi_sdn_free(&tmp_dn); - if (ret != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Failed tpo retrieve entry?!?\n"); - rc = LDAP_NO_SUCH_OBJECT; - goto done; - } - } - - rc = ipapwd_entry_checks(pb, e, - &is_root, &is_krb, &is_smb, - SLAPI_USERPWD_ATTR, SLAPI_ACL_WRITE); - if (rc) { - goto done; - } - - rc = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_DN); - if (rc) { - goto done; - } - - /* run through the mods again and adjust flags if operations affect them */ - tmod = slapi_mod_new(); - smod = slapi_mods_get_first_smod(smods, tmod); - while (smod) { - struct berval *bv; - const char *type; - int mop; - - type = slapi_mod_get_type(smod); - if (slapi_attr_types_equivalent(type, SLAPI_USERPWD_ATTR)) { - mop = slapi_mod_get_operation(smod); - /* check op filtering out LDAP_MOD_BVALUES */ - switch (mop & 0x0f) { - case LDAP_MOD_ADD: - /* FIXME: should we try to track cases where we would end up - * with multiple userPassword entries ?? */ - case LDAP_MOD_REPLACE: - is_pwd_op = 1; - bv = slapi_mod_get_first_value(smod); - if (!bv) { - slapi_mod_free(&tmod); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - slapi_ch_free_string(&userpw); - userpw = slapi_ch_malloc(bv->bv_len+1); - if (!userpw) { - slapi_mod_free(&tmod); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - memcpy(userpw, bv->bv_val, bv->bv_len); - userpw[bv->bv_len] = '\0'; - break; - case LDAP_MOD_DELETE: - /* reset only if we are deleting all values, or the exact - * same value previously set, otherwise we are just trying to - * add a new value and delete an existing one */ - bv = slapi_mod_get_first_value(smod); - if (!bv) { - is_pwd_op = 0; - } else { - if (0 == strncmp(userpw, bv->bv_val, bv->bv_len) || - 0 == strncmp(unhashedpw, bv->bv_val, bv->bv_len)) - is_pwd_op = 0; - } - default: - break; - } - } - - if (slapi_attr_types_equivalent(type, SLAPI_ATTR_OBJECTCLASS)) { - mop = slapi_mod_get_operation(smod); - /* check op filtering out LDAP_MOD_BVALUES */ - switch (mop & 0x0f) { - case LDAP_MOD_REPLACE: - /* if objectclasses are replaced we need to start clean with - * flags, so we sero them out and see if they get set again */ - is_krb = 0; - is_smb = 0; - - case LDAP_MOD_ADD: - bv = slapi_mod_get_first_value(smod); - if (!bv) { - slapi_mod_free(&tmod); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - do { - if (0 == strncasecmp("krbPrincipalAux", bv->bv_val, bv->bv_len)) - is_krb = 1; - if (0 == strncasecmp("sambaSamAccount", bv->bv_val, bv->bv_len)) - is_smb = 1; - } while ((bv = slapi_mod_get_next_value(smod)) != NULL); - - break; - - case LDAP_MOD_DELETE: - /* can this happen for objectclasses ? */ - is_krb = 0; - is_smb = 0; - - default: - break; - } - } - - slapi_mod_done(tmod); - smod = slapi_mods_get_next_smod(smods, tmod); - } - slapi_mod_free(&tmod); - - /* It seem like we have determined that the end result will be deletion of - * the userPassword attribute, so we have no more business here */ - if (! is_pwd_op) { - rc = LDAP_SUCCESS; - goto done; - } - - /* Check this is a clear text password, or refuse operation (only if we need - * to comput other hashes */ - if (! unhashedpw) { - if ('{' == userpw[0]) { - if (0 == strncasecmp(userpw, "{CLEAR}", strlen("{CLEAR}"))) { - unhashedpw = slapi_ch_strdup(&userpw[strlen("{CLEAR}")]); - if (NULL == unhashedpw) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "Strdup failed, Out of memory\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - slapi_ch_free_string(&userpw); - - } else if (slapi_is_encoded(userpw)) { - - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Pre-Encoded passwords are not valid\n"); - errMesg = "Pre-Encoded passwords are not valid\n"; - rc = LDAP_CONSTRAINT_VIOLATION; - goto done; - } - } - } - - /* time to get the operation handler */ - ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op); - if (ret != 0) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "slapi_pblock_get failed!?\n"); - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - pwdop = slapi_get_object_extension(ipapwd_op_ext_list.object_type, - op, ipapwd_op_ext_list.handle); - if (NULL == pwdop) { - rc = LDAP_OPERATIONS_ERROR; - goto done; - } - - pwdop->pwd_op = IPAPWD_OP_MOD; - pwdop->pwdata.password = slapi_ch_strdup(unhashedpw); - pwdop->pwdata.changetype = IPA_CHANGETYPE_NORMAL; - - if (is_root) { - pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR; - } else { - char *binddn; - Slapi_DN *bdn, *tdn; - int i; - - /* Check Bind DN */ - slapi_pblock_get(pb, SLAPI_CONN_DN, &binddn); - bdn = slapi_sdn_new_dn_byref(binddn); - tdn = slapi_sdn_new_dn_byref(dn); - - /* if the change is performed by someone else, - * it is an admin change that will require a new - * password change immediately as per our IPA policy */ - if (slapi_sdn_compare(bdn, tdn)) { - pwdop->pwdata.changetype = IPA_CHANGETYPE_ADMIN; - - /* if it is a passsync manager we also need to skip resets */ - for (i = 0; i < krbcfg->num_passsync_mgrs; i++) { - if (strcasecmp(krbcfg->passsync_mgrs[i], binddn) == 0) { - pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR; - break; - } - } - - } - - slapi_sdn_free(&bdn); - slapi_sdn_free(&tdn); - - } - - pwdop->pwdata.dn = slapi_ch_strdup(dn); - pwdop->pwdata.timeNow = time(NULL); - pwdop->pwdata.target = e; - - ret = ipapwd_CheckPolicy(&pwdop->pwdata); - if (ret) { - errMesg = "Password Fails to meet minimum strength criteria"; - rc = LDAP_CONSTRAINT_VIOLATION; - goto done; - } - - if (is_krb || is_smb) { - - Slapi_Value **svals = NULL; - char *nt = NULL; - char *lm = NULL; - - rc = ipapwd_preop_gen_hashes(krbcfg, - pwdop, unhashedpw, - is_krb, is_smb, - &svals, &nt, &lm); - if (rc) { - goto done; - } - - if (svals) { - /* replace values */ - slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, - "krbPrincipalKey", svals); - ipapwd_free_slapi_value_array(&svals); - } - - if (lm) { - /* replace value */ - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, - "sambaLMPassword", lm); - slapi_ch_free_string(&lm); - } - if (nt) { - /* replace value */ - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, - "sambaNTPassword", nt); - slapi_ch_free_string(&nt); - } - } - - rc = LDAP_SUCCESS; - -done: - free_ipapwd_krbcfg(&krbcfg); - slapi_ch_free_string(&userpw); /* just to be sure */ - slapi_ch_free_string(&unhashedpw); /* we copied it to pwdop */ - if (e) slapi_entry_free(e); /* this is a copy in this function */ - if (pwdop) pwdop->pwdata.target = NULL; - - /* put back a, possibly modified, set of mods */ - if (smods) { - mods = slapi_mods_get_ldapmods_passout(smods); - slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods); - slapi_mods_free(&smods); - } - - if (rc != LDAP_SUCCESS) { - slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL); - return -1; - } - - return 0; -} - -static int ipapwd_post_op(Slapi_PBlock *pb) -{ - char *errMesg = "Internal operations error\n"; - void *op; - struct ipapwd_operation *pwdop = NULL; - Slapi_Mods *smods; - Slapi_Value **pwvals; - struct tm utctime; - char timestr[GENERALIZED_TIME_LENGTH+1]; - int ret; - - slapi_log_error(SLAPI_LOG_TRACE, IPAPWD_PLUGIN_NAME, - "=> ipapwd_post_add\n"); - - /* time to get the operation handler */ - ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op); - if (ret != 0) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "slapi_pblock_get failed!?\n"); - return 0; - } - - pwdop = slapi_get_object_extension(ipapwd_op_ext_list.object_type, - op, ipapwd_op_ext_list.handle); - if (NULL == pwdop) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Internal error, couldn't find pluginextension ?!\n"); - return 0; - } - - /* not interesting */ - if (IPAPWD_OP_NULL == pwdop->pwd_op) - return 0; - - if ( ! (pwdop->is_krb)) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Not a kerberos user, ignore krb attributes\n"); - return 0; - } - - /* prepare changes that can be made only as root */ - smods = slapi_mods_new(); - - /* change Last Password Change field with the current date */ - if (!gmtime_r(&(pwdop->pwdata.timeNow), &utctime)) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "failed to parse current date (buggy gmtime_r ?)\n"); - goto done; - } - strftime(timestr, GENERALIZED_TIME_LENGTH+1, - "%Y%m%d%H%M%SZ", &utctime); - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, - "krbLastPwdChange", timestr); - - /* set Password Expiration date */ - if (!gmtime_r(&(pwdop->pwdata.expireTime), &utctime)) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "failed to parse expiration date (buggy gmtime_r ?)\n"); - goto done; - } - strftime(timestr, GENERALIZED_TIME_LENGTH+1, - "%Y%m%d%H%M%SZ", &utctime); - slapi_mods_add_string(smods, LDAP_MOD_REPLACE, - "krbPasswordExpiration", timestr); - - /* This was a mod operation on an existing entry, make sure we also update - * the password history based on the entry we saved from the pre-op */ - if (IPAPWD_OP_MOD == pwdop->pwd_op) { - Slapi_DN *tmp_dn = slapi_sdn_new_dn_byref(pwdop->pwdata.dn); - if (tmp_dn) { - ret = slapi_search_internal_get_entry(tmp_dn, 0, - &pwdop->pwdata.target, - ipapwd_plugin_id); - slapi_sdn_free(&tmp_dn); - if (ret != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Failed tpo retrieve entry?!?\n"); - goto done; - } - } - pwvals = ipapwd_setPasswordHistory(smods, &pwdop->pwdata); - if (pwvals) { - slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, - "passwordHistory", pwvals); - } - } - - ret = ipapwd_apply_mods(pwdop->pwdata.dn, smods); - if (ret) - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Failed to set additional password attributes in the post-op!\n"); - -done: - if (pwdop && pwdop->pwdata.target) slapi_entry_free(pwdop->pwdata.target); - slapi_mods_free(&smods); - return 0; -} - -/* Copied from ipamo_string2filter() - * - * ipapwd_string2filter() - * - * For some reason slapi_str2filter writes to its input - * which means you cannot pass in a string constant - * so this is a fix up function for that - */ -Slapi_Filter *ipapwd_string2filter(char *strfilter) -{ - Slapi_Filter *ret = NULL; - char *idontbelieveit = slapi_ch_strdup(strfilter); - - ret = slapi_str2filter(idontbelieveit); - - slapi_ch_free_string(&idontbelieveit); - - return ret; -} - -/* Init data structs */ -static int ipapwd_start( Slapi_PBlock *pb ) -{ - krb5_context krbctx; - krb5_error_code krberr; - char *realm = NULL; - char *config_dn; - char *partition_dn; - Slapi_Entry *config_entry = NULL; - int ret; - - krberr = krb5_init_context(&krbctx); - if (krberr) { - slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_start", "krb5_init_context failed\n"); - return LDAP_OPERATIONS_ERROR; - } - - if (slapi_pblock_get(pb, SLAPI_TARGET_DN, &config_dn) != 0) { - slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "No config DN?\n"); - ret = LDAP_OPERATIONS_ERROR; - goto done; - } - - if (ipapwd_getEntry(config_dn, &config_entry, NULL) != LDAP_SUCCESS) { - slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "No config Entry?\n"); - ret = LDAP_OPERATIONS_ERROR; - goto done; - } - - partition_dn = slapi_entry_attr_get_charptr(config_entry, "nsslapd-realmtree"); - if (!partition_dn) { - slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Missing partition configuration entry (nsslapd-realmTree)!\n"); - ret = LDAP_OPERATIONS_ERROR; - goto done; - } - - ret = krb5_get_default_realm(krbctx, &realm); - if (ret) { - slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Failed to get default realm?!\n"); - ret = LDAP_OPERATIONS_ERROR; - goto done; - } - ipa_realm_dn = slapi_ch_smprintf("cn=%s,cn=kerberos,%s", realm, partition_dn); - if (!ipa_realm_dn) { - slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Out of memory ?\n"); - ret = LDAP_OPERATIONS_ERROR; - goto done; - } - - ipa_pwd_config_dn = slapi_ch_strdup(config_dn); - if (!ipa_pwd_config_dn) { - slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Out of memory ?\n"); - ret = LDAP_OPERATIONS_ERROR; - goto done; - } - ipa_changepw_principal_dn = - slapi_ch_smprintf("krbprincipalname=kadmin/changepw@%s,%s", - realm, ipa_realm_dn); - if (!ipa_changepw_principal_dn) { - slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Out of memory ?\n"); - ret = LDAP_OPERATIONS_ERROR; - goto done; - } - - ret = LDAP_SUCCESS; - -done: - free(realm); - krb5_free_context(krbctx); - if (config_entry) slapi_entry_free(config_entry); - return ret; -} - - -static int ipapwd_ext_init() -{ - int ret; - - ipapwd_op_ext_list.object_name = SLAPI_EXT_OPERATION; - - ret = slapi_register_object_extension(IPAPWD_PLUGIN_NAME, - SLAPI_EXT_OPERATION, - ipapwd_op_ext_constructor, - ipapwd_op_ext_destructor, - &ipapwd_op_ext_list.object_type, - &ipapwd_op_ext_list.handle); - - return ret; -} - - -static char *ipapwd_oid_list[] = { - EXOP_PASSWD_OID, - KEYTAB_SET_OID, - NULL -}; - - -static char *ipapwd_name_list[] = { - "Password Change Extended Operation", - "Keytab Retrieval Extended Operation", - NULL -}; - -/* Init pre ops */ -static int ipapwd_pre_init(Slapi_PBlock *pb) -{ - int ret; - - ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&pdesc); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, (void *)ipapwd_pre_add); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, (void *)ipapwd_pre_mod); - - return ret; -} - -/* Init post ops */ -static int ipapwd_post_init(Slapi_PBlock *pb) -{ - int ret; - - ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&pdesc); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, (void *)ipapwd_post_op); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *)ipapwd_post_op); - - return ret; -} - -/* Initialization function */ -int ipapwd_init( Slapi_PBlock *pb ) -{ - int ret; - - /* Get the arguments appended to the plugin extendedop directive. The first argument - * (after the standard arguments for the directive) should contain the OID of the - * extended operation. */ - - ret = slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &ipapwd_plugin_id); - if ((ret != 0) || (NULL == ipapwd_plugin_id)) { - slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_init", - "Could not get identity or identity was NULL\n"); - return -1; - } - - if (ipapwd_ext_init() != 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "Object Extension Operation failed\n"); - return -1; - } - - /* Register the plug-in function as an extended operation - * plug-in function that handles the operation identified by - * OID 1.3.6.1.4.1.4203.1.11.1 . Also specify the version of the server - * plug-in */ - ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, (void *)ipapwd_start); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&pdesc); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, ipapwd_oid_list); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, ipapwd_name_list); - if (!ret) slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_FN, (void *)ipapwd_extop); - if (ret) { - slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_init", - "Failed to set plug-in version, function, and OID.\n" ); - return -1; - } - - slapi_register_plugin("preoperation", 1, - "ipapwd_pre_init", ipapwd_pre_init, - "IPA pwd pre ops", NULL, - ipapwd_plugin_id); - - slapi_register_plugin("postoperation", 1, - "ipapwd_post_init", ipapwd_post_init, - "IPA pwd post ops", NULL, - ipapwd_plugin_id); - - return 0; -} diff --git a/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/pwd-extop-conf.ldif b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/pwd-extop-conf.ldif deleted file mode 100644 index e31a8e79b..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/pwd-extop-conf.ldif +++ /dev/null @@ -1,16 +0,0 @@ -dn: cn=ipa_pwd_extop,cn=plugins,cn=config -changetype: add -objectclass: top -objectclass: nsSlapdPlugin -objectclass: extensibleObject -cn: ipa_pwd_extop -nsslapd-pluginpath: libipa_pwd_extop -nsslapd-plugininitfunc: ipapwd_init -nsslapd-plugintype: extendedop -nsslapd-pluginenabled: on -nsslapd-pluginid: ipa_pwd_extop -nsslapd-pluginversion: 1.0 -nsslapd-pluginvendor: RedHat -nsslapd-plugindescription: Support saving passwords in multiple formats for different consumers (krb5, samba, freeradius, etc.) -nsslapd-plugin-depends-on-type: database -nsslapd-realmTree: $SUFFIX diff --git a/ipa-server/ipa-slapi-plugins/ipa-winsync/Makefile.am b/ipa-server/ipa-slapi-plugins/ipa-winsync/Makefile.am deleted file mode 100644 index 94bc2dc68..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-winsync/Makefile.am +++ /dev/null @@ -1,43 +0,0 @@ -NULL = - -INCLUDES = \ - -I. \ - -I$(srcdir) \ - -DPREFIX=\""$(prefix)"\" \ - -DBINDIR=\""$(bindir)"\" \ - -DLIBDIR=\""$(libdir)"\" \ - -DLIBEXECDIR=\""$(libexecdir)"\" \ - -DDATADIR=\""$(datadir)"\" \ - $(MOZLDAP_CFLAGS) \ - $(WARN_CFLAGS) \ - $(NULL) - -plugindir = $(libdir)/dirsrv/plugins -plugin_LTLIBRARIES = \ - libipa_winsync.la \ - $(NULL) - -libipa_winsync_la_SOURCES = \ - ipa-winsync.c \ - ipa-winsync-config.c \ - $(NULL) - -libipa_winsync_la_LDFLAGS = -avoid-version - -#libipa_winsync_la_LIBADD = \ -# $(MOZLDAP_LIBS) \ -# $(NULL) - -appdir = $(IPA_DATA_DIR) -app_DATA = \ - ipa-winsync-conf.ldif \ - $(NULL) - -EXTRA_DIST = \ - README \ - $(app_DATA) \ - $(NULL) - -MAINTAINERCLEANFILES = \ - *~ \ - Makefile.in diff --git a/ipa-server/ipa-slapi-plugins/ipa-winsync/README b/ipa-server/ipa-slapi-plugins/ipa-winsync/README deleted file mode 100644 index e69de29bb..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-winsync/README +++ /dev/null diff --git a/ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync-conf.ldif b/ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync-conf.ldif deleted file mode 100644 index 5b5c56acb..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync-conf.ldif +++ /dev/null @@ -1,27 +0,0 @@ -dn: cn=ipa-winsync,cn=plugins,cn=config -changetype: add -objectclass: top -objectclass: nsSlapdPlugin -objectclass: extensibleObject -cn: ipa-winsync -nsslapd-pluginpath: libipa_winsync -nsslapd-plugininitfunc: ipa_winsync_plugin_init -nsslapd-pluginDescription: Allows IPA to work with the DS windows sync feature -nsslapd-pluginid: ipa-winsync -nsslapd-pluginversion: 1.0 -nsslapd-pluginvendor: Red Hat -nsslapd-plugintype: preoperation -nsslapd-pluginenabled: on -nsslapd-plugin-depends-on-type: database -ipaWinSyncRealmFilter: (objectclass=krbRealmContainer) -ipaWinSyncRealmAttr: cn -ipaWinSyncNewEntryFilter: (cn=ipaConfig) -ipaWinSyncNewUserOCAttr: ipauserobjectclasses -ipaWinSyncUserFlatten: true -ipaWinsyncHomeDirAttr: ipaHomesRootDir -ipaWinSyncDefaultGroupAttr: ipaDefaultPrimaryGroup -ipaWinSyncDefaultGroupFilter: (gidNumber=*)(objectclass=posixGroup)(objectclass=groupOfNames) -ipaWinSyncAcctDisable: both -ipaWinSyncInactivatedFilter: (&(cn=inactivated)(objectclass=groupOfNames)) -ipaWinSyncActivatedFilter: (&(cn=activated)(objectclass=groupOfNames)) -ipaWinSyncForceSync: true diff --git a/ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync-config.c b/ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync-config.c deleted file mode 100644 index 45efa6df0..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync-config.c +++ /dev/null @@ -1,975 +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; version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., 59 Temple - * Place, Suite 330, Boston, MA 02111-1307 USA. - * - * In addition, as a special exception, Red Hat, Inc. gives You the additional - * right to link the code of this Program with code not covered under the GNU - * General Public License ("Non-GPL Code") and to distribute linked combinations - * including the two, subject to the limitations in this paragraph. Non-GPL Code - * permitted under this exception must only link 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 GNU General Public License. Only Red Hat, Inc. may make changes or - * additions to the list of Approved Interfaces. You must obey the GNU General - * Public License in all respects for all of the Program code and other code - * used in conjunction with the Program except the Non-GPL Code covered by this - * exception. If you modify this file, you may extend this exception to your - * version of the file, but you are not obligated to do so. If you do not wish - * to provide this exception without modification, you must delete this - * exception statement from your version and license this file solely under the - * GPL without exception. - * - * Authors: - * Rich Megginson <rmeggins@redhat.com> - * - * Copyright (C) 2008 Red Hat, Inc. - * All rights reserved. - * END COPYRIGHT BLOCK **/ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -/* - * Windows Synchronization Plug-in for IPA - * This plugin allows IPA to intercept operations sent from - * Windows to the directory server and vice versa. This allows - * IPA to intercept new users added to Windows and synced to the - * directory server, and allows IPA to modify the entry, adding - * objectclasses and attributes, and changing the DN. - */ - -#ifdef WINSYNC_TEST_IPA -#include <slapi-plugin.h> -#include "winsync-plugin.h" -#else -#include <dirsrv/slapi-plugin.h> -#include <dirsrv/winsync-plugin.h> -#endif -#include "ipa-winsync.h" - -#include <string.h> - -#define IPA_WINSYNC_CONFIG_FILTER "(objectclass=*)" - -/* - * function prototypes - */ -static int ipa_winsync_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, - int *returncode, char *returntext, void *arg); -static int ipa_winsync_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, - int *returncode, char *returntext, void *arg); -static int ipa_winsync_search (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, - int *returncode, char *returntext, void *arg) -{ - return SLAPI_DSE_CALLBACK_OK; -} - -/* - * static variables - */ -/* for now, there is only one configuration and it is global to the plugin */ -static IPA_WinSync_Config theConfig; -static int inited = 0; - -static int dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, - int *returncode, char *returntext, void *arg) -{ - *returncode = LDAP_UNWILLING_TO_PERFORM; - return SLAPI_DSE_CALLBACK_ERROR; -} - -IPA_WinSync_Config * -ipa_winsync_get_config() -{ - return &theConfig; -} - -/* - * Read configuration and create a configuration data structure. - * This is called after the server has configured itself so we can check - * schema and whatnot. - * Returns an LDAP error code (LDAP_SUCCESS if all goes well). - */ -int -ipa_winsync_config(Slapi_Entry *config_e) -{ - int returncode = LDAP_SUCCESS; - char returntext[SLAPI_DSE_RETURNTEXT_SIZE]; - - if ( inited ) { - slapi_log_error( SLAPI_LOG_FATAL, IPA_WINSYNC_PLUGIN_NAME, - "Error: IPA WinSync plug-in already configured. " - "Please remove the plugin config entry [%s]\n", - slapi_entry_get_dn_const(config_e)); - return( LDAP_PARAM_ERROR ); - } - - /* initialize fields */ - if ((theConfig.lock = slapi_new_mutex()) == NULL) { - return( LDAP_LOCAL_ERROR ); - } - - /* init defaults */ - theConfig.config_e = slapi_entry_alloc(); - slapi_entry_init(theConfig.config_e, slapi_ch_strdup(""), NULL); - theConfig.flatten = PR_TRUE; - - if (SLAPI_DSE_CALLBACK_OK == ipa_winsync_validate_config(NULL, NULL, config_e, - &returncode, returntext, NULL)) { - ipa_winsync_apply_config(NULL, NULL, config_e, - &returncode, returntext, NULL); - } - - /* config DSE must be initialized before we get here */ - if (returncode == LDAP_SUCCESS) { - const char *config_dn = slapi_entry_get_dn_const(config_e); - slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE, - IPA_WINSYNC_CONFIG_FILTER, ipa_winsync_validate_config,NULL); - slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, config_dn, LDAP_SCOPE_BASE, - IPA_WINSYNC_CONFIG_FILTER, ipa_winsync_apply_config,NULL); - slapi_config_register_callback(SLAPI_OPERATION_MODRDN, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE, - IPA_WINSYNC_CONFIG_FILTER, dont_allow_that, NULL); - slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE, - IPA_WINSYNC_CONFIG_FILTER, dont_allow_that, NULL); - slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE, - IPA_WINSYNC_CONFIG_FILTER, ipa_winsync_search,NULL); - } - - inited = 1; - - if (returncode != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_FATAL, IPA_WINSYNC_PLUGIN_NAME, - "Error %d: %s\n", returncode, returntext); - } - - return returncode; -} - -static int -parse_acct_disable(const char *theval) -{ - int retval = ACCT_DISABLE_INVALID; - if (!theval || !*theval) { - return retval; - } - if (!PL_strcasecmp(theval, IPA_WINSYNC_ACCT_DISABLE_NONE)) { - retval = ACCT_DISABLE_NONE; - } else if (!PL_strcasecmp(theval, IPA_WINSYNC_ACCT_DISABLE_TO_AD)) { - retval = ACCT_DISABLE_TO_AD; - } else if (!PL_strcasecmp(theval, IPA_WINSYNC_ACCT_DISABLE_TO_DS)) { - retval = ACCT_DISABLE_TO_DS; - } else if (!PL_strcasecmp(theval, IPA_WINSYNC_ACCT_DISABLE_BOTH)) { - retval = ACCT_DISABLE_BOTH; - } - - return retval; -} - -/* - Validate the pending changes in the e entry. -*/ -static int -ipa_winsync_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, - int *returncode, char *returntext, void *arg) -{ - char **attrsvals = NULL; - int ii; - Slapi_Attr *testattr = NULL; - char *strattr = NULL; - int acct_disable; - - *returncode = LDAP_UNWILLING_TO_PERFORM; /* be pessimistic */ - - /* get realm filter */ - if (slapi_entry_attr_find(e, IPA_WINSYNC_REALM_FILTER_ATTR, &testattr) || - (NULL == testattr)) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_REALM_FILTER_ATTR); - goto done2; - } - - /* get realm attr */ - if (slapi_entry_attr_find(e, IPA_WINSYNC_REALM_ATTR_ATTR, &testattr) || - (NULL == testattr)) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_REALM_ATTR_ATTR); - goto done2; - } - - /* get new_entry_filter */ - if (slapi_entry_attr_find(e, IPA_WINSYNC_NEW_ENTRY_FILTER_ATTR, - &testattr) || - (NULL == testattr)) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_NEW_ENTRY_FILTER_ATTR); - goto done2; - } - - /* get new_user_oc_attr */ - if (slapi_entry_attr_find(e, IPA_WINSYNC_NEW_USER_OC_ATTR, - &testattr) || - (NULL == testattr)) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_NEW_USER_OC_ATTR); - goto done2; - } - - /* get homedir_prefix_attr */ - if (slapi_entry_attr_find(e, IPA_WINSYNC_HOMEDIR_PREFIX_ATTR, - &testattr) || - (NULL == testattr)) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_HOMEDIR_PREFIX_ATTR); - goto done2; - } - - /* get default_group_attr */ - if (slapi_entry_attr_find(e, IPA_WINSYNC_DEFAULTGROUP_ATTR, - &testattr) || - (NULL == testattr)) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_DEFAULTGROUP_ATTR); - goto done2; - } - - /* get default_group_filter */ - if (slapi_entry_attr_find(e, IPA_WINSYNC_DEFAULTGROUP_FILTER_ATTR, - &testattr) || - (NULL == testattr)) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_DEFAULTGROUP_FILTER_ATTR); - goto done2; - } - - /* get the list of attributes & values */ - /* get new_user_oc_attr */ - if (!(attrsvals = slapi_entry_attr_get_charray( - e, IPA_WINSYNC_NEW_USER_ATTRS_VALS))) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPA_WINSYNC_PLUGIN_NAME, - "Info: no default attributes and values given in [%s]\n", - IPA_WINSYNC_NEW_USER_ATTRS_VALS); - } - - /* format of *attrsvals is "attrname value" */ - /* attrname <space> value */ - /* value may contain spaces - attrname is everything up to the first - space - value is everything after the first space */ - for (ii = 0; attrsvals && attrsvals[ii]; ++ii) { - Slapi_Attr *attr = NULL; - char *oidp = NULL; - char *val = strchr(attrsvals[ii], ' '); - if (!val || !*(val+1)) { /* incorrect format or no value */ - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value or incorrect value given for [%s] " - "value [%s] index [%d] - correct format is attrname SPACE value", - IPA_WINSYNC_NEW_USER_ATTRS_VALS, - attrsvals[ii], ii); - goto done2; - } - *val = '\0'; /* separate attr from val */ - /* check to make sure attribute is in the schema */ - attr = slapi_attr_new(); - slapi_attr_set_type(attr, attrsvals[ii]); - slapi_attr_get_oid_copy(attr, &oidp); - slapi_attr_free(&attr); - if (oidp == NULL) { /* no such attribute */ - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: invalid attribute name [%s] given for [%s] " - "at index [%d] - attribute is not in server schema", - attrsvals[ii], IPA_WINSYNC_NEW_USER_ATTRS_VALS, - ii); - goto done2; - } - - /* attribute is valid - continue */ - slapi_ch_free_string(&oidp); - } - - /* get account disable sync direction */ - if (!(strattr = slapi_entry_attr_get_charptr( - e, IPA_WINSYNC_ACCT_DISABLE))) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_ACCT_DISABLE); - goto done2; - } - - acct_disable = parse_acct_disable(strattr); - if (ACCT_DISABLE_INVALID == acct_disable) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: invalid value [%s] given for [%s] - valid " - "values are " IPA_WINSYNC_ACCT_DISABLE_NONE - ", " IPA_WINSYNC_ACCT_DISABLE_TO_AD - ", " IPA_WINSYNC_ACCT_DISABLE_TO_DS - ", or " IPA_WINSYNC_ACCT_DISABLE_BOTH, - strattr, IPA_WINSYNC_ACCT_DISABLE); - goto done2; - } - - /* if using acct disable sync, must have the attributes - IPA_WINSYNC_INACTIVATED_FILTER and IPA_WINSYNC_ACTIVATED_FILTER - */ - if (acct_disable != ACCT_DISABLE_NONE) { - if (slapi_entry_attr_find(e, IPA_WINSYNC_INACTIVATED_FILTER, - &testattr) || - (NULL == testattr)) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s - " - "required for account disable sync", - IPA_WINSYNC_INACTIVATED_FILTER); - goto done2; - } - if (slapi_entry_attr_find(e, IPA_WINSYNC_ACTIVATED_FILTER, - &testattr) || - (NULL == testattr)) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s - " - "required for account disable sync", - IPA_WINSYNC_ACTIVATED_FILTER); - goto done2; - } - } - - /* success */ - *returncode = LDAP_SUCCESS; - -done2: - slapi_ch_free_string(&strattr); - slapi_ch_array_free(attrsvals); - attrsvals = NULL; - - if (*returncode != LDAP_SUCCESS) { - return SLAPI_DSE_CALLBACK_ERROR; - } else { - return SLAPI_DSE_CALLBACK_OK; - } -} - -static int -ipa_winsync_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, - Slapi_Entry* e, int *returncode, char *returntext, - void *arg) -{ - PRBool flatten = PR_TRUE; - char *realm_filter = NULL; - char *realm_attr = NULL; - char *new_entry_filter = NULL; - char *new_user_oc_attr = NULL; /* don't care about groups for now */ - char *homedir_prefix_attr = NULL; - char *default_group_attr = NULL; - char *default_group_filter = NULL; - char *acct_disable = NULL; - int acct_disable_int; - char *inactivated_filter = NULL; - char *activated_filter = NULL; - char **attrsvals = NULL; - int ii; - Slapi_Attr *testattr = NULL; - PRBool forceSync = PR_FALSE; - - *returncode = LDAP_UNWILLING_TO_PERFORM; /* be pessimistic */ - - /* get flatten value */ - if (!slapi_entry_attr_find(e, IPA_WINSYNC_USER_FLATTEN, &testattr) && - (NULL != testattr)) { - flatten = slapi_entry_attr_get_bool(e, IPA_WINSYNC_USER_FLATTEN); - } - - /* get realm filter */ - if (!(realm_filter = slapi_entry_attr_get_charptr( - e, IPA_WINSYNC_REALM_FILTER_ATTR))) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_REALM_FILTER_ATTR); - goto done3; - } - - /* get realm attr */ - if (!(realm_attr = slapi_entry_attr_get_charptr( - e, IPA_WINSYNC_REALM_ATTR_ATTR))) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_REALM_ATTR_ATTR); - goto done3; - } - - /* get new_entry_filter */ - if (!(new_entry_filter = slapi_entry_attr_get_charptr( - e, IPA_WINSYNC_NEW_ENTRY_FILTER_ATTR))) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_NEW_ENTRY_FILTER_ATTR); - goto done3; - } - - /* get new_user_oc_attr */ - if (!(new_user_oc_attr = slapi_entry_attr_get_charptr( - e, IPA_WINSYNC_NEW_USER_OC_ATTR))) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_NEW_USER_OC_ATTR); - goto done3; - } - - /* get homedir_prefix_attr */ - if (!(homedir_prefix_attr = slapi_entry_attr_get_charptr( - e, IPA_WINSYNC_HOMEDIR_PREFIX_ATTR))) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_HOMEDIR_PREFIX_ATTR); - goto done3; - } - - /* get default_group_attr */ - if (!(default_group_attr = slapi_entry_attr_get_charptr( - e, IPA_WINSYNC_DEFAULTGROUP_ATTR))) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_DEFAULTGROUP_ATTR); - goto done3; - } - - /* get default_group_filter */ - if (!(default_group_filter = slapi_entry_attr_get_charptr( - e, IPA_WINSYNC_DEFAULTGROUP_FILTER_ATTR))) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_DEFAULTGROUP_FILTER_ATTR); - goto done3; - } - - /* get the list of attributes & values */ - /* get new_user_oc_attr */ - if (!(attrsvals = slapi_entry_attr_get_charray( - e, IPA_WINSYNC_NEW_USER_ATTRS_VALS))) { - slapi_log_error(SLAPI_LOG_PLUGIN, IPA_WINSYNC_PLUGIN_NAME, - "Info: no default attributes and values given in [%s]\n", - IPA_WINSYNC_NEW_USER_ATTRS_VALS); - } - - /* get acct disable sync value */ - if (!(acct_disable = slapi_entry_attr_get_charptr( - e, IPA_WINSYNC_ACCT_DISABLE))) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s", - IPA_WINSYNC_ACCT_DISABLE); - goto done3; - } - - acct_disable_int = parse_acct_disable(acct_disable); - if (ACCT_DISABLE_INVALID == acct_disable_int) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: invalid value [%s] given for [%s] - valid " - "values are " IPA_WINSYNC_ACCT_DISABLE_NONE - ", " IPA_WINSYNC_ACCT_DISABLE_TO_AD - ", " IPA_WINSYNC_ACCT_DISABLE_TO_DS - ", or " IPA_WINSYNC_ACCT_DISABLE_BOTH, - acct_disable, IPA_WINSYNC_ACCT_DISABLE); - goto done3; - } - - if (acct_disable_int != ACCT_DISABLE_NONE) { - /* get inactivated group filter */ - if (!(inactivated_filter = slapi_entry_attr_get_charptr( - e, IPA_WINSYNC_INACTIVATED_FILTER))) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s - required for account disable sync", - IPA_WINSYNC_INACTIVATED_FILTER); - goto done3; - } - /* get activated group filter */ - if (!(activated_filter = slapi_entry_attr_get_charptr( - e, IPA_WINSYNC_ACTIVATED_FILTER))) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value given for %s - required for account disable sync", - IPA_WINSYNC_ACTIVATED_FILTER); - goto done3; - } - } - - /* get forceSync value */ - if (!slapi_entry_attr_find(e, IPA_WINSYNC_FORCE_SYNC, &testattr) && - (NULL != testattr)) { - forceSync = slapi_entry_attr_get_bool(e, IPA_WINSYNC_FORCE_SYNC); - } - - /* if we got here, we have valid values for everything - set the config entry */ - slapi_lock_mutex(theConfig.lock); - slapi_entry_free(theConfig.config_e); - theConfig.config_e = slapi_entry_alloc(); - slapi_entry_init(theConfig.config_e, slapi_ch_strdup(""), NULL); - - /* format of *attrsvals is "attrname value" */ - /* attrname <space> value */ - /* value may contain spaces - attrname is everything up to the first - space - value is everything after the first space */ - for (ii = 0; attrsvals && attrsvals[ii]; ++ii) { - int rc; - Slapi_Value *sva[2]; - Slapi_Value *sv = NULL; - char *val = strchr(attrsvals[ii], ' '); - if (!val || !*(val+1)) { /* incorrect format or no value */ - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: no value or incorrect value given for [%s] " - "value [%s] index [%d] - correct format is attrname SPACE value", - IPA_WINSYNC_NEW_USER_ATTRS_VALS, - attrsvals[ii], ii); - goto done3; - } - *val++ = '\0'; /* separate attr from val */ - sv = slapi_value_new_string(val); - sva[0] = sv; - sva[1] = NULL; - if ((rc = slapi_entry_add_values_sv(theConfig.config_e, - attrsvals[ii], sva)) && - (rc != LDAP_SUCCESS)) { - PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, - "Error: could not add value [%s] for attribute name " - "[%s] - ldap error [%d: %s]", val, attrsvals[ii], - attrsvals[ii], IPA_WINSYNC_NEW_USER_ATTRS_VALS, - rc, ldap_err2string(rc)); - slapi_entry_free(theConfig.config_e); - theConfig.config_e = NULL; - slapi_value_free(&sv); - goto done3; - } - slapi_value_free(&sv); - } - - /* all of the attrs and vals have been set - set the other values */ - slapi_ch_free_string(&theConfig.realm_filter); - theConfig.realm_filter = realm_filter; - realm_filter = NULL; - slapi_ch_free_string(&theConfig.realm_attr); - theConfig.realm_attr = realm_attr; - realm_attr = NULL; - slapi_ch_free_string(&theConfig.new_entry_filter); - theConfig.new_entry_filter = new_entry_filter; - new_entry_filter = NULL; - slapi_ch_free_string(&theConfig.new_user_oc_attr); - theConfig.new_user_oc_attr = new_user_oc_attr; - new_user_oc_attr = NULL; - slapi_ch_free_string(&theConfig.homedir_prefix_attr); - theConfig.homedir_prefix_attr = homedir_prefix_attr; - homedir_prefix_attr = NULL; - slapi_ch_free_string(&theConfig.default_group_attr); - theConfig.default_group_attr = default_group_attr; - default_group_attr = NULL; - slapi_ch_free_string(&theConfig.default_group_filter); - theConfig.default_group_filter = default_group_filter; - default_group_filter = NULL; - theConfig.flatten = flatten; - theConfig.acct_disable = parse_acct_disable(acct_disable); - slapi_ch_free_string(&theConfig.inactivated_filter); - theConfig.inactivated_filter = inactivated_filter; - inactivated_filter = NULL; - slapi_ch_free_string(&theConfig.activated_filter); - theConfig.activated_filter = activated_filter; - activated_filter = NULL; - theConfig.forceSync = forceSync; - - /* success */ - *returncode = LDAP_SUCCESS; - -done3: - slapi_unlock_mutex(theConfig.lock); - - slapi_ch_free_string(&realm_filter); - slapi_ch_free_string(&realm_attr); - slapi_ch_free_string(&new_entry_filter); - slapi_ch_free_string(&new_user_oc_attr); - slapi_ch_free_string(&homedir_prefix_attr); - slapi_ch_free_string(&default_group_attr); - slapi_ch_free_string(&default_group_filter); - slapi_ch_array_free(attrsvals); - attrsvals = NULL; - slapi_ch_free_string(&acct_disable); - slapi_ch_free_string(&inactivated_filter); - slapi_ch_free_string(&activated_filter); - - if (*returncode != LDAP_SUCCESS) { - return SLAPI_DSE_CALLBACK_ERROR; - } else { - return SLAPI_DSE_CALLBACK_OK; - } -} - -/* create per-domain config object */ -void * -ipa_winsync_config_new_domain( - const Slapi_DN *ds_subtree, - const Slapi_DN *ad_subtree -) -{ - IPA_WinSync_Domain_Config *iwdc = - (IPA_WinSync_Domain_Config *) - slapi_ch_calloc(1, sizeof(IPA_WinSync_Domain_Config)); - - return (void *)iwdc; -} - -/* destroy per-domain config object */ -void -ipa_winsync_config_destroy_domain( - void *cbdata, const Slapi_DN *ds_subtree, - const Slapi_DN *ad_subtree -) -{ - IPA_WinSync_Domain_Config *iwdc = - (IPA_WinSync_Domain_Config *)cbdata; - slapi_entry_free(iwdc->domain_e); - iwdc->domain_e = NULL; - slapi_ch_free_string(&iwdc->realm_name); - slapi_ch_free_string(&iwdc->homedir_prefix); - slapi_ch_free_string(&iwdc->inactivated_group_dn); - slapi_ch_free_string(&iwdc->activated_group_dn); - slapi_ch_free((void **)&iwdc); - - return; -} - -/* - return the value(s) of the given attribute in the entry that - matches the given criteria. The criteria must match one - and only one entry. - Returns: - -1 - problem doing internal search - LDAP_UNWILLING_TO_PERFORM - more than one matching entry - LDAP_NO_SUCH_OBJECT - no entry found that matched - 0 and attrval == NULL - entry found but no attribute - other ldap error - error doing search for given basedn -*/ -static int -internal_find_entry_get_attr_val(const Slapi_DN *basedn, int scope, - const char *filter, const char *attrname, - Slapi_ValueSet **svs, char **attrval) -{ - Slapi_Entry **entries = NULL; - Slapi_PBlock *pb = NULL; - const char *search_basedn = slapi_sdn_get_dn(basedn); - int search_scope = scope; - int ret = LDAP_SUCCESS; - const char *attrs[2] = {attrname, NULL}; - - if (svs) { - *svs = NULL; - } - if (attrval) { - *attrval = NULL; - } - pb = slapi_pblock_new(); - slapi_search_internal_set_pb(pb, search_basedn, search_scope, filter, - (char **)attrs, 0, NULL, NULL, - ipa_winsync_get_plugin_identity(), 0); - slapi_search_internal_pb(pb); - - /* This search may return no entries, but should never - return an error - */ - slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret); - if (ret != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_FATAL, IPA_WINSYNC_PLUGIN_NAME, - "Error [%d:%s] searching for base [%s] filter [%s]" - " attr [%s]\n", ret, ldap_err2string(ret), - search_basedn, filter, attrs[0]); - goto out1; - } - - slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); - if (entries && entries[0] && entries[1]) { - /* error - should never be more than one matching entry */ - slapi_log_error(SLAPI_LOG_FATAL, IPA_WINSYNC_PLUGIN_NAME, - "Error: more than one entry matches search for " - "base [%s] filter [%s] attr [%s]\n", - search_basedn, filter, attrs[0]); - ret = LDAP_UNWILLING_TO_PERFORM; - goto out1; - } - - if (entries && entries[0]) { /* found one */ - if (svs) { - Slapi_Attr *attr = NULL; - slapi_entry_attr_find(entries[0], attrname, &attr); - if (attr) { - /* slapi_attr_get_valueset allocates svs - must be freed later */ - slapi_attr_get_valueset(attr, svs); - } - } - if (attrval) { - if (!strcmp(attrname, "dn")) { /* special - to just get the DN */ - *attrval = slapi_ch_strdup(slapi_entry_get_dn_const(entries[0])); - } else { - *attrval = slapi_entry_attr_get_charptr(entries[0], attrname); - } - } - } else { - ret = LDAP_NO_SUCH_OBJECT; - slapi_log_error(SLAPI_LOG_PLUGIN, IPA_WINSYNC_PLUGIN_NAME, - "Did not find an entry for search " - "base [%s] filter [%s] attr [%s]\n", - search_basedn, filter, attrs[0]); - } - -out1: - if (pb) { - slapi_free_search_results_internal(pb); - slapi_pblock_destroy(pb); - pb = NULL; - } - - return ret; -} - -/* - * Perform the agreement/domain specific configuration. - * IPA stores its configuration in the tree. We use the - * ds_subtree to search for the domain/realm specific - * configuration entries. - */ -void -ipa_winsync_config_refresh_domain( - void *cbdata, const Slapi_DN *ds_subtree, - const Slapi_DN *ad_subtree -) -{ - IPA_WinSync_Domain_Config *iwdc = - (IPA_WinSync_Domain_Config *)cbdata; - Slapi_DN *config_dn = slapi_sdn_dup(ds_subtree); - char *realm_filter = NULL; - char *realm_attr = NULL; - char *new_entry_filter = NULL; - char *new_user_oc_attr = NULL; /* don't care about groups for now */ - char *homedir_prefix_attr = NULL; - char *default_group_attr = NULL; - char *default_group_filter = NULL; - char *default_group_name = NULL; - char *real_group_filter = NULL; - char *default_gid = NULL; - Slapi_ValueSet *new_user_objclasses = NULL; /* don't care about groups for now */ - int loopdone = 0; - int search_scope = LDAP_SCOPE_SUBTREE; - int ret = LDAP_SUCCESS; - Slapi_Value *sv = NULL; - int acct_disable; - char *inactivated_filter = NULL; - char *activated_filter = NULL; - char *inactivated_group_dn = NULL; - char *activated_group_dn = NULL; - - slapi_lock_mutex(theConfig.lock); - realm_filter = slapi_ch_strdup(theConfig.realm_filter); - realm_attr = slapi_ch_strdup(theConfig.realm_attr); - new_entry_filter = slapi_ch_strdup(theConfig.new_entry_filter); - new_user_oc_attr = slapi_ch_strdup(theConfig.new_user_oc_attr); - homedir_prefix_attr = slapi_ch_strdup(theConfig.homedir_prefix_attr); - default_group_attr = slapi_ch_strdup(theConfig.default_group_attr); - default_group_filter = slapi_ch_strdup(theConfig.default_group_filter); - acct_disable = theConfig.acct_disable; - if (acct_disable != ACCT_DISABLE_NONE) { - inactivated_filter = slapi_ch_strdup(theConfig.inactivated_filter); - activated_filter = slapi_ch_strdup(theConfig.activated_filter); - } - slapi_unlock_mutex(theConfig.lock); - - /* starting at ds_subtree, search for the entry - containing the Kerberos realm to use */ - slapi_ch_free_string(&iwdc->realm_name); - while(!loopdone && !slapi_sdn_isempty(config_dn)) { - ret = internal_find_entry_get_attr_val(config_dn, search_scope, - realm_filter, realm_attr, - NULL, &iwdc->realm_name); - - if ((0 == ret) && iwdc->realm_name) { - loopdone = 1; - } else if ((LDAP_NO_SUCH_OBJECT == ret) && !iwdc->realm_name) { - /* try again */ - Slapi_DN *parent_dn = slapi_sdn_new(); - slapi_sdn_get_parent(config_dn, parent_dn); - slapi_sdn_free(&config_dn); - config_dn = parent_dn; - } else { /* error */ - goto out; - } - } - - if (!iwdc->realm_name) { - /* error - could not find the IPA config entry with the realm name */ - slapi_log_error(SLAPI_LOG_FATAL, IPA_WINSYNC_PLUGIN_NAME, - "Error: could not find the entry containing the realm name for " - "ds subtree [%s] filter [%s] attr [%s]\n", - slapi_sdn_get_dn(ds_subtree), realm_filter, realm_attr); - goto out; - } - - /* look for the entry containing the default objectclasses - to add to new entries */ - ret = internal_find_entry_get_attr_val(config_dn, search_scope, - new_entry_filter, new_user_oc_attr, - &new_user_objclasses, NULL); - if (!new_user_objclasses) { - /* error - could not find the entry containing list of objectclasses */ - slapi_log_error(SLAPI_LOG_FATAL, IPA_WINSYNC_PLUGIN_NAME, - "Error: could not find the entry containing the new user objectclass list for " - "ds subtree [%s] filter [%s] attr [%s]\n", - slapi_sdn_get_dn(ds_subtree), new_entry_filter, new_user_oc_attr); - goto out; - } - - /* get the home directory prefix value */ - /* note - this is in the same entry as the new entry template, so - use the same filter */ - slapi_ch_free_string(&iwdc->homedir_prefix); - ret = internal_find_entry_get_attr_val(config_dn, search_scope, - new_entry_filter, homedir_prefix_attr, - NULL, &iwdc->homedir_prefix); - if (!iwdc->homedir_prefix) { - /* error - could not find the home dir prefix */ - slapi_log_error(SLAPI_LOG_FATAL, IPA_WINSYNC_PLUGIN_NAME, - "Error: could not find the entry containing the home directory prefix for " - "ds subtree [%s] filter [%s] attr [%s]\n", - slapi_sdn_get_dn(ds_subtree), new_entry_filter, homedir_prefix_attr); - goto out; - } - - /* find the default group - the entry above contains the group name, but - we need the gidNumber for posixAccount - so first find the entry - and attr value which has the group name, then lookup the group - number from the group name */ - ret = internal_find_entry_get_attr_val(config_dn, search_scope, - new_entry_filter, default_group_attr, - NULL, &default_group_name); - if (!default_group_name) { - /* error - could not find the default group name */ - slapi_log_error(SLAPI_LOG_FATAL, IPA_WINSYNC_PLUGIN_NAME, - "Error: could not find the entry containing the default group name for " - "ds subtree [%s] filter [%s] attr [%s]\n", - slapi_sdn_get_dn(ds_subtree), new_entry_filter, default_group_attr); - goto out; - } - - /* next, find the group whose name is default_group_name - construct the filter - based on the filter attribute value - assumes the group name is stored - in the cn attribute value, and the gidNumber in the gidNumber attribute value */ - real_group_filter = slapi_ch_smprintf("(&(cn=%s)%s)", default_group_name, - default_group_filter); - ret = internal_find_entry_get_attr_val(config_dn, search_scope, - real_group_filter, "gidNumber", - NULL, &default_gid); - if (!default_gid) { - /* error - could not find the default gidNumber */ - slapi_log_error(SLAPI_LOG_FATAL, IPA_WINSYNC_PLUGIN_NAME, - "Error: could not find the entry containing the default gidNumber " - "ds subtree [%s] filter [%s] attr [%s]\n", - slapi_sdn_get_dn(ds_subtree), new_entry_filter, "gidNumber"); - goto out; - } - - /* If we are syncing account disable, we need to find the groups used - to denote active and inactive users e.g. - dn: cn=inactivated,cn=account inactivation,cn=accounts,$SUFFIX - - dn: cn=Activated,cn=Account Inactivation,cn=accounts,$SUFFIX - - */ - if (acct_disable != ACCT_DISABLE_NONE) { - ret = internal_find_entry_get_attr_val(config_dn, search_scope, - inactivated_filter, "dn", - NULL, &inactivated_group_dn); - if (!inactivated_group_dn) { - /* error - could not find the inactivated group dn */ - slapi_log_error(SLAPI_LOG_FATAL, IPA_WINSYNC_PLUGIN_NAME, - "Error: could not find the DN of the inactivated users group " - "ds subtree [%s] filter [%s]\n", - slapi_sdn_get_dn(ds_subtree), inactivated_filter); - goto out; - } - ret = internal_find_entry_get_attr_val(config_dn, search_scope, - activated_filter, "dn", - NULL, &activated_group_dn); - if (!activated_group_dn) { - /* error - could not find the activated group dn */ - slapi_log_error(SLAPI_LOG_FATAL, IPA_WINSYNC_PLUGIN_NAME, - "Error: could not find the DN of the activated users group " - "ds subtree [%s] filter [%s]\n", - slapi_sdn_get_dn(ds_subtree), activated_filter); - goto out; - } - } - - /* ok, we have our values */ - /* first, clear out the old domain config */ - slapi_entry_free(iwdc->domain_e); - iwdc->domain_e = NULL; - - /* next, copy the global attr config */ - slapi_lock_mutex(theConfig.lock); - iwdc->domain_e = slapi_entry_dup(theConfig.config_e); - slapi_unlock_mutex(theConfig.lock); - - /* set the objectclasses in the domain_e */ - slapi_entry_attr_delete(iwdc->domain_e, "objectclass"); - /* this copies new_user_objclasses */ - slapi_entry_add_valueset(iwdc->domain_e, "objectclass", new_user_objclasses); - - /* set the default gid number */ - sv = slapi_value_new_string_passin(default_gid); - default_gid = NULL; /* passin owns the memory */ - if (!slapi_entry_attr_has_syntax_value(iwdc->domain_e, "gidNumber", sv)) { - slapi_entry_add_value(iwdc->domain_e, "gidNumber", sv); - } - slapi_value_free(&sv); - - slapi_ch_free_string(&iwdc->inactivated_group_dn); - iwdc->inactivated_group_dn = inactivated_group_dn; - inactivated_group_dn = NULL; - slapi_ch_free_string(&iwdc->activated_group_dn); - iwdc->activated_group_dn = activated_group_dn; - activated_group_dn = NULL; - -out: - slapi_valueset_free(new_user_objclasses); - slapi_sdn_free(&config_dn); - slapi_ch_free_string(&realm_filter); - slapi_ch_free_string(&realm_attr); - slapi_ch_free_string(&new_entry_filter); - slapi_ch_free_string(&new_user_oc_attr); - slapi_ch_free_string(&homedir_prefix_attr); - slapi_ch_free_string(&default_group_attr); - slapi_ch_free_string(&default_group_filter); - slapi_ch_free_string(&default_group_name); - slapi_ch_free_string(&real_group_filter); - slapi_ch_free_string(&default_gid); - slapi_ch_free_string(&inactivated_filter); - slapi_ch_free_string(&inactivated_group_dn); - slapi_ch_free_string(&activated_filter); - slapi_ch_free_string(&activated_group_dn); - - if (LDAP_SUCCESS != ret) { - slapi_ch_free_string(&iwdc->realm_name); - slapi_ch_free_string(&iwdc->homedir_prefix); - slapi_entry_free(iwdc->domain_e); - iwdc->domain_e = NULL; - } - - return; -} diff --git a/ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync.c b/ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync.c deleted file mode 100644 index 9ee8805bb..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync.c +++ /dev/null @@ -1,1177 +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; version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., 59 Temple - * Place, Suite 330, Boston, MA 02111-1307 USA. - * - * In addition, as a special exception, Red Hat, Inc. gives You the additional - * right to link the code of this Program with code not covered under the GNU - * General Public License ("Non-GPL Code") and to distribute linked combinations - * including the two, subject to the limitations in this paragraph. Non-GPL Code - * permitted under this exception must only link 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 GNU General Public License. Only Red Hat, Inc. may make changes or - * additions to the list of Approved Interfaces. You must obey the GNU General - * Public License in all respects for all of the Program code and other code - * used in conjunction with the Program except the Non-GPL Code covered by this - * exception. If you modify this file, you may extend this exception to your - * version of the file, but you are not obligated to do so. If you do not wish - * to provide this exception without modification, you must delete this - * exception statement from your version and license this file solely under the - * GPL without exception. - * - * Authors: - * Rich Megginson <rmeggins@redhat.com> - * - * Copyright (C) 2008 Red Hat, Inc. - * All rights reserved. - * END COPYRIGHT BLOCK **/ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -/* - * Windows Synchronization Plug-in for IPA - * This plugin allows IPA to intercept operations sent from - * Windows to the directory server and vice versa. This allows - * IPA to intercept new users added to Windows and synced to the - * directory server, and allows IPA to modify the entry, adding - * objectclasses and attributes, and changing the DN. - */ - -#ifdef WINSYNC_TEST_IPA -#include <slapi-plugin.h> -#include "winsync-plugin.h" -#else -#include <dirsrv/slapi-plugin.h> -#include <dirsrv/winsync-plugin.h> -#endif -#include "ipa-winsync.h" - -static char *ipa_winsync_plugin_name = IPA_WINSYNC_PLUGIN_NAME; - -static void -sync_acct_disable( - void *cbdata, /* the usual domain config data */ - const Slapi_Entry *ad_entry, /* the AD entry */ - Slapi_Entry *ds_entry, /* the DS entry */ - int direction, /* the direction - TO_AD or TO_DS */ - Slapi_Entry *update_entry, /* the entry to update for ADDs */ - Slapi_Mods *smods, /* the mod list for MODIFYs */ - int *do_modify /* set to true if mods were applied */ -); - -static void -do_force_sync( - const Slapi_Entry *ad_entry, /* the AD entry */ - Slapi_Entry *ds_entry, /* the DS entry */ - Slapi_Mods *smods, /* the mod list */ - int *do_modify /* set to true if mods were applied */ -); - -/* This is called when a new agreement is created or loaded - at startup. -*/ -static void * -ipa_winsync_agmt_init(const Slapi_DN *ds_subtree, const Slapi_DN *ad_subtree) -{ - void *cbdata = NULL; - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_agmt_init [%s] [%s] -- begin\n", - slapi_sdn_get_dn(ds_subtree), - slapi_sdn_get_dn(ad_subtree)); - - /* do the domain specific configuration based on the ds subtree */ - cbdata = ipa_winsync_config_new_domain(ds_subtree, ad_subtree); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_agmt_init -- end\n"); - - return cbdata; -} - -static void -ipa_winsync_dirsync_search_params_cb(void *cbdata, const char *agmt_dn, - char **base, int *scope, char **filter, - char ***attrs, LDAPControl ***serverctrls) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_dirsync_search_params_cb -- begin\n"); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_dirsync_search_params_cb -- end\n"); - - return; -} - -/* called before searching for a single entry from AD - agmt_dn will be NULL */ -static void -ipa_winsync_pre_ad_search_cb(void *cbdata, const char *agmt_dn, - char **base, int *scope, char **filter, - char ***attrs, LDAPControl ***serverctrls) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_pre_ad_search_cb -- begin\n"); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_pre_ad_search_cb -- end\n"); - - return; -} - -/* called before an internal search to get a single DS entry - agmt_dn will be NULL */ -static void -ipa_winsync_pre_ds_search_entry_cb(void *cbdata, const char *agmt_dn, - char **base, int *scope, char **filter, - char ***attrs, LDAPControl ***serverctrls) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_pre_ds_search_cb -- begin\n"); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "-- ipa_winsync_pre_ds_search_cb - base [%s] " - "scope [%d] filter [%s]\n", - *base, *scope, *filter); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_pre_ds_search_cb -- end\n"); - - return; -} - -/* called before the total update to get all entries from the DS to sync to AD */ -static void -ipa_winsync_pre_ds_search_all_cb(void *cbdata, const char *agmt_dn, - char **base, int *scope, char **filter, - char ***attrs, LDAPControl ***serverctrls) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_pre_ds_search_all_cb -- orig filter [%s] -- begin\n", - ((filter && *filter) ? *filter : "NULL")); - - /* We only want to grab users from the ds side - no groups */ - slapi_ch_free_string(filter); - /* maybe use ntUniqueId=* - only get users that have already been - synced with AD - ntUniqueId and ntUserDomainId are - indexed for equality only - need to add presence? */ - *filter = slapi_ch_strdup("(&(objectclass=ntuser)(ntUserDomainId=*))"); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_pre_ds_search_all_cb -- end\n"); - - return; -} - -static void -ipa_winsync_pre_ad_mod_user_cb(void *cbdata, const Slapi_Entry *rawentry, - Slapi_Entry *ad_entry, Slapi_Entry *ds_entry, - Slapi_Mods *smods, int *do_modify) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_pre_ad_mod_user_cb -- begin\n"); - - sync_acct_disable(cbdata, rawentry, ds_entry, ACCT_DISABLE_TO_AD, - NULL, smods, do_modify); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_pre_ad_mod_user_cb -- end\n"); - - return; -} - -static void -ipa_winsync_pre_ad_mod_group_cb(void *cbdata, const Slapi_Entry *rawentry, - Slapi_Entry *ad_entry, Slapi_Entry *ds_entry, - Slapi_Mods *smods, int *do_modify) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_pre_ad_mod_group_cb -- begin\n"); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_pre_ad_mod_group_cb -- end\n"); - - return; -} - -static void -ipa_winsync_pre_ds_mod_user_cb(void *cbdata, const Slapi_Entry *rawentry, - Slapi_Entry *ad_entry, Slapi_Entry *ds_entry, - Slapi_Mods *smods, int *do_modify) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_pre_ds_mod_user_cb -- begin\n"); - - sync_acct_disable(cbdata, rawentry, ds_entry, ACCT_DISABLE_TO_DS, - NULL, smods, do_modify); - - do_force_sync(rawentry, ds_entry, smods, do_modify); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_pre_ds_mod_user_cb -- end\n"); - - return; -} - -static void -ipa_winsync_pre_ds_mod_group_cb(void *cbdata, const Slapi_Entry *rawentry, - Slapi_Entry *ad_entry, Slapi_Entry *ds_entry, - Slapi_Mods *smods, int *do_modify) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_pre_ds_mod_group_cb -- begin\n"); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_pre_ds_mod_group_cb -- end\n"); - - return; -} - -static void -ipa_winsync_pre_ds_add_user_cb(void *cbdata, const Slapi_Entry *rawentry, - Slapi_Entry *ad_entry, Slapi_Entry *ds_entry) -{ - IPA_WinSync_Domain_Config *ipaconfig = (IPA_WinSync_Domain_Config *)cbdata; - Slapi_Attr *attr = NULL; - Slapi_Attr *e_attr = NULL; - char *type = NULL; - IPA_WinSync_Config *global_ipaconfig = ipa_winsync_get_config(); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_pre_ds_add_user_cb -- begin\n"); - - if (!ipaconfig || !ipaconfig->domain_e || !ipaconfig->realm_name || - !ipaconfig->homedir_prefix) { - slapi_log_error(SLAPI_LOG_FATAL, ipa_winsync_plugin_name, - "Error: configuration failure: cannot map Windows " - "entry dn [%s], DS entry dn [%s]\n", - slapi_entry_get_dn_const(ad_entry), - slapi_entry_get_dn_const(ds_entry)); - return; - } - - /* add the objectclasses and attributes to the entry */ - for (slapi_entry_first_attr(ipaconfig->domain_e, &attr); attr; - slapi_entry_next_attr(ipaconfig->domain_e, attr, &attr)) - { - slapi_attr_get_type(attr, &type); - if (!type) { - continue; /* should never happen */ - } - - if (!slapi_entry_attr_find(ds_entry, type, &e_attr) && e_attr) { - /* already has attribute - add missing values */ - Slapi_Value *sv = NULL; - int ii = 0; - for (ii = slapi_attr_first_value(attr, &sv); ii != -1; - ii = slapi_attr_next_value(attr, ii, &sv)) - { - if (!slapi_entry_attr_has_syntax_value(ds_entry, type, sv)) { - /* attr-value sv not found in ds_entry; add it */ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_pre_ds_add_user_cb -- " - "adding val for [%s] to new entry [%s]\n", - type, slapi_entry_get_dn_const(ds_entry)); - - slapi_entry_add_value(ds_entry, type, sv); - } - } - } else { /* attr not found */ - Slapi_ValueSet *svs = NULL; - slapi_attr_get_valueset(attr, &svs); /* makes a copy */ - slapi_entry_add_valueset(ds_entry, type, svs); - slapi_valueset_free(svs); /* free the copy */ - } - } - - /* add other attributes */ - type = "krbPrincipalName"; - if (slapi_entry_attr_find(ds_entry, type, &e_attr) || !e_attr) { - char *upn = NULL; - char *uid = NULL; - char *samAccountName = NULL; - /* if the ds_entry already has a uid, use that */ - if ((uid = slapi_entry_attr_get_charptr(ds_entry, "uid"))) { - upn = slapi_ch_smprintf("%s@%s", uid, ipaconfig->realm_name); - slapi_ch_free_string(&uid); - /* otherwise, use the samAccountName from the ad_entry */ - } else if ((samAccountName = - slapi_entry_attr_get_charptr(ad_entry, "samAccountName"))) { - upn = slapi_ch_smprintf("%s@%s", samAccountName, ipaconfig->realm_name); - slapi_ch_free_string(&samAccountName); - } else { /* fatal error - nothing to use for krbPrincipalName */ - slapi_log_error(SLAPI_LOG_FATAL, ipa_winsync_plugin_name, - "Error creating %s for realm [%s] for Windows " - "entry dn [%s], DS entry dn [%s] - Windows entry " - "has no samAccountName, and DS entry has no uid.\n", - type, ipaconfig->realm_name, - slapi_entry_get_dn_const(ad_entry), - slapi_entry_get_dn_const(ds_entry)); - } - - if (upn) { - slapi_entry_attr_set_charptr(ds_entry, type, upn); - slapi_ch_free_string(&upn); - } - } - - type = "homeDirectory"; - if (slapi_entry_attr_find(ds_entry, type, &e_attr) || !e_attr) { - char *homeDir = NULL; - char *uid = NULL; - char *samAccountName = NULL; - /* if the ds_entry already has a uid, use that */ - if ((uid = slapi_entry_attr_get_charptr(ds_entry, "uid"))) { - homeDir = slapi_ch_smprintf("%s/%s", ipaconfig->homedir_prefix, uid); - slapi_ch_free_string(&uid); - /* otherwise, use the samAccountName from the ad_entry */ - } else if ((samAccountName = - slapi_entry_attr_get_charptr(ad_entry, "samAccountName"))) { - homeDir = slapi_ch_smprintf("%s/%s", ipaconfig->homedir_prefix, - samAccountName); - slapi_ch_free_string(&samAccountName); - } else { /* fatal error - nothing to use for homeDirectory */ - slapi_log_error(SLAPI_LOG_FATAL, ipa_winsync_plugin_name, - "Error creating %s for realm [%s] for Windows " - "entry dn [%s], DS entry dn [%s] - Windows entry " - "has no samAccountName, and DS entry has no uid.\n", - type, ipaconfig->realm_name, - slapi_entry_get_dn_const(ad_entry), - slapi_entry_get_dn_const(ds_entry)); - } - - if (homeDir) { - slapi_entry_attr_set_charptr(ds_entry, type, homeDir); - slapi_ch_free_string(&homeDir); - } - } - - /* gecos is not required, but nice to have */ - type = "gecos"; - if (slapi_entry_attr_find(ds_entry, type, &e_attr) || !e_attr) { - char *cn = NULL; - char *displayName = NULL; - /* if the ds_entry already has a cn, use that */ - if ((cn = slapi_entry_attr_get_charptr(ds_entry, "cn"))) { - slapi_entry_attr_set_charptr(ds_entry, type, cn); - slapi_ch_free_string(&cn); - /* otherwise, use the displayName from the ad_entry */ - } else if ((displayName = - slapi_entry_attr_get_charptr(ad_entry, "displayName"))) { - slapi_entry_attr_set_charptr(ds_entry, type, displayName); - slapi_ch_free_string(&displayName); - } - } - - sync_acct_disable(cbdata, rawentry, ds_entry, ACCT_DISABLE_TO_DS, - ds_entry, NULL, NULL); - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_pre_ds_add_user_cb -- end\n"); - - return; -} - -static void -ipa_winsync_pre_ds_add_group_cb(void *cbdata, const Slapi_Entry *rawentry, - Slapi_Entry *ad_entry, Slapi_Entry *ds_entry) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_pre_ds_add_group_cb -- begin\n"); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_pre_ds_add_group_cb -- end\n"); - - return; -} - -static void -ipa_winsync_get_new_ds_user_dn_cb(void *cbdata, const Slapi_Entry *rawentry, - Slapi_Entry *ad_entry, char **new_dn_string, - const Slapi_DN *ds_suffix, const Slapi_DN *ad_suffix) -{ - char **rdns = NULL; - PRBool flatten = PR_TRUE; - IPA_WinSync_Config *ipaconfig = ipa_winsync_get_config(); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_get_new_ds_user_dn_cb -- old dn [%s] -- begin\n", - *new_dn_string); - - slapi_lock_mutex(ipaconfig->lock); - flatten = ipaconfig->flatten; - slapi_unlock_mutex(ipaconfig->lock); - - if (!flatten) { - return; - } - - rdns = ldap_explode_dn(*new_dn_string, 0); - if (!rdns || !rdns[0]) { - ldap_value_free(rdns); - return; - } - - slapi_ch_free_string(new_dn_string); - *new_dn_string = slapi_ch_smprintf("%s,%s", rdns[0], slapi_sdn_get_dn(ds_suffix)); - ldap_value_free(rdns); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_get_new_ds_user_dn_cb -- new dn [%s] -- end\n", - *new_dn_string); - - return; -} - -static void -ipa_winsync_get_new_ds_group_dn_cb(void *cbdata, const Slapi_Entry *rawentry, - Slapi_Entry *ad_entry, char **new_dn_string, - const Slapi_DN *ds_suffix, const Slapi_DN *ad_suffix) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_get_new_ds_group_dn_cb -- begin\n"); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_get_new_ds_group_dn_cb -- end\n"); - - return; -} - -static void -ipa_winsync_pre_ad_mod_user_mods_cb(void *cbdata, const Slapi_Entry *rawentry, - const Slapi_DN *local_dn, - const Slapi_Entry *ds_entry, - LDAPMod * const *origmods, - Slapi_DN *remote_dn, LDAPMod ***modstosend) -{ - Slapi_Mods *smods; - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_pre_ad_mod_user_mods_cb -- begin\n"); - - /* wrap the modstosend in a Slapi_Mods for convenience */ - smods = slapi_mods_new(); - slapi_mods_init_byref(smods, *modstosend); - sync_acct_disable(cbdata, rawentry, (Slapi_Entry *)ds_entry, - ACCT_DISABLE_TO_AD, NULL, smods, NULL); - - /* convert back to LDAPMod ** and clean up */ - *modstosend = slapi_mods_get_ldapmods_passout(smods); - slapi_mods_free(&smods); - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_pre_ad_mod_user_mods_cb -- end\n"); - - return; -} - -static void -ipa_winsync_pre_ad_mod_group_mods_cb(void *cbdata, const Slapi_Entry *rawentry, - const Slapi_DN *local_dn, - const Slapi_Entry *ds_entry, - LDAPMod * const *origmods, - Slapi_DN *remote_dn, LDAPMod ***modstosend) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_pre_ad_mod_group_mods_cb -- begin\n"); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_pre_ad_mod_group_mods_cb -- end\n"); - - return; -} - -static int -ipa_winsync_can_add_entry_to_ad_cb(void *cbdata, const Slapi_Entry *local_entry, - const Slapi_DN *remote_dn) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_can_add_entry_to_ad_cb -- begin\n"); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_can_add_entry_to_ad_cb -- end\n"); - - return 0; /* false - do not allow entries to be added to ad */ -} - -static void -ipa_winsync_begin_update_cb(void *cbdata, const Slapi_DN *ds_subtree, - const Slapi_DN *ad_subtree, int is_total) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_begin_update_cb -- begin\n"); - - ipa_winsync_config_refresh_domain(cbdata, ds_subtree, ad_subtree); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_begin_update_cb -- end\n"); - - return; -} - -static void -ipa_winsync_end_update_cb(void *cbdata, const Slapi_DN *ds_subtree, - const Slapi_DN *ad_subtree, int is_total) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_end_update_cb -- begin\n"); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_end_update_cb -- end\n"); - - return; -} - -static void -ipa_winsync_destroy_agmt_cb(void *cbdata, const Slapi_DN *ds_subtree, - const Slapi_DN *ad_subtree) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_destroy_agmt_cb -- begin\n"); - - ipa_winsync_config_destroy_domain(cbdata, ds_subtree, ad_subtree); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_destroy_agmt_cb -- end\n"); - - return; -} - -static void *ipa_winsync_api[] = { - NULL, /* reserved for api broker use, must be zero */ - ipa_winsync_agmt_init, - ipa_winsync_dirsync_search_params_cb, - ipa_winsync_pre_ad_search_cb, - ipa_winsync_pre_ds_search_entry_cb, - ipa_winsync_pre_ds_search_all_cb, - ipa_winsync_pre_ad_mod_user_cb, - ipa_winsync_pre_ad_mod_group_cb, - ipa_winsync_pre_ds_mod_user_cb, - ipa_winsync_pre_ds_mod_group_cb, - ipa_winsync_pre_ds_add_user_cb, - ipa_winsync_pre_ds_add_group_cb, - ipa_winsync_get_new_ds_user_dn_cb, - ipa_winsync_get_new_ds_group_dn_cb, - ipa_winsync_pre_ad_mod_user_mods_cb, - ipa_winsync_pre_ad_mod_group_mods_cb, - ipa_winsync_can_add_entry_to_ad_cb, - ipa_winsync_begin_update_cb, - ipa_winsync_end_update_cb, - ipa_winsync_destroy_agmt_cb -}; - -/** - * Plugin identifiers - */ -static Slapi_PluginDesc ipa_winsync_pdesc = { - "ipa-winsync-plugin", - "FreeIPA project", - "FreeIPA/1.0", - "ipa winsync plugin" -}; - -static Slapi_ComponentId *ipa_winsync_plugin_id = NULL; - -/* -** Plugin identity mgmt -*/ - -void ipa_winsync_set_plugin_identity(void * identity) -{ - ipa_winsync_plugin_id=identity; -} - -void * ipa_winsync_get_plugin_identity() -{ - return ipa_winsync_plugin_id; -} - -static int -ipa_winsync_plugin_start(Slapi_PBlock *pb) -{ - int rc; - Slapi_Entry *config_e = NULL; /* entry containing plugin config */ - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_plugin_start -- begin\n"); - - if( slapi_apib_register(WINSYNC_v1_0_GUID, ipa_winsync_api) ) { - slapi_log_error( SLAPI_LOG_FATAL, ipa_winsync_plugin_name, - "<-- ipa_winsync_plugin_start -- failed to register winsync api -- end\n"); - return -1; - } - - if ( slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &config_e ) != 0 ) { - slapi_log_error( SLAPI_LOG_FATAL, ipa_winsync_plugin_name, - "missing config entry\n" ); - return( -1 ); - } - - if (( rc = ipa_winsync_config( config_e )) != LDAP_SUCCESS ) { - slapi_log_error( SLAPI_LOG_FATAL, ipa_winsync_plugin_name, - "configuration failed (%s)\n", ldap_err2string( rc )); - return( -1 ); - } - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_plugin_start -- end\n"); - return 0; -} - -static int -ipa_winsync_plugin_close(Slapi_PBlock *pb) -{ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_plugin_close -- begin\n"); - - slapi_apib_unregister(WINSYNC_v1_0_GUID); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_plugin_close -- end\n"); - return 0; -} - -/* this is the slapi plugin init function, - not the one used by the winsync api -*/ -int ipa_winsync_plugin_init(Slapi_PBlock *pb) -{ - void *plugin_id = NULL; - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "--> ipa_winsync_plugin_init -- begin\n"); - - if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, - SLAPI_PLUGIN_VERSION_01 ) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, - (void *) ipa_winsync_plugin_start ) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, - (void *) ipa_winsync_plugin_close ) != 0 || - slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, - (void *)&ipa_winsync_pdesc ) != 0 ) - { - slapi_log_error( SLAPI_LOG_FATAL, ipa_winsync_plugin_name, - "<-- ipa_winsync_plugin_init -- failed to register plugin -- end\n"); - return -1; - } - - /* Retrieve and save the plugin identity to later pass to - internal operations */ - if (slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_id) != 0) { - slapi_log_error(SLAPI_LOG_FATAL, ipa_winsync_plugin_name, - "<-- ipa_winsync_plugin_init -- failed to retrieve plugin identity -- end\n"); - return -1; - } - - ipa_winsync_set_plugin_identity(plugin_id); - - slapi_log_error( SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_winsync_plugin_init -- end\n"); - return 0; -} - -/* - * Check if the given entry has account lock on (i.e. entry is disabled) - * Mostly copied from check_account_lock in the server code. - * Returns: 0 - account is disabled (lock == "true") - * 1 - account is enabled (lock == "false" or empty) - * -1 - some sort of error - */ -static int -ipa_check_account_lock(Slapi_Entry *ds_entry, int *isvirt) -{ - int rc = 1; - Slapi_ValueSet *values = NULL; - int type_name_disposition = 0; - char *actual_type_name = NULL; - int attr_free_flags = 0; - char *strval; - - /* first, see if the attribute is a "real" attribute */ - strval = slapi_entry_attr_get_charptr(ds_entry, "nsAccountLock"); - if (strval) { /* value is real */ - *isvirt = 0; /* value is real */ - rc = 1; /* default to enabled */ - if (PL_strncasecmp(strval, "true", 4) == 0) { - rc = 0; /* account is disabled */ - } - slapi_ch_free_string(&strval); - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_check_account_lock - entry [%s] has real " - "attribute nsAccountLock and entry %s locked\n", - slapi_entry_get_dn_const(ds_entry), - rc ? "is not" : "is"); - return rc; - } - - rc = slapi_vattr_values_get(ds_entry, "nsAccountLock", - &values, - &type_name_disposition, &actual_type_name, - SLAPI_VIRTUALATTRS_REQUEST_POINTERS, - &attr_free_flags); - if (rc == 0) { - Slapi_Value *v = NULL; - const struct berval *bvp = NULL; - - rc = 1; /* default is enabled */ - *isvirt = 1; /* value is virtual */ - if ((slapi_valueset_first_value(values, &v) != -1) && - (bvp = slapi_value_get_berval(v)) != NULL) { - if ( (bvp != NULL) && (PL_strncasecmp(bvp->bv_val, "true", 4) == 0) ) { - slapi_vattr_values_free(&values, &actual_type_name, attr_free_flags); - rc = 0; /* account is disabled */ - } - } - - if (values != NULL) { - slapi_vattr_values_free(&values, &actual_type_name, attr_free_flags); - } - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_check_account_lock - entry [%s] has virtual " - "attribute nsAccountLock and entry %s locked\n", - slapi_entry_get_dn_const(ds_entry), - rc ? "is not" : "is"); - } else { - rc = 1; /* no attr == entry is enabled */ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- ipa_check_account_lock - entry [%s] does not " - "have attribute nsAccountLock - entry %s locked\n", - slapi_entry_get_dn_const(ds_entry), - rc ? "is not" : "is"); - } - - return rc; -} - -static int -do_group_modify(const char *dn, const char *modtype, int modop, const char *modval) -{ - int rc = 0; - LDAPMod mod; - LDAPMod *mods[2]; - const char *val[2]; - Slapi_PBlock *mod_pb = NULL; - - mod_pb = slapi_pblock_new(); - - mods[0] = &mod; - mods[1] = NULL; - - val[0] = modval; - val[1] = NULL; - - mod.mod_op = modop; - mod.mod_type = (char *)modtype; - mod.mod_values = (char **)val; - - slapi_modify_internal_set_pb( - mod_pb, dn, mods, 0, 0, - ipa_winsync_get_plugin_identity(), 0); - - slapi_modify_internal_pb(mod_pb); - - slapi_pblock_get(mod_pb, - SLAPI_PLUGIN_INTOP_RESULT, - &rc); - - slapi_pblock_destroy(mod_pb); - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- do_group_modify - %s value [%s] in attribute [%s] " - "in entry [%s] - result (%d: %s)\n", - (modop & LDAP_MOD_ADD) ? "added" : "deleted", - modval, modtype, dn, - rc, ldap_err2string(rc)); - - return rc; -} - -/* - * This can be used either in the to ad direction or the to ds direction, since in both - * cases we have to read both entries and compare the values. - * ad_entry - entry from AD - * ds_entry - entry from DS - * direction - either ACCT_DISABLE_TO_AD or ACCT_DISABLE_TO_DS - * - * If smods is given, this is the list of mods to send in the given direction. The - * appropriate modify operation will be added to this list or changed to the correct - * value if it already exists. - * Otherwise, if a destination entry is given, the value will be written into - * that entry. - */ -static void -sync_acct_disable( - void *cbdata, /* the usual domain config data */ - const Slapi_Entry *ad_entry, /* the AD entry */ - Slapi_Entry *ds_entry, /* the DS entry */ - int direction, /* the direction - TO_AD or TO_DS */ - Slapi_Entry *update_entry, /* the entry to update for ADDs */ - Slapi_Mods *smods, /* the mod list for MODIFYs */ - int *do_modify /* if not NULL, set this to true if mods were added */ -) -{ - IPA_WinSync_Domain_Config *ipaconfig = (IPA_WinSync_Domain_Config *)cbdata; - IPA_WinSync_Config *global_ipaconfig = ipa_winsync_get_config(); - int acct_disable; - int ds_is_enabled = 1; /* default to true */ - int ad_is_enabled = 1; /* default to true */ - unsigned long adval = 0; /* raw account val from ad entry */ - int isvirt = 1; /* default to virt */ - - slapi_lock_mutex(global_ipaconfig->lock); - acct_disable = global_ipaconfig->acct_disable; - slapi_unlock_mutex(global_ipaconfig->lock); - - if (acct_disable == ACCT_DISABLE_NONE) { - return; /* not supported */ - } - - /* get the account lock state of the ds entry */ - if (0 == ipa_check_account_lock(ds_entry, &isvirt)) { - ds_is_enabled = 0; - } - - /* get the account lock state of the ad entry */ - adval = slapi_entry_attr_get_ulong(ad_entry, "UserAccountControl"); - if (adval & 0x2) { - /* account is disabled */ - ad_is_enabled = 0; - } - - if (ad_is_enabled == ds_is_enabled) { /* both have same value - nothing to do */ - return; - } - - /* have to enable or disable */ - if (direction == ACCT_DISABLE_TO_AD) { - unsigned long mask; - /* set the mod or entry */ - if (update_entry) { - if (ds_is_enabled) { - mask = ~0x2; - adval &= mask; /* unset the 0x2 disable bit */ - } else { - mask = 0x2; - adval |= mask; /* set the 0x2 disable bit */ - } - slapi_entry_attr_set_ulong(update_entry, "userAccountControl", adval); - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- sync_acct_disable - %s AD account [%s] - " - "new value is [%ld]\n", - (ds_is_enabled) ? "enabled" : "disabled", - slapi_entry_get_dn_const(update_entry), - adval); - } else { - /* iterate through the mods - if there is already a mod - for userAccountControl, change it - otherwise, add it */ - char acctvalstr[32]; - LDAPMod *mod = NULL; - struct berval *mod_bval = NULL; - for (mod = slapi_mods_get_first_mod(smods); mod; - mod = slapi_mods_get_next_mod(smods)) { - if (!PL_strcasecmp(mod->mod_type, "userAccountControl") && - mod->mod_bvalues && mod->mod_bvalues[0]) { - mod_bval = mod->mod_bvalues[0]; - /* mod_bval points directly to value inside mod list */ - break; - } - } - if (!mod_bval) { /* not found - add it */ - struct berval tmpbval = {0, NULL}; - Slapi_Mod *smod = slapi_mod_new(); - slapi_mod_init(smod, 1); /* one element */ - slapi_mod_set_type(smod, "userAccountControl"); - slapi_mod_set_operation(smod, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES); - slapi_mod_add_value(smod, &tmpbval); - /* add_value makes a copy of the bval - so let's get a pointer - to that new value - we will change the bval in place */ - mod_bval = slapi_mod_get_first_value(smod); - /* mod_bval points directly to value inside mod list */ - /* now add the new mod to smods */ - slapi_mods_add_ldapmod(smods, - slapi_mod_get_ldapmod_passout(smod)); - /* smods now owns the ldapmod */ - slapi_mod_free(&smod); - if (do_modify) { - *do_modify = 1; /* added mods */ - } - } - if (mod_bval) { - /* this is where we set or update the actual value - mod_bval points directly into the mod list we are - sending */ - if (mod_bval->bv_val && (mod_bval->bv_len > 0)) { - /* get the old val */ - adval = strtol(mod_bval->bv_val, NULL, 10); - } - if (ds_is_enabled) { - mask = ~0x2; - adval &= mask; /* unset the 0x2 disable bit */ - } else { - mask = 0x2; - adval |= mask; /* set the 0x2 disable bit */ - } - PR_snprintf(acctvalstr, sizeof(acctvalstr), "%lu", adval); - slapi_ch_free_string(&mod_bval->bv_val); - mod_bval->bv_val = slapi_ch_strdup(acctvalstr); - mod_bval->bv_len = strlen(acctvalstr); - } - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- sync_acct_disable - %s AD account [%s] - " - "new value is [%ld]\n", - (ds_is_enabled) ? "enabled" : "disabled", - slapi_entry_get_dn_const(ad_entry), - adval); - } - } - - if (direction == ACCT_DISABLE_TO_DS) { - if (!isvirt) { - char *attrtype = NULL; - char *attrval = NULL; - attrtype = "nsAccountLock"; - if (ad_is_enabled) { - attrval = NULL; /* will delete the value */ - } else { - attrval = "true"; - } - - if (update_entry) { - slapi_entry_attr_set_charptr(update_entry, attrtype, attrval); - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- sync_acct_disable - %s DS account [%s]\n", - (ad_is_enabled) ? "enabled" : "disabled", - slapi_entry_get_dn_const(ds_entry)); - } else { /* do mod */ - struct berval tmpbval = {0, NULL}; - Slapi_Mod *smod = slapi_mod_new(); - slapi_mod_init(smod, 1); /* one element */ - slapi_mod_set_type(smod, attrtype); - if (attrval == NULL) { - slapi_mod_set_operation(smod, LDAP_MOD_DELETE|LDAP_MOD_BVALUES); - } else { - slapi_mod_set_operation(smod, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES); - } - slapi_mod_add_value(smod, &tmpbval); - slapi_mods_add_ldapmod(smods, - slapi_mod_get_ldapmod_passout(smod)); - slapi_mod_free(&smod); - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- sync_acct_disable - %s DS account [%s]\n", - (ad_is_enabled) ? "enabled" : "disabled", - slapi_entry_get_dn_const(ds_entry)); - if (do_modify) { - *do_modify = 1; /* added mods */ - } - } - } else { /* use the virtual attr scheme */ - char *adddn, *deldn; - const char *dsdn; - int rc; - /* in the case of disabling a user, need to remove that user from - the activated group, if in there, and add to the inactivated group - however, in the case of enabling a user, we just have to remove - the user from the inactivated group, if in there - if the user - is not in any group, the user is activated by default - */ - if (ad_is_enabled) { - /* add user to activated group, delete from inactivated group */ - adddn = NULL; /* no group means active by default */ - deldn = ipaconfig->inactivated_group_dn; - } else { - /* add user to inactivated group, delete from activated group */ - adddn = ipaconfig->inactivated_group_dn; - deldn = ipaconfig->activated_group_dn; - } - - dsdn = slapi_entry_get_dn_const(ds_entry); - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- sync_acct_disable - %s DS account [%s] - " - "deldn [%s] adddn [%s]\n", - (ad_is_enabled) ? "enabling" : "disabling", - slapi_entry_get_dn_const(ds_entry), - deldn, adddn); - /* first, delete the user from the deldn group - ignore (but log) - value not found errors - means the user wasn't there yet */ - rc = do_group_modify(deldn, "member", LDAP_MOD_DELETE, dsdn); - if (rc == LDAP_NO_SUCH_ATTRIBUTE) { - /* either the value of the attribute doesn't exist */ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "Could not delete user [%s] from the [%s] group: " - "either the user was not in the group already, " - "or the group had no members\n", - dsdn, deldn); - } else if (rc != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_FATAL, ipa_winsync_plugin_name, - "Error deleting user [%s] from the [%s] group: " - "(%d - %s)\n", dsdn, deldn, rc, - ldap_err2string(rc)); - } - /* next, add the user to the adddn group - ignore (but log) - if the user is already in that group */ - if (adddn) { - rc = do_group_modify(adddn, "member", LDAP_MOD_ADD, dsdn); - } else { - rc = LDAP_SUCCESS; - } - if (rc == LDAP_TYPE_OR_VALUE_EXISTS) { - /* user already in that group */ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "Could not add user [%s] to the [%s] group: " - "user is already in that group\n", - dsdn, adddn); - } else if (rc != LDAP_SUCCESS) { - slapi_log_error(SLAPI_LOG_FATAL, ipa_winsync_plugin_name, - "Error adding user [%s] to the [%s] group: " - "(%d - %s)\n", dsdn, adddn, rc, - ldap_err2string(rc)); - } -#ifndef MEMBEROF_WORKS_FOR_INTERNAL_OPS - /* memberOf doesn't currently listen for internal operations - that change group membership - so we manually set the - memberOf attribute in the ds entry - this should not - conflict with memberOf */ - { - Slapi_Value *sv = slapi_value_new(); - slapi_value_init_string(sv, deldn); - if (slapi_entry_attr_has_syntax_value(ds_entry, - "memberOf", sv)) { - if (smods) { - slapi_mods_add_string(smods, LDAP_MOD_DELETE, - "memberOf", deldn); - if (do_modify) { - *do_modify = 1; /* added mods */ - } - } else if (update_entry) { - slapi_entry_delete_string(update_entry, - "memberOf", deldn); - } - } - if (adddn) { - slapi_value_set_string(sv, adddn); - if (!slapi_entry_attr_has_syntax_value(ds_entry, - "memberOf", sv)) { - if (smods) { - slapi_mods_add_string(smods, LDAP_MOD_ADD, - "memberOf", adddn); - if (do_modify) { - *do_modify = 1; /* added mods */ - } - } else if (update_entry) { - slapi_entry_add_string(update_entry, - "memberOf", adddn); - } - } - } - slapi_value_free(&sv); - } -#endif /* MEMBEROF_WORKS_FOR_INTERNAL_OPS */ - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- sync_acct_disable - %s DS account [%s]\n", - (ad_is_enabled) ? "enabled" : "disabled", - slapi_entry_get_dn_const(ds_entry)); - } - } - - return; -} - -/* if entry does not have attribute type and val, and neither - does the smods, add them to the smods */ -static void -find_and_add_mod(Slapi_Entry *ent, Slapi_Mods *smods, const char *type, - const char *val, size_t vallen, int *do_modify) -{ - int found = 1; - Slapi_Value *sv = slapi_value_new(); - LDAPMod *mod = NULL; - - slapi_value_init_string(sv, val); - if (!slapi_entry_attr_has_syntax_value(ent, type, sv)) { - /* entry doesn't have type val - see if there is already - a mod in the mods list that adds it replaces it */ - found = 0; /* not found in entry - see if in mod list */ - for (mod = slapi_mods_get_first_mod(smods); - !found && mod; - mod = slapi_mods_get_next_mod(smods)) { - int ii; - if (PL_strcasecmp(mod->mod_type, type)) { - continue; /* skip - not a mod of this type */ - } - if (!(mod->mod_op & (LDAP_MOD_ADD|LDAP_MOD_REPLACE))) { - continue; /* skip - not an add or replace op */ - } - /* now see if val is in the list of vals for this mod op */ - for (ii = 0; - !found && mod->mod_bvalues && mod->mod_bvalues[ii]; - ++ii) { - if (mod->mod_bvalues[ii]->bv_val) { - found = !PL_strncasecmp(mod->mod_bvalues[ii]->bv_val, - val, vallen); - } - } - } - } - if (!found) { - slapi_mods_add_string(smods, LDAP_MOD_ADD, type, val); - if (do_modify) { - *do_modify = 1; /* added a mod */ - } - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "<-- find_and_add_mod - added value [%s] " - "to attribute [%s] in entry [%s]\n", - val, type, slapi_entry_get_dn_const(ent)); - } - slapi_value_free(&sv); - - return; -} - -/* - * If force sync is true, any time an entry is being added or modified - * in DS, we must ensure the entry has the ntUser objectclass, and that - * it has the ntUserDomainID attribute, and the value of that attribute - * corresponds to the samAccountName in the AD entry. - * ad_entry - entry from AD - * ds_entry - entry from DS - * - * The appropriate modify operation will be added to the given smods - * if it doesn't already exist. - */ -static void -do_force_sync( - const Slapi_Entry *ad_entry, /* the AD entry */ - Slapi_Entry *ds_entry, /* the DS entry */ - Slapi_Mods *smods, /* the mod list for MODIFYs */ - int *do_modify /* if not NULL, set to true if mods were added */ -) -{ - IPA_WinSync_Config *global_ipaconfig = ipa_winsync_get_config(); - PRBool forceSync; - - slapi_lock_mutex(global_ipaconfig->lock); - forceSync = global_ipaconfig->forceSync; - slapi_unlock_mutex(global_ipaconfig->lock); - - if (forceSync == PR_FALSE) { - return; /* not supported */ - } - - slapi_log_error(SLAPI_LOG_PLUGIN, ipa_winsync_plugin_name, - "do_force_sync - forcing sync of AD entry [%s] " - "with DS entry [%s]\n", - slapi_entry_get_dn_const(ad_entry), - slapi_entry_get_dn_const(ds_entry)); - - find_and_add_mod(ds_entry, smods, "objectClass", "ntUser", (size_t)6, do_modify); - - return; -} diff --git a/ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync.h b/ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync.h deleted file mode 100644 index 58a9a6c40..000000000 --- a/ipa-server/ipa-slapi-plugins/ipa-winsync/ipa-winsync.h +++ /dev/null @@ -1,160 +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; version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., 59 Temple - * Place, Suite 330, Boston, MA 02111-1307 USA. - * - * In addition, as a special exception, Red Hat, Inc. gives You the additional - * right to link the code of this Program with code not covered under the GNU - * General Public License ("Non-GPL Code") and to distribute linked combinations - * including the two, subject to the limitations in this paragraph. Non-GPL Code - * permitted under this exception must only link 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 GNU General Public License. Only Red Hat, Inc. may make changes or - * additions to the list of Approved Interfaces. You must obey the GNU General - * Public License in all respects for all of the Program code and other code - * used in conjunction with the Program except the Non-GPL Code covered by this - * exception. If you modify this file, you may extend this exception to your - * version of the file, but you are not obligated to do so. If you do not wish - * to provide this exception without modification, you must delete this - * exception statement from your version and license this file solely under the - * GPL without exception. - * - * Authors: - * Rich Megginson <rmeggins@redhat.com> - * - * Copyright (C) 2008 Red Hat, Inc. - * All rights reserved. - * END COPYRIGHT BLOCK **/ - -#ifndef IPA_WINSYNC_H -#define IPA_WINSYNC_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#ifdef WINSYNC_TEST_IPA -#include <slapi-plugin.h> -#include "winsync-plugin.h" -#else /* the default */ -#include <dirsrv/slapi-plugin.h> -#include <dirsrv/winsync-plugin.h> -#endif /* WINSYNC_TEST_IPA */ - -#define IPA_WINSYNC_PLUGIN_NAME "ipa-winsync" - -typedef struct ipa_winsync_config_struct { - Slapi_Mutex *lock; /* for config access */ - Slapi_Entry *config_e; /* configuration entry */ - PRBool flatten; /* flatten AD DNs */ - char *realm_filter; - char *realm_attr; - char *new_entry_filter; - char *new_user_oc_attr; /* don't care about groups for now */ - char *homedir_prefix_attr; - char *default_group_attr; - char *default_group_filter; - int acct_disable; /* see below for possible values */ - char *inactivated_filter; - char *activated_filter; - PRBool forceSync; -} IPA_WinSync_Config; - -/* - This is the structure that holds our domain - specific configuration -*/ -typedef struct ipa_winsync_domain_config { - Slapi_Entry *domain_e; /* info is stored in this entry */ - char *realm_name; /* realm name */ - char *homedir_prefix; - char *inactivated_group_dn; /* DN of inactivated group */ - char *activated_group_dn; /* DN of activated group */ -} IPA_WinSync_Domain_Config; - -void ipa_winsync_set_plugin_identity(void * identity); -void * ipa_winsync_get_plugin_identity(); - -int ipa_winsync_config( Slapi_Entry *config_e ); -IPA_WinSync_Config *ipa_winsync_get_config( void ); - -/* - * Agreement/domain specific configuration - */ -/* return a new domain specific configuration object */ -void *ipa_winsync_config_new_domain(const Slapi_DN *ds_subtree, const Slapi_DN *ad_subtree); -/* refresh the domain specific configuration object */ -void ipa_winsync_config_refresh_domain(void *cbdata, const Slapi_DN *ds_subtree, const Slapi_DN *ad_subtree); -/* destroy the domain specific configuration object */ -void ipa_winsync_config_destroy_domain(void *cbdata, const Slapi_DN *ds_subtree, const Slapi_DN *ad_subtree); - -/* name of attribute holding the filter to use to - find the ipa realm value -*/ -#define IPA_WINSYNC_REALM_FILTER_ATTR "ipaWinSyncRealmFilter" -/* name of attribute holding the name of the attribute - which contains the ipa realm value -*/ -#define IPA_WINSYNC_REALM_ATTR_ATTR "ipaWinSyncRealmAttr" -/* name of attribute holding the filter to use to - find the new user template entry -*/ -#define IPA_WINSYNC_NEW_ENTRY_FILTER_ATTR "ipaWinSyncNewEntryFilter" -/* name of attribute holding the name of the attribute - in the new user template entry which has the list of objectclasses -*/ -#define IPA_WINSYNC_NEW_USER_OC_ATTR "ipaWinSyncNewUserOCAttr" -/* name of attribute holding the new user attributes and values */ -#define IPA_WINSYNC_NEW_USER_ATTRS_VALS "ipaWinSyncUserAttr" -/* name of attribute holding the name of the attribute which - has the homeDirectory prefix - suffix is the uid */ -#define IPA_WINSYNC_HOMEDIR_PREFIX_ATTR "ipaWinsyncHomeDirAttr" -/* name of attribute holding the name of the attribute which is - used to get the default posix gidNumber */ -#define IPA_WINSYNC_DEFAULTGROUP_ATTR "ipaWinSyncDefaultGroupAttr" -/* filter used to find the group with the gid number whose group name - is in the IPA_WINSYNC_DEFAULTGROUP_ATTR - the filter will have - cn=valueofIPA_WINSYNC_DEFAULTGROUP_ATTR appended to it */ -#define IPA_WINSYNC_DEFAULTGROUP_FILTER_ATTR "ipaWinSyncDefaultGroupFilter" -/* name of attribute holding boolean value to flatten user dns or not */ -#define IPA_WINSYNC_USER_FLATTEN "ipaWinSyncUserFlatten" -/* name of attribute holding account disable sync value */ -#define IPA_WINSYNC_ACCT_DISABLE "ipaWinSyncAcctDisable" -/* possible values of IPA_WINSYNC_ACCT_DISABLE */ -#define IPA_WINSYNC_ACCT_DISABLE_NONE "none" -#define IPA_WINSYNC_ACCT_DISABLE_TO_AD "to_ad" -#define IPA_WINSYNC_ACCT_DISABLE_TO_DS "to_ds" -#define IPA_WINSYNC_ACCT_DISABLE_BOTH "both" -/* enum representing the values above */ -enum { - ACCT_DISABLE_INVALID, /* the invalid value */ - ACCT_DISABLE_NONE, /* do not sync acct disable status */ - ACCT_DISABLE_TO_AD, /* sync only from ds to ad */ - ACCT_DISABLE_TO_DS, /* sync only from ad to ds */ - ACCT_DISABLE_BOTH /* bi-directional sync */ -}; -/* name of attributes holding the search filters to use to find - the DN of the groups that represent inactivated and activated users */ -#define IPA_WINSYNC_INACTIVATED_FILTER "ipaWinSyncInactivatedFilter" -#define IPA_WINSYNC_ACTIVATED_FILTER "ipaWinSyncActivatedFilter" -/* name of attribute holding the value of the forceSync parameter - - this is a boolean attribute - if true, all users in AD that have - a corresponding entry in the DS will be synced - there will be no - way to "turn off sync" on individual entries - if this value is - false, only users which have the ntUser objectclass and an - ntDomainUserID attribute which corresponds to an AD account - with the same value for samAccountName will be synced -*/ -#define IPA_WINSYNC_FORCE_SYNC "ipaWinSyncForceSync" -#endif /* IPA_WINSYNC_H */ |