2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2 if the
4 * License as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 * @file rlm_replicate.c
19 * @brief Duplicate RADIUS requests.
21 * @copyright 2011-2013 The FreeRADIUS server project
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/modules.h>
29 static void cleanup(RADIUS_PACKET *packet)
32 if (packet->sockfd >= 0) {
33 close(packet->sockfd);
39 /** Copy packet to multiple servers
41 * Create a duplicate of the packet and send it to a list of realms
42 * defined by the presence of the Replicate-To-Realm VP in the control
43 * list of the current request.
45 * This is pretty hacky and is 100% fire and forget. If you're looking
46 * to forward authentication requests to multiple realms and process
47 * the responses, this function will not allow you to do that.
49 * @param[in] instance of this module.
50 * @param[in] request The current request.
51 * @param[in] list of attributes to copy to the duplicate packet.
52 * @param[in] code to write into the code field of the duplicate packet.
53 * @return RCODE fail on error, invalid if list does not exist, noop if no replications succeeded, else ok.
55 static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, unsigned int code)
57 int rcode = RLM_MODULE_NOOP;
58 VALUE_PAIR *vp, **vps;
63 RADIUS_PACKET *packet = NULL;
66 * Send as many packets as necessary to different
69 fr_cursor_init(&cursor, &request->config_items);
70 while ((vp = fr_cursor_next_by_num(&cursor, PW_REPLICATE_TO_REALM, 0, TAG_ANY))) {
71 realm = realm_find2(vp->vp_strvalue);
73 REDEBUG2("Cannot Replicate to unknown realm \"%s\"", vp->vp_strvalue);
78 * We shouldn't really do this on every loop.
80 switch (request->packet->code) {
82 REDEBUG2("Cannot replicate unknown packet code %d", request->packet->code);
84 return RLM_MODULE_FAIL;
86 case PW_CODE_ACCESS_REQUEST:
87 pool = realm->auth_pool;
90 #ifdef WITH_ACCOUNTING
92 case PW_CODE_ACCOUNTING_REQUEST:
93 pool = realm->acct_pool;
98 case PW_CODE_COA_REQUEST:
99 case PW_CODE_DISCONNECT_REQUEST:
100 pool = realm->acct_pool;
106 RWDEBUG2("Cancelling replication to Realm %s, as the realm is local", realm->name);
110 home = home_server_ldb(realm->name, pool, request);
112 REDEBUG2("Failed to find live home server for realm %s", realm->name);
117 * For replication to multiple servers we re-use the packet
121 packet = rad_alloc(request, 1);
123 return RLM_MODULE_FAIL;
127 packet->id = fr_rand() & 0xff;
128 packet->sockfd = fr_socket(&home->src_ipaddr, 0);
129 if (packet->sockfd < 0) {
130 REDEBUG("Failed opening socket: %s", fr_strerror());
131 rcode = RLM_MODULE_FAIL;
135 vps = radius_list(request, list);
137 RWDEBUG("List '%s' doesn't exist for this packet",
138 fr_int2str(pair_lists, list, "<INVALID>"));
139 rcode = RLM_MODULE_INVALID;
144 * Don't assume the list actually contains any
148 packet->vps = paircopy(packet, *vps);
150 rcode = RLM_MODULE_FAIL;
156 * For CHAP, create the CHAP-Challenge if
159 if ((code == PW_CODE_ACCESS_REQUEST) &&
160 (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
161 (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
163 vp = radius_paircreate(packet, &packet->vps, PW_CHAP_CHALLENGE, 0);
164 vp->length = AUTH_VECTOR_LEN;
165 vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length);
166 memcpy(p, request->packet->vector, AUTH_VECTOR_LEN);
171 for (i = 0; i < sizeof(packet->vector); i++) {
172 packet->vector[i] = fr_rand() & 0xff;
176 talloc_free(packet->data);
178 packet->data_len = 0;
184 packet->dst_ipaddr = home->ipaddr;
185 packet->dst_port = home->port;
186 memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr));
187 packet->src_port = 0;
190 * Encode, sign and then send the packet.
192 RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "<INVALID>"),
194 if (rad_send(packet, NULL, home->secret) < 0) {
195 REDEBUG("Failed replicating packet: %s", fr_strerror());
196 rcode = RLM_MODULE_FAIL;
201 * We've sent it to at least one destination.
203 rcode = RLM_MODULE_OK;
212 static rlm_rcode_t replicate_packet(UNUSED void *instance,
213 UNUSED REQUEST *request,
214 UNUSED pair_lists_t list,
215 UNUSED unsigned int code)
217 RDEBUG("Replication is unsupported in this build");
218 return RLM_MODULE_FAIL;
222 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
224 return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code);
227 static rlm_rcode_t CC_HINT(nonnull) mod_preaccounting(void *instance, REQUEST *request)
229 return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code);
233 static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(void *instance, REQUEST *request)
235 return replicate_packet(instance, request, PAIR_LIST_PROXY_REQUEST, request->proxy->code);
240 static rlm_rcode_t CC_HINT(nonnull) mod_recv_coa(void *instance, REQUEST *request)
242 return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code);
247 * The module name should be the only globally exported symbol.
248 * That is, everything else should be 'static'.
250 * If the module needs to temporarily modify it's instantiation
251 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
252 * The server will then take care of ensuring that the module
253 * is single-threaded.
255 module_t rlm_replicate = {
258 RLM_TYPE_THREAD_SAFE, /* type */
260 NULL, /* CONF_PARSER */
261 NULL, /* instantiation */
264 NULL, /* authentication */
265 mod_authorize, /* authorization */
266 mod_preaccounting, /* preaccounting */
267 NULL, /* accounting */
268 NULL, /* checksimul */
270 mod_pre_proxy, /* pre-proxy */
271 NULL, /* post-proxy */
277 , mod_recv_coa, /* coa-request */