/* * 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 #include #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, "")); 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, ""), 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 }, };