2 * Copyright (c) 2012, JANET(UK)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
41 #include <tr_cfgwatch.h>
43 #include <tr_config.h>
45 #include <tr_filter.h>
46 #include <trust_router/tr_constraint.h>
50 void tr_print_config (TR_CFG *cfg) {
51 tr_notice("tr_print_config: Logging running trust router configuration.");
52 tr_print_comms(cfg->comms);
55 void tr_print_comms (TR_COMM *comm_list) {
58 for (comm = comm_list; NULL != comm; comm = comm->next) {
59 tr_notice("tr_print_config: Community %s:", comm->id->buf);
61 tr_notice("tr_print_config: - Member IdPs:");
62 tr_print_comm_idps(comm->idp_realms);
64 tr_notice("tr_print_config: - Member RPs:");
65 tr_print_comm_rps(comm->rp_realms);
69 void tr_print_comm_idps (TR_IDP_REALM *idp_list) {
70 TR_IDP_REALM *idp = NULL;
72 for (idp = idp_list; NULL != idp; idp = idp->comm_next) {
73 tr_notice("tr_print_config: - @%s", idp->realm_id->buf);
77 void tr_print_comm_rps(TR_RP_REALM *rp_list) {
78 TR_RP_REALM *rp = NULL;
80 for (rp = rp_list; NULL != rp; rp = rp->next) {
81 tr_notice("tr_print_config: - %s", rp->realm_name->buf);
85 TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx)
87 return talloc_zero(mem_ctx, TR_CFG);
90 void tr_cfg_free (TR_CFG *cfg) {
94 TR_CFG_MGR *tr_cfg_mgr_new(TALLOC_CTX *mem_ctx)
96 return talloc_zero(mem_ctx, TR_CFG_MGR);
99 void tr_cfg_mgr_free (TR_CFG_MGR *cfg_mgr) {
100 talloc_free(cfg_mgr);
103 TR_CFG_RC tr_apply_new_config (TR_CFG_MGR *cfg_mgr)
105 /* cfg_mgr->active is allowed to be null, but new cannot be */
106 if ((cfg_mgr==NULL) || (cfg_mgr->new==NULL))
107 return TR_CFG_BAD_PARAMS;
109 if (cfg_mgr->active != NULL)
110 tr_cfg_free(cfg_mgr->active);
112 cfg_mgr->active = cfg_mgr->new;
113 cfg_mgr->new=NULL; /* only keep a single handle on the new configuration */
115 tr_log_threshold(cfg_mgr->active->internal->log_threshold);
116 tr_console_threshold(cfg_mgr->active->internal->console_threshold);
118 return TR_CFG_SUCCESS;
121 static TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jcfg)
125 json_t *jtidsp = NULL;
126 json_t *jtrpsp = NULL;
127 json_t *jhname = NULL;
129 json_t *jconthres = NULL;
130 json_t *jlogthres = NULL;
131 json_t *jcfgpoll = NULL;
132 json_t *jcfgsettle = NULL;
133 json_t *jroutesweep = NULL;
134 json_t *jrouteupdate = NULL;
135 json_t *jrouteconnect = NULL;
137 if ((!trc) || (!jcfg))
138 return TR_CFG_BAD_PARAMS;
140 if (NULL == trc->internal) {
141 if (NULL == (trc->internal = talloc_zero(trc, TR_CFG_INTERNAL)))
145 if (NULL != (jint = json_object_get(jcfg, "tr_internal"))) {
146 if (NULL != (jmtd = json_object_get(jint, "max_tree_depth"))) {
147 if (json_is_number(jmtd)) {
148 trc->internal->max_tree_depth = json_integer_value(jmtd);
150 tr_debug("tr_cfg_parse_internal: Parsing error, max_tree_depth is not a number.");
151 return TR_CFG_NOPARSE;
154 /* If not configured, use the default */
155 trc->internal->max_tree_depth = TR_DEFAULT_MAX_TREE_DEPTH;
157 if (NULL != (jtidsp = json_object_get(jint, "tids_port"))) {
158 if (json_is_number(jtidsp)) {
159 trc->internal->tids_port = json_integer_value(jtidsp);
161 tr_debug("tr_cfg_parse_internal: Parsing error, tids_port is not a number.");
162 return TR_CFG_NOPARSE;
165 /* If not configured, use the default */
166 trc->internal->tids_port = TR_DEFAULT_TIDS_PORT;
168 if (NULL != (jtrpsp = json_object_get(jint, "trps_port"))) {
169 if (json_is_number(jtrpsp)) {
170 trc->internal->trps_port = json_integer_value(jtrpsp);
172 tr_debug("tr_cfg_parse_internal: Parsing error, trps_port is not a number.");
173 return TR_CFG_NOPARSE;
176 /* If not configured, use the default */
177 trc->internal->trps_port = TR_DEFAULT_TRPS_PORT;
179 if (NULL != (jhname = json_object_get(jint, "hostname"))) {
180 if (json_is_string(jhname)) {
181 trc->internal->hostname = json_string_value(jhname);
183 tr_debug("tr_cfg_parse_internal: Parsing error, hostname is not a string.");
184 return TR_CFG_NOPARSE;
187 if (NULL != (jcfgpoll = json_object_get(jint, "cfg_poll_interval"))) {
188 if (json_is_number(jcfgpoll)) {
189 trc->internal->cfg_poll_interval = json_integer_value(jcfgpoll);
191 tr_debug("tr_cfg_parse_internal: Parsing error, cfg_poll_interval is not a number.");
192 return TR_CFG_NOPARSE;
195 trc->internal->cfg_poll_interval = TR_CFGWATCH_DEFAULT_POLL;
198 if (NULL != (jcfgsettle = json_object_get(jint, "cfg_settling_time"))) {
199 if (json_is_number(jcfgsettle)) {
200 trc->internal->cfg_settling_time = json_integer_value(jcfgsettle);
202 tr_debug("tr_cfg_parse_internal: Parsing error, cfg_settling_time is not a number.");
203 return TR_CFG_NOPARSE;
206 trc->internal->cfg_settling_time = TR_CFGWATCH_DEFAULT_SETTLE;
209 if (NULL != (jrouteconnect = json_object_get(jint, "trp_connect_interval"))) {
210 if (json_is_number(jrouteconnect)) {
211 trc->internal->trp_connect_interval = json_integer_value(jrouteconnect);
213 tr_debug("tr_cfg_parse_internal: Parsing error, trp_connect_interval is not a number.");
214 return TR_CFG_NOPARSE;
217 /* if not configured, use the default */
218 trc->internal->trp_connect_interval=TR_DEFAULT_TRP_CONNECT_INTERVAL;
221 if (NULL != (jroutesweep = json_object_get(jint, "trp_sweep_interval"))) {
222 if (json_is_number(jroutesweep)) {
223 trc->internal->trp_sweep_interval = json_integer_value(jroutesweep);
225 tr_debug("tr_cfg_parse_internal: Parsing error, trp_sweep_interval is not a number.");
226 return TR_CFG_NOPARSE;
229 /* if not configured, use the default */
230 trc->internal->trp_sweep_interval=TR_DEFAULT_TRP_SWEEP_INTERVAL;
233 if (NULL != (jrouteupdate = json_object_get(jint, "trp_update_interval"))) {
234 if (json_is_number(jrouteupdate)) {
235 trc->internal->trp_update_interval = json_integer_value(jrouteupdate);
237 tr_debug("tr_cfg_parse_internal: Parsing error, trp_update_interval is not a number.");
238 return TR_CFG_NOPARSE;
241 /* if not configured, use the default */
242 trc->internal->trp_update_interval=TR_DEFAULT_TRP_UPDATE_INTERVAL;
245 if (NULL != (jlog = json_object_get(jint, "logging"))) {
246 if (NULL != (jlogthres = json_object_get(jlog, "log_threshold"))) {
247 if (json_is_string(jlogthres)) {
248 trc->internal->log_threshold = str2sev(json_string_value(jlogthres));
250 tr_debug("tr_cfg_parse_internal: Parsing error, log_threshold is not a string.");
251 return TR_CFG_NOPARSE;
254 /* If not configured, use the default */
255 trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
258 if (NULL != (jconthres = json_object_get(jlog, "console_threshold"))) {
259 if (json_is_string(jconthres)) {
260 trc->internal->console_threshold = str2sev(json_string_value(jconthres));
262 tr_debug("tr_cfg_parse_internal: Parsing error, console_threshold is not a string.");
263 return TR_CFG_NOPARSE;
266 /* If not configured, use the default */
267 trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
270 /* If not configured, use the default */
271 trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
272 trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
275 tr_debug("tr_cfg_parse_internal: Internal config parsed.");
276 return TR_CFG_SUCCESS;
278 return TR_CFG_SUCCESS;
281 static TR_CONSTRAINT *tr_cfg_parse_one_constraint(TALLOC_CTX *mem_ctx, char *ctype, json_t *jc, TR_CFG_RC *rc)
283 TR_CONSTRAINT *cons=NULL;
286 if ((!ctype) || (!jc) || (!rc) ||
287 (!json_is_array(jc)) ||
288 (0 >= json_array_size(jc)) ||
289 (TR_MAX_CONST_MATCHES < json_array_size(jc)) ||
290 (!json_is_string(json_array_get(jc, 0)))) {
291 tr_err("tr_cfg_parse_one_constraint: config error.");
296 if (NULL==(cons=tr_constraint_new(mem_ctx))) {
297 tr_debug("tr_cfg_parse_one_constraint: Out of memory (cons).");
302 if (NULL==(cons->type=tr_new_name(ctype))) {
303 tr_err("tr_cfg_parse_one_constraint: Out of memory (type).");
305 tr_constraint_free(cons);
309 for (i=0; i < json_array_size(jc); i++) {
310 cons->matches[i]=tr_new_name(json_string_value(json_array_get(jc, i)));
311 if (cons->matches[i]==NULL) {
312 tr_err("tr_cfg_parse_one_constraint: Out of memory (match %d).", i+1);
314 tr_constraint_free(cons);
322 static TR_FILTER *tr_cfg_parse_one_filter(TALLOC_CTX *mem_ctx, json_t *jfilt, TR_FILTER_TYPE ftype, TR_CFG_RC *rc)
324 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
325 TR_FILTER *filt=NULL;
326 json_t *jfaction=NULL;
327 json_t *jfspecs=NULL;
328 json_t *jffield=NULL;
329 json_t *jfmatch=NULL;
336 if ((jfilt==NULL) || (rc==NULL)) {
337 tr_err("tr_cfg_parse_one_filter: null argument");
338 *rc=TR_CFG_BAD_PARAMS;
342 if (NULL==(filt=tr_filter_new(tmp_ctx))) {
343 tr_err("tr_cfg_parse_one_filter: Out of memory.");
347 tr_filter_set_type(filt, ftype);
349 /* make sure we have space to represent the filter */
350 if (json_array_size(jfilt) > TR_MAX_FILTER_LINES) {
351 tr_err("tr_cfg_parse_one_filter: Filter has too many lines, maximum of %d.", TR_MAX_FILTER_LINES);
356 /* For each entry in the filter... */
357 for (i=0; i < json_array_size(jfilt); i++) {
358 if ((NULL==(jfaction=json_object_get(json_array_get(jfilt, i), "action"))) ||
359 (!json_is_string(jfaction))) {
360 tr_debug("tr_cfg_parse_one_filter: Error parsing filter action.");
365 if ((NULL==(jfspecs=json_object_get(json_array_get(jfilt, i), "filter_specs"))) ||
366 (!json_is_array(jfspecs)) ||
367 (0==json_array_size(jfspecs))) {
368 tr_debug("tr_cfg_parse_one_filter: Error parsing filter specs.");
373 if (TR_MAX_FILTER_SPECS < json_array_size(jfspecs)) {
374 tr_debug("tr_cfg_parse_one_filter: Filter has too many filter_specs, maximimum of %d.", TR_MAX_FILTER_SPECS);
379 if (NULL==(filt->lines[i]=tr_fline_new(filt))) {
380 tr_debug("tr_cfg_parse_one_filter: Out of memory allocating filter line %d.", i+1);
385 if (!strcmp(json_string_value(jfaction), "accept")) {
386 filt->lines[i]->action=TR_FILTER_ACTION_ACCEPT;
388 else if (!strcmp(json_string_value(jfaction), "reject")) {
389 filt->lines[i]->action=TR_FILTER_ACTION_REJECT;
392 tr_debug("tr_cfg_parse_one_filter: Error parsing filter action, unknown action' %s'.", json_string_value(jfaction));
397 if (NULL!=(jrc=json_object_get(json_array_get(jfilt, i), "realm_constraints"))) {
398 if (!json_is_array(jrc)) {
399 tr_err("tr_cfg_parse_one_filter: cannot parse realm_constraints, not an array.");
402 } else if (json_array_size(jrc)>TR_MAX_CONST_MATCHES) {
403 tr_err("tr_cfg_parse_one_filter: realm_constraints has too many entries, maximum of %d.",
404 TR_MAX_CONST_MATCHES);
407 } else if (json_array_size(jrc)>0) {
408 /* ok we actually have entries to process */
409 if (NULL==(filt->lines[i]->realm_cons=tr_cfg_parse_one_constraint(filt->lines[i], "realm", jrc, rc))) {
410 tr_debug("tr_cfg_parse_one_filter: Error parsing realm constraint");
418 if (NULL!=(jdc=json_object_get(json_array_get(jfilt, i), "domain_constraints"))) {
419 if (!json_is_array(jdc)) {
420 tr_err("tr_cfg_parse_one_filter: cannot parse domain_constraints, not an array.");
423 } else if (json_array_size(jdc)>TR_MAX_CONST_MATCHES) {
424 tr_err("tr_cfg_parse_one_filter: domain_constraints has too many entries, maximum of %d.",
425 TR_MAX_CONST_MATCHES);
428 } else if (json_array_size(jdc)>0) {
429 if (NULL==(filt->lines[i]->domain_cons=tr_cfg_parse_one_constraint(filt->lines[i], "domain", jdc, rc))) {
430 tr_debug("tr_cfg_parse_one_filter: Error parsing domain constraint");
437 /*For each filter spec within the filter line... */
438 for (j=0; j <json_array_size(jfspecs); j++) {
439 if ((NULL==(jffield=json_object_get(json_array_get(jfspecs, j), "field"))) ||
440 (!json_is_string(jffield)) ||
441 (NULL==(jfmatch=json_object_get(json_array_get(jfspecs, j), "match"))) ||
442 (!json_is_string(jfmatch))) {
443 tr_debug("tr_cfg_parse_one_filter: Error parsing filter field and match for filter spec %d, filter line %d.", i, j);
448 if (NULL==(filt->lines[i]->specs[j]=tr_fspec_new(filt->lines[i]))) {
449 tr_debug("tr_cfg_parse_one_filter: Out of memory.");
454 if ((NULL==(filt->lines[i]->specs[j]->field=tr_new_name(json_string_value(jffield)))) ||
455 (NULL==(filt->lines[i]->specs[j]->match=tr_new_name(json_string_value(jfmatch))))) {
456 tr_debug("tr_cfg_parse_one_filter: Out of memory.");
463 talloc_steal(mem_ctx, filt);
466 talloc_free(tmp_ctx);
470 static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc)
472 TR_AAA_SERVER *aaa = NULL;
475 if ((!jaddr) || (!json_is_string(jaddr))) {
476 tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
477 *rc = TR_CFG_BAD_PARAMS;
481 name=tr_new_name(json_string_value(jaddr));
483 tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating hostname.");
488 aaa=tr_aaa_server_new(mem_ctx, name);
491 tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server.");
499 static TR_AAA_SERVER *tr_cfg_parse_aaa_servers(TALLOC_CTX *mem_ctx, json_t *jaaas, TR_CFG_RC *rc)
501 TALLOC_CTX *tmp_ctx=NULL;
502 TR_AAA_SERVER *aaa = NULL;
503 TR_AAA_SERVER *temp_aaa = NULL;
506 for (i = 0; i < json_array_size(jaaas); i++) {
507 /* rc gets set in here */
508 if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(tmp_ctx, json_array_get(jaaas, i), rc))) {
509 talloc_free(tmp_ctx);
512 /* TBD -- IPv6 addresses */
513 // tr_debug("tr_cfg_parse_aaa_servers: Configuring AAA Server: ip_addr = %s.", inet_ntoa(temp_aaa->aaa_server_addr));
514 temp_aaa->next = aaa;
517 tr_debug("tr_cfg_parse_aaa_servers: Finished (rc=%d)", *rc);
519 for (temp_aaa=aaa; temp_aaa!=NULL; temp_aaa=temp_aaa->next)
520 talloc_steal(mem_ctx, temp_aaa);
521 talloc_free(tmp_ctx);
525 static TR_APC *tr_cfg_parse_apc(TALLOC_CTX *mem_ctx, json_t *japc, TR_CFG_RC *rc)
530 *rc = TR_CFG_SUCCESS; /* presume success */
532 if ((!japc) || (!rc) || (!json_is_string(japc))) {
533 tr_debug("tr_cfg_parse_apc: Bad parameters.");
535 *rc = TR_CFG_BAD_PARAMS;
539 apc=tr_apc_new(mem_ctx);
541 tr_debug("tr_cfg_parse_apc: Out of memory.");
546 name=tr_new_name(json_string_value(japc));
548 tr_debug("tr_cfg_parse_apc: No memory for APC name.");
553 tr_apc_set_id(apc, name); /* apc is now responsible for freeing the name */
558 static TR_NAME *tr_cfg_parse_name(TALLOC_CTX *mem_ctx, json_t *jname, TR_CFG_RC *rc)
563 if ((jname==NULL) || (!json_is_string(jname))) {
564 tr_err("tr_cfg_parse_name: name missing or not a string");
565 *rc=TR_CFG_BAD_PARAMS;
568 name=tr_new_name(json_string_value(jname));
570 tr_err("tr_cfg_parse_name: could not allocate name");
579 static int tr_cfg_parse_shared_config(json_t *jsc, TR_CFG_RC *rc)
581 const char *shared=NULL;
585 (!json_is_string(jsc)) ||
586 (NULL==(shared=json_string_value(jsc)))) {
587 *rc=TR_CFG_BAD_PARAMS;
591 if (0==strcmp(shared, "no"))
593 else if (0==strcmp(shared, "yes"))
596 /* any other value is an error */
597 tr_debug("tr_cfg_parse_shared_config: invalid shared_config value \"%s\" (should be \"yes\" or \"no\")",
603 /* Parse the identity provider object from a realm and fill in the given TR_IDP_REALM. */
604 static TR_CFG_RC tr_cfg_parse_idp(TR_IDP_REALM *idp, json_t *jidp)
606 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
608 TR_AAA_SERVER *aaa=NULL;
609 TR_CFG_RC rc=TR_CFG_ERROR;
614 idp->origin=TR_REALM_LOCAL; /* if we're parsing it from a config file, it's local */
615 idp->shared_config=tr_cfg_parse_shared_config(json_object_get(jidp, "shared_config"), &rc);
616 if (rc!=TR_CFG_SUCCESS) {
617 tr_err("tr_cfg_parse_idp: missing or malformed shared_config specification");
622 apcs=tr_cfg_parse_apc(tmp_ctx, json_object_get(jidp, "apc"), &rc);
623 if ((rc!=TR_CFG_SUCCESS) || (apcs==NULL)) {
624 tr_err("tr_cfg_parse_idp: unable to parse APC");
628 tr_debug("tr_cfg_parse_idp: APC=\"%.*s\"",
632 aaa=tr_cfg_parse_aaa_servers(idp, json_object_get(jidp, "aaa_servers"), &rc);
633 if ((rc!=TR_CFG_SUCCESS) || (aaa==NULL)) {
634 tr_err("tr_cfg_parse_idp: unable to parse AAA servers");
639 /* done, fill in the idp structures */
641 talloc_steal(idp, apcs);
642 idp->aaa_servers=aaa;
646 if (rc!=TR_CFG_SUCCESS) {
650 tr_aaa_server_free(aaa);
653 talloc_free(tmp_ctx);
657 /* parses idp realm */
658 static TR_IDP_REALM *tr_cfg_parse_one_idp_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
660 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
661 TR_IDP_REALM *realm=NULL;
662 json_t *jremote=NULL;
666 TR_CFG_RC call_rc=TR_CFG_ERROR;
668 *rc=TR_CFG_ERROR; /* default to error if not set */
670 if ((!jrealm) || (!rc)) {
671 tr_err("tr_cfg_parse_one_idp_realm: Bad parameters.");
673 *rc=TR_CFG_BAD_PARAMS;
677 if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
678 tr_err("tr_cfg_parse_one_idp_realm: could not allocate idp realm.");
683 /* must have a name */
684 realm->realm_id=tr_cfg_parse_name(realm,
685 json_object_get(jrealm, "realm"),
687 if ((call_rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
688 tr_err("tr_cfg_parse_one_idp_realm: could not parse realm name");
692 tr_debug("tr_cfg_parse_one_idp_realm: realm_id=\"%.*s\"",
693 realm->realm_id->len,
694 realm->realm_id->buf);
696 call_rc=tr_cfg_parse_idp(realm, json_object_get(jrealm, "identity_provider"));
697 if (call_rc!=TR_CFG_SUCCESS) {
698 tr_err("tr_cfg_parse_one_idp_realm: could not parse identity_provider.");
706 if (*rc==TR_CFG_SUCCESS)
707 talloc_steal(mem_ctx, realm);
713 talloc_free(tmp_ctx);
717 /* Determine whether the realm is an IDP realm */
718 static int tr_cfg_is_idp_realm(json_t *jrealm)
720 /* If a realm spec contains an identity_provider, it's an IDP realm. */
721 if (NULL != json_object_get(jrealm, "identity_provider"))
727 /* Parse any idp realms in the j_realms object. Ignores other realm types. */
728 static TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc)
730 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
731 TR_IDP_REALM *realms=NULL;
732 TR_IDP_REALM *new_realm=NULL;
733 json_t *this_jrealm=NULL;
737 if ((jrealms==NULL) || (!json_is_array(jrealms))) {
738 tr_err("tr_cfg_parse_idp_realms: realms not an array");
739 *rc=TR_CFG_BAD_PARAMS;
743 for (ii=0; ii<json_array_size(jrealms); ii++) {
744 this_jrealm=json_array_get(jrealms, ii);
745 if (tr_cfg_is_idp_realm(this_jrealm)) {
746 new_realm=tr_cfg_parse_one_idp_realm(tmp_ctx, this_jrealm, rc);
747 if ((*rc)!=TR_CFG_SUCCESS) {
748 tr_err("tr_cfg_parse_idp_realms: error decoding realm entry %d", ii+1);
752 realms=tr_idp_realm_add(realms, new_realm);
757 talloc_steal(mem_ctx, realms);
760 talloc_free(tmp_ctx);
764 /* takes a talloc context, but currently does not use it */
765 static TR_NAME *tr_cfg_parse_org_name(TALLOC_CTX *mem_ctx, json_t *j_org, TR_CFG_RC *rc)
769 if ((j_org==NULL) || (rc==NULL) || (!json_is_string(j_org))) {
770 tr_debug("tr_cfg_parse_org_name: Bad parameters.");
772 *rc = TR_CFG_BAD_PARAMS; /* fill in return value if we can */
776 name=tr_new_name(json_string_value(j_org));
784 /* Update the community information with data from a new batch of IDP realms.
785 * May partially add realms if there is a failure, no guarantees.
786 * Call like comms=tr_comm_idp_update(comms, new_realms, &rc) */
787 static TR_COMM *tr_cfg_comm_idp_update(TALLOC_CTX *mem_ctx, TR_COMM *comms, TR_IDP_REALM *new_realms, TR_CFG_RC *rc)
789 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
790 TR_COMM *comm=NULL; /* community looked up in comms table */
791 TR_COMM *new_comms=NULL; /* new communities as we create them */
792 TR_IDP_REALM *realm=NULL;
793 TR_APC *apc=NULL; /* apc of one realm */
796 *rc=TR_CFG_BAD_PARAMS;
800 /* start with an empty list communities, then fill that in */
801 for (realm=new_realms; realm!=NULL; realm=realm->next) {
802 for (apc=realm->apcs; apc!=NULL; apc=apc->next) {
803 comm=tr_comm_lookup(comms, apc->id);
805 comm=tr_comm_new(tmp_ctx);
807 tr_debug("tr_cfg_comm_idp_update: unable to allocate new community.");
811 /* fill in the community with info */
812 comm->type=TR_COMM_APC; /* realms added this way are in APCs */
813 comm->expiration_interval=TR_DEFAULT_APC_EXPIRATION_INTERVAL;
814 comm->id=tr_dup_name(apc->id);
815 tr_comm_add_idp_realm(comm, realm);
816 new_comms=tr_comm_add(new_comms, comm);
818 /* add this realm to the comm */
819 tr_comm_add_idp_realm(comm, realm);
824 /* we successfully built a list, add it to the other list */
825 comms=tr_comm_add(comms, new_comms);
826 talloc_steal(mem_ctx, comms);
828 talloc_free(tmp_ctx);
833 static TR_CFG_RC tr_cfg_parse_one_local_org(TR_CFG *trc, json_t *jlorg)
835 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
836 TR_CFG_RC retval=TR_CFG_ERROR; /* our return code */
837 TR_CFG_RC rc=TR_CFG_ERROR; /* return code from functions we call */
838 TR_NAME *org_name=NULL;
840 json_t *j_realms=NULL;
841 TR_IDP_REALM *new_idp_realms=NULL;
842 TR_RP_REALM *new_rp_realms=NULL;
844 tr_debug("tr_cfg_parse_one_local_org: parsing local organization");
846 /* get organization_name (optional) */
847 if (NULL==(j_org=json_object_get(jlorg, "organization_name"))) {
848 tr_debug("tr_cfg_parse_one_local_org: organization_name unspecified");
850 org_name=tr_cfg_parse_org_name(tmp_ctx, j_org, &rc);
851 if (rc==TR_CFG_SUCCESS) {
852 tr_debug("tr_cfg_parse_one_local_org: organization_name=\"%.*s\"",
855 /* we don't actually do anything with this, but we could */
856 tr_free_name(org_name);
861 /* Now get realms. Allow this to be missing; even though that is a pointless organization entry,
862 * it's harmless. Report a warning because that might be unintentional. */
863 if (NULL==(j_realms=json_object_get(jlorg, "realms"))) {
864 tr_warning("tr_cfg_parse_one_local_org: warning - no realms in this local organization");
866 /* Allocate in the tmp_ctx so these will be cleaned up if we do not complete successfully. */
867 new_idp_realms=tr_cfg_parse_idp_realms(tmp_ctx, j_realms, &rc);
868 if (rc!=TR_CFG_SUCCESS)
872 new_rp_realms=tr_cfg_parse_rp_realms(tmp_ctx, j_realms, &rc);
873 if (rc!=TR_CFG_SUCCESS)
877 retval=TR_CFG_SUCCESS;
880 /* if we succeeded, link things to the configuration and move out of tmp context */
881 if (retval==TR_CFG_SUCCESS) {
882 if (new_idp_realms!=NULL) {
883 trc->idp_realms=tr_idp_realm_add(trc->idp_realms, new_idp_realms); /* fixes talloc contexts except for head*/
884 talloc_steal(trc, trc->idp_realms); /* make sure the head is in the right context */
885 trc->comms=tr_cfg_comm_idp_update(trc, trc->comms, new_idp_realms, &rc); /* put realm info in community table */
888 if (new_rp_realms!=NULL)
889 trc->rp_realms=tr_rp_realm_add(trc->rp_realms, new_rp_realms); /* fixes talloc contexts */
893 talloc_free(tmp_ctx);
897 /* Parse local organizations if present. Returns success if there are none. On failure, the configuration is unreliable. */
898 static TR_CFG_RC tr_cfg_parse_local_orgs(TR_CFG *trc, json_t *jcfg)
900 json_t *jlocorgs=NULL;
903 jlocorgs=json_object_get(jcfg, "local_organizations");
905 return TR_CFG_SUCCESS;
907 if (!json_is_array(jlocorgs)) {
908 tr_err("tr_cfg_parse_local_orgs: local_organizations is not an array.");
909 return TR_CFG_NOPARSE;
912 for (ii=0; ii<json_array_size(jlocorgs); ii++) {
913 if (tr_cfg_parse_one_local_org(trc, json_array_get(jlocorgs, ii))!=TR_CFG_SUCCESS) {
914 tr_err("tr_cfg_parse_local_orgs: error parsing local_organization %d.", ii+1);
915 return TR_CFG_NOPARSE;
919 return TR_CFG_SUCCESS;
923 TR_CFG_RC tr_cfg_validate(TR_CFG *trc)
925 TR_CFG_RC rc = TR_CFG_SUCCESS;
928 return TR_CFG_BAD_PARAMS;
930 if ((NULL == trc->internal)||
931 (NULL == trc->internal->hostname)) {
932 tr_debug("tr_cfg_validate: Error: No internal configuration, or no hostname.");
936 if (NULL == trc->rp_clients) {
937 tr_debug("tr_cfg_validate: Error: No RP Clients configured");
941 if (NULL == trc->comms) {
942 tr_debug("tr_cfg_validate: Error: No Communities configured");
946 if ((NULL == trc->default_servers) && (NULL == trc->idp_realms)) {
947 tr_debug("tr_cfg_validate: Error: No default servers or IDPs configured.");
954 /* Join two paths and return a pointer to the result. This should be freed
955 * via talloc_free. Returns NULL on failure. */
956 static char *join_paths(TALLOC_CTX *mem_ctx, const char *p1, const char *p2)
958 return talloc_asprintf(mem_ctx, "%s/%s", p1, p2); /* returns NULL on a failure */
961 TR_CFG_RC tr_cfg_parse_one_config_file(TR_CFG *cfg, const char *file_with_path)
967 if (NULL==(jcfg=json_load_file(file_with_path,
968 JSON_DISABLE_EOF_CHECK, &rc))) {
969 tr_debug("tr_parse_config: Error parsing config file %s.",
971 return TR_CFG_NOPARSE;
974 // Look for serial number and log it if it exists
975 if (NULL!=(jser=json_object_get(jcfg, "serial_number"))) {
976 if (json_is_number(jser)) {
977 tr_notice("tr_read_config: Attempting to load revision %" JSON_INTEGER_FORMAT " of '%s'.",
978 json_integer_value(jser),
983 /* TODO: parse using the new functions */
985 if ((TR_CFG_SUCCESS != tr_cfg_parse_internal(cfg, jcfg)) ||
986 (TR_CFG_SUCCESS != tr_cfg_parse_rp_clients(cfg, jcfg)) ||
987 (TR_CFG_SUCCESS != tr_cfg_parse_idp_realms(cfg, jcfg)) ||
988 (TR_CFG_SUCCESS != tr_cfg_parse_default_servers(cfg, jcfg)) ||
989 (TR_CFG_SUCCESS != tr_cfg_parse_comms(cfg, jcfg))) {
993 if (TR_CFG_SUCCESS != tr_cfg_parse_local_orgs(cfg, jcfg))
996 return TR_CFG_SUCCESS;
999 /* Reads configuration files in config_dir ("" or "./" will use the current directory). */
1000 TR_CFG_RC tr_parse_config(TR_CFG_MGR *cfg_mgr, const char *config_dir, int n, struct dirent **cfg_files)
1002 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1006 char *file_with_path;
1008 TR_CFG_RC cfg_rc=TR_CFG_ERROR;
1010 if ((!cfg_mgr) || (!cfg_files) || (n<=0)) {
1011 cfg_rc=TR_CFG_BAD_PARAMS;
1015 if (cfg_mgr->new != NULL)
1016 tr_cfg_free(cfg_mgr->new);
1017 cfg_mgr->new=tr_cfg_new(tmp_ctx); /* belongs to the temporary context for now */
1018 if (cfg_mgr->new == NULL) {
1019 cfg_rc=TR_CFG_NOMEM;
1023 /* Parse configuration information from each config file */
1024 for (ii=0; ii<n; ii++) {
1025 file_with_path=join_paths(tmp_ctx, config_dir, cfg_files[ii]->d_name); /* must free result with talloc_free */
1026 if(file_with_path == NULL) {
1027 tr_crit("tr_parse_config: error joining path.");
1028 cfg_rc=TR_CFG_NOMEM;
1031 tr_debug("tr_parse_config: Parsing %s.", cfg_files[ii]->d_name); /* print the filename without the path */
1032 cfg_rc=tr_cfg_parse_one_config_file(cfg_mgr->new, file_with_path);
1033 if (cfg_rc!=TR_CFG_SUCCESS) {
1034 tr_crit("tr_parse_config: error parsing %s", file_with_path);
1037 talloc_free(file_with_path); /* done with filename */
1040 /* make sure we got a complete, consistent configuration */
1041 if (TR_CFG_SUCCESS != tr_cfg_validate(cfg_mgr->new)) {
1042 tr_err("tr_parse_config: Error: INVALID CONFIGURATION");
1043 cfg_rc=TR_CFG_ERROR;
1048 talloc_steal(cfg_mgr, cfg_mgr->new); /* hand this over to the cfg_mgr context */
1049 cfg_rc=TR_CFG_SUCCESS;
1052 talloc_free(tmp_ctx);
1056 TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc)
1059 TR_IDP_REALM *cfg_idp;
1061 if ((!tr_cfg) || (!idp_id)) {
1063 *rc = TR_CFG_BAD_PARAMS;
1067 for (cfg_idp = tr_cfg->idp_realms; NULL != cfg_idp; cfg_idp = cfg_idp->next) {
1068 if (!tr_name_cmp (idp_id, cfg_idp->realm_id)) {
1069 tr_debug("tr_cfg_find_idp: Found %s.", idp_id->buf);
1073 /* if we didn't find one, return NULL */
1077 TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc)
1079 TR_RP_CLIENT *cfg_rp;
1082 if ((!tr_cfg) || (!rp_gss)) {
1084 *rc = TR_CFG_BAD_PARAMS;
1088 for (cfg_rp = tr_cfg->rp_clients; NULL != cfg_rp; cfg_rp = cfg_rp->next) {
1089 for (i = 0; i < TR_MAX_GSS_NAMES; i++) {
1090 if (!tr_name_cmp (rp_gss, cfg_rp->gss_names[i])) {
1091 tr_debug("tr_cfg_find_rp: Found %s.", rp_gss->buf);
1096 /* if we didn't find one, return NULL */
1100 static int is_cfg_file(const struct dirent *dent) {
1103 /* Only accept filenames ending in ".cfg" and starting with a character
1104 * other than an ASCII '.' */
1106 /* filename must be at least 4 characters long to be acceptable */
1107 n=strlen(dent->d_name);
1112 /* filename must not start with '.' */
1113 if ('.' == dent->d_name[0]) {
1117 /* If the above passed and the last four characters of the filename are .cfg, accept.
1118 * (n.b., assumes an earlier test checked that the name is >= 4 chars long.) */
1119 if (0 == strcmp(&(dent->d_name[n-4]), ".cfg")) {
1123 /* otherwise, return false. */
1127 /* Find configuration files in a particular directory. Returns the
1128 * number of entries found, 0 if none are found, or <0 for some
1129 * errors. If n>=0, the cfg_files parameter will contain a newly
1130 * allocated array of pointers to struct dirent entries, as returned
1131 * by scandir(). These can be freed with tr_free_config_file_list().
1133 int tr_find_config_files (const char *config_dir, struct dirent ***cfg_files) {
1136 n = scandir(config_dir, cfg_files, is_cfg_file, alphasort);
1140 tr_debug("tr_find_config: scandir error trying to scan %s.", config_dir);
1146 /* Free memory allocated for configuration file list returned from tr_find_config_files().
1147 * This can be called regardless of the return value of tr_find_config_values(). */
1148 void tr_free_config_file_list(int n, struct dirent ***cfg_files) {
1151 /* if n < 0, then scandir did not allocate anything because it failed */
1152 if((n>=0) && (*cfg_files != NULL)) {
1153 for(ii=0; ii<n; ii++) {
1154 free((*cfg_files)[ii]);
1156 free(*cfg_files); /* safe even if n==0 */
1157 *cfg_files=NULL; /* this will help prevent accidentally freeing twice */