In-progress commit.
authorJennifer Richards <jennifer@painless-security.com>
Thu, 8 Jun 2017 23:01:54 +0000 (19:01 -0400)
committerJennifer Richards <jennifer@painless-security.com>
Thu, 8 Jun 2017 23:01:54 +0000 (19:01 -0400)
 * Allow array for filter spec match field
 * Validate filters and filter specs
 * Add filter to TRP_PEER
 * Add filter tests in filt_test.c
 * Add several test filter JSON files

Makefile.am
common/tests/filt_test.c
common/tests/test-filters/invalid-filt-repeated-key.json [new file with mode: 0644]
common/tests/test-filters/invalid-filt-unknown-field.json [new file with mode: 0644]
common/tests/test-filters/valid-filt.json [new file with mode: 0644]
common/tr_config.c
common/tr_filter.c
include/tr_filter.h
include/trp_ptable.h
trp/trp_ptable.c

index b8a5104..2254baf 100644 (file)
@@ -2,7 +2,7 @@ DISTCHECK_CONFIGURE_FLAGS = \
        --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
 bin_PROGRAMS= tr/trust_router tr/trpc tid/example/tidc tid/example/tids common/tests/tr_dh_test common/tests/mq_test \
               common/tests/thread_test trp/msgtst trp/test/rtbl_test trp/test/ptbl_test common/tests/cfg_test \
-              common/tests/commtest common/tests/name_test
+              common/tests/commtest common/tests/name_test common/tests/filt_test
 AM_CPPFLAGS=-I$(srcdir)/include $(GLIB_CFLAGS)
 AM_CFLAGS = -Wall -Werror=missing-prototypes -Werror -Wno-parentheses $(GLIB_CFLAGS)
 SUBDIRS = gsscon 
@@ -37,6 +37,7 @@ common/tr_mq.c
 
 check_PROGRAMS = common/t_constraint
 TESTS = common/t_constraint
+TEST_CFLAGS = -Wno-missing-prototypes
 
 lib_LTLIBRARIES = libtr_tid.la
 
@@ -153,7 +154,15 @@ common_tests_name_test_SOURCES = common/tests/name_test.c \
               $(trp_srcs)
 common_tests_name_test_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
 common_tests_name_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread
+common_tests_name_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
 
+common_tests_filt_test_SOURCES = common/tests/filt_test.c \
+              $(common_srcs) \
+              $(tid_srcs) \
+              $(trp_srcs)
+common_tests_filt_test_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
+common_tests_filt_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread
+common_tests_filt_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
 
 pkginclude_HEADERS = include/trust_router/tid.h include/trust_router/tr_name.h \
        include/tr_debug.h include/trust_router/trp.h \
index cffeebc..c7eb028 100644 (file)
 #include <stdlib.h>
 #include <assert.h>
 
+#include <trp_internal.h>
+#include <tid_internal.h>
 #include <tr_filter.h>
+#include <tr_config.h>
 
-int test_field_lookup(void)
+#define FILTER_PATH "./test-filters/"
+
+/**
+ * Load a JSON file containing filters and return the filters from the first rp_client.
+ *
+ * @param fname File to read
+ * @param filt_out Will point to the loaded filter on success
+ * @return Return value from tr_cfg_parse_one_config_file()
+ */
+int load_filter(const char *fname, TR_FILTER **filt_out)
 {
+  TR_CFG *cfg=tr_cfg_new(NULL);
+  TR_CFG_RC rc=TR_CFG_ERROR;
 
-  return 0;
+  assert(fname);
+  assert(filt_out);
+
+  rc=tr_cfg_parse_one_config_file(cfg, fname);
+  if (rc!=TR_CFG_SUCCESS)
+    goto cleanup;
+
+  /* Steal the filter from the first rp_client */
+  assert(cfg);
+  assert(cfg->rp_clients);
+  assert(cfg->rp_clients->filter);
+  *filt_out=cfg->rp_clients->filter;
+  cfg->rp_clients->filter=NULL; /* can't use the _set_filter() because that will free the filter */
+  talloc_steal(NULL, *filt_out);
+
+cleanup:
+  tr_cfg_free(cfg);
+  return rc;
 }
 
-int main(void)
+/**
+ * Test that filters load / fail to load as expected.
+ *
+ * @return 1 if all tests pass
+ */
+int test_load_filter(void)
+{
+  TR_FILTER *filt=NULL;
+
+  assert(TR_CFG_SUCCESS==load_filter(FILTER_PATH "valid-filt.json", &filt));
+  assert(TR_CFG_NOPARSE==load_filter(FILTER_PATH "invalid-filt-repeated-key.json", &filt));
+  assert(TR_CFG_ERROR==load_filter(FILTER_PATH "invalid-filt-unknown-field.json", &filt));
+  return 1;
+}
+
+int test_trp_inforec_filter(TRP_INFOREC_TYPE type)
 {
+  TRP_INFOREC *inforec=trp_inforec_new(NULL, type);
+  TR_FILTER *filt=tr_filter_new(NULL);
+
+  assert(inforec);
+  assert(filt);
 
-  printf("Success.\n");
+
+  return 1;
+}
+
+
+int test_trp_filter(void)
+{
+  assert(test_trp_inforec_filter(TRP_INFOREC_TYPE_ROUTE));
+  assert(test_trp_inforec_filter(TRP_INFOREC_TYPE_COMMUNITY));
+  return 1;
+}
+
+
+
+int main(void)
+{
+  assert(test_load_filter());
+  printf("Success\n");
   return 0;
 }
\ No newline at end of file
diff --git a/common/tests/test-filters/invalid-filt-repeated-key.json b/common/tests/test-filters/invalid-filt-repeated-key.json
new file mode 100644 (file)
index 0000000..5ff336f
--- /dev/null
@@ -0,0 +1,59 @@
+{
+  "local_organizations": [
+    { "organization_name": "invalid filter - repeated key",
+      "realms": [
+        { "realm": "realm",
+          "gss_names": ["gss"],
+          "filters": {
+            "tid_inbound": [
+              { "action": "accept",
+                "specs": [
+                  { "field": "rp_realm",
+                    "match": [
+                      "a.realm",
+                      "*.a.realm"
+                    ]
+                  }
+                ]
+              },
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["community"]}
+                ]
+              }
+            ],
+            "tid_inbound": [
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["realm"]}
+                ]
+              },
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["community"]}
+                ]
+              }
+            ],
+            "trp_outbound": [
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["realm"]}
+                ]
+              },
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["community"]}
+                ]
+              }
+            ]
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/common/tests/test-filters/invalid-filt-unknown-field.json b/common/tests/test-filters/invalid-filt-unknown-field.json
new file mode 100644 (file)
index 0000000..92c56dd
--- /dev/null
@@ -0,0 +1,59 @@
+{
+  "local_organizations": [
+    { "organization_name": "invalid filter - unknown field",
+      "realms": [
+        { "realm": "realm",
+          "gss_names": ["gss"],
+          "filters": {
+            "tid_inbound": [
+              { "action": "accept",
+                "specs": [
+                  { "field": "rp_realm",
+                    "match": [
+                      "a.realm",
+                      "*.a.realm"
+                    ]
+                  }
+                ]
+              },
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["community"]}
+                ]
+              }
+            ],
+            "trp_inbound": [
+              { "action": "accept",
+                "specs": [
+                  { "field": "this is not a real field",
+                    "match": ["realm"]}
+                ]
+              },
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["community"]}
+                ]
+              }
+            ],
+            "trp_outbound": [
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["realm"]}
+                ]
+              },
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["community"]}
+                ]
+              }
+            ]
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/common/tests/test-filters/valid-filt.json b/common/tests/test-filters/valid-filt.json
new file mode 100644 (file)
index 0000000..b4fdfb3
--- /dev/null
@@ -0,0 +1,53 @@
+{
+  "local_organizations": [
+    { "organization_name": "valid filter test",
+      "realms": [
+        { "realm": "realm",
+          "gss_names": ["gss"],
+          "filters": {
+            "tid_inbound": [
+              { "action": "accept",
+                "specs": [
+                  { "field": "rp_realm",
+                    "match": [
+                      "a.realm",
+                      "*.a.realm"
+                    ]
+                  }
+                ]
+              }
+            ],
+            "trp_inbound": [
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["realm"]}
+                ]
+              },
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["community"]}
+                ]
+              }
+            ],
+            "trp_outbound": [
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["realm"]}
+                ]
+              },
+              { "action": "accept",
+                "specs": [
+                  { "field": "info_type",
+                    "match": ["community"]}
+                ]
+              }
+            ]
+          }
+        }
+      ]
+    }
+  ]
+}
index ab3388e..917a465 100644 (file)
@@ -410,30 +410,62 @@ static TR_CONSTRAINT *tr_cfg_parse_one_constraint(TALLOC_CTX *mem_ctx, char *cty
   return cons;
 }
 
-static TR_FILTER *tr_cfg_parse_one_filter(TALLOC_CTX *mem_ctx, json_t *jfilt, TR_FILTER_TYPE ftype, TR_CFG_RC *rc)
+TR_FILTER_TYPE filter_type[]={TR_FILTER_TYPE_TID_INBOUND,
+                              TR_FILTER_TYPE_TRP_INBOUND,
+                              TR_FILTER_TYPE_TRP_OUTBOUND};
+const char *filter_label[]={"tid_inbound",
+                            "trp_inbound",
+                            "trp_outbound"};
+size_t num_filter_types=sizeof(filter_type)/sizeof(filter_type[0]);
+
+static const char *filter_type_to_string(TR_FILTER_TYPE ftype)
 {
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_FILTER *filt=NULL;
-  json_t *jfaction=NULL;
-  json_t *jfspecs=NULL;
-  json_t *jffield=NULL;
-  json_t *jfmatch=NULL;
-  json_t *jrc=NULL;
-  json_t *jdc=NULL;
-  TR_NAME *name=NULL;
-  int i=0, j=0;
+  size_t ii=0;
 
-  *rc=TR_CFG_ERROR;
+  for (ii=0; ii<num_filter_types; ii++) {
+    if (ftype==filter_type[ii])
+      return filter_label[ii];
+  }
+  return "unknown";
+}
+
+static TR_FILTER_TYPE filter_type_from_string(const char *s)
+{
+  size_t ii=0;
+
+  for(ii=0; ii<num_filter_types; ii++) {
+    if (0==strcmp(s, filter_label[ii]))
+      return filter_type[ii];
+  }
+  return TR_FILTER_TYPE_UNKNOWN;
+}
 
-  if ((jfilt==NULL) || (rc==NULL)) {
+static TR_FILTER *tr_cfg_parse_one_filter(TALLOC_CTX *mem_ctx, json_t *jfilt, TR_FILTER_TYPE ftype, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  TR_FILTER *filt = NULL;
+  json_t *jfaction = NULL;
+  json_t *jfspecs = NULL;
+  json_t *this_jfspec = NULL;
+  json_t *jfield = NULL;
+  json_t *jmatch = NULL;
+  json_t *jrc = NULL;
+  json_t *jdc = NULL;
+  json_t *this_jmatch = NULL;
+  TR_NAME *name = NULL;
+  size_t i = 0, j = 0, k = 0;
+
+  *rc = TR_CFG_ERROR;
+
+  if ((jfilt == NULL) || (rc == NULL)) {
     tr_err("tr_cfg_parse_one_filter: null argument");
-    *rc=TR_CFG_BAD_PARAMS;
+    *rc = TR_CFG_BAD_PARAMS;
     goto cleanup;
   }
-    
-  if (NULL==(filt=tr_filter_new(tmp_ctx))) {
+
+  if (NULL == (filt = tr_filter_new(tmp_ctx))) {
     tr_err("tr_cfg_parse_one_filter: Out of memory.");
-    *rc=TR_CFG_NOMEM;
+    *rc = TR_CFG_NOMEM;
     goto cleanup;
   }
   tr_filter_set_type(filt, ftype);
@@ -441,139 +473,169 @@ static TR_FILTER *tr_cfg_parse_one_filter(TALLOC_CTX *mem_ctx, json_t *jfilt, TR
   /* make sure we have space to represent the filter */
   if (json_array_size(jfilt) > TR_MAX_FILTER_LINES) {
     tr_err("tr_cfg_parse_one_filter: Filter has too many lines, maximum of %d.", TR_MAX_FILTER_LINES);
-    *rc=TR_CFG_NOPARSE;
+    *rc = TR_CFG_NOPARSE;
     goto cleanup;
   }
 
   /* For each entry in the filter... */
-  for (i=0; i < json_array_size(jfilt); i++) {
-    if ((NULL==(jfaction=json_object_get(json_array_get(jfilt, i), "action"))) ||
+  for (i = 0; i < json_array_size(jfilt); i++) {
+    if ((NULL == (jfaction = json_object_get(json_array_get(jfilt, i), "action"))) ||
         (!json_is_string(jfaction))) {
       tr_debug("tr_cfg_parse_one_filter: Error parsing filter action.");
-      *rc=TR_CFG_NOPARSE;
+      *rc = TR_CFG_NOPARSE;
       goto cleanup;
     }
-    if ((NULL==(jfspecs=json_object_get(json_array_get(jfilt, i), "specs"))) ||
+
+    if ((NULL == (jfspecs = json_object_get(json_array_get(jfilt, i), "specs"))) ||
         (!json_is_array(jfspecs)) ||
-        (0==json_array_size(jfspecs))) {
+        (0 == json_array_size(jfspecs))) {
       tr_debug("tr_cfg_parse_one_filter: Error parsing filter specs.");
-      *rc=TR_CFG_NOPARSE;
+      *rc = TR_CFG_NOPARSE;
       goto cleanup;
     }
-  
+
     if (TR_MAX_FILTER_SPECS < json_array_size(jfspecs)) {
       tr_debug("tr_cfg_parse_one_filter: Filter has too many specs, maximimum of %d.", TR_MAX_FILTER_SPECS);
-      *rc=TR_CFG_NOPARSE;
+      *rc = TR_CFG_NOPARSE;
       goto cleanup;
     }
 
-    if (NULL==(filt->lines[i]=tr_fline_new(filt))) {
-      tr_debug("tr_cfg_parse_one_filter: Out of memory allocating filter line %d.", i+1);
-      *rc=TR_CFG_NOMEM;
+    if (NULL == (filt->lines[i] = tr_fline_new(filt))) {
+      tr_debug("tr_cfg_parse_one_filter: Out of memory allocating filter line %d.", i + 1);
+      *rc = TR_CFG_NOMEM;
       goto cleanup;
     }
 
     if (!strcmp(json_string_value(jfaction), "accept")) {
-      filt->lines[i]->action=TR_FILTER_ACTION_ACCEPT;
-    }
-    else if (!strcmp(json_string_value(jfaction), "reject")) {
-      filt->lines[i]->action=TR_FILTER_ACTION_REJECT;
-    }
-    else {
-      tr_debug("tr_cfg_parse_one_filter: Error parsing filter action, unknown action' %s'.", json_string_value(jfaction));
-      *rc=TR_CFG_NOPARSE;
+      filt->lines[i]->action = TR_FILTER_ACTION_ACCEPT;
+    } else if (!strcmp(json_string_value(jfaction), "reject")) {
+      filt->lines[i]->action = TR_FILTER_ACTION_REJECT;
+    } else {
+      tr_debug("tr_cfg_parse_one_filter: Error parsing filter action, unknown action' %s'.",
+               json_string_value(jfaction));
+      *rc = TR_CFG_NOPARSE;
       goto cleanup;
     }
 
-    if (NULL!=(jrc=json_object_get(json_array_get(jfilt, i), "realm_constraints"))) {
+    if (NULL != (jrc = json_object_get(json_array_get(jfilt, i), "realm_constraints"))) {
       if (!json_is_array(jrc)) {
         tr_err("tr_cfg_parse_one_filter: cannot parse realm_constraints, not an array.");
-        *rc=TR_CFG_NOPARSE;
+        *rc = TR_CFG_NOPARSE;
         goto cleanup;
-      } else if (json_array_size(jrc)>TR_MAX_CONST_MATCHES) {
+      } else if (json_array_size(jrc) > TR_MAX_CONST_MATCHES) {
         tr_err("tr_cfg_parse_one_filter: realm_constraints has too many entries, maximum of %d.",
                TR_MAX_CONST_MATCHES);
-        *rc=TR_CFG_NOPARSE;
+        *rc = TR_CFG_NOPARSE;
         goto cleanup;
-      } else if (json_array_size(jrc)>0) {
+      } else if (json_array_size(jrc) > 0) {
         /* ok we actually have entries to process */
-        if (NULL==(filt->lines[i]->realm_cons=tr_cfg_parse_one_constraint(filt->lines[i], "realm", jrc, rc))) {
+        if (NULL == (filt->lines[i]->realm_cons = tr_cfg_parse_one_constraint(filt->lines[i], "realm", jrc, rc))) {
           tr_debug("tr_cfg_parse_one_filter: Error parsing realm constraint");
-          *rc=TR_CFG_NOPARSE;
+          *rc = TR_CFG_NOPARSE;
           goto cleanup;
         }
       }
     }
 
-    if (NULL!=(jdc=json_object_get(json_array_get(jfilt, i), "domain_constraints"))) {
+    if (NULL != (jdc = json_object_get(json_array_get(jfilt, i), "domain_constraints"))) {
       if (!json_is_array(jdc)) {
         tr_err("tr_cfg_parse_one_filter: cannot parse domain_constraints, not an array.");
-        *rc=TR_CFG_NOPARSE;
+        *rc = TR_CFG_NOPARSE;
         goto cleanup;
-      } else if (json_array_size(jdc)>TR_MAX_CONST_MATCHES) {
+      } else if (json_array_size(jdc) > TR_MAX_CONST_MATCHES) {
         tr_err("tr_cfg_parse_one_filter: domain_constraints has too many entries, maximum of %d.",
                TR_MAX_CONST_MATCHES);
-        *rc=TR_CFG_NOPARSE;
+        *rc = TR_CFG_NOPARSE;
         goto cleanup;
-      } else if (json_array_size(jdc)>0) {
-        if (NULL==(filt->lines[i]->domain_cons=tr_cfg_parse_one_constraint(filt->lines[i], "domain", jdc, rc))) {
+      } else if (json_array_size(jdc) > 0) {
+        if (NULL == (filt->lines[i]->domain_cons = tr_cfg_parse_one_constraint(filt->lines[i], "domain", jdc, rc))) {
           tr_debug("tr_cfg_parse_one_filter: Error parsing domain constraint");
-          *rc=TR_CFG_NOPARSE;
+          *rc = TR_CFG_NOPARSE;
           goto cleanup;
         }
       }
     }
 
     /*For each filter spec within the filter line... */
-    for (j=0; j <json_array_size(jfspecs); j++) {
-      if ((NULL==(jffield=json_object_get(json_array_get(jfspecs, j), "field"))) ||
-          (!json_is_string(jffield))) {
-        tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing field for filer spec %d, filter line %d.", i, j);
-        *rc=TR_CFG_NOPARSE;
+    json_array_foreach(jfspecs, j, this_jfspec) {
+      if ((NULL == (jfield = json_object_get(this_jfspec, "field"))) ||
+          (!json_is_string(jfield))) {
+        tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing field for filer spec %d, filter line %d.", i,
+                 j);
+        *rc = TR_CFG_NOPARSE;
         goto cleanup;
       }
 
       /* check that we have a match attribute */
-      if (NULL==(jfmatch=json_object_get(json_array_get(jfspecs, j), "match"))) {
-        tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing match for filer spec %d, filter line %d.", i, j);
-        *rc=TR_CFG_NOPARSE;
+      if (NULL == (jmatch = json_object_get(this_jfspec, "match"))) {
+        tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing match for filer spec %d, filter line %d.", i,
+                 j);
+        *rc = TR_CFG_NOPARSE;
         goto cleanup;
       }
 
-      /* check that match is a string */
-      if (!json_is_string(jfmatch)) {
-        tr_debug("tr_cfg_parse_one_filter: Error parsing filter: match not a string for filter spec %d, filter line %d.", i, j);
-        *rc=TR_CFG_NOPARSE;
+      /* check that match is a string or an array */
+      if ((!json_is_string(jmatch)) && (!json_is_array(jmatch))) {
+        tr_debug(
+            "tr_cfg_parse_one_filter: Error parsing filter: match not a string or array for filter spec %d, filter line %d.",
+            i, j);
+        *rc = TR_CFG_NOPARSE;
         goto cleanup;
       }
 
       /* allocate the filter spec */
-      if (NULL==(filt->lines[i]->specs[j]=tr_fspec_new(filt->lines[i]))) {
+      if (NULL == (filt->lines[i]->specs[j] = tr_fspec_new(filt->lines[i]))) {
         tr_debug("tr_cfg_parse_one_filter: Out of memory.");
-        *rc=TR_CFG_NOMEM;
+        *rc = TR_CFG_NOMEM;
         goto cleanup;
       }
 
       /* fill in the field */
-      if (NULL==(filt->lines[i]->specs[j]->field=tr_new_name(json_string_value(jffield)))) {
+      if (NULL == (filt->lines[i]->specs[j]->field = tr_new_name(json_string_value(jfield)))) {
         tr_debug("tr_cfg_parse_one_filter: Out of memory.");
-        *rc=TR_CFG_NOMEM;
+        *rc = TR_CFG_NOMEM;
         goto cleanup;
       }
 
       /* fill in the matches */
-      if (NULL==(name=tr_new_name(json_string_value(jfmatch)))) {
-        tr_debug("tr_cfg_parse_one_filter: Out of memory.");
-        *rc=TR_CFG_NOMEM;
+      if (json_is_string(jmatch)) {
+        if (NULL == (name = tr_new_name(json_string_value(jmatch)))) {
+          tr_debug("tr_cfg_parse_one_filter: Out of memory.");
+          *rc = TR_CFG_NOMEM;
+          goto cleanup;
+        }
+        tr_fspec_add_match(filt->lines[i]->specs[j], name);
+      } else {
+        /* jmatch is an array (we checked earlier) */
+        json_array_foreach(jmatch, k, this_jmatch) {
+          if (NULL == (name = tr_new_name(json_string_value(this_jmatch)))) {
+            tr_debug("tr_cfg_parse_one_filter: Out of memory.");
+            *rc = TR_CFG_NOMEM;
+            goto cleanup;
+          }
+          tr_fspec_add_match(filt->lines[i]->specs[j], name);
+        }
+      }
+      if (!tr_filter_validate_spec_field(ftype, filt->lines[i]->specs[j])){
+        tr_debug("tr_cfg_parse_one_filter: Invalid filter field \"%.*s\" for %s filter, spec %d, filter %d.",
+                 filt->lines[i]->specs[j]->field->len,
+                 filt->lines[i]->specs[j]->field->buf,
+                 filter_type_to_string(filt->type),
+                 i, j);
+        *rc = TR_CFG_ERROR;
         goto cleanup;
       }
-      tr_fspec_set_match(filt->lines[i]->specs[j], name);
     }
   }
-  *rc=TR_CFG_SUCCESS;
-  talloc_steal(mem_ctx, filt);
-  
+
+  /* check that the filter is valid */
+  if (!tr_filter_validate(filt)) {
+    *rc = TR_CFG_ERROR;
+  } else {
+    *rc = TR_CFG_SUCCESS;
+    talloc_steal(mem_ctx, filt);
+  }
+
  cleanup:
   talloc_free(tmp_ctx);
   if (*rc!=TR_CFG_SUCCESS)
@@ -585,7 +647,9 @@ static TR_FILTER *tr_cfg_parse_filters(TALLOC_CTX *mem_ctx, json_t *jfilts, TR_C
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   json_t *jfilt;
+  const char *filt_label=NULL;
   TR_FILTER *filt=NULL;
+  TR_FILTER_TYPE filt_type=TR_FILTER_TYPE_UNKNOWN;
 
   *rc=TR_CFG_ERROR;
 
@@ -595,20 +659,32 @@ static TR_FILTER *tr_cfg_parse_filters(TALLOC_CTX *mem_ctx, json_t *jfilts, TR_C
     goto cleanup;
   }
 
-  jfilt=json_object_get(jfilts, "tid_inbound");
-  if (jfilt!=NULL) {
-    filt=tr_cfg_parse_one_filter(tmp_ctx, jfilt, TR_FILTER_TYPE_TID_INBOUND, rc);
-    if (*rc!=TR_CFG_SUCCESS) {
-      tr_debug("tr_cfg_parse_filters: Error parsing tid_inbound filter.");
-      *rc=TR_CFG_NOPARSE;
+  json_object_foreach(jfilts, filt_label, jfilt) {
+    /* check that we got a filter */
+    if (jfilt == NULL) {
+      tr_debug("tr_cfg_parse_filters: Definition for %s filter is missing.", filt_label);
+      *rc = TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+
+    /* check that we recognize the filter type */
+    filt_type=filter_type_from_string(filt_label);
+    if (filt_type==TR_FILTER_TYPE_UNKNOWN) {
+      tr_debug("tr_cfg_parse_filters: Unrecognized filter (%s) defined.", filt_label);
+      *rc = TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+
+    /* finally, parse the filter */
+    tr_debug("tr_cfg_parse_filters: Found %s filter.", filt_label);
+    filt = tr_cfg_parse_one_filter(tmp_ctx, jfilt, filt_type, rc);
+    if (*rc != TR_CFG_SUCCESS) {
+      tr_debug("tr_cfg_parse_filters: Error parsing %s filter.", filt_label);
+      *rc = TR_CFG_NOPARSE;
       goto cleanup;
     }
-  } else {
-    tr_debug("tr_cfg_parse_filters: Unknown filter types in filter block.");
-    *rc=TR_CFG_NOPARSE;
-    goto cleanup;
   }
-  
+
   *rc=TR_CFG_SUCCESS;
 
  cleanup:
@@ -1146,7 +1222,7 @@ static TR_FILTER *tr_cfg_default_filter(TALLOC_CTX *mem_ctx, TR_NAME *realm, TR_
     *rc=TR_CFG_NOMEM;
     goto cleanup;
   }
-  tr_fspec_set_match(filt->lines[0]->specs[0], name);
+  tr_fspec_add_match(filt->lines[0]->specs[0], name);
   name=NULL; /* we no longer own the name */
 
   /* now do the wildcard name */
@@ -1160,7 +1236,7 @@ static TR_FILTER *tr_cfg_default_filter(TALLOC_CTX *mem_ctx, TR_NAME *realm, TR_
     goto cleanup;
   }
 
-  tr_fspec_set_match(filt->lines[0]->specs[1], name);
+  tr_fspec_add_match(filt->lines[0]->specs[1], name);
   name=NULL; /* we no longer own the name */
 
   /* domain constraint */
@@ -1511,7 +1587,7 @@ cleanup:
 static TR_CFG_RC tr_cfg_parse_local_orgs(TR_CFG *trc, json_t *jcfg)
 {
   json_t *jlocorgs=NULL;
-  int ii=0;
+  size_t ii=0;
 
   jlocorgs=json_object_get(jcfg, "local_organizations");
   if (jlocorgs==NULL)
@@ -1538,13 +1614,16 @@ static TR_CFG_RC tr_cfg_parse_one_peer_org(TR_CFG *trc, json_t *jporg)
   json_t *jhost=NULL;
   json_t *jport=NULL;
   json_t *jgss=NULL;
+  json_t *jfilt=NULL;
   TRP_PEER *new_peer=NULL;
   TR_GSS_NAMES *names=NULL;
+  TR_FILTER *filt=NULL;
   TR_CFG_RC rc=TR_CFG_ERROR;
 
   jhost=json_object_get(jporg, "hostname");
   jport=json_object_get(jporg, "port");
   jgss=json_object_get(jporg, "gss_names");
+  jfilt=json_object_get(jporg, "filters");
 
   if ((jhost==NULL) || (!json_is_string(jhost))) {
     tr_err("tr_cfg_parse_one_peer_org: hostname not specified or not a string.");
@@ -1558,13 +1637,19 @@ static TR_CFG_RC tr_cfg_parse_one_peer_org(TR_CFG *trc, json_t *jporg)
     rc=TR_CFG_NOPARSE;
     goto cleanup;
   }
-  
+
   if ((jgss==NULL) || (!json_is_array(jgss))) {
     tr_err("tr_cfg_parse_one_peer_org: gss_names not specified or not an array.");
     rc=TR_CFG_NOPARSE;
     goto cleanup;
   }
 
+  if ((jfilt!=NULL) && (!json_is_array(jfilt))) {
+    tr_err("tr_cfg_parse_one_peer_org: filters is not an array.");
+    rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
   new_peer=trp_peer_new(tmp_ctx);
   if (new_peer==NULL) {
     tr_err("tr_cfg_parse_one_peer_org: could not allocate new peer.");
@@ -1586,6 +1671,16 @@ static TR_CFG_RC tr_cfg_parse_one_peer_org(TR_CFG *trc, json_t *jporg)
   }
   trp_peer_set_gss_names(new_peer, names);
 
+  if (jfilt) {
+    filt=tr_cfg_parse_filters(tmp_ctx, jfilt, &rc);
+    if (rc!=TR_CFG_SUCCESS) {
+      tr_err("tr_cfg_parse_one_peer_org: unable to parse filters.");
+      rc=TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+    trp_peer_set_filter(new_peer, filt);
+  }
+
   /* success! */
   trp_ptable_add(trc->peers, new_peer);
   rc=TR_CFG_SUCCESS;
@@ -1937,7 +2032,7 @@ TR_CFG_RC tr_cfg_parse_one_config_file(TR_CFG *cfg, const char *file_with_path)
   json_error_t rc;
 
   if (NULL==(jcfg=json_load_file(file_with_path, 
-                                 JSON_DISABLE_EOF_CHECK, &rc))) {
+                                 JSON_DISABLE_EOF_CHECK|JSON_REJECT_DUPLICATES, &rc))) {
     tr_debug("tr_cfg_parse_one_config_file: Error parsing config file %s.", 
              file_with_path);
     tr_cfg_log_json_error("tr_cfg_parse_one_config_file", &rc);
index 701ec85..c13be74 100644 (file)
 #include <trp_internal.h>
 #include <tid_internal.h>
 
-const TR_FILTER_TYPE tr_filter_types[] = {
-    TR_FILTER_TYPE_TID_INBOUND,
-    TR_FILTER_TYPE_TRP_INBOUND,
-    TR_FILTER_TYPE_TRP_OUTBOUND
-};
-const size_t tr_num_filter_types=sizeof(tr_filter_types)/sizeof(tr_filter_types[0]);
-static const char *tr_filter_type_strings[] = {
-    "tid_inbound",
-    "trp_inbound",
-    "trp_outbound"
-};
-
-const char *tr_filter_type_to_string(TR_FILTER_TYPE t) {
-  int ii;
-  for (ii=0; ii<tr_num_filter_types;ii++) {
-    if (t==tr_filter_types[ii])
-      return tr_filter_type_strings[ii];
-  }
-  return NULL;
-}
-
-TR_FILTER_TYPE tr_filter_type_from_string(const char *s)
-{
-  int ii;
-  for (ii=0; ii<tr_num_filter_types; ii++) {
-    if (strcasecmp(s,  tr_filter_type_strings[ii])==0)
-      return tr_filter_types[ii];
-  }
-  return TR_FILTER_TYPE_UNKNOWN;
-}
-
 /* Function types for handling filter fields generally. All target values
  * are represented as strings in a TR_NAME.
  */
@@ -273,31 +242,42 @@ void tr_fspec_free(TR_FSPEC *fspec)
 static int tr_fspec_destructor(void *obj)
 {
   TR_FSPEC *fspec = talloc_get_type_abort(obj, TR_FSPEC);
+  size_t ii;
 
   if (fspec->field != NULL)
     tr_free_name(fspec->field);
-  if (fspec->match != NULL)
-    tr_free_name(fspec->match);
+  for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++) {
+    if (fspec->match[ii] != NULL)
+      tr_free_name(fspec->match[ii]);
+  }
   return 0;
 }
 
 TR_FSPEC *tr_fspec_new(TALLOC_CTX *mem_ctx)
 {
   TR_FSPEC *fspec = talloc(mem_ctx, TR_FSPEC);
+  size_t ii=0;
 
   if (fspec != NULL) {
     fspec->field = NULL;
-    fspec->match = NULL;
-    talloc_set_destructor((void *) fspec, tr_fspec_destructor);
+    for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++)
+      fspec->match[ii] = NULL;
+
+    talloc_set_destructor((void *)fspec, tr_fspec_destructor);
   }
   return fspec;
 }
 
-void tr_fspec_set_match(TR_FSPEC *fspec, TR_NAME *match)
+void tr_fspec_add_match(TR_FSPEC *fspec, TR_NAME *match)
 {
-  if (fspec->match != NULL)
-    tr_free_name(fspec->match);
-  fspec->match = match;
+  size_t ii;
+  for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++) {
+    if (fspec->match[ii]==NULL) {
+      fspec->match[ii]=match;
+      break;
+    }
+  }
+  /* TODO: handle case that adding the match failed */
 }
 
 /* returns 1 if the spec matches */
@@ -305,8 +285,9 @@ int tr_fspec_matches(TR_FSPEC *fspec, TR_FILTER_TYPE ftype, void *target)
 {
   struct tr_filter_field_entry *field=NULL;
   TR_NAME *name=NULL;
+  size_t ii=0;
 
-  if ((fspec==NULL) || (fspec->match==NULL))
+  if (fspec==NULL)
     return 0;
 
   /* Look up how to handle the requested field */
@@ -315,8 +296,13 @@ int tr_fspec_matches(TR_FSPEC *fspec, TR_FILTER_TYPE ftype, void *target)
     return 0;
 
   name=field->get(target);
-  return ((fspec->match != NULL) &&
-          (0 != tr_name_prefix_wildcard_match(name, fspec->match)));
+  for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++) {
+    if (fspec->match[ii]!=NULL) {
+      if (tr_name_prefix_wildcard_match(name, fspec->match[ii]))
+        return 1;
+    }
+  }
+  return 0;
 }
 
 void tr_fline_free(TR_FLINE *fline)
@@ -366,3 +352,70 @@ TR_FILTER_TYPE tr_filter_get_type(TR_FILTER *filt)
 {
   return filt->type;
 }
+
+/**
+ * Check that a filter is valid, i.e., can be processed.
+ *
+ * @param filt Filter to verify
+ * @return 1 if the filter is valid, 0 otherwise
+ */
+int tr_filter_validate(TR_FILTER *filt)
+{
+  size_t ii=0, jj=0, kk=0;
+
+  if (!filt)
+    return 0;
+
+  /* check that we recognize the type */
+  switch(filt->type) {
+    case TR_FILTER_TYPE_TID_INBOUND:
+    case TR_FILTER_TYPE_TRP_INBOUND:
+    case TR_FILTER_TYPE_TRP_OUTBOUND:
+      break;
+
+    default:
+      return 0; /* if we get here, either TR_FILTER_TYPE_UNKNOWN or an invalid value was found */
+  }
+  for (ii=0; ii<TR_MAX_FILTER_LINES; ii++) {
+    if (filt->lines[ii]==NULL)
+      continue; /* an empty filter line is valid */
+
+    /* check that we recognize the action */
+    switch(filt->lines[ii]->action) {
+      case TR_FILTER_ACTION_ACCEPT:
+      case TR_FILTER_ACTION_REJECT:
+        break;
+
+      default:
+        /* if we get here, either TR_FILTER_ACTION_UNKNOWN or an invalid value was found */
+        return 0;
+    }
+
+    for (jj=0; jj<TR_MAX_FILTER_SPECS; jj++) {
+      if (filt->lines[ii]->specs[jj]==NULL)
+        continue; /* an empty filter spec is valid */
+
+      if (!tr_filter_validate_spec_field(filt->type, filt->lines[ii]->specs[jj]))
+        return 0;
+
+      /* check that at least one match is non-null */
+      for (kk=0; kk<TR_MAX_FILTER_SPEC_MATCHES; kk++) {
+        if (filt->lines[ii]->specs[jj]->match[kk]!=NULL)
+          break;
+      }
+      if (kk==TR_MAX_FILTER_SPEC_MATCHES)
+        return 0;
+    }
+  }
+
+  /* We ran the gauntlet. Success! */
+  return 1;
+}
+
+int tr_filter_validate_spec_field(TR_FILTER_TYPE ftype, TR_FSPEC *fspec)
+{
+  if ((fspec==NULL) || (tr_filter_field_entry(ftype, fspec->field)==NULL))
+    return 0; /* unknown field */
+
+  return 1;
+}
\ No newline at end of file
index c3d4ed1..38082de 100644 (file)
@@ -46,6 +46,7 @@
 #define TR_MAX_FILTERS  5
 #define TR_MAX_FILTER_LINES 8
 #define TR_MAX_FILTER_SPECS 8
+#define TR_MAX_FILTER_SPEC_MATCHES 8
 
 /* Filter actions */
 typedef enum {
@@ -66,13 +67,9 @@ typedef enum {
     TR_FILTER_TYPE_UNKNOWN
 } TR_FILTER_TYPE;
 
-extern const size_t tr_num_filter_types;
-const char *tr_filter_type_to_string(TR_FILTER_TYPE t);
-TR_FILTER_TYPE tr_filter_type_from_string(const char *s);
-
 typedef struct tr_fspec {
     TR_NAME *field;
-    TR_NAME *match;
+    TR_NAME *match[TR_MAX_FILTER_SPEC_MATCHES];
 } TR_FSPEC;
 
 typedef struct tr_fline {
@@ -103,7 +100,7 @@ TR_FSPEC *tr_fspec_new(TALLOC_CTX *mem_ctx);
 
 void tr_fspec_free(TR_FSPEC *fspec);
 
-void tr_fspec_set_match(TR_FSPEC *fspec, TR_NAME *match);
+void tr_fspec_add_match(TR_FSPEC *fspec, TR_NAME *match);
 
 int tr_fspec_matches(TR_FSPEC *fspec, TR_FILTER_TYPE ftype, void *target);
 
@@ -117,4 +114,7 @@ int tr_filter_process_rp_permitted(TR_NAME *rp_realm, TR_FILTER *rpp_filter, TR_
 
 TR_CONSTRAINT_SET *tr_constraint_set_from_fline(TR_FLINE *fline);
 
+int tr_filter_validate(TR_FILTER *filt);
+int tr_filter_validate_spec_field(TR_FILTER_TYPE ftype, TR_FSPEC *fspec);
+
 #endif
index a0d17b3..303bbc3 100644 (file)
@@ -41,6 +41,7 @@
 #include <trust_router/tr_name.h>
 #include <tr_gss.h>
 #include <trust_router/trp.h>
+#include <tr_filter.h>
 
 typedef enum trp_peer_conn_status {
   PEER_DISCONNECTED=0,
@@ -61,6 +62,7 @@ struct trp_peer {
   TRP_PEER_CONN_STATUS incoming_status;
   void (*conn_status_cb)(TRP_PEER *, void *); /* callback for connected status change */
   void *conn_status_cookie;
+  TR_FILTER *filter;
 };
 
 typedef struct trp_ptable {
@@ -106,6 +108,8 @@ void trp_peer_set_incoming_status(TRP_PEER *peer, TRP_PEER_CONN_STATUS status);
 int trp_peer_is_connected(TRP_PEER *peer);
 void trp_peer_set_linkcost(TRP_PEER *peer, unsigned int linkcost);
 void trp_peer_set_conn_status_cb(TRP_PEER *peer, void (*cb)(TRP_PEER *, void *), void *cookie);
+void trp_peer_set_filter(TRP_PEER *peer, TR_FILTER *filt);
+TR_FILTER *trp_peer_get_filter(TRP_PEER *peer);
 char *trp_peer_to_str(TALLOC_CTX *memctx, TRP_PEER *peer, const char *sep);
 
 #endif /* _TRP_PTABLE_H_ */
index f5ea1fb..4cb0705 100644 (file)
@@ -66,6 +66,7 @@ TRP_PEER *trp_peer_new(TALLOC_CTX *memctx)
     peer->incoming_status=PEER_DISCONNECTED;
     peer->conn_status_cb=NULL;
     peer->conn_status_cookie=NULL;
+    peer->filter=NULL;
     talloc_set_destructor((void *)peer, trp_peer_destructor);
   }
   return peer;
@@ -147,7 +148,7 @@ void trp_peer_add_gss_name(TRP_PEER *peer, TR_NAME *gss_name)
 void trp_peer_set_gss_names(TRP_PEER *peer, TR_GSS_NAMES *gss_names)
 {
   if (peer->gss_names!=NULL)
-    talloc_free(peer->gss_names);
+    tr_gss_names_free(peer->gss_names);
 
   peer->gss_names=gss_names;
   talloc_steal(peer, gss_names);
@@ -206,6 +207,27 @@ void trp_peer_set_conn_status_cb(TRP_PEER *peer, void (*cb)(TRP_PEER *, void *),
   peer->conn_status_cookie=cookie;
 }
 
+/**
+ * Set the filter associated with this peer. Any existing filter will be freed. Takes responsibility for
+ * freeing the new filter.
+ *
+ * @param peer Peer to modify
+ * @param filt New filter to attach to the peer
+ */
+void trp_peer_set_filter(TRP_PEER *peer, TR_FILTER *filt)
+{
+  if (peer->filter!=NULL)
+    tr_filter_free(peer->filter);
+
+  peer->filter=filt;
+  talloc_steal(peer, filt);
+}
+
+TR_FILTER *trp_peer_get_filter(TRP_PEER *peer)
+{
+  return peer->filter;
+}
+
 struct timespec *trp_peer_get_last_conn_attempt(TRP_PEER *peer)
 {
   return &(peer->last_conn_attempt);