Move ident.h macros into build.h
[freeradius.git] / src / modules / rlm_replicate / rlm_replicate.c
1 /*
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.
5  *
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.
10  *
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
14  */
15
16 /**
17  * $Id$
18  * @file rlm_replicate.c
19  * @brief Duplicate RADIUS requests.
20  *
21  * @copyright 2011-2013  The FreeRADIUS server project
22  */
23 RCSID("$Id$")
24
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/modules.h>
27
28 #ifdef WITH_PROXY
29 static void cleanup(RADIUS_PACKET *packet)
30 {
31         if (!packet) return;
32         if (packet->sockfd >= 0) close(packet->sockfd);
33         rad_free(&packet);
34 }
35
36 /** Copy packet to multiple servers
37  *
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.
41  *
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.
45  *
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.
52  */
53 static int replicate_packet(UNUSED void *instance, REQUEST *request,
54                             pair_lists_t list, unsigned int code)
55 {
56         int rcode = RLM_MODULE_NOOP;
57         VALUE_PAIR *vp, **vps, *last;
58         home_server *home;
59         REALM *realm;
60         home_pool_t *pool;
61         RADIUS_PACKET *packet = NULL;
62
63         last = request->config_items;
64
65         /*
66          *      Send as many packets as necessary to different
67          *      destinations.
68          */
69         while (1) {
70                 vp = pairfind(last, PW_REPLICATE_TO_REALM, 0, TAG_ANY);
71                 if (!vp) break;
72
73                 last = vp->next;
74
75                 realm = realm_find2(vp->vp_strvalue);
76                 if (!realm) {
77                         RDEBUG2E("Cannot Replicate to unknown realm %s", realm);
78                         continue;
79                 }
80                 
81                 /*
82                  *      We shouldn't really do this on every loop.
83                  */
84                 switch (request->packet->code) {
85                 default:
86                         RDEBUG2E("Cannot replicate unknown packet code %d",
87                                 request->packet->code);
88                         cleanup(packet);
89                         return RLM_MODULE_FAIL;
90                 
91                 case PW_AUTHENTICATION_REQUEST:
92                         pool = realm->auth_pool;
93                         break;
94                         
95 #ifdef WITH_ACCOUNTING
96                         
97                 case PW_ACCOUNTING_REQUEST:
98                         pool = realm->acct_pool;
99                         break;
100 #endif
101                         
102 #ifdef WITH_COA
103                 case PW_COA_REQUEST:
104                 case PW_DISCONNECT_REQUEST:
105                         pool = realm->acct_pool;
106                         break;
107 #endif
108                 }
109                 
110                 if (!pool) {
111                         RDEBUG2W("Cancelling replication to Realm %s, as the realm is local.", realm->name);
112                         continue;
113                 }
114                 
115                 home = home_server_ldb(realm->name, pool, request);
116                 if (!home) {
117                         RDEBUG2E("Failed to find live home server for realm %s",
118                                 realm->name);
119                         continue;
120                 }
121                 
122                 /*
123                  *      For replication to multiple servers we re-use the packet
124                  *      we built here.
125                  */
126                 if (!packet) {
127                         packet = rad_alloc(NULL, 1);
128                         if (!packet) return RLM_MODULE_FAIL;
129                         packet->sockfd = -1;
130                         packet->code = code;
131                         packet->id = fr_rand() & 0xff;
132
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;
137                                 goto done;
138                         }
139                         
140                         vps = radius_list(request, list);
141                         if (!vps) {
142                                 RDEBUGW("List '%s' doesn't exist for "
143                                        "this packet", fr_int2str(pair_lists,
144                                        list, "?unknown?"));
145                                 rcode = RLM_MODULE_INVALID;
146                                 goto done;
147                         }
148                         
149                         /*
150                          *      Don't assume the list actually contains any
151                          *      attributes.
152                          */
153                         if (*vps) {
154                                 packet->vps = paircopy(packet, *vps);
155                                 if (!packet->vps) {
156                                         rcode = RLM_MODULE_FAIL;
157                                         goto done;
158                                 }
159                         }
160                         
161
162
163                         /*
164                          *      For CHAP, create the CHAP-Challenge if
165                          *      it doesn't exist.
166                          */
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,
174                                        AUTH_VECTOR_LEN);
175                         }
176                 } else {
177                         size_t i;
178
179                         for (i = 0; i < sizeof(packet->vector); i++) {
180                                 packet->vector[i] = fr_rand() & 0xff;
181                         }
182
183                         packet->id++;
184                         free(packet->data);
185                         packet->data = NULL;
186                         packet->data_len = 0;
187                 }
188
189                 /*
190                  *      (Re)-Write these.
191                  */
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;
196                 
197                 /*
198                  *      Encode, sign and then send the packet.
199                  */
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",
204                                fr_strerror());
205                         rcode = RLM_MODULE_FAIL;
206                         goto done;
207                 }
208
209                 /*
210                  *      We've sent it to at least one destination.
211                  */
212                 rcode = RLM_MODULE_OK;
213         }
214         
215         done:
216         
217         cleanup(packet);
218         return rcode;
219 }
220 #else
221 static rlm_rcode_t replicate_packet(void *instance, REQUEST *request,
222                             pair_lists_t list, unsigned int code)
223 {
224         RDEBUG("Replication is unsupported in this build.");
225         return RLM_MODULE_FAIL;
226 }
227 #endif
228
229 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
230 {
231         return replicate_packet(instance, request, PAIR_LIST_REQUEST,
232                                 request->packet->code);
233 }
234
235 static rlm_rcode_t mod_preaccounting(void *instance, REQUEST *request)
236 {
237         return replicate_packet(instance, request, PAIR_LIST_REQUEST,
238                                 request->packet->code);
239 }
240
241 static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
242 {
243         return replicate_packet(instance, request, PAIR_LIST_REPLY,
244                                 request->reply->code);
245 }
246
247 static rlm_rcode_t mod_pre_proxy(void *instance, REQUEST *request)
248 {
249         return replicate_packet(instance, request, PAIR_LIST_PROXY_REQUEST,
250                                 request->proxy->code);
251 }
252
253 static rlm_rcode_t mod_post_proxy(void *instance, REQUEST *request)
254 {
255         return replicate_packet(instance, request, PAIR_LIST_PROXY_REPLY,
256                                 request->proxy_reply->code);
257 }
258
259 static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
260 {
261         return replicate_packet(instance, request, PAIR_LIST_REPLY,
262                                 request->reply->code);
263 }
264
265 static rlm_rcode_t mod_recv_coa(void *instance, REQUEST *request)
266 {
267         return replicate_packet(instance, request, PAIR_LIST_REQUEST,
268                                 request->packet->code);
269 }
270
271 /*
272  *      The module name should be the only globally exported symbol.
273  *      That is, everything else should be 'static'.
274  *
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.
279  */
280 module_t rlm_replicate = {
281         RLM_MODULE_INIT,
282         "replicate",
283         RLM_TYPE_THREAD_SAFE,           /* type */
284         0,
285         NULL,                           /* CONF_PARSER */
286         NULL,                           /* instantiation */
287         NULL,                           /* detach */
288         {
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 */
297 #ifdef WITH_COA
298                 , mod_recv_coa, /* coa-request */
299                 NULL
300 #endif
301         },
302 };