The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_totp.c
Go to the documentation of this file.
1 /*
2  * This program is is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: 8a292c3a3b7f06e78b647e9f4b420b34f42efbc9 $
19  * @file rlm_totp.c
20  * @brief Execute commands and parse the results.
21  *
22  * @copyright 2021 The FreeRADIUS server project
23  * @copyright 2021 Network RADIUS SAS (legal@networkradius.com)
24  */
25 RCSID("$Id: 8a292c3a3b7f06e78b647e9f4b420b34f42efbc9 $")
26 
27 #include <freeradius-devel/server/base.h>
28 #include <freeradius-devel/server/module_rlm.h>
29 #include <freeradius-devel/unlang/interpret.h>
30 #include <freeradius-devel/util/base32.h>
31 
32 #include <freeradius-devel/unlang/call_env.h>
33 
34 #include "totp.h"
35 
36 typedef struct {
41 
42 static const call_env_method_t method_env = {
44  .env = (call_env_parser_t[]) {
46  .pair.dflt = "&control.TOTP.Secret", .pair.dflt_quote = T_BARE_WORD },
47 
49  .pair.dflt = "&control.TOTP.key", .pair.dflt_quote = T_BARE_WORD },
50 
52  .pair.dflt = "&request.TOTP.From-User", .pair.dflt_quote = T_BARE_WORD },
53 
55  }
56 };
57 
58 /* Define a structure for the configuration variables */
59 typedef struct rlm_totp_t {
60  char const *name; //!< name of this instance */
61  fr_totp_t totp; //! configuration entries passed to libfreeradius-totp
63 
64 /* Map configuration file names to internal variables */
65 static const conf_parser_t module_config[] = {
66  { FR_CONF_OFFSET("time_step", rlm_totp_t, totp.time_step), .dflt = "30" },
67  { FR_CONF_OFFSET("otp_length", rlm_totp_t, totp.otp_length), .dflt = "6" },
68  { FR_CONF_OFFSET("lookback_steps", rlm_totp_t, totp.lookback_steps), .dflt = "1" },
69  { FR_CONF_OFFSET("lookback_interval", rlm_totp_t, totp.lookback_interval), .dflt = "30" },
70  { FR_CONF_OFFSET("lookforward_steps", rlm_totp_t, totp.lookforward_steps), .dflt = "0" },
72 };
73 
74 static int mod_bootstrap(module_inst_ctx_t const *mctx)
75 {
76  rlm_totp_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_totp_t);
77  CONF_SECTION *conf = mctx->inst->conf;
78 
79  inst->name = cf_section_name2(conf);
80  if (!inst->name) inst->name = cf_section_name1(conf);
81 
82  return 0;
83 }
84 
85 /*
86  * Do any per-module initialization that is separate to each
87  * configured instance of the module. e.g. set up connections
88  * to external databases, read configuration files, set up
89  * dictionary entries, etc.
90  *
91  * If configuration information is given in the config section
92  * that must be referenced in later calls, store a handle to it
93  * in *instance otherwise put a null pointer there.
94  */
95 static int mod_instantiate(module_inst_ctx_t const *mctx)
96 {
97  rlm_totp_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_totp_t);
98 
99  FR_INTEGER_BOUND_CHECK("time_step", inst->totp.time_step, >=, 5);
100  FR_INTEGER_BOUND_CHECK("time_step", inst->totp.time_step, <=, 120);
101 
102  FR_INTEGER_BOUND_CHECK("lookback_steps", inst->totp.lookback_steps, >=, 1);
103  FR_INTEGER_BOUND_CHECK("lookback_steps", inst->totp.lookback_steps, <=, 10);
104 
105  FR_INTEGER_BOUND_CHECK("lookforward_steps", inst->totp.lookforward_steps, <=, 10);
106 
107  FR_INTEGER_BOUND_CHECK("lookback_interval", inst->totp.lookback_interval, <=, inst->totp.time_step);
108 
109  FR_INTEGER_BOUND_CHECK("otp_length", inst->totp.otp_length, >=, 6);
110  FR_INTEGER_BOUND_CHECK("otp_length", inst->totp.otp_length, <=, 8);
111 
112  if (inst->totp.otp_length == 7) inst->totp.otp_length = 8;
113 
114  return 0;
115 }
116 
117 /*
118  * Do the authentication
119  */
120 static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
121 {
122  rlm_totp_call_env_t *env_data = talloc_get_type_abort(mctx->env_data, rlm_totp_call_env_t);
124  fr_value_box_t *user_password = &env_data->user_password;
125  fr_value_box_t *secret = &env_data->secret;
126  fr_value_box_t *key = &env_data->key;
127 
128  uint8_t const *our_key;
129  size_t our_keylen;
130  uint8_t buffer[80]; /* multiple of 5*8 characters */
131 
132  if (fr_type_is_null(user_password->type)) RETURN_MODULE_NOOP;
133 
134  if (user_password->vb_length == 0) {
135  RWARN("TOTP.From-User is empty");
137  }
138 
139  if ((user_password->vb_length != 6) && (user_password->vb_length != 8)) {
140  RWARN("TOTP.From-User has incorrect length. Expected 6 or 8, got %zu", user_password->vb_length);
142  }
143 
144  /*
145  * Look for the raw key first.
146  */
147  if (!fr_type_is_null(key->type)) {
148  our_key = key->vb_octets;
149  our_keylen = key->vb_length;
150 
151  } else {
152  ssize_t len;
153 
155 
156  len = fr_base32_decode(&FR_DBUFF_TMP((uint8_t *) buffer, sizeof(buffer)), &FR_SBUFF_IN(secret->vb_strvalue, secret->vb_length), true, true);
157  if (len < 0) {
158  RERROR("TOTP.Secret cannot be decoded");
160  }
161 
162  our_key = buffer;
163  our_keylen = len;
164  }
165 
166  switch (fr_totp_cmp(&inst->totp, request, fr_time_to_sec(request->packet->timestamp), our_key, our_keylen, user_password->vb_strvalue)) {
167  case 0:
169 
170  case -2:
172 
173  default:
175  }
176 }
177 
178 /*
179  * The module name should be the only globally exported symbol.
180  * That is, everything else should be 'static'.
181  *
182  * If the module needs to temporarily modify it's instantiation
183  * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
184  * The server will then take care of ensuring that the module
185  * is single-threaded.
186  */
187 extern module_rlm_t rlm_totp;
189  .common = {
190  .magic = MODULE_MAGIC_INIT,
191  .name = "totp",
192  .flags = MODULE_TYPE_THREAD_SAFE,
193  .inst_size = sizeof(rlm_totp_t),
195  .bootstrap = mod_bootstrap,
197  },
198  .method_names = (module_method_name_t[]){
199  { .name1 = "authenticate", .name2 = CF_IDENT_ANY, .method = mod_authenticate, .method_env = &method_env },
201  }
202 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
static int const char char buffer[256]
Definition: acutest.h:574
#define fr_base32_decode(_out, _in, _expect_padding, _no_trailing)
Definition: base32.h:69
#define RCSID(id)
Definition: build.h:444
#define CALL_ENV_TERMINATOR
Definition: call_env.h:212
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition: call_env.h:216
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition: call_env.h:78
#define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field)
Specify a call_env_parser_t which writes out runtime results to the specified field.
Definition: call_env.h:316
Per method call config.
Definition: call_env.h:171
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition: cf_parse.h:486
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:563
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition: cf_util.c:1126
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition: cf_util.c:1112
#define CF_IDENT_ANY
Definition: cf_util.h:78
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:509
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
CONF_SECTION *_CONST conf
Module's instance configuration.
Definition: dl_module.h:166
#define RWARN(fmt,...)
Definition: log.h:297
#define RERROR(fmt,...)
Definition: log.h:298
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
void * env_data
Per call environment data.
Definition: module_ctx.h:44
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:51
Specifies a module method identifier.
Definition: module_method.c:36
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
static const conf_parser_t config[]
Definition: base.c:188
static char * secret
Definition: radclient-ng.c:69
static rs_t * conf
Definition: radsniff.c:53
#define RETURN_MODULE_REJECT
Definition: rcode.h:55
#define RETURN_MODULE_NOOP
Definition: rcode.h:62
#define RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1312
static const call_env_method_t method_env
Definition: rlm_totp.c:42
module_rlm_t rlm_totp
Definition: rlm_totp.c:188
fr_value_box_t secret
Definition: rlm_totp.c:37
static unlang_action_t mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: rlm_totp.c:120
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition: rlm_totp.c:74
fr_value_box_t user_password
Definition: rlm_totp.c:39
fr_value_box_t key
Definition: rlm_totp.c:38
fr_totp_t totp
Definition: rlm_totp.c:61
char const * name
name of this instance *‍/
Definition: rlm_totp.c:60
static const conf_parser_t module_config[]
Definition: rlm_totp.c:65
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_totp.c:95
struct rlm_totp_t rlm_totp_t
#define FR_SBUFF_IN(_start, _len_or_end)
@ MODULE_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: module.h:49
#define MODULE_NAME_TERMINATOR
Definition: module.h:135
RETURN_MODULE_FAIL
eap_aka_sim_process_conf_t * inst
#define talloc_get_type_abort_const
Definition: talloc.h:270
static int64_t fr_time_to_sec(fr_time_t when)
Convert an fr_time_t (internal time) to number of sec since the unix epoch (wallclock time)
Definition: time.h:729
@ T_BARE_WORD
Definition: token.h:120
int fr_totp_cmp(fr_totp_t const *cfg, request_t *request, time_t now, uint8_t const *key, size_t keylen, char const *totp)
Implement RFC 6238 TOTP algorithm (HMAC-SHA1).
Definition: totp.c:71
Definition: totp.h:33
#define fr_type_is_null(_x)
Definition: types.h:326
int nonnull(2, 5))