summaryrefslogtreecommitdiffstats
path: root/src/modules/rlm_logintime/rlm_logintime.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/rlm_logintime/rlm_logintime.c')
-rw-r--r--src/modules/rlm_logintime/rlm_logintime.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/src/modules/rlm_logintime/rlm_logintime.c b/src/modules/rlm_logintime/rlm_logintime.c
new file mode 100644
index 0000000..ca8249d
--- /dev/null
+++ b/src/modules/rlm_logintime/rlm_logintime.c
@@ -0,0 +1,260 @@
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file rlm_logintime.c
+ * @brief Allow login only during a given timeslot.
+ *
+ * @copyright 2001,2006 The FreeRADIUS server project
+ * @copyright 2004 Kostas Kalevras <kkalev@noc.ntua.gr>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+
+#include <ctype.h>
+
+/* timestr.c */
+int timestr_match(char const *, time_t);
+
+/*
+ * Define a structure for our module configuration.
+ *
+ * These variables do not need to be in a structure, but it's
+ * a lot cleaner to do so, and a pointer to the structure can
+ * be used as the instance handle.
+ */
+typedef struct rlm_logintime_t {
+ uint32_t min_time;
+} rlm_logintime_t;
+
+/*
+ * A mapping of configuration file names to internal variables.
+ *
+ * Note that the string is dynamically allocated, so it MUST
+ * be freed. When the configuration file parse re-reads the string,
+ * it free's the old one, and strdup's the new one, placing the pointer
+ * to the strdup'd string into 'config.string'. This gets around
+ * buffer over-flows.
+ */
+static const CONF_PARSER module_config[] = {
+ { "minimum-timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_logintime_t, min_time), NULL },
+ { "minimum_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_logintime_t, min_time), "60" },
+ CONF_PARSER_TERMINATOR
+};
+
+
+/*
+ * Compare the current time to a range.
+ */
+static int timecmp(UNUSED void *instance, REQUEST *req, UNUSED VALUE_PAIR *request, VALUE_PAIR *check,
+ UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
+{
+ /*
+ * If there's a request, use that timestamp.
+ */
+ if (timestr_match(check->vp_strvalue,
+ req ? req->timestamp : time(NULL)) >= 0)
+ return 0;
+
+ return -1;
+}
+
+
+/*
+ * Time-Of-Day support
+ */
+static int time_of_day(UNUSED void *instance, REQUEST *req, UNUSED VALUE_PAIR *request, VALUE_PAIR *check,
+ UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
+{
+ int scan;
+ int hhmmss, when;
+ char const *p;
+ struct tm *tm, s_tm;
+
+ /*
+ * Must be called with a request pointer.
+ */
+ if (!req) return -1;
+
+ if (strspn(check->vp_strvalue, "0123456789: ") != strlen(check->vp_strvalue)) {
+ DEBUG("rlm_logintime: Bad Time-Of-Day value \"%s\"",
+ check->vp_strvalue);
+ return -1;
+ }
+
+ tm = localtime_r(&req->timestamp, &s_tm);
+ hhmmss = (tm->tm_hour * 3600) + (tm->tm_min * 60) + tm->tm_sec;
+
+ /*
+ * Time of day is a 24-hour clock
+ */
+ p = check->vp_strvalue;
+ scan = atoi(p);
+ p = strchr(p, ':');
+ if ((scan > 23) || !p) {
+ DEBUG("rlm_logintime: Bad Time-Of-Day value \"%s\"",
+ check->vp_strvalue);
+ return -1;
+ }
+ when = scan * 3600;
+ p++;
+
+ scan = atoi(p);
+ if (scan > 59) {
+ DEBUG("rlm_logintime: Bad Time-Of-Day value \"%s\"",
+ check->vp_strvalue);
+ return -1;
+ }
+ when += scan * 60;
+
+ p = strchr(p, ':');
+ if (p) {
+ scan = atoi(p + 1);
+ if (scan > 59) {
+ DEBUG("rlm_logintime: Bad Time-Of-Day value \"%s\"",
+ check->vp_strvalue);
+ return -1;
+ }
+ when += scan;
+ }
+
+ fprintf(stderr, "returning %d - %d\n",
+ hhmmss, when);
+
+ return hhmmss - when;
+}
+
+/*
+ * Check if account has expired, and if user may login now.
+ */
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
+{
+ rlm_logintime_t *inst = instance;
+ VALUE_PAIR *ends, *timeout;
+ int left;
+
+ ends = fr_pair_find_by_num(request->config, PW_LOGIN_TIME, 0, TAG_ANY);
+ if (!ends) {
+ return RLM_MODULE_NOOP;
+ }
+
+ /*
+ * Authentication is OK. Now see if this user may login at this time of the day.
+ */
+ RDEBUG("Checking Login-Time");
+
+ /*
+ * Compare the time the request was received with the current Login-Time value
+ */
+ left = timestr_match(ends->vp_strvalue, request->timestamp);
+ if (left < 0) return RLM_MODULE_USERLOCK; /* outside of the allowed time */
+
+ /*
+ * Do nothing, login time is not controlled (unendsed).
+ */
+ if (left == 0) {
+ return RLM_MODULE_OK;
+ }
+
+ /*
+ * The min_time setting is to deal with NAS that won't allow Session-Timeout values below a certain value
+ * For example some Alcatel Lucent products won't allow a Session-Timeout < 300 (5 minutes).
+ *
+ * We don't know were going to get another chance to lock out the user, so we need to do it now.
+ */
+ if (left < (int) inst->min_time) {
+ REDEBUG("Login outside of allowed time-slot (session end %s, with lockout %i seconds before)",
+ ends->vp_strvalue, inst->min_time);
+
+ return RLM_MODULE_USERLOCK;
+ }
+
+ /* else left > inst->min_time */
+
+ /*
+ * There's time left in the users session, inform the NAS by including a Session-Timeout
+ * attribute in the reply, or modifying the existing one.
+ */
+ RDEBUG("Login within allowed time-slot, %d seconds left in this session", left);
+
+ timeout = fr_pair_find_by_num(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
+ if (timeout) { /* just update... */
+ if (timeout->vp_integer > (unsigned int) left) {
+ timeout->vp_integer = left;
+ }
+ } else {
+ timeout = radius_pair_create(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
+ timeout->vp_integer = left;
+ }
+
+ RDEBUG("reply:Session-Timeout set to %d", left);
+
+ return RLM_MODULE_UPDATED;
+}
+
+
+/*
+ * 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(CONF_SECTION *conf, void *instance)
+{
+ rlm_logintime_t *inst = instance;
+
+ if (inst->min_time == 0) {
+ cf_log_err_cs(conf, "Invalid value '0' for minimum_timeout");
+ return -1;
+ }
+
+ /*
+ * Register a Current-Time comparison function
+ */
+ paircompare_register(dict_attrbyvalue(PW_CURRENT_TIME, 0), NULL, true, timecmp, inst);
+ paircompare_register(dict_attrbyvalue(PW_TIME_OF_DAY, 0), NULL, true, time_of_day, inst);
+
+ return 0;
+}
+
+/*
+ * The module name should be the only globally exported symbol.
+ * That is, everything else should be 'static'.
+ *
+ * If the module needs to temporarily modify it's instantiation
+ * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
+ * The server will then take care of ensuring that the module
+ * is single-threaded.
+ */
+extern module_t rlm_logintime;
+module_t rlm_logintime = {
+ .magic = RLM_MODULE_INIT,
+ .name = "logintime",
+ .inst_size = sizeof(rlm_logintime_t),
+ .config = module_config,
+ .instantiate = mod_instantiate,
+ .methods = {
+ [MOD_AUTHORIZE] = mod_authorize,
+ [MOD_POST_AUTH] = mod_authorize
+ },
+};