d527ede508d449b4de618b045d8cad438b884711
[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_config.h>
43 #include <tr_debug.h>
44 #include <tr_filter.h>
45 #include <trust_router/tr_constraint.h>
46 #include <tr_idp.h>
47 #include <tr.h>
48
49 void tr_print_config (TR_CFG *cfg) {
50   tr_notice("tr_print_config: Logging running trust router configuration.");
51   tr_print_comms(cfg->comms);
52 }
53
54 void tr_print_comms (TR_COMM *comm_list) {
55   TR_COMM *comm = NULL;
56
57   for (comm = comm_list; NULL != comm; comm = comm->next) {
58     tr_notice("tr_print_config: Community %s:", comm->id->buf);
59
60     tr_notice("tr_print_config:  - Member IdPs:");
61     tr_print_comm_idps(comm->idp_realms);
62
63     tr_notice("tr_print_config:  - Member RPs:");
64     tr_print_comm_rps(comm->rp_realms);
65   }
66 }
67
68 void tr_print_comm_idps (TR_IDP_REALM *idp_list) {
69   TR_IDP_REALM *idp = NULL;
70
71   for (idp = idp_list; NULL != idp; idp = idp->comm_next) {
72     tr_notice("tr_print_config:    - @%s", idp->realm_id->buf);
73   }
74 }
75
76 void tr_print_comm_rps(TR_RP_REALM *rp_list) {
77   TR_RP_REALM *rp = NULL;
78
79   for (rp = rp_list; NULL != rp; rp = rp->next) {
80     tr_notice("tr_print_config:    - %s", rp->realm_name->buf);
81   }
82 }
83
84 TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx)
85 {
86   return talloc_zero(mem_ctx, TR_CFG);
87 }
88
89 void tr_cfg_free (TR_CFG *cfg) {
90   talloc_free(cfg);
91 }
92
93 TR_CFG_MGR *tr_cfg_mgr_new(TALLOC_CTX *mem_ctx)
94 {
95   return talloc_zero(mem_ctx, TR_CFG_MGR);
96 }
97
98 void tr_cfg_mgr_free (TR_CFG_MGR *cfg_mgr) {
99   talloc_free(cfg_mgr);
100 }
101
102 TR_CFG_RC tr_apply_new_config (TR_CFG_MGR *cfg_mgr)
103 {
104   /* cfg_mgr->active is allowed to be null, but new cannot be */
105   if ((cfg_mgr==NULL) || (cfg_mgr->new==NULL))
106     return TR_CFG_BAD_PARAMS;
107
108   if (cfg_mgr->active != NULL)
109     tr_cfg_free(cfg_mgr->active);
110
111   cfg_mgr->active = cfg_mgr->new;
112   cfg_mgr->new=NULL; /* only keep a single handle on the new configuration */
113
114   tr_log_threshold(cfg_mgr->active->internal->log_threshold);
115   tr_console_threshold(cfg_mgr->active->internal->console_threshold);
116
117   return TR_CFG_SUCCESS;
118 }
119
120 static TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jcfg)
121 {
122   json_t *jint = NULL;
123   json_t *jmtd = NULL;
124   json_t *jtidsp = NULL;
125   json_t *jtrpsp = NULL;
126   json_t *jhname = NULL;
127   json_t *jlog = NULL;
128   json_t *jconthres = NULL;
129   json_t *jlogthres = NULL;
130   json_t *jcfgpoll = NULL;
131   json_t *jcfgsettle = NULL;
132   json_t *jroutesweep = NULL;
133   json_t *jrouteupdate = NULL;
134   json_t *jrouteconnect = NULL;
135
136   if ((!trc) || (!jcfg))
137     return TR_CFG_BAD_PARAMS;
138
139   if (NULL == trc->internal) {
140     if (NULL == (trc->internal = talloc_zero(trc, TR_CFG_INTERNAL)))
141       return TR_CFG_NOMEM;
142   }
143
144   if (NULL != (jint = json_object_get(jcfg, "tr_internal"))) {
145     if (NULL != (jmtd = json_object_get(jint, "max_tree_depth"))) {
146       if (json_is_number(jmtd)) {
147         trc->internal->max_tree_depth = json_integer_value(jmtd);
148       } else {
149         tr_debug("tr_cfg_parse_internal: Parsing error, max_tree_depth is not a number.");
150         return TR_CFG_NOPARSE;
151       }
152     } else {
153       /* If not configured, use the default */
154       trc->internal->max_tree_depth = TR_DEFAULT_MAX_TREE_DEPTH;
155     }
156     if (NULL != (jtidsp = json_object_get(jint, "tids_port"))) {
157       if (json_is_number(jtidsp)) {
158         trc->internal->tids_port = json_integer_value(jtidsp);
159       } else {
160         tr_debug("tr_cfg_parse_internal: Parsing error, tids_port is not a number.");
161         return TR_CFG_NOPARSE;
162       }
163     } else {
164       /* If not configured, use the default */
165       trc->internal->tids_port = TR_DEFAULT_TIDS_PORT;
166     }
167     if (NULL != (jtrpsp = json_object_get(jint, "trps_port"))) {
168       if (json_is_number(jtrpsp)) {
169         trc->internal->trps_port = json_integer_value(jtrpsp);
170       } else {
171         tr_debug("tr_cfg_parse_internal: Parsing error, trps_port is not a number.");
172         return TR_CFG_NOPARSE;
173       }
174     } else {
175       /* If not configured, use the default */
176       trc->internal->trps_port = TR_DEFAULT_TRPS_PORT;
177     }
178     if (NULL != (jhname = json_object_get(jint, "hostname"))) {
179       if (json_is_string(jhname)) {
180         trc->internal->hostname = json_string_value(jhname);
181       } else {
182         tr_debug("tr_cfg_parse_internal: Parsing error, hostname is not a string.");
183         return TR_CFG_NOPARSE;
184       }
185     }
186     if (NULL != (jcfgpoll = json_object_get(jint, "cfg_poll_interval"))) {
187       if (json_is_number(jcfgpoll)) {
188         trc->internal->cfg_poll_interval = json_integer_value(jcfgpoll);
189       } else {
190         tr_debug("tr_cfg_parse_internal: Parsing error, cfg_poll_interval is not a number.");
191         return TR_CFG_NOPARSE;
192       }
193     } else {
194       trc->internal->cfg_poll_interval = TR_CFGWATCH_DEFAULT_POLL;
195     }
196
197     if (NULL != (jcfgsettle = json_object_get(jint, "cfg_settling_time"))) {
198       if (json_is_number(jcfgsettle)) {
199         trc->internal->cfg_settling_time = json_integer_value(jcfgsettle);
200       } else {
201         tr_debug("tr_cfg_parse_internal: Parsing error, cfg_settling_time is not a number.");
202         return TR_CFG_NOPARSE;
203       }
204     } else {
205       trc->internal->cfg_settling_time = TR_CFGWATCH_DEFAULT_SETTLE;
206     }
207
208     if (NULL != (jrouteconnect = json_object_get(jint, "trp_connect_interval"))) {
209       if (json_is_number(jrouteconnect)) {
210         trc->internal->trp_connect_interval = json_integer_value(jrouteconnect);
211       } else {
212         tr_debug("tr_cfg_parse_internal: Parsing error, trp_connect_interval is not a number.");
213         return TR_CFG_NOPARSE;
214       }
215     } else {
216       /* if not configured, use the default */
217       trc->internal->trp_connect_interval=TR_DEFAULT_TRP_CONNECT_INTERVAL;
218     }
219
220     if (NULL != (jroutesweep = json_object_get(jint, "trp_sweep_interval"))) {
221       if (json_is_number(jroutesweep)) {
222         trc->internal->trp_sweep_interval = json_integer_value(jroutesweep);
223       } else {
224         tr_debug("tr_cfg_parse_internal: Parsing error, trp_sweep_interval is not a number.");
225         return TR_CFG_NOPARSE;
226       }
227     } else {
228       /* if not configured, use the default */
229       trc->internal->trp_sweep_interval=TR_DEFAULT_TRP_SWEEP_INTERVAL;
230     }
231
232     if (NULL != (jrouteupdate = json_object_get(jint, "trp_update_interval"))) {
233       if (json_is_number(jrouteupdate)) {
234         trc->internal->trp_update_interval = json_integer_value(jrouteupdate);
235       } else {
236         tr_debug("tr_cfg_parse_internal: Parsing error, trp_update_interval is not a number.");
237         return TR_CFG_NOPARSE;
238       }
239     } else {
240       /* if not configured, use the default */
241       trc->internal->trp_update_interval=TR_DEFAULT_TRP_UPDATE_INTERVAL;
242     }
243
244     if (NULL != (jlog = json_object_get(jint, "logging"))) {
245       if (NULL != (jlogthres = json_object_get(jlog, "log_threshold"))) {
246         if (json_is_string(jlogthres)) {
247           trc->internal->log_threshold = str2sev(json_string_value(jlogthres));
248         } else {
249           tr_debug("tr_cfg_parse_internal: Parsing error, log_threshold is not a string.");
250           return TR_CFG_NOPARSE;
251         }
252       } else {
253         /* If not configured, use the default */
254         trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
255       }
256
257       if (NULL != (jconthres = json_object_get(jlog, "console_threshold"))) {
258         if (json_is_string(jconthres)) {
259             trc->internal->console_threshold = str2sev(json_string_value(jconthres));
260         } else {
261           tr_debug("tr_cfg_parse_internal: Parsing error, console_threshold is not a string.");
262           return TR_CFG_NOPARSE;
263         }
264       } else {
265         /* If not configured, use the default */
266         trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
267       }
268     } else {
269         /* If not configured, use the default */
270         trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
271         trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
272     }
273
274     tr_debug("tr_cfg_parse_internal: Internal config parsed.");
275     return TR_CFG_SUCCESS;
276   }
277   return TR_CFG_SUCCESS;
278 }
279
280 static TR_CONSTRAINT *tr_cfg_parse_one_constraint(TALLOC_CTX *mem_ctx, char *ctype, json_t *jc, TR_CFG_RC *rc)
281 {
282   TR_CONSTRAINT *cons=NULL;
283   int i=0;
284
285   if ((!ctype) || (!jc) || (!rc) ||
286       (!json_is_array(jc)) ||
287       (0 >= json_array_size(jc)) ||
288       (TR_MAX_CONST_MATCHES < json_array_size(jc)) ||
289       (!json_is_string(json_array_get(jc, 0)))) {
290     tr_err("tr_cfg_parse_one_constraint: config error.");
291     *rc=TR_CFG_NOPARSE;
292     return NULL;
293   }
294
295   if (NULL==(cons=tr_constraint_new(mem_ctx))) {
296     tr_debug("tr_cfg_parse_one_constraint: Out of memory (cons).");
297     *rc=TR_CFG_NOMEM;
298     return NULL;
299   }
300
301   if (NULL==(cons->type=tr_new_name(ctype))) {
302     tr_err("tr_cfg_parse_one_constraint: Out of memory (type).");
303     *rc=TR_CFG_NOMEM;
304     tr_constraint_free(cons);
305     return NULL;
306   }
307
308   for (i=0; i < json_array_size(jc); i++) {
309     cons->matches[i]=tr_new_name(json_string_value(json_array_get(jc, i)));
310     if (cons->matches[i]==NULL) {
311       tr_err("tr_cfg_parse_one_constraint: Out of memory (match %d).", i+1);
312       *rc=TR_CFG_NOMEM;
313       tr_constraint_free(cons);
314       return NULL;
315     }
316   }
317
318   return cons;
319 }
320
321 static TR_FILTER *tr_cfg_parse_one_filter(TALLOC_CTX *mem_ctx, json_t *jfilt, TR_FILTER_TYPE ftype, TR_CFG_RC *rc)
322 {
323   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
324   TR_FILTER *filt=NULL;
325   json_t *jfaction=NULL;
326   json_t *jfspecs=NULL;
327   json_t *jffield=NULL;
328   json_t *jfmatch=NULL;
329   json_t *jrc=NULL;
330   json_t *jdc=NULL;
331   int i=0, j=0;
332
333   *rc=TR_CFG_ERROR;
334
335   if ((jfilt==NULL) || (rc==NULL)) {
336     tr_err("tr_cfg_parse_one_filter: null argument");
337     *rc=TR_CFG_BADARG;
338     goto cleanup;
339   }
340     
341   if (NULL==(filt=tr_filter_new(tmp_ctx))) {
342     tr_err("tr_cfg_parse_one_filter: Out of memory.");
343     *rc=TR_CFG_NOMEM;
344     goto cleanup;
345   }
346   tr_filter_set_type(filt, ftype);
347
348   /* make sure we have space to represent the filter */
349   if (json_array_size(jfilt) > TR_MAX_FILTER_LINES) {
350     tr_err("tr_cfg_parse_one_filter: Filter has too many lines, maximum of %d.", TR_MAX_FILTER_LINES);
351     *rc=TR_CFG_NOPARSE;
352     goto cleanup
353   }
354
355   /* For each entry in the filter... */
356   for (i=0; i < json_array_size(jfilt); i++) {
357     if ((NULL==(jfaction=json_object_get(json_array_get(jfilt, i), "action"))) ||
358         (!json_is_string(jfaction))) {
359       tr_debug("tr_cfg_parse_one_filter: Error parsing filter action.");
360       *rc=TR_CFG_NOPARSE;
361       goto cleanup;
362     }
363  
364     if ((NULL==(jfspecs=json_object_get(json_array_get(jfilt, i), "filter_specs"))) ||
365         (!json_is_array(jfspecs)) ||
366         (0==json_array_size(jfspecs))) {
367       tr_debug("tr_cfg_parse_one_filter: Error parsing filter specs.");
368       *rc=TR_CFG_NOPARSE;
369       goto cleanup;
370     }
371   
372     if (TR_MAX_FILTER_SPECS < json_array_size(jfspecs)) {
373       tr_debug("tr_cfg_parse_one_filter: Filter has too many filter_specs, maximimum of %d.", TR_MAX_FILTER_SPECS);
374       *rc=TR_CFG_NOPARSE;
375       goto cleanup;
376     }
377
378     if (NULL==(filt->lines[i]=tr_fline_new(filt))) {
379       tr_debug("tr_cfg_parse_one_filter: Out of memory allocating filter line %d.", i+1);
380       *rc=TR_CFG_NOMEM;
381       goto cleanup;
382     }
383
384     if (!strcmp(json_string_value(jfaction), "accept")) {
385         filt->lines[i]->action=TR_FILTER_ACTION_ACCEPT;
386     }
387     else if (!strcmp(json_string_value(jfaction), "reject")) {
388       filt->lines[i]->action=TR_FILTER_ACTION_REJECT;
389     }
390     else {
391       tr_debug("tr_cfg_parse_one_filter: Error parsing filter action, unknown action' %s'.", json_string_value(jfaction));
392       *rc=TR_CFG_NOPARSE;
393       goto cleanup;
394     }
395
396     if (NULL!=(jrc=json_object_get(json_array_get(jfilt, i), "realm_constraints")) {
397         if (!json_is_array(jrc)) {
398           tr_err("tr_cfg_parse_one_filter: cannot parse realm_constraints, not an array.");
399           *rc=TR_CFG_NOPARSE;
400           goto cleanup;
401         } else if (json_array_size(jrc)>TR_MAX_CONST_MATCHES) {
402           tr_err("tr_cfg_parse_one_filter: realm_constraints has too many entries, maximum of %d.",
403                  TR_MAX_CONST_MATCHES);
404           *rc=TR_CFG_NOPARSE;
405           goto cleanup;
406         } else if (json_array_size(jrc)>0) {
407           /* ok we actually have entries to process */
408           if (NULL==(filt->lines[i]->realm_cons=tr_cfg_parse_one_constraint(filt->lines[i], "realm", jrc, rc))) {
409             tr_debug("tr_cfg_parse_one_filter: Error parsing realm constraint");
410             *rc=TR_CFG_NOPARSE;
411             goto cleanup;
412           }
413         }
414       }
415     }
416
417     if (NULL!=(jdc=json_object_get(json_array_get(jfilt, i), "domain_constraints"))) {
418       if (!json_is_array(jdc)) {
419           tr_err("tr_cfg_parse_one_filter: cannot parse domain_constraints, not an array.");
420           *rc=TR_CFG_NOPARSE;
421           goto cleanup;
422       } else if (json_array_size(jdc)>TR_MAX_CONST_MATCHES) {
423           tr_err("tr_cfg_parse_one_filter: domain_constraints has too many entries, maximum of %d.",
424                  TR_MAX_CONST_MATCHES);
425           *rc=TR_CFG_NOPARSE;
426           goto cleanup;
427       } else if (json_array_size(jdc)>0) {
428         if (NULL==(filt->lines[i]->domain_cons=tr_cfg_parse_one_constraint(filt->lines[i], "domain", jdc, rc))) {
429           tr_debug("tr_cfg_parse_one_filter: Error parsing domain constraint");
430           *rc=TR_CFG_NOPARSE;
431           goto cleanup;
432         }
433       }
434     }
435
436     /*For each filter spec within the filter line... */
437     for (j=0; j <json_array_size(jfspecs); j++) {
438       if ((NULL==(jffield=json_object_get(json_array_get(jfspecs, j), "field"))) ||
439           (!json_is_string(jffield)) ||
440           (NULL==(jfmatch=json_object_get(json_array_get(jfspecs, j), "match"))) ||
441           (!json_is_string(jfmatch))) {
442         tr_debug("tr_cfg_parse_one_filter: Error parsing filter field and match for filter spec %d, filter line %d.", i, j);
443         *rc=TR_CFG_NOPARSE;
444         goto cleanup;
445       }
446
447       if (NULL==(filt->lines[i]->specs[j]=tr_fspec_new(filt->lines[i]))) {
448         tr_debug("tr_cfg_parse_one_filter: Out of memory.");
449         *rc=TR_CFG_NOMEM;
450         goto cleanup;
451       }
452
453       if ((NULL==(filt->lines[i]->specs[j]->field=tr_new_name(json_string_value(jffield)))) ||
454           (NULL==(filt->lines[i]->specs[j]->match=tr_new_name(json_string_value(jfmatch))))) {
455         tr_debug("tr_cfg_parse_one_filter: Out of memory.");
456         *rc=TR_CFG_NOMEM;
457         goto cleanup;
458       }
459     }
460   }
461   *rc=TR_CFG_SUCCESS;
462   talloc_steal(mem_ctx, filt);
463
464  cleanup:
465   talloc_free(tmp_ctx);
466   return filt;
467 }
468
469 static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc)
470 {
471   TR_AAA_SERVER *aaa = NULL;
472   TR_NAME *name=NULL;
473
474   if ((!jaddr) || (!json_is_string(jaddr))) {
475     tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
476     *rc = TR_CFG_BAD_PARAMS;
477     return NULL;
478   }
479
480   name=tr_new_name(json_string_value(jaddr));
481   if (name==NULL) {
482     tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating hostname.");
483     *rc = TR_CFG_NOMEM;
484     return NULL;
485   }
486
487   aaa=tr_aaa_server_new(mem_ctx, name);
488   if (aaa==NULL) {
489     tr_free_name(name);
490     tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server.");
491     *rc = TR_CFG_NOMEM;
492     return NULL;
493   }
494
495   return aaa;
496 }
497
498 static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc)
499 {
500   TR_AAA_SERVER *aaa = NULL;
501   TR_NAME *name=NULL;
502
503   if ((!jaddr) || (!json_is_string(jaddr))) {
504     tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
505     *rc = TR_CFG_BAD_PARAMS;
506     return NULL;
507   }
508
509   name=tr_new_name(json_string_value(jaddr));
510   if (name==NULL) {
511     tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating hostname.");
512     *rc = TR_CFG_NOMEM;
513     return NULL;
514   }
515
516   aaa=tr_aaa_server_new(mem_ctx, name);
517   if (aaa==NULL) {
518     tr_free_name(name);
519     tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server.");
520     *rc = TR_CFG_NOMEM;
521     return NULL;
522   }
523
524   return aaa;
525 }
526
527 static TR_AAA_SERVER *tr_cfg_parse_aaa_servers(TALLOC_CTX *mem_ctx, json_t *jaaas, TR_CFG_RC *rc) 
528 {
529   TALLOC_CTX *tmp_ctx=NULL;
530   TR_AAA_SERVER *aaa = NULL;
531   TR_AAA_SERVER *temp_aaa = NULL;
532   int i = 0;
533
534   for (i = 0; i < json_array_size(jaaas); i++) {
535     if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(tmp_ctx, json_array_get(jaaas, i), rc))) {
536       talloc_free(tmp_ctx);
537       return NULL;
538     }
539     /* TBD -- IPv6 addresses */
540     //    tr_debug("tr_cfg_parse_aaa_servers: Configuring AAA Server: ip_addr = %s.", inet_ntoa(temp_aaa->aaa_server_addr));
541     temp_aaa->next = aaa;
542     aaa = temp_aaa;
543   }
544   tr_debug("tr_cfg_parse_aaa_servers: Finished (rc=%d)", *rc);
545
546   for (temp_aaa=aaa; temp_aaa!=NULL; temp_aaa=temp_aaa->next)
547     talloc_steal(mem_ctx, temp_aaa);
548   talloc_free(tmp_ctx);
549   return aaa;
550 }
551
552 /* not talloc-aware */
553 static TR_APC *tr_cfg_parse_apc(json_t *japc, TR_CFG_RC *rc)
554 {
555   TR_APC *apc=NULL;
556   TR_NAME *name=NULL;
557   
558   *rc = TR_CFG_SUCCESS;         /* presume success */
559
560   if ((!japc) || (!rc) || (!json_is_string(japc))) {
561     tr_debug("tr_cfg_parse_apc: Bad parameters.");
562     if (rc) 
563       *rc = TR_CFG_BAD_PARAMS;
564     return NULL;
565   }
566
567   apc=tr_apc_new(mem_ctx);
568   if (apc==NULL) {
569     tr_debug("tr_cfg_parse_apc: Out of memory.");
570     *rc = TR_CFG_NOMEM;
571     return NULL;
572   }
573
574   name=tr_new_name(json_string_value(json));
575   if (name==NULL) {
576     tr_debug("tr_cfg_parse_apc: No memory for APC name.");
577     tr_apc_free(apc);
578     *rc = TR_CFG_NOMEM;
579     return NULL;
580   }
581   tr_apc_set_id(apc, name); /* apc is now responsible for freeing the name */
582
583   return apc;
584 }
585
586 static TR_NAME *tr_cfg_parse_name(TALLOC_CTX *mem_ctx, json_t *jname, TR_CFG_RC *rc)
587 {
588   TR_NAME *name=NULL;
589   *rc=TR_CFG_ERROR;
590
591   if ((jname==NULL) || (!json_is_string(jname))) {
592     tr_err("tr_cfg_parse_name: name missing or not a string");
593     *rc=TR_CFG_BADARG;
594     name=NULL;
595   } else {
596     name=tr_new_name(json_string_value(jname));
597     if (name==NULL) {
598       tr_err("tr_cfg_parse_name: could not allocate name");
599       *rc=TR_CFG_NOMEM;
600     } else {
601       *rc=TR_CFG_SUCCESS;
602     }
603   }
604   return name;  
605 }
606
607 static int tr_cfg_parse_shared_config(json_t *jsc, TR_CFG_RC *rc)
608 {
609   const char *shared=NULL;
610   *rc=TR_CFG_SUCCESS;
611   
612   if ((jsc==NULL) ||
613       (!json_is_string(jsc)) ||
614       (NULL==(shared=json_string_value(jsc)))) {
615     *rc=TR_CFG_BADARG;
616     return -1;
617   }
618
619   if (0==strcmp(shared, "no"))
620     return 0;
621   else if (0==strcmp(shared, "yes"))
622     return 1;
623
624   /* any other value is an error */
625   tr_debug("tr_cfg_parse_shared_config: invalid shared_config value \"%s\" (should be \"yes\" or \"no\")",
626            shared);
627   *rc=TR_CFG_NOPARSE;
628   return -1;
629 }
630
631 /* Parse the identity provider object from a realm and fill in the given TR_IDP_REALM. */
632 static TR_CFG_RC tr_cfg_parse_idp(TR_IDP_REALM *idp, json_t *jidp)
633 {
634   TR_NAME *apc=NULL;
635   TR_AAA_SERVER *aaa=NULL;
636   TR_CFG_RC rc=TR_CFG_ERROR;
637
638   if (jidp==NULL)
639     goto cleanup;
640
641   idp->origin=TR_REALM_LOCAL; /* if we're parsing it from a config file, it's local */
642   idp->shared_config=tr_cfg_parse_shared_config(json_object_get(jidp, "shared_config"), &rc);
643   if (rc!=TR_CFG_SUCCESS) {
644     tr_err("tr_cfg_parse_idp: missing or malformed shared_config specification");
645     rc=TR_CFG_NOPARSE;
646     goto cleanup;
647   }
648
649   apc=tr_cfg_parse_name(json_object_get(jidp, "apc"), &rc);
650   if ((rc!=TR_CFG_SUCCESS) || (apc==NULL)) {
651     tr_err("tr_cfg_parse_idp: unable to parse APC");
652     rc=TR_CFG_NOPARSE;
653     goto cleanup;
654   }
655
656   aaa=tr_cfg_parse_aaa_servers(idp, json_object_get(jidp, "aaa_servers"), &rc);
657   if ((rc!=TR_CFG_SUCCESS) || (aaa==NULL)) {
658     tr_err("tr_cfg_parse_idp: unable to parse AAA servers");
659     rc=TR_CFG_NOPARSE;
660     goto cleanup;
661   }
662
663   /* done, fill in the idp structures */
664   idp->apcs=tr_apc_new(idp);
665   if (idp->apcs==NULL) {
666     tr_err("tr_cfg_parse_idp: unable to allocate APC.");
667     rc=TR_CFG_NOMEM;
668     goto cleanup;
669   }
670   tr_apc_set_id(idp->apcs, apc);
671   idp->aaa_servers=aaa;
672   rc=TR_CxFG_SUCCESS;
673
674  cleanup:
675   if (rc!=TR_CFG_SUCCESS) {
676     if (apc!=NULL)
677       tr_free_name(apc);
678     if (aaa!=NULL)
679       tr_aaa_server_free(aaa);
680   }
681
682   return rc;
683 }
684
685 /* parses idp realm */
686 static TR_IDP_REALM *tr_cfg_parse_one_idp_realm(TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jrealm, TR_CFG_RC *rc)
687 {
688   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
689   TR_IDP_REALM *realm=NULL;
690   json_t *jremote=NULL;
691   json_t *jscfg=NULL;
692   json_t *jsrvrs=NULL;
693   json_t *japcs=NULL;
694   TR_CFG_RC call_rc=TR_CFG_ERROR;
695
696   *rc=TR_CFG_ERROR; /* default to error if not set */
697
698   if ((!trc) || (!jrealm) || (!rc)) {
699     tr_err("tr_cfg_parse_one_idp_realm: Bad parameters.");
700     if (rc)
701       *rc=TR_CFG_BAD_PARAMS;
702     goto cleanup;
703   }
704
705   if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
706     tr_err("tr_cfg_parse_one_idp_realm: could not allocate idp realm.");
707     *rc=TR_CFG_NOMEM;
708     goto cleanup;
709   }
710
711   /* must have a name */
712   realm->realm_id=tr_cfg_parse_realm_name(realm,
713                                           json_object_get(jrealm, "realm"),
714                                          &call_rc);
715   if ((call_rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
716     tr_err("tr_cfg_parse_one_idp_realm: could not parse realm name");
717     *rc=TR_CFG_NOPARSE;
718     goto cleanup;
719   }
720         
721   call_rc=tr_cfg_parse_idp(realm, json_object_get(jrealm, "identity_provider"));
722   if (call_rc!=TR_CFG_SUCCESS) {
723     tr_err("tr_cfg_parse_one_idp_realm: could not parse identity_provider.");
724     *rc=TR_CFG_NOPARSE;
725     goto cleanup;
726   }
727
728   cleanup:
729     if (*rc==TR_CFG_SUCCESS)
730       talloc_steal(mem_ctx, realm);
731     else {
732       talloc_free(realm);
733       realm=NULL;
734     }
735
736     talloc_free(tmp_ctx);
737     return realm;
738   }
739
740   /* Determine whether the realm is an IDP realm */
741 static int tr_cfg_is_idp_realm(json_t *jrealm)
742 {
743   /* If a realm spec contains an identity_provider, it's an IDP realm. */
744   if (NULL != json_object_get(jrealm, "identity_provider"))
745     return 1;
746   else
747     return 0;
748 }
749
750 /* Parse any idp realms in the j_realms object. Ignores other realm types. */
751 static TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc)
752 {
753   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
754   TR_IDP_REALM *realms=NULL;
755   TR_IDP_REALM *new_realm=NULL;
756   json_t *this_jrealm=NULL;
757   int ii=0;
758   TR_CFG_RC rc=TR_CFG_ERROR;
759
760   *rc=TR_CFG_ERROR;
761   if ((jrealms==NULL) || (!json_is_array(jrealms))) {
762     tr_err("tr_cfg_parse_idp_realms: realms not an array");
763     *rc=TR_CFG_BADARG;
764     goto cleanup;
765   }
766
767   for (ii=0; ii<json_array_size(jrealms); ii++) {
768     this_jrealm=json_array_get(jrealms, ii);
769     if (tr_cfg_is_idp_realm(this_jrealm)) {
770       new_realm=tr_cfg_parse_one_idp_realm(tmp_ctx, this_jrealm, &rc);
771       if (rc!=TR_CFG_SUCCESS) {
772         tr_err("tr_cfg_parse_idp_realms: error decoding realm entry %d", ii+1);
773         *rc=TR_CFG_NOPARSE;
774         goto cleanup;
775       }
776       realms=tr_idp_realm_add(realms, new_realm);
777     }
778   }
779   
780   *rc=TR_CFG_SUCCESS;
781   talloc_steal(mem_ctx, realms);
782
783 cleanup:
784   talloc_free(tmp_ctx);
785   return realms;
786 }
787
788 /* takes a talloc context, but currently does not use it */
789 static TR_NAME *tr_cfg_parse_org_name(TALLOC_CTX *mem_ctx, json_t *j_org, TR_CFG_RC *rc)
790 {
791   TR_NAME *name=NULL;
792
793   if ((j_org==NULL) || (rc==NULL) || (!json_is_string(j_org))) {
794     tr_debug("tr_cfg_parse_org_name: Bad parameters.");
795     if (rc!=NULL)
796       *rc = TR_CFG_BAD_PARAMS; /* fill in return value if we can */
797     return NULL;
798   }
799
800   name=tr_new_name(json_string_value(j_org));
801   if (name==NULL)
802     *rc=TR_CFG_NOMEM;
803   else
804     *rc=TR_CFG_SUCCESS;
805   return name;
806 }
807
808 static TR_CFG_RC tr_cfg_parse_one_local_org(TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jlorg)
809 {
810   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
811   TR_CFG_RC retval=TR_CFG_ERROR; /* our return code */
812   TR_CFG_RC rc=TR_CFG_ERROR; /* return code from functions we call */
813   TR_NAME *org_name=NULL;
814   json_t *j_org=NULL;
815   json_t *j_realms=NULL;
816   TRP_IDP_REALM *new_idp_realms=NULL;
817   TR_RP_REALM *new_rp_realms=NULL;
818
819   tr_debug("tr_cfg_parse_one_local_org: parsing local organization");
820
821   /* get organization_name (optional) */
822   if (NULL==(j_org=json_object_get(jlorg, "organization_name"))) {
823     tr_debug("tr_cfg_parse_one_local_org: organization_name unspecified");
824   } else {
825     org_name=tr_cfg_parse_org_name(tmp_ctx, j_org, &rc);
826     if (rc==TR_SUCCESS) {
827       tr_debug("tr_cfg_parse_one_local_org: organization_name=%.*s", org_name->len, org_name->buf);
828       /* we don't actually do anything with this, but we could */
829       tr_free_name(org_name);
830       org_name=NULL; 
831     }
832   }
833
834   /* Now get realms. Allow this to be missing; even though that is a pointless organization entry,
835    * it's harmless. Report a warning because that might be unintentional. */
836   if (NULL==(j_realms=json_object_get(jlorg, "realms"))) {
837     tr_warning("tr_cfg_parse_one_local_org: warning - no realms in this local organization");
838   } else {
839     /* Allocate in the tmp_ctx so these will be cleaned up if we do not complete successfully. */
840     new_idp_realms=tr_cfg_parse_idp_realms(tmp_ctx, j_realms, &rc);
841     if (rc!=TR_CFG_SUCCESS)
842       goto cleanup;
843
844     new_rp_realms=tr_cfg_parse_rp_realms(tmp_ctx, j_realms, &rc);
845     if (rc!=TR_CFG_SUCCESS)
846       goto cleanup;
847   }
848   retval=TR_CFG_SUCCESS;
849   
850 cleanup:
851   /* if we succeeded, link things to the configuration and move out of tmp context */
852   if (retval==TR_CFG_SUCCESS) {
853     if (new_idp_realms!=NULL)
854       trc->idp_realms=tr_idp_realm_add(trc->idp_realms, new_idp_realms); /* fixes talloc contexts */
855     if (new_rp_realms!=NULL)
856       trc->rp_realms=tr_rp_realm_add(trc->rp_realms, new_rp_realms); /* fixes talloc contexts */
857   }
858
859   talloc_free(tmp_ctx);
860   return rc;
861 }
862
863 /* Parse local organizations if present. Returns success if there are none. */
864
865              
866
867 TR_CFG_RC tr_cfg_validate(TR_CFG *trc)
868 {
869   TR_CFG_RC rc = TR_CFG_SUCCESS;
870
871   if (!trc)
872     return TR_CFG_BAD_PARAMS;
873
874   if ((NULL == trc->internal)||
875       (NULL == trc->internal->hostname)) {
876     tr_debug("tr_cfg_validate: Error: No internal configuration, or no hostname.");
877     rc = TR_CFG_ERROR;
878   }
879
880   if (NULL == trc->rp_clients) {
881     tr_debug("tr_cfg_validate: Error: No RP Clients configured");
882     rc = TR_CFG_ERROR;
883   }
884
885   if (NULL == trc->comms) {
886     tr_debug("tr_cfg_validate: Error: No Communities configured");
887     rc = TR_CFG_ERROR;
888   }
889
890   if ((NULL == trc->default_servers) && (NULL == trc->idp_realms)) {
891     tr_debug("tr_cfg_validate: Error: No default servers or IDPs configured.");
892     rc = TR_CFG_ERROR;
893   }
894   
895   return rc;
896 }
897
898 /* Join two paths and return a pointer to the result. This should be freed
899  * via talloc_free. Returns NULL on failure. */
900 static char *join_paths(TALLOC_CTX *mem_ctx, const char *p1, const char *p2)
901 {
902   return talloc_asprintf(mem_ctx, "%s/%s", p1, p2); /* returns NULL on a failure */
903 }
904
905 /* Reads configuration files in config_dir ("" or "./" will use the current directory). */
906 TR_CFG_RC tr_parse_config (TR_CFG_MGR *cfg_mgr, const char *config_dir, int n, struct dirent **cfg_files)
907 {
908   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
909   json_t *jcfg;
910   json_t *jser;
911   json_error_t rc;
912   char *file_with_path;
913   int ii;
914   TR_CFG_RC cfg_rc=TR_CFG_ERROR;
915
916   if ((!cfg_mgr) || (!cfg_files) || (n<=0)) {
917     cfg_rc=TR_CFG_BAD_PARAMS;
918     goto cleanup;
919   }
920
921   if (cfg_mgr->new != NULL)
922     tr_cfg_free(cfg_mgr->new);
923   cfg_mgr->new=tr_cfg_new(tmp_ctx); /* belongs to the temporary context for now */
924   if (cfg_mgr->new == NULL) {
925     cfg_rc=TR_CFG_NOMEM;
926     goto cleanup;
927   }
928
929   /* Parse configuration information from each config file */
930   for (ii=0; ii<n; ii++) {
931     file_with_path=join_paths(tmp_ctx, config_dir, cfg_files[ii]->d_name); /* must free result with talloc_free */
932     if(file_with_path == NULL) {
933       tr_crit("tr_parse_config: error joining path.");
934       cfg_rc=TR_CFG_NOMEM;
935       goto cleanup;
936     }
937     tr_debug("tr_parse_config: Parsing %s.", cfg_files[ii]->d_name); /* print the filename without the path */
938     if (NULL == (jcfg = json_load_file(file_with_path, 
939                                        JSON_DISABLE_EOF_CHECK, &rc))) {
940       tr_debug("tr_parse_config: Error parsing config file %s.", 
941                cfg_files[ii]->d_name);
942       cfg_rc=TR_CFG_NOPARSE;
943       goto cleanup;
944     }
945     talloc_free(file_with_path); /* done with filename */
946
947     // Look for serial number and log it if it exists
948     if (NULL != (jser = json_object_get(jcfg, "serial_number"))) {
949       if (json_is_number(jser)) {
950         tr_notice("tr_read_config: Attempting to load revision %" JSON_INTEGER_FORMAT " of '%s'.",
951                   json_integer_value(jser),
952                   cfg_files[n]->d_name);
953       }
954     }
955
956     /* TODO: parse using the new functions */
957     if ((TR_CFG_SUCCESS != tr_cfg_parse_internal(cfg_mgr->new, jcfg)) ||
958         (TR_CFG_SUCCESS != tr_cfg_parse_rp_clients(cfg_mgr->new, jcfg)) ||
959         (TR_CFG_SUCCESS != tr_cfg_parse_idp_realms(cfg_mgr->new, jcfg)) ||
960         (TR_CFG_SUCCESS != tr_cfg_parse_default_servers(cfg_mgr->new, jcfg)) ||
961         (TR_CFG_SUCCESS != tr_cfg_parse_comms(cfg_mgr->new, jcfg))) {
962       cfg_rc=TR_CFG_ERROR;
963       goto cleanup;
964     }
965   }
966
967   /* make sure we got a complete, consistent configuration */
968   if (TR_CFG_SUCCESS != tr_cfg_validate(cfg_mgr->new)) {
969     tr_err("tr_parse_config: Error: INVALID CONFIGURATION");
970     cfg_rc=TR_CFG_ERROR;
971     goto cleanup;
972   }
973
974   /* success! */
975   talloc_steal(cfg_mgr, cfg_mgr->new); /* hand this over to the cfg_mgr context */
976   cfg_rc=TR_CFG_SUCCESS;
977
978 cleanup:
979   talloc_free(tmp_ctx);
980   return cfg_rc;
981 }
982
983 TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc)
984 {
985
986   TR_IDP_REALM *cfg_idp;
987
988   if ((!tr_cfg) || (!idp_id)) {
989     if (rc)
990       *rc = TR_CFG_BAD_PARAMS;
991     return NULL;
992   }
993
994   for (cfg_idp = tr_cfg->idp_realms; NULL != cfg_idp; cfg_idp = cfg_idp->next) {
995     if (!tr_name_cmp (idp_id, cfg_idp->realm_id)) {
996       tr_debug("tr_cfg_find_idp: Found %s.", idp_id->buf);
997       return cfg_idp;
998     }
999   }
1000   /* if we didn't find one, return NULL */ 
1001   return NULL;
1002 }
1003
1004 TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc)
1005 {
1006   TR_RP_CLIENT *cfg_rp;
1007   int i;
1008
1009   if ((!tr_cfg) || (!rp_gss)) {
1010     if (rc)
1011       *rc = TR_CFG_BAD_PARAMS;
1012     return NULL;
1013   }
1014
1015   for (cfg_rp = tr_cfg->rp_clients; NULL != cfg_rp; cfg_rp = cfg_rp->next) {
1016     for (i = 0; i < TR_MAX_GSS_NAMES; i++) {
1017       if (!tr_name_cmp (rp_gss, cfg_rp->gss_names[i])) {
1018         tr_debug("tr_cfg_find_rp: Found %s.", rp_gss->buf);
1019         return cfg_rp;
1020       }
1021     }
1022   }
1023   /* if we didn't find one, return NULL */ 
1024   return NULL;
1025 }
1026
1027 static int is_cfg_file(const struct dirent *dent) {
1028   int n;
1029
1030   /* Only accept filenames ending in ".cfg" and starting with a character
1031    * other than an ASCII '.' */
1032
1033   /* filename must be at least 4 characters long to be acceptable */
1034   n=strlen(dent->d_name);
1035   if (n < 4) {
1036     return 0;
1037   }
1038
1039   /* filename must not start with '.' */
1040   if ('.' == dent->d_name[0]) {
1041     return 0;
1042   }
1043
1044   /* If the above passed and the last four characters of the filename are .cfg, accept.
1045    * (n.b., assumes an earlier test checked that the name is >= 4 chars long.) */
1046   if (0 == strcmp(&(dent->d_name[n-4]), ".cfg")) {
1047     return 1;
1048   }
1049
1050   /* otherwise, return false. */
1051   return 0;
1052 }
1053
1054 /* Find configuration files in a particular directory. Returns the
1055  * number of entries found, 0 if none are found, or <0 for some
1056  * errors. If n>=0, the cfg_files parameter will contain a newly
1057  * allocated array of pointers to struct dirent entries, as returned
1058  * by scandir(). These can be freed with tr_free_config_file_list().
1059  */
1060 int tr_find_config_files (const char *config_dir, struct dirent ***cfg_files) {
1061   int n = 0;
1062   
1063   n = scandir(config_dir, cfg_files, is_cfg_file, alphasort);
1064
1065   if (n < 0) {
1066     perror("scandir");
1067     tr_debug("tr_find_config: scandir error trying to scan %s.", config_dir);
1068   } 
1069
1070   return n;
1071 }
1072
1073 /* Free memory allocated for configuration file list returned from tr_find_config_files().
1074  * This can be called regardless of the return value of tr_find_config_values(). */
1075 void tr_free_config_file_list(int n, struct dirent ***cfg_files) {
1076   int ii;
1077
1078   /* if n < 0, then scandir did not allocate anything because it failed */
1079   if((n>=0) && (*cfg_files != NULL)) {
1080     for(ii=0; ii<n; ii++) {
1081       free((*cfg_files)[ii]);
1082     }
1083     free(*cfg_files); /* safe even if n==0 */
1084     *cfg_files=NULL; /* this will help prevent accidentally freeing twice */
1085   }
1086 }