summaryrefslogtreecommitdiffstats
path: root/src/modules/rlm_replicate/rlm_replicate.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/rlm_replicate/rlm_replicate.c')
-rw-r--r--src/modules/rlm_replicate/rlm_replicate.c290
1 files changed, 290 insertions, 0 deletions
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
+ },
+};