summaryrefslogtreecommitdiffstats
path: root/src/modules/rlm_preprocess
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 14:11:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 14:11:00 +0000
commitaf754e596a8dbb05ed8580c342e7fe02e08b28e0 (patch)
treeb2f334c2b55ede42081aa6710a72da784547d8ea /src/modules/rlm_preprocess
parentInitial commit. (diff)
downloadfreeradius-af754e596a8dbb05ed8580c342e7fe02e08b28e0.tar.xz
freeradius-af754e596a8dbb05ed8580c342e7fe02e08b28e0.zip
Adding upstream version 3.2.3+dfsg.upstream/3.2.3+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/modules/rlm_preprocess')
-rw-r--r--src/modules/rlm_preprocess/README.md11
-rw-r--r--src/modules/rlm_preprocess/all.mk2
-rw-r--r--src/modules/rlm_preprocess/rlm_preprocess.c736
3 files changed, 749 insertions, 0 deletions
diff --git a/src/modules/rlm_preprocess/README.md b/src/modules/rlm_preprocess/README.md
new file mode 100644
index 0000000..f3a6fc5
--- /dev/null
+++ b/src/modules/rlm_preprocess/README.md
@@ -0,0 +1,11 @@
+# rlm_preprocess
+## Metadata
+<dl>
+ <dt>category</dt><dd>policy</dd>
+</dl>
+
+## Summary
+
+Helper module to pre-process incoming packets. This processes
+'huntgroups' and 'hints' files, as well as fixing up a number of
+NAS attribute issues.
diff --git a/src/modules/rlm_preprocess/all.mk b/src/modules/rlm_preprocess/all.mk
new file mode 100644
index 0000000..6b18994
--- /dev/null
+++ b/src/modules/rlm_preprocess/all.mk
@@ -0,0 +1,2 @@
+TARGET := rlm_preprocess.a
+SOURCES := rlm_preprocess.c
diff --git a/src/modules/rlm_preprocess/rlm_preprocess.c b/src/modules/rlm_preprocess/rlm_preprocess.c
new file mode 100644
index 0000000..e6e12d4
--- /dev/null
+++ b/src/modules/rlm_preprocess/rlm_preprocess.c
@@ -0,0 +1,736 @@
+/*
+ * 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_preprocess.c
+ * @brief Fixes up requests, and processes huntgroups/hints files.
+ *
+ * @copyright 2000,2006 The FreeRADIUS server project
+ * @copyright 2000 Alan DeKok <aland@ox.org>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/rad_assert.h>
+
+#include <ctype.h>
+
+typedef struct rlm_preprocess_t {
+ char const *huntgroup_file;
+ char const *hints_file;
+ PAIR_LIST *huntgroups;
+ PAIR_LIST *hints;
+ bool with_ascend_hack;
+ uint32_t ascend_channels_per_line;
+ bool with_ntdomain_hack;
+ bool with_specialix_jetstream_hack;
+ bool with_cisco_vsa_hack;
+ bool with_alvarion_vsa_hack;
+ bool with_cablelabs_vsa_hack;
+} rlm_preprocess_t;
+
+static const CONF_PARSER module_config[] = {
+ { "huntgroups", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_preprocess_t, huntgroup_file), NULL },
+ { "hints", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_preprocess_t, hints_file), NULL },
+ { "with_ascend_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_ascend_hack), "no" },
+ { "ascend_channels_per_line", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_preprocess_t, ascend_channels_per_line), "23" },
+
+ { "with_ntdomain_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_ntdomain_hack), "no" },
+ { "with_specialix_jetstream_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_specialix_jetstream_hack), "no" },
+ { "with_cisco_vsa_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_cisco_vsa_hack), "no" },
+ { "with_alvarion_vsa_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_alvarion_vsa_hack), "no" },
+#if 0
+ { "with_cablelabs_vsa_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_cablelabs_vsa_hack), NULL },
+#endif
+ CONF_PARSER_TERMINATOR
+};
+
+/*
+ * See if a VALUE_PAIR list contains Fall-Through = Yes
+ */
+static int fall_through(VALUE_PAIR *vp)
+{
+ VALUE_PAIR *tmp;
+ tmp = fr_pair_find_by_num(vp, PW_FALL_THROUGH, 0, TAG_ANY);
+
+ return tmp ? tmp->vp_integer : 0;
+}
+
+/*
+ * This hack changes Ascend's wierd port numberings
+ * to standard 0-??? port numbers so that the "+" works
+ * for IP address assignments.
+ */
+static void ascend_nasport_hack(VALUE_PAIR *nas_port, int channels_per_line)
+{
+ int service;
+ int line;
+ int channel;
+
+ if (!nas_port) {
+ return;
+ }
+
+ if (nas_port->vp_integer > 9999) {
+ service = nas_port->vp_integer/10000; /* 1=digital 2=analog */
+ line = (nas_port->vp_integer - (10000 * service)) / 100;
+ channel = nas_port->vp_integer - ((10000 * service) + (100 * line));
+ nas_port->vp_integer = (channel - 1) + ((line - 1) * channels_per_line);
+ }
+}
+
+/*
+ * This hack strips out Cisco's VSA duplicities in lines
+ * (Cisco not implemented VSA's in standard way.
+ *
+ * Cisco sends it's VSA attributes with the attribute name *again*
+ * in the string, like: H323-Attribute = "h323-attribute=value".
+ * This sort of behaviour is nonsense.
+ */
+static void cisco_vsa_hack(REQUEST *request)
+{
+ int vendorcode;
+ char *ptr;
+ char newattr[MAX_STRING_LEN];
+ VALUE_PAIR *vp;
+ vp_cursor_t cursor;
+ for (vp = fr_cursor_init(&cursor, &request->packet->vps);
+ vp;
+ vp = fr_cursor_next(&cursor)) {
+ vendorcode = vp->da->vendor;
+ if (!((vendorcode == 9) || (vendorcode == 6618) || (vendorcode == 35265))) {
+ continue; /* not a Cisco, Quintum or Eltex VSA, continue */
+ }
+
+ if (vp->da->type != PW_TYPE_STRING) {
+ continue;
+ }
+
+ /*
+ * No weird packing. Ignore it.
+ */
+ ptr = strchr(vp->vp_strvalue, '='); /* find an '=' */
+ if (!ptr) {
+ continue;
+ }
+
+ /*
+ * Cisco-AVPair's get packed as:
+ *
+ * Cisco-AVPair = "h323-foo-bar = baz"
+ * Cisco-AVPair = "h323-foo-bar=baz"
+ *
+ * which makes sense only if you're a lunatic.
+ * This code looks for the attribute named inside
+ * of the string, and if it exists, adds it as a new
+ * attribute.
+ */
+ if (vp->da->attr == 1) {
+ char const *p;
+
+ p = vp->vp_strvalue;
+ gettoken(&p, newattr, sizeof(newattr), false);
+
+ if (dict_attrbyname(newattr) != NULL) {
+ pair_make_request(newattr, ptr + 1, T_OP_EQ);
+ }
+ } else { /* h322-foo-bar = "h323-foo-bar = baz" */
+ /*
+ * We strip out the duplicity from the
+ * value field, we use only the value on
+ * the right side of the '=' character.
+ */
+ fr_pair_value_strcpy(vp, ptr + 1);
+ }
+ }
+}
+
+
+/*
+ * Don't even ask what this is doing...
+ */
+static void alvarion_vsa_hack(VALUE_PAIR *vp)
+{
+ int number = 1;
+ vp_cursor_t cursor;
+
+ for (vp = fr_cursor_init(&cursor, &vp);
+ vp;
+ vp = fr_cursor_next(&cursor)) {
+ DICT_ATTR const *da;
+
+ if (vp->da->vendor != 12394) {
+ continue;
+ }
+
+ if (vp->da->type != PW_TYPE_STRING) {
+ continue;
+ }
+
+ da = dict_attrbyvalue(number, 12394);
+ if (!da) {
+ continue;
+ }
+
+ vp->da = da;
+
+ number++;
+ }
+}
+
+/*
+ * Cablelabs magic, taken from:
+ *
+ * http://www.cablelabs.com/packetcable/downloads/specs/PKT-SP-EM-I12-05812.pdf
+ *
+ * Sample data is:
+ *
+ * 0x0001d2d2026d30310000000000003030
+ * 3130303030000e812333000100033031
+ * 00000000000030303130303030000000
+ * 00063230313230313331303630323231
+ * 2e3633390000000081000500
+ */
+
+typedef struct cl_timezone_t {
+ uint8_t dst;
+ uint8_t sign;
+ uint8_t hh[2];
+ uint8_t mm[2];
+ uint8_t ss[2];
+} cl_timezone_t;
+
+typedef struct cl_bcid_t {
+ uint32_t timestamp;
+ uint8_t element_id[8];
+ cl_timezone_t timezone;
+ uint32_t event_counter;
+} cl_bcid_t;
+
+typedef struct cl_em_hdr_t {
+ uint16_t version;
+ cl_bcid_t bcid;
+ uint16_t message_type;
+ uint16_t element_type;
+ uint8_t element_id[8];
+ cl_timezone_t time_zone;
+ uint32_t sequence_number;
+ uint8_t event_time[18];
+ uint8_t status[4];
+ uint8_t priority;
+ uint16_t attr_count; /* of normal Cablelabs VSAs */
+ uint8_t event_object;
+} cl_em_hdr_t;
+
+
+static void cablelabs_vsa_hack(VALUE_PAIR **list)
+{
+ VALUE_PAIR *ev;
+
+ ev = fr_pair_find_by_num(*list, 1, 4491, TAG_ANY); /* Cablelabs-Event-Message */
+ if (!ev) {
+ return;
+ }
+
+ /*
+ * FIXME: write 100's of lines of code to decode
+ * each data structure above.
+ */
+}
+
+/*
+ * Mangle username if needed, IN PLACE.
+ */
+static void rad_mangle(rlm_preprocess_t *inst, REQUEST *request)
+{
+ int num_proxy_state;
+ VALUE_PAIR *namepair;
+ VALUE_PAIR *request_pairs;
+ VALUE_PAIR *tmp;
+ vp_cursor_t cursor;
+
+ /*
+ * Get the username from the request
+ * If it isn't there, then we can't mangle the request.
+ */
+ request_pairs = request->packet->vps;
+ namepair = fr_pair_find_by_num(request_pairs, PW_USER_NAME, 0, TAG_ANY);
+ if (!namepair || (namepair->vp_length == 0)) {
+ return;
+ }
+
+ if (inst->with_ntdomain_hack) {
+ char *ptr;
+ char newname[MAX_STRING_LEN];
+
+ /*
+ * Windows NT machines often authenticate themselves as
+ * NT_DOMAIN\username. Try to be smart about this.
+ *
+ * FIXME: should we handle this as a REALM ?
+ */
+ if ((ptr = strchr(namepair->vp_strvalue, '\\')) != NULL) {
+ strlcpy(newname, ptr + 1, sizeof(newname));
+ /* Same size */
+ fr_pair_value_strcpy(namepair, newname);
+ }
+ }
+
+ if (inst->with_specialix_jetstream_hack) {
+ /*
+ * Specialix Jetstream 8500 24 port access server.
+ * If the user name is 10 characters or longer, a "/"
+ * and the excess characters after the 10th are
+ * appended to the user name.
+ *
+ * Reported by Lucas Heise <root@laonet.net>
+ */
+ if ((strlen(namepair->vp_strvalue) > 10) &&
+ (namepair->vp_strvalue[10] == '/')) {
+ fr_pair_value_strcpy(namepair, namepair->vp_strvalue + 11);
+ }
+ }
+
+ /*
+ * Small check: if Framed-Protocol present but Service-Type
+ * is missing, add Service-Type = Framed-User.
+ */
+ if (fr_pair_find_by_num(request_pairs, PW_FRAMED_PROTOCOL, 0, TAG_ANY) != NULL &&
+ fr_pair_find_by_num(request_pairs, PW_SERVICE_TYPE, 0, TAG_ANY) == NULL) {
+ tmp = radius_pair_create(request->packet, &request->packet->vps, PW_SERVICE_TYPE, 0);
+ tmp->vp_integer = PW_FRAMED_USER;
+ }
+
+ num_proxy_state = 0;
+ for (tmp = fr_cursor_init(&cursor, &request->packet->vps);
+ tmp;
+ tmp = fr_cursor_next(&cursor)) {
+ if (tmp->da->vendor != 0) {
+ continue;
+ }
+
+ if (tmp->da->attr != PW_PROXY_STATE) {
+ continue;
+ }
+
+ num_proxy_state++;
+ }
+
+ if (num_proxy_state > 10) {
+ RWDEBUG("There are more than 10 Proxy-State attributes in the request");
+ RWDEBUG("You have likely configured an infinite proxy loop");
+ }
+}
+
+/*
+ * Compare the request with the "reply" part in the
+ * huntgroup, which normally only contains username or group.
+ * At least one of the "reply" items has to match.
+ */
+static int hunt_paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check)
+{
+ vp_cursor_t cursor;
+ VALUE_PAIR *check_item;
+ VALUE_PAIR *tmp;
+ int result = -1;
+
+ if (!check) return 0;
+
+ for (check_item = fr_cursor_init(&cursor, &check);
+ check_item && (result != 0);
+ check_item = fr_cursor_next(&cursor)) {
+ /* FIXME: fr_pair_list_copy should be removed once VALUE_PAIRs are no longer in linked lists */
+ tmp = fr_pair_copy(request, check_item);
+ tmp->op = check_item->op;
+ result = paircompare(req, request, tmp, NULL);
+ fr_pair_list_free(&tmp);
+ }
+
+ return result;
+}
+
+
+/*
+ * Add hints to the info sent by the terminal server
+ * based on the pattern of the username, and other attributes.
+ */
+static int hints_setup(PAIR_LIST *hints, REQUEST *request)
+{
+ char const *name;
+ VALUE_PAIR *add;
+ VALUE_PAIR *tmp;
+ PAIR_LIST *i;
+ int updated = 0, ft;
+
+ if (!hints || !request->packet->vps)
+ return RLM_MODULE_NOOP;
+
+ /*
+ * Check for valid input, zero length names not permitted
+ */
+ name = (tmp = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY)) ?
+ tmp->vp_strvalue : NULL;
+ if (!name || name[0] == 0) {
+ /*
+ * No name, nothing to do.
+ */
+ return RLM_MODULE_NOOP;
+ }
+
+ for (i = hints; i; i = i->next) {
+ /*
+ * Use "paircompare", which is a little more general...
+ */
+ if (((strcmp(i->name, "DEFAULT") == 0) || (strcmp(i->name, name) == 0)) &&
+ (paircompare(request, request->packet->vps, i->check, NULL) == 0)) {
+ RDEBUG2("hints: Matched %s at %d", i->name, i->lineno);
+ /*
+ * Now add all attributes to the request list,
+ * except PW_STRIP_USER_NAME and PW_FALL_THROUGH
+ * and xlat them.
+ */
+ add = fr_pair_list_copy(request->packet, i->reply);
+ ft = fall_through(add);
+
+ fr_pair_delete_by_num(&add, PW_STRIP_USER_NAME, 0, TAG_ANY);
+ fr_pair_delete_by_num(&add, PW_FALL_THROUGH, 0, TAG_ANY);
+ radius_pairmove(request, &request->packet->vps, add, true);
+
+ updated = 1;
+ if (!ft) {
+ break;
+ }
+ }
+ }
+
+ if (updated == 0) {
+ return RLM_MODULE_NOOP;
+ }
+
+ return RLM_MODULE_UPDATED;
+}
+
+/*
+ * See if we have access to the huntgroup.
+ */
+static int huntgroup_access(REQUEST *request, PAIR_LIST *huntgroups)
+{
+ PAIR_LIST *i;
+ int r = RLM_MODULE_OK;
+ VALUE_PAIR *request_pairs = request->packet->vps;
+
+ /*
+ * We're not controlling access by huntgroups:
+ * Allow them in.
+ */
+ if (!huntgroups) {
+ return RLM_MODULE_OK;
+ }
+
+ for (i = huntgroups; i; i = i->next) {
+ /*
+ * See if this entry matches.
+ */
+ if (paircompare(request, request_pairs, i->check, NULL) != 0) {
+ continue;
+ }
+
+ /*
+ * Now check for access.
+ */
+ r = RLM_MODULE_REJECT;
+ if (hunt_paircmp(request, request_pairs, i->reply) == 0) {
+ VALUE_PAIR *vp;
+
+ /*
+ * We've matched the huntgroup, so add it in
+ * to the list of request pairs.
+ */
+ vp = fr_pair_find_by_num(request_pairs, PW_HUNTGROUP_NAME, 0, TAG_ANY);
+ if (!vp) {
+ vp = radius_pair_create(request->packet, &request->packet->vps, PW_HUNTGROUP_NAME, 0);
+ fr_pair_value_strcpy(vp, i->name);
+ }
+ r = RLM_MODULE_OK;
+ }
+ break;
+ }
+
+ return r;
+}
+
+/*
+ * If the NAS wasn't smart enought to add a NAS-IP-Address
+ * to the request, then add it ourselves.
+ */
+static int add_nas_attr(REQUEST *request)
+{
+ VALUE_PAIR *nas;
+
+ switch (request->packet->src_ipaddr.af) {
+ case AF_INET:
+ nas = fr_pair_find_by_num(request->packet->vps, PW_NAS_IP_ADDRESS, 0, TAG_ANY);
+ if (!nas) {
+ nas = radius_pair_create(request->packet, &request->packet->vps, PW_NAS_IP_ADDRESS, 0);
+ nas->vp_ipaddr = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
+ }
+ break;
+
+ case AF_INET6:
+ nas = fr_pair_find_by_num(request->packet->vps, PW_NAS_IPV6_ADDRESS, 0, TAG_ANY);
+ if (!nas) {
+ nas = radius_pair_create(request->packet, &request->packet->vps, PW_NAS_IPV6_ADDRESS, 0);
+ memcpy(&nas->vp_ipv6addr, &request->packet->src_ipaddr.ipaddr,
+ sizeof(request->packet->src_ipaddr.ipaddr));
+ }
+ break;
+
+ default:
+ ERROR("Unknown address family for packet");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Initialize.
+ */
+static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
+{
+ int ret;
+ rlm_preprocess_t *inst = instance;
+
+ /*
+ * Read the huntgroups file.
+ */
+ if (inst->huntgroup_file) {
+ ret = pairlist_read(inst, inst->huntgroup_file, &(inst->huntgroups), 0);
+ if (ret < 0) {
+ ERROR("rlm_preprocess: Error reading %s", inst->huntgroup_file);
+
+ return -1;
+ }
+ }
+
+ /*
+ * Read the hints file.
+ */
+ if (inst->hints_file) {
+ ret = pairlist_read(inst, inst->hints_file, &(inst->hints), 0);
+ if (ret < 0) {
+ ERROR("rlm_preprocess: Error reading %s", inst->hints_file);
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Preprocess a request.
+ */
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
+{
+ int r;
+ rlm_preprocess_t *inst = instance;
+
+ VALUE_PAIR *vp;
+
+ /*
+ * Mangle the username, to get rid of stupid implementation
+ * bugs.
+ */
+ rad_mangle(inst, request);
+
+ if (inst->with_ascend_hack) {
+ /*
+ * If we're using Ascend systems, hack the NAS-Port-Id
+ * in place, to go from Ascend's weird values to something
+ * approaching rationality.
+ */
+ ascend_nasport_hack(fr_pair_find_by_num(request->packet->vps, PW_NAS_PORT, 0, TAG_ANY),
+ inst->ascend_channels_per_line);
+ }
+
+ if (inst->with_cisco_vsa_hack) {
+ /*
+ * We need to run this hack because the h323-conf-id
+ * attribute should be used.
+ */
+ cisco_vsa_hack(request);
+ }
+
+ if (inst->with_alvarion_vsa_hack) {
+ /*
+ * We need to run this hack because the Alvarion
+ * people are crazy.
+ */
+ alvarion_vsa_hack(request->packet->vps);
+ }
+
+ if (inst->with_cablelabs_vsa_hack) {
+ /*
+ * We need to run this hack because the Cablelabs
+ * people are crazy.
+ */
+ cablelabs_vsa_hack(&request->packet->vps);
+ }
+
+ /*
+ * Add an event timestamp. Means Event-Timestamp can be used
+ * consistently instead of one letter expansions.
+ */
+ vp = fr_pair_find_by_num(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY);
+ if (!vp) {
+ vp = radius_pair_create(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0);
+ vp->vp_date = request->packet->timestamp.tv_sec;
+ }
+
+ /*
+ * Note that we add the Request-Src-IP-Address to the request
+ * structure BEFORE checking huntgroup access. This allows
+ * the Request-Src-IP-Address to be used for huntgroup
+ * comparisons.
+ */
+ if (add_nas_attr(request) < 0) {
+ return RLM_MODULE_FAIL;
+ }
+
+ hints_setup(inst->hints, request);
+
+ /*
+ * If there is a PW_CHAP_PASSWORD attribute but there
+ * is PW_CHAP_CHALLENGE we need to add it so that other
+ * modules can use it as a normal attribute.
+ */
+ if (fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
+ fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
+ vp = radius_pair_create(request->packet, &request->packet->vps, PW_CHAP_CHALLENGE, 0);
+ fr_pair_value_memcpy(vp, request->packet->vector, AUTH_VECTOR_LEN);
+ }
+
+ if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) {
+ char buf[1024];
+ RIDEBUG("No huntgroup access: [%s] (%s)",
+ request->username ? request->username->vp_strvalue : "<NO User-Name>",
+ auth_name(buf, sizeof(buf), request, 1));
+
+ return r;
+ }
+
+ return RLM_MODULE_OK; /* Meaning: try next authorization module */
+}
+
+/*
+ * Preprocess a request before accounting
+ */
+static rlm_rcode_t CC_HINT(nonnull) mod_preaccounting(void *instance, REQUEST *request)
+{
+ int r;
+ VALUE_PAIR *vp;
+ rlm_preprocess_t *inst = instance;
+
+ /*
+ * Ensure that we have the SAME user name for both
+ * authentication && accounting.
+ */
+ rad_mangle(inst, request);
+
+ if (inst->with_cisco_vsa_hack) {
+ /*
+ * We need to run this hack because the h323-conf-id
+ * attribute should be used.
+ */
+ cisco_vsa_hack(request);
+ }
+
+ if (inst->with_alvarion_vsa_hack) {
+ /*
+ * We need to run this hack because the Alvarion
+ * people are crazy.
+ */
+ alvarion_vsa_hack(request->packet->vps);
+ }
+
+ if (inst->with_cablelabs_vsa_hack) {
+ /*
+ * We need to run this hack because the Cablelabs
+ * people are crazy.
+ */
+ cablelabs_vsa_hack(&request->packet->vps);
+ }
+
+ /*
+ * Ensure that we log the NAS IP Address in the packet.
+ */
+ if (add_nas_attr(request) < 0) {
+ return RLM_MODULE_FAIL;
+ }
+
+ hints_setup(inst->hints, request);
+
+ /*
+ * Add an event timestamp. This means that the rest of
+ * the server can use it, rather than various error-prone
+ * manual calculations.
+ */
+ vp = fr_pair_find_by_num(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY);
+ if (!vp) {
+ VALUE_PAIR *delay;
+
+ vp = radius_pair_create(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0);
+ vp->vp_date = request->packet->timestamp.tv_sec;
+
+ delay = fr_pair_find_by_num(request->packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
+ if (delay) {
+ if ((delay->vp_integer >= vp->vp_date) || (delay->vp_integer == UINT32_MAX)) {
+ RWARN("Ignoring invalid Acct-Delay-time of %u seconds", delay->vp_integer);
+ } else {
+ vp->vp_date -= delay->vp_integer;
+ }
+ }
+ }
+
+ if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) {
+ char buf[1024];
+ RIDEBUG("No huntgroup access: [%s] (%s)",
+ request->username ? request->username->vp_strvalue : "<NO User-Name>",
+ auth_name(buf, sizeof(buf), request, 1));
+ return r;
+ }
+
+ return r;
+}
+
+/* globally exported name */
+extern module_t rlm_preprocess;
+module_t rlm_preprocess = {
+ .magic = RLM_MODULE_INIT,
+ .name = "preprocess",
+ .inst_size = sizeof(rlm_preprocess_t),
+ .config = module_config,
+ .instantiate = mod_instantiate,
+ .methods = {
+ [MOD_AUTHORIZE] = mod_authorize,
+ [MOD_PREACCT] = mod_preaccounting
+ },
+};
+