Merge branch 'master' into jennifer/trp-devel
[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   json_t *jint = NULL;
122   json_t *jmtd = NULL;
123   json_t *jtidsp = NULL;
124   json_t *jtrpsp = NULL;
125   json_t *jhname = NULL;
126   json_t *jlog = NULL;
127   json_t *jconthres = NULL;
128   json_t *jlogthres = NULL;
129   json_t *jcfgpoll = NULL;
130   json_t *jcfgsettle = NULL;
131   json_t *jroutesweep = NULL;
132   json_t *jrouteupdate = NULL;
133   json_t *jrouteconnect = NULL;
134
135   if ((!trc) || (!jcfg))
136     return TR_CFG_BAD_PARAMS;
137
138   if (NULL == trc->internal) {
139     if (NULL == (trc->internal = talloc_zero(trc, TR_CFG_INTERNAL)))
140       return TR_CFG_NOMEM;
141   }
142
143   if (NULL != (jint = json_object_get(jcfg, "tr_internal"))) {
144     if (NULL != (jmtd = json_object_get(jint, "max_tree_depth"))) {
145       if (json_is_number(jmtd)) {
146         trc->internal->max_tree_depth = json_integer_value(jmtd);
147       } else {
148         tr_debug("tr_cfg_parse_internal: Parsing error, max_tree_depth is not a number.");
149         return TR_CFG_NOPARSE;
150       }
151     } else {
152       /* If not configured, use the default */
153       trc->internal->max_tree_depth = TR_DEFAULT_MAX_TREE_DEPTH;
154     }
155     if (NULL != (jtidsp = json_object_get(jint, "tids_port"))) {
156       if (json_is_number(jtidsp)) {
157         trc->internal->tids_port = json_integer_value(jtidsp);
158       } else {
159         tr_debug("tr_cfg_parse_internal: Parsing error, tids_port is not a number.");
160         return TR_CFG_NOPARSE;
161       }
162     } else {
163       /* If not configured, use the default */
164       trc->internal->tids_port = TR_DEFAULT_TIDS_PORT;
165     }
166     if (NULL != (jtrpsp = json_object_get(jint, "trps_port"))) {
167       if (json_is_number(jtrpsp)) {
168         trc->internal->trps_port = json_integer_value(jtrpsp);
169       } else {
170         tr_debug("tr_cfg_parse_internal: Parsing error, trps_port is not a number.");
171         return TR_CFG_NOPARSE;
172       }
173     } else {
174       /* If not configured, use the default */
175       trc->internal->trps_port = TR_DEFAULT_TRPS_PORT;
176     }
177     if (NULL != (jhname = json_object_get(jint, "hostname"))) {
178       if (json_is_string(jhname)) {
179         trc->internal->hostname = json_string_value(jhname);
180       } else {
181         tr_debug("tr_cfg_parse_internal: Parsing error, hostname is not a string.");
182         return TR_CFG_NOPARSE;
183       }
184     }
185     if (NULL != (jcfgpoll = json_object_get(jint, "cfg_poll_interval"))) {
186       if (json_is_number(jcfgpoll)) {
187         trc->internal->cfg_poll_interval = json_integer_value(jcfgpoll);
188       } else {
189         tr_debug("tr_cfg_parse_internal: Parsing error, cfg_poll_interval is not a number.");
190         return TR_CFG_NOPARSE;
191       }
192     } else {
193       trc->internal->cfg_poll_interval = TR_CFGWATCH_DEFAULT_POLL;
194     }
195
196     if (NULL != (jcfgsettle = json_object_get(jint, "cfg_settling_time"))) {
197       if (json_is_number(jcfgsettle)) {
198         trc->internal->cfg_settling_time = json_integer_value(jcfgsettle);
199       } else {
200         tr_debug("tr_cfg_parse_internal: Parsing error, cfg_settling_time is not a number.");
201         return TR_CFG_NOPARSE;
202       }
203     } else {
204       trc->internal->cfg_settling_time = TR_CFGWATCH_DEFAULT_SETTLE;
205     }
206
207     if (NULL != (jrouteconnect = json_object_get(jint, "trp_connect_interval"))) {
208       if (json_is_number(jrouteconnect)) {
209         trc->internal->trp_connect_interval = json_integer_value(jrouteconnect);
210       } else {
211         tr_debug("tr_cfg_parse_internal: Parsing error, trp_connect_interval is not a number.");
212         return TR_CFG_NOPARSE;
213       }
214     } else {
215       /* if not configured, use the default */
216       trc->internal->trp_connect_interval=TR_DEFAULT_TRP_CONNECT_INTERVAL;
217     }
218
219     if (NULL != (jroutesweep = json_object_get(jint, "trp_sweep_interval"))) {
220       if (json_is_number(jroutesweep)) {
221         trc->internal->trp_sweep_interval = json_integer_value(jroutesweep);
222       } else {
223         tr_debug("tr_cfg_parse_internal: Parsing error, trp_sweep_interval is not a number.");
224         return TR_CFG_NOPARSE;
225       }
226     } else {
227       /* if not configured, use the default */
228       trc->internal->trp_sweep_interval=TR_DEFAULT_TRP_SWEEP_INTERVAL;
229     }
230
231     if (NULL != (jrouteupdate = json_object_get(jint, "trp_update_interval"))) {
232       if (json_is_number(jrouteupdate)) {
233         trc->internal->trp_update_interval = json_integer_value(jrouteupdate);
234       } else {
235         tr_debug("tr_cfg_parse_internal: Parsing error, trp_update_interval is not a number.");
236         return TR_CFG_NOPARSE;
237       }
238     } else {
239       /* if not configured, use the default */
240       trc->internal->trp_update_interval=TR_DEFAULT_TRP_UPDATE_INTERVAL;
241     }
242
243     if (NULL != (jlog = json_object_get(jint, "logging"))) {
244       if (NULL != (jlogthres = json_object_get(jlog, "log_threshold"))) {
245         if (json_is_string(jlogthres)) {
246           trc->internal->log_threshold = str2sev(json_string_value(jlogthres));
247         } else {
248           tr_debug("tr_cfg_parse_internal: Parsing error, log_threshold is not a string.");
249           return TR_CFG_NOPARSE;
250         }
251       } else {
252         /* If not configured, use the default */
253         trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
254       }
255
256       if (NULL != (jconthres = json_object_get(jlog, "console_threshold"))) {
257         if (json_is_string(jconthres)) {
258             trc->internal->console_threshold = str2sev(json_string_value(jconthres));
259         } else {
260           tr_debug("tr_cfg_parse_internal: Parsing error, console_threshold is not a string.");
261           return TR_CFG_NOPARSE;
262         }
263       } else {
264         /* If not configured, use the default */
265         trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
266       }
267     } else {
268         /* If not configured, use the default */
269         trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
270         trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
271     }
272
273     tr_debug("tr_cfg_parse_internal: Internal config parsed.");
274     return TR_CFG_SUCCESS;
275   }
276   return TR_CFG_SUCCESS;
277 }
278
279 static TR_CONSTRAINT *tr_cfg_parse_one_constraint (TR_CFG *trc, char *ctype, json_t *jc, TR_CFG_RC *rc)
280 {
281   TR_CONSTRAINT *cons;
282   int i;
283
284   if ((!trc) || (!ctype) || (!jc) || (!rc) ||
285       (!json_is_array(jc)) ||
286       (0 >= json_array_size(jc)) ||
287       (TR_MAX_CONST_MATCHES < json_array_size(jc)) ||
288       (!json_is_string(json_array_get(jc, 0)))) {
289     tr_debug("tr_cfg_parse_one_constraint: config error.");
290     *rc = TR_CFG_NOPARSE;
291     return NULL;
292   }
293
294   if (NULL == (cons = talloc_zero(trc, TR_CONSTRAINT))) {
295     tr_debug("tr_cfg_parse_one_constraint: Out of memory (cons).");
296     *rc = TR_CFG_NOMEM;
297     return NULL;
298   }
299
300   if (NULL == (cons->type = tr_new_name(ctype))) {
301     tr_debug("tr_cfg_parse_one_constraint: Out of memory (type).");
302     *rc = TR_CFG_NOMEM;
303     return NULL;
304   }
305
306   for (i = 0; i < json_array_size(jc); i++) {
307     cons->matches[i] = tr_new_name((char *)(json_string_value(json_array_get(jc, i))));
308   }
309
310   return cons;
311 }
312
313 static TR_FILTER *tr_cfg_parse_one_filter (TR_CFG *trc, json_t *jfilt, TR_CFG_RC *rc)
314 {
315   TR_FILTER *filt = NULL;
316   json_t *jftype = NULL;
317   json_t *jfls = NULL;
318   json_t *jfaction = NULL;
319   json_t *jfspecs = NULL;
320   json_t *jffield = NULL;
321   json_t *jfmatch = NULL;
322   json_t *jrc = NULL;
323   json_t *jdc = NULL;
324   int i = 0, j = 0;
325
326   if ((NULL == (jftype = json_object_get(jfilt, "type"))) ||
327       (!json_is_string(jftype))) {
328     tr_debug("tr_cfg_parse_one_filter: Error parsing filter type.");
329     *rc = TR_CFG_NOPARSE;
330     return NULL;
331   }
332
333   if ((NULL == (jfls = json_object_get(jfilt, "filter_lines"))) ||
334       (!json_is_array(jfls))) {
335     tr_debug("tr_cfg_parse_one_filter: Error parsing filter type.");
336     *rc = TR_CFG_NOPARSE;
337     return NULL;
338   }
339
340   if (TR_MAX_FILTER_LINES < json_array_size(jfls)) {
341     tr_debug("tr_cfg_parse_one_filter: Filter has too many filter_lines, maximimum of %d.", TR_MAX_FILTER_LINES);
342     *rc = TR_CFG_NOPARSE;
343     return NULL;
344   }
345
346   if (NULL == (filt = talloc_zero(trc, TR_FILTER))) {
347     tr_debug("tr_cfg_parse_one_filter: Out of memory.");
348     *rc = TR_CFG_NOMEM;
349     return NULL;
350   }
351
352   if (!strcmp(json_string_value(jftype), "rp_permitted")) {
353     filt->type = TR_FILTER_TYPE_RP_PERMITTED;
354   }
355   else {
356     tr_debug("tr_cfg_parse_one_filter: Error parsing filter type, unknown type '%s'.", json_string_value(jftype));
357     *rc = TR_CFG_NOPARSE;
358     tr_filter_free(filt);
359     return NULL;
360   }
361
362   /* For each filter line... */
363   for (i = 0; i < json_array_size(jfls); i++) {
364
365     if ((NULL == (jfaction = json_object_get(json_array_get(jfls, i), "action"))) ||
366         (!json_is_string(jfaction))) {
367       tr_debug("tr_cfg_parse_one_filter: Error parsing filter action.");
368       *rc = TR_CFG_NOPARSE;
369       tr_filter_free(filt);
370       return NULL;
371     }
372  
373     if ((NULL == (jfspecs = json_object_get(json_array_get(jfls, i), "filter_specs"))) ||
374         (!json_is_array(jfspecs)) ||
375         (0 == json_array_size(jfspecs))) {
376       tr_debug("tr_cfg_parse_one_filter: Error parsing filter specs.");
377       *rc = TR_CFG_NOPARSE;
378       tr_filter_free(filt);
379       return NULL;
380     }
381   
382     if (TR_MAX_FILTER_SPECS < json_array_size(jfspecs)) {
383       tr_debug("tr_cfg_parse_one_filter: Filter has too many filter_specs, maximimum of %d.", TR_MAX_FILTER_SPECS);
384       *rc = TR_CFG_NOPARSE;
385       tr_filter_free(filt);
386       return NULL;
387     }
388
389     if (NULL == (filt->lines[i] = talloc_zero(trc, TR_FLINE))) {
390       tr_debug("tr_cfg_parse_one_filter: Out of memory (fline).");
391       *rc = TR_CFG_NOMEM;
392       tr_filter_free(filt);
393       return NULL;
394     }
395
396     if (!strcmp(json_string_value(jfaction), "accept")) {
397         filt->lines[i]->action = TR_FILTER_ACTION_ACCEPT;
398     }
399     else if (!strcmp(json_string_value(jfaction), "reject")) {
400       filt->lines[i]->action = TR_FILTER_ACTION_REJECT;
401     }
402     else {
403       tr_debug("tr_cfg_parse_one_filter: Error parsing filter action, unknown action' %s'.", json_string_value(jfaction));
404       *rc = TR_CFG_NOPARSE;
405       tr_filter_free(filt);
406       return NULL;
407     }
408
409     if ((NULL != (jrc = json_object_get(json_array_get(jfls, i), "realm_constraints"))) &&
410         (json_is_array(jrc)) &&
411         (0 != json_array_size(jrc)) &&
412         (TR_MAX_CONST_MATCHES >= json_array_size(jrc))) {
413
414       if (NULL == (filt->lines[i]->realm_cons = tr_cfg_parse_one_constraint(trc, "realm", jrc, rc))) {
415         tr_debug("tr_cfg_parse_one_filter: Error parsing realm constraint");
416       tr_filter_free(filt);
417       return NULL;
418       }
419     }
420
421     if ((NULL != (jdc = json_object_get(json_array_get(jfls, i), "domain_constraints"))) &&
422         (json_is_array(jdc)) &&
423         (0 != json_array_size(jdc)) &&
424         (TR_MAX_CONST_MATCHES >= json_array_size(jdc))) {
425
426       if (NULL == (filt->lines[i]->domain_cons = tr_cfg_parse_one_constraint(trc, "domain", jdc, rc))) {
427         tr_debug("tr_cfg_parse_one_filter: Error parsing domain constraint");
428       tr_filter_free(filt);
429       return NULL;
430       }
431     }
432
433     /*For each filter spec within the filter line... */
434     for (j = 0; j <json_array_size(jfspecs); j++) {
435       
436       if ((NULL == (jffield = json_object_get(json_array_get(jfspecs, j), "field"))) ||
437           (!json_is_string(jffield)) ||
438           (NULL == (jfmatch = json_object_get(json_array_get(jfspecs, j), "match"))) ||
439           (!json_is_string(jfmatch))) {
440         tr_debug("tr_cfg_parse_one_filter: Error parsing filter field and match for filter spec %d, filter line %d.", i, j);
441         *rc = TR_CFG_NOPARSE;
442         tr_filter_free(filt);
443         return NULL;
444       }
445
446       if (NULL == (filt->lines[i]->specs[j] = talloc_zero(trc, TR_FSPEC))) {
447         tr_debug("tr_cfg_parse_one_filter: Out of memory.");
448         *rc = TR_CFG_NOMEM;
449         tr_filter_free(filt);
450         return NULL;
451       }
452
453       if ((NULL == (filt->lines[i]->specs[j]->field = tr_new_name((char *)json_string_value(jffield)))) ||
454           (NULL == (filt->lines[i]->specs[j]->match = tr_new_name((char *)json_string_value(jfmatch))))) {
455         tr_debug("tr_cfg_parse_one_filter: Out of memory.");
456         *rc = TR_CFG_NOMEM;
457         tr_filter_free(filt);
458         return NULL;
459       }
460     }
461   }
462
463   return filt;
464 }
465
466 static TR_RP_CLIENT *tr_cfg_parse_one_rp_client (TR_CFG *trc, json_t *jrp, TR_CFG_RC *rc)
467 {
468   TR_RP_CLIENT *rp = NULL;
469   json_t *jgns = NULL;
470   json_t *jfilt = NULL;
471   json_t *jftype = NULL;
472   int i = 0;
473
474   if ((!trc) || (!jrp) || (!rc)) {
475     tr_debug("tr_cfg_parse_one_rp_realm: Bad parameters.");
476     if (rc)
477       *rc = TR_CFG_BAD_PARAMS;
478     return NULL;
479   }
480
481   if ((NULL == (jgns = json_object_get(jrp, "gss_names"))) ||
482       (!json_is_array(jgns))) {
483     tr_debug("tr_cfg_parse_one_rp_client: Error parsing RP client configuration, no GSS names.");
484     *rc = TR_CFG_NOPARSE;
485     return NULL;
486   }
487
488   /* TBD -- Support more than one filter per RP client? */
489   if (NULL == (jfilt = json_object_get(jrp, "filter"))) {
490     tr_debug("tr_cfg_parse_one_rp_client: Error parsing RP client configuration, no filter.");
491     *rc = TR_CFG_NOPARSE;
492     return NULL;
493   }
494
495   /* We only support rp_permitted filters for RP clients */
496   if ((NULL == (jftype = json_object_get(jfilt, "type"))) ||
497       (!json_is_string(jftype)) ||
498       (strcmp(json_string_value(jftype), "rp_permitted"))) {
499     tr_debug("tr_cfg_parse_one_rp_client: Error parsing RP client filter type.");
500     *rc = TR_CFG_NOPARSE;
501     return NULL;
502   }
503
504   if (TR_MAX_GSS_NAMES < json_array_size(jgns)) {
505     tr_debug("tr_cfg_parse_one_rp_client: RP Client has too many GSS Names.");
506     *rc = TR_CFG_NOPARSE;
507     return NULL;
508   }
509
510   if (NULL == (rp = talloc_zero(trc, TR_RP_CLIENT))) {
511     tr_debug("tr_cfg_parse_one_rp_realm: Out of memory.");
512     *rc = TR_CFG_NOMEM;
513     return NULL;
514   }
515
516   /* TBD -- support more than one filter entry per RP Client? */
517   if (NULL == (rp->filter = tr_cfg_parse_one_filter(trc, jfilt, rc))) {
518     tr_debug("tr_cfg_parse_one_rp_client: Error parsing filter.");
519     *rc = TR_CFG_NOPARSE;
520     return NULL;
521   }
522     
523   for (i = 0; i < json_array_size(jgns); i++) {
524     if (NULL == (rp->gss_names[i] = tr_new_name ((char *)json_string_value(json_array_get(jgns, i))))) {
525       tr_debug("tr_cfg_parse_one_rp_client: No memory for GSS Name.");
526       *rc = TR_CFG_NOMEM;
527       return NULL;
528     }
529   }
530   
531   return rp;
532 }
533
534 static TR_CFG_RC tr_cfg_parse_rp_clients (TR_CFG *trc, json_t *jcfg) {
535   json_t *jrps = NULL;
536   TR_RP_CLIENT *rp = NULL;
537   TR_CFG_RC rc = TR_CFG_SUCCESS;
538   int i = 0;
539
540   if ((!trc) || (!jcfg))
541     return TR_CFG_BAD_PARAMS;
542
543   if (NULL != (jrps = json_object_get(jcfg, "rp_clients"))) {
544
545     if (!json_is_array(jrps)) {
546       return TR_CFG_NOPARSE;
547     }
548
549     for (i = 0; i < json_array_size(jrps); i++) {
550       if (NULL == (rp = tr_cfg_parse_one_rp_client(trc, 
551                                                    json_array_get(jrps, i), 
552                                                    &rc))) {
553         return rc;
554       }
555       tr_debug("tr_cfg_parse_rp_clients: RP client configured -- first gss: %s", rp->gss_names[0]->buf);
556       rp->next = trc->rp_clients;
557       trc->rp_clients = rp;
558     }
559   }
560   tr_debug("tr_cfg_parse_rp_clients: Finished (rc=%d)", rc);
561   return rc;
562 }
563
564 static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jaddr, TR_CFG_RC *rc) {
565   TR_AAA_SERVER *aaa = NULL;
566   TR_NAME *name=NULL;
567
568   if ((!trc) || (!jaddr) || (!json_is_string(jaddr))) {
569     tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
570     *rc = TR_CFG_BAD_PARAMS;
571     return NULL;
572   }
573
574   name=tr_new_name((char *)(json_string_value(jaddr)));
575   if (name==NULL) {
576     tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating hostname.");
577     *rc = TR_CFG_NOMEM;
578     return NULL;
579   }
580
581   aaa=tr_aaa_server_new(mem_ctx, name);
582   if (aaa==NULL) {
583     tr_free_name(name);
584     tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server.");
585     *rc = TR_CFG_NOMEM;
586     return NULL;
587   }
588
589   return aaa;
590 }
591
592 static TR_AAA_SERVER *tr_cfg_parse_aaa_servers (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jaaas, TR_CFG_RC *rc) 
593 {
594   TALLOC_CTX *tmp_ctx=NULL;
595   TR_AAA_SERVER *aaa = NULL;
596   TR_AAA_SERVER *temp_aaa = NULL;
597   int i = 0;
598
599   for (i = 0; i < json_array_size(jaaas); i++) {
600     if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(mem_ctx, trc, json_array_get(jaaas, i), rc))) {
601       talloc_free(tmp_ctx);
602       return NULL;
603     }
604     /* TBD -- IPv6 addresses */
605     //    tr_debug("tr_cfg_parse_aaa_servers: Configuring AAA Server: ip_addr = %s.", inet_ntoa(temp_aaa->aaa_server_addr));
606     temp_aaa->next = aaa;
607     aaa = temp_aaa;
608   }
609   tr_debug("tr_cfg_parse_aaa_servers: Finished (rc=%d)", *rc);
610
611   for (temp_aaa=aaa; temp_aaa!=NULL; temp_aaa=temp_aaa->next)
612     talloc_steal(mem_ctx, temp_aaa);
613   talloc_free(tmp_ctx);
614   return aaa;
615 }
616
617 static TR_APC *tr_cfg_parse_apcs (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *japcs, TR_CFG_RC *rc)
618 {
619   TR_APC *apc;
620
621   *rc = TR_CFG_SUCCESS;         /* presume success */
622
623   if ((!trc) || (!japcs) || (!rc)) {
624     tr_debug("tr_cfg_parse_apcs: Bad parameters.");
625     if (rc) 
626       *rc = TR_CFG_BAD_PARAMS;
627     return NULL;
628   }
629
630   apc=tr_apc_new(mem_ctx);
631   if (apc==NULL) {
632     tr_debug("tr_cfg_parse_apcs: Out of memory.");
633     *rc = TR_CFG_NOMEM;
634     return NULL;
635   }
636
637   /* TBD, deal with more than one APC.  In the meantime, though...                */
638   /* Only parse the first APC, because we only know how to deal with one, anyway. */
639   if (0 == json_array_size(japcs)) {
640     talloc_free(apc);
641     return NULL;
642   }
643
644   if (NULL == (apc->id = tr_new_name((char *)json_string_value(json_array_get(japcs, 0))))) {
645     tr_debug("tr_cfg_parse_apcs: No memory for APC name.");
646     *rc = TR_CFG_NOMEM;
647     talloc_free(apc);
648     return NULL;
649   }
650
651   tr_debug("tr_cfg_parse_apcs: Finished (rc=%d)", *rc);
652   return apc;
653 }
654
655 static TR_IDP_REALM *tr_cfg_parse_one_idp_realm (TR_CFG *trc, json_t *jidp, TR_CFG_RC *rc) {
656   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
657   TR_IDP_REALM *idp = NULL;
658   json_t *jremote = NULL;
659   json_t *jrid = NULL;
660   json_t *jscfg = NULL;
661   json_t *jsrvrs = NULL;
662   json_t *japcs = NULL;
663
664   if ((!trc) || (!jidp) || (!rc)) {
665     tr_debug("tr_cfg_parse_one_idp_realm: Bad parameters.");
666     if (rc)
667       *rc = TR_CFG_BAD_PARAMS;
668     return NULL;
669   }
670
671   if (NULL == (idp = tr_idp_realm_new(tmp_ctx))) {
672     tr_debug("tr_cfg_parse_one_idp_realm: Out of memory.");
673     *rc = TR_CFG_NOMEM;
674     talloc_free(tmp_ctx);
675     return NULL;
676   }
677
678   /* Assume local route unless specified as remote. */
679   jremote = json_object_get(jidp, "remote");
680   if ((jremote!=NULL) && (!json_is_number(jremote))) {
681     tr_debug("tr_cfg_parse_one_idp_realm: Error parsing IDP realm configuration (remote is not a number).");
682     *rc=TR_CFG_NOPARSE;
683     talloc_free(tmp_ctx);
684     return NULL;
685   }
686
687   if ((NULL == (jrid = json_object_get(jidp, "realm_id"))) ||
688       (!json_is_string(jrid))) {
689       tr_debug("tr_cfg_parse_one_idp_realm: Error parsing IDP realm configuration (realm_id missing or invalid).");
690       *rc = TR_CFG_NOPARSE;
691       talloc_free(tmp_ctx);
692       return NULL;
693   }
694         
695   if ((jremote==NULL) || (0==json_integer_value(jremote))) {
696     idp->origin=TR_REALM_LOCAL;
697
698     if ((NULL == (jscfg = json_object_get(jidp, "shared_config"))) ||
699         (!json_is_string(jscfg)) ||
700         (NULL == (jsrvrs = json_object_get(jidp, "aaa_servers"))) ||
701         (!json_is_array(jsrvrs))) {
702       tr_debug("tr_cfg_parse_one_idp_realm: Error parsing IDP realm configuration.");
703       *rc = TR_CFG_NOPARSE;
704       talloc_free(tmp_ctx);
705       return NULL;
706     }
707
708     if (0 == strcmp(json_string_value(jscfg), "no")) {
709       idp->shared_config = 0;
710     } else {
711       idp->shared_config = 1;
712     }
713   } else
714     idp->origin=TR_REALM_REMOTE_INCOMPLETE;
715
716   if (NULL == (idp->realm_id = tr_new_name((char *)json_string_value(jrid)))) {
717     tr_debug("tr_cfg_parse_one_idp_realm: No memory for realm id.");
718     *rc = TR_CFG_NOMEM;
719     talloc_free(tmp_ctx);
720     return NULL;
721   }
722
723   if ((NULL != (japcs = json_object_get(jidp, "apcs"))) &&
724       (json_is_array(japcs))) {
725     if (NULL == (idp->apcs = tr_cfg_parse_apcs(idp, trc, japcs, rc))) {
726       tr_debug("tr_cfg_parse_one_idp_realm: Can't parse APCs for realm %s .", idp->realm_id->buf);
727       talloc_free(tmp_ctx);
728       return NULL;
729     }
730   } 
731
732   if ((idp->origin==TR_REALM_LOCAL) &&
733       (NULL == (idp->aaa_servers = tr_cfg_parse_aaa_servers(idp, trc, jsrvrs, rc)))) {
734     tr_debug("tr_cfg_parse_one_idp_realm: Can't parse AAA servers for realm %s.", idp->realm_id->buf);
735     talloc_free(tmp_ctx);
736     return NULL;
737   }
738
739   talloc_steal(trc, idp);
740   talloc_free(tmp_ctx);
741   return idp;
742 }
743
744 static TR_CFG_RC tr_cfg_parse_default_servers (TR_CFG *trc, json_t *jcfg) 
745 {
746   json_t *jdss = NULL;
747   TR_CFG_RC rc = TR_CFG_SUCCESS;
748   TR_AAA_SERVER *ds = NULL;
749   int i = 0;
750
751   if ((!trc) || (!jcfg))
752     return TR_CFG_BAD_PARAMS;
753
754   /* If there are default servers, store them */
755   if ((NULL != (jdss = json_object_get(jcfg, "default_servers"))) &&
756       (json_is_array(jdss)) &&
757       (0 < json_array_size(jdss))) {
758
759     for (i = 0; i < json_array_size(jdss); i++) {
760       if (NULL == (ds = tr_cfg_parse_one_aaa_server(trc, trc, 
761                                                   json_array_get(jdss, i), 
762                                                   &rc))) {
763         return rc;
764       }
765       tr_debug("tr_cfg_parse_default_servers: Default server configured: %s", ds->hostname->buf);
766       ds->next = trc->default_servers;
767       trc->default_servers = ds;
768     }
769   } 
770
771   tr_debug("tr_cfg_parse_default_servers: Finished (rc=%d)", rc);
772   return rc;
773 }
774
775 static TR_CFG_RC tr_cfg_parse_idp_realms (TR_CFG *trc, json_t *jcfg) 
776 {
777   json_t *jidps = NULL;
778   TR_CFG_RC rc = TR_CFG_SUCCESS;
779   TR_IDP_REALM *idp = NULL;
780   int i = 0;
781
782   if ((!trc) || (!jcfg))
783     return TR_CFG_BAD_PARAMS;
784
785   /* If there are any IDP Realms, parse them */
786   if ((NULL != (jidps = json_object_get(jcfg, "idp_realms"))) &&
787       (json_is_array(jidps))) {
788     for (i = 0; i < json_array_size(jidps); i++) {
789       if (NULL == (idp = tr_cfg_parse_one_idp_realm(trc,
790                                                     json_array_get(jidps, i), 
791                                                     &rc))) {
792         return rc;
793       }
794       tr_debug("tr_cfg_parse_idp_realms: IDP realm configured: %s.", idp->realm_id->buf);
795       idp->next = trc->idp_realms;
796       trc->idp_realms = idp;
797     }
798   }
799
800   tr_debug("tr_cfg_parse_idp_realms: Finished (rc=%d)", rc);
801   return rc;
802 }
803
804 static TR_IDP_REALM *tr_cfg_parse_comm_idps (TR_CFG *trc, json_t *jidps, TR_CFG_RC *rc)
805 {
806   TR_IDP_REALM *idp = NULL;
807   TR_IDP_REALM *found_idp = NULL;
808   TR_IDP_REALM *temp_idp = NULL;
809   int i = 0;
810
811   if ((!trc) ||
812       (!jidps) ||
813       (!json_is_array(jidps))) {
814     if (rc)
815       *rc = TR_CFG_BAD_PARAMS;
816     return NULL;
817   }
818
819   for (i = 0; i < json_array_size(jidps); i++) {
820     if (NULL == (temp_idp = talloc(trc, TR_IDP_REALM))) {
821       tr_debug("tr_cfg_parse_comm_idps: Can't allocate memory for IdP Realm.");
822       if (rc)
823         *rc = TR_CFG_NOMEM;
824       return NULL;
825     }
826     memset (temp_idp, 0, sizeof(TR_IDP_REALM));
827
828     if (NULL == (found_idp = (tr_cfg_find_idp(trc, 
829                                              tr_new_name((char *)json_string_value(json_array_get(jidps, i))), 
830                                              rc)))) {
831       tr_debug("tr_cfg_parse_comm_idps: Unknown IDP %s.", 
832               (char *)json_string_value(json_array_get(jidps, i)));
833       return NULL;
834     }
835
836     // We *MUST* do a dereferenced copy here or the second community will corrupt the linked list we create here.
837     *temp_idp = *found_idp;
838
839     temp_idp->comm_next = idp;
840     idp = temp_idp;
841   }
842
843   return idp;
844 }
845
846 static TR_RP_REALM *tr_cfg_parse_comm_rps (TR_CFG *trc, json_t *jrps, TR_CFG_RC *rc)
847 {
848   TR_RP_REALM *rp = NULL;
849   TR_RP_REALM *temp_rp = NULL;
850   int i = 0;
851
852   if ((!trc) ||
853       (!jrps) ||
854       (!json_is_array(jrps))) {
855     if (rc)
856       *rc = TR_CFG_BAD_PARAMS;
857     return NULL;
858   }
859
860   for (i = (json_array_size(jrps)-1); i >= 0; i--) {
861     if (NULL == (temp_rp = talloc_zero(trc, TR_RP_REALM))) {
862       tr_debug("tr_cfg_parse_comm_rps: Can't allocate memory for RP Realm.");
863       if (rc)
864         *rc = TR_CFG_NOMEM;
865       return NULL;
866     }
867
868     if (NULL == (temp_rp->realm_name = tr_new_name((char *)json_string_value(json_array_get(jrps, i))))) {
869       tr_debug("tr_cfg_parse_comm_rps: No memory for RP Realm Name.");
870       if (rc)
871         *rc = TR_CFG_NOMEM;
872       return NULL;
873     }
874
875     temp_rp->next = rp;
876     rp = temp_rp;
877   }
878
879   return rp;
880 }
881
882 static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc) {
883   TR_COMM *comm = NULL;
884   json_t *jid = NULL;
885   json_t *jtype = NULL;
886   json_t *japcs = NULL;
887   json_t *jidps = NULL;
888   json_t *jrps = NULL;
889
890   if ((!trc) || (!jcomm) || (!rc)) {
891     tr_debug("tr_cfg_parse_one_comm: Bad parameters.");
892     if (rc)
893       *rc = TR_CFG_BAD_PARAMS;
894     return NULL;
895   }
896
897   if (NULL == (comm = talloc_zero(trc, TR_COMM))) {
898     tr_crit("tr_cfg_parse_one_comm: Out of memory.");
899     *rc = TR_CFG_NOMEM;
900     return NULL;
901   }
902
903
904   if ((NULL == (jid = json_object_get(jcomm, "community_id"))) ||
905       (!json_is_string(jid)) ||
906       (NULL == (jtype = json_object_get(jcomm, "type"))) ||
907       (!json_is_string(jtype)) ||
908       (NULL == (japcs = json_object_get(jcomm, "apcs"))) ||
909       (!json_is_array(japcs)) ||
910       (NULL == (jidps = json_object_get(jcomm, "idp_realms"))) ||
911       (!json_is_array(jidps)) ||
912       (NULL == (jrps = json_object_get(jcomm, "rp_realms"))) ||
913       (!json_is_array(jrps))) {
914     tr_debug("tr_cfg_parse_one_comm: Error parsing Communities configuration.");
915     *rc = TR_CFG_NOPARSE;
916     return NULL;
917   }
918
919   if (NULL == (comm->id = tr_new_name((char *)json_string_value(jid)))) {
920     tr_debug("tr_cfg_parse_one_comm: No memory for community id.");
921     *rc = TR_CFG_NOMEM;
922     return NULL;
923   }
924
925   if (0 == strcmp(json_string_value(jtype), "apc")) {
926     comm->type = TR_COMM_APC;
927   } else if (0 == strcmp(json_string_value(jtype), "coi")) {
928     comm->type = TR_COMM_COI;
929     if (NULL == (comm->apcs = tr_cfg_parse_apcs(trc, trc, japcs, rc))) {
930       tr_debug("tr_cfg_parse_one_comm: Can't parse APCs for COI %s.", comm->id->buf);
931       tr_free_name(comm->id);
932       return NULL;
933     }
934   } else {
935     tr_debug("tr_cfg_parse_one_comm: Invalid community type, comm = %s, type = %s", comm->id->buf, json_string_value(jtype));
936     tr_free_name(comm->id);
937     *rc = TR_CFG_NOPARSE;
938     return NULL;
939   }
940
941   comm->idp_realms = tr_cfg_parse_comm_idps(trc, jidps, rc);
942   if (TR_CFG_SUCCESS != *rc) {
943     tr_debug("tr_cfg_parse_one_comm: Can't parse IDP realms for comm %s.", comm->id->buf);
944     tr_free_name(comm->id);
945     return NULL;
946   }
947
948   comm->rp_realms = tr_cfg_parse_comm_rps(trc, jrps, rc);
949   if (TR_CFG_SUCCESS != *rc) {
950     tr_debug("tr_cfg_parse_comm: Can't parse RP realms for comm %s .", comm->id->buf);
951     tr_free_name(comm->id);
952     return NULL;
953   }
954
955   if (TR_COMM_APC == comm->type) {
956     json_t *jexpire  = json_object_get(jcomm, "expiration_interval");
957     comm->expiration_interval = 43200; /*30 days*/
958     if (jexpire) {
959         if (!json_is_integer(jexpire)) {
960           fprintf(stderr, "tr_parse_comm: expirae_interval is not an integer\n");
961           return NULL;
962         }
963         comm->expiration_interval = json_integer_value(jexpire);
964         if (comm->expiration_interval <= 10)
965           comm->expiration_interval = 11; /* Freeradius waits 10 minutes between successful TR queries*/
966         if (comm->expiration_interval > 129600) /* 90 days*/
967         comm->expiration_interval = 129600;
968     }
969   }
970   
971   return comm;
972 }
973
974 static TR_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg) 
975 {
976   json_t *jcomms = NULL;
977   TR_CFG_RC rc = TR_CFG_SUCCESS;
978   TR_COMM *comm = NULL;
979   int i = 0;
980
981   if ((!trc) || (!jcfg)) {
982     tr_debug("tr_cfg_parse_comms: Bad Parameters.");
983     return TR_CFG_BAD_PARAMS;
984   }
985
986   if (NULL != (jcomms = json_object_get(jcfg, "communities"))) {
987     if (!json_is_array(jcomms)) {
988       return TR_CFG_NOPARSE;
989     }
990
991     for (i = 0; i < json_array_size(jcomms); i++) {
992       if (NULL == (comm = tr_cfg_parse_one_comm(trc, 
993                                                 json_array_get(jcomms, i), 
994                                                 &rc))) {
995         return rc;
996       }
997       tr_debug("tr_cfg_parse_comms: Community configured: %s.", comm->id->buf);
998       comm->next = trc->comms;
999       trc->comms = comm;
1000     }
1001   }
1002   tr_debug("tr_cfg_parse_comms: Finished (rc=%d)", rc);
1003   return rc;
1004 }
1005
1006 TR_CFG_RC tr_cfg_validate (TR_CFG *trc) {
1007   TR_CFG_RC rc = TR_CFG_SUCCESS;
1008
1009   if (!trc)
1010     return TR_CFG_BAD_PARAMS;
1011
1012   if ((NULL == trc->internal)||
1013       (NULL == trc->internal->hostname)) {
1014     tr_debug("tr_cfg_validate: Error: No internal configuration, or no hostname.");
1015     rc = TR_CFG_ERROR;
1016   }
1017
1018   if (NULL == trc->rp_clients) {
1019     tr_debug("tr_cfg_validate: Error: No RP Clients configured");
1020     rc = TR_CFG_ERROR;
1021   }
1022
1023   if (NULL == trc->comms) {
1024     tr_debug("tr_cfg_validate: Error: No Communities configured");
1025     rc = TR_CFG_ERROR;
1026   }
1027
1028   if ((NULL == trc->default_servers) && (NULL == trc->idp_realms)) {
1029     tr_debug("tr_cfg_validate: Error: No default servers or IDPs configured.");
1030     rc = TR_CFG_ERROR;
1031   }
1032   
1033   return rc;
1034 }
1035
1036 /* Join two paths and return a pointer to the result. This should be freed
1037  * via talloc_free. Returns NULL on failure. */
1038 static char *join_paths(TALLOC_CTX *mem_ctx, const char *p1, const char *p2) {
1039   return talloc_asprintf(mem_ctx, "%s/%s", p1, p2); /* returns NULL on a failure */
1040 }
1041
1042 /* Reads configuration files in config_dir ("" or "./" will use the current directory). */
1043 TR_CFG_RC tr_parse_config (TR_CFG_MGR *cfg_mgr, const char *config_dir, int n, struct dirent **cfg_files)
1044 {
1045   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1046   json_t *jcfg;
1047   json_t *jser;
1048   json_error_t rc;
1049   char *file_with_path;
1050   int ii;
1051   TR_CFG_RC cfg_rc=TR_CFG_ERROR;
1052
1053   if ((!cfg_mgr) || (!cfg_files) || (n<=0)) {
1054     cfg_rc=TR_CFG_BAD_PARAMS;
1055     goto cleanup;
1056   }
1057
1058   if (cfg_mgr->new != NULL)
1059     tr_cfg_free(cfg_mgr->new);
1060   cfg_mgr->new=tr_cfg_new(tmp_ctx); /* belongs to the temporary context for now */
1061   if (cfg_mgr->new == NULL) {
1062     cfg_rc=TR_CFG_NOMEM;
1063     goto cleanup;
1064   }
1065
1066   /* Parse configuration information from each config file */
1067   for (ii=0; ii<n; ii++) {
1068     file_with_path=join_paths(tmp_ctx, config_dir, cfg_files[ii]->d_name); /* must free result with talloc_free */
1069     if(file_with_path == NULL) {
1070       tr_crit("tr_parse_config: error joining path.");
1071       cfg_rc=TR_CFG_NOMEM;
1072       goto cleanup;
1073     }
1074     tr_debug("tr_parse_config: Parsing %s.", cfg_files[ii]->d_name); /* print the filename without the path */
1075     if (NULL == (jcfg = json_load_file(file_with_path, 
1076                                        JSON_DISABLE_EOF_CHECK, &rc))) {
1077       tr_debug("tr_parse_config: Error parsing config file %s.", 
1078                cfg_files[ii]->d_name);
1079       cfg_rc=TR_CFG_NOPARSE;
1080       goto cleanup;
1081     }
1082     talloc_free(file_with_path); /* done with filename */
1083
1084     // Look for serial number and log it if it exists
1085     if (NULL != (jser = json_object_get(jcfg, "serial_number"))) {
1086       if (json_is_number(jser)) {
1087         tr_notice("tr_read_config: Attempting to load revision %" JSON_INTEGER_FORMAT " of '%s'.",
1088                   json_integer_value(jser),
1089                   cfg_files[n]->d_name);
1090       }
1091     }
1092
1093     if ((TR_CFG_SUCCESS != tr_cfg_parse_internal(cfg_mgr->new, jcfg)) ||
1094         (TR_CFG_SUCCESS != tr_cfg_parse_rp_clients(cfg_mgr->new, jcfg)) ||
1095         (TR_CFG_SUCCESS != tr_cfg_parse_idp_realms(cfg_mgr->new, jcfg)) ||
1096         (TR_CFG_SUCCESS != tr_cfg_parse_default_servers(cfg_mgr->new, jcfg)) ||
1097         (TR_CFG_SUCCESS != tr_cfg_parse_comms(cfg_mgr->new, jcfg))) {
1098       cfg_rc=TR_CFG_ERROR;
1099       goto cleanup;
1100     }
1101   }
1102
1103   /* make sure we got a complete, consistent configuration */
1104   if (TR_CFG_SUCCESS != tr_cfg_validate(cfg_mgr->new)) {
1105     tr_err("tr_parse_config: Error: INVALID CONFIGURATION");
1106     cfg_rc=TR_CFG_ERROR;
1107     goto cleanup;
1108   }
1109
1110   /* success! */
1111   talloc_steal(cfg_mgr, cfg_mgr->new); /* hand this over to the cfg_mgr context */
1112   cfg_rc=TR_CFG_SUCCESS;
1113
1114 cleanup:
1115   talloc_free(tmp_ctx);
1116   return cfg_rc;
1117 }
1118
1119 TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc)
1120 {
1121
1122   TR_IDP_REALM *cfg_idp;
1123
1124   if ((!tr_cfg) || (!idp_id)) {
1125     if (rc)
1126       *rc = TR_CFG_BAD_PARAMS;
1127     return NULL;
1128   }
1129
1130   for (cfg_idp = tr_cfg->idp_realms; NULL != cfg_idp; cfg_idp = cfg_idp->next) {
1131     if (!tr_name_cmp (idp_id, cfg_idp->realm_id)) {
1132       tr_debug("tr_cfg_find_idp: Found %s.", idp_id->buf);
1133       return cfg_idp;
1134     }
1135   }
1136   /* if we didn't find one, return NULL */ 
1137   return NULL;
1138 }
1139
1140 TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc)
1141 {
1142   TR_RP_CLIENT *cfg_rp;
1143   int i;
1144
1145   if ((!tr_cfg) || (!rp_gss)) {
1146     if (rc)
1147       *rc = TR_CFG_BAD_PARAMS;
1148     return NULL;
1149   }
1150
1151   for (cfg_rp = tr_cfg->rp_clients; NULL != cfg_rp; cfg_rp = cfg_rp->next) {
1152     for (i = 0; i < TR_MAX_GSS_NAMES; i++) {
1153       if (!tr_name_cmp (rp_gss, cfg_rp->gss_names[i])) {
1154         tr_debug("tr_cfg_find_rp: Found %s.", rp_gss->buf);
1155         return cfg_rp;
1156       }
1157     }
1158   }
1159   /* if we didn't find one, return NULL */ 
1160   return NULL;
1161 }
1162
1163 static int is_cfg_file(const struct dirent *dent) {
1164   int n;
1165
1166   /* Only accept filenames ending in ".cfg" and starting with a character
1167    * other than an ASCII '.' */
1168
1169   /* filename must be at least 4 characters long to be acceptable */
1170   n=strlen(dent->d_name);
1171   if (n < 4) {
1172     return 0;
1173   }
1174
1175   /* filename must not start with '.' */
1176   if ('.' == dent->d_name[0]) {
1177     return 0;
1178   }
1179
1180   /* If the above passed and the last four characters of the filename are .cfg, accept.
1181    * (n.b., assumes an earlier test checked that the name is >= 4 chars long.) */
1182   if (0 == strcmp(&(dent->d_name[n-4]), ".cfg")) {
1183     return 1;
1184   }
1185
1186   /* otherwise, return false. */
1187   return 0;
1188 }
1189
1190 /* Find configuration files in a particular directory. Returns the
1191  * number of entries found, 0 if none are found, or <0 for some
1192  * errors. If n>=0, the cfg_files parameter will contain a newly
1193  * allocated array of pointers to struct dirent entries, as returned
1194  * by scandir(). These can be freed with tr_free_config_file_list().
1195  */
1196 int tr_find_config_files (const char *config_dir, struct dirent ***cfg_files) {
1197   int n = 0;
1198   
1199   n = scandir(config_dir, cfg_files, is_cfg_file, alphasort);
1200
1201   if (n < 0) {
1202     perror("scandir");
1203     tr_debug("tr_find_config: scandir error trying to scan %s.", config_dir);
1204   } 
1205
1206   return n;
1207 }
1208
1209 /* Free memory allocated for configuration file list returned from tr_find_config_files().
1210  * This can be called regardless of the return value of tr_find_config_values(). */
1211 void tr_free_config_file_list(int n, struct dirent ***cfg_files) {
1212   int ii;
1213
1214   /* if n < 0, then scandir did not allocate anything because it failed */
1215   if((n>=0) && (*cfg_files != NULL)) {
1216     for(ii=0; ii<n; ii++) {
1217       free((*cfg_files)[ii]);
1218     }
1219     free(*cfg_files); /* safe even if n==0 */
1220     *cfg_files=NULL; /* this will help prevent accidentally freeing twice */
1221   }
1222 }