Fix some clang specific compiler warnings
[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 #include <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28
29 #ifdef WITH_PROXY
30 static void cleanup(RADIUS_PACKET *packet)
31 {
32         if (!packet) return;
33         if (packet->sockfd >= 0) close(packet->sockfd);
34         rad_free(&packet);
35 }
36
37 /** Copy packet to multiple servers
38  *
39  * Create a duplicate of the packet and send it to a list of realms
40  * defined by the presence of the Replicate-To-Realm VP in the control
41  * list of the current request.
42  *
43  * This is pretty hacky and is 100% fire and forget. If you're looking
44  * to forward authentication requests to multiple realms and process
45  * the responses, this function will not allow you to do that.
46  *
47  * @param[in] instance  of this module.
48  * @param[in] request   The current request.
49  * @param[in] list      of attributes to copy to the duplicate packet.
50  * @param[in] code      to write into the code field of the duplicate packet.
51  * @return RCODE fail on error, invalid if list does not exist, noop if no
52  *         replications succeeded, else ok.
53  */
54 static int replicate_packet(UNUSED void *instance, REQUEST *request,
55                             pair_lists_t list, unsigned int code)
56 {
57         int rcode = RLM_MODULE_NOOP;
58         VALUE_PAIR *vp, **vps, *last;
59         home_server *home;
60         REALM *realm;
61         home_pool_t *pool;
62         RADIUS_PACKET *packet = NULL;
63
64         last = request->config_items;
65
66         /*
67          *      Send as many packets as necessary to different
68          *      destinations.
69          */
70         while (1) {
71                 vp = pairfind(last, PW_REPLICATE_TO_REALM, 0, TAG_ANY);
72                 if (!vp) break;
73
74                 last = vp->next;
75
76                 realm = realm_find2(vp->vp_strvalue);
77                 if (!realm) {
78                         RDEBUG2E("Cannot Replicate to unknown realm %s", realm);
79                         continue;
80                 }
81                 
82                 /*
83                  *      We shouldn't really do this on every loop.
84                  */
85                 switch (request->packet->code) {
86                 default:
87                         RDEBUG2E("Cannot replicate unknown packet code %d",
88                                 request->packet->code);
89                         cleanup(packet);
90                         return RLM_MODULE_FAIL;
91                 
92                 case PW_AUTHENTICATION_REQUEST:
93                         pool = realm->auth_pool;
94                         break;
95                         
96 #ifdef WITH_ACCOUNTING
97                         
98                 case PW_ACCOUNTING_REQUEST:
99                         pool = realm->acct_pool;
100                         break;
101 #endif
102                         
103 #ifdef WITH_COA
104                 case PW_COA_REQUEST:
105                 case PW_DISCONNECT_REQUEST:
106                         pool = realm->acct_pool;
107                         break;
108 #endif
109                 }
110                 
111                 if (!pool) {
112                         RDEBUG2W("Cancelling replication to Realm %s, as the realm is local.", realm->name);
113                         continue;
114                 }
115                 
116                 home = home_server_ldb(realm->name, pool, request);
117                 if (!home) {
118                         RDEBUG2E("Failed to find live home server for realm %s",
119                                 realm->name);
120                         continue;
121                 }
122                 
123                 /*
124                  *      For replication to multiple servers we re-use the packet
125                  *      we built here.
126                  */
127                 if (!packet) {
128                         packet = rad_alloc(NULL, 1);
129                         if (!packet) return RLM_MODULE_FAIL;
130                         packet->sockfd = -1;
131                         packet->code = code;
132                         packet->id = fr_rand() & 0xff;
133
134                         packet->sockfd = fr_socket(&home->src_ipaddr, 0);
135                         if (packet->sockfd < 0) {
136                                 RDEBUGE("Failed opening socket: %s", fr_strerror());
137                                 rcode = RLM_MODULE_FAIL;
138                                 goto done;
139                         }
140                         
141                         vps = radius_list(request, list);
142                         if (!vps) {
143                                 RDEBUGW("List '%s' doesn't exist for "
144                                        "this packet", fr_int2str(pair_lists,
145                                        list, "?unknown?"));
146                                 rcode = RLM_MODULE_INVALID;
147                                 goto done;
148                         }
149                         
150                         /*
151                          *      Don't assume the list actually contains any
152                          *      attributes.
153                          */
154                         if (*vps) {
155                                 packet->vps = paircopy(packet, *vps);
156                                 if (!packet->vps) {
157                                         rcode = RLM_MODULE_FAIL;
158                                         goto done;
159                                 }
160                         }
161                         
162
163
164                         /*
165                          *      For CHAP, create the CHAP-Challenge if
166                          *      it doesn't exist.
167                          */
168                         if ((code == PW_AUTHENTICATION_REQUEST) &&
169                             (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
170                             (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
171                                 vp = radius_paircreate(request, &packet->vps,
172                                                        PW_CHAP_CHALLENGE, 0);
173                                 vp->length = AUTH_VECTOR_LEN;
174                                 memcpy(vp->vp_strvalue, request->packet->vector,
175                                        AUTH_VECTOR_LEN);
176                         }
177                 } else {
178                         size_t i;
179
180                         for (i = 0; i < sizeof(packet->vector); i++) {
181                                 packet->vector[i] = fr_rand() & 0xff;
182                         }
183
184                         packet->id++;
185                         free(packet->data);
186                         packet->data = NULL;
187                         packet->data_len = 0;
188                 }
189
190                 /*
191                  *      (Re)-Write these.
192                  */
193                 packet->dst_ipaddr = home->ipaddr;
194                 packet->dst_port = home->port;
195                 memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr));
196                 packet->src_port = 0;
197                 
198                 /*
199                  *      Encode, sign and then send the packet.
200                  */
201                 RDEBUG("Replicating list '%s' to Realm '%s'",
202                        fr_int2str(pair_lists, list, "¿unknown?"),realm->name);
203                 if (rad_send(packet, NULL, home->secret) < 0) {
204                         RDEBUGE("Failed replicating packet: %s",
205                                fr_strerror());
206                         rcode = RLM_MODULE_FAIL;
207                         goto done;
208                 }
209
210                 /*
211                  *      We've sent it to at least one destination.
212                  */
213                 rcode = RLM_MODULE_OK;
214         }
215         
216         done:
217         
218         cleanup(packet);
219         return rcode;
220 }
221 #else
222 static rlm_rcode_t replicate_packet(void *instance, REQUEST *request,
223                             pair_lists_t list, unsigned int code)
224 {
225         RDEBUG("Replication is unsupported in this build.");
226         return RLM_MODULE_FAIL;
227 }
228 #endif
229
230 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
231 {
232         return replicate_packet(instance, request, PAIR_LIST_REQUEST,
233                                 request->packet->code);
234 }
235
236 static rlm_rcode_t mod_preaccounting(void *instance, REQUEST *request)
237 {
238         return replicate_packet(instance, request, PAIR_LIST_REQUEST,
239                                 request->packet->code);
240 }
241
242 static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
243 {
244         return replicate_packet(instance, request, PAIR_LIST_REPLY,
245                                 request->reply->code);
246 }
247
248 static rlm_rcode_t mod_pre_proxy(void *instance, REQUEST *request)
249 {
250         return replicate_packet(instance, request, PAIR_LIST_PROXY_REQUEST,
251                                 request->proxy->code);
252 }
253
254 static rlm_rcode_t mod_post_proxy(void *instance, REQUEST *request)
255 {
256         return replicate_packet(instance, request, PAIR_LIST_PROXY_REPLY,
257                                 request->proxy_reply->code);
258 }
259
260 static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
261 {
262         return replicate_packet(instance, request, PAIR_LIST_REPLY,
263                                 request->reply->code);
264 }
265
266 static rlm_rcode_t mod_recv_coa(void *instance, REQUEST *request)
267 {
268         return replicate_packet(instance, request, PAIR_LIST_REQUEST,
269                                 request->packet->code);
270 }
271
272 /*
273  *      The module name should be the only globally exported symbol.
274  *      That is, everything else should be 'static'.
275  *
276  *      If the module needs to temporarily modify it's instantiation
277  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
278  *      The server will then take care of ensuring that the module
279  *      is single-threaded.
280  */
281 module_t rlm_replicate = {
282         RLM_MODULE_INIT,
283         "replicate",
284         RLM_TYPE_THREAD_SAFE,           /* type */
285         0,
286         NULL,                           /* CONF_PARSER */
287         NULL,                           /* instantiation */
288         NULL,                           /* detach */
289         {
290                 NULL,                   /* authentication */
291                 mod_authorize,  /* authorization */
292                 mod_preaccounting,/* preaccounting */
293                 mod_accounting, /* accounting */
294                 NULL,                   /* checksimul */
295                 mod_pre_proxy,  /* pre-proxy */
296                 mod_post_proxy, /* post-proxy */
297                 mod_post_auth   /* post-auth */
298 #ifdef WITH_COA
299                 , mod_recv_coa, /* coa-request */
300                 NULL
301 #endif
302         },
303 };