Merge pull request #62 from painless-security/jennifer/report_incoming_ipaddr
[trust_router.git] / common / tr_constraint.c
index 44a60ba..f9fcf61 100644 (file)
  *
  */
 #include <jansson.h>
+#if JANSSON_VERSION_HEX < 0x020500
 #include "jansson_iterators.h"
+#endif
 #include <assert.h>
 #include <talloc.h>
 
 #include <tr_filter.h>
-#include <tr_debug.h>
-
-#include <trust_router/tr_constraint.h>
 #include <tid_internal.h>
+#include <tr_debug.h>
+#include <tr_constraint_internal.h>
 
-
+/**
+ * Helper for tr_constraint_destructor - calls tr_free_name on its first argument
+ *
+ * @param item void pointer to a TR_NAME
+ * @param cookie ignored
+ */
+static void constraint_destruct_helper(void *item, void *cookie)
+{
+  TR_NAME *name = (TR_NAME *) item;
+  tr_free_name(name);
+}
 static int tr_constraint_destructor(void *obj)
 {
-  TR_CONSTRAINT *cons=talloc_get_type_abort(obj, TR_CONSTRAINT);
-  int ii=0;
+  TR_CONSTRAINT *cons = talloc_get_type_abort(obj, TR_CONSTRAINT);
 
-  if (cons->type!=NULL)
+  if (cons->type)
     tr_free_name(cons->type);
-  for (ii=0; ii<TR_MAX_CONST_MATCHES; ii++) {
-    if (cons->matches[ii]!=NULL)
-      tr_free_name(cons->matches[ii]);
-  }
+
+  if (cons->matches)
+    tr_list_foreach(cons->matches, constraint_destruct_helper, NULL);
+
   return 0;
 }
 
 TR_CONSTRAINT *tr_constraint_new(TALLOC_CTX *mem_ctx)
 {
-  TR_CONSTRAINT *cons=talloc(mem_ctx, TR_CONSTRAINT);
-  int ii=0;
-
-  if (cons!=NULL) {
-    cons->type=NULL;
-    for (ii=0; ii<TR_MAX_CONST_MATCHES; ii++)
-      cons->matches[ii]=NULL;
-    talloc_set_destructor((void *)cons, tr_constraint_destructor);
+  TR_CONSTRAINT *cons = talloc(mem_ctx, TR_CONSTRAINT);
+
+  if (cons != NULL) {
+    cons->type = NULL;
+    cons->matches = tr_list_new(cons);
+    if (cons->matches == NULL) {
+      talloc_free(cons);
+      return NULL;
+    }
+    talloc_set_destructor((void *) cons, tr_constraint_destructor);
   }
   return cons;
 }
@@ -76,9 +88,73 @@ void tr_constraint_free(TR_CONSTRAINT *cons)
   talloc_free(cons);
 }
 
-/* Returns TRUE (1) if the the string (str) matchs the wildcard string (wc_str), FALSE (0) if not.
+/**
+ * Helper for tr_constraint_dup - duplicates a TR_NAME and adds it as a TR_CONSTRAINT match
+ *
+ * No return value. If this succeeds, it will have added a new entry to the TR_CONSTRAINT
+ * match list. Check the length of that - you won't be able to tell whether the allocation
+ * of the duplicate TR_NAME or the addition to the list failed, but either of those is probably
+ * due to a memory allocation failure, in which case the system is probably crashing anyway.
+ *
+ * @param item void pointer to a TR_NAME to add as a match
+ * @param cookie void pointer to a TR_CONSTRAINT to add the match to
+ */
+static void cons_dup_helper(void *item, void *cookie)
+{
+  TR_CONSTRAINT *new_cons = talloc_get_type_abort(cookie, TR_CONSTRAINT);
+  TR_NAME *new_name = tr_dup_name((TR_NAME *) item);
+  if (new_name) {
+    /* check that new_name is added, free if it fails */
+    if (tr_constraint_add_match(new_cons, new_name) == NULL)
+      tr_free_name(new_name);
+  }
+}
+/**
+ * Duplicate a TR_CONSTRAINT
+ *
+ * @param mem_ctx talloc context for the result
+ * @param cons TR_CONSTRAINT to duplicate
+ * @return pointer to the new TR_CONSTRAINT, or NULL on error
  */
-int tr_prefix_wildcard_match (const char *str, const char *wc_str) {
+TR_CONSTRAINT *tr_constraint_dup(TALLOC_CTX *mem_ctx, TR_CONSTRAINT *cons)
+{
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  TR_CONSTRAINT *new = NULL;
+
+  if (cons == NULL)
+    goto cleanup;
+
+  new = tr_constraint_new(tmp_ctx);
+  if (new == NULL)
+    goto cleanup;
+
+  new->type = tr_dup_name(cons->type);
+  if (new->type == NULL) {
+    new = NULL;
+    goto cleanup;
+  }
+
+  tr_list_foreach(cons->matches, cons_dup_helper, new); /* copies matches to new->matches */
+  /* check that we were successful - if we were, then the lists will be the same length */
+  if (tr_list_length(new->matches) != tr_list_length(cons->matches)) {
+    new = NULL;
+    goto cleanup; /* at least one dup or add failed */
+  }
+
+  /* success */
+  talloc_steal(mem_ctx, new);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return new;
+}
+
+/* Returns TRUE (1) if the the string (str) matches the wildcard string (wc_str), FALSE (0) if not.
+ * Allows for a single '*' as the wildcard character if it is the first character. Leading white
+ * space is significant.
+ */
+int tr_prefix_wildcard_match(const char *str, const char *wc_str)
+{
   const char *wc_post = wc_str;
   size_t len = 0;
   size_t wc_len = 0;
@@ -94,21 +170,22 @@ int tr_prefix_wildcard_match (const char *str, const char *wc_str) {
   if ('*' == wc_str[0]) {
     wc_post = &(wc_str[1]);
     wc_len--;
-  }else if (len != wc_len)
+  } else if (len != wc_len)
     return 0;
 
 
   if (wc_len > len)
     return 0;
-  
-  if (0 == strcmp(&(str[len-wc_len]), wc_post)) {
+
+  if (0 == strcmp(&(str[len - wc_len]), wc_post)) {
     return 1;
-  }
-  else
+  } else
     return 0;
-  }
+}
 
-TR_CONSTRAINT_SET *tr_constraint_set_from_fline (TR_FLINE *fline)
+/* This combines the two constraints in a filter line (TR_FLINE) into a single
+ * set with two constraints. */
+TR_CONSTRAINT_SET *tr_constraint_set_from_fline(TR_FLINE *fline)
 {
   json_t *cset = NULL;
 
@@ -116,10 +193,10 @@ TR_CONSTRAINT_SET *tr_constraint_set_from_fline (TR_FLINE *fline)
     return NULL;
 
   if (fline->realm_cons)
-    tr_constraint_add_to_set((TR_CONSTRAINT_SET **)&cset, fline->realm_cons);
+    tr_constraint_add_to_set((TR_CONSTRAINT_SET **) &cset, fline->realm_cons);
   if (fline->domain_cons)
-    tr_constraint_add_to_set((TR_CONSTRAINT_SET **)&cset, fline->domain_cons);
-  
+    tr_constraint_add_to_set((TR_CONSTRAINT_SET **) &cset, fline->domain_cons);
+
   return (TR_CONSTRAINT_SET *) cset;
 }
 
@@ -129,13 +206,16 @@ TR_CONSTRAINT_SET *tr_constraint_set_from_fline (TR_FLINE *fline)
  *
  *     {cset: [{domain: [a.com, b.co.uk]},
  *             {realm: [c.net, d.org]}]}
+ *
+ * This routine takes a TR_CONSTRAINT, converts it to its JSON representation,
+ * and adds that to the TR_CONSTRAINT_SET.
  */
-
-void tr_constraint_add_to_set (TR_CONSTRAINT_SET **cset, TR_CONSTRAINT *cons)
+void tr_constraint_add_to_set(TR_CONSTRAINT_SET **cset, TR_CONSTRAINT *cons)
 {
   json_t *jcons = NULL;
   json_t *jmatches = NULL;
-  int i = 0;
+  TR_NAME *this_match = NULL;
+  TR_CONSTRAINT_ITER iter = {0};
 
   if ((!cset) || (!cons))
     return;
@@ -148,22 +228,27 @@ void tr_constraint_add_to_set (TR_CONSTRAINT_SET **cset, TR_CONSTRAINT *cons)
   jmatches = json_array();
   jcons = json_object();
 
-  for (i = 0; ((i < TR_MAX_CONST_MATCHES) && (NULL != cons->matches[i])); i++) {
-    json_array_append_new(jmatches, json_string(cons->matches[i]->buf));
+  for (this_match = tr_constraint_iter_first(&iter, cons);
+       this_match != NULL;
+       this_match = tr_constraint_iter_next(&iter))
+  {
+    json_array_append_new(jmatches, tr_name_to_json_string(this_match));
   }
 
   json_object_set_new(jcons, cons->type->buf, jmatches);
-  
+
   /* Add the created object to the cset object */
   json_array_append_new((json_t *) *cset, jcons);
-} 
+}
 
- int tr_constraint_set_validate(TR_CONSTRAINT_SET *cset)
-{
+/* Test whether a JSON object has a valid structure
+ * to represent a constraint set.
+ */
+int tr_constraint_set_validate(TR_CONSTRAINT_SET *cset) {
   json_t *json = (json_t *) cset;
   size_t i;
   json_t *set_member;
-  if (!json_is_array(json)){
+  if (!json_is_array(json)) {
     tr_debug("Constraint_set is not an array");
     return 0;
   }
@@ -174,44 +259,50 @@ void tr_constraint_add_to_set (TR_CONSTRAINT_SET **cset, TR_CONSTRAINT *cons)
       tr_debug("Constraint member at %zu is not an object\n", i);
       return 0;
     }
-    json_object_foreach( set_member, key, value) {
+    json_object_foreach(set_member, key, value) {
       size_t inner_index;
       json_t *inner_value;
       if (!json_is_array(value)) {
-       tr_debug("Constraint type %s at index %zu in constraint set is not an array\n", key,
-                i);
-       return 0;
+        tr_debug("Constraint type %s at index %zu in constraint set is not an array\n", key,
+                 i);
+        return 0;
       }
       json_array_foreach(value, inner_index, inner_value) {
-       if (!json_is_string(inner_value)) {
-         tr_debug("Constraint type %s at index %zu in constraint set has non-string element %zu\n",
-                  key, i, inner_index);
-         return 0;
-       }
+        if (!json_is_string(inner_value)) {
+          tr_debug("Constraint type %s at index %zu in constraint set has non-string element %zu\n",
+                   key, i, inner_index);
+          return 0;
+        }
       }
-       }
+    }
   }
   return 1;
 }
 
-
-TR_CONSTRAINT_SET *tr_constraint_set_filter( TID_REQ *request,
-                                            TR_CONSTRAINT_SET *orig,
-                                            const char *constraint_type)
+/**
+ * Create a new constraint set containing all constraints from #orig
+ * with constraint_type #constraint_type and no others.  This constraint set is
+ * live until #request is freed.
+ */
+TR_CONSTRAINT_SET *tr_constraint_set_filter(TID_REQ *request,
+                                            TR_CONSTRAINT_SET *orig,
+                                            const char *constraint_type)
 {
-  json_t *orig_cset = (json_t*) orig;
-  json_t  *new_cs = NULL;
+  json_t *orig_cset = (json_t *) orig;
+  json_t *new_cs = NULL;
   size_t index;
   json_t *set_member;
-  if (!tr_constraint_set_validate( (TR_CONSTRAINT_SET *) orig_cset)) {
+  if (!tr_constraint_set_validate((TR_CONSTRAINT_SET *) orig_cset)) {
     tr_debug ("tr_constraint_set_filter: not a valid constraint set\n");
     return NULL;
   }
-  assert (new_cs = json_array());
+  new_cs = json_array();
+  assert(new_cs);
   json_array_foreach(orig_cset, index, set_member) {
-    if (json_object_get( set_member, constraint_type))
+    if (json_object_get(set_member, constraint_type))
       json_array_append(new_cs, set_member);
   }
+  tid_req_cleanup_json(request, new_cs);
   return (TR_CONSTRAINT_SET *) new_cs;
 }
 
@@ -232,40 +323,39 @@ static void merge_constraints(json_t *constraint, const char *key)
    */
   constraint_array = json_object_get(constraint, key);
   if (NULL == constraint_array)
-      return;
-  json_array_foreach( constraint_array, index_1, value_1)
-    json_array_foreach( constraint_array, index_2, value_2) {
-    if (index_2 <= index_1)
-      continue;
-    if ( tr_prefix_wildcard_match( json_string_value(value_2),
-                                  json_string_value(value_1))) {
-      json_array_remove(constraint_array, index_2);
-      index_2--;
-    }else if (tr_prefix_wildcard_match( json_string_value(value_1),
-                                       json_string_value(value_2))) {
-      json_array_set(constraint_array, index_1, value_2);
-      json_array_remove(constraint_array, index_2);
-      index_2--;
+    return;
+  json_array_foreach(constraint_array, index_1, value_1)json_array_foreach(constraint_array, index_2, value_2) {
+      if (index_2 <= index_1)
+        continue;
+      if (tr_prefix_wildcard_match(json_string_value(value_2),
+                                   json_string_value(value_1))) {
+        json_array_remove(constraint_array, index_2);
+        index_2--;
+      } else if (tr_prefix_wildcard_match(json_string_value(value_1),
+                                          json_string_value(value_2))) {
+        json_array_set(constraint_array, index_1, value_2);
+        json_array_remove(constraint_array, index_2);
+        index_2--;
+      }
     }
-  }
 }
 
 /**
  * Returns an array of constraint strings that is the intersection of
  * all constraints in the constraint_set of type #type
  */
-static json_t *constraint_intersect_internal( TR_CONSTRAINT_SET *constraints,
-                                             const char *constraint_type)
+static json_t *constraint_intersect_internal(TR_CONSTRAINT_SET *constraints,
+                                             const char *constraint_type)
 {
   json_t *constraint, *result = NULL;
   size_t i;
-  json_array_foreach( (json_t *) constraints, i, constraint) {
-    merge_constraints( constraint, constraint_type);
+  json_array_foreach((json_t *) constraints, i, constraint) {
+    merge_constraints(constraint, constraint_type);
     if (NULL == result) {
       result = json_object_get(constraint, constraint_type);
       if (NULL != result)
-       result = json_copy(result);
-    }    else {
+        result = json_copy(result);
+    } else {
       json_t *intersect, *value_1, *value_2;
       size_t index_1, index_2;
       intersect = json_object_get(constraint, constraint_type);
@@ -275,24 +365,25 @@ static json_t *constraint_intersect_internal( TR_CONSTRAINT_SET *constraints,
        *     constraint type we return empty (no access) rather than universal
        * access.*/
       if (!intersect)
-       continue;
-    result_loop:
+        continue;
+      result_loop:
       json_array_foreach(result, index_1, value_1) {
-       json_array_foreach(intersect, index_2, value_2) {
-         if (tr_prefix_wildcard_match( json_string_value(value_1),
-                                       json_string_value(value_2)))
-           goto result_acceptable;
-         else if (tr_prefix_wildcard_match(json_string_value( value_2),
-                                           json_string_value(value_1))) {
-           json_array_set(result, index_1, value_2);
-           goto result_acceptable;
-       }
-       }
-       json_array_remove(result, index_1);
-       if (index_1 == 0)
-         goto result_loop;
-       index_1--;
-      result_acceptable: continue;
+        json_array_foreach(intersect, index_2, value_2) {
+          if (tr_prefix_wildcard_match(json_string_value(value_1),
+                                       json_string_value(value_2)))
+            goto result_acceptable;
+          else if (tr_prefix_wildcard_match(json_string_value(value_2),
+                                            json_string_value(value_1))) {
+            json_array_set(result, index_1, value_2);
+            goto result_acceptable;
+          }
+        }
+        json_array_remove(result, index_1);
+        if (index_1 == 0)
+          goto result_loop;
+        index_1--;
+        result_acceptable:
+        continue;
       }
     }
   }
@@ -303,33 +394,39 @@ static json_t *constraint_intersect_internal( TR_CONSTRAINT_SET *constraints,
  * Return the intersection of domain and realm constraints.
  * Return is live until #request is freed.
  */
-TR_CONSTRAINT_SET *tr_constraint_set_intersect( TID_REQ *request,
-                                               TR_CONSTRAINT_SET *input)
+TR_CONSTRAINT_SET *tr_constraint_set_intersect(TID_REQ *request,
+                                               TR_CONSTRAINT_SET *input)
 {
-  json_t *domain=NULL, *realm=NULL;
+  json_t *domain = NULL, *realm = NULL;
   json_t *result = NULL, *result_array = NULL;
   if (tr_constraint_set_validate(input)) {
     domain = constraint_intersect_internal(input, "domain");
     realm = constraint_intersect_internal(input, "realm");
   }
-  assert(result = json_object());
+  result = json_object();
+  assert(result);
   if (domain)
     json_object_set_new(result, "domain", domain);
   if (realm)
     json_object_set_new(result, "realm", realm);
-  assert(result_array = json_array());
+  result_array = json_array();
+  assert(result_array);
   json_array_append_new(result_array, result);
-  tid_req_cleanup_json( request, result_array);
+  tid_req_cleanup_json(request, result_array);
   return (TR_CONSTRAINT_SET *) result_array;
 }
 
-
-int tr_constraint_set_get_match_strings(
-                                       TID_REQ *request,
-                                       TR_CONSTRAINT_SET *constraints,
-                                       const char *constraint_type,
-                                       tr_const_string **output,
-                                       size_t *output_len)
+/** Get the set of wildcard strings that matches a fully intersected
+ * constraint set.  Requires that the constraint set only have one
+ * constraint in it, but the constraint may have multiple matches for
+ * a given type.  Returns true on success false on failure.  The
+ * output is live as long as the request is live.
+ */
+int tr_constraint_set_get_match_strings(TID_REQ *request,
+                                        TR_CONSTRAINT_SET *constraints,
+                                        const char *constraint_type,
+                                        tr_const_string **output,
+                                        size_t *output_len)
 {
   json_t *cset = (json_t *) constraints;
   json_t *member, *matches, *value;;
@@ -349,8 +446,7 @@ int tr_constraint_set_get_match_strings(
   if (array_size == 0)
     return -1;
   *output = talloc_array_ptrtype(request, *output, array_size);
-  json_array_foreach( matches, index, value)
-    (*output)[index] = json_string_value(value);
+  json_array_foreach(matches, index, value)(*output)[index] = json_string_value(value);
   *output_len = array_size;
   return 0;
 }