Clean up formatting, no functional changes.
[trust_router.git] / common / tr_constraint.c
1 /*
2  * Copyright (c) 2012-2014, 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 #include <jansson.h>
35 #include "jansson_iterators.h"
36 #include <assert.h>
37 #include <talloc.h>
38
39 #include <tr_filter.h>
40 #include <tr_debug.h>
41
42 #include <trust_router/tr_constraint.h>
43 #include <tid_internal.h>
44
45
46 static int tr_constraint_destructor(void *obj)
47 {
48   TR_CONSTRAINT *cons = talloc_get_type_abort(obj, TR_CONSTRAINT);
49   int ii = 0;
50
51   if (cons->type != NULL)
52     tr_free_name(cons->type);
53   for (ii = 0; ii < TR_MAX_CONST_MATCHES; ii++) {
54     if (cons->matches[ii] != NULL)
55       tr_free_name(cons->matches[ii]);
56   }
57   return 0;
58 }
59
60 TR_CONSTRAINT *tr_constraint_new(TALLOC_CTX *mem_ctx)
61 {
62   TR_CONSTRAINT *cons = talloc(mem_ctx, TR_CONSTRAINT);
63   int ii = 0;
64
65   if (cons != NULL) {
66     cons->type = NULL;
67     for (ii = 0; ii < TR_MAX_CONST_MATCHES; ii++)
68       cons->matches[ii] = NULL;
69     talloc_set_destructor((void *) cons, tr_constraint_destructor);
70   }
71   return cons;
72 }
73
74 void tr_constraint_free(TR_CONSTRAINT *cons)
75 {
76   talloc_free(cons);
77 }
78
79 TR_CONSTRAINT *tr_constraint_dup(TALLOC_CTX *mem_ctx, TR_CONSTRAINT *cons)
80 {
81   TALLOC_CTX *tmp_ctx = NULL;
82   TR_CONSTRAINT *new = NULL;
83   int ii = 0;
84
85   if (cons == NULL)
86     return NULL;
87
88   tmp_ctx = talloc_new(NULL);
89   new = tr_constraint_new(tmp_ctx);
90
91   if (new != NULL) {
92     new->type = tr_dup_name(cons->type);
93     for (ii = 0; ii < TR_MAX_CONST_MATCHES; ii++)
94       new->matches[ii] = tr_dup_name(cons->matches[ii]);
95     talloc_steal(mem_ctx, new);
96   }
97
98   talloc_free(tmp_ctx);
99   return new;
100 }
101
102 /* Returns TRUE (1) if the the string (str) matchs the wildcard string (wc_str), FALSE (0) if not.
103  */
104 int tr_prefix_wildcard_match(const char *str, const char *wc_str)
105 {
106   const char *wc_post = wc_str;
107   size_t len = 0;
108   size_t wc_len = 0;
109
110   if ((!str) || (!wc_str))
111     return 0;
112
113   len = strlen(str);
114   if (0 == (wc_len = strlen(wc_str)))
115     return 0;
116
117   /* TBD -- skip leading white space? */
118   if ('*' == wc_str[0]) {
119     wc_post = &(wc_str[1]);
120     wc_len--;
121   } else if (len != wc_len)
122     return 0;
123
124
125   if (wc_len > len)
126     return 0;
127
128   if (0 == strcmp(&(str[len - wc_len]), wc_post)) {
129     return 1;
130   } else
131     return 0;
132 }
133
134 TR_CONSTRAINT_SET *tr_constraint_set_from_fline(TR_FLINE *fline)
135 {
136   json_t *cset = NULL;
137
138   if (!fline)
139     return NULL;
140
141   if (fline->realm_cons)
142     tr_constraint_add_to_set((TR_CONSTRAINT_SET **) &cset, fline->realm_cons);
143   if (fline->domain_cons)
144     tr_constraint_add_to_set((TR_CONSTRAINT_SET **) &cset, fline->domain_cons);
145
146   return (TR_CONSTRAINT_SET *) cset;
147 }
148
149 /* A constraint set is represented in json as an array of constraint
150  * objects.  So, a constraint set (cset) that consists of one realm
151  * constraint and one domain constraint might look like:
152  *
153  *      {cset: [{domain: [a.com, b.co.uk]},
154  *              {realm: [c.net, d.org]}]}
155  */
156
157 void tr_constraint_add_to_set(TR_CONSTRAINT_SET **cset, TR_CONSTRAINT *cons)
158 {
159   json_t *jcons = NULL;
160   json_t *jmatches = NULL;
161   int i = 0;
162
163   if ((!cset) || (!cons))
164     return;
165
166   /* If we don't already have a json object, create one */
167   if (!(*cset))
168     *cset = (TR_CONSTRAINT_SET *) json_array();
169
170   /* Create a json object representing cons */
171   jmatches = json_array();
172   jcons = json_object();
173
174   for (i = 0; ((i < TR_MAX_CONST_MATCHES) && (NULL != cons->matches[i])); i++) {
175     json_array_append_new(jmatches, json_string(cons->matches[i]->buf));
176   }
177
178   json_object_set_new(jcons, cons->type->buf, jmatches);
179
180   /* Add the created object to the cset object */
181   json_array_append_new((json_t *) *cset, jcons);
182 }
183
184 int tr_constraint_set_validate(TR_CONSTRAINT_SET *cset) {
185   json_t *json = (json_t *) cset;
186   size_t i;
187   json_t *set_member;
188   if (!json_is_array(json)) {
189     tr_debug("Constraint_set is not an array");
190     return 0;
191   }
192   json_array_foreach(json, i, set_member) {
193     json_t *value;
194     const char *key;
195     if (!json_is_object(set_member)) {
196       tr_debug("Constraint member at %zu is not an object\n", i);
197       return 0;
198     }
199     json_object_foreach(set_member, key, value) {
200       size_t inner_index;
201       json_t *inner_value;
202       if (!json_is_array(value)) {
203         tr_debug("Constraint type %s at index %zu in constraint set is not an array\n", key,
204                  i);
205         return 0;
206       }
207       json_array_foreach(value, inner_index, inner_value) {
208         if (!json_is_string(inner_value)) {
209           tr_debug("Constraint type %s at index %zu in constraint set has non-string element %zu\n",
210                    key, i, inner_index);
211           return 0;
212         }
213       }
214     }
215   }
216   return 1;
217 }
218
219
220 TR_CONSTRAINT_SET *tr_constraint_set_filter(TID_REQ *request,
221                                             TR_CONSTRAINT_SET *orig,
222                                             const char *constraint_type)
223 {
224   json_t *orig_cset = (json_t *) orig;
225   json_t *new_cs = NULL;
226   size_t index;
227   json_t *set_member;
228   if (!tr_constraint_set_validate((TR_CONSTRAINT_SET *) orig_cset)) {
229     tr_debug ("tr_constraint_set_filter: not a valid constraint set\n");
230     return NULL;
231   }
232   assert (new_cs = json_array());
233   json_array_foreach(orig_cset, index, set_member) {
234     if (json_object_get(set_member, constraint_type))
235       json_array_append(new_cs, set_member);
236   }
237   return (TR_CONSTRAINT_SET *) new_cs;
238 }
239
240 /**
241  * Within a given constraint object merge any overlapping domain or
242  * realm constraints.  For example ['*','*.net'] can be simplified to
243  * ['*']
244  */
245 static void merge_constraints(json_t *constraint, const char *key)
246 {
247   json_t *value_1, *value_2, *constraint_array;
248   size_t index_1, index_2;
249   /*
250    * Go through the loop pairwise linear, removing elements where one
251    * element is a subset of the other.  Always shrik the array from
252    * the end so that index_1 never becomes invalid (swapping if
253    * needed).
254    */
255   constraint_array = json_object_get(constraint, key);
256   if (NULL == constraint_array)
257     return;
258   json_array_foreach(constraint_array, index_1, value_1)json_array_foreach(constraint_array, index_2, value_2) {
259       if (index_2 <= index_1)
260         continue;
261       if (tr_prefix_wildcard_match(json_string_value(value_2),
262                                    json_string_value(value_1))) {
263         json_array_remove(constraint_array, index_2);
264         index_2--;
265       } else if (tr_prefix_wildcard_match(json_string_value(value_1),
266                                           json_string_value(value_2))) {
267         json_array_set(constraint_array, index_1, value_2);
268         json_array_remove(constraint_array, index_2);
269         index_2--;
270       }
271     }
272 }
273
274 /**
275  * Returns an array of constraint strings that is the intersection of
276  * all constraints in the constraint_set of type #type
277  */
278 static json_t *constraint_intersect_internal(TR_CONSTRAINT_SET *constraints,
279                                              const char *constraint_type)
280 {
281   json_t *constraint, *result = NULL;
282   size_t i;
283   json_array_foreach((json_t *) constraints, i, constraint) {
284     merge_constraints(constraint, constraint_type);
285     if (NULL == result) {
286       result = json_object_get(constraint, constraint_type);
287       if (NULL != result)
288         result = json_copy(result);
289     } else {
290       json_t *intersect, *value_1, *value_2;
291       size_t index_1, index_2;
292       intersect = json_object_get(constraint, constraint_type);
293       /*If an element of the constraint set doesn't have a particular
294        * constraint type, we ignore that element of the constraint set.
295        * However, if no element of the constraint set has a particular
296        *     constraint type we return empty (no access) rather than universal
297        * access.*/
298       if (!intersect)
299         continue;
300       result_loop:
301       json_array_foreach(result, index_1, value_1) {
302         json_array_foreach(intersect, index_2, value_2) {
303           if (tr_prefix_wildcard_match(json_string_value(value_1),
304                                        json_string_value(value_2)))
305             goto result_acceptable;
306           else if (tr_prefix_wildcard_match(json_string_value(value_2),
307                                             json_string_value(value_1))) {
308             json_array_set(result, index_1, value_2);
309             goto result_acceptable;
310           }
311         }
312         json_array_remove(result, index_1);
313         if (index_1 == 0)
314           goto result_loop;
315         index_1--;
316         result_acceptable:
317         continue;
318       }
319     }
320   }
321   return result;
322 }
323
324 /**
325  * Return the intersection of domain and realm constraints.
326  * Return is live until #request is freed.
327  */
328 TR_CONSTRAINT_SET *tr_constraint_set_intersect(TID_REQ *request,
329                                                TR_CONSTRAINT_SET *input)
330 {
331   json_t *domain = NULL, *realm = NULL;
332   json_t *result = NULL, *result_array = NULL;
333   if (tr_constraint_set_validate(input)) {
334     domain = constraint_intersect_internal(input, "domain");
335     realm = constraint_intersect_internal(input, "realm");
336   }
337   assert(result = json_object());
338   if (domain)
339     json_object_set_new(result, "domain", domain);
340   if (realm)
341     json_object_set_new(result, "realm", realm);
342   assert(result_array = json_array());
343   json_array_append_new(result_array, result);
344   tid_req_cleanup_json(request, result_array);
345   return (TR_CONSTRAINT_SET *) result_array;
346 }
347
348
349 int tr_constraint_set_get_match_strings(TID_REQ *request,
350                                         TR_CONSTRAINT_SET *constraints,
351                                         const char *constraint_type,
352                                         tr_const_string **output,
353                                         size_t *output_len)
354 {
355   json_t *cset = (json_t *) constraints;
356   json_t *member, *matches, *value;;
357   size_t index, array_size;
358   assert (output && output_len);
359   *output = NULL;
360   *output_len = 0;
361   if (json_array_size(cset) != 1) {
362     tr_debug("Constraint set for get_match_strings has more than one member\n");
363     return -1;
364   }
365   member = json_array_get(cset, 0);
366   matches = json_object_get(member, constraint_type);
367   if (!matches)
368     return -1;
369   array_size = json_array_size(matches);
370   if (array_size == 0)
371     return -1;
372   *output = talloc_array_ptrtype(request, *output, array_size);
373   json_array_foreach(matches, index, value)(*output)[index] = json_string_value(value);
374   *output_len = array_size;
375   return 0;
376 }