]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add TOTP functionality in libfreeradius-totp.a
authorJorge Pereira <jpereira@freeradius.org>
Thu, 14 Sep 2023 01:09:02 +0000 (22:09 -0300)
committerAlan T. DeKok <aland@freeradius.org>
Wed, 27 Sep 2023 14:27:37 +0000 (10:27 -0400)
And add it to the packages

debian/libfreeradius4.install
redhat/freeradius.spec
src/modules/rlm_totp/all.mk
src/modules/rlm_totp/libfreeradius-totp.mk [new file with mode: 0644]
src/modules/rlm_totp/rlm_totp.mk [new file with mode: 0644]
src/modules/rlm_totp/totp.c [new file with mode: 0644]
src/modules/rlm_totp/totp.h [new file with mode: 0644]

index 44754cb059c10c3c25a3b8368ed66b3ffa0e446a..9d2aa78d1bc47cd549e0dfbae925aaf66335b6d3 100644 (file)
@@ -14,6 +14,7 @@ usr/lib/freeradius/libfreeradius-sim.so
 usr/lib/freeradius/libfreeradius-tacacs.so
 usr/lib/freeradius/libfreeradius-tftp.so
 usr/lib/freeradius/libfreeradius-tls.so
+usr/lib/freeradius/libfreeradius-totp.so
 usr/lib/freeradius/libfreeradius-unlang.so
 usr/lib/freeradius/libfreeradius-util.so
 usr/lib/freeradius/libfreeradius-vmps.so
index 05ff79eb41839fab0926c6176bc4e40a7ace18c4..a783fe7717f6dfff2397f6c626a18621a2a98815 100644 (file)
@@ -946,6 +946,7 @@ fi
 %{_libdir}/freeradius/libfreeradius-tacacs.so
 %{_libdir}/freeradius/libfreeradius-tftp.so
 %{_libdir}/freeradius/libfreeradius-tls.so
+%{_libdir}/freeradius/libfreeradius-totp.so
 %{_libdir}/freeradius/libfreeradius-unlang.so
 %{_libdir}/freeradius/libfreeradius-vmps.so
 
index 660d816c7d0386faa8ed7dabef5ffb0797a96aa9..115808db7e89899df50a3dff22afefa10d9a636a 100644 (file)
@@ -1,6 +1,2 @@
-TARGETNAME     := rlm_totp
+SUBMAKEFILES := rlm_totp.mk libfreeradius-totp.mk
 
-TARGET         := $(TARGETNAME)$(L)
-SOURCES                := $(TARGETNAME).c
-
-LOG_ID_LIB     = 53
diff --git a/src/modules/rlm_totp/libfreeradius-totp.mk b/src/modules/rlm_totp/libfreeradius-totp.mk
new file mode 100644 (file)
index 0000000..0b14f3a
--- /dev/null
@@ -0,0 +1,4 @@
+TARGETNAME  := libfreeradius-totp
+TARGET      := $(TARGETNAME)$(L)
+
+SOURCES            := totp.c
diff --git a/src/modules/rlm_totp/rlm_totp.mk b/src/modules/rlm_totp/rlm_totp.mk
new file mode 100644 (file)
index 0000000..660d816
--- /dev/null
@@ -0,0 +1,6 @@
+TARGETNAME     := rlm_totp
+
+TARGET         := $(TARGETNAME)$(L)
+SOURCES                := $(TARGETNAME).c
+
+LOG_ID_LIB     = 53
diff --git a/src/modules/rlm_totp/totp.c b/src/modules/rlm_totp/totp.c
new file mode 100644 (file)
index 0000000..94d4f6c
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ *   This program is 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 2 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, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file totp.c
+ * @brief Common function for TOTP validation.
+ *
+ * @copyright 2023 The FreeRADIUS server project
+ */
+#include <freeradius-devel/util/sha1.h>
+#include <freeradius-devel/util/debug.h>
+
+#include "totp.h"
+
+/** Implement RFC 6238 TOTP algorithm (HMAC-SHA1).
+ *
+ *     Appendix B has test vectors.  Note that the test vectors are
+ *     for 8-character challenges, and not for 6 character
+ *     challenges!
+ *
+ * @param[in] cfg      Instance of fr_totp_t
+ * @param[in] key      Key to decrypt.
+ * @param[in] keylen   Length of key field.
+ * @param[in] totp     TOTP password entered by the user.
+ * @return
+ *     -  0  On Success
+ *     - -1  On Failure
+ */
+int fr_totp_cmp(fr_totp_t const *cfg, time_t now, uint8_t const *key, size_t keylen, char const *totp)
+{
+       time_t then;
+       unsigned int i;
+       uint8_t offset;
+       uint32_t challenge;
+       uint64_t padded;
+       uint8_t data[8];
+       uint8_t digest[SHA1_DIGEST_LENGTH];
+       char buffer[9];
+       char buf_now[32], buf_then[32];
+
+       fr_assert(cfg != NULL);
+       fr_assert(cfg->otp_length == 6 || cfg->otp_length == 8);
+       fr_assert(key != NULL);
+       fr_assert(totp != NULL);
+
+       if (!cfg) {
+               fr_strerror_const("Invalid 'cfg' parameter value.");
+               return -1;
+       }
+
+       if (cfg->otp_length != 6 && cfg->otp_length != 8) {
+               fr_strerror_const("The 'cfg->opt_length' has incorrect length. Expected 6 or 8.");
+               return -1;
+       }
+
+       if (keylen < 1) {
+               fr_strerror_const("Invalid 'keylen' parameter value.");
+               return -1;
+       }
+
+       if (!totp || strlen(totp) < 1) {
+               fr_strerror_const("Invalid 'totp' parameter value.");
+               return -1;
+       }
+
+       /*
+        *      First try to authenticate against the current OTP, then step
+        *      back in increments of BACK_STEP_SECS, up to BACK_STEPS times,
+        *      to authenticate properly in cases of long transit delay, as
+        *      described in RFC 6238, secion 5.2.
+        */
+
+       for (i = 0, then = now; i <= cfg->lookback_steps; i++, then -= cfg->lookback_steps) {
+               fr_sbuff_t snow = FR_SBUFF_IN(buf_now, sizeof(buf_now));
+               fr_sbuff_t sthen = FR_SBUFF_IN(buf_then, sizeof(buf_then));
+
+               padded = ((uint64_t) now) / cfg->time_step;
+               data[0] = padded >> 56;
+               data[1] = padded >> 48;
+               data[2] = padded >> 40;
+               data[3] = padded >> 32;
+               data[4] = padded >> 24;
+               data[5] = padded >> 16;
+               data[6] = padded >> 8;
+               data[7] = padded & 0xff;
+
+               /*
+                *      Encrypt the network order time with the key.
+                */
+               fr_hmac_sha1(digest, data, 8, key, keylen);
+
+               /*
+                *      Take the least significant 4 bits.
+                */
+               offset = digest[SHA1_DIGEST_LENGTH - 1] & 0x0f;
+
+               /*
+                *      Grab the 32bits at "offset", and drop the high bit.
+                */
+               challenge = (digest[offset] & 0x7f) << 24;
+               challenge |= digest[offset + 1] << 16;
+               challenge |= digest[offset + 2] << 8;
+               challenge |= digest[offset + 3];
+
+               /*
+                *      The token is the last 6 digits in the number (or 8 for testing)..
+                */
+               snprintf(buffer, sizeof(buffer), ((cfg->otp_length == 6) ? "%06u" : "%08u"),
+                                challenge % ((cfg->otp_length == 6) ? 1000000 : 100000000));
+
+               fr_time_strftime_local(&snow, fr_time_wrap(now), "%a %b %d %H:%M:%S %Y");
+               fr_time_strftime_local(&sthen, fr_time_wrap(then), "%a %b %d %H:%M:%S %Y");
+
+               DEBUG3("Now: %zu (%s), Then: %zu (%s)", (size_t) now, fr_sbuff_start(&snow), (size_t) then, fr_sbuff_start(&sthen));
+               DEBUG3("Expected %s", buffer);
+               DEBUG3("Received %s", totp);
+
+               if (fr_digest_cmp((uint8_t const *) buffer, (uint8_t const *) totp, cfg->otp_length) == 0) return 0;
+       }
+
+       return -1;
+}
diff --git a/src/modules/rlm_totp/totp.h b/src/modules/rlm_totp/totp.h
new file mode 100644 (file)
index 0000000..7343bdb
--- /dev/null
@@ -0,0 +1,44 @@
+#pragma once
+/*
+ *   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 2 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, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file lib/totp/base.h
+ * @brief Common functions for TOTP library
+ *
+ * @copyright 2023 The FreeRADIUS server project
+ */
+RCSIDH(totp_h, "$Id$")
+
+#include <freeradius-devel/server/request.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+       uint32_t time_step;             //!< seconds
+       uint32_t otp_length;            //!< forced to 6 or 8
+       uint32_t lookback_steps;        //!< number of steps to look back
+       uint32_t lookback_interval;     //!< interval in seconds between steps
+} fr_totp_t;
+
+int fr_totp_cmp(fr_totp_t const *cfg, time_t now, uint8_t const *key, size_t keylen, char const *totp);
+
+#ifdef __cplusplus
+}
+#endif