Merge pull request #86 from painless-security/jennifer/aaa_server_port
[trust_router.git] / common / tr_config_realms.c
1 /*
2  * Copyright (c) 2012-2018, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <jansson.h>
38 #include <dirent.h>
39 #include <talloc.h>
40
41 #include <tr_cfgwatch.h>
42 #include <tr_comm.h>
43 #include <tr_config.h>
44 #include <tr_gss_names.h>
45 #include <tr_debug.h>
46 #include <tr_filter.h>
47 #include <trust_router/tr_constraint.h>
48 #include <tr_idp.h>
49 #include <tr.h>
50 #include <trust_router/trp.h>
51
52 #if JANSSON_VERSION_HEX < 0x020500
53 #include "jansson_iterators.h"
54 #endif
55
56 TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc)
57 {
58   TALLOC_CTX *tmp_ctx = talloc_new(NULL);
59   TR_AAA_SERVER *aaa = NULL;
60
61   if ((!jaddr) || (!json_is_string(jaddr))) {
62     tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
63     *rc = TR_CFG_BAD_PARAMS;
64     goto cleanup;
65   }
66
67   aaa = tr_aaa_server_from_string(mem_ctx, json_string_value(jaddr));
68   if (aaa == NULL) {
69     tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server.");
70     *rc = TR_CFG_NOMEM;
71     goto cleanup;
72   }
73
74   if (tr_aaa_server_get_hostname(aaa)->len == 0) {
75     tr_debug("tr_cfg_parse_one_aaa_server: Invalid hostname for AAA server (%s)",
76              json_string_value(jaddr));
77     *rc = TR_CFG_NOPARSE;
78     goto cleanup;
79   }
80
81   if ((tr_aaa_server_get_port(aaa) <= 0)
82       || (tr_aaa_server_get_port(aaa) > 65535)) {
83     tr_debug("tr_cfg_parse_one_aaa_server: Invalid AAA server port (%s)",
84              json_string_value(jaddr));
85     *rc = TR_CFG_NOPARSE;
86     goto cleanup;
87   }
88
89   /* success ! */
90   *rc = TR_CFG_SUCCESS;
91   talloc_steal(mem_ctx, aaa);
92
93 cleanup:
94   if (*rc != TR_CFG_SUCCESS)
95     aaa = NULL;
96   talloc_free(tmp_ctx);
97   return aaa;
98 }
99
100 static TR_AAA_SERVER *tr_cfg_parse_aaa_servers(TALLOC_CTX *mem_ctx, json_t *jaaas, TR_CFG_RC *rc)
101 {
102   TALLOC_CTX *tmp_ctx=NULL;
103   TR_AAA_SERVER *aaa = NULL;
104   TR_AAA_SERVER *temp_aaa = NULL;
105   int i = 0;
106
107   for (i = 0; i < json_array_size(jaaas); i++) {
108     /* rc gets set in here */
109     if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(tmp_ctx, json_array_get(jaaas, i), rc))) {
110       talloc_free(tmp_ctx);
111       return NULL;
112     }
113     /* TBD -- IPv6 addresses */
114     //    tr_debug("tr_cfg_parse_aaa_servers: Configuring AAA Server: ip_addr = %s.", inet_ntoa(temp_aaa->aaa_server_addr));
115     temp_aaa->next = aaa;
116     aaa = temp_aaa;
117   }
118   tr_debug("tr_cfg_parse_aaa_servers: Finished (rc=%d)", *rc);
119
120   for (temp_aaa=aaa; temp_aaa!=NULL; temp_aaa=temp_aaa->next)
121     talloc_steal(mem_ctx, temp_aaa);
122   talloc_free(tmp_ctx);
123   return aaa;
124 }
125
126 static TR_APC *tr_cfg_parse_one_apc(TALLOC_CTX *mem_ctx, json_t *japc, TR_CFG_RC *rc)
127 {
128   TR_APC *apc=NULL;
129   TR_NAME *name=NULL;
130
131   *rc = TR_CFG_SUCCESS;         /* presume success */
132
133   if ((!japc) || (!rc) || (!json_is_string(japc))) {
134     tr_debug("tr_cfg_parse_one_apc: Bad parameters.");
135     if (rc)
136       *rc = TR_CFG_BAD_PARAMS;
137     return NULL;
138   }
139
140   apc=tr_apc_new(mem_ctx);
141   if (apc==NULL) {
142     tr_debug("tr_cfg_parse_one_apc: Out of memory.");
143     *rc = TR_CFG_NOMEM;
144     return NULL;
145   }
146
147   name=tr_new_name(json_string_value(japc));
148   if (name==NULL) {
149     tr_debug("tr_cfg_parse_one_apc: No memory for APC name.");
150     tr_apc_free(apc);
151     *rc = TR_CFG_NOMEM;
152     return NULL;
153   }
154   tr_apc_set_id(apc, name); /* apc is now responsible for freeing the name */
155
156   return apc;
157 }
158
159 TR_APC *tr_cfg_parse_apcs(TALLOC_CTX *mem_ctx, json_t *japcs, TR_CFG_RC *rc)
160 {
161   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
162   TR_APC *apcs=NULL;
163   TR_APC *new_apc=NULL;
164   int ii=0;
165   TR_CFG_RC call_rc=TR_CFG_ERROR;
166
167   *rc = TR_CFG_SUCCESS;         /* presume success */
168
169   if ((!japcs) || (!rc) || (!json_is_array(japcs))) {
170     tr_debug("tr_cfg_parse_one_apc: Bad parameters.");
171     if (rc)
172       *rc = TR_CFG_BAD_PARAMS;
173     return NULL;
174   }
175
176   for (ii=0; ii<json_array_size(japcs); ii++) {
177     new_apc=tr_cfg_parse_one_apc(tmp_ctx, json_array_get(japcs, ii), &call_rc);
178     if ((call_rc!=TR_CFG_SUCCESS) || (new_apc==NULL)) {
179       tr_debug("tr_cfg_parse_apcs: Error parsing APC %d.", ii+1);
180       *rc=TR_CFG_NOPARSE;
181       goto cleanup;
182     }
183     tr_apc_add(apcs, new_apc);
184   }
185
186   talloc_steal(mem_ctx, apcs);
187   *rc=TR_CFG_SUCCESS;
188
189 cleanup:
190   talloc_free(tmp_ctx);
191   return apcs;
192 }
193
194 static TR_NAME *tr_cfg_parse_name(TALLOC_CTX *mem_ctx, json_t *jname, TR_CFG_RC *rc)
195 {
196   TR_NAME *name=NULL;
197   *rc=TR_CFG_ERROR;
198
199   if ((jname==NULL) || (!json_is_string(jname))) {
200     tr_err("tr_cfg_parse_name: name missing or not a string");
201     *rc=TR_CFG_BAD_PARAMS;
202     name=NULL;
203   } else {
204     name=tr_new_name(json_string_value(jname));
205     if (name==NULL) {
206       tr_err("tr_cfg_parse_name: could not allocate name");
207       *rc=TR_CFG_NOMEM;
208     } else {
209       *rc=TR_CFG_SUCCESS;
210     }
211   }
212   return name;
213 }
214
215 static int tr_cfg_parse_shared_config(json_t *jsc, TR_CFG_RC *rc)
216 {
217   const char *shared=NULL;
218   *rc=TR_CFG_SUCCESS;
219
220   if ((jsc==NULL) ||
221       (!json_is_string(jsc)) ||
222       (NULL==(shared=json_string_value(jsc)))) {
223     *rc=TR_CFG_BAD_PARAMS;
224     return -1;
225   }
226
227   if (0==strcmp(shared, "no"))
228     return 0;
229   else if (0==strcmp(shared, "yes"))
230     return 1;
231
232   /* any other value is an error */
233   tr_debug("tr_cfg_parse_shared_config: invalid shared_config value \"%s\" (should be \"yes\" or \"no\")",
234            shared);
235   *rc=TR_CFG_NOPARSE;
236   return -1;
237 }
238
239 static TR_REALM_ORIGIN tr_cfg_realm_origin(json_t *jrealm)
240 {
241   json_t *jremote=json_object_get(jrealm, "remote");
242   const char *s=NULL;
243
244   if (jremote==NULL)
245     return TR_REALM_LOCAL;
246   if (!json_is_string(jremote)) {
247     tr_warning("tr_cfg_realm_origin: \"remote\" is not a string, assuming this is a local realm.");
248     return TR_REALM_LOCAL;
249   }
250   s=json_string_value(jremote);
251   if (strcasecmp(s, "yes")==0)
252     return TR_REALM_REMOTE_INCOMPLETE;
253   else if (strcasecmp(s, "no")!=0)
254     tr_warning("tr_cfg_realm_origin: \"remote\" is neither 'yes' nor 'no', assuming this is a local realm.");
255
256   return TR_REALM_LOCAL;
257 }
258
259 /* Parse the identity provider object from a realm and fill in the given TR_IDP_REALM. */
260 static TR_CFG_RC tr_cfg_parse_idp(TR_IDP_REALM *idp, json_t *jidp)
261 {
262   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
263   TR_APC *apcs=NULL;
264   TR_AAA_SERVER *aaa=NULL;
265   TR_CFG_RC rc=TR_CFG_ERROR;
266
267   if (jidp==NULL)
268     goto cleanup;
269
270   idp->shared_config=tr_cfg_parse_shared_config(json_object_get(jidp, "shared_config"), &rc);
271   if (rc!=TR_CFG_SUCCESS) {
272     tr_err("tr_cfg_parse_idp: missing or malformed shared_config specification");
273     rc=TR_CFG_NOPARSE;
274     goto cleanup;
275   }
276
277   apcs=tr_cfg_parse_apcs(tmp_ctx, json_object_get(jidp, "apcs"), &rc);
278   if ((rc!=TR_CFG_SUCCESS) || (apcs==NULL)) {
279     tr_err("tr_cfg_parse_idp: unable to parse APC");
280     rc=TR_CFG_NOPARSE;
281     goto cleanup;
282   }
283
284   aaa=tr_cfg_parse_aaa_servers(idp, json_object_get(jidp, "aaa_servers"), &rc);
285   if (rc!=TR_CFG_SUCCESS) {
286     tr_err("tr_cfg_parse_idp: unable to parse AAA servers");
287     rc=TR_CFG_NOPARSE;
288     goto cleanup;
289   }
290
291   tr_debug("tr_cfg_parse_idp: APC=\"%.*s\"",
292            apcs->id->len,
293            apcs->id->buf);
294
295   /* done, fill in the idp structures */
296   idp->apcs=apcs;
297   talloc_steal(idp, apcs);
298   idp->aaa_servers=aaa;
299   rc=TR_CFG_SUCCESS;
300
301 cleanup:
302   if (rc!=TR_CFG_SUCCESS) {
303     if (apcs!=NULL)
304       tr_apc_free(apcs);
305     if (aaa!=NULL)
306       tr_aaa_server_free(aaa);
307   }
308
309   talloc_free(tmp_ctx);
310   return rc;
311 }
312
313 /* parses idp realm */
314 static TR_IDP_REALM *tr_cfg_parse_one_idp_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
315 {
316   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
317   TR_IDP_REALM *realm=NULL;
318   TR_CFG_RC call_rc=TR_CFG_ERROR;
319
320   *rc=TR_CFG_ERROR; /* default to error if not set */
321
322   if ((!jrealm) || (!rc)) {
323     tr_err("tr_cfg_parse_one_idp_realm: Bad parameters.");
324     if (rc)
325       *rc=TR_CFG_BAD_PARAMS;
326     goto cleanup;
327   }
328
329   if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
330     tr_err("tr_cfg_parse_one_idp_realm: could not allocate idp realm.");
331     *rc=TR_CFG_NOMEM;
332     goto cleanup;
333   }
334
335   realm->origin=tr_cfg_realm_origin(jrealm);
336   if (realm->origin!=TR_REALM_LOCAL) {
337     tr_debug("tr_cfg_parse_one_idp_realm: realm is remote, should not have full IdP info.");
338     *rc=TR_CFG_NOPARSE;
339     goto cleanup;
340   }
341
342   /* must have a name */
343   realm->realm_id=tr_cfg_parse_name(realm,
344                                     json_object_get(jrealm, "realm"),
345                                     &call_rc);
346   if ((call_rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
347     tr_err("tr_cfg_parse_one_idp_realm: could not parse realm name");
348     *rc=TR_CFG_NOPARSE;
349     goto cleanup;
350   }
351   tr_debug("tr_cfg_parse_one_idp_realm: realm_id=\"%.*s\"",
352            realm->realm_id->len,
353            realm->realm_id->buf);
354
355   call_rc=tr_cfg_parse_idp(realm, json_object_get(jrealm, "identity_provider"));
356   if (call_rc!=TR_CFG_SUCCESS) {
357     tr_err("tr_cfg_parse_one_idp_realm: could not parse identity_provider.");
358     *rc=TR_CFG_NOPARSE;
359     goto cleanup;
360   }
361
362   *rc=TR_CFG_SUCCESS;
363
364 cleanup:
365   if (*rc==TR_CFG_SUCCESS)
366     talloc_steal(mem_ctx, realm);
367   else {
368     talloc_free(realm);
369     realm=NULL;
370   }
371
372   talloc_free(tmp_ctx);
373   return realm;
374 }
375
376 /* Determine whether the realm is an IDP realm */
377 static int tr_cfg_is_idp_realm(json_t *jrealm)
378 {
379   /* If a realm spec contains an identity_provider, it's an IDP realm. */
380   if (NULL != json_object_get(jrealm, "identity_provider"))
381     return 1;
382   else
383     return 0;
384 }
385
386 static TR_IDP_REALM *tr_cfg_parse_one_remote_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
387 {
388   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
389   TR_IDP_REALM *realm=talloc(mem_ctx, TR_IDP_REALM);
390
391   *rc=TR_CFG_ERROR; /* default to error if not set */
392
393   if ((!jrealm) || (!rc)) {
394     tr_err("tr_cfg_parse_one_remote_realm: Bad parameters.");
395     if (rc)
396       *rc=TR_CFG_BAD_PARAMS;
397     goto cleanup;
398   }
399
400   if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
401     tr_err("tr_cfg_parse_one_remote_realm: could not allocate idp realm.");
402     *rc=TR_CFG_NOMEM;
403     goto cleanup;
404   }
405
406   /* must have a name */
407   realm->realm_id=tr_cfg_parse_name(realm,
408                                     json_object_get(jrealm, "realm"),
409                                     rc);
410   if ((*rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
411     tr_err("tr_cfg_parse_one_remote_realm: could not parse realm name");
412     *rc=TR_CFG_NOPARSE;
413     goto cleanup;
414   }
415   tr_debug("tr_cfg_parse_one_remote_realm: realm_id=\"%.*s\"",
416            realm->realm_id->len,
417            realm->realm_id->buf);
418
419   realm->origin=tr_cfg_realm_origin(jrealm);
420   *rc=TR_CFG_SUCCESS;
421
422 cleanup:
423   if (*rc==TR_CFG_SUCCESS)
424     talloc_steal(mem_ctx, realm);
425   else {
426     talloc_free(realm);
427     realm=NULL;
428   }
429
430   talloc_free(tmp_ctx);
431   return realm;
432 }
433
434 static int tr_cfg_is_remote_realm(json_t *jrealm)
435 {
436   return (tr_cfg_realm_origin(jrealm)!=TR_REALM_LOCAL);
437 }
438
439 /* Parse any idp realms in the j_realms object. Ignores other realm types. */
440 TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc)
441 {
442   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
443   TR_IDP_REALM *realms=NULL;
444   TR_IDP_REALM *new_realm=NULL;
445   json_t *this_jrealm=NULL;
446   int ii=0;
447
448   *rc=TR_CFG_ERROR;
449   if ((jrealms==NULL) || (!json_is_array(jrealms))) {
450     tr_err("tr_cfg_parse_idp_realms: realms not an array");
451     *rc=TR_CFG_BAD_PARAMS;
452     goto cleanup;
453   }
454
455   for (ii=0; ii<json_array_size(jrealms); ii++) {
456     this_jrealm=json_array_get(jrealms, ii);
457     if (tr_cfg_is_idp_realm(this_jrealm)) {
458       new_realm=tr_cfg_parse_one_idp_realm(tmp_ctx, this_jrealm, rc);
459       if ((*rc)!=TR_CFG_SUCCESS) {
460         tr_err("tr_cfg_parse_idp_realms: error decoding realm entry %d", ii+1);
461         *rc=TR_CFG_NOPARSE;
462         goto cleanup;
463       }
464       tr_idp_realm_add(realms, new_realm);
465     } else if (tr_cfg_is_remote_realm(this_jrealm)) {
466       new_realm=tr_cfg_parse_one_remote_realm(tmp_ctx, this_jrealm, rc);
467       if ((*rc)!=TR_CFG_SUCCESS) {
468         tr_err("tr_cfg_parse_idp_realms: error decoding remote realm entry %d", ii+1);
469         *rc=TR_CFG_NOPARSE;
470         goto cleanup;
471       }
472       tr_idp_realm_add(realms, new_realm);
473     }
474   }
475
476   *rc=TR_CFG_SUCCESS;
477   talloc_steal(mem_ctx, realms);
478
479 cleanup:
480   talloc_free(tmp_ctx);
481   return realms;
482 }