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" },
59 { NULL, -1, 0, NULL, NULL }
64 * Compare the current time to a range.
66 static int timecmp(UNUSED void *instance, REQUEST *req, UNUSED VALUE_PAIR *request, VALUE_PAIR *check,
67 UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
70 * If there's a request, use that timestamp.
72 if (timestr_match(check->vp_strvalue,
73 req ? req->timestamp : time(NULL)) >= 0)
83 static int time_of_day(UNUSED void *instance, REQUEST *req, UNUSED VALUE_PAIR *request, VALUE_PAIR *check,
84 UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
92 * Must be called with a request pointer.
96 if (strspn(check->vp_strvalue, "0123456789: ") != strlen(check->vp_strvalue)) {
97 DEBUG("rlm_logintime: Bad Time-Of-Day value \"%s\"",
102 tm = localtime_r(&req->timestamp, &s_tm);
103 hhmmss = (tm->tm_hour * 3600) + (tm->tm_min * 60) + tm->tm_sec;
106 * Time of day is a 24-hour clock
108 p = check->vp_strvalue;
111 if ((scan > 23) || !p) {
112 DEBUG("rlm_logintime: Bad Time-Of-Day value \"%s\"",
121 DEBUG("rlm_logintime: Bad Time-Of-Day value \"%s\"",
131 DEBUG("rlm_logintime: Bad Time-Of-Day value \"%s\"",
138 fprintf(stderr, "returning %d - %d\n",
141 return hhmmss - when;
145 * Check if account has expired, and if user may login now.
147 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
149 rlm_logintime_t *inst = instance;
150 VALUE_PAIR *ends, *timeout;
153 ends = fr_pair_find_by_num(request->config, PW_LOGIN_TIME, 0, TAG_ANY);
155 return RLM_MODULE_NOOP;
159 * Authentication is OK. Now see if this user may login at this time of the day.
161 RDEBUG("Checking Login-Time");
164 * Compare the time the request was received with the current Login-Time value
166 left = timestr_match(ends->vp_strvalue, request->timestamp);
167 if (left < 0) return RLM_MODULE_USERLOCK; /* outside of the allowed time */
170 * Do nothing, login time is not controlled (unendsed).
173 return RLM_MODULE_OK;
177 * The min_time setting is to deal with NAS that won't allow Session-Timeout values below a certain value
178 * For example some Alcatel Lucent products won't allow a Session-Timeout < 300 (5 minutes).
180 * We don't know were going to get another chance to lock out the user, so we need to do it now.
182 if (left < (int) inst->min_time) {
183 REDEBUG("Login outside of allowed time-slot (session end %s, with lockout %i seconds before)",
184 ends->vp_strvalue, inst->min_time);
186 return RLM_MODULE_USERLOCK;
189 /* else left > inst->min_time */
192 * There's time left in the users session, inform the NAS by including a Session-Timeout
193 * attribute in the reply, or modifying the existing one.
195 RDEBUG("Login within allowed time-slot, %d seconds left in this session", left);
197 timeout = fr_pair_find_by_num(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
198 if (timeout) { /* just update... */
199 if (timeout->vp_integer > (unsigned int) left) {
200 timeout->vp_integer = left;
203 timeout = radius_pair_create(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
204 timeout->vp_integer = left;
207 RDEBUG("reply:Session-Timeout set to %d", left);
209 return RLM_MODULE_UPDATED;
214 * Do any per-module initialization that is separate to each
215 * configured instance of the module. e.g. set up connections
216 * to external databases, read configuration files, set up
217 * dictionary entries, etc.
219 * If configuration information is given in the config section
220 * that must be referenced in later calls, store a handle to it
221 * in *instance otherwise put a null pointer there.
223 static int mod_instantiate(CONF_SECTION *conf, void *instance)
225 rlm_logintime_t *inst = instance;
227 if (inst->min_time == 0) {
228 cf_log_err_cs(conf, "Invalid value '0' for minimum_timeout");
233 * Register a Current-Time comparison function
235 paircompare_register(dict_attrbyvalue(PW_CURRENT_TIME, 0), NULL, true, timecmp, inst);
236 paircompare_register(dict_attrbyvalue(PW_TIME_OF_DAY, 0), NULL, true, time_of_day, inst);
242 * The module name should be the only globally exported symbol.
243 * That is, everything else should be 'static'.
245 * If the module needs to temporarily modify it's instantiation
246 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
247 * The server will then take care of ensuring that the module
248 * is single-threaded.
250 extern module_t rlm_logintime;
251 module_t rlm_logintime = {
252 .magic = RLM_MODULE_INIT,
254 .inst_size = sizeof(rlm_logintime_t),
255 .config = module_config,
256 .instantiate = mod_instantiate,
258 [MOD_AUTHORIZE] = mod_authorize,
259 [MOD_POST_AUTH] = mod_authorize