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.
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.
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
19 * @file rlm_logintime.c
20 * @brief Allow login only during a given timeslot.
22 * @copyright 2001,2006 The FreeRADIUS server project
23 * @copyright 2004 Kostas Kalevras <kkalev@noc.ntua.gr>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
33 int timestr_match(char const *, time_t);
36 * Define a structure for our module configuration.
38 * These variables do not need to be in a structure, but it's
39 * a lot cleaner to do so, and a pointer to the structure can
40 * be used as the instance handle.
42 typedef struct rlm_logintime_t {
47 * A mapping of configuration file names to internal variables.
49 * Note that the string is dynamically allocated, so it MUST
50 * be freed. When the configuration file parse re-reads the string,
51 * it free's the old one, and strdup's the new one, placing the pointer
52 * to the strdup'd string into 'config.string'. This gets around
55 static const CONF_PARSER module_config[] = {
56 { "minimum-timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_logintime_t, min_time), NULL },
57 { "minimum_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_logintime_t, min_time), "60" },
58 CONF_PARSER_TERMINATOR
63 * Compare the current time to a range.
65 static int timecmp(UNUSED void *instance, REQUEST *req, UNUSED VALUE_PAIR *request, VALUE_PAIR *check,
66 UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
69 * If there's a request, use that timestamp.
71 if (timestr_match(check->vp_strvalue,
72 req ? req->timestamp : time(NULL)) >= 0)
82 static int time_of_day(UNUSED void *instance, REQUEST *req, UNUSED VALUE_PAIR *request, VALUE_PAIR *check,
83 UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
91 * Must be called with a request pointer.
95 if (strspn(check->vp_strvalue, "0123456789: ") != strlen(check->vp_strvalue)) {
96 DEBUG("rlm_logintime: Bad Time-Of-Day value \"%s\"",
101 tm = localtime_r(&req->timestamp, &s_tm);
102 hhmmss = (tm->tm_hour * 3600) + (tm->tm_min * 60) + tm->tm_sec;
105 * Time of day is a 24-hour clock
107 p = check->vp_strvalue;
110 if ((scan > 23) || !p) {
111 DEBUG("rlm_logintime: Bad Time-Of-Day value \"%s\"",
120 DEBUG("rlm_logintime: Bad Time-Of-Day value \"%s\"",
130 DEBUG("rlm_logintime: Bad Time-Of-Day value \"%s\"",
137 fprintf(stderr, "returning %d - %d\n",
140 return hhmmss - when;
144 * Check if account has expired, and if user may login now.
146 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
148 rlm_logintime_t *inst = instance;
149 VALUE_PAIR *ends, *timeout;
152 ends = fr_pair_find_by_num(request->config, PW_LOGIN_TIME, 0, TAG_ANY);
154 return RLM_MODULE_NOOP;
158 * Authentication is OK. Now see if this user may login at this time of the day.
160 RDEBUG("Checking Login-Time");
163 * Compare the time the request was received with the current Login-Time value
165 left = timestr_match(ends->vp_strvalue, request->timestamp);
166 if (left < 0) return RLM_MODULE_USERLOCK; /* outside of the allowed time */
169 * Do nothing, login time is not controlled (unendsed).
172 return RLM_MODULE_OK;
176 * The min_time setting is to deal with NAS that won't allow Session-Timeout values below a certain value
177 * For example some Alcatel Lucent products won't allow a Session-Timeout < 300 (5 minutes).
179 * We don't know were going to get another chance to lock out the user, so we need to do it now.
181 if (left < (int) inst->min_time) {
182 REDEBUG("Login outside of allowed time-slot (session end %s, with lockout %i seconds before)",
183 ends->vp_strvalue, inst->min_time);
185 return RLM_MODULE_USERLOCK;
188 /* else left > inst->min_time */
191 * There's time left in the users session, inform the NAS by including a Session-Timeout
192 * attribute in the reply, or modifying the existing one.
194 RDEBUG("Login within allowed time-slot, %d seconds left in this session", left);
196 timeout = fr_pair_find_by_num(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
197 if (timeout) { /* just update... */
198 if (timeout->vp_integer > (unsigned int) left) {
199 timeout->vp_integer = left;
202 timeout = radius_pair_create(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
203 timeout->vp_integer = left;
206 RDEBUG("reply:Session-Timeout set to %d", left);
208 return RLM_MODULE_UPDATED;
213 * Do any per-module initialization that is separate to each
214 * configured instance of the module. e.g. set up connections
215 * to external databases, read configuration files, set up
216 * dictionary entries, etc.
218 * If configuration information is given in the config section
219 * that must be referenced in later calls, store a handle to it
220 * in *instance otherwise put a null pointer there.
222 static int mod_instantiate(CONF_SECTION *conf, void *instance)
224 rlm_logintime_t *inst = instance;
226 if (inst->min_time == 0) {
227 cf_log_err_cs(conf, "Invalid value '0' for minimum_timeout");
232 * Register a Current-Time comparison function
234 paircompare_register(dict_attrbyvalue(PW_CURRENT_TIME, 0), NULL, true, timecmp, inst);
235 paircompare_register(dict_attrbyvalue(PW_TIME_OF_DAY, 0), NULL, true, time_of_day, inst);
241 * The module name should be the only globally exported symbol.
242 * That is, everything else should be 'static'.
244 * If the module needs to temporarily modify it's instantiation
245 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
246 * The server will then take care of ensuring that the module
247 * is single-threaded.
249 extern module_t rlm_logintime;
250 module_t rlm_logintime = {
251 .magic = RLM_MODULE_INIT,
253 .inst_size = sizeof(rlm_logintime_t),
254 .config = module_config,
255 .instantiate = mod_instantiate,
257 [MOD_AUTHORIZE] = mod_authorize,
258 [MOD_POST_AUTH] = mod_authorize