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: 9e6800ec4bfaf776927b5adfd62a4e698a5a2c94 $
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: 9e6800ec4bfaf776927b5adfd62a4e698a5a2c94 $")
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  fr_totp_t totp; //! configuration entries passed to libfreeradius-totp
62 
63 /* Map configuration file names to internal variables */
64 static const conf_parser_t module_config[] = {
65  { FR_CONF_OFFSET("time_step", rlm_totp_t, totp.time_step), .dflt = "30" },
66  { FR_CONF_OFFSET("otp_length", rlm_totp_t, totp.otp_length), .dflt = "6" },
67  { FR_CONF_OFFSET("lookback_steps", rlm_totp_t, totp.lookback_steps), .dflt = "1" },
68  { FR_CONF_OFFSET("lookback_interval", rlm_totp_t, totp.lookback_interval), .dflt = "30" },
69  { FR_CONF_OFFSET("lookforward_steps", rlm_totp_t, totp.lookforward_steps), .dflt = "0" },
71 };
72 
73 /*
74  * Do the authentication
75  */
76 static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
77 {
78  rlm_totp_call_env_t *env_data = talloc_get_type_abort(mctx->env_data, rlm_totp_call_env_t);
80  fr_value_box_t *user_password = &env_data->user_password;
81  fr_value_box_t *secret = &env_data->secret;
82  fr_value_box_t *key = &env_data->key;
83 
84  uint8_t const *our_key;
85  size_t our_keylen;
86  uint8_t buffer[80]; /* multiple of 5*8 characters */
87 
88  if (fr_type_is_null(user_password->type)) RETURN_MODULE_NOOP;
89 
90  if (user_password->vb_length == 0) {
91  RWARN("TOTP.From-User is empty");
93  }
94 
95  if ((user_password->vb_length != 6) && (user_password->vb_length != 8)) {
96  RWARN("TOTP.From-User has incorrect length. Expected 6 or 8, got %zu", user_password->vb_length);
98  }
99 
100  /*
101  * Look for the raw key first.
102  */
103  if (!fr_type_is_null(key->type)) {
104  our_key = key->vb_octets;
105  our_keylen = key->vb_length;
106 
107  } else {
108  ssize_t len;
109 
111 
112  len = fr_base32_decode(&FR_DBUFF_TMP((uint8_t *) buffer, sizeof(buffer)), &FR_SBUFF_IN(secret->vb_strvalue, secret->vb_length), true, true);
113  if (len < 0) {
114  RERROR("TOTP.Secret cannot be decoded");
116  }
117 
118  our_key = buffer;
119  our_keylen = len;
120  }
121 
122  switch (fr_totp_cmp(&inst->totp, request, fr_time_to_sec(request->packet->timestamp), our_key, our_keylen, user_password->vb_strvalue)) {
123  case 0:
125 
126  case -2:
128 
129  default:
131  }
132 }
133 
134 /*
135  * Do any per-module initialization that is separate to each
136  * configured instance of the module. e.g. set up connections
137  * to external databases, read configuration files, set up
138  * dictionary entries, etc.
139  *
140  * If configuration information is given in the config section
141  * that must be referenced in later calls, store a handle to it
142  * in *instance otherwise put a null pointer there.
143  */
144 static int mod_instantiate(module_inst_ctx_t const *mctx)
145 {
146  rlm_totp_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_totp_t);
147 
148  FR_INTEGER_BOUND_CHECK("time_step", inst->totp.time_step, >=, 5);
149  FR_INTEGER_BOUND_CHECK("time_step", inst->totp.time_step, <=, 120);
150 
151  FR_INTEGER_BOUND_CHECK("lookback_steps", inst->totp.lookback_steps, >=, 1);
152  FR_INTEGER_BOUND_CHECK("lookback_steps", inst->totp.lookback_steps, <=, 10);
153 
154  FR_INTEGER_BOUND_CHECK("lookforward_steps", inst->totp.lookforward_steps, <=, 10);
155 
156  FR_INTEGER_BOUND_CHECK("lookback_interval", inst->totp.lookback_interval, <=, inst->totp.time_step);
157 
158  FR_INTEGER_BOUND_CHECK("otp_length", inst->totp.otp_length, >=, 6);
159  FR_INTEGER_BOUND_CHECK("otp_length", inst->totp.otp_length, <=, 8);
160 
161  if (inst->totp.otp_length == 7) inst->totp.otp_length = 8;
162 
163  return 0;
164 }
165 
166 /*
167  * The module name should be the only globally exported symbol.
168  * That is, everything else should be 'static'.
169  *
170  * If the module needs to temporarily modify it's instantiation
171  * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
172  * The server will then take care of ensuring that the module
173  * is single-threaded.
174  */
175 extern module_rlm_t rlm_totp;
177  .common = {
178  .magic = MODULE_MAGIC_INIT,
179  .name = "totp",
180  .inst_size = sizeof(rlm_totp_t),
183  },
184  .method_group = {
185  .bindings = (module_method_binding_t[]){
186  { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate, .method_env = &method_env },
188  }
189  }
190 };
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:481
#define CALL_ENV_TERMINATOR
Definition: call_env.h:231
#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:235
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition: call_env.h:80
#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:335
Per method call config.
Definition: call_env.h:175
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition: cf_parse.h:487
#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:564
#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:514
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
#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
module_instance_t const * mi
Instance of the module being instantiated.
Definition: module_ctx.h:42
module_instance_t * mi
Instance of the module being instantiated.
Definition: module_ctx.h:51
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:50
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:39
static const conf_parser_t config[]
Definition: base.c:183
static char * secret
Definition: radclient-ng.c:69
#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:1302
static const call_env_method_t method_env
Definition: rlm_totp.c:42
module_rlm_t rlm_totp
Definition: rlm_totp.c:176
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:76
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:60
static const conf_parser_t module_config[]
Definition: rlm_totp.c:64
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_totp.c:144
struct rlm_totp_t rlm_totp_t
#define FR_SBUFF_IN(_start, _len_or_end)
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition: section.h:40
void * data
Module's instance data.
Definition: module.h:271
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition: module.h:151
Named methods exported by a module.
Definition: module.h:173
RETURN_MODULE_FAIL
eap_aka_sim_process_conf_t * inst
#define talloc_get_type_abort_const
Definition: talloc.h:282
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:731
@ 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))