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) close(packet->sockfd);
36 /** Copy packet to multiple servers
38 * Create a duplicate of the packet and send it to a list of realms
39 * defined by the presence of the Replicate-To-Realm VP in the control
40 * list of the current request.
42 * This is pretty hacky and is 100% fire and forget. If you're looking
43 * to forward authentication requests to multiple realms and process
44 * the responses, this function will not allow you to do that.
46 * @param[in] instance of this module.
47 * @param[in] request The current request.
48 * @param[in] list of attributes to copy to the duplicate packet.
49 * @param[in] code to write into the code field of the duplicate packet.
50 * @return RCODE fail on error, invalid if list does not exist, noop if no
51 * replications succeeded, else ok.
53 static int replicate_packet(UNUSED void *instance, REQUEST *request,
54 pair_lists_t list, unsigned int code)
56 int rcode = RLM_MODULE_NOOP;
57 VALUE_PAIR *vp, **vps, *last;
61 RADIUS_PACKET *packet = NULL;
63 last = request->config_items;
66 * Send as many packets as necessary to different
70 vp = pairfind(last, PW_REPLICATE_TO_REALM, 0, TAG_ANY);
75 realm = realm_find2(vp->vp_strvalue);
77 RDEBUG2E("Cannot Replicate to unknown realm %s", realm);
82 * We shouldn't really do this on every loop.
84 switch (request->packet->code) {
86 RDEBUG2E("Cannot replicate unknown packet code %d",
87 request->packet->code);
89 return RLM_MODULE_FAIL;
91 case PW_AUTHENTICATION_REQUEST:
92 pool = realm->auth_pool;
95 #ifdef WITH_ACCOUNTING
97 case PW_ACCOUNTING_REQUEST:
98 pool = realm->acct_pool;
104 case PW_DISCONNECT_REQUEST:
105 pool = realm->acct_pool;
111 RDEBUG2W("Cancelling replication to Realm %s, as the realm is local.", realm->name);
115 home = home_server_ldb(realm->name, pool, request);
117 RDEBUG2E("Failed to find live home server for realm %s",
123 * For replication to multiple servers we re-use the packet
127 packet = rad_alloc(NULL, 1);
128 if (!packet) return RLM_MODULE_FAIL;
131 packet->id = fr_rand() & 0xff;
133 packet->sockfd = fr_socket(&home->src_ipaddr, 0);
134 if (packet->sockfd < 0) {
135 RDEBUGE("Failed opening socket: %s", fr_strerror());
136 rcode = RLM_MODULE_FAIL;
140 vps = radius_list(request, list);
142 RDEBUGW("List '%s' doesn't exist for "
143 "this packet", fr_int2str(pair_lists,
145 rcode = RLM_MODULE_INVALID;
150 * Don't assume the list actually contains any
154 packet->vps = paircopy(packet, *vps);
156 rcode = RLM_MODULE_FAIL;
164 * For CHAP, create the CHAP-Challenge if
167 if ((code == PW_AUTHENTICATION_REQUEST) &&
168 (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
169 (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
170 vp = radius_paircreate(request, &packet->vps,
171 PW_CHAP_CHALLENGE, 0);
172 vp->length = AUTH_VECTOR_LEN;
173 memcpy(vp->vp_strvalue, request->packet->vector,
179 for (i = 0; i < sizeof(packet->vector); i++) {
180 packet->vector[i] = fr_rand() & 0xff;
186 packet->data_len = 0;
192 packet->dst_ipaddr = home->ipaddr;
193 packet->dst_port = home->port;
194 memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr));
195 packet->src_port = 0;
198 * Encode, sign and then send the packet.
200 RDEBUG("Replicating list '%s' to Realm '%s'",
201 fr_int2str(pair_lists, list, "¿unknown?"),realm->name);
202 if (rad_send(packet, NULL, home->secret) < 0) {
203 RDEBUGE("Failed replicating packet: %s",
205 rcode = RLM_MODULE_FAIL;
210 * We've sent it to at least one destination.
212 rcode = RLM_MODULE_OK;
221 static rlm_rcode_t replicate_packet(void *instance, REQUEST *request,
222 pair_lists_t list, unsigned int code)
224 RDEBUG("Replication is unsupported in this build.");
225 return RLM_MODULE_FAIL;
229 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
231 return replicate_packet(instance, request, PAIR_LIST_REQUEST,
232 request->packet->code);
235 static rlm_rcode_t mod_preaccounting(void *instance, REQUEST *request)
237 return replicate_packet(instance, request, PAIR_LIST_REQUEST,
238 request->packet->code);
241 static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
243 return replicate_packet(instance, request, PAIR_LIST_REPLY,
244 request->reply->code);
247 static rlm_rcode_t mod_pre_proxy(void *instance, REQUEST *request)
249 return replicate_packet(instance, request, PAIR_LIST_PROXY_REQUEST,
250 request->proxy->code);
253 static rlm_rcode_t mod_post_proxy(void *instance, REQUEST *request)
255 return replicate_packet(instance, request, PAIR_LIST_PROXY_REPLY,
256 request->proxy_reply->code);
259 static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
261 return replicate_packet(instance, request, PAIR_LIST_REPLY,
262 request->reply->code);
265 static rlm_rcode_t mod_recv_coa(void *instance, REQUEST *request)
267 return replicate_packet(instance, request, PAIR_LIST_REQUEST,
268 request->packet->code);
272 * The module name should be the only globally exported symbol.
273 * That is, everything else should be 'static'.
275 * If the module needs to temporarily modify it's instantiation
276 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
277 * The server will then take care of ensuring that the module
278 * is single-threaded.
280 module_t rlm_replicate = {
283 RLM_TYPE_THREAD_SAFE, /* type */
285 NULL, /* CONF_PARSER */
286 NULL, /* instantiation */
289 NULL, /* authentication */
290 mod_authorize, /* authorization */
291 mod_preaccounting,/* preaccounting */
292 mod_accounting, /* accounting */
293 NULL, /* checksimul */
294 mod_pre_proxy, /* pre-proxy */
295 mod_post_proxy, /* post-proxy */
296 mod_post_auth /* post-auth */
298 , mod_recv_coa, /* coa-request */