The pwdch extop would just validate the old password before setting the
new one. Becuase this operation returns INVALID_CREDENTIALS when the
password is wrong, it provides an opportunity to brute force the first
factor distinct from the second factor.

This patch causes the pwdch extop to validate the OTP as well. This
closes the above attack vector. It is also, conveniently, the behavior
most users will probably expect.

https://fedorahosted.org/freeipa/ticket/4248
>From 1da047f41b3f07a3c659ee2f1a75be483d483359 Mon Sep 17 00:00:00 2001
From: Nathaniel McCallum <npmccal...@redhat.com>
Date: Fri, 2 May 2014 13:10:09 -0400
Subject: [PATCH] Validate OTP during password change requests

The pwdch extop would just validate the old password before setting the new
one. Becuase this operation returns INVALID_CREDENTIALS when the password is
wrong, it provides an opportunity to brute force the first factor distinct
from the second factor.

This patch causes the pwdch extop to validate the OTP as well. This closes
the above attack vector. It is also, conveniently, the behavior most
users will probably expect.

https://fedorahosted.org/freeipa/ticket/4248
---
 .../ipa-slapi-plugins/ipa-pwd-extop/Makefile.am    |   1 +
 daemons/ipa-slapi-plugins/ipa-pwd-extop/authotp.c  | 129 +++++++++++++++++++++
 daemons/ipa-slapi-plugins/ipa-pwd-extop/authotp.h  |  64 ++++++++++
 .../ipa-pwd-extop/ipa_pwd_extop.c                  |  14 ++-
 daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h   |   3 +-
 daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c  |  95 +--------------
 6 files changed, 210 insertions(+), 96 deletions(-)
 create mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/authotp.c
 create mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/authotp.h

diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am b/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
index 4cf80ec802b40bb579a44fc9357c6a8119dab577..2045a6e6989115ba9e769a91ea38b768ed64c3f3 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
@@ -41,6 +41,7 @@ plugin_LTLIBRARIES = libipa_pwd_extop.la
 libipa_pwd_extop_la_LIBADD  = $(builddir)/../libotp/libotp.la
 libipa_pwd_extop_la_SOURCES = 		\
 	authcfg.c			\
+	authotp.c			\
 	common.c			\
 	encoding.c			\
 	prepost.c			\
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/authotp.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/authotp.c
new file mode 100644
index 0000000000000000000000000000000000000000..f309796cf7b0cbee0ec5151a0d934f2571a4781f
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/authotp.c
@@ -0,0 +1,129 @@
+/** 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 **/
+
+/*
+ * Authenticates creds against OTP tokens. Returns true when authentication
+ * completed successfully against a token OR when a user has no active tokens.
+ *
+ * WARNING: This function DOES NOT authenticate the first factor. Only the OTP
+ *          code is validated! You still need to validate the first factor.
+ *
+ * NOTE: When successful, this function truncates creds to remove the token
+ *       value at the end. This leaves only the password in creds for later
+ *       validation.
+ */
+
+#include "authotp.h"
+#include "authcfg.h"
+#include "ipapwd.h"
+
+#include <util.h>
+#include <libotp.h>
+
+#define OTP_VALIDATE_STEPS 3
+
+extern void *ipapwd_plugin_id;
+
+bool ipapwd_authenticate_otp(const char *bind_dn, Slapi_Entry *entry,
+                             struct berval *creds)
+{
+    uint32_t auth_types;
+
+    /* Get the configured authentication types. */
+    auth_types = authcfg_get_auth_types(entry);
+
+    /* If global disabled flag is set, just punt. */
+    if (auth_types & AUTHCFG_AUTH_TYPE_DISABLED)
+        return true;
+
+    /*
+     * IMPORTANT SECTION!
+     *
+     * This section handles authentication logic, so be careful!
+     *
+     * The basic idea of this section is:
+     * 1. If OTP is enabled, validate OTP.
+     * 2. If PWD is enabled or OTP succeeded, fall through to PWD validation.
+     */
+    if (auth_types & AUTHCFG_AUTH_TYPE_OTP) {
+        struct otptoken **tokens = NULL;
+        bool success = false;
+
+        LOG_PLUGIN_NAME(IPAPWD_PLUGIN_NAME,
+                        "Attempting OTP authentication for '%s'.\n", bind_dn);
+
+        /* Find all of the user's active tokens. */
+        tokens = otptoken_find(ipapwd_plugin_id, bind_dn, NULL, true, NULL);
+        if (tokens == NULL) {
+            slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME,
+                            "%s: can't find tokens for '%s'.\n",
+                            __func__, bind_dn);
+            return false;
+        }
+
+        /* If the user has no active tokens, succeed. */
+        success = tokens[0] == NULL;
+
+        /* Loop through each token. */
+        for (int i = 0; tokens[i] && !success; i++) {
+            /* Attempt authentication. */
+            success = otptoken_validate_string(tokens[i], OTP_VALIDATE_STEPS,
+                                               creds->bv_val, creds->bv_len,
+                                               true);
+
+            /* Truncate the password to remove the OTP code at the end. */
+            if (success) {
+                creds->bv_len -= otptoken_get_digits(tokens[i]);
+                creds->bv_val[creds->bv_len] = '\0';
+            }
+
+            slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
+                            "%s: token authentication %s "
+                            "(user: '%s', token: '%s\').\n", __func__,
+                            success ? "succeeded" : "failed", bind_dn,
+                            slapi_sdn_get_ndn(otptoken_get_sdn(tokens[i])));
+        }
+
+        otptoken_free_array(tokens);
+        if (success)
+            return true;
+    }
+
+    return auth_types & AUTHCFG_AUTH_TYPE_PASSWORD;
+}
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/authotp.h b/daemons/ipa-slapi-plugins/ipa-pwd-extop/authotp.h
new file mode 100644
index 0000000000000000000000000000000000000000..361a7b4a75ced1de1a0b4a934d3cdb64221a0e39
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/authotp.h
@@ -0,0 +1,64 @@
+/** 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 **/
+
+
+#ifndef AUTHOTP_H_
+#define AUTHOTP_H_
+
+#include <dirsrv/slapi-plugin.h>
+#include <stdbool.h>
+
+/**
+ * This function authenticates an OTP code at the end of the credentials.
+ *
+ * A return value of false means that an "invalid credentials" error should be
+ * returned to the client. A return value of true means that further validation
+ * of creds is required.
+ *
+ * If an OTP code is validated, this function returns true and truncates creds
+ * such that it no longer contains an OTP code. However, true may also be
+ * returned in cases where an OTP was not verified. This simply means that one
+ * must still verify creds.
+ *
+ * NOTE: the entry parameter MUST include the ipaUserAuthType for the user.
+ */
+bool ipapwd_authenticate_otp(const char *bind_dn, Slapi_Entry *entry,
+                             struct berval *creds);
+
+#endif /* AUTHOTP_H_ */
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
index d8af3915300384933e621ffe8adea8904588985d..301954e8f98d7ea871f97ad92625080569f8477c 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
@@ -40,6 +40,7 @@
 #include "ipapwd.h"
 #include "util.h"
 #include "authcfg.h"
+#include "authotp.h"
 
 /*
  * Password Modify - LDAP Extended Operation.
@@ -348,6 +349,7 @@ parse_req_done:
         ret = slapi_sdn_compare(bind_sdn, target_sdn);
         if (ret == 0) {
             Slapi_Value *cpw[2] = { NULL, NULL };
+            struct berval creds;
             Slapi_Value *pw;
             char *cur_pw;
 
@@ -357,8 +359,16 @@ parse_req_done:
                 goto free_and_return;
             }
 
-            /* if the user is changing his own password we need to check that
-             * oldPasswd matches the current password */
+            /* Validate OTP on the credentials. */
+            creds.bv_val = oldPasswd;
+            creds.bv_len = strlen(oldPasswd);
+            if (!ipapwd_authenticate_otp(dn, targetEntry, &creds)) {
+                LOG_TRACE("Invalid OTP!\n");
+                rc = LDAP_INVALID_CREDENTIALS;
+                goto free_and_return;
+            }
+
+            /* Validate that the credentials match the password. */
             cur_pw = slapi_entry_attr_get_charptr(targetEntry,
                                                   "userPassword");
             if (!cur_pw) {
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
index e18bf7bb641cc8010335cd2e466092262dc0c708..74db8293d2b5bbfd9217ffcc9f519a1516a2c763 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
@@ -41,9 +41,10 @@
 #  include <config.h>
 #endif
 
-#include <libotp.h>
+#include <dirsrv/slapi-plugin.h>
 
 #include <stdio.h>
+#include <stdbool.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
index 23c7cb18c9a1cb5256254f20080c5d9aaec25579..f9f1e8636b9e1b29e0ad1fca88c2bb8a1e70fbe8 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
@@ -64,13 +64,12 @@
 #include "util.h"
 #include "syncreq.h"
 #include "authcfg.h"
+#include "authotp.h"
 
 #define IPAPWD_OP_NULL 0
 #define IPAPWD_OP_ADD 1
 #define IPAPWD_OP_MOD 2
 
-#define OTP_VALIDATE_STEPS 3
-
 extern Slapi_PluginDesc ipapwd_plugin_desc;
 extern void *ipapwd_plugin_id;
 extern const char *ipa_realm_tree;
@@ -1126,96 +1125,6 @@ done:
     return 0;
 }
 
-/*
- * Authenticates creds against OTP tokens. Returns true when authentication
- * completed successfully against a token OR when a user has no active tokens.
- *
- * WARNING: This function DOES NOT authenticate the first factor. Only the OTP
- *          code is validated! You still need to validate the first factor.
- *
- * NOTE: When successful, this function truncates creds to remove the token
- *       value at the end. This leaves only the password in creds for later
- *       validation.
- */
-static bool ipapwd_do_otp_auth(const char *dn, Slapi_Entry *bind_entry,
-                               struct berval *creds)
-{
-    struct otptoken **tokens = NULL;
-    bool success = false;
-
-    /* Find all of the user's active tokens. */
-    tokens = otptoken_find(ipapwd_plugin_id, dn, NULL, true, NULL);
-    if (tokens == NULL) {
-        slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME,
-                        "%s: can't find tokens for '%s'.\n", __func__, dn);
-        return false;
-    }
-
-    /* If the user has no active tokens, succeed. */
-    success = tokens[0] == NULL;
-
-    /* Loop through each token. */
-    for (int i = 0; tokens[i] && !success; i++) {
-        /* Attempt authentication. */
-        success = otptoken_validate_string(tokens[i], OTP_VALIDATE_STEPS,
-                                           creds->bv_val, creds->bv_len, true);
-
-        /* Truncate the password to remove the OTP code at the end. */
-        if (success) {
-            creds->bv_len -= otptoken_get_digits(tokens[i]);
-            creds->bv_val[creds->bv_len] = '\0';
-        }
-
-        slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
-                        "%s: token authentication %s "
-                        "(user: '%s', token: '%s\').\n", __func__,
-                        success ? "succeeded" : "failed", dn,
-                        slapi_sdn_get_ndn(otptoken_get_sdn(tokens[i])));
-    }
-
-    otptoken_free_array(tokens);
-    return success;
-}
-
-/*
- * This function handles the bind functionality for OTP. The return value
- * indicates if the OTP portion of authentication was successful.
- *
- * NOTE: This function may modify creds. See explanation in the comment for
- *       ipapwd_do_otp_auth() above.
- */
-static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry,
-                                struct berval *creds)
-{
-    uint32_t auth_types;
-
-    /* Get the configured authentication types. */
-    auth_types = authcfg_get_auth_types(entry);
-
-    /* If global disabled flag is set, just punt. */
-    if (auth_types & AUTHCFG_AUTH_TYPE_DISABLED)
-        return true;
-
-    /*
-     * IMPORTANT SECTION!
-     *
-     * This section handles authentication logic, so be careful!
-     *
-     * The basic idea of this section is:
-     * 1. If OTP is enabled, validate OTP.
-     * 2. If PWD is enabled or OTP succeeded, fall through to PWD validation.
-     */
-
-    if (auth_types & AUTHCFG_AUTH_TYPE_OTP) {
-        LOG_PLUGIN_NAME(IPAPWD_PLUGIN_NAME,
-                        "Attempting OTP authentication for '%s'.\n", bind_dn);
-        if (ipapwd_do_otp_auth(bind_dn, entry, creds))
-            return true;
-    }
-
-    return auth_types & AUTHCFG_AUTH_TYPE_PASSWORD;
-}
-
 static int ipapwd_authenticate(const char *dn, Slapi_Entry *entry,
                                const struct berval *credentials)
 {
@@ -1446,7 +1355,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
 
     /* Try to do OTP first. */
     syncreq = sync_request_present(pb);
-    if (!syncreq && !ipapwd_pre_bind_otp(dn, entry, credentials)) {
+    if (!syncreq && !ipapwd_authenticate_otp(dn, entry, credentials)) {
         slapi_entry_free(entry);
         slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS,
                                NULL, NULL, 0, NULL);
-- 
1.9.0

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

Reply via email to