modules, rlm_logintime and rlm_expiration. Move timestr.c to rlm_logintime.
Update makefiles and radiusd.conf
}
#
+ # The expiration module. This handles the Expiration attribute
+ # It should be included in the *end* of the authorize section
+ # in order to handle user Expiration. It should also be included
+ # in the instantiate section in order to register the Expiration
+ # compare function
+ #
+ expiration {
+ #
+ # The Reply-Message which will be sent back in case the
+ # account has expired. Dynamic substitution is supported
+ #
+ reply-message = "Password Has Expired\r\n"
+# reply-message = "Your account has expired, %{User-Name}\r\n"
+ }
+
+ #
+ # The logintime module. Handles the Login-Time and Current-Time attributes.
+ # It should be included in the *end* of the authorize section
+ # in order to handle Login-Time checks. It should also be included in
+ # the instantiate section in order to register the Current-Time compare
+ # function.
+ # If the user is allowed to logon a Session-Timeout is calculated based on the
+ # remaining time.
+ #
+ logintime {
+ #
+ # The Reply-Message which will be sent back in case the account
+ # is calling outside of the allowed timespan. Dynamic substitution is
+ # supported.
+ #
+ reply-message = "You are calling outside your allowed timespan\r\n"
+# reply-message = "Outside allowed timespan (%{check:Login-Time}), %{User-Name}\r\n"
+ #
+ # The minimum timeout (in seconds) a user is allowed to have. If the calculated
+ # timeout is lower we don't allow the logon. Some NASes do not handle
+ # values lower than 60 seconds well.
+ #
+ minimum-timeout = 60
+ }
+ #
# Execute external programs
#
# This module is useful only for 'xlat'. To use it,
include ../../Make.inc
SERVER_OBJS = radiusd.o files.o util.o acct.o nas.o log.o valuepair.o \
- version.o proxy.o exec.o auth.o timestr.o conffile.o \
+ version.o proxy.o exec.o auth.o conffile.o \
modules.o modcall.o session.o xlat.o threads.o smux.o \
radius_snmp.o client.o request_list.o mainconfig.o
INCLUDES = ../include/radiusd.h ../include/radius.h ../include/libradius.h ../include/conf.h ../include/autoconf.h
mainconfig.o: mainconfig.c $(INCLUDES) ../include/modules.h
$(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c mainconfig.c
-timestr.o: timestr.c $(INCLUDES)
- $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c timestr.c
-
modules.o: modules.c $(INCLUDES)
$(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(VFLAGS) $(INCLTDL) -c modules.c
}
-/*
- * Check if account has expired, and if user may login now.
- */
-static int check_expiration(REQUEST *request)
-{
- int result;
- VALUE_PAIR *check_item = request->config_items;
-
- result = 0;
- while (result == 0 && check_item != NULL) {
-
- /*
- * Check expiration date if we are doing password aging.
- */
- if (check_item->attribute == PW_EXPIRATION) {
- /*
- * Has this user's password expired?
- *
- * If so, remove ALL reply attributes,
- * and add our own Reply-Message, saying
- * why they're being rejected.
- */
- if (check_item->lvalue < (unsigned) time(NULL)) {
- VALUE_PAIR *vp;
-
- result = -1;
- vp = pairmake("Reply-Message",
- "Password Has Expired\r\n",
- T_OP_ADD);
- pairfree(&request->reply->vps);
- request->reply->vps = vp;
- break;
- }
- }
- check_item = check_item->next;
- }
- return result;
-}
-
/*
* Make sure user/pass are clean
* Validate the user
*/
do {
- if ((result = check_expiration(request)) < 0)
- break;
result = rad_check_password(request);
if (result > 0) {
/* don't reply! */
}
}
- if (result >= 0 &&
- (check_item = pairfind(request->config_items, PW_LOGIN_TIME)) != NULL) {
-
- /*
- * Authentication is OK. Now see if this
- * user may login at this time of the day.
- */
- r = timestr_match((char *)check_item->strvalue,
- request->timestamp);
-
- if (r == 0) { /* unlimited */
- /*
- * Do nothing: login-time is OK.
- */
-
- /*
- * Session-Timeout needs to be at least
- * 60 seconds, some terminal servers
- * ignore smaller values.
- */
- } else if (r < 60) {
- /*
- * User called outside allowed time interval.
- */
- result = -1;
- user_msg = "You are calling outside your allowed timespan\r\n";
-
- request->reply->code = PW_AUTHENTICATION_REJECT;
- pairfree(&request->reply->vps);
-
- tmp = pairmake("Reply-Message", user_msg, T_OP_SET);
- request->reply->vps = tmp;
-
- snprintf(logstr, sizeof(logstr), "Outside allowed timespan (time allowed %s)",
- check_item->strvalue);
- rad_authlog(logstr, request, 1);
-
- } else if (r > 0) {
-
- /*
- * User is allowed, but set Session-Timeout.
- */
- if ((reply_item = pairfind(request->reply->vps,
- PW_SESSION_TIMEOUT)) != NULL) {
- if (reply_item->lvalue > (unsigned) r)
- reply_item->lvalue = r;
- } else {
- if ((reply_item = paircreate(
- PW_SESSION_TIMEOUT,
- PW_TYPE_INTEGER)) == NULL) {
- radlog(L_ERR|L_CONS, "no memory");
- exit(1);
- }
- reply_item->lvalue = r;
- pairadd(&request->reply->vps, reply_item);
- }
- }
- }
-
/*
* Result should be >= 0 here - if not, it means the user
* is rejected, so we overwrite the Post-Auth-Type with
/*
- * Compare the current time to a range.
- */
-static int timecmp(void *instance,
- REQUEST *req,
- VALUE_PAIR *request, VALUE_PAIR *check,
- VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
-{
- instance = instance;
- request = request; /* shut the compiler up */
- check_pairs = check_pairs;
- reply_pairs = reply_pairs;
-
- /*
- * If there's a request, use that timestamp.
- */
- if (timestr_match((char *)check->strvalue,
- req ? req->timestamp : time(NULL)) >= 0) {
- return 0;
- }
- return -1;
-}
-
-/*
* Matches if there is NO SUCH ATTRIBUTE as the one named
* in check->strvalue. If there IS such an attribute, it
* doesn't match.
}
/*
- * Compare the expiration date.
- */
-static int expirecmp(void *instance, REQUEST *req UNUSED,
- VALUE_PAIR *request, VALUE_PAIR *check,
- VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
-{
- time_t now;
-
- instance = instance;
- request = request; /* shut the compiler up */
- check_pairs = check_pairs;
- reply_pairs = reply_pairs;
-
- /*
- * FIXME! This should be request->timestamp!
- */
- now = time(NULL);
-
- if (now <= (signed)check->lvalue) {
- return 0;
- }
-
- return +1;
-}
-
-/*
* Compare the request packet type.
*/
static int packetcmp(void *instance UNUSED, REQUEST *req,
paircompare_register(PW_PREFIX, PW_USER_NAME, presufcmp, NULL);
paircompare_register(PW_SUFFIX, PW_USER_NAME, presufcmp, NULL);
paircompare_register(PW_CONNECT_RATE, PW_CONNECT_INFO, connectcmp, NULL);
- paircompare_register(PW_CURRENT_TIME, 0, timecmp, NULL);
paircompare_register(PW_NO_SUCH_ATTRIBUTE, 0, attrcmp, NULL);
- paircompare_register(PW_EXPIRATION, 0, expirecmp, NULL);
paircompare_register(PW_PACKET_TYPE, 0, packetcmp, NULL);
paircompare_register(PW_RESPONSE_PACKET_TYPE, 0, responsecmp, NULL);
}
--- /dev/null
+TARGET = rlm_expiration
+SRCS = rlm_expiration.c
+
+include ../rules.mak
--- /dev/null
+/*
+ * rlm_expiration.c
+ *
+ * Version: $Id$
+ *
+ * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright 2001 The FreeRADIUS server project
+ * Copyright 2004 Kostas Kalevras <kkalev@noc.ntua.gr>
+ */
+
+#include "autoconf.h"
+#include "libradius.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "radiusd.h"
+#include "modules.h"
+#include "conffile.h"
+
+
+static const char rcsid[] = "$Id$";
+
+/*
+ * 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_expiration_t {
+ char *msg; /* The Reply-Message passed back to the user if the account is expired */
+} rlm_expiration_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 CONF_PARSER module_config[] = {
+ { "reply-message", PW_TYPE_STRING_PTR, offsetof(rlm_expiration_t,msg), NULL, "Password Has Expired\r\n"},
+ { NULL, -1, 0, NULL, NULL }
+};
+
+static int expiration_detach(void *instance);
+
+/*
+ * Check if account has expired, and if user may login now.
+ */
+static int expiration_authorize(void *instance, REQUEST *request)
+{
+ rlm_expiration_t *data = (rlm_expiration_t *)instance;
+ VALUE_PAIR *check_item = NULL;
+ char msg[MAX_STRING_LEN];
+
+ if ((check_item = pairfind(request->config_items, PW_EXPIRATION)) != NULL){
+ /*
+ * Has this user's password expired?
+ *
+ * If so, remove ALL reply attributes,
+ * and add our own Reply-Message, saying
+ * why they're being rejected.
+ */
+ DEBUG("rlm_expiration: Checking Expiration time: '%s'",check_item->strvalue);
+ if (check_item->lvalue < (unsigned) time(NULL)) {
+ char logstr[MAX_STRING_LEN];
+ VALUE_PAIR *module_fmsg_vp;
+
+ DEBUG("rlm_expiration: Account has expired");
+
+ if (data->msg){
+ VALUE_PAIR *vp;
+
+ if (!radius_xlat(msg, sizeof(msg), data->msg, request, NULL)) {
+ radlog(L_ERR, "rlm_expiration: xlat failed.");
+ return RLM_MODULE_FAIL;
+ }
+
+ vp = pairmake("Reply-Message", msg, T_OP_ADD);
+ pairfree(&request->reply->vps);
+ request->reply->vps = vp;
+ }
+ snprintf(logstr, sizeof(logstr), "Account has expired [Expiration %s]",check_item->strvalue);
+ module_fmsg_vp = pairmake("Module-Failure-Message", logstr, T_OP_EQ);
+ pairadd(&request->packet->vps, module_fmsg_vp);
+
+ return RLM_MODULE_USERLOCK;
+ }
+ }
+ else
+ return RLM_MODULE_NOOP;
+
+ return RLM_MODULE_OK;
+}
+
+/*
+ * Compare the expiration date.
+ */
+static int expirecmp(void *instance, REQUEST *req,
+ VALUE_PAIR *request, VALUE_PAIR *check,
+ VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
+{
+ time_t now = 0;
+ instance = instance;
+ request = request; /* shut the compiler up */
+ check_pairs = check_pairs;
+ reply_pairs = reply_pairs;
+
+ now = (req) ? req->timestamp : time(NULL);
+
+ if (now <= (signed)check->lvalue)
+ return 0;
+ return +1;
+}
+
+
+/*
+ * 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 expiration_instantiate(CONF_SECTION *conf, void **instance)
+{
+ rlm_expiration_t *data;
+
+ /*
+ * Set up a storage area for instance data
+ */
+ data = rad_malloc(sizeof(*data));
+ if (!data) {
+ radlog(L_ERR, "rlm_expiration: rad_malloc() failed.");
+ return -1;
+ }
+ memset(data, 0, sizeof(*data));
+
+ /*
+ * If the configuration parameters can't be parsed, then
+ * fail.
+ */
+ if (cf_section_parse(conf, data, module_config) < 0) {
+ free(data);
+ radlog(L_ERR, "rlm_expiration: Configuration parsing failed.");
+ return -1;
+ }
+
+ /*
+ * If we are passed an empty reply-message don't use it
+ */
+ if (!strlen(data->msg)){
+ free(data->msg);
+ data->msg = NULL;
+ }
+
+ /*
+ * Register the expiration comparison operation.
+ */
+ paircompare_register(PW_EXPIRATION, 0, expirecmp, data);
+
+ *instance = data;
+
+ return 0;
+}
+
+static int expiration_detach(void *instance)
+{
+ rlm_expiration_t *data = (rlm_expiration_t *) instance;
+
+ paircompare_unregister(PW_CURRENT_TIME, expirecmp);
+ if (data->msg)
+ free(data->msg);
+ free(instance);
+ 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.
+ */
+module_t rlm_expiration = {
+ "Expiration",
+ RLM_TYPE_THREAD_SAFE, /* type */
+ NULL, /* initialization */
+ expiration_instantiate, /* instantiation */
+ {
+ NULL, /* authentication */
+ expiration_authorize, /* authorization */
+ NULL, /* preaccounting */
+ NULL, /* accounting */
+ NULL, /* checksimul */
+ NULL, /* pre-proxy */
+ NULL, /* post-proxy */
+ NULL /* post-auth */
+ },
+ expiration_detach, /* detach */
+ NULL, /* destroy */
+};
--- /dev/null
+TARGET = rlm_logintime
+SRCS = rlm_logintime.c timestr.c
+
+include ../rules.mak
--- /dev/null
+/*
+ * rlm_logintime.c
+ *
+ * Version: $Id$
+ *
+ * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright 2001 The FreeRADIUS server project
+ * Copyright 2004 Kostas Kalevras <kkalev@noc.ntua.gr>
+ */
+
+#include "autoconf.h"
+#include "libradius.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "radiusd.h"
+#include "modules.h"
+#include "conffile.h"
+
+
+static const char rcsid[] = "$Id$";
+
+/*
+ * 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 {
+ char *msg; /* The Reply-Message passed back to the user
+ * if the account is outside allowed timestamp */
+ int 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 CONF_PARSER module_config[] = {
+ { "reply-message", PW_TYPE_STRING_PTR, offsetof(rlm_logintime_t,msg), NULL,
+ "You are calling outside your allowed timespan\r\n"},
+ { "minimum-timeout", PW_TYPE_INTEGER, offsetof(rlm_logintime_t,min_time), NULL, "60" },
+ { NULL, -1, 0, NULL, NULL }
+};
+
+static int logintime_detach(void *instance);
+
+/*
+ * Compare the current time to a range.
+ */
+static int timecmp(void *instance,
+ REQUEST *req,
+ VALUE_PAIR *request, VALUE_PAIR *check,
+ VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
+{
+ instance = instance;
+ request = request; /* shut the compiler up */
+ check_pairs = check_pairs;
+ reply_pairs = reply_pairs;
+
+ /*
+ * If there's a request, use that timestamp.
+ */
+ if (timestr_match((char *)check->strvalue,
+ req ? req->timestamp : time(NULL)) >= 0)
+ return 0;
+
+ return -1;
+}
+
+/*
+ * Check if account has expired, and if user may login now.
+ */
+static int logintime_authorize(void *instance, REQUEST *request)
+{
+ rlm_logintime_t *data = (rlm_logintime_t *)instance;
+ VALUE_PAIR *check_item = NULL;
+ int r;
+
+ if ((check_item = pairfind(request->config_items, PW_LOGIN_TIME)) != NULL) {
+
+ /*
+ * Authentication is OK. Now see if this
+ * user may login at this time of the day.
+ */
+ DEBUG("rlm_logintime: Checking Login-Time: '%s'",check_item->strvalue);
+ r = timestr_match((char *)check_item->strvalue,
+ request->timestamp);
+ if (r == 0) { /* unlimited */
+ /*
+ * Do nothing: login-time is OK.
+ */
+
+ /*
+ * Session-Timeout needs to be at least
+ * 60 seconds, some terminal servers
+ * ignore smaller values.
+ */
+ DEBUG("rlm_logintime: timestr returned unlimited");
+ } else if (r < data->min_time) {
+ char logstr[MAX_STRING_LEN];
+ VALUE_PAIR *module_fmsg_vp;
+
+ /*
+ * User called outside allowed time interval.
+ */
+
+ DEBUG("rlm_logintime: timestr returned reject");
+ if (data->msg){
+ char msg[MAX_STRING_LEN];
+ VALUE_PAIR *tmp;
+
+ if (!radius_xlat(msg, sizeof(msg), data->msg, request, NULL)) {
+ radlog(L_ERR, "rlm_logintime: xlat failed.");
+ return RLM_MODULE_FAIL;
+ }
+ pairfree(&request->reply->vps);
+ tmp = pairmake("Reply-Message", msg, T_OP_SET);
+ request->reply->vps = tmp;
+ }
+
+ snprintf(logstr, sizeof(logstr), "Outside allowed timespan (time allowed %s)",
+ check_item->strvalue);
+ module_fmsg_vp = pairmake("Module-Failure-Message", logstr, T_OP_EQ);
+ pairadd(&request->packet->vps, module_fmsg_vp);
+
+ return RLM_MODULE_REJECT;
+
+ } else if (r > 0) {
+ VALUE_PAIR *reply_item;
+
+ /*
+ * User is allowed, but set Session-Timeout.
+ */
+ DEBUG("rlm_logintime: timestr returned accept");
+ if ((reply_item = pairfind(request->reply->vps, PW_SESSION_TIMEOUT)) != NULL) {
+ if (reply_item->lvalue > (unsigned) r)
+ reply_item->lvalue = r;
+ } else {
+ if ((reply_item = paircreate( PW_SESSION_TIMEOUT, PW_TYPE_INTEGER)) == NULL) {
+ radlog(L_ERR|L_CONS, "no memory");
+ return RLM_MODULE_FAIL;
+ }
+ reply_item->lvalue = r;
+ pairadd(&request->reply->vps, reply_item);
+ }
+ DEBUG("rlm_logintime: Session-Timeout set to: %d",r);
+ }
+ }
+ else
+ return RLM_MODULE_NOOP;
+
+ return RLM_MODULE_OK;
+}
+
+
+/*
+ * 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 logintime_instantiate(CONF_SECTION *conf, void **instance)
+{
+ rlm_logintime_t *data;
+
+ /*
+ * Set up a storage area for instance data
+ */
+ data = rad_malloc(sizeof(*data));
+ if (!data) {
+ radlog(L_ERR, "rlm_logintime: rad_malloc() failed.");
+ return -1;
+ }
+ memset(data, 0, sizeof(*data));
+
+ /*
+ * If the configuration parameters can't be parsed, then
+ * fail.
+ */
+ if (cf_section_parse(conf, data, module_config) < 0) {
+ free(data);
+ radlog(L_ERR, "rlm_logintime: Configuration parsing failed.");
+ return -1;
+ }
+
+ /*
+ * If we are passed an empty reply-message don't use it
+ */
+ if (!strlen(data->msg)){
+ free(data->msg);
+ data->msg = NULL;
+ }
+
+ if (data->min_time == 0){
+ radlog(L_ERR, "rlm_logintime: Minimum timeout should be non zero.");
+ free(data->msg);
+ free(data);
+ return -1;
+ }
+
+ /*
+ * Register a Current-Time comparison function
+ */
+ paircompare_register(PW_CURRENT_TIME, 0, timecmp, data);
+
+ *instance = data;
+
+ return 0;
+}
+
+static int logintime_detach(void *instance)
+{
+ rlm_logintime_t *data = (rlm_logintime_t *) instance;
+
+ paircompare_unregister(PW_CURRENT_TIME, timecmp);
+ if (data->msg)
+ free(data->msg);
+ free(instance);
+ 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.
+ */
+module_t rlm_logintime = {
+ "Login Time",
+ RLM_TYPE_THREAD_SAFE, /* type */
+ NULL, /* initialization */
+ logintime_instantiate, /* instantiation */
+ {
+ NULL, /* authentication */
+ logintime_authorize, /* authorization */
+ NULL, /* preaccounting */
+ NULL, /* accounting */
+ NULL, /* checksimul */
+ NULL, /* pre-proxy */
+ NULL, /* post-proxy */
+ NULL /* post-auth */
+ },
+ logintime_detach, /* detach */
+ NULL, /* destroy */
+};
rlm_digest
rlm_eap
rlm_exec
+rlm_expiration
rlm_expr
rlm_fastusers
rlm_files
rlm_ippool
rlm_krb5
rlm_ldap
+rlm_logintime
rlm_mschap
rlm_ns_mta_md5
rlm_pam