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