3c189ee348031d421b1bc59a195743624f217bfd
[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   if (*rc != TR_CFG_SUCCESS)
191     apcs = NULL;
192
193   talloc_free(tmp_ctx);
194   return apcs;
195 }
196
197 static TR_NAME *tr_cfg_parse_name(TALLOC_CTX *mem_ctx, json_t *jname, TR_CFG_RC *rc)
198 {
199   TR_NAME *name=NULL;
200   *rc=TR_CFG_ERROR;
201
202   if ((jname==NULL) || (!json_is_string(jname))) {
203     tr_err("tr_cfg_parse_name: name missing or not a string");
204     *rc=TR_CFG_BAD_PARAMS;
205     name=NULL;
206   } else {
207     name=tr_new_name(json_string_value(jname));
208     if (name==NULL) {
209       tr_err("tr_cfg_parse_name: could not allocate name");
210       *rc=TR_CFG_NOMEM;
211     } else {
212       *rc=TR_CFG_SUCCESS;
213     }
214   }
215   return name;
216 }
217
218 static int tr_cfg_parse_shared_config(json_t *jsc, TR_CFG_RC *rc)
219 {
220   const char *shared=NULL;
221   *rc=TR_CFG_SUCCESS;
222
223   if ((jsc==NULL) ||
224       (!json_is_string(jsc)) ||
225       (NULL==(shared=json_string_value(jsc)))) {
226     *rc=TR_CFG_BAD_PARAMS;
227     return -1;
228   }
229
230   if (0==strcmp(shared, "no"))
231     return 0;
232   else if (0==strcmp(shared, "yes"))
233     return 1;
234
235   /* any other value is an error */
236   tr_debug("tr_cfg_parse_shared_config: invalid shared_config value \"%s\" (should be \"yes\" or \"no\")",
237            shared);
238   *rc=TR_CFG_NOPARSE;
239   return -1;
240 }
241
242 static TR_REALM_ORIGIN tr_cfg_realm_origin(json_t *jrealm)
243 {
244   json_t *jremote=json_object_get(jrealm, "remote");
245   const char *s=NULL;
246
247   if (jremote==NULL)
248     return TR_REALM_LOCAL;
249   if (!json_is_string(jremote)) {
250     tr_warning("tr_cfg_realm_origin: \"remote\" is not a string, assuming this is a local realm.");
251     return TR_REALM_LOCAL;
252   }
253   s=json_string_value(jremote);
254   if (strcasecmp(s, "yes")==0)
255     return TR_REALM_REMOTE_INCOMPLETE;
256   else if (strcasecmp(s, "no")!=0)
257     tr_warning("tr_cfg_realm_origin: \"remote\" is neither 'yes' nor 'no', assuming this is a local realm.");
258
259   return TR_REALM_LOCAL;
260 }
261
262 /* Parse the identity provider object from a realm and fill in the given TR_IDP_REALM. */
263 static TR_CFG_RC tr_cfg_parse_idp(TR_IDP_REALM *idp, json_t *jidp)
264 {
265   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
266   TR_APC *apcs=NULL;
267   TR_AAA_SERVER *aaa=NULL;
268   TR_CFG_RC rc=TR_CFG_ERROR;
269
270   if (jidp==NULL)
271     goto cleanup;
272
273   idp->shared_config=tr_cfg_parse_shared_config(json_object_get(jidp, "shared_config"), &rc);
274   if (rc!=TR_CFG_SUCCESS) {
275     tr_err("tr_cfg_parse_idp: missing or malformed shared_config specification");
276     rc=TR_CFG_NOPARSE;
277     goto cleanup;
278   }
279
280   apcs=tr_cfg_parse_apcs(tmp_ctx, json_object_get(jidp, "apcs"), &rc);
281   if ((rc!=TR_CFG_SUCCESS) || (apcs==NULL)) {
282     tr_err("tr_cfg_parse_idp: unable to parse APC");
283     rc=TR_CFG_NOPARSE;
284     goto cleanup;
285   }
286
287   aaa=tr_cfg_parse_aaa_servers(idp, json_object_get(jidp, "aaa_servers"), &rc);
288   if (rc!=TR_CFG_SUCCESS) {
289     tr_err("tr_cfg_parse_idp: unable to parse AAA servers");
290     rc=TR_CFG_NOPARSE;
291     goto cleanup;
292   }
293
294   tr_debug("tr_cfg_parse_idp: APC=\"%.*s\"",
295            apcs->id->len,
296            apcs->id->buf);
297
298   /* done, fill in the idp structures */
299   idp->apcs=apcs;
300   talloc_steal(idp, apcs);
301   idp->aaa_servers=aaa;
302   rc=TR_CFG_SUCCESS;
303
304 cleanup:
305   if (rc!=TR_CFG_SUCCESS) {
306     if (apcs!=NULL)
307       tr_apc_free(apcs);
308     if (aaa!=NULL)
309       tr_aaa_server_free(aaa);
310   }
311
312   talloc_free(tmp_ctx);
313   return rc;
314 }
315
316 /* parses idp realm */
317 static TR_IDP_REALM *tr_cfg_parse_one_idp_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
318 {
319   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
320   TR_IDP_REALM *realm=NULL;
321   TR_CFG_RC call_rc=TR_CFG_ERROR;
322
323   *rc=TR_CFG_ERROR; /* default to error if not set */
324
325   if ((!jrealm) || (!rc)) {
326     tr_err("tr_cfg_parse_one_idp_realm: Bad parameters.");
327     if (rc)
328       *rc=TR_CFG_BAD_PARAMS;
329     goto cleanup;
330   }
331
332   if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
333     tr_err("tr_cfg_parse_one_idp_realm: could not allocate idp realm.");
334     *rc=TR_CFG_NOMEM;
335     goto cleanup;
336   }
337
338   realm->origin=tr_cfg_realm_origin(jrealm);
339   if (realm->origin!=TR_REALM_LOCAL) {
340     tr_debug("tr_cfg_parse_one_idp_realm: realm is remote, should not have full IdP info.");
341     *rc=TR_CFG_NOPARSE;
342     goto cleanup;
343   }
344
345   /* must have a name */
346   realm->realm_id=tr_cfg_parse_name(realm,
347                                     json_object_get(jrealm, "realm"),
348                                     &call_rc);
349   if ((call_rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
350     tr_err("tr_cfg_parse_one_idp_realm: could not parse realm name");
351     *rc=TR_CFG_NOPARSE;
352     goto cleanup;
353   }
354   tr_debug("tr_cfg_parse_one_idp_realm: realm_id=\"%.*s\"",
355            realm->realm_id->len,
356            realm->realm_id->buf);
357
358   call_rc=tr_cfg_parse_idp(realm, json_object_get(jrealm, "identity_provider"));
359   if (call_rc!=TR_CFG_SUCCESS) {
360     tr_err("tr_cfg_parse_one_idp_realm: could not parse identity_provider.");
361     *rc=TR_CFG_NOPARSE;
362     goto cleanup;
363   }
364
365   *rc=TR_CFG_SUCCESS;
366
367 cleanup:
368   if (*rc==TR_CFG_SUCCESS)
369     talloc_steal(mem_ctx, realm);
370   else {
371     talloc_free(realm);
372     realm=NULL;
373   }
374
375   talloc_free(tmp_ctx);
376   return realm;
377 }
378
379 /* Determine whether the realm is an IDP realm */
380 static int tr_cfg_is_idp_realm(json_t *jrealm)
381 {
382   /* If a realm spec contains an identity_provider, it's an IDP realm. */
383   if (NULL != json_object_get(jrealm, "identity_provider"))
384     return 1;
385   else
386     return 0;
387 }
388
389 static TR_IDP_REALM *tr_cfg_parse_one_remote_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
390 {
391   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
392   TR_IDP_REALM *realm=talloc(mem_ctx, TR_IDP_REALM);
393
394   *rc=TR_CFG_ERROR; /* default to error if not set */
395
396   if ((!jrealm) || (!rc)) {
397     tr_err("tr_cfg_parse_one_remote_realm: Bad parameters.");
398     if (rc)
399       *rc=TR_CFG_BAD_PARAMS;
400     goto cleanup;
401   }
402
403   if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
404     tr_err("tr_cfg_parse_one_remote_realm: could not allocate idp realm.");
405     *rc=TR_CFG_NOMEM;
406     goto cleanup;
407   }
408
409   /* must have a name */
410   realm->realm_id=tr_cfg_parse_name(realm,
411                                     json_object_get(jrealm, "realm"),
412                                     rc);
413   if ((*rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
414     tr_err("tr_cfg_parse_one_remote_realm: could not parse realm name");
415     *rc=TR_CFG_NOPARSE;
416     goto cleanup;
417   }
418   tr_debug("tr_cfg_parse_one_remote_realm: realm_id=\"%.*s\"",
419            realm->realm_id->len,
420            realm->realm_id->buf);
421
422   realm->origin=tr_cfg_realm_origin(jrealm);
423   *rc=TR_CFG_SUCCESS;
424
425 cleanup:
426   if (*rc==TR_CFG_SUCCESS)
427     talloc_steal(mem_ctx, realm);
428   else {
429     talloc_free(realm);
430     realm=NULL;
431   }
432
433   talloc_free(tmp_ctx);
434   return realm;
435 }
436
437 static int tr_cfg_is_remote_realm(json_t *jrealm)
438 {
439   return (tr_cfg_realm_origin(jrealm)!=TR_REALM_LOCAL);
440 }
441
442 /* Parse any idp realms in the j_realms object. Ignores other realm types. */
443 TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc)
444 {
445   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
446   TR_IDP_REALM *realms=NULL;
447   TR_IDP_REALM *new_realm=NULL;
448   json_t *this_jrealm=NULL;
449   int ii=0;
450
451   *rc=TR_CFG_ERROR;
452   if ((jrealms==NULL) || (!json_is_array(jrealms))) {
453     tr_err("tr_cfg_parse_idp_realms: realms not an array");
454     *rc=TR_CFG_BAD_PARAMS;
455     goto cleanup;
456   }
457
458   for (ii=0; ii<json_array_size(jrealms); ii++) {
459     this_jrealm=json_array_get(jrealms, ii);
460     if (tr_cfg_is_idp_realm(this_jrealm)) {
461       new_realm=tr_cfg_parse_one_idp_realm(tmp_ctx, this_jrealm, rc);
462       if ((*rc)!=TR_CFG_SUCCESS) {
463         tr_err("tr_cfg_parse_idp_realms: error decoding realm entry %d", ii+1);
464         *rc=TR_CFG_NOPARSE;
465         goto cleanup;
466       }
467       tr_idp_realm_add(realms, new_realm);
468     } else if (tr_cfg_is_remote_realm(this_jrealm)) {
469       new_realm=tr_cfg_parse_one_remote_realm(tmp_ctx, this_jrealm, rc);
470       if ((*rc)!=TR_CFG_SUCCESS) {
471         tr_err("tr_cfg_parse_idp_realms: error decoding remote realm entry %d", ii+1);
472         *rc=TR_CFG_NOPARSE;
473         goto cleanup;
474       }
475       tr_idp_realm_add(realms, new_realm);
476     }
477   }
478
479   *rc=TR_CFG_SUCCESS;
480   talloc_steal(mem_ctx, realms);
481
482 cleanup:
483   talloc_free(tmp_ctx);
484   return realms;
485 }