Real syslog support for trust router
[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_config.h>
42 #include <tr_debug.h>
43 #include <tr.h>
44 #include <tr_filter.h>
45 #include <trust_router/tr_constraint.h>
46
47 void tr_print_config (FILE *stream, TR_CFG *cfg) {
48   fprintf(stream, "tr_print_config: Not yet implemented.");
49   return;
50 }
51
52 void tr_cfg_free (TR_CFG *cfg) {
53   talloc_free(cfg);
54   return;
55 }
56
57 TR_CFG_RC tr_apply_new_config (TR_INSTANCE *tr) {
58   if (!tr)
59     return TR_CFG_BAD_PARAMS;
60
61   if (tr->active_cfg)
62     tr_cfg_free(tr->active_cfg);
63
64   tr->active_cfg = tr->new_cfg;
65
66   tr_log_threshold(tr->active_cfg->internal->log_threshold);
67   tr_console_threshold(tr->active_cfg->internal->console_threshold);
68
69   return TR_CFG_SUCCESS;
70 }
71
72 static TR_CFG_RC tr_cfg_parse_internal (TR_CFG *trc, json_t *jcfg) {
73   json_t *jint = NULL;
74   json_t *jmtd = NULL;
75   json_t *jtp = NULL;
76   json_t *jhname = NULL;
77   json_t *jlog = NULL;
78   json_t *jconthres = NULL;
79   json_t *jlogthres = NULL;
80
81   if ((!trc) || (!jcfg))
82     return TR_CFG_BAD_PARAMS;
83
84   if (NULL == trc->internal) {
85     if (NULL == (trc->internal = talloc(trc, TR_CFG_INTERNAL)))
86       return TR_CFG_NOMEM;
87
88     memset(trc->internal, 0, sizeof(TR_CFG_INTERNAL));
89   }
90
91   if (NULL != (jint = json_object_get(jcfg, "tr_internal"))) {
92     if (NULL != (jmtd = json_object_get(jint, "max_tree_depth"))) {
93       if (json_is_number(jmtd)) {
94         trc->internal->max_tree_depth = json_integer_value(jmtd);
95       } else {
96         tr_debug("tr_cfg_parse_internal: Parsing error, max_tree_depth is not a number.");
97         return TR_CFG_NOPARSE;
98       }
99     } else {
100       /* If not configured, use the default */
101       trc->internal->max_tree_depth = TR_DEFAULT_MAX_TREE_DEPTH;
102     }
103     if (NULL != (jtp = json_object_get(jint, "tids_port"))) {
104       if (json_is_number(jtp)) {
105         trc->internal->tids_port = json_integer_value(jtp);
106       } else {
107         tr_debug("tr_cfg_parse_internal: Parsing error, port is not a number.");
108         return TR_CFG_NOPARSE;
109       }
110     } else {
111       /* If not configured, use the default */
112       trc->internal->tids_port = TR_DEFAULT_TIDS_PORT;
113     }
114     if (NULL != (jhname = json_object_get(jint, "hostname"))) {
115       if (json_is_string(jhname)) {
116         trc->internal->hostname = json_string_value(jhname);
117       } else {
118         tr_debug("tr_cfg_parse_internal: Parsing error, hostname is not a string.");
119         return TR_CFG_NOPARSE;
120       }
121     }
122
123     if (NULL != (jlog = json_object_get(jcfg, "logging"))) {
124       if (NULL != (jlogthres = json_object_get(jint, "log_threshold"))) {
125         if (json_is_string(jlogthres)) {
126           trc->internal->log_threshold = str2sev(json_string_value(jlogthres));
127         } else {
128           tr_debug("tr_cfg_parse_internal: Parsing error, log_threshold is not a string.");
129           return TR_CFG_NOPARSE;
130         }
131       } else {
132         /* If not configured, use the default */
133         trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
134       }
135
136       if (NULL != (jconthres = json_object_get(jint, "console_threshold"))) {
137         if (json_is_string(jconthres)) {
138             trc->internal->console_threshold = str2sev(json_string_value(jconthres));
139         } else {
140           tr_debug("tr_cfg_parse_internal: Parsing error, console_threshold is not a string.");
141           return TR_CFG_NOPARSE;
142         }
143       } else {
144         /* If not configured, use the default */
145         trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
146       }
147     } else {
148         /* If not configured, use the default */
149         trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
150         trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
151     }
152
153     tr_debug("tr_cfg_parse_internal: Internal config parsed.");
154     return TR_CFG_SUCCESS;
155   }
156   return TR_CFG_SUCCESS;
157 }
158
159 static TR_CONSTRAINT *tr_cfg_parse_one_constraint (TR_CFG *trc, char *ctype, json_t *jc, TR_CFG_RC *rc)
160 {
161   TR_CONSTRAINT *cons;
162   int i;
163
164   if ((!trc) || (!ctype) || (!jc) || (!rc) ||
165       (!json_is_array(jc)) ||
166       (0 >= json_array_size(jc)) ||
167       (TR_MAX_CONST_MATCHES < json_array_size(jc)) ||
168       (!json_is_string(json_array_get(jc, 0)))) {
169     tr_debug("tr_cfg_parse_one_constraint: config error.");
170     *rc = TR_CFG_NOPARSE;
171     return NULL;
172   }
173
174   if (NULL == (cons = talloc(trc, TR_CONSTRAINT))) {
175     tr_debug("tr_cfg_parse_one_constraint: Out of memory (cons).");
176     *rc = TR_CFG_NOMEM;
177     return NULL;
178   }
179
180   memset(cons, 0, sizeof(TR_CONSTRAINT));
181
182   if (NULL == (cons->type = tr_new_name(ctype))) {
183     tr_debug("tr_cfg_parse_one_constraint: Out of memory (type).");
184     *rc = TR_CFG_NOMEM;
185     return NULL;
186   }
187
188   for (i = 0; i < json_array_size(jc); i++) {
189     cons->matches[i] = tr_new_name((char *)(json_string_value(json_array_get(jc, i))));
190   }
191
192   return cons;
193 }
194
195 static TR_FILTER *tr_cfg_parse_one_filter (TR_CFG *trc, json_t *jfilt, TR_CFG_RC *rc)
196 {
197   TR_FILTER *filt = NULL;
198   json_t *jftype = NULL;
199   json_t *jfls = NULL;
200   json_t *jfaction = NULL;
201   json_t *jfspecs = NULL;
202   json_t *jffield = NULL;
203   json_t *jfmatch = NULL;
204   json_t *jrc = NULL;
205   json_t *jdc = NULL;
206   int i = 0, j = 0;
207
208   if ((NULL == (jftype = json_object_get(jfilt, "type"))) ||
209       (!json_is_string(jftype))) {
210     tr_debug("tr_cfg_parse_one_filter: Error parsing filter type.");
211     *rc = TR_CFG_NOPARSE;
212     return NULL;
213   }
214
215   if ((NULL == (jfls = json_object_get(jfilt, "filter_lines"))) ||
216       (!json_is_array(jfls))) {
217     tr_debug("tr_cfg_parse_one_filter: Error parsing filter type.");
218     *rc = TR_CFG_NOPARSE;
219     return NULL;
220   }
221
222   if (TR_MAX_FILTER_LINES < json_array_size(jfls)) {
223     tr_debug("tr_cfg_parse_one_filter: Filter has too many filter_lines, maximimum of %d.", TR_MAX_FILTER_LINES);
224     *rc = TR_CFG_NOPARSE;
225     return NULL;
226   }
227
228   if (NULL == (filt = talloc(trc, TR_FILTER))) {
229     tr_debug("tr_cfg_parse_one_filter: Out of memory.");
230     *rc = TR_CFG_NOMEM;
231     return NULL;
232   }
233
234   memset(filt, 0, sizeof(TR_FILTER));
235
236   if (!strcmp(json_string_value(jftype), "rp_permitted")) {
237     filt->type = TR_FILTER_TYPE_RP_PERMITTED;
238   }
239   else {
240     tr_debug("tr_cfg_parse_one_filter: Error parsing filter type, unknown type '%s'.", json_string_value(jftype));
241     *rc = TR_CFG_NOPARSE;
242     tr_filter_free(filt);
243     return NULL;
244   }
245
246   /* For each filter line... */
247   for (i = 0; i < json_array_size(jfls); i++) {
248
249     if ((NULL == (jfaction = json_object_get(json_array_get(jfls, i), "action"))) ||
250         (!json_is_string(jfaction))) {
251       tr_debug("tr_cfg_parse_one_filter: Error parsing filter action.");
252       *rc = TR_CFG_NOPARSE;
253       tr_filter_free(filt);
254       return NULL;
255     }
256  
257     if ((NULL == (jfspecs = json_object_get(json_array_get(jfls, i), "filter_specs"))) ||
258         (!json_is_array(jfspecs)) ||
259         (0 == json_array_size(jfspecs))) {
260       tr_debug("tr_cfg_parse_one_filter: Error parsing filter specs.");
261       *rc = TR_CFG_NOPARSE;
262       tr_filter_free(filt);
263       return NULL;
264     }
265   
266     if (TR_MAX_FILTER_SPECS < json_array_size(jfspecs)) {
267       tr_debug("tr_cfg_parse_one_filter: Filter has too many filter_specs, maximimum of %d.", TR_MAX_FILTER_SPECS);
268       *rc = TR_CFG_NOPARSE;
269       tr_filter_free(filt);
270       return NULL;
271     }
272
273     if (NULL == (filt->lines[i] = talloc(trc, TR_FLINE))) {
274       tr_debug("tr_cfg_parse_one_filter: Out of memory (fline).");
275       *rc = TR_CFG_NOMEM;
276       tr_filter_free(filt);
277       return NULL;
278     }
279
280     memset(filt->lines[i], 0, sizeof(TR_FLINE));
281
282     if (!strcmp(json_string_value(jfaction), "accept")) {
283         filt->lines[i]->action = TR_FILTER_ACTION_ACCEPT;
284     }
285     else if (!strcmp(json_string_value(jfaction), "reject")) {
286       filt->lines[i]->action = TR_FILTER_ACTION_REJECT;
287     }
288     else {
289       tr_debug("tr_cfg_parse_one_filter: Error parsing filter action, unknown action' %s'.", json_string_value(jfaction));
290       *rc = TR_CFG_NOPARSE;
291       tr_filter_free(filt);
292       return NULL;
293     }
294
295     if ((NULL != (jrc = json_object_get(json_array_get(jfls, i), "realm_constraints"))) &&
296         (json_is_array(jrc)) &&
297         (0 != json_array_size(jrc)) &&
298         (TR_MAX_CONST_MATCHES >= json_array_size(jrc))) {
299
300       if (NULL == (filt->lines[i]->realm_cons = tr_cfg_parse_one_constraint(trc, "realm", jrc, rc))) {
301         tr_debug("tr_cfg_parse_one_filter: Error parsing realm constraint");
302       tr_filter_free(filt);
303       return NULL;
304       }
305     }
306
307     if ((NULL != (jdc = json_object_get(json_array_get(jfls, i), "domain_constraints"))) &&
308         (json_is_array(jdc)) &&
309         (0 != json_array_size(jdc)) &&
310         (TR_MAX_CONST_MATCHES >= json_array_size(jdc))) {
311
312       if (NULL == (filt->lines[i]->domain_cons = tr_cfg_parse_one_constraint(trc, "domain", jdc, rc))) {
313         tr_debug("tr_cfg_parse_one_filter: Error parsing domain constraint");
314       tr_filter_free(filt);
315       return NULL;
316       }
317     }
318
319     /*For each filter spec within the filter line... */
320     for (j = 0; j <json_array_size(jfspecs); j++) {
321       
322       if ((NULL == (jffield = json_object_get(json_array_get(jfspecs, j), "field"))) ||
323           (!json_is_string(jffield)) ||
324           (NULL == (jfmatch = json_object_get(json_array_get(jfspecs, j), "match"))) ||
325           (!json_is_string(jfmatch))) {
326         tr_debug("tr_cfg_parse_one_filter: Error parsing filter field and match for filter spec %d, filter line %d.", i, j);
327         *rc = TR_CFG_NOPARSE;
328         tr_filter_free(filt);
329         return NULL;
330       }
331
332       if (NULL == (filt->lines[i]->specs[j] = talloc(trc, TR_FSPEC))) {
333         tr_debug("tr_cfg_parse_one_filter: Out of memory.");
334         *rc = TR_CFG_NOMEM;
335         tr_filter_free(filt);
336         return NULL;
337       }
338
339       memset(filt->lines[i]->specs[j], 0, sizeof(TR_FSPEC));
340     
341       if ((NULL == (filt->lines[i]->specs[j]->field = tr_new_name((char *)json_string_value(jffield)))) ||
342           (NULL == (filt->lines[i]->specs[j]->match = tr_new_name((char *)json_string_value(jfmatch))))) {
343         tr_debug("tr_cfg_parse_one_filter: Out of memory.");
344         *rc = TR_CFG_NOMEM;
345         tr_filter_free(filt);
346         return NULL;
347       }
348     }
349   }
350
351   return filt;
352 }
353
354 static TR_RP_CLIENT *tr_cfg_parse_one_rp_client (TR_CFG *trc, json_t *jrp, TR_CFG_RC *rc)
355 {
356   TR_RP_CLIENT *rp = NULL;
357   json_t *jgns = NULL;
358   json_t *jfilt = NULL;
359   json_t *jftype = NULL;
360   int i = 0;
361
362   if ((!trc) || (!jrp) || (!rc)) {
363     tr_debug("tr_cfg_parse_one_rp_realm: Bad parameters.");
364     if (rc)
365       *rc = TR_CFG_BAD_PARAMS;
366     return NULL;
367   }
368
369   if ((NULL == (jgns = json_object_get(jrp, "gss_names"))) ||
370       (!json_is_array(jgns))) {
371     tr_debug("tr_cfg_parse_one_rp_client: Error parsing RP client configuration, no GSS names.");
372     *rc = TR_CFG_NOPARSE;
373     return NULL;
374   }
375
376   /* TBD -- Support more than one filter per RP client? */
377   if (NULL == (jfilt = json_object_get(jrp, "filter"))) {
378     tr_debug("tr_cfg_parse_one_rp_client: Error parsing RP client configuration, no filter.");
379     *rc = TR_CFG_NOPARSE;
380     return NULL;
381   }
382
383   /* We only support rp_permitted filters for RP clients */
384   if ((NULL == (jftype = json_object_get(jfilt, "type"))) ||
385       (!json_is_string(jftype)) ||
386       (strcmp(json_string_value(jftype), "rp_permitted"))) {
387     tr_debug("tr_cfg_parse_one_rp_client: Error parsing RP client filter type.");
388     *rc = TR_CFG_NOPARSE;
389     return NULL;
390   }
391
392   if (TR_MAX_GSS_NAMES < json_array_size(jgns)) {
393     tr_debug("tr_cfg_parse_one_rp_client: RP Client has too many GSS Names.");
394     *rc = TR_CFG_NOPARSE;
395     return NULL;
396   }
397
398   if (NULL == (rp = talloc(trc, TR_RP_CLIENT))) {
399     tr_debug("tr_cfg_parse_one_rp_realm: Out of memory.");
400     *rc = TR_CFG_NOMEM;
401     return NULL;
402   }
403   
404   memset(rp, 0, sizeof(TR_RP_CLIENT));
405
406   /* TBD -- support more than one filter entry per RP Client? */
407   if (NULL == (rp->filter = tr_cfg_parse_one_filter(trc, jfilt, rc))) {
408     tr_debug("tr_cfg_parse_one_rp_client: Error parsing filter.");
409     *rc = TR_CFG_NOPARSE;
410     return NULL;
411   }
412     
413   for (i = 0; i < json_array_size(jgns); i++) {
414     if (NULL == (rp->gss_names[i] = tr_new_name ((char *)json_string_value(json_array_get(jgns, i))))) {
415       tr_debug("tr_cfg_parse_one_rp_client: No memory for GSS Name.");
416       *rc = TR_CFG_NOMEM;
417       return NULL;
418     }
419   }
420   
421   return rp;
422 }
423
424 static TR_CFG_RC tr_cfg_parse_rp_clients (TR_CFG *trc, json_t *jcfg) {
425   json_t *jrps = NULL;
426   TR_RP_CLIENT *rp = NULL;
427   TR_CFG_RC rc = TR_CFG_SUCCESS;
428   int i = 0;
429
430   if ((!trc) || (!jcfg))
431     return TR_CFG_BAD_PARAMS;
432
433   if (NULL != (jrps = json_object_get(jcfg, "rp_clients"))) {
434
435     if (!json_is_array(jrps)) {
436       return TR_CFG_NOPARSE;
437     }
438
439     for (i = 0; i < json_array_size(jrps); i++) {
440       if (NULL == (rp = tr_cfg_parse_one_rp_client(trc, 
441                                                    json_array_get(jrps, i), 
442                                                    &rc))) {
443         return rc;
444       }
445       tr_debug("tr_cfg_parse_rp_clients: RP client configured -- first gss: %s", rp->gss_names[0]->buf);
446       rp->next = trc->rp_clients;
447       trc->rp_clients = rp;
448     }
449   }
450   return rc;
451 }
452
453 static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server (TR_CFG *trc, json_t *jaddr, TR_CFG_RC *rc) {
454   TR_AAA_SERVER *aaa = NULL;
455
456   if ((!trc) || (!jaddr) || (!json_is_string(jaddr))) {
457     tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
458     *rc = TR_CFG_BAD_PARAMS;
459     return NULL;
460   }
461
462   if (NULL == (aaa = talloc(trc, TR_AAA_SERVER))) {
463     tr_debug("tr_cfg_parse_one_aaa_server: Out of memory.");
464     *rc = TR_CFG_NOMEM;
465     return NULL;
466   }
467
468   memset(aaa, 0, sizeof(TR_AAA_SERVER));
469
470   aaa->hostname = tr_new_name((char *)(json_string_value(jaddr)));
471
472   return aaa;
473 }
474
475 static TR_AAA_SERVER *tr_cfg_parse_aaa_servers (TR_CFG *trc, json_t *jaaas, TR_CFG_RC *rc) 
476 {
477   TR_AAA_SERVER *aaa = NULL;
478   TR_AAA_SERVER *temp_aaa = NULL;
479   int i = 0;
480
481   for (i = 0; i < json_array_size(jaaas); i++) {
482     if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(trc, json_array_get(jaaas, i), rc))) {
483       return NULL;
484     }
485     /* TBD -- IPv6 addresses */
486     //    tr_debug("tr_cfg_parse_aaa_servers: Configuring AAA Server: ip_addr = %s.", inet_ntoa(temp_aaa->aaa_server_addr));
487     temp_aaa->next = aaa;
488     aaa = temp_aaa;
489   }
490   return aaa;
491 }
492
493 static TR_APC *tr_cfg_parse_apcs (TR_CFG *trc, json_t *japcs, TR_CFG_RC *rc)
494 {
495   TR_APC *apc;
496
497   *rc = TR_CFG_SUCCESS;         /* presume success */
498
499   if ((!trc) || (!japcs) || (!rc)) {
500     tr_debug("tr_cfg_parse_apcs: Bad parameters.");
501     if (rc) 
502       *rc = TR_CFG_BAD_PARAMS;
503     return NULL;
504   }
505
506   if (NULL == (apc = talloc(trc, TR_APC))) {
507     tr_debug("tr_cfg_parse_apcs: Out of memory.");
508     *rc = TR_CFG_NOMEM;
509     return NULL;
510   }
511
512   memset(apc, 0, sizeof(TR_APC));
513
514   /* TBD, deal with more than one APC.  In the meantime, though...                */
515   /* Only parse the first APC, because we only know how to deal with one, anyway. */
516   if (0 == json_array_size(japcs))
517     return NULL;
518
519   if (NULL == (apc->id = tr_new_name((char *)json_string_value(json_array_get(japcs, 0))))) {
520     tr_debug("tr_cfg_parse_apcs: No memory for APC name.");
521     *rc = TR_CFG_NOMEM;
522     return NULL;
523   }
524
525   return apc;
526 }
527
528 static TR_IDP_REALM *tr_cfg_parse_one_idp_realm (TR_CFG *trc, json_t *jidp, TR_CFG_RC *rc) {
529   TR_IDP_REALM *idp = NULL;
530   json_t *jrid = NULL;
531   json_t *jscfg = NULL;
532   json_t *jsrvrs = NULL;
533   json_t *japcs = NULL;
534
535   if ((!trc) || (!jidp) || (!rc)) {
536     tr_debug("tr_cfg_parse_one_idp_realm: Bad parameters.");
537     if (rc)
538       *rc = TR_CFG_BAD_PARAMS;
539     return NULL;
540   }
541
542   if (NULL == (idp = talloc(trc, TR_IDP_REALM))) {
543     tr_debug("tr_cfg_parse_one_idp_realm: Out of memory.");
544     *rc = TR_CFG_NOMEM;
545     return NULL;
546   }
547
548   memset(idp, 0, sizeof(TR_IDP_REALM));
549
550   if ((NULL == (jrid = json_object_get(jidp, "realm_id"))) ||
551       (!json_is_string(jrid)) ||
552       (NULL == (jscfg = json_object_get(jidp, "shared_config"))) ||
553       (!json_is_string(jscfg)) ||
554       (NULL == (jsrvrs = json_object_get(jidp, "aaa_servers"))) ||
555       (!json_is_array(jsrvrs))) {
556     tr_debug("tr_cfg_parse_one_idp_realm: Error parsing IDP realm configuration.");
557     *rc = TR_CFG_NOPARSE;
558     return NULL;
559   }
560
561   if (0 == strcmp(json_string_value(jscfg), "no")) {
562     idp->shared_config = 0;
563   } else {
564     idp->shared_config = 1;
565   }
566
567   if (NULL == (idp->realm_id = tr_new_name((char *)json_string_value(jrid)))) {
568     tr_debug("tr_cfg_parse_one_idp_realm: No memory for realm id.");
569     *rc = TR_CFG_NOMEM;
570     return NULL;
571   }
572
573   if (NULL == (idp->aaa_servers = tr_cfg_parse_aaa_servers(trc, jsrvrs, rc))) {
574     tr_debug("tr_cfg_parse_one_idp_realm: Can't parse AAA servers for realm %s.", idp->realm_id->buf);
575     tr_free_name(idp->realm_id);
576     return NULL;
577   }
578
579   if ((NULL != (japcs = json_object_get(jidp, "apcs"))) &&
580       (json_is_array(japcs))) {
581     if (NULL == (idp->apcs = tr_cfg_parse_apcs(trc, japcs, rc))) {
582       tr_debug("tr_cfg_parse_one_idp_realm: Can't parse APCs for realm %s .", idp->realm_id->buf);
583       tr_free_name(idp->realm_id);
584       /* TBD -- free aaa_servers */;
585       return NULL;
586     }
587   } 
588   return idp;
589 }
590
591 static TR_CFG_RC tr_cfg_parse_default_servers (TR_CFG *trc, json_t *jcfg) 
592 {
593   json_t *jdss = NULL;
594   TR_CFG_RC rc = TR_CFG_SUCCESS;
595   TR_AAA_SERVER *ds = NULL;
596   int i = 0;
597
598   if ((!trc) || (!jcfg))
599     return TR_CFG_BAD_PARAMS;
600
601   /* If there are default servers, store them */
602   if ((NULL != (jdss = json_object_get(jcfg, "default_servers"))) &&
603       (json_is_array(jdss)) &&
604       (0 < json_array_size(jdss))) {
605
606     for (i = 0; i < json_array_size(jdss); i++) {
607       if (NULL == (ds = tr_cfg_parse_one_aaa_server(trc, 
608                                                   json_array_get(jdss, i), 
609                                                   &rc))) {
610         return rc;
611       }
612       tr_debug("tr_cfg_parse_default_servers: Default server configured: %s", ds->hostname->buf);
613       ds->next = trc->default_servers;
614       trc->default_servers = ds;
615     }
616   } 
617
618   return rc;
619 }
620
621 static TR_CFG_RC tr_cfg_parse_idp_realms (TR_CFG *trc, json_t *jcfg) 
622 {
623   json_t *jidps = NULL;
624   TR_CFG_RC rc = TR_CFG_SUCCESS;
625   TR_IDP_REALM *idp = NULL;
626   int i = 0;
627
628   if ((!trc) || (!jcfg))
629     return TR_CFG_BAD_PARAMS;
630
631   /* If there are any IDP Realms, parse them */
632   if ((NULL != (jidps = json_object_get(jcfg, "idp_realms"))) &&
633       (json_is_array(jidps))) {
634     for (i = 0; i < json_array_size(jidps); i++) {
635       if (NULL == (idp = tr_cfg_parse_one_idp_realm(trc,
636                                                     json_array_get(jidps, i), 
637                                                     &rc))) {
638         return rc;
639       }
640       tr_debug("tr_cfg_parse_idp_realms: IDP realm configured: %s.", idp->realm_id->buf);
641       idp->next = trc->idp_realms;
642       trc->idp_realms = idp;
643     }
644   }
645
646   return rc;
647 }
648
649 static TR_IDP_REALM *tr_cfg_parse_comm_idps (TR_CFG *trc, json_t *jidps, TR_CFG_RC *rc)
650 {
651   TR_IDP_REALM *idp = NULL;
652   TR_IDP_REALM *temp_idp = NULL;
653   int i = 0;
654
655   if ((!trc) ||
656       (!jidps) ||
657       (!json_is_array(jidps))) {
658     if (rc)
659       *rc = TR_CFG_BAD_PARAMS;
660     return NULL;
661   }
662
663   for (i = 0; i < json_array_size(jidps); i++) {
664     if (NULL == (temp_idp = (tr_cfg_find_idp(trc, 
665                                              tr_new_name((char *)json_string_value(json_array_get(jidps, i))), 
666                                              rc)))) {
667       tr_debug("tr_cfg_parse_comm_idps: Unknown IDP %s.", 
668               (char *)json_string_value(json_array_get(jidps, i)));
669       return NULL;
670     }
671
672     temp_idp->comm_next = idp;
673     idp = temp_idp;
674   }
675
676   return idp;
677 }
678
679 static TR_RP_REALM *tr_cfg_parse_comm_rps (TR_CFG *trc, json_t *jrps, TR_CFG_RC *rc)
680 {
681   TR_RP_REALM *rp = NULL;
682   TR_RP_REALM *temp_rp = NULL;
683   int i = 0;
684
685   if ((!trc) ||
686       (!jrps) ||
687       (!json_is_array(jrps))) {
688     if (rc)
689       *rc = TR_CFG_BAD_PARAMS;
690     return NULL;
691   }
692
693   for (i = (json_array_size(jrps)-1); i >= 0; i--) {
694     if (NULL == (temp_rp = talloc(trc, TR_RP_REALM))) {
695       tr_debug("tr_cfg_parse_comm_rps: Can't allocate memory for RP Realm.");
696       if (rc)
697         *rc = TR_CFG_NOMEM;
698       return NULL;
699     }
700     memset (temp_rp, 0, sizeof(TR_RP_REALM));
701
702     if (NULL == (temp_rp->realm_name = tr_new_name((char *)json_string_value(json_array_get(jrps, i))))) {
703       tr_debug("tr_cfg_parse_comm_rps: No memory for RP Realm Name.");
704       if (rc)
705         *rc = TR_CFG_NOMEM;
706       return NULL;
707     }
708
709     temp_rp->next = rp;
710     rp = temp_rp;
711   }
712
713   return rp;
714 }
715
716 static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc) {
717   TR_COMM *comm = NULL;
718   json_t *jid = NULL;
719   json_t *jtype = NULL;
720   json_t *japcs = NULL;
721   json_t *jidps = NULL;
722   json_t *jrps = NULL;
723
724   if ((!trc) || (!jcomm) || (!rc)) {
725     tr_debug("tr_cfg_parse_one_comm: Bad parameters.");
726     if (rc)
727       *rc = TR_CFG_BAD_PARAMS;
728     return NULL;
729   }
730
731   if (NULL == (comm = talloc_zero(trc, TR_COMM))) {
732     tr_crit("tr_cfg_parse_one_comm: Out of memory.");
733     *rc = TR_CFG_NOMEM;
734     return NULL;
735   }
736
737
738   if ((NULL == (jid = json_object_get(jcomm, "community_id"))) ||
739       (!json_is_string(jid)) ||
740       (NULL == (jtype = json_object_get(jcomm, "type"))) ||
741       (!json_is_string(jtype)) ||
742       (NULL == (japcs = json_object_get(jcomm, "apcs"))) ||
743       (!json_is_array(japcs)) ||
744       (NULL == (jidps = json_object_get(jcomm, "idp_realms"))) ||
745       (!json_is_array(jidps)) ||
746       (NULL == (jrps = json_object_get(jcomm, "rp_realms"))) ||
747       (!json_is_array(jrps))) {
748     tr_debug("tr_cfg_parse_one_comm: Error parsing Communities configuration.");
749     *rc = TR_CFG_NOPARSE;
750     return NULL;
751   }
752
753   if (NULL == (comm->id = tr_new_name((char *)json_string_value(jid)))) {
754     tr_debug("tr_cfg_parse_one_comm: No memory for community id.");
755     *rc = TR_CFG_NOMEM;
756     return NULL;
757   }
758
759   if (0 == strcmp(json_string_value(jtype), "apc")) {
760     comm->type = TR_COMM_APC;
761   } else if (0 == strcmp(json_string_value(jtype), "coi")) {
762     comm->type = TR_COMM_COI;
763     if (NULL == (comm->apcs = tr_cfg_parse_apcs(trc, japcs, rc))) {
764       tr_debug("tr_cfg_parse_one_comm: Can't parse APCs for COI %s.", comm->id->buf);
765       tr_free_name(comm->id);
766       return NULL;
767     }
768   } else {
769     tr_debug("tr_cfg_parse_one_comm: Invalid community type, comm = %s, type = %s", comm->id->buf, json_string_value(jtype));
770     tr_free_name(comm->id);
771     *rc = TR_CFG_NOPARSE;
772     return NULL;
773   }
774
775   comm->idp_realms = tr_cfg_parse_comm_idps(trc, jidps, rc);
776   if (TR_CFG_SUCCESS != *rc) {
777     tr_debug("tr_cfg_parse_one_comm: Can't parse IDP realms for comm %s.", comm->id->buf);
778     tr_free_name(comm->id);
779     return NULL;
780   }
781
782   comm->rp_realms = tr_cfg_parse_comm_rps(trc, jrps, rc);
783   if (TR_CFG_SUCCESS != *rc) {
784     tr_debug("tr_cfg_parse_comm: Can't parse RP realms for comm %s .", comm->id->buf);
785     tr_free_name(comm->id);
786     return NULL;
787   }
788
789   if (TR_COMM_APC == comm->type) {
790     json_t *jexpire  = json_object_get(jcomm, "expiration_interval");
791     comm->expiration_interval = 43200; /*30 days*/
792     if (jexpire) {
793         if (!json_is_integer(jexpire)) {
794           fprintf(stderr, "tr_parse_comm: expirae_interval is not an integer\n");
795           return NULL;
796         }
797         comm->expiration_interval = json_integer_value(jexpire);
798         if (comm->expiration_interval <= 10)
799           comm->expiration_interval = 11; /* Freeradius waits 10 minutes between successful TR queries*/
800         if (comm->expiration_interval > 129600) /* 90 days*/
801         comm->expiration_interval = 129600;
802     }
803   }
804   
805   return comm;
806 }
807
808 static TR_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg) 
809 {
810   json_t *jcomms = NULL;
811   TR_CFG_RC rc = TR_CFG_SUCCESS;
812   TR_COMM *comm = NULL;
813   int i = 0;
814
815   if ((!trc) || (!jcfg)) {
816     tr_debug("tr_cfg_parse_comms: Bad Parameters.");
817     return TR_CFG_BAD_PARAMS;
818   }
819
820   if (NULL != (jcomms = json_object_get(jcfg, "communities"))) {
821     if (!json_is_array(jcomms)) {
822       return TR_CFG_NOPARSE;
823     }
824
825     for (i = 0; i < json_array_size(jcomms); i++) {
826       if (NULL == (comm = tr_cfg_parse_one_comm(trc, 
827                                                 json_array_get(jcomms, i), 
828                                                 &rc))) {
829         return rc;
830       }
831       tr_debug("tr_cfg_parse_comms: Community configured: %s.", comm->id->buf);
832       comm->next = trc->comms;
833       trc->comms = comm;
834     }
835   }
836   return rc;
837 }
838
839 TR_CFG_RC tr_cfg_validate (TR_CFG *trc) {
840   TR_CFG_RC rc = TR_CFG_SUCCESS;
841
842   if (!trc)
843     return TR_CFG_BAD_PARAMS;
844
845   if ((NULL == trc->internal)||
846       (NULL == trc->internal->hostname)) {
847     tr_debug("tr_cfg_validate: Error: No internal configuration, or no hostname.");
848     rc = TR_CFG_ERROR;
849   }
850
851   if (NULL == trc->rp_clients) {
852     tr_debug("tr_cfg_validate: Error: No RP Clients configured");
853     rc = TR_CFG_ERROR;
854   }
855
856   if (NULL == trc->comms) {
857     tr_debug("tr_cfg_validate: Error: No Communities configured");
858     rc = TR_CFG_ERROR;
859   }
860
861   if ((NULL == trc->default_servers) && (NULL == trc->idp_realms)) {
862     tr_debug("tr_cfg_validate: Error: No default servers or IDPs configured.");
863     rc = TR_CFG_ERROR;
864   }
865   
866   return rc;
867 }
868
869 TR_CFG_RC tr_parse_config (TR_INSTANCE *tr, int n, struct dirent **cfg_files) {
870   json_t *jcfg;
871   json_error_t rc;
872
873   if ((!tr) || (!cfg_files))
874     return TR_CFG_BAD_PARAMS;
875
876   /* If there is a partial/abandoned config lying around, free it */
877   if (tr->new_cfg) 
878     tr_cfg_free(tr->new_cfg);
879   
880   if (NULL == (tr->new_cfg = talloc(NULL, TR_CFG)))
881     return TR_CFG_NOMEM;
882
883   memset(tr->new_cfg, 0, sizeof(TR_CFG));
884
885   /* Parse configuration information from each config file */
886   while (n--) {
887     tr_debug("tr_read_config: Parsing %s.", cfg_files[n]->d_name);
888     if (NULL == (jcfg = json_load_file(cfg_files[n]->d_name, 
889                                        JSON_DISABLE_EOF_CHECK, &rc))) {
890       tr_debug("tr_read_config: Error parsing config file %s.", 
891                cfg_files[n]->d_name);
892       return TR_CFG_NOPARSE;
893     }
894         
895     if ((TR_CFG_SUCCESS != tr_cfg_parse_internal(tr->new_cfg, jcfg)) ||
896         (TR_CFG_SUCCESS != tr_cfg_parse_rp_clients(tr->new_cfg, jcfg)) ||
897         (TR_CFG_SUCCESS != tr_cfg_parse_idp_realms(tr->new_cfg, jcfg)) ||
898         (TR_CFG_SUCCESS != tr_cfg_parse_default_servers(tr->new_cfg, jcfg)) ||
899         (TR_CFG_SUCCESS != tr_cfg_parse_comms(tr->new_cfg, jcfg))) {
900       tr_cfg_free(tr->new_cfg);
901       return TR_CFG_ERROR;
902     }
903   }
904
905   /* make sure we got a complete, consistent configuration */
906   if (TR_CFG_SUCCESS != tr_cfg_validate(tr->new_cfg)) {
907     tr_debug("tr_parse_config: Error: INVALID CONFIGURATION, EXITING");
908     return TR_CFG_ERROR;
909   }
910
911   return TR_CFG_SUCCESS;
912 }
913
914 TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc)
915 {
916
917   TR_IDP_REALM *cfg_idp;
918
919   if ((!tr_cfg) || (!idp_id)) {
920     if (rc)
921       *rc = TR_CFG_BAD_PARAMS;
922     return NULL;
923   }
924
925   for (cfg_idp = tr_cfg->idp_realms; NULL != cfg_idp; cfg_idp = cfg_idp->next) {
926     if (!tr_name_cmp (idp_id, cfg_idp->realm_id)) {
927       tr_debug("tr_cfg_find_idp: Found %s.", idp_id->buf);
928       return cfg_idp;
929     }
930   }
931   /* if we didn't find one, return NULL */ 
932   return NULL;
933 }
934
935 TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc)
936 {
937   TR_RP_CLIENT *cfg_rp;
938   int i;
939
940   if ((!tr_cfg) || (!rp_gss)) {
941     if (rc)
942       *rc = TR_CFG_BAD_PARAMS;
943     return NULL;
944   }
945
946   for (cfg_rp = tr_cfg->rp_clients; NULL != cfg_rp; cfg_rp = cfg_rp->next) {
947     for (i = 0; i < TR_MAX_GSS_NAMES; i++) {
948       if (!tr_name_cmp (rp_gss, cfg_rp->gss_names[i])) {
949         tr_debug("tr_cfg_find_rp: Found %s.", rp_gss->buf);
950         return cfg_rp;
951       }
952     }
953   }
954   /* if we didn't find one, return NULL */ 
955   return NULL;
956 }
957
958 static int is_cfg_file(const struct dirent *dent) {
959   int n;
960
961   /* if the last four letters of the filename are .cfg, return true. */
962   if ((4 <= (n = strlen(dent->d_name))) &&
963       (0 == strcmp(&(dent->d_name[n-4]), ".cfg"))) {
964     return 1;
965   }
966
967   /* otherwise, return false. */
968   return 0;
969 }
970
971 int tr_find_config_files (struct dirent ***cfg_files) {
972   int n = 0, i = 0;
973   
974   n = scandir(".", cfg_files, &is_cfg_file, 0);
975
976   if (n < 0) {
977     perror("scandir");
978     tr_debug("tr_find_config: scandir error.");
979     return 0;
980   }
981
982   if (n == 0) {
983     tr_debug("tr_find_config: No config files found.");
984     return 0;
985   }
986
987   i = n;
988   while(i--) {
989     tr_debug("tr_find_config: Config file found (%s).", (*cfg_files)[i]->d_name);
990   }
991     
992   return n;
993 }