newvector should be a bool
[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) {
33                 close(packet->sockfd);
34         }
35
36         rad_free(&packet);
37 }
38
39 /** Copy packet to multiple servers
40  *
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.
44  *
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.
48  *
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.
54  */
55 static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, unsigned int code)
56 {
57         int rcode = RLM_MODULE_NOOP;
58         VALUE_PAIR *vp, **vps;
59         vp_cursor_t cursor;
60         home_server_t *home;
61         REALM *realm;
62         home_pool_t *pool;
63         RADIUS_PACKET *packet = NULL;
64
65         /*
66          *      Send as many packets as necessary to different
67          *      destinations.
68          */
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);
72                 if (!realm) {
73                         REDEBUG2("Cannot Replicate to unknown realm \"%s\"", vp->vp_strvalue);
74                         continue;
75                 }
76
77                 /*
78                  *      We shouldn't really do this on every loop.
79                  */
80                 switch (request->packet->code) {
81                 default:
82                         REDEBUG2("Cannot replicate unknown packet code %d", request->packet->code);
83                         cleanup(packet);
84                         return RLM_MODULE_FAIL;
85
86                 case PW_CODE_ACCESS_REQUEST:
87                         pool = realm->auth_pool;
88                         break;
89
90 #ifdef WITH_ACCOUNTING
91
92                 case PW_CODE_ACCOUNTING_REQUEST:
93                         pool = realm->acct_pool;
94                         break;
95 #endif
96
97 #ifdef WITH_COA
98                 case PW_CODE_COA_REQUEST:
99                 case PW_CODE_DISCONNECT_REQUEST:
100                         pool = realm->acct_pool;
101                         break;
102 #endif
103                 }
104
105                 if (!pool) {
106                         RWDEBUG2("Cancelling replication to Realm %s, as the realm is local", realm->name);
107                         continue;
108                 }
109
110                 home = home_server_ldb(realm->name, pool, request);
111                 if (!home) {
112                         REDEBUG2("Failed to find live home server for realm %s", realm->name);
113                         continue;
114                 }
115
116                 /*
117                  *      For replication to multiple servers we re-use the packet
118                  *      we built here.
119                  */
120                 if (!packet) {
121                         packet = rad_alloc(request, true);
122                         if (!packet) {
123                                 return RLM_MODULE_FAIL;
124                         }
125
126                         packet->code = code;
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;
132                                 goto done;
133                         }
134
135                         vps = radius_list(request, list);
136                         if (!vps) {
137                                 RWDEBUG("List '%s' doesn't exist for this packet",
138                                         fr_int2str(pair_lists, list, "<INVALID>"));
139                                 rcode = RLM_MODULE_INVALID;
140                                 goto done;
141                         }
142
143                         /*
144                          *      Don't assume the list actually contains any
145                          *      attributes.
146                          */
147                         if (*vps) {
148                                 packet->vps = paircopy(packet, *vps);
149                                 if (!packet->vps) {
150                                         rcode = RLM_MODULE_FAIL;
151                                         goto done;
152                                 }
153                         }
154
155                         /*
156                          *      For CHAP, create the CHAP-Challenge if
157                          *      it doesn't exist.
158                          */
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)) {
162                                 uint8_t *p;
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);
167                         }
168                 } else {
169                         size_t i;
170
171                         for (i = 0; i < sizeof(packet->vector); i++) {
172                                 packet->vector[i] = fr_rand() & 0xff;
173                         }
174
175                         packet->id++;
176                         talloc_free(packet->data);
177                         packet->data = NULL;
178                         packet->data_len = 0;
179                 }
180
181                 /*
182                  *      (Re)-Write these.
183                  */
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;
188
189                 /*
190                  *      Encode, sign and then send the packet.
191                  */
192                 RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "<INVALID>"),
193                        realm->name);
194                 if (rad_send(packet, NULL, home->secret) < 0) {
195                         REDEBUG("Failed replicating packet: %s", fr_strerror());
196                         rcode = RLM_MODULE_FAIL;
197                         goto done;
198                 }
199
200                 /*
201                  *      We've sent it to at least one destination.
202                  */
203                 rcode = RLM_MODULE_OK;
204         }
205
206         done:
207
208         cleanup(packet);
209         return rcode;
210 }
211 #else
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)
216 {
217         RDEBUG("Replication is unsupported in this build");
218         return RLM_MODULE_FAIL;
219 }
220 #endif
221
222 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
223 {
224         return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code);
225 }
226
227 static rlm_rcode_t CC_HINT(nonnull) mod_preaccounting(void *instance, REQUEST *request)
228 {
229         return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code);
230 }
231
232 #ifdef WITH_PROXY
233 static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(void *instance, REQUEST *request)
234 {
235         return replicate_packet(instance, request, PAIR_LIST_PROXY_REQUEST, request->proxy->code);
236 }
237 #endif
238
239 #ifdef WITH_COA
240 static rlm_rcode_t CC_HINT(nonnull) mod_recv_coa(void *instance, REQUEST *request)
241 {
242         return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code);
243 }
244 #endif
245
246 /*
247  *      The module name should be the only globally exported symbol.
248  *      That is, everything else should be 'static'.
249  *
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.
254  */
255 module_t rlm_replicate = {
256         RLM_MODULE_INIT,
257         "replicate",
258         RLM_TYPE_THREAD_SAFE,           /* type */
259         0,
260         NULL,                           /* CONF_PARSER */
261         NULL,                           /* instantiation */
262         NULL,                           /* detach */
263         {
264                 NULL,                   /* authentication */
265                 mod_authorize,          /* authorization */
266                 mod_preaccounting,      /* preaccounting */
267                 NULL,                   /* accounting */
268                 NULL,                   /* checksimul */
269 #ifdef WITH_PROXY
270                 mod_pre_proxy,          /* pre-proxy */
271                 NULL,                   /* post-proxy */
272 #else
273                 NULL, NULL,
274 #endif
275                 NULL                    /* post-auth */
276 #ifdef WITH_COA
277                 , mod_recv_coa,         /* coa-request */
278                 NULL
279 #endif
280         },
281 };