diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
commit | af754e596a8dbb05ed8580c342e7fe02e08b28e0 (patch) | |
tree | b2f334c2b55ede42081aa6710a72da784547d8ea /src/modules/rlm_replicate | |
parent | Initial commit. (diff) | |
download | freeradius-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_replicate')
-rw-r--r-- | src/modules/rlm_replicate/README.md | 10 | ||||
-rw-r--r-- | src/modules/rlm_replicate/all.mk | 2 | ||||
-rw-r--r-- | src/modules/rlm_replicate/rlm_replicate.c | 290 |
3 files changed, 302 insertions, 0 deletions
diff --git a/src/modules/rlm_replicate/README.md b/src/modules/rlm_replicate/README.md new file mode 100644 index 0000000..8563983 --- /dev/null +++ b/src/modules/rlm_replicate/README.md @@ -0,0 +1,10 @@ +# rlm_replicate +## Metadata +<dl> + <dt>category</dt><dd>io</dd> +</dl> + +## Summary + +Provide a way to make a copy of incoming packets and forward them +to another server. diff --git a/src/modules/rlm_replicate/all.mk b/src/modules/rlm_replicate/all.mk new file mode 100644 index 0000000..d49dd26 --- /dev/null +++ b/src/modules/rlm_replicate/all.mk @@ -0,0 +1,2 @@ +TARGET := rlm_replicate.a +SOURCES := rlm_replicate.c diff --git a/src/modules/rlm_replicate/rlm_replicate.c b/src/modules/rlm_replicate/rlm_replicate.c new file mode 100644 index 0000000..cee26cc --- /dev/null +++ b/src/modules/rlm_replicate/rlm_replicate.c @@ -0,0 +1,290 @@ +/* + * 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_replicate.c + * @brief Duplicate RADIUS requests. + * + * @copyright 2011-2013 The FreeRADIUS server project + */ +RCSID("$Id$") + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/modules.h> + +#ifdef WITH_PROXY +static void cleanup(RADIUS_PACKET *packet) +{ + if (!packet) return; + if (packet->sockfd >= 0) { + close(packet->sockfd); + } + + rad_free(&packet); +} + +/** Copy packet to multiple servers + * + * Create a duplicate of the packet and send it to a list of realms + * defined by the presence of the Replicate-To-Realm VP in the control + * list of the current request. + * + * This is pretty hacky and is 100% fire and forget. If you're looking + * to forward authentication requests to multiple realms and process + * the responses, this function will not allow you to do that. + * + * @param[in] instance of this module. + * @param[in] request The current request. + * @param[in] list of attributes to copy to the duplicate packet. + * @param[in] code to write into the code field of the duplicate packet. + * @return RCODE fail on error, invalid if list does not exist, noop if no replications succeeded, else ok. + */ +static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, unsigned int code) +{ + int rcode = RLM_MODULE_NOOP; + VALUE_PAIR *vp, **vps; + vp_cursor_t cursor; + home_server_t *home; + REALM *realm; + home_pool_t *pool; + RADIUS_PACKET *packet = NULL; + + /* + * Send as many packets as necessary to different + * destinations. + */ + fr_cursor_init(&cursor, &request->config); + while ((vp = fr_cursor_next_by_num(&cursor, PW_REPLICATE_TO_REALM, 0, TAG_ANY))) { + realm = realm_find2(vp->vp_strvalue); + if (!realm) { + REDEBUG2("Cannot Replicate to unknown realm \"%s\"", vp->vp_strvalue); + continue; + } + + /* + * We shouldn't really do this on every loop. + */ + switch (request->packet->code) { + default: + REDEBUG2("Cannot replicate unknown packet code %d", request->packet->code); + cleanup(packet); + return RLM_MODULE_FAIL; + + case PW_CODE_ACCESS_REQUEST: + pool = realm->auth_pool; + break; + +#ifdef WITH_ACCOUNTING + + case PW_CODE_ACCOUNTING_REQUEST: + pool = realm->acct_pool; + break; +#endif + +#ifdef WITH_COA + case PW_CODE_COA_REQUEST: + case PW_CODE_DISCONNECT_REQUEST: + pool = realm->coa_pool; + break; +#endif + } + + if (!pool) { + RWDEBUG2("Cancelling replication to Realm %s, as the realm is local", realm->name); + continue; + } + + home = home_server_ldb(realm->name, pool, request); + if (!home) { + REDEBUG2("Failed to find live home server for realm %s", realm->name); + continue; + } + +#ifdef WITH_TCP + if (home->proto != IPPROTO_UDP) { + REDEBUG("The replicate module only does UDP - Cannot send to TCP home_server %s", home->name); + continue; + } +#endif + +#ifdef WITH_TLS + if (home->tls) { + REDEBUG("The replicate module only does UDP - Cannot send to TLS home_server %s", home->name); + continue; + } +#endif + + /* + * For replication to multiple servers we re-use the packet + * we built here. + */ + if (!packet) { + packet = rad_alloc(request, true); + if (!packet) { + return RLM_MODULE_FAIL; + } + + packet->code = code; + packet->id = fr_rand() & 0xff; + packet->sockfd = fr_socket(&home->src_ipaddr, 0); + if (packet->sockfd < 0) { + REDEBUG("Failed opening socket: %s", fr_strerror()); + rcode = RLM_MODULE_FAIL; + goto done; + } + + vps = radius_list(request, list); + if (!vps) { + RWDEBUG("List '%s' doesn't exist for this packet", + fr_int2str(pair_lists, list, "<INVALID>")); + rcode = RLM_MODULE_INVALID; + goto done; + } + + /* + * Don't assume the list actually contains any + * attributes. + */ + if (*vps) { + packet->vps = fr_pair_list_copy(packet, *vps); + if (!packet->vps) { + rcode = RLM_MODULE_FAIL; + goto done; + } + } + + /* + * For CHAP, create the CHAP-Challenge if + * it doesn't exist. + */ + if ((code == PW_CODE_ACCESS_REQUEST) && + (fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) && + (fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) { + uint8_t *p; + vp = radius_pair_create(packet, &packet->vps, PW_CHAP_CHALLENGE, 0); + vp->length = AUTH_VECTOR_LEN; + vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length); + memcpy(p, request->packet->vector, AUTH_VECTOR_LEN); + } + } else { + size_t i; + + for (i = 0; i < sizeof(packet->vector); i++) { + packet->vector[i] = fr_rand() & 0xff; + } + + packet->id++; + TALLOC_FREE(packet->data); + packet->data_len = 0; + } + + /* + * (Re)-Write these. + */ + packet->dst_ipaddr = home->ipaddr; + packet->dst_port = home->port; + memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); + packet->src_port = 0; + + /* + * Encode, sign and then send the packet. + */ + RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "<INVALID>"), + realm->name); + if (rad_send(packet, NULL, home->secret) < 0) { + REDEBUG("Failed replicating packet: %s", fr_strerror()); + rcode = RLM_MODULE_FAIL; + goto done; + } + + /* + * We've sent it to at least one destination. + */ + rcode = RLM_MODULE_OK; + } + + done: + + cleanup(packet); + return rcode; +} +#else +static rlm_rcode_t replicate_packet(UNUSED void *instance, + UNUSED REQUEST *request, + UNUSED pair_lists_t list, + UNUSED unsigned int code) +{ + RDEBUG("Replication is unsupported in this build"); + return RLM_MODULE_FAIL; +} +#endif + +static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request) +{ + return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code); +} + +static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request) +{ + return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code); +} + +static rlm_rcode_t CC_HINT(nonnull) mod_preaccounting(void *instance, REQUEST *request) +{ + return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code); +} + +#ifdef WITH_PROXY +static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(void *instance, REQUEST *request) +{ + return replicate_packet(instance, request, PAIR_LIST_PROXY_REQUEST, request->proxy->code); +} +#endif + +#ifdef WITH_COA +static rlm_rcode_t CC_HINT(nonnull) mod_recv_coa(void *instance, REQUEST *request) +{ + return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code); +} +#endif + +/* + * 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_replicate; +module_t rlm_replicate = { + .magic = RLM_MODULE_INIT, + .name = "replicate", + .type = RLM_TYPE_THREAD_SAFE, + .methods = { + [MOD_AUTHORIZE] = mod_authorize, + [MOD_ACCOUNTING] = mod_accounting, + [MOD_PREACCT] = mod_preaccounting, +#ifdef WITH_PROXY + [MOD_PRE_PROXY] = mod_pre_proxy, +#endif +#ifdef WITH_COA + [MOD_RECV_COA] = mod_recv_coa +#endif + }, +}; |