From: Jorge Pereira Date: Thu, 14 Sep 2023 01:09:02 +0000 (-0300) Subject: Add TOTP functionality in libfreeradius-totp.a X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=249a690cf87964013eb72c102e37110baa08adf6;p=thirdparty%2Ffreeradius-server.git Add TOTP functionality in libfreeradius-totp.a And add it to the packages --- diff --git a/debian/libfreeradius4.install b/debian/libfreeradius4.install index 44754cb059c..9d2aa78d1bc 100644 --- a/debian/libfreeradius4.install +++ b/debian/libfreeradius4.install @@ -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 diff --git a/redhat/freeradius.spec b/redhat/freeradius.spec index 05ff79eb418..a783fe7717f 100644 --- a/redhat/freeradius.spec +++ b/redhat/freeradius.spec @@ -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 diff --git a/src/modules/rlm_totp/all.mk b/src/modules/rlm_totp/all.mk index 660d816c7d0..115808db7e8 100644 --- a/src/modules/rlm_totp/all.mk +++ b/src/modules/rlm_totp/all.mk @@ -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 index 00000000000..0b14f3a834e --- /dev/null +++ b/src/modules/rlm_totp/libfreeradius-totp.mk @@ -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 index 00000000000..660d816c7d0 --- /dev/null +++ b/src/modules/rlm_totp/rlm_totp.mk @@ -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 index 00000000000..94d4f6cb414 --- /dev/null +++ b/src/modules/rlm_totp/totp.c @@ -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 +#include + +#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 index 00000000000..7343bdb8667 --- /dev/null +++ b/src/modules/rlm_totp/totp.h @@ -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 + +#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