701ec85220a1e93c3650be60859b92edd3244439
[trust_router.git] / common / tr_filter.c
1 /*
2  * Copyright (c) 2012, 2013, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <talloc.h>
40 #include <assert.h>
41
42 #include <tr_filter.h>
43 #include <trp_internal.h>
44 #include <tid_internal.h>
45
46 const TR_FILTER_TYPE tr_filter_types[] = {
47     TR_FILTER_TYPE_TID_INBOUND,
48     TR_FILTER_TYPE_TRP_INBOUND,
49     TR_FILTER_TYPE_TRP_OUTBOUND
50 };
51 const size_t tr_num_filter_types=sizeof(tr_filter_types)/sizeof(tr_filter_types[0]);
52 static const char *tr_filter_type_strings[] = {
53     "tid_inbound",
54     "trp_inbound",
55     "trp_outbound"
56 };
57
58 const char *tr_filter_type_to_string(TR_FILTER_TYPE t) {
59   int ii;
60   for (ii=0; ii<tr_num_filter_types;ii++) {
61     if (t==tr_filter_types[ii])
62       return tr_filter_type_strings[ii];
63   }
64   return NULL;
65 }
66
67 TR_FILTER_TYPE tr_filter_type_from_string(const char *s)
68 {
69   int ii;
70   for (ii=0; ii<tr_num_filter_types; ii++) {
71     if (strcasecmp(s,  tr_filter_type_strings[ii])==0)
72       return tr_filter_types[ii];
73   }
74   return TR_FILTER_TYPE_UNKNOWN;
75 }
76
77 /* Function types for handling filter fields generally. All target values
78  * are represented as strings in a TR_NAME.
79  */
80 typedef int (*TR_FILTER_FIELD_CMP)(void *target, TR_NAME *val); /* returns 1 on match, 0 on no match */
81 typedef TR_NAME *(*TR_FILTER_FIELD_GET)(void *target); /* returns string form of the field value */
82
83 /* static handler prototypes */
84 static int tr_ff_cmp_tid_rp_realm(void *rp_req_arg, TR_NAME *val);
85 static TR_NAME *tr_ff_get_tid_rp_realm(void *rp_req_arg);
86 static int tr_ff_cmp_trp_info_type(void *inforec_arg, TR_NAME *val);
87 static TR_NAME *tr_ff_get_trp_info_type(void *inforec_arg);
88
89 /**
90  * Filter field handler table
91  */
92 struct tr_filter_field_entry {
93     TR_FILTER_TYPE filter_type;
94     const char *name;
95     TR_FILTER_FIELD_CMP cmp;
96     TR_FILTER_FIELD_GET get;
97 };
98 static struct tr_filter_field_entry tr_filter_field_table[] = {
99     {TR_FILTER_TYPE_TID_INBOUND, "rp_realm", tr_ff_cmp_tid_rp_realm, tr_ff_get_tid_rp_realm},
100     {TR_FILTER_TYPE_TRP_INBOUND, "info_type", tr_ff_cmp_trp_info_type, tr_ff_get_trp_info_type},
101     {TR_FILTER_TYPE_TRP_OUTBOUND, "info_type", tr_ff_cmp_trp_info_type, tr_ff_get_trp_info_type},
102     {TR_FILTER_TYPE_UNKNOWN, NULL } /* This must be the final entry */
103 };
104
105 static struct tr_filter_field_entry *tr_filter_field_entry(TR_FILTER_TYPE filter_type, TR_NAME *field_name)
106 {
107   unsigned int ii;
108
109   for (ii=0; tr_filter_field_table[ii].filter_type!=TR_FILTER_TYPE_UNKNOWN; ii++) {
110     if ((tr_filter_field_table[ii].filter_type==filter_type)
111         && (tr_name_cmp_str(field_name, tr_filter_field_table[ii].name)==0)) {
112       return tr_filter_field_table+ii;
113     }
114   }
115   return NULL;
116 }
117
118 static int tr_ff_cmp_tid_rp_realm(void *rp_req_arg, TR_NAME *val)
119 {
120   TID_REQ *req=talloc_get_type_abort(rp_req_arg, TID_REQ);
121   assert(req);
122   return 0==tr_name_cmp(val, req->rp_realm);
123 }
124
125 static TR_NAME *tr_ff_get_tid_rp_realm(void *rp_req_arg)
126 {
127   TID_REQ *req=talloc_get_type_abort(rp_req_arg, TID_REQ);
128   assert(req);
129   return tr_dup_name(req->rp_realm);
130 }
131
132 static int tr_ff_cmp_trp_info_type(void *inforec_arg, TR_NAME *val)
133 {
134   TRP_INFOREC *inforec=talloc_get_type_abort(inforec_arg, TRP_INFOREC);
135   char *valstr=NULL;
136   int val_type=0;
137
138   assert(val);
139   assert(inforec);
140
141   /* nothing matches unknown */
142   if (inforec->type==TRP_INFOREC_TYPE_UNKNOWN)
143     return 0;
144
145   valstr = tr_name_strdup(val); /* get this as an official null-terminated string */
146   val_type = trp_inforec_type_from_string(valstr);
147   free(valstr);
148
149   return (val_type==inforec->type);
150 }
151
152 static TR_NAME *tr_ff_get_trp_info_type(void *inforec_arg)
153 {
154   TRP_INFOREC *inforec=talloc_get_type_abort(inforec_arg, TRP_INFOREC);
155   return tr_new_name(trp_inforec_type_to_string(inforec->type));
156 }
157
158 /**
159  * Apply a filter to a target record or TID request.
160  *
161  * If one of the filter lines matches, out_action is set to the applicable action. If constraints
162  * is not NULL, the constraints from the matching filter line will be added to the constraint set
163  * *constraints, or to a new one if *constraints is NULL. In this case, TR_FILTER_MATCH will be
164  * returned.
165  *
166  * If there is no match, returns TR_FILTER_NO_MATCH, out_action is undefined, and constraints
167  * will not be changed.
168  *
169  * @param target Record or request to which the filter is applied
170  * @param filt Filter to apply
171  * @param constraints Pointer to existing set of constraints (NULL if not tracking constraints)
172  * @param out_action Action to be carried out (output)
173  * @return TR_FILTER_MATCH or TR_FILTER_NO_MATCH
174  */
175 int tr_filter_apply(void *target,
176                     TR_FILTER *filt,
177                     TR_CONSTRAINT_SET **constraints,
178                     TR_FILTER_ACTION *out_action)
179 {
180   unsigned int ii=0, jj=0;
181   int retval=TR_FILTER_NO_MATCH;
182
183   /* Default action is reject */
184   *out_action = TR_FILTER_ACTION_REJECT;
185
186   /* Validate filter */
187   if ((filt==NULL) || (filt->type==TR_FILTER_TYPE_UNKNOWN))
188     return TR_FILTER_NO_MATCH;
189
190   /* Step through filter lines looking for a match. If a line matches, retval
191    * will be set to TR_FILTER_MATCH, so stop then. */
192   for (ii=0, retval=TR_FILTER_NO_MATCH;
193        (ii<TR_MAX_FILTER_LINES) && (retval==TR_FILTER_NO_MATCH);
194        ii++) {
195     /* skip empty lines (these shouldn't really happen) */
196     if (filt->lines[ii]==NULL)
197       continue;
198
199     /* Assume we are going to succeed. If any specs fail to match, we'll set
200      * this to TR_FILTER_NO_MATCH. */
201     retval=TR_FILTER_MATCH;
202     for (jj=0; jj<TR_MAX_FILTER_SPECS; jj++) {
203       /* skip empty specs (these shouldn't really happen either) */
204       if (filt->lines[ii]->specs[jj]==NULL)
205         continue;
206
207       if (!tr_fspec_matches(filt->lines[ii]->specs[jj], filt->type, target)) {
208         retval=TR_FILTER_NO_MATCH; /* set this in case this is the last filter line */
209         break; /* give up on this filter line */
210       }
211     }
212   }
213
214   if (retval==TR_FILTER_MATCH) {
215     /* Matched line ii. Grab its action and constraints. */
216     *out_action = filt->lines[ii]->action;
217     if (constraints!=NULL) {
218       /* if either constraint is missing, these are no-ops */
219       tr_constraint_add_to_set(constraints, filt->lines[ii]->realm_cons);
220       tr_constraint_add_to_set(constraints, filt->lines[ii]->domain_cons);
221     }
222   }
223
224   return retval;
225 }
226
227 int tr_filter_process_rp_permitted(TR_NAME *rp_realm,
228                                    TR_FILTER *rpp_filter,
229                                    TR_CONSTRAINT_SET *in_constraints,
230                                    TR_CONSTRAINT_SET **out_constraints,
231                                    TR_FILTER_ACTION *out_action)
232 {
233   int i = 0, j = 0;
234
235   *out_action = TR_FILTER_ACTION_REJECT;
236   *out_constraints = NULL;
237
238   /* If this isn't a valid rp_permitted filter, return no match. */
239   if ((!rpp_filter) ||
240       (TR_FILTER_TYPE_TID_INBOUND != rpp_filter->type)) {
241     return TR_FILTER_NO_MATCH;
242   }
243
244   /* Check if there is a match for this filter. */
245   for (i = 0; i < TR_MAX_FILTER_LINES; i++) {
246     for (j = 0; j < TR_MAX_FILTER_SPECS; j++) {
247
248       if ((rpp_filter->lines[i]) &&
249           (rpp_filter->lines[i]->specs[j]) &&
250           (tr_fspec_matches(rpp_filter->lines[i]->specs[j], 0, rp_realm))) { /* todo: fix or remove */
251         *out_action = rpp_filter->lines[i]->action;
252         *out_constraints = in_constraints;
253         if (rpp_filter->lines[i]->realm_cons)
254           tr_constraint_add_to_set(out_constraints,
255                                    rpp_filter->lines[i]->realm_cons);
256         if (rpp_filter->lines[i]->domain_cons)
257           tr_constraint_add_to_set(out_constraints,
258                                    rpp_filter->lines[i]->domain_cons);
259
260         return TR_FILTER_MATCH;
261       }
262     }
263   }
264   /* If there is no match, indicate that. */
265   return TR_FILTER_NO_MATCH;
266 }
267
268 void tr_fspec_free(TR_FSPEC *fspec)
269 {
270   talloc_free(fspec);
271 }
272
273 static int tr_fspec_destructor(void *obj)
274 {
275   TR_FSPEC *fspec = talloc_get_type_abort(obj, TR_FSPEC);
276
277   if (fspec->field != NULL)
278     tr_free_name(fspec->field);
279   if (fspec->match != NULL)
280     tr_free_name(fspec->match);
281   return 0;
282 }
283
284 TR_FSPEC *tr_fspec_new(TALLOC_CTX *mem_ctx)
285 {
286   TR_FSPEC *fspec = talloc(mem_ctx, TR_FSPEC);
287
288   if (fspec != NULL) {
289     fspec->field = NULL;
290     fspec->match = NULL;
291     talloc_set_destructor((void *) fspec, tr_fspec_destructor);
292   }
293   return fspec;
294 }
295
296 void tr_fspec_set_match(TR_FSPEC *fspec, TR_NAME *match)
297 {
298   if (fspec->match != NULL)
299     tr_free_name(fspec->match);
300   fspec->match = match;
301 }
302
303 /* returns 1 if the spec matches */
304 int tr_fspec_matches(TR_FSPEC *fspec, TR_FILTER_TYPE ftype, void *target)
305 {
306   struct tr_filter_field_entry *field=NULL;
307   TR_NAME *name=NULL;
308
309   if ((fspec==NULL) || (fspec->match==NULL))
310     return 0;
311
312   /* Look up how to handle the requested field */
313   field = tr_filter_field_entry(ftype, fspec->field);
314   if (field==NULL)
315     return 0;
316
317   name=field->get(target);
318   return ((fspec->match != NULL) &&
319           (0 != tr_name_prefix_wildcard_match(name, fspec->match)));
320 }
321
322 void tr_fline_free(TR_FLINE *fline)
323 {
324   talloc_free(fline);
325 }
326
327 TR_FLINE *tr_fline_new(TALLOC_CTX *mem_ctx)
328 {
329   TR_FLINE *fl = talloc(mem_ctx, TR_FLINE);
330   int ii = 0;
331
332   if (fl != NULL) {
333     fl->action = TR_FILTER_ACTION_UNKNOWN;
334     fl->realm_cons = NULL;
335     fl->domain_cons = NULL;
336     for (ii = 0; ii < TR_MAX_FILTER_SPECS; ii++)
337       fl->specs[ii] = NULL;
338   }
339   return fl;
340 }
341
342 TR_FILTER *tr_filter_new(TALLOC_CTX *mem_ctx)
343 {
344   TR_FILTER *f = talloc(mem_ctx, TR_FILTER);
345   int ii = 0;
346
347   if (f != NULL) {
348     f->type = TR_FILTER_TYPE_UNKNOWN;
349     for (ii = 0; ii < TR_MAX_FILTER_LINES; ii++)
350       f->lines[ii] = NULL;
351   }
352   return f;
353 }
354
355 void tr_filter_free(TR_FILTER *filt)
356 {
357   talloc_free(filt);
358 }
359
360 void tr_filter_set_type(TR_FILTER *filt, TR_FILTER_TYPE type)
361 {
362   filt->type = type;
363 }
364
365 TR_FILTER_TYPE tr_filter_get_type(TR_FILTER *filt)
366 {
367   return filt->type;
368 }