From: Margaret Cullen Date: Tue, 15 Aug 2023 19:03:47 +0000 (-0400) Subject: Add config variables for lookback code, and make TIME_STEP and OTP sixe configurable. X-Git-Tag: release_3_2_4~162 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c49bf2a34e9c1cef5badd1538ad483c94cb2d2af;p=thirdparty%2Ffreeradius-server.git Add config variables for lookback code, and make TIME_STEP and OTP sixe configurable. Signed-off-by: Margaret Cullen --- diff --git a/src/modules/rlm_totp/rlm_totp.c b/src/modules/rlm_totp/rlm_totp.c index 32328b0fcc8..68d666d8771 100644 --- a/src/modules/rlm_totp/rlm_totp.c +++ b/src/modules/rlm_totp/rlm_totp.c @@ -28,9 +28,37 @@ RCSID("$Id$") #include #include -#define TIME_STEP (30) -#define BACK_STEPS (1) -#define BACK_STEP_SECS (30) +#include + +/* Define a structure for the configuration variables */ +typedef struct rlm_totp_t { + char const *name; //!< name of this instance */ + 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 +} rlm_totp_t; + +#ifndef TESTING +/* Map configuration file names to internal variables */ +static const CONF_PARSER module_config[] = { + { "time_step", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_totp_t, time_step), "30" }, + { "otp_length", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_totp_t, otp_length), "6" }, + { "lookback_steps", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_totp_t, lookback_steps), "1" }, + { "lookback_interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_totp_t, lookback_interval), "30" }, + CONF_PARSER_TERMINATOR +}; + +#define TIME_STEP (inst->time_step) +#define OTP_LEN (inst->otp_length) +#define BACK_STEPS (inst->lookback_steps) +#define BACK_STEP_SECS (inst->lookback_interval) +#else +#define TIME_STEP (30) +#define OTP_LEN (8) +#define BACK_STEPS (1) +#define BACK_STEP_SECS (30) +#endif /* * RFC 4648 base32 decoding. @@ -144,23 +172,60 @@ static ssize_t base32_decode(uint8_t *out, size_t outlen, char const *in) return b - out; } + #ifndef TESTING #define TESTING_UNUSED -#define LEN 6 -#define PRINT "%06u" -#define DIV 1000000 - -#else -#define LEN 8 -#define PRINT "%08u" -#define DIV 100000000 - +#else /* TESTING */ #undef RDEBUG3 #define RDEBUG3(fmt, ...) printf(fmt "\n", ## __VA_ARGS__) #define TESTING_UNUSED UNUSED #endif +#ifndef TESTING +static int mod_bootstrap(CONF_SECTION *conf, void *instance) +{ + rlm_totp_t *inst = instance; + + inst->name = cf_section_name2(conf); + if (!inst->name) { + inst->name = cf_section_name1(conf); + } + + return 0; +} + +/* + * Do any per-module initialization that is separate to each + * configured instance of the module. e.g. set up connections + * to external databases, read configuration files, set up + * dictionary entries, etc. + * + * If configuration information is given in the config section + * that must be referenced in later calls, store a handle to it + * in *instance otherwise put a null pointer there. + */ +static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance) +{ + rlm_totp_t *inst = instance; + + FR_INTEGER_BOUND_CHECK("time_step", inst->time_step, >=, 5); + FR_INTEGER_BOUND_CHECK("time_step", inst->time_step, <=, 120); + + FR_INTEGER_BOUND_CHECK("lookback_steps", inst->lookback_steps, >=, 1); + FR_INTEGER_BOUND_CHECK("lookback_steps", inst->lookback_steps, <=, 10); + + FR_INTEGER_BOUND_CHECK("lookback_interval", inst->lookback_interval, <=, inst->time_step); + + FR_INTEGER_BOUND_CHECK("otp_length", inst->otp_length, >=, 6); + FR_INTEGER_BOUND_CHECK("otp_length", inst->otp_length, <=, 8); + + if (inst->otp_length == 7) inst->otp_length = 8; + + return 0; +} +#endif + /* * Implement RFC 6238 TOTP algorithm. * @@ -168,16 +233,19 @@ static ssize_t base32_decode(uint8_t *out, size_t outlen, char const *in) * for 8-character challenges, and not for 6 character * challenges! */ -static int totp_cmp(TESTING_UNUSED REQUEST *request, time_t now, uint8_t const *key, size_t keylen, char const *totp) +static int totp_cmp(TESTING_UNUSED REQUEST *request, time_t now, uint8_t const *key, size_t keylen, char const *totp, TESTING_UNUSED void *instance) { +#ifndef TESTING + rlm_totp_t *inst = instance; +#endif + time_t then; + unsigned int i; uint8_t offset; uint32_t challenge; uint64_t padded; char buffer[9]; uint8_t data[8]; uint8_t digest[SHA1_DIGEST_LENGTH]; - time_t then; - int i; /* * First try to authenticate against the current OTP, then step @@ -185,6 +253,7 @@ static int totp_cmp(TESTING_UNUSED REQUEST *request, time_t now, uint8_t const * * to authenticate properly in cases of long transit delay, as * described in RFC 6238, secion 5.2. */ + for (i = 0, then = now; i <= BACK_STEPS; i++, then -= BACK_STEP_SECS) { padded = (uint64_t) then / TIME_STEP; data[0] = padded >> 56; @@ -217,13 +286,14 @@ static int totp_cmp(TESTING_UNUSED REQUEST *request, time_t now, uint8_t const * /* * The token is the last 6 digits in the number (or 8 for testing).. */ - snprintf(buffer, sizeof(buffer), PRINT, challenge % DIV); + snprintf(buffer, sizeof(buffer), ((OTP_LEN == 6) ? "%06u" : "%08u"), + challenge % ((OTP_LEN == 6) ? 1000000 : 100000000)); RDEBUG3("Now: %zu, Then: %zu", (size_t) now, (size_t) then); RDEBUG3("Expected %s", buffer); RDEBUG3("Received %s", totp); - if (rad_digest_cmp((uint8_t const *) buffer, (uint8_t const *) totp, LEN) == 0) + if (rad_digest_cmp((uint8_t const *) buffer, (uint8_t const *) totp, OTP_LEN) == 0) return 0; } return 1; @@ -234,7 +304,7 @@ static int totp_cmp(TESTING_UNUSED REQUEST *request, time_t now, uint8_t const * /* * Do the authentication */ -static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQUEST *request) +static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request) { VALUE_PAIR *vp, *password; uint8_t const *key; @@ -245,8 +315,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU password = fr_pair_find_by_num(request->packet->vps, PW_TOTP_PASSWORD, 0, TAG_ANY); if (!password) return RLM_MODULE_NOOP; - if (password->vp_length != 6) { - REDEBUG("TOTP-Password has incorrect length %d", (int) password->vp_length); + if ((password->vp_length != 6) && (password->vp_length != 8)) { + RDEBUG("TOTP-Password has incorrect length %d", (int) password->vp_length); return RLM_MODULE_FAIL; } @@ -262,8 +332,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU ssize_t len; vp = fr_pair_find_by_num(request->config, PW_TOTP_SECRET, 0, TAG_ANY); - if (!vp) return RLM_MODULE_NOOP; - + if (!vp) { + RDEBUG("TOTP mod_authenticate() did not receive a TOTP-Secret"); + return RLM_MODULE_NOOP; + } len = base32_decode(buffer, sizeof(buffer), vp->vp_strvalue); if (len < 0) { REDEBUG("TOTP-Secret cannot be decoded"); @@ -274,7 +346,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU keylen = len; } - if (totp_cmp(request, now, key, keylen, password->vp_strvalue) == 0) + if (totp_cmp(request, now, key, keylen, password->vp_strvalue, instance) == 0) { return RLM_MODULE_OK; } return RLM_MODULE_FAIL; @@ -295,6 +367,10 @@ module_t rlm_totp = { .magic = RLM_MODULE_INIT, .name = "totp", .type = RLM_TYPE_THREAD_SAFE, + .inst_size = sizeof(rlm_totp_t), + .config = module_config, + .bootstrap = mod_bootstrap, + .instantiate = mod_instantiate, .methods = { [MOD_AUTHENTICATE] = mod_authenticate, }, @@ -329,7 +405,7 @@ int main(int argc, char **argv) } /* - * TOTP