Code for testing the filters now works
[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 /* Function types for handling filter fields generally. All target values
47  * are represented as strings in a TR_NAME.
48  */
49 typedef int (*TR_FILTER_FIELD_CMP)(void *target, TR_NAME *val); /* returns 1 on match, 0 on no match */
50 typedef TR_NAME *(*TR_FILTER_FIELD_GET)(void *target); /* returns string form of the field value */
51
52 /* static handler prototypes */
53 static int tr_ff_cmp_tid_rp_realm(void *rp_req_arg, TR_NAME *val);
54 static TR_NAME *tr_ff_get_tid_rp_realm(void *rp_req_arg);
55 static int tr_ff_cmp_trp_info_type(void *inforec_arg, TR_NAME *val);
56 static TR_NAME *tr_ff_get_trp_info_type(void *inforec_arg);
57
58 /**
59  * Filter field handler table
60  */
61 struct tr_filter_field_entry {
62     TR_FILTER_TYPE filter_type;
63     const char *name;
64     TR_FILTER_FIELD_CMP cmp;
65     TR_FILTER_FIELD_GET get;
66 };
67 static struct tr_filter_field_entry tr_filter_field_table[] = {
68     {TR_FILTER_TYPE_TID_INBOUND, "rp_realm", tr_ff_cmp_tid_rp_realm, tr_ff_get_tid_rp_realm},
69     {TR_FILTER_TYPE_TRP_INBOUND, "info_type", tr_ff_cmp_trp_info_type, tr_ff_get_trp_info_type},
70     {TR_FILTER_TYPE_TRP_OUTBOUND, "info_type", tr_ff_cmp_trp_info_type, tr_ff_get_trp_info_type},
71     {TR_FILTER_TYPE_UNKNOWN, NULL } /* This must be the final entry */
72 };
73
74 static struct tr_filter_field_entry *tr_filter_field_entry(TR_FILTER_TYPE filter_type, TR_NAME *field_name)
75 {
76   unsigned int ii;
77
78   for (ii=0; tr_filter_field_table[ii].filter_type!=TR_FILTER_TYPE_UNKNOWN; ii++) {
79     if ((tr_filter_field_table[ii].filter_type==filter_type)
80         && (tr_name_cmp_str(field_name, tr_filter_field_table[ii].name)==0)) {
81       return tr_filter_field_table+ii;
82     }
83   }
84   return NULL;
85 }
86
87 static int tr_ff_cmp_tid_rp_realm(void *rp_req_arg, TR_NAME *val)
88 {
89   TID_REQ *req=talloc_get_type_abort(rp_req_arg, TID_REQ);
90   assert(req);
91   return 0==tr_name_cmp(val, req->rp_realm);
92 }
93
94 static TR_NAME *tr_ff_get_tid_rp_realm(void *rp_req_arg)
95 {
96   TID_REQ *req=talloc_get_type_abort(rp_req_arg, TID_REQ);
97   assert(req);
98   return tr_dup_name(req->rp_realm);
99 }
100
101 static int tr_ff_cmp_trp_info_type(void *inforec_arg, TR_NAME *val)
102 {
103   TRP_INFOREC *inforec=talloc_get_type_abort(inforec_arg, TRP_INFOREC);
104   char *valstr=NULL;
105   int val_type=0;
106
107   assert(val);
108   assert(inforec);
109
110   /* nothing matches unknown */
111   if (inforec->type==TRP_INFOREC_TYPE_UNKNOWN)
112     return 0;
113
114   valstr = tr_name_strdup(val); /* get this as an official null-terminated string */
115   val_type = trp_inforec_type_from_string(valstr);
116   free(valstr);
117
118   return (val_type==inforec->type);
119 }
120
121 static TR_NAME *tr_ff_get_trp_info_type(void *inforec_arg)
122 {
123   TRP_INFOREC *inforec=talloc_get_type_abort(inforec_arg, TRP_INFOREC);
124   return tr_new_name(trp_inforec_type_to_string(inforec->type));
125 }
126
127 /**
128  * Apply a filter to a target record or TID request.
129  *
130  * If one of the filter lines matches, out_action is set to the applicable action. If constraints
131  * is not NULL, the constraints from the matching filter line will be added to the constraint set
132  * *constraints, or to a new one if *constraints is NULL. In this case, TR_FILTER_MATCH will be
133  * returned.
134  *
135  * If there is no match, returns TR_FILTER_NO_MATCH, out_action is undefined, and constraints
136  * will not be changed.
137  *
138  * @param target Record or request to which the filter is applied
139  * @param filt Filter to apply
140  * @param constraints Pointer to existing set of constraints (NULL if not tracking constraints)
141  * @param out_action Action to be carried out (output)
142  * @return TR_FILTER_MATCH or TR_FILTER_NO_MATCH
143  */
144 int tr_filter_apply(void *target,
145                     TR_FILTER *filt,
146                     TR_CONSTRAINT_SET **constraints,
147                     TR_FILTER_ACTION *out_action)
148 {
149   unsigned int ii=0, jj=0;
150   int retval=TR_FILTER_NO_MATCH;
151
152   /* Default action is reject */
153   *out_action = TR_FILTER_ACTION_REJECT;
154
155   /* Validate filter */
156   if ((filt==NULL) || (filt->type==TR_FILTER_TYPE_UNKNOWN))
157     return TR_FILTER_NO_MATCH;
158
159   /* Step through filter lines looking for a match. If a line matches, retval
160    * will be set to TR_FILTER_MATCH, so stop then. */
161   for (ii=0, retval=TR_FILTER_NO_MATCH;
162        ii<TR_MAX_FILTER_LINES;
163        ii++) {
164     /* skip empty lines (these shouldn't really happen) */
165     if (filt->lines[ii]==NULL)
166       continue;
167
168     /* Assume we are going to succeed. If any specs fail to match, we'll set
169      * this to TR_FILTER_NO_MATCH. */
170     retval=TR_FILTER_MATCH;
171     for (jj=0; jj<TR_MAX_FILTER_SPECS; jj++) {
172       /* skip empty specs (these shouldn't really happen either) */
173       if (filt->lines[ii]->specs[jj]==NULL)
174         continue;
175
176       if (!tr_fspec_matches(filt->lines[ii]->specs[jj], filt->type, target)) {
177         retval=TR_FILTER_NO_MATCH; /* set this in case this is the last filter line */
178         break; /* give up on this filter line */
179       }
180     }
181
182     if (retval==TR_FILTER_MATCH)
183       break;
184   }
185
186   if (retval==TR_FILTER_MATCH) {
187     /* Matched line ii. Grab its action and constraints. */
188     *out_action = filt->lines[ii]->action;
189     if (constraints!=NULL) {
190       /* if either constraint is missing, these are no-ops */
191       tr_constraint_add_to_set(constraints, filt->lines[ii]->realm_cons);
192       tr_constraint_add_to_set(constraints, filt->lines[ii]->domain_cons);
193     }
194   }
195
196   return retval;
197 }
198
199 int tr_filter_process_rp_permitted(TR_NAME *rp_realm,
200                                    TR_FILTER *rpp_filter,
201                                    TR_CONSTRAINT_SET *in_constraints,
202                                    TR_CONSTRAINT_SET **out_constraints,
203                                    TR_FILTER_ACTION *out_action)
204 {
205   int i = 0, j = 0;
206
207   *out_action = TR_FILTER_ACTION_REJECT;
208   *out_constraints = NULL;
209
210   /* If this isn't a valid rp_permitted filter, return no match. */
211   if ((!rpp_filter) ||
212       (TR_FILTER_TYPE_TID_INBOUND != rpp_filter->type)) {
213     return TR_FILTER_NO_MATCH;
214   }
215
216   /* Check if there is a match for this filter. */
217   for (i = 0; i < TR_MAX_FILTER_LINES; i++) {
218     for (j = 0; j < TR_MAX_FILTER_SPECS; j++) {
219
220       if ((rpp_filter->lines[i]) &&
221           (rpp_filter->lines[i]->specs[j]) &&
222           (tr_fspec_matches(rpp_filter->lines[i]->specs[j], 0, rp_realm))) { /* todo: fix or remove */
223         *out_action = rpp_filter->lines[i]->action;
224         *out_constraints = in_constraints;
225         if (rpp_filter->lines[i]->realm_cons)
226           tr_constraint_add_to_set(out_constraints,
227                                    rpp_filter->lines[i]->realm_cons);
228         if (rpp_filter->lines[i]->domain_cons)
229           tr_constraint_add_to_set(out_constraints,
230                                    rpp_filter->lines[i]->domain_cons);
231
232         return TR_FILTER_MATCH;
233       }
234     }
235   }
236   /* If there is no match, indicate that. */
237   return TR_FILTER_NO_MATCH;
238 }
239
240 void tr_fspec_free(TR_FSPEC *fspec)
241 {
242   talloc_free(fspec);
243 }
244
245 static int tr_fspec_destructor(void *obj)
246 {
247   TR_FSPEC *fspec = talloc_get_type_abort(obj, TR_FSPEC);
248   size_t ii;
249
250   if (fspec->field != NULL)
251     tr_free_name(fspec->field);
252   for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++) {
253     if (fspec->match[ii] != NULL)
254       tr_free_name(fspec->match[ii]);
255   }
256   return 0;
257 }
258
259 TR_FSPEC *tr_fspec_new(TALLOC_CTX *mem_ctx)
260 {
261   TR_FSPEC *fspec = talloc(mem_ctx, TR_FSPEC);
262   size_t ii=0;
263
264   if (fspec != NULL) {
265     fspec->field = NULL;
266     for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++)
267       fspec->match[ii] = NULL;
268
269     talloc_set_destructor((void *)fspec, tr_fspec_destructor);
270   }
271   return fspec;
272 }
273
274 void tr_fspec_add_match(TR_FSPEC *fspec, TR_NAME *match)
275 {
276   size_t ii;
277   for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++) {
278     if (fspec->match[ii]==NULL) {
279       fspec->match[ii]=match;
280       break;
281     }
282   }
283   /* TODO: handle case that adding the match failed */
284 }
285
286 /* returns 1 if the spec matches */
287 int tr_fspec_matches(TR_FSPEC *fspec, TR_FILTER_TYPE ftype, void *target)
288 {
289   struct tr_filter_field_entry *field=NULL;
290   TR_NAME *name=NULL;
291   size_t ii=0;
292
293   if (fspec==NULL)
294     return 0;
295
296   /* Look up how to handle the requested field */
297   field = tr_filter_field_entry(ftype, fspec->field);
298   if (field==NULL)
299     return 0;
300
301   name=field->get(target);
302   for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++) {
303     if (fspec->match[ii]!=NULL) {
304       if (tr_name_prefix_wildcard_match(name, fspec->match[ii]))
305         return 1;
306     }
307   }
308   return 0;
309 }
310
311 void tr_fline_free(TR_FLINE *fline)
312 {
313   talloc_free(fline);
314 }
315
316 TR_FLINE *tr_fline_new(TALLOC_CTX *mem_ctx)
317 {
318   TR_FLINE *fl = talloc(mem_ctx, TR_FLINE);
319   int ii = 0;
320
321   if (fl != NULL) {
322     fl->action = TR_FILTER_ACTION_UNKNOWN;
323     fl->realm_cons = NULL;
324     fl->domain_cons = NULL;
325     for (ii = 0; ii < TR_MAX_FILTER_SPECS; ii++)
326       fl->specs[ii] = NULL;
327   }
328   return fl;
329 }
330
331 TR_FILTER *tr_filter_new(TALLOC_CTX *mem_ctx)
332 {
333   TR_FILTER *f = talloc(mem_ctx, TR_FILTER);
334   int ii = 0;
335
336   if (f != NULL) {
337     f->type = TR_FILTER_TYPE_UNKNOWN;
338     for (ii = 0; ii < TR_MAX_FILTER_LINES; ii++)
339       f->lines[ii] = NULL;
340   }
341   return f;
342 }
343
344 void tr_filter_free(TR_FILTER *filt)
345 {
346   talloc_free(filt);
347 }
348
349 void tr_filter_set_type(TR_FILTER *filt, TR_FILTER_TYPE type)
350 {
351   filt->type = type;
352 }
353
354 TR_FILTER_TYPE tr_filter_get_type(TR_FILTER *filt)
355 {
356   return filt->type;
357 }
358
359 /**
360  * Check that a filter is valid, i.e., can be processed.
361  *
362  * @param filt Filter to verify
363  * @return 1 if the filter is valid, 0 otherwise
364  */
365 int tr_filter_validate(TR_FILTER *filt)
366 {
367   size_t ii=0, jj=0, kk=0;
368
369   if (!filt)
370     return 0;
371
372   /* check that we recognize the type */
373   switch(filt->type) {
374     case TR_FILTER_TYPE_TID_INBOUND:
375     case TR_FILTER_TYPE_TRP_INBOUND:
376     case TR_FILTER_TYPE_TRP_OUTBOUND:
377       break;
378
379     default:
380       return 0; /* if we get here, either TR_FILTER_TYPE_UNKNOWN or an invalid value was found */
381   }
382   for (ii=0; ii<TR_MAX_FILTER_LINES; ii++) {
383     if (filt->lines[ii]==NULL)
384       continue; /* an empty filter line is valid */
385
386     /* check that we recognize the action */
387     switch(filt->lines[ii]->action) {
388       case TR_FILTER_ACTION_ACCEPT:
389       case TR_FILTER_ACTION_REJECT:
390         break;
391
392       default:
393         /* if we get here, either TR_FILTER_ACTION_UNKNOWN or an invalid value was found */
394         return 0;
395     }
396
397     for (jj=0; jj<TR_MAX_FILTER_SPECS; jj++) {
398       if (filt->lines[ii]->specs[jj]==NULL)
399         continue; /* an empty filter spec is valid */
400
401       if (!tr_filter_validate_spec_field(filt->type, filt->lines[ii]->specs[jj]))
402         return 0;
403
404       /* check that at least one match is non-null */
405       for (kk=0; kk<TR_MAX_FILTER_SPEC_MATCHES; kk++) {
406         if (filt->lines[ii]->specs[jj]->match[kk]!=NULL)
407           break;
408       }
409       if (kk==TR_MAX_FILTER_SPEC_MATCHES)
410         return 0;
411     }
412   }
413
414   /* We ran the gauntlet. Success! */
415   return 1;
416 }
417
418 int tr_filter_validate_spec_field(TR_FILTER_TYPE ftype, TR_FSPEC *fspec)
419 {
420   if ((fspec==NULL) || (tr_filter_field_entry(ftype, fspec->field)==NULL))
421     return 0; /* unknown field */
422
423   return 1;
424 }
425
426 /**
427  * Allocate a new filter set.
428  *
429  * @param mem_ctx Talloc context for the new set
430  * @return Pointer to new set, or null on error
431  */
432 TR_FILTER_SET *tr_filter_set_new(TALLOC_CTX *mem_ctx)
433 {
434   TR_FILTER_SET *set=talloc(mem_ctx, TR_FILTER_SET);
435   if (set!=NULL) {
436     set->next=NULL;
437     set->this=NULL;
438   }
439   return set;
440 }
441
442 /**
443  * Free a filter set
444  *
445  * @param fs Filter set to free
446  */
447 void tr_filter_set_free(TR_FILTER_SET *fs)
448 {
449   talloc_free(fs);
450 }
451
452 /**
453  * Find the tail of the filter set linked list.
454  *
455  * @param set Set to find tail of
456  * @return Last element in the list
457  */
458 static TR_FILTER_SET *tr_filter_set_tail(TR_FILTER_SET *set)
459 {
460   while (set->next)
461     set=set->next;
462   return set;
463 }
464
465 /**
466  * Add new filter to filter set.
467  *
468  * @param set Filter set
469  * @param new New filter to add
470  * @return 0 on success, nonzero on error
471  */
472 int tr_filter_set_add(TR_FILTER_SET *set, TR_FILTER *new)
473 {
474   TR_FILTER_SET *tail=NULL;
475
476   if (set->this==NULL)
477     tail=set;
478   else {
479     tail=tr_filter_set_tail(set);
480     tail->next=tr_filter_set_new(set);
481     if (tail->next==NULL)
482       return 1;
483     tail=tail->next;
484   }
485   tail->this=new;
486   talloc_steal(tail, new);
487   return 0;
488 }
489
490 /**
491  * Find a filter of a given type in the filter set. If there are multiple, returns the first one.
492  *
493  * @param set Filter set to search
494  * @param type Type of filter to find
495  * @return Borrowed pointer to the filter, or null if no filter of that type is found
496  */
497 TR_FILTER *tr_filter_set_get(TR_FILTER_SET *set, TR_FILTER_TYPE type)
498 {
499   TR_FILTER_SET *cur=set;
500   while(cur!=NULL) {
501     if ((cur->this != NULL) && (cur->this->type == type))
502       return cur->this;
503     cur=cur->next;
504   }
505   return NULL;
506 }
507
508 TR_FILTER_TYPE filter_type[]={TR_FILTER_TYPE_TID_INBOUND,
509                               TR_FILTER_TYPE_TRP_INBOUND,
510                               TR_FILTER_TYPE_TRP_OUTBOUND};
511 const char *filter_label[]={"tid_inbound",
512                             "trp_inbound",
513                             "trp_outbound"};
514 size_t num_filter_types=sizeof(filter_type)/sizeof(filter_type[0]);
515
516 const char *tr_filter_type_to_string(TR_FILTER_TYPE ftype)
517 {
518   size_t ii=0;
519
520   for (ii=0; ii<num_filter_types; ii++) {
521     if (ftype==filter_type[ii])
522       return filter_label[ii];
523   }
524   return "unknown";
525 }
526
527 TR_FILTER_TYPE tr_filter_type_from_string(const char *s)
528 {
529   size_t ii=0;
530
531   for(ii=0; ii<num_filter_types; ii++) {
532     if (0==strcmp(s, filter_label[ii]))
533       return filter_type[ii];
534   }
535   return TR_FILTER_TYPE_UNKNOWN;
536 }
537