On Wed, 2014-10-08 at 17:19 -0400, Simo Sorce wrote: > On Wed, 08 Oct 2014 15:53:39 -0400 > Nathaniel McCallum <npmccal...@redhat.com> wrote: > > > As I understand my code, all servers will have csnD. Some servers will > > have valueB and others will have valueD, but valueB == valueD. > > > > We *never* discard a CSN. We only discard the counter/watermark mods > > in the replication operation. > > What Thierry is saying is that the individual attributes in the entry > have associate the last CSN that modified them. Because you remove the > mods when ValueD == ValueB the counter attribute will not have the > associated CSN changed. But it doesn't really matter because the plugin > will always keep things consistent.
Attached is a new version. It removes this optimization. If server X has valueB/csnB and receives valueD/csnD and valueB == valueD, the replication will be applied without any modification. However, if valueB > valueD and csnD > csnB, the counter mods will still be stripped. It also collapses the error check from betxnpre to bepre now that we have a fix for https://fedorahosted.org/389/ticket/47919 committed. The betxnpre functions are completely removed. Also, a dependency on 389 1.3.3.4 (not yet released) is added. Nathaniel
From 368eb782ec7e4d4c245f4cee5bb819eac4ef2a30 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum <npmccal...@redhat.com> Date: Wed, 10 Sep 2014 17:31:37 -0400 Subject: [PATCH] Create ipa-otp-counter 389DS plugin This plugin ensures that all counter/watermark operations are atomic and never decrement. Also, deletion is not permitted. Because this plugin also ensures internal operations behave properly, this also gives ipa-pwd-extop the appropriate behavior for OTP authentication. https://fedorahosted.org/freeipa/ticket/4493 https://fedorahosted.org/freeipa/ticket/4494 --- daemons/configure.ac | 1 + daemons/ipa-slapi-plugins/Makefile.am | 1 + .../ipa-slapi-plugins/ipa-otp-counter/Makefile.am | 25 ++ daemons/ipa-slapi-plugins/ipa-otp-counter/berval.c | 96 +++++ daemons/ipa-slapi-plugins/ipa-otp-counter/berval.h | 66 ++++ .../ipa-otp-counter/ipa-otp-counter.sym | 1 + .../ipa-otp-counter/ipa_otp_counter.c | 436 +++++++++++++++++++++ .../ipa-slapi-plugins/ipa-otp-counter/ldapmod.c | 110 ++++++ .../ipa-slapi-plugins/ipa-otp-counter/ldapmod.h | 54 +++ .../ipa-otp-counter/otp-counter-conf.ldif | 15 + freeipa.spec.in | 8 +- ipaserver/install/dsinstance.py | 4 + 12 files changed, 814 insertions(+), 3 deletions(-) create mode 100644 daemons/ipa-slapi-plugins/ipa-otp-counter/Makefile.am create mode 100644 daemons/ipa-slapi-plugins/ipa-otp-counter/berval.c create mode 100644 daemons/ipa-slapi-plugins/ipa-otp-counter/berval.h create mode 100644 daemons/ipa-slapi-plugins/ipa-otp-counter/ipa-otp-counter.sym create mode 100644 daemons/ipa-slapi-plugins/ipa-otp-counter/ipa_otp_counter.c create mode 100644 daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.c create mode 100644 daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.h create mode 100644 daemons/ipa-slapi-plugins/ipa-otp-counter/otp-counter-conf.ldif diff --git a/daemons/configure.ac b/daemons/configure.ac index b4507a6d972f854331925e72869898576bdfd76f..bfcdeadcd1dc73762d8c773ee50210d9bdb91e92 100644 --- a/daemons/configure.ac +++ b/daemons/configure.ac @@ -314,6 +314,7 @@ AC_CONFIG_FILES([ ipa-slapi-plugins/ipa-dns/Makefile ipa-slapi-plugins/ipa-enrollment/Makefile ipa-slapi-plugins/ipa-lockout/Makefile + ipa-slapi-plugins/ipa-otp-counter/Makefile ipa-slapi-plugins/ipa-otp-lasttoken/Makefile ipa-slapi-plugins/ipa-pwd-extop/Makefile ipa-slapi-plugins/ipa-extdom-extop/Makefile diff --git a/daemons/ipa-slapi-plugins/Makefile.am b/daemons/ipa-slapi-plugins/Makefile.am index 06e6ee8b86f138cce05f2184ac98c39ffaf9757f..07733921e43ac2eb9e248b276351d915a854bf7e 100644 --- a/daemons/ipa-slapi-plugins/Makefile.am +++ b/daemons/ipa-slapi-plugins/Makefile.am @@ -7,6 +7,7 @@ SUBDIRS = \ ipa-enrollment \ ipa-lockout \ ipa-modrdn \ + ipa-otp-counter \ ipa-otp-lasttoken \ ipa-pwd-extop \ ipa-extdom-extop \ diff --git a/daemons/ipa-slapi-plugins/ipa-otp-counter/Makefile.am b/daemons/ipa-slapi-plugins/ipa-otp-counter/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..6b18467613e9bd301ce7432b7052f0fb15aae886 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-otp-counter/Makefile.am @@ -0,0 +1,25 @@ +MAINTAINERCLEANFILES = *~ Makefile.in +PLUGIN_COMMON_DIR = ../common +AM_CPPFLAGS = \ + -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) + +plugindir = $(libdir)/dirsrv/plugins +plugin_LTLIBRARIES = libipa_otp_counter.la +libipa_otp_counter_la_SOURCES = berval.c berval.h ldapmod.c ldapmod.h ipa_otp_counter.c +libipa_otp_counter_la_LDFLAGS = -avoid-version -export-symbols ipa-otp-counter.sym +libipa_otp_counter_la_LIBADD = $(LDAP_LIBS) + +appdir = $(IPA_DATA_DIR) +app_DATA = otp-counter-conf.ldif +EXTRA_DIST = $(app_DATA) diff --git a/daemons/ipa-slapi-plugins/ipa-otp-counter/berval.c b/daemons/ipa-slapi-plugins/ipa-otp-counter/berval.c new file mode 100644 index 0000000000000000000000000000000000000000..884e1a21004c5440f3bbad9da57d43bba8649d5f --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-otp-counter/berval.c @@ -0,0 +1,96 @@ +/** 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: + * Nathaniel McCallum <npmccal...@redhat.com> + * + * Copyright (C) 2014 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "berval.h" + +#include <slapi-plugin.h> + +#include <limits.h> + +struct berval * +berval_new_longlong(long long value) +{ + struct berval *bv; + + bv = (struct berval*) slapi_ch_malloc(sizeof(struct berval*)); + bv->bv_val = slapi_ch_smprintf("%lld", value); + bv->bv_len = strlen(bv->bv_val); + + return bv; +} + +void +berval_free(struct berval **bv) +{ + if (*bv == NULL) + return; + + slapi_ch_free((void **) &(*bv)->bv_val); + slapi_ch_free((void **) bv); +} + +long long +berval_to_longlong(const struct berval *bv) +{ + char buf[bv->bv_len + 1]; + memcpy(buf, bv->bv_val, bv->bv_len); + buf[sizeof(buf)-1] = '\0'; + + return strtoll(buf, NULL, 10); +} + +struct berval ** +bervals_new_longlong(long long value) +{ + struct berval **bvs; + + bvs = (struct berval**) slapi_ch_calloc(2, sizeof(struct berval*)); + bvs[0] = berval_new_longlong(value); + + return bvs; +} + +void +bervals_free(struct berval ***bvals) +{ + for (struct berval **itr = *bvals; *itr != NULL; itr++) + berval_free(itr); + + slapi_ch_free((void**) bvals); +} diff --git a/daemons/ipa-slapi-plugins/ipa-otp-counter/berval.h b/daemons/ipa-slapi-plugins/ipa-otp-counter/berval.h new file mode 100644 index 0000000000000000000000000000000000000000..5ad4ebc147043bd61dbcd53b6675bf3b170d19ae --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-otp-counter/berval.h @@ -0,0 +1,66 @@ +/** 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: + * Nathaniel McCallum <npmccal...@redhat.com> + * + * Copyright (C) 2014 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#pragma once + +#include <lber.h> +#include <stdbool.h> + +/* Creates a new berval from a long long. */ +struct berval * +berval_new_longlong(long long value); + +/* Frees a berval. */ +void +berval_free(struct berval **bv); + +/* Converts a berval to a long long. + * If the value will not fit in a long long, + * LLONG_MIN or LLONG_MAX will be returned. + */ +long long +berval_to_longlong(const struct berval *bv); + +/* Creates a NULL-terminated array with a single berval. */ +struct berval ** +bervals_new_longlong(long long value); + +/* Frees a NULL-terminated array of bervals. */ +void +bervals_free(struct berval ***bvals); diff --git a/daemons/ipa-slapi-plugins/ipa-otp-counter/ipa-otp-counter.sym b/daemons/ipa-slapi-plugins/ipa-otp-counter/ipa-otp-counter.sym new file mode 100644 index 0000000000000000000000000000000000000000..acc5e9394b8f7e7cc6eb767d700073f7745416ea --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-otp-counter/ipa-otp-counter.sym @@ -0,0 +1 @@ +ipa_otp_counter_init diff --git a/daemons/ipa-slapi-plugins/ipa-otp-counter/ipa_otp_counter.c b/daemons/ipa-slapi-plugins/ipa-otp-counter/ipa_otp_counter.c new file mode 100644 index 0000000000000000000000000000000000000000..aea9aa56cc4d720fe7bd73dbb74fca39b378e95b --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-otp-counter/ipa_otp_counter.c @@ -0,0 +1,436 @@ +/** 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: + * Nathaniel McCallum <npmccal...@redhat.com> + * + * Copyright (C) 2014 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/** + * The purpose of this plugin is to ensure that counter/watermark values: + * 1. Have atomic operations. + * 2. Never go backwards. + * 3. Never get deleted. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "berval.h" +#include "ldapmod.h" + +#include <limits.h> + +#include <plstr.h> + +#define COUNTER_UNSET LLONG_MIN + +static void *plugin_id; + +static long long +get_counter(Slapi_Entry *entry, const char *attr) +{ + Slapi_Attr *sattr = NULL; + + if (slapi_entry_attr_find(entry, attr, &sattr) == 0) + return slapi_entry_attr_get_longlong(entry, attr); + + return COUNTER_UNSET; +} + +/** + * Determines the name of the counter or watermark attribute based + * upon the objectClass of the entry. + * + * If no match is found, this function returns NULL indicating that + * this entry is not a known token type. + */ +static const char * +find_counter_name(Slapi_Entry *entry) +{ + static struct { + const char *clss; + const char *attr; + } table[] = { + { "ipatokenHOTP", "ipatokenHOTPcounter" }, + { "ipatokenTOTP", "ipatokenTOTPwatermark" }, + { NULL, NULL } + }; + + const char *attr = NULL; + char **clsses = NULL; + + clsses = slapi_entry_attr_get_charray(entry, "objectClass"); + if (clsses == NULL) + return NULL; + + for (size_t i = 0; attr == NULL && clsses[i] != NULL; i++) { + for (size_t j = 0; attr == NULL && table[j].clss != NULL; j++) { + if (PL_strcasecmp(table[j].clss, clsses[i]) == 0) + attr = table[j].attr; + } + } + + slapi_ch_array_free(clsses); + return attr; +} + +/** + * Normalizes the input values of counter/watermark modifications. + * + * 1. All INCREMENT and REPLACE operations need to be replace by + * equivalent DELETE/ADD combination operations. This ensures + * atomicity. + * + * 2. Any incoming DELETE operations need to be sanity checked. + * + * If no value is specified, the current counter value is added + * to the operation. Without this, we cannot guarantee that the + * operation will not causes a decrement. + * + * This function returns the size of the new LDAPMod* array or zero + * if there are no counter/watermark operations. + */ +static size_t +normalize_input(LDAPMod ***mods, const char *attr, long long ctr) +{ + LDAPMod **tmp; + size_t o; /* Counts the number of operations. */ + size_t c; /* Counts the number of counter operations. */ + size_t e; /* Counts the number of expansions. */ + + /* Get the size of the mods when all expansions are performed. */ + for (o = c = e = 0; (*mods)[o] != NULL; o++) { + if (PL_strcasecmp((*mods)[o]->mod_type, attr) != 0) + continue; + + switch ((*mods)[o]->mod_op & LDAP_MOD_OP) { + case LDAP_MOD_REPLACE: + case LDAP_MOD_INCREMENT: + e++; + default: + c++; + } + } + + if (c == 0) + return 0; + + /* Filter the modify operations. */ + tmp = (LDAPMod **) slapi_ch_calloc(o + e + 1, sizeof(LDAPMod*)); + for (size_t i = 0, j = 0; (*mods)[i] != NULL; tmp[j++] = (*mods)[i++]) { + LDAPMod *mod = (*mods)[i]; + + if (PL_strcasecmp(mod->mod_type, attr) != 0) + continue; + + /* This is not strictly needed, but simplifies the code. */ + ldapmod_convert_bvalues(mod); + + switch (mod->mod_op & LDAP_MOD_OP) { + case LDAP_MOD_DELETE: + /* Normalize input: if an empty array is allocated, free it. */ + if (mod->mod_bvalues != NULL && mod->mod_bvalues[0] == NULL) + bervals_free(&mod->mod_bvalues); + + if (mod->mod_bvalues == NULL) + mod->mod_bvalues = bervals_new_longlong(ctr); + + ctr = COUNTER_UNSET; + break; + + case LDAP_MOD_INCREMENT: + if (ctr != COUNTER_UNSET) + tmp[j++] = ldapmod_new_longlong(LDAP_MOD_DELETE, attr, ctr); + + ctr += ldapmod_get_value(mod, 1); + + bervals_free(&mod->mod_bvalues); + mod->mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; + mod->mod_bvalues = bervals_new_longlong(ctr); + break; + + case LDAP_MOD_REPLACE: + if (ctr != COUNTER_UNSET) + tmp[j++] = ldapmod_new_longlong(LDAP_MOD_DELETE, attr, ctr); + + mod->mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; + + /* Fall through. */ + + case LDAP_MOD_ADD: + ctr = ldapmod_get_value(mod, 0); + break; + } + } + + slapi_ch_free((void **) mods); + *mods = tmp; + return o + e; +} + +/** + * Simulates how the specified mods will impact the counter. + */ +static long long +simulate(LDAPMod **mods, const char *attr, long long ctr) +{ + for (size_t i = 0; mods[i] != NULL; i++) { + if (PL_strcasecmp(mods[i]->mod_type, attr) != 0) + continue; + + switch (mods[i]->mod_op & LDAP_MOD_OP) { + case LDAP_MOD_DELETE: + ctr = COUNTER_UNSET; + break; + + case LDAP_MOD_INCREMENT: + ctr = ldapmod_get_value(mods[i], ctr + 1); + break; + + case LDAP_MOD_REPLACE: + case LDAP_MOD_ADD: + ctr = ldapmod_get_value(mods[i], 0); + break; + } + } + + return ctr; +} + +/** + * Modifies input to ensure correct counter behavior. + * + * For non-replication operations, we change all REPLACE and INCREMENT + * operations into DELETE/ADD pair operations. We also sanity check + * incoming DELETE operations. If the request would cause the counter to + * decrement or delete, fail the operation. + * + * For replication operations, if the transaction would decrement the + * counter, delete it or modify it to the same value, we remove all mods + * related to the counter and let the replication request continue. In + * the first two cases, this enforces correct behavior. In the last case, + * this reduces write contention on the counter when a replica-set-wide + * authentication collision has occurred. + */ +static int +preop_mod(Slapi_PBlock *pb) +{ + Slapi_Entry *epre = NULL; + const char *attr = NULL; + LDAPMod **mods = NULL; + long long cpost; + long long cpre; + int repl = 0; + int rc = 0; + + rc |= slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &repl); + rc |= slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &epre); + rc |= slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); + if (rc != 0 || epre == NULL || mods == NULL) + return 0; + + attr = find_counter_name(epre); + if (attr == NULL) + return 0; /* Not a token. */ + + cpre = get_counter(epre, attr); + + if (repl == 0) { + if (normalize_input(&mods, attr, cpre) != 0) + slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods); + } + + cpost = simulate(mods, attr, cpre); + if (cpost < cpre) { + if (repl == 0) { + char *msg = NULL; + + msg = slapi_ch_smprintf("Will not %s %s", + cpost == COUNTER_UNSET ? "delete" : "decrement", attr); + + rc = LDAP_UNWILLING_TO_PERFORM; + slapi_send_ldap_result(pb, rc, NULL, msg, 0, NULL); + slapi_pblock_set(pb, SLAPI_RESULT_CODE, &rc); + + free(msg); + return rc; + } + + /* Remove counter attribute modifications. */ + for (size_t i = 0, j = 0 ; ; i++, j++) { + mods[j] = mods[i]; + if (mods[j] == NULL) + break; + + if (PL_strcasecmp(mods[j]->mod_type, attr) == 0) + ldapmod_free(&mods[j--]); + } + } + + return 0; +} + +static void +writeback(Slapi_Entry *entry, const char *attr, + long long cold, long long cnew) +{ + Slapi_PBlock *pb = NULL; + char dbuf[32]; + char abuf[32]; + + LDAPMod *mods[] = { + &(LDAPMod) { + LDAP_MOD_DELETE, (char *) attr, + .mod_values = (char *[]) { dbuf, NULL } + }, + &(LDAPMod) { + LDAP_MOD_ADD, (char *) attr, + .mod_values = (char *[]) { abuf, NULL } + }, + NULL + }; + + snprintf(dbuf, sizeof(dbuf), "%lld", cold); + snprintf(abuf, sizeof(abuf), "%lld", cnew); + + pb = slapi_pblock_new(); + slapi_modify_internal_set_pb(pb, slapi_entry_get_dn_const(entry), + mods, NULL, NULL, plugin_id, 0); + slapi_modify_internal_pb(pb); + slapi_pblock_destroy(pb); +} + +/** + * Ensures replications receive the highest value seen. + * + * A replication request that arrives at the server may be internally + * discarded, even if it has a higher counter value, because of a lower + * CSN. However, we always want to record the highest value seen. + * + * We solve this problem by checking the value of the replication request + * against the value of the entry after the replication. If the replication + * request contained a higher value than what the entry contains, we create + * a new modification to bump up the counter to the highest value. + * + * This check is only for replication operations. + */ +static int +postop_mod(Slapi_PBlock *pb) +{ + Slapi_Entry *epost = NULL; + Slapi_Entry *epre = NULL; + const char *attr = NULL; + LDAPMod **mods = NULL; + long long cpost; + long long cpre; + long long csim; + int repl = 0; + int rc = 0; + + rc |= slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &repl); + rc |= slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &epost); + rc |= slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &epre); + rc |= slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); + if (rc != 0 || epost == NULL || epre == NULL || mods == NULL) + return 0; + + if (repl == 0) + return 0; + + attr = find_counter_name(epost); + if (attr == NULL) + return 0; /* Not a token. */ + + cpost = get_counter(epost, attr); + cpre = get_counter(epre, attr); + csim = simulate(mods, attr, cpre); + + if (csim > cpost) + writeback(epost, attr, cpost, csim); + + return 0; +} + +static int +preop_init(Slapi_PBlock *pb) +{ + return slapi_pblock_set(pb, SLAPI_PLUGIN_BE_PRE_MODIFY_FN, preop_mod); +} + +static int +postop_init(Slapi_PBlock *pb) +{ + return slapi_pblock_set(pb, SLAPI_PLUGIN_BE_POST_MODIFY_FN, postop_mod); +} + +static int +start_fn(Slapi_PBlock *pb) +{ + return 0; +} + +static int +close_fn(Slapi_PBlock *pb) +{ + return 0; +} + +int +ipa_otp_counter_init(Slapi_PBlock *pb) +{ + static const Slapi_PluginDesc desc = { + "ipa-otp-counter", + "FreeIPA", + "FreeIPA/1.0", + "Ensure proper OTP token counter operation" + }; + + int ret = 0; + + ret |= slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_id); + ret |= slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); + ret |= slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *) &desc); + ret |= slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, start_fn); + ret |= slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, close_fn); + ret |= slapi_register_plugin("bepreoperation", 1, __func__, preop_init, + "ipa-otp-counter bepreoperation", NULL, + plugin_id); + ret |= slapi_register_plugin("bepostoperation", 1, __func__, postop_init, + "ipa-otp-counter bepostoperation", NULL, + plugin_id); + + return ret; +} diff --git a/daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.c b/daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.c new file mode 100644 index 0000000000000000000000000000000000000000..34fb0479475594c823b02bc55321ff426580f6ea --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.c @@ -0,0 +1,110 @@ +/** 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: + * Nathaniel McCallum <npmccal...@redhat.com> + * + * Copyright (C) 2014 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "ldapmod.h" +#include "berval.h" + +#include <limits.h> + +long long +ldapmod_get_value(const LDAPMod *mod, long long def) +{ + long long v; + + if (mod == NULL) + return def; + + if (mod->mod_bvalues == NULL) + return def; + + if (mod->mod_bvalues[0] == NULL) + return def; + + v = berval_to_longlong(mod->mod_bvalues[0]); + if (v == LLONG_MIN || v == LLONG_MAX) + return def; + + return v; +} + +LDAPMod * +ldapmod_new_longlong(int op, const char *attr, long long value) +{ + LDAPMod *mod; + + mod = (LDAPMod*) slapi_ch_malloc(sizeof(LDAPMod)); + mod->mod_op = op | LDAP_MOD_BVALUES; + mod->mod_type = slapi_ch_strdup(attr); + mod->mod_bvalues = bervals_new_longlong(value); + + return mod; +} + +void +ldapmod_convert_bvalues(LDAPMod *mod) +{ + if (mod == NULL || (mod->mod_op & LDAP_MOD_BVALUES)) + return; + + mod->mod_op |= LDAP_MOD_BVALUES; + + if (mod->mod_values == NULL) { + mod->mod_bvalues = NULL; + return; + } + + for (size_t i = 0; mod->mod_values[i] != NULL; i++) { + struct berval *bv; + bv = (struct berval*) slapi_ch_malloc(sizeof(struct berval)); + bv->bv_val = mod->mod_values[i]; + bv->bv_len = strlen(bv->bv_val); + mod->mod_bvalues[i] = bv; + } +} + +void +ldapmod_free(LDAPMod **mod) +{ + if (mod == NULL || *mod == NULL) + return; + + bervals_free(&(*mod)->mod_bvalues); + slapi_ch_free_string(&(*mod)->mod_type); + slapi_ch_free((void **) mod); +} diff --git a/daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.h b/daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.h new file mode 100644 index 0000000000000000000000000000000000000000..45f43904b2288a97802ad2d698a30be972e2d8b7 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.h @@ -0,0 +1,54 @@ +/** 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: + * Nathaniel McCallum <npmccal...@redhat.com> + * + * Copyright (C) 2014 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#pragma once + +#include <slapi-plugin.h> + +long long +ldapmod_get_value(const LDAPMod *mod, long long def); + +LDAPMod * +ldapmod_new_longlong(int op, const char *attr, long long value); + +void +ldapmod_convert_bvalues(LDAPMod *mod); + +void +ldapmod_free(LDAPMod **mod); diff --git a/daemons/ipa-slapi-plugins/ipa-otp-counter/otp-counter-conf.ldif b/daemons/ipa-slapi-plugins/ipa-otp-counter/otp-counter-conf.ldif new file mode 100644 index 0000000000000000000000000000000000000000..875d669120d4b8a44706702effe0ece0e8ab10dc --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-otp-counter/otp-counter-conf.ldif @@ -0,0 +1,15 @@ +dn: cn=IPA OTP Counter,cn=plugins,cn=config +changetype: add +objectclass: top +objectclass: nsSlapdPlugin +objectclass: extensibleObject +cn: IPA OTP Counter +nsslapd-pluginpath: libipa_otp_counter +nsslapd-plugininitfunc: ipa_otp_counter_init +nsslapd-plugintype: preoperation +nsslapd-pluginenabled: on +nsslapd-pluginid: ipa-otp-counter +nsslapd-pluginversion: 1.0 +nsslapd-pluginvendor: Red Hat, Inc. +nsslapd-plugindescription: IPA OTP Counter plugin +nsslapd-plugin-depends-on-type: database diff --git a/freeipa.spec.in b/freeipa.spec.in index b0d4b06a033e07fd0b4d80ddbde6dffc3d0ace85..aa906ca9cff32578b780f65502de805a222d7442 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -19,7 +19,7 @@ Source0: freeipa-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) %if ! %{ONLY_CLIENT} -BuildRequires: 389-ds-base-devel >= 1.3.3.2 +BuildRequires: 389-ds-base-devel >= 1.3.3.4 BuildRequires: svrcore-devel BuildRequires: policycoreutils >= %{POLICYCOREUTILSVER} BuildRequires: systemd-units @@ -88,7 +88,7 @@ Group: System Environment/Base Requires: %{name}-python = %{version}-%{release} Requires: %{name}-client = %{version}-%{release} Requires: %{name}-admintools = %{version}-%{release} -Requires: 389-ds-base >= 1.3.3.2 +Requires: 389-ds-base >= 1.3.3.4 Requires: openldap-clients > 2.4.35-4 Requires: nss >= 3.14.3-12.0 Requires: nss-tools >= 3.14.3-12.0 @@ -125,7 +125,7 @@ Requires: zip Requires: policycoreutils >= %{POLICYCOREUTILSVER} Requires: tar Requires(pre): certmonger >= 0.75.13 -Requires(pre): 389-ds-base >= 1.3.3.2 +Requires(pre): 389-ds-base >= 1.3.3.4 Requires: fontawesome-fonts Requires: open-sans-fonts @@ -359,6 +359,7 @@ rm %{buildroot}/%{plugin_dir}/libipa_sidgen.la rm %{buildroot}/%{plugin_dir}/libipa_sidgen_task.la rm %{buildroot}/%{plugin_dir}/libipa_extdom_extop.la rm %{buildroot}/%{plugin_dir}/libipa_range_check.la +rm %{buildroot}/%{plugin_dir}/libipa_otp_counter.la rm %{buildroot}/%{plugin_dir}/libipa_otp_lasttoken.la rm %{buildroot}/%{_libdir}/krb5/plugins/kdb/ipadb.la rm %{buildroot}/%{_libdir}/samba/pdb/ipasam.la @@ -720,6 +721,7 @@ fi %attr(755,root,root) %{plugin_dir}/libipa_cldap.so %attr(755,root,root) %{plugin_dir}/libipa_dns.so %attr(755,root,root) %{plugin_dir}/libipa_range_check.so +%attr(755,root,root) %{plugin_dir}/libipa_otp_counter.so %attr(755,root,root) %{plugin_dir}/libipa_otp_lasttoken.so %dir %{_localstatedir}/lib/ipa %attr(700,root,root) %dir %{_localstatedir}/lib/ipa/backup diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py index c9a1c15e9495108382f6e2e8a58f6cc4e8f79c98..411d2e82967a6e72556209d77f3e473c9b22676d 100644 --- a/ipaserver/install/dsinstance.py +++ b/ipaserver/install/dsinstance.py @@ -241,6 +241,7 @@ class DsInstance(service.Service): self.step("configuring DNS plugin", self.__config_dns_module) self.step("enabling entryUSN plugin", self.__enable_entryusn) self.step("configuring lockout plugin", self.__config_lockout_module) + self.step("configuring OTP counter plugin", self.__config_otp_counter_module) self.step("configuring OTP last token plugin", self.__config_otp_lasttoken_module) self.step("creating indices", self.__create_indices) self.step("enabling referential integrity plugin", self.__add_referint_module) @@ -553,6 +554,9 @@ class DsInstance(service.Service): def __config_lockout_module(self): self._ldap_mod("lockout-conf.ldif") + def __config_otp_counter_module(self): + self._ldap_mod("otp-counter-conf.ldif") + def __config_otp_lasttoken_module(self): self._ldap_mod("otp-lasttoken-conf.ldif") -- 2.1.0
_______________________________________________ Freeipa-devel mailing list Freeipa-devel@redhat.com https://www.redhat.com/mailman/listinfo/freeipa-devel