This new version now depends on my patch 0029. Sorry for the reverse
ordering.

On Mon, 2013-12-23 at 12:56 -0500, Nathaniel McCallum wrote:
> This plugin prevents the deletion or deactivation of the last valid
> token for a user. This prevents the user from migrating back to single
> factor authentication once OTP has been enabled.
> 
> Thanks to Mark Reynolds for helping me with this patch.
> _______________________________________________
> Freeipa-devel mailing list
> Freeipa-devel@redhat.com
> https://www.redhat.com/mailman/listinfo/freeipa-devel

>From 919da83ceb5b1bb54436ff04af65a51b5e79e2b2 Mon Sep 17 00:00:00 2001
From: Nathaniel McCallum <npmccal...@redhat.com>
Date: Mon, 16 Dec 2013 16:19:08 -0500
Subject: [PATCH] Add OTP last token plugin

This plugin prevents the deletion or deactivation of the last
valid token for a user. This prevents the user from migrating
back to single factor authentication once OTP has been enabled.

Thanks to Mark Reynolds for helping me with this patch.
---
 daemons/configure.ac                               |   1 +
 daemons/ipa-slapi-plugins/Makefile.am              |   1 +
 .../ipa-otp-lasttoken/Makefile.am                  |  28 +++
 .../ipa-otp-lasttoken/ipa-otp-lasttoken.sym        |   1 +
 .../ipa-otp-lasttoken/ipa_otp_lasttoken.c          | 188 +++++++++++++++++++++
 .../ipa-otp-lasttoken/otp-lasttoken-conf.ldif      |  15 ++
 freeipa.spec.in                                    |   2 +
 ipaserver/install/dsinstance.py                    |   4 +
 8 files changed, 240 insertions(+)
 create mode 100644 daemons/ipa-slapi-plugins/ipa-otp-lasttoken/Makefile.am
 create mode 100644 daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa-otp-lasttoken.sym
 create mode 100644 daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c
 create mode 100644 daemons/ipa-slapi-plugins/ipa-otp-lasttoken/otp-lasttoken-conf.ldif

diff --git a/daemons/configure.ac b/daemons/configure.ac
index b0bbe96a632337b973316a2849c9d35b5d642f5f..3cdb9384c116e73a19c605a3c9401661772cf4d1 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-lasttoken/Makefile
     ipa-slapi-plugins/ipa-pwd-extop/Makefile
     ipa-slapi-plugins/ipa-extdom-extop/Makefile
     ipa-slapi-plugins/ipa-winsync/Makefile
diff --git a/daemons/ipa-slapi-plugins/Makefile.am b/daemons/ipa-slapi-plugins/Makefile.am
index 40725d2259d09010d2f82381543fc77d84435040..06e6ee8b86f138cce05f2184ac98c39ffaf9757f 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-lasttoken	\
 	ipa-pwd-extop		\
 	ipa-extdom-extop	\
 	ipa-uuid		\
diff --git a/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/Makefile.am b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..1e3869bfda9f8fd14cd4d93d0d466780932ac40f
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/Makefile.am
@@ -0,0 +1,28 @@
+MAINTAINERCLEANFILES = *~ Makefile.in
+PLUGIN_COMMON_DIR = ../common
+AM_CPPFLAGS =							\
+	-I.							\
+	-I$(srcdir)						\
+	-I$(srcdir)/../libotp					\
+	-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_lasttoken.la
+libipa_otp_lasttoken_la_SOURCES = ipa_otp_lasttoken.c
+libipa_otp_lasttoken_la_LDFLAGS = -avoid-version -export-symbols ipa-otp-lasttoken.sym
+libipa_otp_lasttoken_la_LIBADD =		\
+	$(LDAP_LIBS)				\
+	$(builddir)/../libotp/libotp.la
+
+appdir = $(IPA_DATA_DIR)
+app_DATA = otp-lasttoken-conf.ldif
+EXTRA_DIST = $(app_DATA)
diff --git a/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa-otp-lasttoken.sym b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa-otp-lasttoken.sym
new file mode 100644
index 0000000000000000000000000000000000000000..e32dc32f5693547bf604480490f42511368fdb81
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa-otp-lasttoken.sym
@@ -0,0 +1 @@
+ipa_otp_lasttoken_init
diff --git a/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c
new file mode 100644
index 0000000000000000000000000000000000000000..de54970416af3b9dd595e4789a464ac732f7c0cf
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c
@@ -0,0 +1,188 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception.
+ *
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <libotp.h>
+#include <time.h>
+
+#define PLUGIN_NAME               "ipa-otp-lasttoken"
+#define LOG(sev, ...) \
+    slapi_log_error(SLAPI_LOG_ ## sev, PLUGIN_NAME, \
+                    "%s: %s\n", __func__, __VA_ARGS__), -1
+
+static void *plugin_id;
+static const Slapi_PluginDesc preop_desc = {
+    PLUGIN_NAME,
+    "FreeIPA",
+    "FreeIPA/1.0",
+    "Protect the user's last active token"
+};
+
+static bool
+target_is_only_enabled_token(Slapi_PBlock *pb)
+{
+    Slapi_DN *target_sdn = NULL;
+    Slapi_DN *token_sdn = NULL;
+    Slapi_DN *owner_sdn = NULL;
+    struct otptoken **tokens;
+    char *dn = NULL;
+    bool match;
+
+    /* Ignore internal operations. */
+    if (slapi_op_internal(pb))
+        return false;
+
+    /* Get the current user's SDN. */
+    slapi_pblock_get(pb, SLAPI_CONN_DN, &dn);
+    if (dn == NULL)
+        return false;
+    owner_sdn = slapi_sdn_new_dn_passin(dn);
+    if (owner_sdn == NULL) {
+        slapi_ch_free_string(&dn);
+        return false;
+    }
+
+    /* Get the SDN of the only enabled token. */
+    tokens = otptoken_find(plugin_id, owner_sdn, NULL, true, NULL);
+    slapi_sdn_free(&owner_sdn);
+    if (tokens != NULL && tokens[0] != NULL && tokens[1] == NULL)
+        token_sdn = slapi_sdn_dup(otptoken_get_sdn(tokens[0]));
+    otptoken_free_array(tokens);
+    if (token_sdn == NULL)
+        return false;
+
+    /* Get the target SDN. */
+    slapi_pblock_get(pb, SLAPI_TARGET_SDN, &target_sdn);
+    if (target_sdn == NULL) {
+        slapi_sdn_free(&token_sdn);
+        return false;
+    }
+
+    /* Does the target SDN match the only enabled token SDN? */
+    match = slapi_sdn_compare(token_sdn, target_sdn) == 0;
+    slapi_sdn_free(&token_sdn);
+    return match;
+}
+
+static inline int
+send_error(Slapi_PBlock *pb, int rc, char *errstr)
+{
+    slapi_send_ldap_result(pb, rc, NULL, errstr, 0, NULL);
+    slapi_pblock_set(pb, SLAPI_RESULT_CODE, &rc);
+    return rc;
+}
+
+static int
+preop_del(Slapi_PBlock *pb)
+{
+    if (!target_is_only_enabled_token(pb))
+        return 0;
+
+    return send_error(pb, LDAP_UNWILLING_TO_PERFORM,
+                      "Can't delete last active token");
+}
+
+static int
+preop_mod(Slapi_PBlock *pb)
+{
+    LDAPMod **mods = NULL;
+
+    if (!target_is_only_enabled_token(pb))
+        return 0;
+
+    /* Do not permit deactivation of the last active token. */
+    slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+    for (int i = 0; mods != NULL && mods[i] != NULL; i++) {
+        if (strcasecmp(mods[i]->mod_type, "ipatokenDisabled") == 0) {
+            return send_error(pb, LDAP_UNWILLING_TO_PERFORM,
+                              "Can't disable last active token");
+        }
+
+        if (strcasecmp(mods[i]->mod_type, "ipatokenOwner") == 0) {
+            return send_error(pb, LDAP_UNWILLING_TO_PERFORM,
+                              "Can't change last active token's owner");
+        }
+
+        if (strcasecmp(mods[i]->mod_type, "ipatokenNotBefore") == 0) {
+            return send_error(pb, LDAP_UNWILLING_TO_PERFORM,
+                              "Can't change last active token's start time");
+        }
+
+        if (strcasecmp(mods[i]->mod_type, "ipatokenNotAfter") == 0) {
+            return send_error(pb, LDAP_UNWILLING_TO_PERFORM,
+                              "Can't change last active token's end time");
+        }
+    }
+
+    return 0;
+}
+
+static int
+preop_init(Slapi_PBlock *pb)
+{
+    if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01))
+        goto error;
+
+    if (slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *) &preop_desc))
+        goto error;
+
+    if (slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_PRE_DELETE_FN, preop_del))
+        goto error;
+
+    if (slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN, preop_mod))
+        goto error;
+
+    return 0;
+
+error:
+    return LOG(FATAL, "failed to register be_txn_pre_op plugin");
+}
+
+int
+ipa_otp_lasttoken_init(Slapi_PBlock *pb)
+{
+    slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_id);
+
+    if (slapi_register_plugin("betxnpreoperation", 1, __func__, preop_init,
+                              PLUGIN_NAME, NULL, plugin_id))
+        return LOG(FATAL, "failed to register plugin");
+
+    return 0;
+}
diff --git a/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/otp-lasttoken-conf.ldif b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/otp-lasttoken-conf.ldif
new file mode 100644
index 0000000000000000000000000000000000000000..767883848b7c85d6de5c677d2f8243ab9da76acc
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/otp-lasttoken-conf.ldif
@@ -0,0 +1,15 @@
+dn: cn=IPA OTP Last Token,cn=plugins,cn=config
+changetype: add
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: IPA OTP Last Token
+nsslapd-pluginpath: libipa_otp_lasttoken
+nsslapd-plugininitfunc: ipa_otp_lasttoken_init
+nsslapd-plugintype: preoperation
+nsslapd-pluginenabled: on
+nsslapd-pluginid: ipa-otp-lasttoken
+nsslapd-pluginversion: 1.0
+nsslapd-pluginvendor: Red Hat, Inc.
+nsslapd-plugindescription: IPA OTP Last Token plugin
+nsslapd-plugin-depends-on-type: database
diff --git a/freeipa.spec.in b/freeipa.spec.in
index acbbcfcbeff3127aca7a178ca9410df5dabec110..1130fd06209c9e76db1e5534553597294700888e 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -393,6 +393,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_lasttoken.la
 rm %{buildroot}/%{_libdir}/krb5/plugins/kdb/ipadb.la
 rm %{buildroot}/%{_libdir}/samba/pdb/ipasam.la
 
@@ -731,6 +732,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_lasttoken.so
 %dir %{_localstatedir}/lib/ipa
 %attr(700,root,root) %dir %{_localstatedir}/lib/ipa/backup
 %attr(700,root,root) %dir %{_localstatedir}/lib/ipa/sysrestore
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index de804059cd29574bed0afcd8fb0d7fb78d16985b..8fa900f8db803d15b1dbe6ba9bbbb5f56f771401 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -270,6 +270,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 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)
         if enable_ssl:
@@ -571,6 +572,9 @@ class DsInstance(service.Service):
     def __config_lockout_module(self):
         self._ldap_mod("lockout-conf.ldif")
 
+    def __config_otp_lasttoken_module(self):
+        self._ldap_mod("otp-lasttoken-conf.ldif")
+
     def __repoint_managed_entries(self):
         self._ldap_mod("repoint-managed-entries.ldif", self.sub_dict)
 
-- 
1.8.4.2

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to