blob: c8f4333b718cd41dcf1f98f7c0d0ad439b05b87d [file] [log] [blame]
Brian Waters13d96012017-12-08 16:53:31 -06001/*********************************************************************************************************
2* Software License Agreement (BSD License) *
3* Author: Sebastien Decugis <sdecugis@freediameter.net> *
4* *
5* Copyright (c) 2013, WIDE Project and NICT *
6* All rights reserved. *
7* *
8* Redistribution and use of this software in source and binary forms, with or without modification, are *
9* permitted provided that the following conditions are met: *
10* *
11* * Redistributions of source code must retain the above *
12* copyright notice, this list of conditions and the *
13* following disclaimer. *
14* *
15* * Redistributions in binary form must reproduce the above *
16* copyright notice, this list of conditions and the *
17* following disclaimer in the documentation and/or other *
18* materials provided with the distribution. *
19* *
20* * Neither the name of the WIDE Project or NICT nor the *
21* names of its contributors may be used to endorse or *
22* promote products derived from this software without *
23* specific prior written permission of WIDE Project and *
24* NICT. *
25* *
26* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
27* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
28* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
29* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
30* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
32* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
33* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
34*********************************************************************************************************/
35
36/* See rgwx_echodrop.h for details */
37
38#include "rgwx_echodrop.h"
39
40struct sess_state {
41 struct fd_list sentinel;
42};
43
44/* If a session is destroyed, empty the list of ed_saved_attribute */
45static void state_delete(struct sess_state * arg, os0_t sid, void * opaque) {
46 while (!FD_IS_LIST_EMPTY(&arg->sentinel)) {
47 struct ed_saved_attribute * esa = (struct ed_saved_attribute *)(arg->sentinel.next);
48 fd_list_unlink(&esa->chain);
49 free(esa);
50 }
51 free(arg);
52}
53
54static DECLARE_FD_DUMP_PROTOTYPE(ed_session_state_dump, struct sess_state * st)
55{
56 struct fd_list * li;
57 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "[rgwx sess_state](@%p):\n", st), return NULL);
58 for (li = st->sentinel.next; li != &st->sentinel; li = li->next) {
59 struct ed_saved_attribute * esa = (struct ed_saved_attribute *)(li);
60 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "[rgwx sess_state {esa}] t:%2hhx l:%2hhx d:", esa->attr.type, esa->attr.length), return NULL);
61 CHECK_MALLOC_DO( fd_dump_extend_hexdump(FD_DUMP_STD_PARAMS, (&esa->attr.length) + 1, esa->attr.length - 2, 0,0), return NULL);
62 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
63 }
64 return *buf;
65}
66
67/* Initialize the plugin and parse the configuration. */
68static int ed_conf_parse(char * conffile, struct rgwp_config ** state)
69{
70 struct rgwp_config * new;
71
72 TRACE_ENTRY("%p %p", conffile, state);
73 CHECK_PARAMS( state );
74 CHECK_PARAMS_DO( conffile, { fd_log_debug("[echodrop.rgwx] The configuration file is not optional for this plugin."); return EINVAL; } );
75
76 CHECK_MALLOC( new = malloc(sizeof(struct rgwp_config)) );
77 memset(new, 0, sizeof(struct rgwp_config));
78
79 /* Initialize the list of attributes to handle */
80 fd_list_init(&new->attributes, NULL);
81
82 /* Create the session handler */
83 CHECK_FCT( fd_sess_handler_create( &new->sess_hdl, state_delete, ed_session_state_dump, NULL ) );
84
85 /* Parse the configuration file */
86 CHECK_FCT( ed_conffile_parse(conffile, new) );
87
88 if (TRACE_BOOL(FULL)) {
89 TRACE_DEBUG(INFO, "Echo/Drop plugin configuration ('%s'):", conffile);
90 struct fd_list * li;
91
92 for (li = new->attributes.next; li != &new->attributes; li = li->next) {
93 struct ed_conf_attribute * eca = (struct ed_conf_attribute *)li;
94 char * act = (eca->action == ACT_ECHO) ? "ECHO" : "DROP";
95 if (eca->ext) {
96 fd_log_debug(" %s Code: %hhu, Vendor: %u, Ext-Type: %hu", act, eca->code, eca->vendor_id, eca->extype);
97 continue;
98 }
99 if (eca->tlv) {
100 fd_log_debug(" %s Code: %hhu, Vendor: %u, Type: %hhu", act, eca->code, eca->vendor_id, eca->type);
101 continue;
102 }
103 if (eca->vsa) {
104 fd_log_debug(" %s Code: %hhu, Vendor: %u", act, eca->code, eca->vendor_id);
105 continue;
106 }
107 fd_log_debug(" %s Code: %hhu", act, eca->code);
108 }
109 }
110
111 /* OK, we are done */
112 *state = new;
113 return 0;
114}
115
116/* Destroy the state */
117static void ed_conf_free(struct rgwp_config * state)
118{
119 TRACE_ENTRY("%p", state);
120 CHECK_PARAMS_DO( state, return );
121 CHECK_FCT_DO( fd_sess_handler_destroy( &state->sess_hdl, NULL ), );
122 while (! FD_IS_LIST_EMPTY(&state->attributes) ) {
123 struct fd_list * li = state->attributes.next;
124 fd_list_unlink(li);
125 free(li);
126 }
127 free(state);
128 return;
129}
130
131
132/* Handle attributes from a RADIUS request as specified in the configuration */
133static int ed_rad_req( struct rgwp_config * cs, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
134{
135 size_t nattr_used = 0;
136 int idx;
137 struct fd_list echo_list = FD_LIST_INITIALIZER(echo_list);
138 struct fd_list *li;
139
140 TRACE_ENTRY("%p %p %p %p %p", cs, rad_req, rad_ans, diam_fw, cli);
141 CHECK_PARAMS(cs && rad_req);
142
143 /* For each attribute in the original message */
144 for (idx = 0; idx < rad_req->attr_used; idx++) {
145 int action = 0;
146 struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
147
148 /* Look if we have a matching attribute in our configuration */
149 for (li = cs->attributes.next; li != &cs->attributes; li = li->next) {
150 struct ed_conf_attribute * eca = (struct ed_conf_attribute *)li;
151 uint32_t vid;
152 unsigned char * ptr;
153
154 if (eca->code < attr->type)
155 continue;
156 if (eca->code > attr->type)
157 break;
158
159 /* the code matches one in our configuration, check additional data if needed */
160
161 if (! eca->vsa) {
162 action = eca->action;
163 break;
164 }
165
166 if (attr->length < 8)
167 continue;
168
169 ptr = (unsigned char *)(attr + 1);
170 /* since attr is not aligned, we may not access *(attr+1) directly */
171 memcpy(&vid, ptr, sizeof(uint32_t));
172
173 if (eca->vendor_id < ntohl(vid))
174 continue;
175 if (eca->vendor_id > ntohl(vid))
176 break;
177
178 /* The vendor matches our configured line... */
179
180 if ( ! eca->tlv && ! eca->ext ) {
181 action = eca->action;
182 break;
183 }
184
185 if (attr->length < 10)
186 continue;
187
188 if (eca->tlv) {
189 struct radius_attr_vendor * tl = (struct radius_attr_vendor *)(ptr + sizeof(uint32_t));
190 if (tl->vendor_type == eca->type) {
191 action = eca->action;
192 break;
193 }
194 continue;
195 }
196
197 if (eca->ext) {
198 /* To be done */
199 fd_log_debug("Extended attributes are not implemented yet!");
200 ASSERT(0);
201 continue;
202 }
203 }
204
205 switch (action) {
206 case ACT_DROP:
207 TRACE_DEBUG(FULL, "Dropping attribute with code %hhd", attr->type);
208 break;
209
210 case ACT_ECHO:
211 {
212 struct ed_saved_attribute * esa = NULL;
213 TRACE_DEBUG(FULL, "Saving attribute with code %hhd", attr->type);
214 CHECK_MALLOC( esa = malloc(sizeof(struct ed_saved_attribute) + attr->length - sizeof(struct radius_attr_hdr)) );
215 fd_list_init(&esa->chain, NULL);
216 memcpy(&esa->attr, attr, attr->length);
217 fd_list_insert_before(&echo_list, &esa->chain);
218 }
219 break;
220
221 default: /* Attribute was not specified in the configuration */
222 /* We just keep the attribute in the RADIUS message */
223 rad_req->attr_pos[nattr_used++] = rad_req->attr_pos[idx];
224 }
225 }
226 rad_req->attr_used = nattr_used;
227
228 /* Save the echoed values in the session, if any */
229 if (!FD_IS_LIST_EMPTY(&echo_list)) {
230 struct session * sess;
231 struct sess_state * st;
232
233 CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_fw, &sess, NULL) );
234
235 CHECK_PARAMS_DO(sess,
236 {
237 fd_log_debug( "[echodrop.rgwx] The extension is configured to echo some attributes from this message, but no session object has been created for it (yet)."
238 " Please check your configuration file and include a session-generating extension BEFORE calling echodrop.rgwx to echo attributes."
239 " Please use debug.rgwx to retrieve more information." );
240 return EINVAL;
241 } );
242
243 /* Move the values in a dynamically allocated list */
244 CHECK_MALLOC( st = malloc(sizeof(struct sess_state)) );
245 fd_list_init(&st->sentinel, NULL);
246 fd_list_move_end(&st->sentinel, &echo_list);
247
248 /* Save the list in the session */
249 CHECK_FCT( fd_sess_state_store( cs->sess_hdl, sess, &st ) );
250 }
251
252 return 0;
253}
254
255/* Process an answer: add the ECHO attributes back, if any */
256static int ed_diam_ans( struct rgwp_config * cs, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
257{
258 struct session * sess;
259 struct sess_state * st;
260
261 TRACE_ENTRY("%p %p %p %p", cs, diam_ans, rad_fw, cli);
262 CHECK_PARAMS(cs);
263
264 CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_ans, &sess, NULL) );
265
266 /* If there is no session associated, just give up */
267 if (! sess ) {
268 TRACE_DEBUG(FULL, "No session associated with the message, nothing to do here...");
269 return 0;
270 }
271
272 /* Now try and retrieve any data from the session */
273 CHECK_FCT( fd_sess_state_retrieve( cs->sess_hdl, sess, &st ) );
274 if (st == NULL) {
275 /* No attribute saved in the session, just return */
276 return 0;
277 }
278
279 /* From this point on, we have a list of attributes to add to the radius message */
280
281 CHECK_PARAMS( rad_fw && *rad_fw);
282
283 while (! FD_IS_LIST_EMPTY(&st->sentinel) ) {
284 struct ed_saved_attribute * esa = (struct ed_saved_attribute *)(st->sentinel.next);
285
286 fd_list_unlink(&esa->chain);
287
288 TRACE_DEBUG(FULL, "Echo attribute in the RADIUS answer: type %hhu, len: %hhu", esa->attr.type, esa->attr.length);
289
290 /* Add this attribute in the RADIUS message */
291 CHECK_MALLOC( radius_msg_add_attr(*rad_fw, esa->attr.type, (unsigned char *)(esa + 1), esa->attr.length - sizeof(struct radius_attr_hdr)) );
292
293 free(esa);
294 }
295 free(st);
296
297 return 0;
298}
299
300
301
302/* The exported symbol */
303struct rgw_api rgwp_descriptor = {
304 .rgwp_name = "echo/drop",
305 .rgwp_conf_parse = ed_conf_parse,
306 .rgwp_conf_free = ed_conf_free,
307 .rgwp_rad_req = ed_rad_req,
308 .rgwp_diam_ans = ed_diam_ans
309};