From e30cd6ba42c256d2016db45146d616f329455e86 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Thu, 29 Jan 2009 16:26:07 -0500 Subject: Mass tree reorganization for IPAv2. To view previous history of files use: % git log --follow -- 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 --- daemons/ipa-slapi-plugins/ipa-winsync/Makefile.am | 43 + daemons/ipa-slapi-plugins/ipa-winsync/README | 0 .../ipa-winsync/ipa-winsync-conf.ldif | 27 + .../ipa-winsync/ipa-winsync-config.c | 975 ++++++++++++++++ .../ipa-slapi-plugins/ipa-winsync/ipa-winsync.c | 1177 ++++++++++++++++++++ .../ipa-slapi-plugins/ipa-winsync/ipa-winsync.h | 160 +++ 6 files changed, 2382 insertions(+) create mode 100644 daemons/ipa-slapi-plugins/ipa-winsync/Makefile.am create mode 100644 daemons/ipa-slapi-plugins/ipa-winsync/README create mode 100644 daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync-conf.ldif create mode 100644 daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync-config.c create mode 100644 daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync.c create mode 100644 daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync.h (limited to 'daemons/ipa-slapi-plugins/ipa-winsync') diff --git a/daemons/ipa-slapi-plugins/ipa-winsync/Makefile.am b/daemons/ipa-slapi-plugins/ipa-winsync/Makefile.am new file mode 100644 index 000000000..94bc2dc68 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-winsync/Makefile.am @@ -0,0 +1,43 @@ +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/daemons/ipa-slapi-plugins/ipa-winsync/README b/daemons/ipa-slapi-plugins/ipa-winsync/README new file mode 100644 index 000000000..e69de29bb diff --git a/daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync-conf.ldif b/daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync-conf.ldif new file mode 100644 index 000000000..5b5c56acb --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync-conf.ldif @@ -0,0 +1,27 @@ +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/daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync-config.c b/daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync-config.c new file mode 100644 index 000000000..45efa6df0 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync-config.c @@ -0,0 +1,975 @@ +/** 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 + * + * Copyright (C) 2008 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include +#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 +#include "winsync-plugin.h" +#else +#include +#include +#endif +#include "ipa-winsync.h" + +#include + +#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 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 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/daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync.c b/daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync.c new file mode 100644 index 000000000..9ee8805bb --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync.c @@ -0,0 +1,1177 @@ +/** 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 + * + * Copyright (C) 2008 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include +#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 +#include "winsync-plugin.h" +#else +#include +#include +#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/daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync.h b/daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync.h new file mode 100644 index 000000000..58a9a6c40 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-winsync/ipa-winsync.h @@ -0,0 +1,160 @@ +/** 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 + * + * 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 +#endif + +#ifdef WINSYNC_TEST_IPA +#include +#include "winsync-plugin.h" +#else /* the default */ +#include +#include +#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 */ -- cgit