summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--daemons/configure.ac1
-rw-r--r--daemons/ipa-slapi-plugins/Makefile.am1
-rw-r--r--daemons/ipa-slapi-plugins/ipa-range-check/Makefile.am46
-rw-r--r--daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c423
-rw-r--r--daemons/ipa-slapi-plugins/ipa-range-check/range-check-conf.ldif16
-rw-r--r--freeipa.spec.in2
6 files changed, 489 insertions, 0 deletions
diff --git a/daemons/configure.ac b/daemons/configure.ac
index 76ebaa67f..b94673026 100644
--- a/daemons/configure.ac
+++ b/daemons/configure.ac
@@ -342,6 +342,7 @@ AC_CONFIG_FILES([
ipa-slapi-plugins/ipa-uuid/Makefile
ipa-slapi-plugins/ipa-modrdn/Makefile
ipa-slapi-plugins/ipa-sidgen/Makefile
+ ipa-slapi-plugins/ipa-range-check/Makefile
])
AC_OUTPUT
diff --git a/daemons/ipa-slapi-plugins/Makefile.am b/daemons/ipa-slapi-plugins/Makefile.am
index 5a3c9e703..c79e68db1 100644
--- a/daemons/ipa-slapi-plugins/Makefile.am
+++ b/daemons/ipa-slapi-plugins/Makefile.am
@@ -11,6 +11,7 @@ SUBDIRS = \
ipa-version \
ipa-winsync \
ipa-sidgen \
+ ipa-range-check \
$(NULL)
EXTRA_DIST = \
diff --git a/daemons/ipa-slapi-plugins/ipa-range-check/Makefile.am b/daemons/ipa-slapi-plugins/ipa-range-check/Makefile.am
new file mode 100644
index 000000000..f284b42ff
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-range-check/Makefile.am
@@ -0,0 +1,46 @@
+NULL =
+
+PLUGIN_COMMON_DIR=../common
+
+INCLUDES = \
+ -I. \
+ -I$(srcdir) \
+ -I$(PLUGIN_COMMON_DIR) \
+ -I/usr/include/dirsrv \
+ -DPREFIX=\""$(prefix)"\" \
+ -DBINDIR=\""$(bindir)"\" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ $(AM_CFLAGS) \
+ $(LDAP_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(NULL)
+
+plugindir = $(libdir)/dirsrv/plugins
+plugin_LTLIBRARIES = \
+ libipa_range_check.la \
+ $(NULL)
+
+libipa_range_check_la_SOURCES = \
+ ipa_range_check.c \
+ $(NULL)
+
+libipa_range_check_la_LDFLAGS = -avoid-version
+
+libipa_range_check_la_LIBADD = \
+ $(LDAP_LIBS) \
+ $(NULL)
+
+appdir = $(IPA_DATA_DIR)
+app_DATA = \
+ range-check-conf.ldif \
+ $(NULL)
+
+EXTRA_DIST = \
+ $(app_DATA) \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ *~ \
+ Makefile.in
diff --git a/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c b/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c
new file mode 100644
index 000000000..c3235b9b8
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c
@@ -0,0 +1,423 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * In the following paragraph, "GPL" means the GNU General Public
+ * License, version 3 or any later version, and "Non-GPL Code" means
+ * code that is governed neither by the GPL nor a license
+ * compatible with the GPL.
+ *
+ * You may link the code of this Program with Non-GPL Code and convey
+ * linked combinations including the two, provided that such Non-GPL
+ * Code only links to the code of this Program through those well
+ * defined interfaces identified in the file named EXCEPTION found in
+ * the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline
+ * functions from the Approved Interfaces without causing the resulting
+ * work to be covered by the GPL. Only the copyright holders of this
+ * Program may make changes or additions to the list of Approved
+ * Interfaces.
+ *
+ * Authors:
+ * Sumit Bose <sbose@redhat.com>
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <dirsrv/slapi-plugin.h>
+
+#include "util.h"
+
+#define IPA_BASE_ID "ipaBaseID"
+#define IPA_ID_RANGE_SIZE "ipaIDRangeSize"
+#define IPA_BASE_RID "ipaBaseRID"
+#define IPA_SECONDARY_BASE_RID "ipaSecondaryBaseRID"
+#define RANGES_FILTER "objectclass=ipaIDRange"
+
+#define IPA_PLUGIN_NAME "ipa-range-check"
+#define IPA_RANGE_CHECK_FEATURE_DESC "IPA ID range check plugin"
+#define IPA_RANGE_CHECK_PLUGIN_DESC "Check if newly added or modified " \
+ "ID ranges do not overlap with existing ones"
+
+Slapi_PluginDesc ipa_range_check_plugin_desc = {
+ IPA_RANGE_CHECK_FEATURE_DESC,
+ "FreeIPA project",
+ "FreeIPA/1.0",
+ IPA_RANGE_CHECK_PLUGIN_DESC
+};
+
+struct ipa_range_check_ctx {
+ Slapi_ComponentId *plugin_id;
+ const char *base_dn;
+};
+
+struct range_info {
+ uint32_t base_id;
+ uint32_t id_range_size;
+ uint32_t base_rid;
+ uint32_t secondary_base_rid;
+};
+
+static int slapi_entry_to_range_info(struct slapi_entry *entry,
+ struct range_info **_range)
+{
+ int ret;
+ unsigned long ul_val;
+ struct range_info *range = NULL;
+
+ range = calloc(1, sizeof(struct range_info));
+ if (range == NULL) {
+ return ENOMEM;
+ }
+
+ ul_val = slapi_entry_attr_get_ulong(entry, IPA_BASE_ID);
+ if (ul_val == 0 || ul_val >= UINT32_MAX) {
+ ret = ERANGE;
+ goto done;
+ }
+ range->base_id = ul_val;
+
+ ul_val = slapi_entry_attr_get_ulong(entry, IPA_ID_RANGE_SIZE);
+ if (ul_val == 0 || ul_val >= UINT32_MAX) {
+ ret = ERANGE;
+ goto done;
+ }
+ range->id_range_size = ul_val;
+
+ ul_val = slapi_entry_attr_get_ulong(entry, IPA_BASE_RID);
+ if (ul_val >= UINT32_MAX) {
+ ret = ERANGE;
+ goto done;
+ }
+ range->base_rid = ul_val;
+
+ ul_val = slapi_entry_attr_get_ulong(entry, IPA_SECONDARY_BASE_RID);
+ if (ul_val >= UINT32_MAX) {
+ ret = ERANGE;
+ goto done;
+ }
+ range->secondary_base_rid = ul_val;
+
+ *_range = range;
+ ret = 0;
+
+done:
+ if (ret != 0) {
+ free(range);
+ }
+
+ return ret;
+}
+
+#define IN_RANGE(x,base,size) ( (x) >= (base) && ((x) - (base)) < (size) )
+static bool ranges_overlap(struct range_info *r1, struct range_info *r2)
+{
+ if (IN_RANGE(r1->base_id, r2->base_id, r2->id_range_size) ||
+ IN_RANGE((r1->base_id + r1->id_range_size - 1), r2->base_id, r2->id_range_size) ||
+ IN_RANGE(r2->base_id, r1->base_id, r1->id_range_size) ||
+ IN_RANGE((r2->base_id + r2->id_range_size - 1), r1->base_id, r1->id_range_size)) {
+ return true;
+ }
+
+ return false;
+}
+
+static int ipa_range_check_start(Slapi_PBlock *pb)
+{
+ return 0;
+}
+
+static int ipa_range_check_close(Slapi_PBlock *pb)
+{
+ return 0;
+}
+
+static int ipa_range_check_pre_op(Slapi_PBlock *pb, int modtype)
+{
+ int ret;
+ int is_repl_op;
+ struct slapi_entry *entry = NULL;
+ struct range_info *new_range = NULL;
+ struct range_info *old_range = NULL;
+ const char *dn_str;
+ Slapi_DN *dn = NULL;
+ struct ipa_range_check_ctx *ctx;
+ LDAPMod **mods = NULL;
+ Slapi_PBlock *search_pb = NULL;
+ int search_result;
+ Slapi_Entry **search_entries = NULL;
+ size_t c;
+ bool overlap = true;
+ const char *check_attr;
+ char *errmsg = NULL;
+
+ ret = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl_op);
+ if (ret != 0) {
+ LOG_FATAL("slapi_pblock_get failed!?\n");
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (is_repl_op) {
+ LOG("Is replicated operation, nothing to do.\n");
+ return LDAP_SUCCESS;
+ }
+
+ ret = slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &ctx);
+ if (ret != 0) {
+ LOG_FATAL("Missing private plugin context.\n");
+ goto done;
+ }
+
+ ret = slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn_str);
+ if (ret != 0) {
+ LOG_FATAL("Missing target DN.\n");
+ goto done;
+ }
+
+ dn = slapi_sdn_new_dn_byref(dn_str);
+ if (dn == NULL) {
+ LOG_FATAL("Failed to convert target DN.\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+
+ switch (modtype) {
+ case LDAP_CHANGETYPE_ADD:
+ ret = slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &entry);
+ if (ret != 0) {
+ LOG_FATAL("Missing entry to add.\n");
+ goto done;
+ }
+
+ /* Check if this is a range object */
+ check_attr = slapi_entry_attr_get_charptr(entry, IPA_BASE_ID);
+ if (check_attr == NULL) {
+ LOG("Not an ID range object, nothing to do.\n");
+ ret = 0;
+ goto done;
+ }
+
+ break;
+ case LDAP_CHANGETYPE_MODIFY:
+ ret = slapi_search_internal_get_entry(dn, NULL, &entry,
+ ctx->plugin_id);
+ if (ret != 0 || entry == NULL) {
+ LOG_FATAL("Missing entry to modify.\n");
+ ret = LDAP_NO_SUCH_OBJECT;
+ goto done;
+ }
+
+ /* Check if this is a range object */
+ check_attr = slapi_entry_attr_get_charptr(entry, IPA_BASE_ID);
+ if (check_attr == NULL) {
+ LOG("Not an ID range object, nothing to do.\n");
+ ret = 0;
+ goto done;
+ }
+
+ ret = slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ if (ret != 0) {
+ LOG_FATAL("Missing modify values.\n");
+ goto done;
+ }
+
+ ret = slapi_entry_apply_mods(entry, mods);
+ if (ret != 0) {
+ LOG_FATAL("Failed to apply modifications.\n");
+ goto done;
+ }
+
+ break;
+ default:
+ ret = LDAP_OPERATIONS_ERROR;
+ LOG_FATAL("Unsupported LDAP operation.\n");
+ goto done;
+ }
+
+ ret = slapi_entry_to_range_info(entry, &new_range);
+ if (ret != 0) {
+ LOG_FATAL("Failed to convert LDAP entry to range struct.\n");
+ goto done;
+ }
+
+ search_pb = slapi_pblock_new();
+ if (search_pb == NULL) {
+ LOG_FATAL("Failed to create new pblock.\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ slapi_search_internal_set_pb(search_pb, ctx->base_dn,
+ LDAP_SCOPE_SUBTREE, RANGES_FILTER,
+ NULL, 0, NULL, NULL, ctx->plugin_id, 0);
+
+ ret = slapi_search_internal_pb(search_pb);
+ if (ret != 0) {
+ LOG_FATAL("Starting internal search failed.\n");
+ goto done;
+ }
+
+ ret = slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &search_result);
+ if (ret != 0 || search_result != LDAP_SUCCESS) {
+ LOG_FATAL("Internal search failed.\n");
+ ret = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ret = slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &search_entries);
+ if (ret != 0) {
+ LOG_FATAL("Failed to read searched entries.\n");
+ goto done;
+ }
+
+ if (search_entries == NULL || search_entries[0] == NULL) {
+ LOG("No existing entries.\n");
+ ret = 0;
+ goto done;
+ }
+
+ for (c = 0; search_entries[c] != NULL; c++) {
+ ret = slapi_entry_to_range_info(search_entries[c], &old_range);
+ if (ret != 0) {
+ LOG_FATAL("Failed to convert LDAP entry to range struct.\n");
+ goto done;
+ }
+
+ overlap = ranges_overlap(old_range, new_range);
+ free(old_range);
+ old_range = NULL;
+ if (overlap) {
+ LOG_FATAL("New range overlaps with existing one.\n");
+ ret = LDAP_CONSTRAINT_VIOLATION;
+ errmsg = "New range overlaps with existing one.";
+ goto done;
+ }
+ }
+ LOG("No overlaps found.\n");
+
+ ret = 0;
+
+done:
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ slapi_sdn_free(&dn);
+ free(old_range);
+ free(new_range);
+
+ if (ret != 0) {
+ if (errmsg == NULL) {
+ errmsg = "Range Check error";
+ }
+ slapi_send_ldap_result(pb, ret, NULL, errmsg, 0, NULL);
+ }
+
+ return ret;
+}
+
+static int ipa_range_check_mod_pre_op(Slapi_PBlock * pb)
+{
+ return ipa_range_check_pre_op(pb, LDAP_CHANGETYPE_MODIFY);
+}
+
+static int ipa_range_check_add_pre_op(Slapi_PBlock *pb)
+{
+ return ipa_range_check_pre_op(pb, LDAP_CHANGETYPE_ADD);
+}
+
+static int ipa_range_check_init_ctx(Slapi_PBlock *pb,
+ struct ipa_range_check_ctx **_ctx)
+{
+ struct ipa_range_check_ctx *ctx;
+ Slapi_Entry *entry;
+ int ret;
+
+ ctx = calloc(1, sizeof(struct ipa_range_check_ctx));
+ if (ctx == NULL) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ ret = slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &ctx->plugin_id);
+ if ((ret != 0) || (ctx->plugin_id == NULL)) {
+ LOG_FATAL("Could not get identity or identity was NULL\n");
+ if (ret == 0) {
+ ret = -1;
+ }
+ goto done;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &entry);
+ if (entry == NULL) {
+ LOG_FATAL("Plugin configuration not found!\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ctx->base_dn = slapi_entry_attr_get_charptr(entry, "nsslapd-basedn");
+ if (ctx->base_dn == NULL) {
+ LOG_FATAL("Base DN not found in plugin configuration!\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret != 0) {
+ free(ctx);
+ } else {
+ *_ctx = ctx;
+ }
+
+ return ret;
+}
+
+int ipa_range_check_init(Slapi_PBlock *pb)
+{
+ int ret;
+ struct ipa_range_check_ctx *rc_ctx;
+
+ ret = ipa_range_check_init_ctx(pb, &rc_ctx);
+ if (ret != 0) {
+ LOG_FATAL("Failed ot initialize range check plugin.\n");
+ /* do not cause DS to stop, simply do nothing */
+ return 0;
+ }
+
+ ret = 0;
+ if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+ (void *) ipa_range_check_start) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) ipa_range_check_close) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *) &ipa_range_check_plugin_desc) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN,
+ (void *) ipa_range_check_mod_pre_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN,
+ (void *) ipa_range_check_add_pre_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_PRIVATE, rc_ctx) != 0) {
+ LOG_FATAL("failed to register plugin\n");
+ ret = EFAIL;
+ }
+
+ return ret;
+}
diff --git a/daemons/ipa-slapi-plugins/ipa-range-check/range-check-conf.ldif b/daemons/ipa-slapi-plugins/ipa-range-check/range-check-conf.ldif
new file mode 100644
index 000000000..6452304f4
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-range-check/range-check-conf.ldif
@@ -0,0 +1,16 @@
+dn: cn=IPA Range-Check,cn=plugins,cn=config
+changetype: add
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: IPA Range-Check
+nsslapd-pluginpath: libipa_range_check
+nsslapd-plugininitfunc: ipa_range_check_init
+nsslapd-plugintype: preoperation
+nsslapd-pluginenabled: on
+nsslapd-pluginid: ipa_range_check_version
+nsslapd-pluginversion: 1.0
+nsslapd-pluginvendor: Red Hat, Inc.
+nsslapd-plugindescription: IPA Range-Check plugin
+nsslapd-plugin-depends-on-type: database
+nsslapd-basedn: $SUFFIX
diff --git a/freeipa.spec.in b/freeipa.spec.in
index 398d17cea..8d55108ae 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -360,6 +360,7 @@ rm %{buildroot}/%{plugin_dir}/libipa_cldap.la
rm %{buildroot}/%{plugin_dir}/libipa_extdom_extop.la
rm %{buildroot}/%{plugin_dir}/libipa_sidgen.la
rm %{buildroot}/%{plugin_dir}/libipa_sidgen_task.la
+rm %{buildroot}/%{plugin_dir}/libipa_range_check.la
rm %{buildroot}/%{_libdir}/krb5/plugins/kdb/ipadb.la
rm %{buildroot}/%{_libdir}/samba/pdb/ipasam.la
@@ -636,6 +637,7 @@ fi
%attr(755,root,root) %{plugin_dir}/libipa_modrdn.so
%attr(755,root,root) %{plugin_dir}/libipa_lockout.so
%attr(755,root,root) %{plugin_dir}/libipa_cldap.so
+%attr(755,root,root) %{plugin_dir}/libipa_range_check.so
%dir %{_localstatedir}/lib/ipa
%attr(700,root,root) %dir %{_localstatedir}/lib/ipa/sysrestore
%attr(700,root,root) %dir %{_localstatedir}/lib/ipa/sysupgrade