Merge pull request #50 from painless-security/jennifer/refactoring_tids
[trust_router.git] / common / tr_config.c
1 /*
2  * Copyright (c) 2012, 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 <stdlib.h>
36 #include <string.h>
37 #include <jansson.h>
38 #include <dirent.h>
39 #include <talloc.h>
40
41 #include <tr_cfgwatch.h>
42 #include <tr_comm.h>
43 #include <tr_config.h>
44 #include <tr_gss_names.h>
45 #include <tr_debug.h>
46 #include <tr_filter.h>
47 #include <trust_router/tr_constraint.h>
48 #include <tr_idp.h>
49 #include <tr.h>
50 #include <trust_router/trp.h>
51
52 #if JANSSON_VERSION_HEX < 0x020500
53 #include "jansson_iterators.h"
54 #endif
55
56 void tr_print_config (TR_CFG *cfg) {
57   tr_notice("tr_print_config: Logging running trust router configuration.");
58   tr_print_comms(cfg->ctable);
59 }
60
61 void tr_print_comms (TR_COMM_TABLE *ctab)
62 {
63   TR_COMM *comm = NULL;
64
65   for (comm = ctab->comms; NULL != comm; comm = comm->next) {
66     tr_notice("tr_print_config: Community %s:", comm->id->buf);
67
68     tr_notice("tr_print_config:  - Member IdPs:");
69     tr_print_comm_idps(ctab, comm);
70
71     tr_notice("tr_print_config:  - Member RPs:");
72     tr_print_comm_rps(ctab, comm);
73   }
74 }
75
76 void tr_print_comm_idps(TR_COMM_TABLE *ctab, TR_COMM *comm)
77 {
78   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
79   TR_COMM_ITER *iter=NULL;
80   TR_IDP_REALM *idp = NULL;
81   char *s=NULL;
82
83   iter=tr_comm_iter_new(tmp_ctx);
84   if (iter==NULL) {
85     tr_notice("tr_print_config: unable to allocate IdP iterator.");
86     talloc_free(tmp_ctx);
87     return;
88   }
89   
90   for (idp=tr_idp_realm_iter_first(iter, ctab, tr_comm_get_id(comm));
91        NULL!=idp;
92        idp=tr_idp_realm_iter_next(iter)) {
93     s=tr_idp_realm_to_str(tmp_ctx, idp);
94     if (s!=NULL)
95       tr_notice("tr_print_config:    - @%s", s);
96     else
97       tr_notice("tr_print_config: unable to allocate IdP output string.");
98   }
99   talloc_free(tmp_ctx);
100 }
101
102 void tr_print_comm_rps(TR_COMM_TABLE *ctab, TR_COMM *comm)
103 {
104   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
105   TR_COMM_ITER *iter=NULL;
106   TR_RP_REALM *rp = NULL;
107   char *s=NULL;
108
109   iter=tr_comm_iter_new(tmp_ctx);
110   if (iter==NULL) {
111     tr_notice("tr_print_config: unable to allocate RP iterator.");
112     talloc_free(tmp_ctx);
113     return;
114   }
115   
116   for (rp=tr_rp_realm_iter_first(iter, ctab, tr_comm_get_id(comm));
117        NULL!=rp;
118        rp=tr_rp_realm_iter_next(iter)) {
119     s=tr_rp_realm_to_str(tmp_ctx, rp);
120     if (s!=NULL)
121       tr_notice("tr_print_config:    - @%s", s);
122     else
123       tr_notice("tr_print_config: unable to allocate RP output string.");
124   }
125   talloc_free(tmp_ctx);
126 }
127
128 TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx)
129 {
130   TR_CFG *cfg=talloc(mem_ctx, TR_CFG);
131   if (cfg!=NULL) {
132     cfg->internal=NULL;
133     cfg->rp_clients=NULL;
134     cfg->peers=NULL;
135     cfg->default_servers=NULL;
136     cfg->ctable=tr_comm_table_new(cfg);
137     if (cfg->ctable==NULL) {
138       talloc_free(cfg);
139       cfg=NULL;
140     }
141   }
142   return cfg;
143 }
144
145 void tr_cfg_free (TR_CFG *cfg)
146 {
147   talloc_free(cfg);
148 }
149
150 TR_CFG_MGR *tr_cfg_mgr_new(TALLOC_CTX *mem_ctx)
151 {
152   return talloc_zero(mem_ctx, TR_CFG_MGR);
153 }
154
155 void tr_cfg_mgr_free (TR_CFG_MGR *cfg_mgr) {
156   talloc_free(cfg_mgr);
157 }
158
159 TR_CFG_RC tr_apply_new_config (TR_CFG_MGR *cfg_mgr)
160 {
161   /* cfg_mgr->active is allowed to be null, but new cannot be */
162   if ((cfg_mgr==NULL) || (cfg_mgr->new==NULL))
163     return TR_CFG_BAD_PARAMS;
164
165   if (cfg_mgr->active != NULL)
166     tr_cfg_free(cfg_mgr->active);
167
168   cfg_mgr->active = cfg_mgr->new;
169   cfg_mgr->new=NULL; /* only keep a single handle on the new configuration */
170
171   tr_log_threshold(cfg_mgr->active->internal->log_threshold);
172   tr_console_threshold(cfg_mgr->active->internal->console_threshold);
173
174   return TR_CFG_SUCCESS;
175 }
176
177 static TR_CONSTRAINT *tr_cfg_parse_one_constraint(TALLOC_CTX *mem_ctx, char *ctype, json_t *jc, TR_CFG_RC *rc)
178 {
179   TR_CONSTRAINT *cons=NULL;
180   int i=0;
181
182   if ((!ctype) || (!jc) || (!rc) ||
183       (!json_is_array(jc)) ||
184       (0 >= json_array_size(jc)) ||
185       (TR_MAX_CONST_MATCHES < json_array_size(jc)) ||
186       (!json_is_string(json_array_get(jc, 0)))) {
187     tr_err("tr_cfg_parse_one_constraint: config error.");
188     *rc=TR_CFG_NOPARSE;
189     return NULL;
190   }
191
192   if (NULL==(cons=tr_constraint_new(mem_ctx))) {
193     tr_debug("tr_cfg_parse_one_constraint: Out of memory (cons).");
194     *rc=TR_CFG_NOMEM;
195     return NULL;
196   }
197
198   if (NULL==(cons->type=tr_new_name(ctype))) {
199     tr_err("tr_cfg_parse_one_constraint: Out of memory (type).");
200     *rc=TR_CFG_NOMEM;
201     tr_constraint_free(cons);
202     return NULL;
203   }
204
205   for (i=0; i < json_array_size(jc); i++) {
206     cons->matches[i]=tr_new_name(json_string_value(json_array_get(jc, i)));
207     if (cons->matches[i]==NULL) {
208       tr_err("tr_cfg_parse_one_constraint: Out of memory (match %d).", i+1);
209       *rc=TR_CFG_NOMEM;
210       tr_constraint_free(cons);
211       return NULL;
212     }
213   }
214
215   return cons;
216 }
217
218 static TR_FILTER *tr_cfg_parse_one_filter(TALLOC_CTX *mem_ctx, json_t *jfilt, TR_FILTER_TYPE ftype, TR_CFG_RC *rc)
219 {
220   TALLOC_CTX *tmp_ctx = talloc_new(NULL);
221   TR_FILTER *filt = NULL;
222   json_t *jfaction = NULL;
223   json_t *jfline = NULL;
224   json_t *jfspecs = NULL;
225   json_t *this_jfspec = NULL;
226   json_t *jfield = NULL;
227   json_t *jmatch = NULL;
228   json_t *jrc = NULL;
229   json_t *jdc = NULL;
230   json_t *this_jmatch = NULL;
231   TR_NAME *name = NULL;
232   size_t i = 0, j = 0, k = 0;
233
234   *rc = TR_CFG_ERROR;
235
236   if ((jfilt == NULL) || (rc == NULL)) {
237     tr_err("tr_cfg_parse_one_filter: null argument");
238     *rc = TR_CFG_BAD_PARAMS;
239     goto cleanup;
240   }
241
242   if (NULL == (filt = tr_filter_new(tmp_ctx))) {
243     tr_err("tr_cfg_parse_one_filter: Out of memory.");
244     *rc = TR_CFG_NOMEM;
245     goto cleanup;
246   }
247   tr_filter_set_type(filt, ftype);
248
249   /* make sure we have space to represent the filter */
250   if (json_array_size(jfilt) > TR_MAX_FILTER_LINES) {
251     tr_err("tr_cfg_parse_one_filter: Filter has too many lines, maximum of %d.", TR_MAX_FILTER_LINES);
252     *rc = TR_CFG_NOPARSE;
253     goto cleanup;
254   }
255
256   /* For each entry in the filter... */
257   json_array_foreach(jfilt, i, jfline) {
258     if ((NULL == (jfaction = json_object_get(jfline, "action"))) ||
259         (!json_is_string(jfaction))) {
260       tr_debug("tr_cfg_parse_one_filter: Error parsing filter action.");
261       *rc = TR_CFG_NOPARSE;
262       goto cleanup;
263     }
264
265     if ((NULL == (jfspecs = json_object_get(jfline, "specs"))) ||
266         (!json_is_array(jfspecs)) ||
267         (0 == json_array_size(jfspecs))) {
268       tr_debug("tr_cfg_parse_one_filter: Error parsing filter specs.");
269       *rc = TR_CFG_NOPARSE;
270       goto cleanup;
271     }
272
273     if (TR_MAX_FILTER_SPECS < json_array_size(jfspecs)) {
274       tr_debug("tr_cfg_parse_one_filter: Filter has too many specs, maximimum of %d.", TR_MAX_FILTER_SPECS);
275       *rc = TR_CFG_NOPARSE;
276       goto cleanup;
277     }
278
279     if (NULL == (filt->lines[i] = tr_fline_new(filt))) {
280       tr_debug("tr_cfg_parse_one_filter: Out of memory allocating filter line %d.", i + 1);
281       *rc = TR_CFG_NOMEM;
282       goto cleanup;
283     }
284
285     if (!strcmp(json_string_value(jfaction), "accept")) {
286       filt->lines[i]->action = TR_FILTER_ACTION_ACCEPT;
287     } else if (!strcmp(json_string_value(jfaction), "reject")) {
288       filt->lines[i]->action = TR_FILTER_ACTION_REJECT;
289     } else {
290       tr_debug("tr_cfg_parse_one_filter: Error parsing filter action, unknown action' %s'.",
291                json_string_value(jfaction));
292       *rc = TR_CFG_NOPARSE;
293       goto cleanup;
294     }
295
296     if (NULL != (jrc = json_object_get(jfline, "realm_constraints"))) {
297       if (!json_is_array(jrc)) {
298         tr_err("tr_cfg_parse_one_filter: cannot parse realm_constraints, not an array.");
299         *rc = TR_CFG_NOPARSE;
300         goto cleanup;
301       } else if (json_array_size(jrc) > TR_MAX_CONST_MATCHES) {
302         tr_err("tr_cfg_parse_one_filter: realm_constraints has too many entries, maximum of %d.",
303                TR_MAX_CONST_MATCHES);
304         *rc = TR_CFG_NOPARSE;
305         goto cleanup;
306       } else if (json_array_size(jrc) > 0) {
307         /* ok we actually have entries to process */
308         if (NULL == (filt->lines[i]->realm_cons = tr_cfg_parse_one_constraint(filt->lines[i], "realm", jrc, rc))) {
309           tr_debug("tr_cfg_parse_one_filter: Error parsing realm constraint");
310           *rc = TR_CFG_NOPARSE;
311           goto cleanup;
312         }
313       }
314     }
315
316     if (NULL != (jdc = json_object_get(jfline, "domain_constraints"))) {
317       if (!json_is_array(jdc)) {
318         tr_err("tr_cfg_parse_one_filter: cannot parse domain_constraints, not an array.");
319         *rc = TR_CFG_NOPARSE;
320         goto cleanup;
321       } else if (json_array_size(jdc) > TR_MAX_CONST_MATCHES) {
322         tr_err("tr_cfg_parse_one_filter: domain_constraints has too many entries, maximum of %d.",
323                TR_MAX_CONST_MATCHES);
324         *rc = TR_CFG_NOPARSE;
325         goto cleanup;
326       } else if (json_array_size(jdc) > 0) {
327         if (NULL == (filt->lines[i]->domain_cons = tr_cfg_parse_one_constraint(filt->lines[i], "domain", jdc, rc))) {
328           tr_debug("tr_cfg_parse_one_filter: Error parsing domain constraint");
329           *rc = TR_CFG_NOPARSE;
330           goto cleanup;
331         }
332       }
333     }
334
335     /*For each filter spec within the filter line... */
336     json_array_foreach(jfspecs, j, this_jfspec) {
337       if ((NULL == (jfield = json_object_get(this_jfspec, "field"))) ||
338           (!json_is_string(jfield))) {
339         tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing field for filer spec %d, filter line %d.", i,
340                  j);
341         *rc = TR_CFG_NOPARSE;
342         goto cleanup;
343       }
344
345       /* check that we have a match attribute */
346       if (NULL == (jmatch = json_object_get(this_jfspec, "match"))) {
347         tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing match for filer spec %d, filter line %d.", i,
348                  j);
349         *rc = TR_CFG_NOPARSE;
350         goto cleanup;
351       }
352
353       /* check that match is a string or an array */
354       if ((!json_is_string(jmatch)) && (!json_is_array(jmatch))) {
355         tr_debug(
356             "tr_cfg_parse_one_filter: Error parsing filter: match not a string or array for filter spec %d, filter line %d.",
357             i, j);
358         *rc = TR_CFG_NOPARSE;
359         goto cleanup;
360       }
361
362       /* allocate the filter spec */
363       if (NULL == (filt->lines[i]->specs[j] = tr_fspec_new(filt->lines[i]))) {
364         tr_debug("tr_cfg_parse_one_filter: Out of memory.");
365         *rc = TR_CFG_NOMEM;
366         goto cleanup;
367       }
368
369       /* fill in the field */
370       if (NULL == (filt->lines[i]->specs[j]->field = tr_new_name(json_string_value(jfield)))) {
371         tr_debug("tr_cfg_parse_one_filter: Out of memory.");
372         *rc = TR_CFG_NOMEM;
373         goto cleanup;
374       }
375
376       /* fill in the matches */
377       if (json_is_string(jmatch)) {
378         if (NULL == (name = tr_new_name(json_string_value(jmatch)))) {
379           tr_debug("tr_cfg_parse_one_filter: Out of memory.");
380           *rc = TR_CFG_NOMEM;
381           goto cleanup;
382         }
383         tr_fspec_add_match(filt->lines[i]->specs[j], name);
384       } else {
385         /* jmatch is an array (we checked earlier) */
386         json_array_foreach(jmatch, k, this_jmatch) {
387           if (NULL == (name = tr_new_name(json_string_value(this_jmatch)))) {
388             tr_debug("tr_cfg_parse_one_filter: Out of memory.");
389             *rc = TR_CFG_NOMEM;
390             goto cleanup;
391           }
392           tr_fspec_add_match(filt->lines[i]->specs[j], name);
393         }
394       }
395       if (!tr_filter_validate_spec_field(ftype, filt->lines[i]->specs[j])){
396         tr_debug("tr_cfg_parse_one_filter: Invalid filter field \"%.*s\" for %s filter, spec %d, filter %d.",
397                  filt->lines[i]->specs[j]->field->len,
398                  filt->lines[i]->specs[j]->field->buf,
399                  tr_filter_type_to_string(filt->type),
400                  i, j);
401         *rc = TR_CFG_ERROR;
402         goto cleanup;
403       }
404     }
405   }
406
407   /* check that the filter is valid */
408   if (!tr_filter_validate(filt)) {
409     *rc = TR_CFG_ERROR;
410   } else {
411     *rc = TR_CFG_SUCCESS;
412     talloc_steal(mem_ctx, filt);
413   }
414
415  cleanup:
416   talloc_free(tmp_ctx);
417   if (*rc!=TR_CFG_SUCCESS)
418     filt=NULL;
419   return filt;
420 }
421
422 static TR_FILTER_SET *tr_cfg_parse_filters(TALLOC_CTX *mem_ctx, json_t *jfilts, TR_CFG_RC *rc)
423 {
424   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
425   json_t *jfilt;
426   const char *filt_label=NULL;
427   TR_FILTER *filt=NULL;
428   TR_FILTER_SET *filt_set=NULL;
429   TR_FILTER_TYPE filt_type=TR_FILTER_TYPE_UNKNOWN;
430
431   *rc=TR_CFG_ERROR;
432
433   /* no filters */
434   if (jfilts==NULL) {
435     *rc=TR_CFG_SUCCESS;
436     goto cleanup;
437   }
438
439   filt_set=tr_filter_set_new(tmp_ctx);
440   if (filt_set==NULL) {
441     tr_debug("tr_cfg_parse_filters: Unable to allocate filter set.");
442     *rc = TR_CFG_NOMEM;
443     goto cleanup;
444   }
445
446   json_object_foreach(jfilts, filt_label, jfilt) {
447     /* check that we got a filter */
448     if (jfilt == NULL) {
449       tr_debug("tr_cfg_parse_filters: Definition for %s filter is missing.", filt_label);
450       *rc = TR_CFG_NOPARSE;
451       goto cleanup;
452     }
453
454     /* check that we recognize the filter type */
455     filt_type=tr_filter_type_from_string(filt_label);
456     if (filt_type==TR_FILTER_TYPE_UNKNOWN) {
457       tr_debug("tr_cfg_parse_filters: Unrecognized filter (%s) defined.", filt_label);
458       *rc = TR_CFG_NOPARSE;
459       goto cleanup;
460     }
461
462     /* finally, parse the filter */
463     tr_debug("tr_cfg_parse_filters: Found %s filter.", filt_label);
464     filt = tr_cfg_parse_one_filter(tmp_ctx, jfilt, filt_type, rc);
465     tr_filter_set_add(filt_set, filt);
466     if (*rc != TR_CFG_SUCCESS) {
467       tr_debug("tr_cfg_parse_filters: Error parsing %s filter.", filt_label);
468       *rc = TR_CFG_NOPARSE;
469       goto cleanup;
470     }
471   }
472
473   *rc=TR_CFG_SUCCESS;
474
475  cleanup:
476   if (*rc==TR_CFG_SUCCESS)
477     talloc_steal(mem_ctx, filt_set);
478   else if (filt_set!=NULL) {
479     talloc_free(filt_set);
480     filt_set=NULL;
481   }
482
483   talloc_free(tmp_ctx);
484   return filt_set;
485 }
486
487 static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc)
488 {
489   TR_AAA_SERVER *aaa = NULL;
490   TR_NAME *name=NULL;
491
492   if ((!jaddr) || (!json_is_string(jaddr))) {
493     tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
494     *rc = TR_CFG_BAD_PARAMS;
495     return NULL;
496   }
497
498   name=tr_new_name(json_string_value(jaddr));
499   if (name==NULL) {
500     tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating hostname.");
501     *rc = TR_CFG_NOMEM;
502     return NULL;
503   }
504
505   aaa=tr_aaa_server_new(mem_ctx, name);
506   if (aaa==NULL) {
507     tr_free_name(name);
508     tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server.");
509     *rc = TR_CFG_NOMEM;
510     return NULL;
511   }
512
513   return aaa;
514 }
515
516 static TR_AAA_SERVER *tr_cfg_parse_aaa_servers(TALLOC_CTX *mem_ctx, json_t *jaaas, TR_CFG_RC *rc) 
517 {
518   TALLOC_CTX *tmp_ctx=NULL;
519   TR_AAA_SERVER *aaa = NULL;
520   TR_AAA_SERVER *temp_aaa = NULL;
521   int i = 0;
522
523   for (i = 0; i < json_array_size(jaaas); i++) {
524     /* rc gets set in here */
525     if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(tmp_ctx, json_array_get(jaaas, i), rc))) {
526       talloc_free(tmp_ctx);
527       return NULL;
528     }
529     /* TBD -- IPv6 addresses */
530     //    tr_debug("tr_cfg_parse_aaa_servers: Configuring AAA Server: ip_addr = %s.", inet_ntoa(temp_aaa->aaa_server_addr));
531     temp_aaa->next = aaa;
532     aaa = temp_aaa;
533   }
534   tr_debug("tr_cfg_parse_aaa_servers: Finished (rc=%d)", *rc);
535
536   for (temp_aaa=aaa; temp_aaa!=NULL; temp_aaa=temp_aaa->next)
537     talloc_steal(mem_ctx, temp_aaa);
538   talloc_free(tmp_ctx);
539   return aaa;
540 }
541
542 static TR_APC *tr_cfg_parse_one_apc(TALLOC_CTX *mem_ctx, json_t *japc, TR_CFG_RC *rc)
543 {
544   TR_APC *apc=NULL;
545   TR_NAME *name=NULL;
546   
547   *rc = TR_CFG_SUCCESS;         /* presume success */
548
549   if ((!japc) || (!rc) || (!json_is_string(japc))) {
550     tr_debug("tr_cfg_parse_one_apc: Bad parameters.");
551     if (rc) 
552       *rc = TR_CFG_BAD_PARAMS;
553     return NULL;
554   }
555
556   apc=tr_apc_new(mem_ctx);
557   if (apc==NULL) {
558     tr_debug("tr_cfg_parse_one_apc: Out of memory.");
559     *rc = TR_CFG_NOMEM;
560     return NULL;
561   }
562
563   name=tr_new_name(json_string_value(japc));
564   if (name==NULL) {
565     tr_debug("tr_cfg_parse_one_apc: No memory for APC name.");
566     tr_apc_free(apc);
567     *rc = TR_CFG_NOMEM;
568     return NULL;
569   }
570   tr_apc_set_id(apc, name); /* apc is now responsible for freeing the name */
571
572   return apc;
573 }
574
575 static TR_APC *tr_cfg_parse_apcs(TALLOC_CTX *mem_ctx, json_t *japcs, TR_CFG_RC *rc)
576 {
577   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
578   TR_APC *apcs=NULL;
579   TR_APC *new_apc=NULL;
580   int ii=0;
581   TR_CFG_RC call_rc=TR_CFG_ERROR;
582   
583   *rc = TR_CFG_SUCCESS;         /* presume success */
584
585   if ((!japcs) || (!rc) || (!json_is_array(japcs))) {
586     tr_debug("tr_cfg_parse_one_apc: Bad parameters.");
587     if (rc) 
588       *rc = TR_CFG_BAD_PARAMS;
589     return NULL;
590   }
591
592   for (ii=0; ii<json_array_size(japcs); ii++) {
593     new_apc=tr_cfg_parse_one_apc(tmp_ctx, json_array_get(japcs, ii), &call_rc);
594     if ((call_rc!=TR_CFG_SUCCESS) || (new_apc==NULL)) {
595       tr_debug("tr_cfg_parse_apcs: Error parsing APC %d.", ii+1);
596       *rc=TR_CFG_NOPARSE;
597       goto cleanup;
598     }
599     tr_apc_add(apcs, new_apc);
600   }
601
602   talloc_steal(mem_ctx, apcs);
603   *rc=TR_CFG_SUCCESS;
604
605  cleanup:
606   talloc_free(tmp_ctx);
607   return apcs;
608 }
609
610 static TR_NAME *tr_cfg_parse_name(TALLOC_CTX *mem_ctx, json_t *jname, TR_CFG_RC *rc)
611 {
612   TR_NAME *name=NULL;
613   *rc=TR_CFG_ERROR;
614
615   if ((jname==NULL) || (!json_is_string(jname))) {
616     tr_err("tr_cfg_parse_name: name missing or not a string");
617     *rc=TR_CFG_BAD_PARAMS;
618     name=NULL;
619   } else {
620     name=tr_new_name(json_string_value(jname));
621     if (name==NULL) {
622       tr_err("tr_cfg_parse_name: could not allocate name");
623       *rc=TR_CFG_NOMEM;
624     } else {
625       *rc=TR_CFG_SUCCESS;
626     }
627   }
628   return name;  
629 }
630
631 static int tr_cfg_parse_shared_config(json_t *jsc, TR_CFG_RC *rc)
632 {
633   const char *shared=NULL;
634   *rc=TR_CFG_SUCCESS;
635   
636   if ((jsc==NULL) ||
637       (!json_is_string(jsc)) ||
638       (NULL==(shared=json_string_value(jsc)))) {
639     *rc=TR_CFG_BAD_PARAMS;
640     return -1;
641   }
642
643   if (0==strcmp(shared, "no"))
644     return 0;
645   else if (0==strcmp(shared, "yes"))
646     return 1;
647
648   /* any other value is an error */
649   tr_debug("tr_cfg_parse_shared_config: invalid shared_config value \"%s\" (should be \"yes\" or \"no\")",
650            shared);
651   *rc=TR_CFG_NOPARSE;
652   return -1;
653 }
654
655 static TR_REALM_ORIGIN tr_cfg_realm_origin(json_t *jrealm)
656 {
657   json_t *jremote=json_object_get(jrealm, "remote");
658   const char *s=NULL;
659
660   if (jremote==NULL)
661     return TR_REALM_LOCAL;
662   if (!json_is_string(jremote)) {
663     tr_warning("tr_cfg_realm_origin: \"remote\" is not a string, assuming this is a local realm.");
664     return TR_REALM_LOCAL;
665   }
666   s=json_string_value(jremote);
667   if (strcasecmp(s, "yes")==0)
668     return TR_REALM_REMOTE_INCOMPLETE;
669   else if (strcasecmp(s, "no")!=0)
670     tr_warning("tr_cfg_realm_origin: \"remote\" is neither 'yes' nor 'no', assuming this is a local realm.");
671
672   return TR_REALM_LOCAL;
673 }
674
675 /* Parse the identity provider object from a realm and fill in the given TR_IDP_REALM. */
676 static TR_CFG_RC tr_cfg_parse_idp(TR_IDP_REALM *idp, json_t *jidp)
677 {
678   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
679   TR_APC *apcs=NULL;
680   TR_AAA_SERVER *aaa=NULL;
681   TR_CFG_RC rc=TR_CFG_ERROR;
682   
683   if (jidp==NULL)
684     goto cleanup;
685
686   idp->shared_config=tr_cfg_parse_shared_config(json_object_get(jidp, "shared_config"), &rc);
687   if (rc!=TR_CFG_SUCCESS) {
688     tr_err("tr_cfg_parse_idp: missing or malformed shared_config specification");
689     rc=TR_CFG_NOPARSE;
690     goto cleanup;
691   }
692
693   apcs=tr_cfg_parse_apcs(tmp_ctx, json_object_get(jidp, "apcs"), &rc);
694   if ((rc!=TR_CFG_SUCCESS) || (apcs==NULL)) {
695     tr_err("tr_cfg_parse_idp: unable to parse APC");
696     rc=TR_CFG_NOPARSE;
697     goto cleanup;
698   }
699   
700   aaa=tr_cfg_parse_aaa_servers(idp, json_object_get(jidp, "aaa_servers"), &rc);
701   if (rc!=TR_CFG_SUCCESS) {
702     tr_err("tr_cfg_parse_idp: unable to parse AAA servers");
703     rc=TR_CFG_NOPARSE;
704     goto cleanup;
705   }
706   
707   tr_debug("tr_cfg_parse_idp: APC=\"%.*s\"",
708            apcs->id->len,
709            apcs->id->buf);
710   
711   /* done, fill in the idp structures */
712   idp->apcs=apcs;
713   talloc_steal(idp, apcs);
714   idp->aaa_servers=aaa;
715   rc=TR_CFG_SUCCESS;
716   
717 cleanup:
718   if (rc!=TR_CFG_SUCCESS) {
719     if (apcs!=NULL)
720       tr_apc_free(apcs);
721     if (aaa!=NULL)
722       tr_aaa_server_free(aaa);
723   }
724   
725   talloc_free(tmp_ctx);
726   return rc;
727 }
728
729 /* parses idp realm */
730 static TR_IDP_REALM *tr_cfg_parse_one_idp_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
731 {
732   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
733   TR_IDP_REALM *realm=NULL;
734   TR_CFG_RC call_rc=TR_CFG_ERROR;
735
736   *rc=TR_CFG_ERROR; /* default to error if not set */
737
738   if ((!jrealm) || (!rc)) {
739     tr_err("tr_cfg_parse_one_idp_realm: Bad parameters.");
740     if (rc)
741       *rc=TR_CFG_BAD_PARAMS;
742     goto cleanup;
743   }
744
745   if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
746     tr_err("tr_cfg_parse_one_idp_realm: could not allocate idp realm.");
747     *rc=TR_CFG_NOMEM;
748     goto cleanup;
749   }
750
751   realm->origin=tr_cfg_realm_origin(jrealm);
752   if (realm->origin!=TR_REALM_LOCAL) {
753     tr_debug("tr_cfg_parse_one_idp_realm: realm is remote, should not have full IdP info.");
754     *rc=TR_CFG_NOPARSE;
755     goto cleanup;
756   }
757
758   /* must have a name */
759   realm->realm_id=tr_cfg_parse_name(realm,
760                                     json_object_get(jrealm, "realm"),
761                                    &call_rc);
762   if ((call_rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
763     tr_err("tr_cfg_parse_one_idp_realm: could not parse realm name");
764     *rc=TR_CFG_NOPARSE;
765     goto cleanup;
766   }
767   tr_debug("tr_cfg_parse_one_idp_realm: realm_id=\"%.*s\"",
768            realm->realm_id->len,
769            realm->realm_id->buf);
770         
771   call_rc=tr_cfg_parse_idp(realm, json_object_get(jrealm, "identity_provider"));
772   if (call_rc!=TR_CFG_SUCCESS) {
773     tr_err("tr_cfg_parse_one_idp_realm: could not parse identity_provider.");
774     *rc=TR_CFG_NOPARSE;
775     goto cleanup;
776   }
777   
778   *rc=TR_CFG_SUCCESS;
779
780 cleanup:
781   if (*rc==TR_CFG_SUCCESS)
782     talloc_steal(mem_ctx, realm);
783   else {
784     talloc_free(realm);
785     realm=NULL;
786   }
787   
788   talloc_free(tmp_ctx);
789   return realm;
790 }
791
792   /* Determine whether the realm is an IDP realm */
793 static int tr_cfg_is_idp_realm(json_t *jrealm)
794 {
795   /* If a realm spec contains an identity_provider, it's an IDP realm. */
796   if (NULL != json_object_get(jrealm, "identity_provider"))
797     return 1;
798   else
799     return 0;
800 }
801
802 static TR_IDP_REALM *tr_cfg_parse_one_remote_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
803 {
804   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
805   TR_IDP_REALM *realm=talloc(mem_ctx, TR_IDP_REALM);
806   
807   *rc=TR_CFG_ERROR; /* default to error if not set */
808
809   if ((!jrealm) || (!rc)) {
810     tr_err("tr_cfg_parse_one_remote_realm: Bad parameters.");
811     if (rc)
812       *rc=TR_CFG_BAD_PARAMS;
813     goto cleanup;
814   }
815
816   if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
817     tr_err("tr_cfg_parse_one_remote_realm: could not allocate idp realm.");
818     *rc=TR_CFG_NOMEM;
819     goto cleanup;
820   }
821
822   /* must have a name */
823   realm->realm_id=tr_cfg_parse_name(realm,
824                                     json_object_get(jrealm, "realm"),
825                                     rc);
826   if ((*rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
827     tr_err("tr_cfg_parse_one_remote_realm: could not parse realm name");
828     *rc=TR_CFG_NOPARSE;
829     goto cleanup;
830   }
831   tr_debug("tr_cfg_parse_one_remote_realm: realm_id=\"%.*s\"",
832            realm->realm_id->len,
833            realm->realm_id->buf);
834
835   realm->origin=tr_cfg_realm_origin(jrealm);
836   *rc=TR_CFG_SUCCESS;
837
838 cleanup:
839   if (*rc==TR_CFG_SUCCESS)
840     talloc_steal(mem_ctx, realm);
841   else {
842     talloc_free(realm);
843     realm=NULL;
844   }
845   
846   talloc_free(tmp_ctx);
847   return realm;
848 }
849
850 static int tr_cfg_is_remote_realm(json_t *jrealm)
851 {
852   return (tr_cfg_realm_origin(jrealm)!=TR_REALM_LOCAL);
853 }
854
855 /* Parse any idp realms in the j_realms object. Ignores other realm types. */
856 static TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc)
857 {
858   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
859   TR_IDP_REALM *realms=NULL;
860   TR_IDP_REALM *new_realm=NULL;
861   json_t *this_jrealm=NULL;
862   int ii=0;
863
864   *rc=TR_CFG_ERROR;
865   if ((jrealms==NULL) || (!json_is_array(jrealms))) {
866     tr_err("tr_cfg_parse_idp_realms: realms not an array");
867     *rc=TR_CFG_BAD_PARAMS;
868     goto cleanup;
869   }
870
871   for (ii=0; ii<json_array_size(jrealms); ii++) {
872     this_jrealm=json_array_get(jrealms, ii);
873     if (tr_cfg_is_idp_realm(this_jrealm)) {
874       new_realm=tr_cfg_parse_one_idp_realm(tmp_ctx, this_jrealm, rc);
875       if ((*rc)!=TR_CFG_SUCCESS) {
876         tr_err("tr_cfg_parse_idp_realms: error decoding realm entry %d", ii+1);
877         *rc=TR_CFG_NOPARSE;
878         goto cleanup;
879       }
880       tr_idp_realm_add(realms, new_realm);
881     } else if (tr_cfg_is_remote_realm(this_jrealm)) {
882       new_realm=tr_cfg_parse_one_remote_realm(tmp_ctx, this_jrealm, rc);
883       if ((*rc)!=TR_CFG_SUCCESS) {
884         tr_err("tr_cfg_parse_idp_realms: error decoding remote realm entry %d", ii+1);
885         *rc=TR_CFG_NOPARSE;
886         goto cleanup;
887       }
888       tr_idp_realm_add(realms, new_realm);
889     }
890   }
891   
892   *rc=TR_CFG_SUCCESS;
893   talloc_steal(mem_ctx, realms);
894
895 cleanup:
896   talloc_free(tmp_ctx);
897   return realms;
898 }
899
900 static TR_GSS_NAMES *tr_cfg_parse_gss_names(TALLOC_CTX *mem_ctx, json_t *jgss_names, TR_CFG_RC *rc)
901 {
902   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
903   TR_GSS_NAMES *gn=NULL;
904   json_t *jname=NULL;
905   int ii=0;
906   TR_NAME *name=NULL;
907
908   if ((rc==NULL) || (jgss_names==NULL)) {
909     tr_err("tr_cfg_parse_gss_names: Bad parameters.");
910     *rc=TR_CFG_BAD_PARAMS;
911
912   }
913
914   if (!json_is_array(jgss_names)) {
915     tr_err("tr_cfg_parse_gss_names: gss_names not an array.");
916     *rc=TR_CFG_NOPARSE;
917     goto cleanup;
918   }
919
920   gn=tr_gss_names_new(tmp_ctx);
921   for (ii=0; ii<json_array_size(jgss_names); ii++) {
922     jname=json_array_get(jgss_names, ii);
923     if (!json_is_string(jname)) {
924       tr_err("tr_cfg_parse_gss_names: Encountered non-string gss name.");
925       *rc=TR_CFG_NOPARSE;
926       goto cleanup;
927     }
928
929     name=tr_new_name(json_string_value(jname));
930     if (name==NULL) {
931       tr_err("tr_cfg_parse_gss_names: Out of memory allocating gss name.");
932       *rc=TR_CFG_NOMEM;
933       goto cleanup;
934     }
935
936     if (tr_gss_names_add(gn, name)!=0) {
937       tr_free_name(name);
938       tr_err("tr_cfg_parse_gss_names: Unable to add gss name to RP client.");
939       *rc=TR_CFG_ERROR;
940       goto cleanup;
941     }
942   }
943
944   talloc_steal(mem_ctx, gn);
945   *rc=TR_CFG_SUCCESS;
946
947  cleanup:
948   talloc_free(tmp_ctx);
949   if ((*rc!=TR_CFG_SUCCESS) && (gn!=NULL))
950     gn=NULL;
951   return gn;
952 }
953
954 /* default filter accepts realm and *.realm */
955 static TR_FILTER_SET *tr_cfg_default_filters(TALLOC_CTX *mem_ctx, TR_NAME *realm, TR_CFG_RC *rc)
956 {
957   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
958   TR_FILTER *filt=NULL;
959   TR_FILTER_SET *filt_set=NULL;
960   TR_CONSTRAINT *cons=NULL;
961   TR_NAME *name=NULL;
962   TR_NAME *n_prefix=tr_new_name("*.");
963   TR_NAME *n_rp_realm_1=tr_new_name("rp_realm");
964   TR_NAME *n_rp_realm_2=tr_new_name("rp_realm");
965   TR_NAME *n_domain=tr_new_name("domain");
966   TR_NAME *n_realm=tr_new_name("realm");
967   
968
969   if ((realm==NULL) || (rc==NULL)) {
970     tr_debug("tr_cfg_default_filters: invalid arguments.");
971     if (rc!=NULL)
972       *rc=TR_CFG_BAD_PARAMS;
973     goto cleanup;
974   }
975
976   if ((n_prefix==NULL) ||
977       (n_rp_realm_1==NULL) ||
978       (n_rp_realm_2==NULL) ||
979       (n_domain==NULL) ||
980       (n_realm==NULL)) {
981     tr_debug("tr_cfg_default_filters: unable to allocate names.");
982     *rc=TR_CFG_NOMEM;
983     goto cleanup;
984   }
985
986   filt=tr_filter_new(tmp_ctx);
987   if (filt==NULL) {
988     tr_debug("tr_cfg_default_filters: could not allocate filter.");
989     *rc=TR_CFG_NOMEM;
990     goto cleanup;
991   }
992   tr_filter_set_type(filt, TR_FILTER_TYPE_TID_INBOUND);
993   filt->lines[0]=tr_fline_new(filt);
994   if (filt->lines[0]==NULL) {
995     tr_debug("tr_cfg_default_filters: could not allocate filter line.");
996     *rc=TR_CFG_NOMEM;
997     goto cleanup;
998   }
999
1000   filt->lines[0]->action=TR_FILTER_ACTION_ACCEPT;
1001   filt->lines[0]->specs[0]=tr_fspec_new(filt->lines[0]);
1002   filt->lines[0]->specs[0]->field=n_rp_realm_1;
1003   n_rp_realm_1=NULL; /* we don't own this name any more */
1004
1005   name=tr_dup_name(realm);
1006   if (name==NULL) {
1007     tr_debug("tr_cfg_default_filters: could not allocate realm name.");
1008     *rc=TR_CFG_NOMEM;
1009     goto cleanup;
1010   }
1011   tr_fspec_add_match(filt->lines[0]->specs[0], name);
1012   name=NULL; /* we no longer own the name */
1013
1014   /* now do the wildcard name */
1015   filt->lines[0]->specs[1]=tr_fspec_new(filt->lines[0]);
1016   filt->lines[0]->specs[1]->field=n_rp_realm_2;
1017   n_rp_realm_2=NULL; /* we don't own this name any more */
1018
1019   if (NULL==(name=tr_name_cat(n_prefix, realm))) {
1020     tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name.");
1021     *rc=TR_CFG_NOMEM;
1022     goto cleanup;
1023   }
1024
1025   tr_fspec_add_match(filt->lines[0]->specs[1], name);
1026   name=NULL; /* we no longer own the name */
1027
1028   /* domain constraint */
1029   if (NULL==(cons=tr_constraint_new(filt->lines[0]))) {
1030     tr_debug("tr_cfg_default_filters: could not allocate domain constraint.");
1031     *rc=TR_CFG_NOMEM;
1032     goto cleanup;
1033   }
1034
1035   cons->type=n_domain;
1036   n_domain=NULL; /* belongs to the constraint now */
1037   name=tr_dup_name(realm);
1038   if (name==NULL) {
1039     tr_debug("tr_cfg_default_filters: could not allocate realm name for domain constraint.");
1040     *rc=TR_CFG_NOMEM;
1041     goto cleanup;
1042   }
1043   cons->matches[0]=name;
1044   name=tr_name_cat(n_prefix, realm);
1045   if (name==NULL) {
1046     tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name for domain constraint.");
1047     *rc=TR_CFG_NOMEM;
1048     goto cleanup;
1049   }
1050   cons->matches[1]=name;
1051   name=NULL;
1052   filt->lines[0]->domain_cons=cons;
1053
1054
1055   /* realm constraint */
1056   if (NULL==(cons=tr_constraint_new(filt->lines[0]))) {
1057     tr_debug("tr_cfg_default_filters: could not allocate realm constraint.");
1058     *rc=TR_CFG_NOMEM;
1059     goto cleanup;
1060   }
1061
1062   cons->type=n_realm;
1063   n_realm=NULL; /* belongs to the constraint now */
1064   name=tr_dup_name(realm);
1065   if (name==NULL) {
1066     tr_debug("tr_cfg_default_filters: could not allocate realm name for realm constraint.");
1067     *rc=TR_CFG_NOMEM;
1068     goto cleanup;
1069   }
1070   cons->matches[0]=name;
1071   name=tr_name_cat(n_prefix, realm);
1072   if (name==NULL) {
1073     tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name for realm constraint.");
1074     *rc=TR_CFG_NOMEM;
1075     goto cleanup;
1076   }
1077   cons->matches[1]=name;
1078   name=NULL;
1079   filt->lines[0]->realm_cons=cons;
1080
1081   /* put the filter in a set */
1082   filt_set=tr_filter_set_new(tmp_ctx);
1083   if ((filt_set==NULL)||(0!=tr_filter_set_add(filt_set, filt))) {
1084     tr_debug("tr_cfg_default_filters: could not allocate filter set.");
1085     *rc=TR_CFG_NOMEM;
1086     goto cleanup;
1087   }
1088   talloc_steal(mem_ctx, filt_set);
1089
1090 cleanup:
1091   talloc_free(tmp_ctx);
1092
1093   if (*rc!=TR_CFG_SUCCESS)
1094     filt=NULL;
1095
1096   if (n_prefix!=NULL)
1097     tr_free_name(n_prefix);
1098   if (n_rp_realm_1!=NULL)
1099     tr_free_name(n_rp_realm_1);
1100   if (n_rp_realm_2!=NULL)
1101     tr_free_name(n_rp_realm_2);
1102   if (n_realm!=NULL)
1103     tr_free_name(n_realm);
1104   if (n_domain!=NULL)
1105     tr_free_name(n_domain);
1106   if (name!=NULL)
1107     tr_free_name(name);
1108
1109   return filt_set;
1110 }
1111
1112 /* parses rp client */
1113 static TR_RP_CLIENT *tr_cfg_parse_one_rp_client(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
1114 {
1115   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1116   TR_RP_CLIENT *client=NULL;
1117   TR_CFG_RC call_rc=TR_CFG_ERROR;
1118   TR_FILTER_SET *new_filts=NULL;
1119   TR_NAME *realm=NULL;
1120   json_t *jfilt=NULL;
1121   json_t *jrealm_id=NULL;
1122
1123   *rc=TR_CFG_ERROR; /* default to error if not set */
1124
1125   if ((!jrealm) || (!rc)) {
1126     tr_err("tr_cfg_parse_one_rp_client: Bad parameters.");
1127     if (rc)
1128       *rc=TR_CFG_BAD_PARAMS;
1129     goto cleanup;
1130   }
1131
1132   if ((NULL==(jrealm_id=json_object_get(jrealm, "realm"))) || (!json_is_string(jrealm_id))) {
1133     tr_debug("tr_cfg_parse_one_rp_client: no realm ID found.");
1134     *rc=TR_CFG_BAD_PARAMS;
1135     goto cleanup;
1136   } 
1137
1138   tr_debug("tr_cfg_parse_one_rp_client: realm_id=\"%s\"", json_string_value(jrealm_id));
1139   realm=tr_new_name(json_string_value(jrealm_id));
1140   if (realm==NULL) {
1141     tr_err("tr_cfg_parse_one_rp_client: could not allocate realm ID.");
1142     *rc=TR_CFG_NOMEM;
1143     goto cleanup;
1144   }
1145
1146   if (NULL==(client=tr_rp_client_new(tmp_ctx))) {
1147     tr_err("tr_cfg_parse_one_rp_client: could not allocate rp client.");
1148     *rc=TR_CFG_NOMEM;
1149     goto cleanup;
1150   }
1151
1152   client->gss_names=tr_cfg_parse_gss_names(client, json_object_get(jrealm, "gss_names"), &call_rc);
1153
1154   if (call_rc!=TR_CFG_SUCCESS) {
1155     tr_err("tr_cfg_parse_one_rp_client: could not parse gss_names.");
1156     *rc=TR_CFG_NOPARSE;
1157     goto cleanup;
1158   }
1159
1160   /* parse filters */
1161   jfilt=json_object_get(jrealm, "filters");
1162   if (jfilt!=NULL) {
1163     new_filts=tr_cfg_parse_filters(tmp_ctx, jfilt, &call_rc);
1164     if (call_rc!=TR_CFG_SUCCESS) {
1165       tr_err("tr_cfg_parse_one_rp_client: could not parse filters.");
1166       *rc=TR_CFG_NOPARSE;
1167       goto cleanup;
1168     }
1169   } else {
1170     tr_debug("tr_cfg_parse_one_rp_client: no filters specified, using default filters.");
1171     new_filts= tr_cfg_default_filters(tmp_ctx, realm, &call_rc);
1172     if (call_rc!=TR_CFG_SUCCESS) {
1173       tr_err("tr_cfg_parse_one_rp_client: could not set default filters.");
1174       *rc=TR_CFG_NOPARSE;
1175       goto cleanup;
1176     }
1177   }
1178
1179   tr_rp_client_set_filters(client, new_filts);
1180   *rc=TR_CFG_SUCCESS;
1181
1182   cleanup:
1183   if (realm!=NULL)
1184     tr_free_name(realm);
1185
1186   if (*rc==TR_CFG_SUCCESS)
1187     talloc_steal(mem_ctx, client);
1188   else {
1189     talloc_free(client);
1190     client=NULL;
1191   }
1192
1193   talloc_free(tmp_ctx);
1194   return client;
1195 }
1196
1197   /* Determine whether the realm is an RP realm */
1198 static int tr_cfg_is_rp_realm(json_t *jrealm)
1199 {
1200   /* Check that we have a gss name. */
1201   if (NULL != json_object_get(jrealm, "gss_names"))
1202     return 1;
1203   else
1204     return 0;
1205 }
1206
1207 /* Parse any rp clients in the j_realms object. Ignores other realms. */
1208 static TR_RP_CLIENT *tr_cfg_parse_rp_clients(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc)
1209 {
1210   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1211   TR_RP_CLIENT *clients=NULL;
1212   TR_RP_CLIENT *new_client=NULL;
1213   json_t *this_jrealm=NULL;
1214   int ii=0;
1215
1216   *rc=TR_CFG_ERROR;
1217   if ((jrealms==NULL) || (!json_is_array(jrealms))) {
1218     tr_err("tr_cfg_parse_rp_clients: realms not an array");
1219     *rc=TR_CFG_BAD_PARAMS;
1220     goto cleanup;
1221   }
1222
1223   for (ii=0; ii<json_array_size(jrealms); ii++) {
1224     this_jrealm=json_array_get(jrealms, ii);
1225     if (tr_cfg_is_rp_realm(this_jrealm)) {
1226       new_client=tr_cfg_parse_one_rp_client(tmp_ctx, this_jrealm, rc);
1227       if ((*rc)!=TR_CFG_SUCCESS) {
1228         tr_err("tr_cfg_parse_rp_clients: error decoding realm entry %d", ii+1);
1229         *rc=TR_CFG_NOPARSE;
1230         goto cleanup;
1231       }
1232       tr_rp_client_add(clients, new_client);
1233     }
1234   }
1235   
1236   *rc=TR_CFG_SUCCESS;
1237   talloc_steal(mem_ctx, clients);
1238
1239 cleanup:
1240   talloc_free(tmp_ctx);
1241   return clients;
1242 }
1243
1244 /* takes a talloc context, but currently does not use it */
1245 static TR_NAME *tr_cfg_parse_org_name(TALLOC_CTX *mem_ctx, json_t *j_org, TR_CFG_RC *rc)
1246 {
1247   TR_NAME *name=NULL;
1248
1249   if ((j_org==NULL) || (rc==NULL) || (!json_is_string(j_org))) {
1250     tr_debug("tr_cfg_parse_org_name: Bad parameters.");
1251     if (rc!=NULL)
1252       *rc = TR_CFG_BAD_PARAMS; /* fill in return value if we can */
1253     return NULL;
1254   }
1255
1256   name=tr_new_name(json_string_value(j_org));
1257   if (name==NULL)
1258     *rc=TR_CFG_NOMEM;
1259   else
1260     *rc=TR_CFG_SUCCESS;
1261   return name;
1262 }
1263
1264 static TR_CFG_RC tr_cfg_parse_one_local_org(TR_CFG *trc, json_t *jlorg)
1265 {
1266   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1267   TR_CFG_RC retval=TR_CFG_ERROR; /* our return code */
1268   TR_CFG_RC rc=TR_CFG_ERROR; /* return code from functions we call */
1269   TR_NAME *org_name=NULL;
1270   json_t *j_org=NULL;
1271   json_t *j_realms=NULL;
1272   TR_IDP_REALM *new_idp_realms=NULL;
1273   TR_RP_CLIENT *new_rp_clients=NULL;
1274
1275   tr_debug("tr_cfg_parse_one_local_org: parsing local organization");
1276
1277   /* get organization_name (optional) */
1278   if (NULL==(j_org=json_object_get(jlorg, "organization_name"))) {
1279     tr_debug("tr_cfg_parse_one_local_org: organization_name unspecified");
1280   } else {
1281     org_name=tr_cfg_parse_org_name(tmp_ctx, j_org, &rc);
1282     if (rc==TR_CFG_SUCCESS) {
1283       tr_debug("tr_cfg_parse_one_local_org: organization_name=\"%.*s\"",
1284                org_name->len,
1285                org_name->buf);
1286       /* we don't actually do anything with this, but we could */
1287       tr_free_name(org_name);
1288       org_name=NULL; 
1289     }
1290   }
1291
1292   /* Now get realms. Allow this to be missing; even though that is a pointless organization entry,
1293    * it's harmless. Report a warning because that might be unintentional. */
1294   if (NULL==(j_realms=json_object_get(jlorg, "realms"))) {
1295     tr_warning("tr_cfg_parse_one_local_org: warning - no realms in this local organization");
1296   } else {
1297     /* Allocate in the tmp_ctx so these will be cleaned up if we do not complete successfully. */
1298     new_idp_realms=tr_cfg_parse_idp_realms(tmp_ctx, j_realms, &rc);
1299     if (rc!=TR_CFG_SUCCESS)
1300       goto cleanup;
1301
1302     new_rp_clients=tr_cfg_parse_rp_clients(tmp_ctx, j_realms, &rc);
1303     if (rc!=TR_CFG_SUCCESS)
1304       goto cleanup;
1305   }
1306   retval=TR_CFG_SUCCESS;
1307   
1308 cleanup:
1309   /* if we succeeded, link things to the configuration and move out of tmp context */
1310   if (retval==TR_CFG_SUCCESS) {
1311     if (new_idp_realms!=NULL) {
1312       tr_idp_realm_add(trc->ctable->idp_realms, new_idp_realms); /* fixes talloc contexts except for head*/
1313       talloc_steal(trc, trc->ctable->idp_realms); /* make sure the head is in the right context */
1314     }
1315
1316     if (new_rp_clients!=NULL) {
1317       tr_rp_client_add(trc->rp_clients, new_rp_clients); /* fixes talloc contexts */
1318       talloc_steal(trc, trc->rp_clients); /* make sure head is in the right context */
1319     }
1320   }
1321
1322   talloc_free(tmp_ctx);
1323   return rc;
1324 }
1325
1326 /* Parse local organizations if present. Returns success if there are none. On failure, the configuration is unreliable. */
1327 static TR_CFG_RC tr_cfg_parse_local_orgs(TR_CFG *trc, json_t *jcfg)
1328 {
1329   json_t *jlocorgs=NULL;
1330   size_t ii=0;
1331
1332   jlocorgs=json_object_get(jcfg, "local_organizations");
1333   if (jlocorgs==NULL)
1334     return TR_CFG_SUCCESS;
1335
1336   if (!json_is_array(jlocorgs)) {
1337     tr_err("tr_cfg_parse_local_orgs: local_organizations is not an array.");
1338     return TR_CFG_NOPARSE;
1339   }
1340
1341   for (ii=0; ii<json_array_size(jlocorgs); ii++) {
1342     if (tr_cfg_parse_one_local_org(trc, json_array_get(jlocorgs, ii))!=TR_CFG_SUCCESS) {
1343       tr_err("tr_cfg_parse_local_orgs: error parsing local_organization %d.", ii+1);
1344       return TR_CFG_NOPARSE;
1345     }
1346   }
1347
1348   return TR_CFG_SUCCESS;
1349 }
1350
1351 static TR_CFG_RC tr_cfg_parse_one_peer_org(TR_CFG *trc, json_t *jporg)
1352 {
1353   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1354   json_t *jhost=NULL;
1355   json_t *jport=NULL;
1356   json_t *jgss=NULL;
1357   json_t *jfilt=NULL;
1358   TRP_PEER *new_peer=NULL;
1359   TR_GSS_NAMES *names=NULL;
1360   TR_FILTER_SET *filt_set=NULL;
1361   TR_CFG_RC rc=TR_CFG_ERROR;
1362
1363   jhost=json_object_get(jporg, "hostname");
1364   jport=json_object_get(jporg, "port");
1365   jgss=json_object_get(jporg, "gss_names");
1366   jfilt=json_object_get(jporg, "filters");
1367
1368   if ((jhost==NULL) || (!json_is_string(jhost))) {
1369     tr_err("tr_cfg_parse_one_peer_org: hostname not specified or not a string.");
1370     rc=TR_CFG_NOPARSE;
1371     goto cleanup;
1372   }
1373
1374   if ((jport!=NULL) && (!json_is_number(jport))) {
1375     /* note that not specifying the port is allowed, but if set it must be a number */
1376     tr_err("tr_cfg_parse_one_peer_org: port is not a number.");
1377     rc=TR_CFG_NOPARSE;
1378     goto cleanup;
1379   }
1380
1381   if ((jgss==NULL) || (!json_is_array(jgss))) {
1382     tr_err("tr_cfg_parse_one_peer_org: gss_names not specified or not an array.");
1383     rc=TR_CFG_NOPARSE;
1384     goto cleanup;
1385   }
1386
1387   if ((jfilt!=NULL) && (!json_is_object(jfilt))) {
1388     tr_err("tr_cfg_parse_one_peer_org: filters is not an object.");
1389     rc=TR_CFG_NOPARSE;
1390     goto cleanup;
1391   }
1392
1393   new_peer=trp_peer_new(tmp_ctx);
1394   if (new_peer==NULL) {
1395     tr_err("tr_cfg_parse_one_peer_org: could not allocate new peer.");
1396     rc=TR_CFG_NOMEM;
1397     goto cleanup;
1398   }
1399
1400   trp_peer_set_server(new_peer, json_string_value(jhost)); /* string is strdup'ed in _set_server() */
1401   if (jport==NULL)
1402     trp_peer_set_port(new_peer, TRP_PORT);
1403   else
1404     trp_peer_set_port(new_peer, json_integer_value(jport));
1405
1406   names=tr_cfg_parse_gss_names(tmp_ctx, jgss, &rc);
1407   if (rc!=TR_CFG_SUCCESS) {
1408     tr_err("tr_cfg_parse_one_peer_org: unable to parse gss names.");
1409     rc=TR_CFG_NOPARSE;
1410     goto cleanup;
1411   }
1412   trp_peer_set_gss_names(new_peer, names);
1413
1414   if (jfilt) {
1415     filt_set=tr_cfg_parse_filters(tmp_ctx, jfilt, &rc);
1416     if (rc!=TR_CFG_SUCCESS) {
1417       tr_err("tr_cfg_parse_one_peer_org: unable to parse filters.");
1418       rc=TR_CFG_NOPARSE;
1419       goto cleanup;
1420     }
1421     trp_peer_set_filters(new_peer, filt_set);
1422   }
1423
1424   /* success! */
1425   trp_ptable_add(trc->peers, new_peer);
1426   rc=TR_CFG_SUCCESS;
1427
1428  cleanup:
1429   talloc_free(tmp_ctx);
1430   return rc;
1431 }
1432
1433 /* Parse peer organizations, if present. Returns success if there are none. */
1434 static TR_CFG_RC tr_cfg_parse_peer_orgs(TR_CFG *trc, json_t *jcfg)
1435 {
1436   json_t *jpeerorgs=NULL;
1437   int ii=0;
1438
1439   jpeerorgs=json_object_get(jcfg, "peer_organizations");
1440   if (jpeerorgs==NULL)
1441     return TR_CFG_SUCCESS;
1442
1443   if (!json_is_array(jpeerorgs)) {
1444     tr_err("tr_cfg_parse_peer_orgs: peer_organizations is not an array.");
1445     return TR_CFG_NOPARSE;
1446   }
1447
1448   for (ii=0; ii<json_array_size(jpeerorgs); ii++) {
1449     if (tr_cfg_parse_one_peer_org(trc, json_array_get(jpeerorgs, ii))!=TR_CFG_SUCCESS) {
1450       tr_err("tr_cfg_parse_peer_orgs: error parsing peer_organization %d.", ii+1);
1451       return TR_CFG_NOPARSE;
1452     }
1453   }
1454
1455   return TR_CFG_SUCCESS;
1456 }
1457              
1458 static TR_CFG_RC tr_cfg_parse_default_servers (TR_CFG *trc, json_t *jcfg) 
1459 {
1460   json_t *jdss = NULL;
1461   TR_CFG_RC rc = TR_CFG_SUCCESS;
1462   TR_AAA_SERVER *ds = NULL;
1463   int i = 0;
1464
1465   if ((!trc) || (!jcfg))
1466     return TR_CFG_BAD_PARAMS;
1467
1468   /* If there are default servers, store them */
1469   if ((NULL != (jdss = json_object_get(jcfg, "default_servers"))) &&
1470       (json_is_array(jdss)) &&
1471       (0 < json_array_size(jdss))) {
1472
1473     for (i = 0; i < json_array_size(jdss); i++) {
1474       if (NULL == (ds = tr_cfg_parse_one_aaa_server(trc,
1475                                                     json_array_get(jdss, i), 
1476                                                    &rc))) {
1477         return rc;
1478       }
1479       tr_debug("tr_cfg_parse_default_servers: Default server configured: %s", ds->hostname->buf);
1480       ds->next = trc->default_servers;
1481       trc->default_servers = ds;
1482     }
1483   } 
1484
1485   tr_debug("tr_cfg_parse_default_servers: Finished (rc=%d)", rc);
1486   return rc;
1487 }
1488
1489 static void tr_cfg_parse_comm_idps(TR_CFG *trc, json_t *jidps, TR_COMM *comm, TR_CFG_RC *rc)
1490 {
1491   TR_IDP_REALM *found_idp=NULL;
1492   json_t *jidp_name=NULL;
1493   TR_NAME *idp_name=NULL;
1494   size_t ii = 0;
1495
1496   if ((!trc) ||
1497       (!jidps) ||
1498       (!json_is_array(jidps))) {
1499     if (rc)
1500       *rc = TR_CFG_BAD_PARAMS;
1501     return;
1502   }
1503
1504   json_array_foreach(jidps, ii, jidp_name) {
1505     idp_name=tr_new_name(json_string_value(jidp_name));
1506     if (idp_name==NULL) {
1507       *rc = TR_CFG_NOMEM;
1508       return;
1509     }
1510     found_idp=tr_cfg_find_idp(trc, idp_name, rc);
1511     tr_free_name(idp_name);
1512
1513     if ((found_idp==NULL) || (*rc!=TR_CFG_SUCCESS)) {
1514       tr_debug("tr_cfg_parse_comm_idps: Unknown IDP %s.", json_string_value(jidp_name));
1515       *rc=TR_CFG_ERROR;
1516       return;
1517     }
1518     tr_comm_add_idp_realm(trc->ctable, comm, found_idp, 0, NULL, NULL); /* no provenance, never expires */
1519   }
1520
1521   *rc=TR_CFG_SUCCESS;
1522   return;
1523 }
1524
1525 static void tr_cfg_parse_comm_rps(TR_CFG *trc, json_t *jrps, TR_COMM *comm, TR_CFG_RC *rc)
1526 {
1527   TR_RP_REALM *found_rp=NULL;
1528   TR_RP_REALM *new_rp=NULL;
1529   TR_NAME *rp_name=NULL;
1530   const char *s=NULL;
1531   int ii=0;
1532
1533   if ((!trc) ||
1534       (!jrps) ||
1535       (!json_is_array(jrps))) {
1536     if (rc)
1537       *rc = TR_CFG_BAD_PARAMS;
1538     return;
1539   }
1540
1541   for (ii=0; ii<json_array_size(jrps); ii++) {
1542     /* get the RP name as a string */
1543     s=json_string_value(json_array_get(jrps, ii));
1544     if (s==NULL) {
1545       tr_notice("tr_cfg_parse_comm_rps: null RP found in community %.*s, ignoring.",
1546                 tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
1547       continue;
1548     }
1549
1550     /* convert string to TR_NAME */
1551     rp_name=tr_new_name(s);
1552     if (rp_name==NULL) {
1553       tr_err("tr_cfg_parse_comm_rps: unable to allocate RP name for %s in community %.*s.",
1554              s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
1555     }
1556
1557     /* see if we already have this RP in this community */
1558     found_rp=tr_comm_find_rp(trc->ctable, comm, rp_name);
1559     if (found_rp!=NULL) {
1560       tr_notice("tr_cfg_parse_comm_rps: RP %s repeated in community %.*s.",
1561                 s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
1562       tr_free_name(rp_name);
1563       continue;
1564     }
1565
1566     /* Add the RP to the community, first see if we have the RP in any community */
1567     found_rp=tr_rp_realm_lookup(trc->ctable->rp_realms, rp_name);
1568     if (found_rp!=NULL) {
1569       tr_debug("tr_cfg_parse_comm_rps: RP realm %s already exists.", s);
1570       new_rp=found_rp; /* use it rather than creating a new realm record */
1571     } else {
1572       new_rp=tr_rp_realm_new(NULL);
1573       if (new_rp==NULL) {
1574         tr_err("tr_cfg_parse_comm_rps: unable to allocate RP record for %s in community %.*s.",
1575                s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
1576       }
1577       tr_debug("tr_cfg_parse_comm_rps: setting name to %s", rp_name->buf);
1578       tr_rp_realm_set_id(new_rp, rp_name);
1579       rp_name=NULL; /* rp_name no longer belongs to us */
1580       tr_rp_realm_add(trc->ctable->rp_realms, new_rp);
1581       talloc_steal(trc->ctable, trc->ctable->rp_realms); /* make sure head is in the right context */
1582     }
1583     tr_comm_add_rp_realm(trc->ctable, comm, new_rp, 0, NULL, NULL);
1584   }
1585 }
1586
1587 static TR_COMM *tr_cfg_parse_one_comm (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc)
1588 {
1589   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1590   TR_COMM *comm = NULL;
1591   json_t *jid = NULL;
1592   json_t *jtype = NULL;
1593   json_t *japcs = NULL;
1594   json_t *jidps = NULL;
1595   json_t *jrps = NULL;
1596
1597   if ((!trc) || (!jcomm) || (!rc)) {
1598     tr_debug("tr_cfg_parse_one_comm: Bad parameters.");
1599     if (rc)
1600       *rc = TR_CFG_BAD_PARAMS;
1601     goto cleanup;
1602   }
1603
1604   comm=tr_comm_new(tmp_ctx);
1605   if (comm==NULL) {
1606     tr_crit("tr_cfg_parse_one_comm: Out of memory.");
1607     *rc = TR_CFG_NOMEM;
1608     goto cleanup;
1609   }
1610
1611
1612   if ((NULL == (jid = json_object_get(jcomm, "community_id"))) ||
1613       (!json_is_string(jid)) ||
1614       (NULL == (jtype = json_object_get(jcomm, "type"))) ||
1615       (!json_is_string(jtype)) ||
1616       (NULL == (japcs = json_object_get(jcomm, "apcs"))) ||
1617       (!json_is_array(japcs)) ||
1618       (NULL == (jidps = json_object_get(jcomm, "idp_realms"))) ||
1619       (!json_is_array(jidps)) ||
1620       (NULL == (jrps = json_object_get(jcomm, "rp_realms"))) ||
1621       (!json_is_array(jrps))) {
1622     tr_debug("tr_cfg_parse_one_comm: Error parsing Communities configuration.");
1623     *rc = TR_CFG_NOPARSE;
1624     comm=NULL;
1625     goto cleanup;
1626   }
1627
1628   tr_comm_set_id(comm, tr_new_name(json_string_value(jid)));
1629   if (NULL == tr_comm_get_id(comm)) {
1630     tr_debug("tr_cfg_parse_one_comm: No memory for community id.");
1631     *rc = TR_CFG_NOMEM;
1632     comm=NULL;
1633     goto cleanup;
1634   }
1635
1636   if (0 == strcmp(json_string_value(jtype), "apc")) {
1637     comm->type = TR_COMM_APC;
1638   } else if (0 == strcmp(json_string_value(jtype), "coi")) {
1639     comm->type = TR_COMM_COI;
1640     if (NULL == (comm->apcs = tr_cfg_parse_apcs(trc, japcs, rc))) {
1641       tr_debug("tr_cfg_parse_one_comm: Can't parse APCs for COI %s.",
1642                tr_comm_get_id(comm)->buf);
1643       comm=NULL;
1644       goto cleanup;
1645     }
1646   } else {
1647     tr_debug("tr_cfg_parse_one_comm: Invalid community type, comm = %s, type = %s",
1648              tr_comm_get_id(comm)->buf, json_string_value(jtype));
1649     *rc = TR_CFG_NOPARSE;
1650     comm=NULL;
1651     goto cleanup;
1652   }
1653
1654   tr_cfg_parse_comm_idps(trc, jidps, comm, rc);
1655   if (TR_CFG_SUCCESS != *rc) {
1656     tr_debug("tr_cfg_parse_one_comm: Can't parse IDP realms for comm %s.",
1657              tr_comm_get_id(comm)->buf);
1658     comm=NULL;
1659     goto cleanup;
1660   }
1661
1662   tr_cfg_parse_comm_rps(trc, jrps, comm, rc);
1663   if (TR_CFG_SUCCESS != *rc) {
1664     tr_debug("tr_cfg_parse_one_comm: Can't parse RP realms for comm %s .",
1665              tr_comm_get_id(comm)->buf);
1666     comm=NULL;
1667     goto cleanup;
1668   }
1669
1670   if (TR_COMM_APC == comm->type) {
1671     json_t *jexpire  = json_object_get(jcomm, "expiration_interval");
1672     comm->expiration_interval = 43200; /*30 days*/
1673     if (jexpire) {
1674       if (!json_is_integer(jexpire)) {
1675         tr_err("tr_parse_one_comm: expiration_interval is not an integer for comm %.*s",
1676                  tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
1677         comm=NULL;
1678         goto cleanup;
1679       }
1680       comm->expiration_interval = json_integer_value(jexpire);
1681       if (comm->expiration_interval <= 10) {
1682         comm->expiration_interval = 11; /* Freeradius waits 10 minutes between successful TR queries*/
1683         tr_notice(
1684             "tr_parse_one_comm: expiration interval for %.*s less than minimum of 11 minutes; using 11 minutes instead.",
1685             tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
1686       }
1687       if (comm->expiration_interval > 129600) {
1688         /* > 90 days*/
1689         comm->expiration_interval = 129600;
1690         tr_notice(
1691             "tr_parse_one_comm: expiration interval for %.*s exceeds maximum of 90 days; using 90 days instead.",
1692             tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
1693       }
1694     }
1695   }
1696
1697 cleanup:
1698   if (comm!=NULL)
1699     talloc_steal(mem_ctx, comm);
1700   talloc_free(tmp_ctx);
1701   return comm;
1702 }
1703
1704 static TR_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg) 
1705 {
1706   json_t *jcomms = NULL;
1707   TR_CFG_RC rc = TR_CFG_SUCCESS;
1708   TR_COMM *comm = NULL;
1709   int i = 0;
1710
1711   if ((!trc) || (!jcfg)) {
1712     tr_debug("tr_cfg_parse_comms: Bad Parameters.");
1713     return TR_CFG_BAD_PARAMS;
1714   }
1715
1716   if (NULL != (jcomms = json_object_get(jcfg, "communities"))) {
1717     if (!json_is_array(jcomms)) {
1718       return TR_CFG_NOPARSE;
1719     }
1720
1721     for (i = 0; i < json_array_size(jcomms); i++) {
1722       if (NULL == (comm = tr_cfg_parse_one_comm(NULL, /* TODO: use a talloc context */
1723                                                 trc, 
1724                                                 json_array_get(jcomms, i), 
1725                                                &rc))) {
1726         return rc;
1727       }
1728       tr_debug("tr_cfg_parse_comms: Community configured: %s.",
1729                tr_comm_get_id(comm)->buf);
1730
1731       tr_comm_table_add_comm(trc->ctable, comm);
1732     }
1733   }
1734   tr_debug("tr_cfg_parse_comms: Finished (rc=%d)", rc);
1735   return rc;
1736 }
1737
1738 TR_CFG_RC tr_cfg_validate(TR_CFG *trc)
1739 {
1740   TR_CFG_RC rc = TR_CFG_SUCCESS;
1741
1742   if (!trc)
1743     return TR_CFG_BAD_PARAMS;
1744
1745   if ((NULL == trc->internal)||
1746       (NULL == trc->internal->hostname)) {
1747     tr_debug("tr_cfg_validate: Error: No internal configuration, or no hostname.");
1748     rc = TR_CFG_ERROR;
1749   }
1750
1751   if (NULL == trc->rp_clients) {
1752     tr_debug("tr_cfg_validate: Error: No RP Clients configured");
1753     rc = TR_CFG_ERROR;
1754   }
1755
1756   if (0==tr_comm_table_size(trc->ctable)) {
1757     tr_debug("tr_cfg_validate: Error: No Communities configured");
1758     rc = TR_CFG_ERROR;
1759   }
1760
1761   if ((NULL == trc->default_servers) && (NULL == trc->ctable->idp_realms)) {
1762     tr_debug("tr_cfg_validate: Error: No default servers or IDPs configured.");
1763     rc = TR_CFG_ERROR;
1764   }
1765   
1766   return rc;
1767 }
1768
1769 static void tr_cfg_log_json_error(const char *label, json_error_t *rc)
1770 {
1771   tr_debug("%s: JSON parse error on line %d: %s",
1772            label,
1773            rc->line,
1774            rc->text);
1775 }
1776
1777 /**
1778  * Parse a config file and return its JSON structure. Also emits a serial number to the log
1779  * if one is present.
1780  *
1781  * @param file_with_path The file (with path!) to parse
1782  * @return Pointer to the result of parsing, or null on error
1783  */
1784 static json_t *tr_cfg_parse_one_config_file(const char *file_with_path)
1785 {
1786   json_t *jcfg=NULL;
1787   json_t *jser=NULL;
1788   json_error_t rc;
1789
1790   if (NULL==(jcfg=json_load_file(file_with_path, 
1791                                  JSON_DISABLE_EOF_CHECK|JSON_REJECT_DUPLICATES, &rc))) {
1792     tr_debug("tr_cfg_parse_one_config_file: Error parsing config file %s.", 
1793              file_with_path);
1794     tr_cfg_log_json_error("tr_cfg_parse_one_config_file", &rc);
1795     return NULL;
1796   }
1797
1798   // Look for serial number and log it if it exists (borrowed reference, so no need to free it later)
1799   if (NULL!=(jser=json_object_get(jcfg, "serial_number"))) {
1800     if (json_is_number(jser)) {
1801       tr_notice("tr_parse_one_config_file: Attempting to load revision %" JSON_INTEGER_FORMAT " of '%s'.",
1802                 json_integer_value(jser),
1803                 file_with_path);
1804     }
1805   }
1806
1807   return jcfg;
1808 }
1809
1810 /**
1811  * Helper to free an array returned by tr_cfg_parse_config_files
1812  * @param n_jcfgs
1813  * @param jcfgs
1814  */
1815 static void tr_cfg_parse_free_jcfgs(unsigned int n_jcfgs, json_t **jcfgs)
1816 {
1817   int ii=0;
1818   for (ii=0; ii<n_jcfgs; ii++)
1819     json_decref(jcfgs[ii]);
1820   talloc_free(jcfgs);
1821 }
1822
1823 /**
1824  * Parse a list of configuration files. Returns an array of JSON objects, free this with
1825  * tr_cfg_parse_free_jcfgs(), a helper function
1826  *
1827  * @param config_dir
1828  * @param n_files
1829  * @param cfg_files
1830  * @return
1831  */
1832 static json_t **tr_cfg_parse_config_files(TALLOC_CTX *mem_ctx, unsigned int n_files, char **files_with_paths)
1833 {
1834   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1835   unsigned int ii=0;
1836   json_t **jcfgs=NULL;
1837
1838   /* first allocate the jcfgs */
1839   jcfgs=talloc_array(NULL, json_t *, n_files);
1840   if (jcfgs==NULL) {
1841     tr_crit("tr_parse_config_files: cannot allocate JSON structure array");
1842     goto cleanup;
1843   }
1844   for (ii=0; ii<n_files; ii++) {
1845     jcfgs[ii]=tr_cfg_parse_one_config_file(files_with_paths[ii]);
1846     if (jcfgs[ii]==NULL) {
1847       tr_err("tr_parse_config: Error parsing JSON in %s", files_with_paths[ii]);
1848       tr_cfg_parse_free_jcfgs(ii, jcfgs); /* frees the JSON objects and the jcfgs array */
1849       jcfgs=NULL;
1850       goto cleanup;
1851     }
1852   }
1853 cleanup:
1854   if (jcfgs)
1855     talloc_steal(mem_ctx, jcfgs); /* give this to the caller's context if we succeeded */
1856   talloc_free(tmp_ctx);
1857   return jcfgs;
1858 }
1859
1860 /* define a type for config parse functions */
1861 typedef TR_CFG_RC (TR_CFG_PARSE_FN)(TR_CFG *, json_t *);
1862 /**
1863  * Helper function to parse a collection of JSON structures using a generic parse function.
1864  *
1865  * @param cfg Config structure to receive results
1866  * @param parse_fn Function to apply
1867  * @param n_jcfg Number of JSON structures in the array
1868  * @param jcfgs Pointer to an array of decoded JSON structures
1869  * @param key Key to extract from each jcfg before parsing, or NULL to use the object itself
1870  * @return TR_CFG_SUCCESS on success, _FAIL or an error code on failure
1871  */
1872 static TR_CFG_RC tr_cfg_parse_helper(TR_CFG *cfg,
1873                                      TR_CFG_PARSE_FN parse_fn,
1874                                      unsigned int n_jcfg,
1875                                      json_t **jcfgs,
1876                                      const char *key)
1877 {
1878   size_t ii=0;
1879   json_t *this_jcfg=NULL;
1880   TR_CFG_RC ret=TR_CFG_ERROR;
1881
1882   if ((cfg==NULL) || (jcfgs==NULL) || (parse_fn==NULL))
1883     return TR_CFG_ERROR;
1884
1885   for (ii=0; ii<n_jcfg; ii++) {
1886     if (key)
1887       this_jcfg = json_object_get(jcfgs[ii], key);
1888     else
1889       this_jcfg = jcfgs[ii];
1890
1891     /* do not try to parse a missing jcfg */
1892     if (this_jcfg == NULL)
1893       continue;
1894
1895     ret=parse_fn(cfg, this_jcfg);
1896     if (ret!=TR_CFG_SUCCESS)
1897       break;
1898   }
1899   return ret;
1900 }
1901
1902
1903 /**
1904  *  Reads configuration files in config_dir ("" or "./" will use the current directory).
1905  *
1906  * @param cfg_mgr Configuration manager
1907  * @param n_files Number of entries in cfg_files
1908  * @param files_with_paths Array of filenames with path to load
1909  * @return TR_CFG_SUCCESS on success, TR_CFG_ERROR or a more specific error on failure
1910  */
1911 TR_CFG_RC tr_parse_config(TR_CFG_MGR *cfg_mgr, unsigned int n_files, char **files_with_paths)
1912 {
1913   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1914   json_t **jcfgs=NULL;
1915   TR_CFG_RC cfg_rc=TR_CFG_ERROR;
1916
1917   if ((!cfg_mgr) || (!files_with_paths)) {
1918     cfg_rc=TR_CFG_BAD_PARAMS;
1919     goto cleanup;
1920   }
1921
1922   /* get a fresh config to fill in, freeing old one if needed */
1923   if (cfg_mgr->new != NULL)
1924     tr_cfg_free(cfg_mgr->new);
1925   cfg_mgr->new=tr_cfg_new(tmp_ctx); /* belongs to the temporary context for now */
1926   if (cfg_mgr->new == NULL) {
1927     cfg_rc=TR_CFG_NOMEM;
1928     goto cleanup;
1929   }
1930
1931   /* first parse the json */
1932   jcfgs=tr_cfg_parse_config_files(tmp_ctx, n_files, files_with_paths);
1933   if (jcfgs==NULL) {
1934     cfg_rc=TR_CFG_NOPARSE;
1935     goto cleanup;
1936   }
1937
1938   cfg_mgr->new->peers=trp_ptable_new(cfg_mgr); /* not sure why this isn't in cfg_mgr->new's context */
1939
1940   /* now run through the parsers on the JSON */
1941   if ((TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_internal, n_files, jcfgs, "tr_internal"))) ||
1942       (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_local_orgs, n_files, jcfgs, NULL))) ||
1943       (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_peer_orgs, n_files, jcfgs, NULL))) ||
1944       (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_default_servers, n_files, jcfgs,
1945                                                       NULL))) ||
1946       (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_comms, n_files, jcfgs, NULL))))
1947     goto cleanup; /* cfg_rc was set above */
1948
1949   /* make sure we got a complete, consistent configuration */
1950   if (TR_CFG_SUCCESS != tr_cfg_validate(cfg_mgr->new)) {
1951     tr_err("tr_parse_config: Error: INVALID CONFIGURATION");
1952     cfg_rc=TR_CFG_ERROR;
1953     goto cleanup;
1954   }
1955
1956   /* success! */
1957   talloc_steal(cfg_mgr, cfg_mgr->new); /* hand this over to the cfg_mgr context */
1958   cfg_rc=TR_CFG_SUCCESS;
1959
1960 cleanup:
1961   if (jcfgs!=NULL)
1962     tr_cfg_parse_free_jcfgs(n_files, jcfgs);
1963   talloc_free(tmp_ctx);
1964   return cfg_rc;
1965 }
1966
1967 TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc)
1968 {
1969
1970   TR_IDP_REALM *cfg_idp;
1971
1972   if ((!tr_cfg) || (!idp_id)) {
1973     if (rc)
1974       *rc = TR_CFG_BAD_PARAMS;
1975     return NULL;
1976   }
1977
1978   for (cfg_idp = tr_cfg->ctable->idp_realms; NULL != cfg_idp; cfg_idp = cfg_idp->next) {
1979     if (!tr_name_cmp (idp_id, cfg_idp->realm_id)) {
1980       tr_debug("tr_cfg_find_idp: Found %s.", idp_id->buf);
1981       return cfg_idp;
1982     }
1983   }
1984   /* if we didn't find one, return NULL */ 
1985   return NULL;
1986 }
1987
1988 TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc)
1989 {
1990   TR_RP_CLIENT *cfg_rp;
1991
1992   if ((!tr_cfg) || (!rp_gss)) {
1993     if (rc)
1994       *rc = TR_CFG_BAD_PARAMS;
1995     return NULL;
1996   }
1997
1998   for (cfg_rp = tr_cfg->rp_clients; NULL != cfg_rp; cfg_rp = cfg_rp->next) {
1999     if (tr_gss_names_matches(cfg_rp->gss_names, rp_gss)) {
2000       tr_debug("tr_cfg_find_rp: Found %s.", rp_gss->buf);
2001       return cfg_rp;
2002     }
2003   }
2004   /* if we didn't find one, return NULL */ 
2005   return NULL;
2006 }
2007
2008 static int is_cfg_file(const struct dirent *dent) {
2009   int n;
2010
2011   /* Only accept filenames ending in ".cfg" and starting with a character
2012    * other than an ASCII '.' */
2013
2014   /* filename must be at least 4 characters long to be acceptable */
2015   n=strlen(dent->d_name);
2016   if (n < 4) {
2017     return 0;
2018   }
2019
2020   /* filename must not start with '.' */
2021   if ('.' == dent->d_name[0]) {
2022     return 0;
2023   }
2024
2025   /* If the above passed and the last four characters of the filename are .cfg, accept.
2026    * (n.b., assumes an earlier test checked that the name is >= 4 chars long.) */
2027   if (0 == strcmp(&(dent->d_name[n-4]), ".cfg")) {
2028     return 1;
2029   }
2030
2031   /* otherwise, return false. */
2032   return 0;
2033 }
2034
2035 /* Find configuration files in a particular directory. Returns the
2036  * number of entries found, 0 if none are found, or <0 for some
2037  * errors. If n>=0, the cfg_files parameter will contain a newly
2038  * allocated array of pointers to struct dirent entries, as returned
2039  * by scandir(). These can be freed with tr_free_config_file_list().
2040  */
2041 int tr_find_config_files(const char *config_dir, struct dirent ***cfg_files) {
2042   int n = 0;
2043   
2044   n = scandir(config_dir, cfg_files, is_cfg_file, alphasort);
2045
2046   if (n < 0) {
2047     perror("scandir");
2048     tr_debug("tr_find_config: scandir error trying to scan %s.", config_dir);
2049   } 
2050
2051   return n;
2052 }
2053
2054 /* Free memory allocated for configuration file list returned from tr_find_config_files().
2055  * This can be called regardless of the return value of tr_find_config_values(). */
2056 void tr_free_config_file_list(int n, struct dirent ***cfg_files) {
2057   int ii;
2058
2059   /* if n < 0, then scandir did not allocate anything because it failed */
2060   if((n>=0) && (*cfg_files != NULL)) {
2061     for(ii=0; ii<n; ii++) {
2062       free((*cfg_files)[ii]);
2063     }
2064     free(*cfg_files); /* safe even if n==0 */
2065     *cfg_files=NULL; /* this will help prevent accidentally freeing twice */
2066   }
2067 }