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