From: Jennifer Richards Date: Thu, 3 May 2018 20:50:54 +0000 (-0400) Subject: Merge branch 'milestone/monitoring' into jennifer/request_id X-Git-Tag: 3.4.0~1^2~38^2~1 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=trust_router.git;a=commitdiff_plain;h=3c5fb17459ff56d5e23cea059503f46a42150a1e;hp=6e1647f2f4714d8c0c4c27ef376302bab1ae4bb3 Merge branch 'milestone/monitoring' into jennifer/request_id # Conflicts: # include/trust_router/tid.h # tid/tidc.c # tr/tr_tid.c --- diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4ae8dba --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,114 @@ +# Rudimentary CMakeLists.txt +# +# This is not used for real builds, it mostly exists to enable code navigation in +# CLion. Real builds use autotools + make. +# +cmake_minimum_required(VERSION 3.6) +project(trust_router) + +set(CMAKE_CXX_STANDARD 11) + +include(FindPkgConfig) +pkg_check_modules(GLIB glib-2.0 REQUIRED) +include_directories(${GLIB_INCLUDE_DIRS}) + +include_directories(include) + +# Fill these in so CLion doesn't complain. The real versions are set in the Makefile +add_definitions(-DPACKAGE_VERSION="built-with-cmake" + -DPACKAGE_NAME="Moonshot Trust Router" + -DPACKAGE_BUGREPORT="bugs@painless-security.com") + +set(SOURCE_FILES + common/tests/cfg_test.c + common/tests/commtest.c + common/tests/dh_test.c + common/tests/mq_test.c + common/tests/thread_test.c + common/jansson_iterators.h + common/t_constraint.c + common/tr_apc.c + common/tr_comm.c + common/tr_config.c + common/tr_constraint.c + common/tr_debug.c + common/tr_dh.c + common/tr_filter.c + common/tr_gss_names.c + common/tr_idp.c + common/tr_mq.c + common/tr_msg.c + common/tr_name.c + common/tr_rp.c + common/tr_util.c + gsscon/test/gsscon_client.c + gsscon/test/gsscon_server.c + gsscon/gsscon_active.c + gsscon/gsscon_common.c + gsscon/gsscon_passive.c + include/trust_router/tid.h + include/trust_router/tr_constraint.h + include/trust_router/tr_dh.h + include/trust_router/tr_name.h + include/trust_router/tr_versioning.h + include/trust_router/trp.h + include/gsscon.h + include/tid_internal.h + include/tr.h + include/tr_apc.h + include/tr_cfgwatch.h + include/tr_comm.h + include/tr_config.h + include/tr_debug.h + include/tr_event.h + include/tr_filter.h + include/tr_gss_names.h + include/tr_idp.h + include/tr_mq.h + include/tr_msg.h + include/tr_rp.h + include/tr_tid.h + include/tr_trp.h + include/tr_util.h + include/trp_internal.h + include/trp_ptable.h + include/trp_rtable.h + tid/example/tidc_main.c + tid/example/tids_main.c + tid/tid_req.c + tid/tid_resp.c + tid/tidc.c + tid/tids.c + tr/tr.c + tr/tr_cfgwatch.c + tr/tr_event.c + tr/tr_main.c + tr/tr_tid.c + tr/tr_trp.c + tr/trpc_main.c + trp/test/ptbl_test.c + trp/test/rtbl_test.c + trp/msgtst.c + trp/trp_conn.c + trp/trp_ptable.c + trp/trp_req.c + trp/trp_rtable.c + trp/trp_upd.c + trp/trpc.c + trp/trps.c include/tr_name_internal.h mon/mon_req.c mon/mon_req_encode.c mon/mon_req_decode.c + mon/mon_resp.c mon/mon_common.c mon/mon_resp_encode.c mon/mon_resp_decode.c tr/tr_mon.c mon/mons.c include/tr_socket.h common/tr_gss.c include/tr_gss.h common/tr_config_internal.c mon/mons_handlers.c include/mons_handlers.h tr/tr_tid_mons.c tr/tr_tid_mons.c trp/trp_route.c include/trp_route.h trp/trp_rtable_encoders.c trp/trp_route_encoders.c trp/trp_peer.c include/trp_peer.h trp/trp_peer_encoders.c trp/trp_ptable_encoders.c common/tr_idp_encoders.c common/tr_comm_encoders.c common/tr_rp_client.c include/tr_rp_client.h common/tr_rp_client_encoders.c common/tr_filter_encoders.c common/tr_config_encoders.c common/tr_config_filters.c common/tr_config_realms.c common/tr_config_rp_clients.c common/tr_config_orgs.c common/tr_config_comms.c) + +# Does not actually build! +add_executable(trust_router ${SOURCE_FILES}) + +add_executable(trmon mon/monc.c tr/trmon_main.c common/tr_gss_client.c include/tr_gss_client.h) + +# Test build targets - for debugging +add_executable(test_mon_req_encode mon/mon_common.c mon/mon_req.c mon/tests/test_mon_req_encode.c mon/mon_req_encode.c) +target_link_libraries(test_mon_req_encode jansson talloc glib-2.0) + +add_executable(test_mon_req_decode mon/mon_common.c mon/mon_req.c mon/tests/test_mon_req_decode.c mon/mon_req_decode.c) +target_link_libraries(test_mon_req_decode jansson talloc glib-2.0) + +add_executable(test_mon_resp_encode mon/mon_common.c mon/mon_req.c mon/mon_resp.c mon/mon_resp_encode.c common/tr_name.c mon/tests/test_mon_resp_encode.c) +target_link_libraries(test_mon_resp_encode jansson talloc glib-2.0) diff --git a/Makefile.am b/Makefile.am index fbc763c..1231793 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,8 +1,10 @@ +ACLOCAL_AMFLAGS = -I m4 DISTCHECK_CONFIGURE_FLAGS = \ --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) bin_PROGRAMS= tr/trust_router tr/trpc tid/example/tidc tid/example/tids common/tests/tr_dh_test common/tests/mq_test \ common/tests/thread_test trp/msgtst trp/test/rtbl_test trp/test/ptbl_test common/tests/cfg_test \ - common/tests/commtest common/tests/name_test common/tests/filt_test + common/tests/commtest common/tests/name_test common/tests/filt_test mon/tests/test_mon_req_encode \ + mon/tests/test_mon_req_decode mon/tests/test_mon_resp_encode tr/trmon AM_CPPFLAGS=-I$(srcdir)/include $(GLIB_CFLAGS) AM_CFLAGS = -Wall -Werror=missing-prototypes -Werror -Wno-parentheses $(GLIB_CFLAGS) SUBDIRS = gsscon @@ -15,10 +17,17 @@ common_srcs = common/tr_name.c \ common/tr_util.c \ common/tr_apc.c \ common/tr_comm.c \ + common/tr_comm_encoders.c \ common/tr_rp.c \ + common/tr_rp_client.c \ + common/tr_rp_client_encoders.c \ common/tr_idp.c \ + common/tr_idp_encoders.c \ common/tr_filter.c \ - common/tr_gss.c + common/tr_filter_encoders.c \ + common/tr_gss_names.c \ + common/tr_socket.c \ + $(mon_srcs) tid_srcs = tid/tid_resp.c \ tid/tid_req.c \ @@ -29,12 +38,44 @@ common/tr_rand_id.c trp_srcs = trp/trp_conn.c \ trp/trps.c \ trp/trpc.c \ +trp/trp_peer.c \ +trp/trp_peer_encoders.c \ trp/trp_ptable.c \ +trp/trp_ptable_encoders.c \ +trp/trp_route.c \ +trp/trp_route_encoders.c \ trp/trp_rtable.c \ +trp/trp_rtable_encoders.c \ trp/trp_req.c \ trp/trp_upd.c \ -common/tr_config.c \ -common/tr_mq.c +common/tr_mq.c \ +$(config_srcs) + +# configuration parsing sources +config_srcs = \ + common/tr_config.c \ + common/tr_config_comms.c \ + common/tr_config_encoders.c \ + common/tr_config_filters.c \ + common/tr_config_internal.c \ + common/tr_config_orgs.c \ + common/tr_config_realms.c \ + common/tr_config_rp_clients.c + +# general monitoring message sources +mon_srcs = \ + mon/mon_common.c \ + mon/mon_req.c \ + mon/mon_req_encode.c \ + mon/mon_req_decode.c \ + mon/mon_resp.c \ + mon/mon_resp_decode.c \ + mon/mon_resp_encode.c + +# monitoring server sources +mons_srcs = \ + mon/mons.c \ + mon/mons_handlers.c check_PROGRAMS = common/t_constraint TESTS = common/t_constraint @@ -68,15 +109,24 @@ tr/tr.c \ tr/tr_event.c \ tr/tr_cfgwatch.c \ tr/tr_tid.c \ +tr/tr_tid_mons.c \ tr/tr_trp.c \ +tr/tr_trp_mons.c \ +tr/tr_mon.c \ +common/tr_gss.c \ +common/tr_gss_client.c \ $(tid_srcs) \ $(trp_srcs) \ -$(common_srcs) +$(common_srcs) \ +$(mons_srcs) tr_trust_router_LDFLAGS = $(AM_LDFLAGS) -levent_pthreads -pthread tr_trust_router_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) + tr_trpc_SOURCES =tr/trpc_main.c \ tr/tr_trp.c \ +common/tr_gss.c \ +common/tr_gss_client.c \ $(trp_srcs) \ $(tid_srcs) \ $(common_srcs) @@ -84,6 +134,17 @@ $(common_srcs) tr_trpc_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) tr_trpc_LDFLAGS = $(AM_LDFLAGS) -pthread +tr_trmon_SOURCES = tr/trmon_main.c \ +common/tr_gss.c \ +common/tr_gss_client.c \ +$(tid_srcs) \ +$(trp_srcs) \ +$(common_srcs) \ +mon/monc.c + +tr_trmon_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) +tr_trmon_LDFLAGS = $(AM_LDFLAGS) -pthread + trp_msgtst_SOURCES = trp/msgtst.c \ $(common_srcs) \ common/tr_rand_id.c \ @@ -95,12 +156,18 @@ trp_msgtst_LDADD = $(GLIB_LIBS) trp_test_rtbl_test_SOURCES = trp/test/rtbl_test.c \ common/tr_name.c \ -common/tr_gss.c \ +common/tr_gss_names.c \ common/tr_debug.c \ -trp/trp_rtable.c +common/tr_util.c \ +trp/trp_route.c \ +trp/trp_route_encoders.c \ +trp/trp_rtable.c \ +trp/trp_rtable_encoders.c trp_test_rtbl_test_LDADD = $(GLIB_LIBS) trp_test_ptbl_test_SOURCES = trp/test/ptbl_test.c \ +common/tr_gss.c \ +common/tr_gss_client.c \ $(tid_srcs) \ $(trp_srcs) \ $(common_srcs) @@ -108,6 +175,8 @@ trp_test_ptbl_test_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) trp_test_ptbl_test_LDFLAGS = $(AM_LDFLAGS) -pthread tid_example_tidc_SOURCES = tid/example/tidc_main.c \ +common/tr_gss.c \ +common/tr_gss_client.c \ $(tid_srcs) \ $(trp_srcs) \ $(common_srcs) @@ -115,6 +184,8 @@ tid_example_tidc_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) tid_example_tidc_LDFLAGS = $(AM_LDFLAGS) -pthread tid_example_tids_SOURCES = tid/example/tids_main.c \ +common/tr_gss.c \ +common/tr_gss_client.c \ $(tid_srcs) \ $(trp_srcs) \ $(common_srcs) @@ -133,6 +204,8 @@ common_tests_mq_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread common_tests_cfg_test_SOURCES = common/tests/cfg_test.c \ $(common_srcs) \ +common/tr_gss.c \ +common/tr_gss_client.c \ $(tid_srcs) \ $(trp_srcs) common_tests_cfg_test_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) @@ -144,6 +217,8 @@ common/tests/thread_test.c common_tests_commtest_SOURCES = common/tests/commtest.c \ $(common_srcs) \ +common/tr_gss.c \ +common/tr_gss_client.c \ $(tid_srcs) \ $(trp_srcs) common_tests_commtest_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) @@ -153,6 +228,8 @@ common_tests_thread_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread common_tests_name_test_SOURCES = common/tests/name_test.c \ $(common_srcs) \ + common/tr_gss.c \ + common/tr_gss_client.c \ $(tid_srcs) \ $(trp_srcs) common_tests_name_test_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) @@ -161,12 +238,32 @@ common_tests_name_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) common_tests_filt_test_SOURCES = common/tests/filt_test.c \ $(common_srcs) \ + common/tr_gss.c \ + common/tr_gss_client.c \ $(tid_srcs) \ $(trp_srcs) common_tests_filt_test_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) common_tests_filt_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread common_tests_filt_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) +mon_tests_test_mon_req_encode_SOURCES = mon/tests/test_mon_req_encode.c \ + $(mon_srcs) \ + common/tr_name.c +mon_tests_test_mon_req_encode_LDADD = $(GLIB_LIBS) +mon_tests_test_mon_req_encode_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + +mon_tests_test_mon_req_decode_SOURCES = mon/tests/test_mon_req_decode.c \ + $(mon_srcs) \ + common/tr_name.c +mon_tests_test_mon_req_decode_LDADD = $(GLIB_LIBS) +mon_tests_test_mon_req_decode_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + +mon_tests_test_mon_resp_encode_SOURCES = mon/tests/test_mon_resp_encode.c \ + $(mon_srcs) \ + common/tr_name.c +mon_tests_test_mon_resp_encode_LDADD = $(GLIB_LIBS) +mon_tests_test_mon_resp_encode_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + pkginclude_HEADERS = include/trust_router/tid.h include/trust_router/tr_name.h \ include/tr_debug.h include/trust_router/trp.h \ include/trust_router/tr_dh.h \ @@ -177,13 +274,13 @@ noinst_HEADERS = include/gsscon.h include/tr_config.h \ include/tr_msg.h include/tr.h \ include/tr_idp.h include/tr_rp.h \ include/tr_comm.h include/tr_apc.h \ - include/tr_tid.h include/tr_trp.h \ - include/tr_filter.h include/tr_gss.h \ - include/tid_internal.h include/trp_internal.h \ + include/tr_tid.h include/tr_trp.h include/tr_mon.h \ + include/tr_filter.h include/tr_gss_names.h \ + include/tid_internal.h include/trp_internal.h include/trp_route.h \ include/tr_cfgwatch.h include/tr_event.h \ - include/tr_mq.h include/trp_ptable.h \ + include/tr_mq.h include/trp_peer.h include/trp_ptable.h \ include/trp_rtable.h include/tr_util.h \ - include/tr_name_internal.h + include/tr_name_internal.h include/tr_gss.h pkgdata_DATA=schema.sql nobase_dist_pkgdata_DATA=redhat/init redhat/sysconfig redhat/organizations.cfg redhat/tidc-wrapper redhat/trust_router-wrapper redhat/tr-test-internal.cfg redhat/default-internal.cfg redhat/tids-wrapper redhat/sysconfig.tids diff --git a/common/tests/filt_test.c b/common/tests/filt_test.c index d7dfe06..342c27b 100644 --- a/common/tests/filt_test.c +++ b/common/tests/filt_test.c @@ -119,7 +119,7 @@ TRP_INFOREC *load_inforec(const char *fname) json_decref(decoded); assert(encoded); - assert(msg=tr_msg_decode(encoded, strlen(encoded))); + assert(msg= tr_msg_decode(NULL, encoded, strlen(encoded))); assert(upd=tr_msg_get_trp_upd(msg)); assert(inforec=trp_upd_get_inforec(upd)); /* now remove the inforec from the update context */ @@ -146,7 +146,7 @@ TID_REQ *load_tid_req(const char *fname) msglen=fread(msgbuf, 1, MAX_FILE_SIZE, f); assert(msglen); assert(feof(f)); - msg=tr_msg_decode(msgbuf, msglen); + msg= tr_msg_decode(NULL, msgbuf, msglen); free(msgbuf); msgbuf=NULL; diff --git a/common/tr_comm.c b/common/tr_comm.c index dd3c304..f8f91df 100644 --- a/common/tr_comm.c +++ b/common/tr_comm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, JANET(UK) + * Copyright (c) 2012-2018, JANET(UK) * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/common/tr_comm_encoders.c b/common/tr_comm_encoders.c new file mode 100644 index 0000000..c33c604 --- /dev/null +++ b/common/tr_comm_encoders.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2012-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include + +static json_t *expiry_to_json_string(TR_COMM_MEMB *memb) +{ + struct timespec ts_zero = {0, 0}; + char *s = NULL; + json_t *jstr = NULL; + + if (tr_cmp_timespec(tr_comm_memb_get_expiry(memb), &ts_zero) == 0) { + s = strdup(""); + } else { + s = timespec_to_str(tr_comm_memb_get_expiry(memb)); + } + + if (s) { + jstr = json_string(s); + free(s); + } + + return jstr; +} + +/** + * Get the provenance from the member, handling empty provenance safely + */ +static json_t *provenance_to_json(TR_COMM_MEMB *memb) +{ + json_t *prov = tr_comm_memb_get_provenance(memb); + + if (prov) { + json_incref(prov); + return prov; + } else { + return json_array(); + } +} + +/* helper for below */ +#define OBJECT_SET_OR_FAIL(jobj, key, val) \ +do { \ + if (val) \ + json_object_set_new((jobj),(key),(val)); \ + else \ + goto cleanup; \ +} while (0) + +#define ARRAY_APPEND_OR_FAIL(jary, val) \ +do { \ + if (val) \ + json_array_append_new((jary),(val)); \ + else \ + goto cleanup; \ +} while (0) + +static json_t *tr_comm_memb_to_json(TR_COMM_MEMB *memb) +{ + json_t *memb_json = NULL; + json_t *retval = NULL; + + memb_json = json_object(); + if (memb_json == NULL) + goto cleanup; + + if (tr_comm_memb_get_origin(memb) == NULL) { + OBJECT_SET_OR_FAIL(memb_json, "origin", json_string("file")); + } else { + OBJECT_SET_OR_FAIL(memb_json, "origin", + tr_name_to_json_string(tr_comm_memb_get_origin(memb))); + OBJECT_SET_OR_FAIL(memb_json, "provenance", + provenance_to_json(memb)); + OBJECT_SET_OR_FAIL(memb_json, "expires", + expiry_to_json_string(memb)); + OBJECT_SET_OR_FAIL(memb_json, "announce_interval", + json_integer(tr_comm_memb_get_interval(memb))); + OBJECT_SET_OR_FAIL(memb_json, "times_expired", + json_integer(tr_comm_memb_get_times_expired(memb))); + } + + /* succeeded - set the return value and increment the reference count */ + retval = memb_json; + json_incref(retval); + +cleanup: + if (memb_json) + json_decref(memb_json); + return retval; +} + +/** + * Summarize the different reasons we believe a realm belongs to a community + */ +static json_t *tr_comm_memb_sources_to_json(TR_COMM_MEMB *first_memb) +{ + json_t *jarray = NULL; + json_t *retval = NULL; + TR_COMM_ITER *iter = NULL; + TR_COMM_MEMB *memb = NULL; + + jarray = json_array(); + if (jarray == NULL) + goto cleanup; + + iter = tr_comm_iter_new(NULL); + if (iter == NULL) + goto cleanup; + + /* Iterate over all the memberships for this realm/comm pair that come from different origins */ + memb = tr_comm_memb_iter_first(iter, first_memb); + while (memb) { + ARRAY_APPEND_OR_FAIL(jarray, tr_comm_memb_to_json(memb)); + memb = tr_comm_memb_iter_next(iter); + } + + /* success */ + retval = jarray; + json_incref(retval); + +cleanup: + if (jarray) + json_decref(jarray); + if (iter) + talloc_free(iter); + return retval; +} + +static json_t *tr_comm_realms_to_json(TR_COMM_TABLE *ctable, TR_NAME *comm_name, TR_REALM_ROLE role) +{ + json_t *jarray = json_array(); + json_t *realm_json = NULL; + json_t *retval = NULL; + TR_COMM_ITER *iter = NULL; + TR_REALM *realm = NULL; + TR_COMM_MEMB *memb = NULL; + + iter = tr_comm_iter_new(NULL); + realm = tr_realm_iter_first(iter, ctable, comm_name); + + /* Do not display the full realm json here, only the name and info relevant to the community listing */ + while(realm) { + if (realm->role == role) { + realm_json = json_object(); + OBJECT_SET_OR_FAIL(realm_json, "realm", + tr_name_to_json_string(tr_realm_get_id(realm))); + memb = tr_comm_table_find_memb(ctable, + tr_realm_get_id(realm), + comm_name); + if (memb == NULL) { + /* This should not happen - there must be a matching membership if we + * believed the realm was in the community in the first place! */ + goto cleanup; + } + OBJECT_SET_OR_FAIL(realm_json, "sources", + tr_comm_memb_sources_to_json(memb)); + json_array_append_new(jarray, realm_json); + realm_json = NULL; /* so we don't free this twice during cleanup */ + } + realm = tr_realm_iter_next(iter); + } + + /* Success - increment the reference count so return value survives */ + retval = jarray; + json_incref(retval); + +cleanup: + if (jarray) + json_decref(jarray); + + if (realm_json) + json_decref(realm_json); + + if (iter) + tr_comm_iter_free(iter); + + return retval; +} + +static json_t *tr_comm_to_json(TR_COMM_TABLE *ctable, TR_COMM *comm) +{ + json_t *comm_json = NULL; + json_t *retval = NULL; + + comm_json = json_object(); + if (comm_json == NULL) + goto cleanup; + + OBJECT_SET_OR_FAIL(comm_json, "type", + json_string(tr_comm_type_to_str(tr_comm_get_type(comm)))); + if (tr_comm_get_type(comm) == TR_COMM_APC) { + OBJECT_SET_OR_FAIL(comm_json, "expiration_interval", + json_integer(comm->expiration_interval)); + } else { + /* just get the first apc */ + OBJECT_SET_OR_FAIL(comm_json, "apc", + tr_name_to_json_string( + tr_apc_get_id( + tr_comm_get_apcs(comm)))); + } + OBJECT_SET_OR_FAIL(comm_json, "name", + tr_name_to_json_string(tr_comm_get_id(comm))); + if (tr_comm_get_owner_realm(comm)) { + OBJECT_SET_OR_FAIL(comm_json, "owner_realm", + tr_name_to_json_string(tr_comm_get_owner_realm(comm))); + } + if (tr_comm_get_owner_contact(comm)) { + OBJECT_SET_OR_FAIL(comm_json, "owner_contact", + tr_name_to_json_string(tr_comm_get_owner_contact(comm))); + } + OBJECT_SET_OR_FAIL(comm_json, "idp_realms", + tr_comm_realms_to_json(ctable, tr_comm_get_id(comm), TR_ROLE_IDP)); + OBJECT_SET_OR_FAIL(comm_json, "rp_realms", + tr_comm_realms_to_json(ctable, tr_comm_get_id(comm), TR_ROLE_RP)); + + /* succeeded - set the return value and increment the reference count */ + retval = comm_json; + json_incref(retval); + + cleanup: + if (comm_json) + json_decref(comm_json); + return retval; +} + +json_t *tr_comm_table_to_json(TR_COMM_TABLE *ctable) +{ + json_t *ctable_json = NULL; + json_t *retval = NULL; + json_t *comm_json = NULL; + TR_COMM_ITER *iter = NULL; + TR_COMM *comm = NULL; + + ctable_json = json_array(); + if (ctable_json == NULL) + goto cleanup; + + iter = tr_comm_iter_new(NULL); + if (iter == NULL) + goto cleanup; + + /* Iterate over communities in the table */ + comm = tr_comm_table_iter_first(iter, ctable); + while (comm) { + comm_json = tr_comm_to_json(ctable, comm); + + if (comm_json == NULL) + goto cleanup; + + json_array_append_new(ctable_json, comm_json); + comm = tr_comm_table_iter_next(iter); + } + + /* succeeded - set the return value and increment the reference count */ + retval = ctable_json; + json_incref(retval); + +cleanup: + if (iter) + tr_comm_iter_free(iter); + + if (ctable_json) + json_decref(ctable_json); + + return retval; + +} \ No newline at end of file diff --git a/common/tr_config.c b/common/tr_config.c index c1fc6d9..8c0f577 100644 --- a/common/tr_config.c +++ b/common/tr_config.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, JANET(UK) + * Copyright (c) 2012-2018, JANET(UK) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include #include @@ -125,1814 +125,68 @@ void tr_print_comm_rps(TR_COMM_TABLE *ctab, TR_COMM *comm) talloc_free(tmp_ctx); } -TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx) -{ - TR_CFG *cfg=talloc(mem_ctx, TR_CFG); - if (cfg!=NULL) { - cfg->internal=NULL; - cfg->rp_clients=NULL; - cfg->peers=NULL; - cfg->default_servers=NULL; - cfg->ctable=tr_comm_table_new(cfg); - if (cfg->ctable==NULL) { - talloc_free(cfg); - cfg=NULL; - } - } - return cfg; -} - -void tr_cfg_free (TR_CFG *cfg) -{ - talloc_free(cfg); -} - -TR_CFG_MGR *tr_cfg_mgr_new(TALLOC_CTX *mem_ctx) -{ - return talloc_zero(mem_ctx, TR_CFG_MGR); -} - -void tr_cfg_mgr_free (TR_CFG_MGR *cfg_mgr) { - talloc_free(cfg_mgr); -} - -TR_CFG_RC tr_apply_new_config (TR_CFG_MGR *cfg_mgr) -{ - /* cfg_mgr->active is allowed to be null, but new cannot be */ - if ((cfg_mgr==NULL) || (cfg_mgr->new==NULL)) - return TR_CFG_BAD_PARAMS; - - if (cfg_mgr->active != NULL) - tr_cfg_free(cfg_mgr->active); - - cfg_mgr->active = cfg_mgr->new; - cfg_mgr->new=NULL; /* only keep a single handle on the new configuration */ - - tr_log_threshold(cfg_mgr->active->internal->log_threshold); - tr_console_threshold(cfg_mgr->active->internal->console_threshold); - - return TR_CFG_SUCCESS; -} - -static TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jcfg) -{ - json_t *jint = NULL; - json_t *jmtd = NULL; - json_t *jtidsp = NULL; - json_t *jtrpsp = NULL; - json_t *jhname = NULL; - json_t *jlog = NULL; - json_t *jconthres = NULL; - json_t *jlogthres = NULL; - json_t *jcfgpoll = NULL; - json_t *jcfgsettle = NULL; - json_t *jroutesweep = NULL; - json_t *jrouteupdate = NULL; - json_t *jtidreq_timeout = NULL; - json_t *jtidresp_numer = NULL; - json_t *jtidresp_denom = NULL; - json_t *jrouteconnect = NULL; - - if ((!trc) || (!jcfg)) - return TR_CFG_BAD_PARAMS; - - if (NULL == trc->internal) { - if (NULL == (trc->internal = talloc_zero(trc, TR_CFG_INTERNAL))) - return TR_CFG_NOMEM; - } - - if (NULL != (jint = json_object_get(jcfg, "tr_internal"))) { - if (NULL != (jmtd = json_object_get(jint, "max_tree_depth"))) { - if (json_is_number(jmtd)) { - trc->internal->max_tree_depth = json_integer_value(jmtd); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, max_tree_depth is not a number."); - return TR_CFG_NOPARSE; - } - } else { - /* If not configured, use the default */ - trc->internal->max_tree_depth = TR_DEFAULT_MAX_TREE_DEPTH; - } - if (NULL != (jtidsp = json_object_get(jint, "tids_port"))) { - if (json_is_number(jtidsp)) { - trc->internal->tids_port = json_integer_value(jtidsp); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, tids_port is not a number."); - return TR_CFG_NOPARSE; - } - } else { - /* If not configured, use the default */ - trc->internal->tids_port = TR_DEFAULT_TIDS_PORT; - } - if (NULL != (jtrpsp = json_object_get(jint, "trps_port"))) { - if (json_is_number(jtrpsp)) { - trc->internal->trps_port = json_integer_value(jtrpsp); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, trps_port is not a number."); - return TR_CFG_NOPARSE; - } - } else { - /* If not configured, use the default */ - trc->internal->trps_port = TR_DEFAULT_TRPS_PORT; - } - if (NULL != (jhname = json_object_get(jint, "hostname"))) { - if (json_is_string(jhname)) { - trc->internal->hostname = talloc_strdup(trc->internal, json_string_value(jhname)); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, hostname is not a string."); - return TR_CFG_NOPARSE; - } - } - if (NULL != (jcfgpoll = json_object_get(jint, "cfg_poll_interval"))) { - if (json_is_number(jcfgpoll)) { - trc->internal->cfg_poll_interval = json_integer_value(jcfgpoll); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, cfg_poll_interval is not a number."); - return TR_CFG_NOPARSE; - } - } else { - trc->internal->cfg_poll_interval = TR_CFGWATCH_DEFAULT_POLL; - } - - if (NULL != (jcfgsettle = json_object_get(jint, "cfg_settling_time"))) { - if (json_is_number(jcfgsettle)) { - trc->internal->cfg_settling_time = json_integer_value(jcfgsettle); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, cfg_settling_time is not a number."); - return TR_CFG_NOPARSE; - } - } else { - trc->internal->cfg_settling_time = TR_CFGWATCH_DEFAULT_SETTLE; - } - - if (NULL != (jrouteconnect = json_object_get(jint, "trp_connect_interval"))) { - if (json_is_number(jrouteconnect)) { - trc->internal->trp_connect_interval = json_integer_value(jrouteconnect); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, trp_connect_interval is not a number."); - return TR_CFG_NOPARSE; - } - } else { - /* if not configured, use the default */ - trc->internal->trp_connect_interval=TR_DEFAULT_TRP_CONNECT_INTERVAL; - } - - if (NULL != (jroutesweep = json_object_get(jint, "trp_sweep_interval"))) { - if (json_is_number(jroutesweep)) { - trc->internal->trp_sweep_interval = json_integer_value(jroutesweep); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, trp_sweep_interval is not a number."); - return TR_CFG_NOPARSE; - } - } else { - /* if not configured, use the default */ - trc->internal->trp_sweep_interval=TR_DEFAULT_TRP_SWEEP_INTERVAL; - } - - if (NULL != (jrouteupdate = json_object_get(jint, "trp_update_interval"))) { - if (json_is_number(jrouteupdate)) { - trc->internal->trp_update_interval = json_integer_value(jrouteupdate); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, trp_update_interval is not a number."); - return TR_CFG_NOPARSE; - } - } else { - /* if not configured, use the default */ - trc->internal->trp_update_interval=TR_DEFAULT_TRP_UPDATE_INTERVAL; - } - - if (NULL != (jtidreq_timeout = json_object_get(jint, "tid_request_timeout"))) { - if (json_is_number(jtidreq_timeout)) { - trc->internal->tid_req_timeout = json_integer_value(jtidreq_timeout); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, tid_request_timeout is not a number."); - return TR_CFG_NOPARSE; - } - } else { - /* if not configured, use the default */ - trc->internal->tid_req_timeout=TR_DEFAULT_TID_REQ_TIMEOUT; - } - - if (NULL != (jtidresp_numer = json_object_get(jint, "tid_response_numerator"))) { - if (json_is_number(jtidresp_numer)) { - trc->internal->tid_resp_numer = json_integer_value(jtidresp_numer); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, tid_response_numerator is not a number."); - return TR_CFG_NOPARSE; - } - } else { - /* if not configured, use the default */ - trc->internal->tid_resp_numer=TR_DEFAULT_TID_RESP_NUMER; - } - - if (NULL != (jtidresp_denom = json_object_get(jint, "tid_response_denominator"))) { - if (json_is_number(jtidresp_denom)) { - trc->internal->tid_resp_denom = json_integer_value(jtidresp_denom); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, tid_response_denominator is not a number."); - return TR_CFG_NOPARSE; - } - } else { - /* if not configured, use the default */ - trc->internal->tid_resp_denom=TR_DEFAULT_TID_RESP_DENOM; - } - - if (NULL != (jlog = json_object_get(jint, "logging"))) { - if (NULL != (jlogthres = json_object_get(jlog, "log_threshold"))) { - if (json_is_string(jlogthres)) { - trc->internal->log_threshold = str2sev(json_string_value(jlogthres)); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, log_threshold is not a string."); - return TR_CFG_NOPARSE; - } - } else { - /* If not configured, use the default */ - trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD; - } - - if (NULL != (jconthres = json_object_get(jlog, "console_threshold"))) { - if (json_is_string(jconthres)) { - trc->internal->console_threshold = str2sev(json_string_value(jconthres)); - } else { - tr_debug("tr_cfg_parse_internal: Parsing error, console_threshold is not a string."); - return TR_CFG_NOPARSE; - } - } else { - /* If not configured, use the default */ - trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD; - } - } else { - /* If not configured, use the default */ - trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD; - trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD; - } - - tr_debug("tr_cfg_parse_internal: Internal config parsed."); - return TR_CFG_SUCCESS; - } - return TR_CFG_SUCCESS; -} - -static TR_CONSTRAINT *tr_cfg_parse_one_constraint(TALLOC_CTX *mem_ctx, char *ctype, json_t *jc, TR_CFG_RC *rc) -{ - TR_CONSTRAINT *cons=NULL; - int i=0; - - if ((!ctype) || (!jc) || (!rc) || - (!json_is_array(jc)) || - (0 >= json_array_size(jc)) || - (TR_MAX_CONST_MATCHES < json_array_size(jc)) || - (!json_is_string(json_array_get(jc, 0)))) { - tr_err("tr_cfg_parse_one_constraint: config error."); - *rc=TR_CFG_NOPARSE; - return NULL; - } - - if (NULL==(cons=tr_constraint_new(mem_ctx))) { - tr_debug("tr_cfg_parse_one_constraint: Out of memory (cons)."); - *rc=TR_CFG_NOMEM; - return NULL; - } - - if (NULL==(cons->type=tr_new_name(ctype))) { - tr_err("tr_cfg_parse_one_constraint: Out of memory (type)."); - *rc=TR_CFG_NOMEM; - tr_constraint_free(cons); - return NULL; - } - - for (i=0; i < json_array_size(jc); i++) { - cons->matches[i]=tr_new_name(json_string_value(json_array_get(jc, i))); - if (cons->matches[i]==NULL) { - tr_err("tr_cfg_parse_one_constraint: Out of memory (match %d).", i+1); - *rc=TR_CFG_NOMEM; - tr_constraint_free(cons); - return NULL; - } - } - - return cons; -} - -static TR_FILTER *tr_cfg_parse_one_filter(TALLOC_CTX *mem_ctx, json_t *jfilt, TR_FILTER_TYPE ftype, TR_CFG_RC *rc) -{ - TALLOC_CTX *tmp_ctx = talloc_new(NULL); - TR_FILTER *filt = NULL; - json_t *jfaction = NULL; - json_t *jfline = NULL; - json_t *jfspecs = NULL; - json_t *this_jfspec = NULL; - json_t *jfield = NULL; - json_t *jmatch = NULL; - json_t *jrc = NULL; - json_t *jdc = NULL; - json_t *this_jmatch = NULL; - TR_NAME *name = NULL; - size_t i = 0, j = 0, k = 0; - - *rc = TR_CFG_ERROR; - - if ((jfilt == NULL) || (rc == NULL)) { - tr_err("tr_cfg_parse_one_filter: null argument"); - *rc = TR_CFG_BAD_PARAMS; - goto cleanup; - } - - if (NULL == (filt = tr_filter_new(tmp_ctx))) { - tr_err("tr_cfg_parse_one_filter: Out of memory."); - *rc = TR_CFG_NOMEM; - goto cleanup; - } - tr_filter_set_type(filt, ftype); - - /* make sure we have space to represent the filter */ - if (json_array_size(jfilt) > TR_MAX_FILTER_LINES) { - tr_err("tr_cfg_parse_one_filter: Filter has too many lines, maximum of %d.", TR_MAX_FILTER_LINES); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - - /* For each entry in the filter... */ - json_array_foreach(jfilt, i, jfline) { - if ((NULL == (jfaction = json_object_get(jfline, "action"))) || - (!json_is_string(jfaction))) { - tr_debug("tr_cfg_parse_one_filter: Error parsing filter action."); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - - if ((NULL == (jfspecs = json_object_get(jfline, "specs"))) || - (!json_is_array(jfspecs)) || - (0 == json_array_size(jfspecs))) { - tr_debug("tr_cfg_parse_one_filter: Error parsing filter specs."); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - - if (TR_MAX_FILTER_SPECS < json_array_size(jfspecs)) { - tr_debug("tr_cfg_parse_one_filter: Filter has too many specs, maximimum of %d.", TR_MAX_FILTER_SPECS); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - - if (NULL == (filt->lines[i] = tr_fline_new(filt))) { - tr_debug("tr_cfg_parse_one_filter: Out of memory allocating filter line %d.", i + 1); - *rc = TR_CFG_NOMEM; - goto cleanup; - } - - if (!strcmp(json_string_value(jfaction), "accept")) { - filt->lines[i]->action = TR_FILTER_ACTION_ACCEPT; - } else if (!strcmp(json_string_value(jfaction), "reject")) { - filt->lines[i]->action = TR_FILTER_ACTION_REJECT; - } else { - tr_debug("tr_cfg_parse_one_filter: Error parsing filter action, unknown action' %s'.", - json_string_value(jfaction)); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - - if (NULL != (jrc = json_object_get(jfline, "realm_constraints"))) { - if (!json_is_array(jrc)) { - tr_err("tr_cfg_parse_one_filter: cannot parse realm_constraints, not an array."); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } else if (json_array_size(jrc) > TR_MAX_CONST_MATCHES) { - tr_err("tr_cfg_parse_one_filter: realm_constraints has too many entries, maximum of %d.", - TR_MAX_CONST_MATCHES); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } else if (json_array_size(jrc) > 0) { - /* ok we actually have entries to process */ - if (NULL == (filt->lines[i]->realm_cons = tr_cfg_parse_one_constraint(filt->lines[i], "realm", jrc, rc))) { - tr_debug("tr_cfg_parse_one_filter: Error parsing realm constraint"); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - } - } - - if (NULL != (jdc = json_object_get(jfline, "domain_constraints"))) { - if (!json_is_array(jdc)) { - tr_err("tr_cfg_parse_one_filter: cannot parse domain_constraints, not an array."); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } else if (json_array_size(jdc) > TR_MAX_CONST_MATCHES) { - tr_err("tr_cfg_parse_one_filter: domain_constraints has too many entries, maximum of %d.", - TR_MAX_CONST_MATCHES); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } else if (json_array_size(jdc) > 0) { - if (NULL == (filt->lines[i]->domain_cons = tr_cfg_parse_one_constraint(filt->lines[i], "domain", jdc, rc))) { - tr_debug("tr_cfg_parse_one_filter: Error parsing domain constraint"); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - } - } - - /*For each filter spec within the filter line... */ - json_array_foreach(jfspecs, j, this_jfspec) { - if ((NULL == (jfield = json_object_get(this_jfspec, "field"))) || - (!json_is_string(jfield))) { - tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing field for filer spec %d, filter line %d.", i, - j); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - - /* check that we have a match attribute */ - if (NULL == (jmatch = json_object_get(this_jfspec, "match"))) { - tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing match for filer spec %d, filter line %d.", i, - j); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - - /* check that match is a string or an array */ - if ((!json_is_string(jmatch)) && (!json_is_array(jmatch))) { - tr_debug( - "tr_cfg_parse_one_filter: Error parsing filter: match not a string or array for filter spec %d, filter line %d.", - i, j); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - - /* allocate the filter spec */ - if (NULL == (filt->lines[i]->specs[j] = tr_fspec_new(filt->lines[i]))) { - tr_debug("tr_cfg_parse_one_filter: Out of memory."); - *rc = TR_CFG_NOMEM; - goto cleanup; - } - - /* fill in the field */ - if (NULL == (filt->lines[i]->specs[j]->field = tr_new_name(json_string_value(jfield)))) { - tr_debug("tr_cfg_parse_one_filter: Out of memory."); - *rc = TR_CFG_NOMEM; - goto cleanup; - } - - /* fill in the matches */ - if (json_is_string(jmatch)) { - if (NULL == (name = tr_new_name(json_string_value(jmatch)))) { - tr_debug("tr_cfg_parse_one_filter: Out of memory."); - *rc = TR_CFG_NOMEM; - goto cleanup; - } - tr_fspec_add_match(filt->lines[i]->specs[j], name); - } else { - /* jmatch is an array (we checked earlier) */ - json_array_foreach(jmatch, k, this_jmatch) { - if (NULL == (name = tr_new_name(json_string_value(this_jmatch)))) { - tr_debug("tr_cfg_parse_one_filter: Out of memory."); - *rc = TR_CFG_NOMEM; - goto cleanup; - } - tr_fspec_add_match(filt->lines[i]->specs[j], name); - } - } - if (!tr_filter_validate_spec_field(ftype, filt->lines[i]->specs[j])){ - tr_debug("tr_cfg_parse_one_filter: Invalid filter field \"%.*s\" for %s filter, spec %d, filter %d.", - filt->lines[i]->specs[j]->field->len, - filt->lines[i]->specs[j]->field->buf, - tr_filter_type_to_string(filt->type), - i, j); - *rc = TR_CFG_ERROR; - goto cleanup; - } - } - } - - /* check that the filter is valid */ - if (!tr_filter_validate(filt)) { - *rc = TR_CFG_ERROR; - } else { - *rc = TR_CFG_SUCCESS; - talloc_steal(mem_ctx, filt); - } - - cleanup: - talloc_free(tmp_ctx); - if (*rc!=TR_CFG_SUCCESS) - filt=NULL; - return filt; -} - -static TR_FILTER_SET *tr_cfg_parse_filters(TALLOC_CTX *mem_ctx, json_t *jfilts, TR_CFG_RC *rc) -{ - TALLOC_CTX *tmp_ctx=talloc_new(NULL); - json_t *jfilt; - const char *filt_label=NULL; - TR_FILTER *filt=NULL; - TR_FILTER_SET *filt_set=NULL; - TR_FILTER_TYPE filt_type=TR_FILTER_TYPE_UNKNOWN; - - *rc=TR_CFG_ERROR; - - /* no filters */ - if (jfilts==NULL) { - *rc=TR_CFG_SUCCESS; - goto cleanup; - } - - filt_set=tr_filter_set_new(tmp_ctx); - if (filt_set==NULL) { - tr_debug("tr_cfg_parse_filters: Unable to allocate filter set."); - *rc = TR_CFG_NOMEM; - goto cleanup; - } - - json_object_foreach(jfilts, filt_label, jfilt) { - /* check that we got a filter */ - if (jfilt == NULL) { - tr_debug("tr_cfg_parse_filters: Definition for %s filter is missing.", filt_label); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - - /* check that we recognize the filter type */ - filt_type=tr_filter_type_from_string(filt_label); - if (filt_type==TR_FILTER_TYPE_UNKNOWN) { - tr_debug("tr_cfg_parse_filters: Unrecognized filter (%s) defined.", filt_label); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - - /* finally, parse the filter */ - tr_debug("tr_cfg_parse_filters: Found %s filter.", filt_label); - filt = tr_cfg_parse_one_filter(tmp_ctx, jfilt, filt_type, rc); - tr_filter_set_add(filt_set, filt); - if (*rc != TR_CFG_SUCCESS) { - tr_debug("tr_cfg_parse_filters: Error parsing %s filter.", filt_label); - *rc = TR_CFG_NOPARSE; - goto cleanup; - } - } - - *rc=TR_CFG_SUCCESS; - - cleanup: - if (*rc==TR_CFG_SUCCESS) - talloc_steal(mem_ctx, filt_set); - else if (filt_set!=NULL) { - talloc_free(filt_set); - filt_set=NULL; - } - - talloc_free(tmp_ctx); - return filt_set; -} - -static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc) -{ - TR_AAA_SERVER *aaa = NULL; - TR_NAME *name=NULL; - - if ((!jaddr) || (!json_is_string(jaddr))) { - tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters."); - *rc = TR_CFG_BAD_PARAMS; - return NULL; - } - - name=tr_new_name(json_string_value(jaddr)); - if (name==NULL) { - tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating hostname."); - *rc = TR_CFG_NOMEM; - return NULL; - } - - aaa=tr_aaa_server_new(mem_ctx, name); - if (aaa==NULL) { - tr_free_name(name); - tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server."); - *rc = TR_CFG_NOMEM; - return NULL; - } - - return aaa; -} - -static TR_AAA_SERVER *tr_cfg_parse_aaa_servers(TALLOC_CTX *mem_ctx, json_t *jaaas, TR_CFG_RC *rc) -{ - TALLOC_CTX *tmp_ctx=NULL; - TR_AAA_SERVER *aaa = NULL; - TR_AAA_SERVER *temp_aaa = NULL; - int i = 0; - - for (i = 0; i < json_array_size(jaaas); i++) { - /* rc gets set in here */ - if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(tmp_ctx, json_array_get(jaaas, i), rc))) { - talloc_free(tmp_ctx); - return NULL; - } - /* TBD -- IPv6 addresses */ - // tr_debug("tr_cfg_parse_aaa_servers: Configuring AAA Server: ip_addr = %s.", inet_ntoa(temp_aaa->aaa_server_addr)); - temp_aaa->next = aaa; - aaa = temp_aaa; - } - tr_debug("tr_cfg_parse_aaa_servers: Finished (rc=%d)", *rc); - - for (temp_aaa=aaa; temp_aaa!=NULL; temp_aaa=temp_aaa->next) - talloc_steal(mem_ctx, temp_aaa); - talloc_free(tmp_ctx); - return aaa; -} - -static TR_APC *tr_cfg_parse_one_apc(TALLOC_CTX *mem_ctx, json_t *japc, TR_CFG_RC *rc) -{ - TR_APC *apc=NULL; - TR_NAME *name=NULL; - - *rc = TR_CFG_SUCCESS; /* presume success */ - - if ((!japc) || (!rc) || (!json_is_string(japc))) { - tr_debug("tr_cfg_parse_one_apc: Bad parameters."); - if (rc) - *rc = TR_CFG_BAD_PARAMS; - return NULL; - } - - apc=tr_apc_new(mem_ctx); - if (apc==NULL) { - tr_debug("tr_cfg_parse_one_apc: Out of memory."); - *rc = TR_CFG_NOMEM; - return NULL; - } - - name=tr_new_name(json_string_value(japc)); - if (name==NULL) { - tr_debug("tr_cfg_parse_one_apc: No memory for APC name."); - tr_apc_free(apc); - *rc = TR_CFG_NOMEM; - return NULL; - } - tr_apc_set_id(apc, name); /* apc is now responsible for freeing the name */ - - return apc; -} - -static TR_APC *tr_cfg_parse_apcs(TALLOC_CTX *mem_ctx, json_t *japcs, TR_CFG_RC *rc) -{ - TALLOC_CTX *tmp_ctx=talloc_new(NULL); - TR_APC *apcs=NULL; - TR_APC *new_apc=NULL; - int ii=0; - TR_CFG_RC call_rc=TR_CFG_ERROR; - - *rc = TR_CFG_SUCCESS; /* presume success */ - - if ((!japcs) || (!rc) || (!json_is_array(japcs))) { - tr_debug("tr_cfg_parse_one_apc: Bad parameters."); - if (rc) - *rc = TR_CFG_BAD_PARAMS; - return NULL; - } - - for (ii=0; iishared_config=tr_cfg_parse_shared_config(json_object_get(jidp, "shared_config"), &rc); - if (rc!=TR_CFG_SUCCESS) { - tr_err("tr_cfg_parse_idp: missing or malformed shared_config specification"); - rc=TR_CFG_NOPARSE; - goto cleanup; - } - - apcs=tr_cfg_parse_apcs(tmp_ctx, json_object_get(jidp, "apcs"), &rc); - if ((rc!=TR_CFG_SUCCESS) || (apcs==NULL)) { - tr_err("tr_cfg_parse_idp: unable to parse APC"); - rc=TR_CFG_NOPARSE; - goto cleanup; - } - - aaa=tr_cfg_parse_aaa_servers(idp, json_object_get(jidp, "aaa_servers"), &rc); - if (rc!=TR_CFG_SUCCESS) { - tr_err("tr_cfg_parse_idp: unable to parse AAA servers"); - rc=TR_CFG_NOPARSE; - goto cleanup; - } - - tr_debug("tr_cfg_parse_idp: APC=\"%.*s\"", - apcs->id->len, - apcs->id->buf); - - /* done, fill in the idp structures */ - idp->apcs=apcs; - talloc_steal(idp, apcs); - idp->aaa_servers=aaa; - rc=TR_CFG_SUCCESS; - -cleanup: - if (rc!=TR_CFG_SUCCESS) { - if (apcs!=NULL) - tr_apc_free(apcs); - if (aaa!=NULL) - tr_aaa_server_free(aaa); - } - - talloc_free(tmp_ctx); - return rc; -} - -/* parses idp realm */ -static TR_IDP_REALM *tr_cfg_parse_one_idp_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc) -{ - TALLOC_CTX *tmp_ctx=talloc_new(NULL); - TR_IDP_REALM *realm=NULL; - TR_CFG_RC call_rc=TR_CFG_ERROR; - - *rc=TR_CFG_ERROR; /* default to error if not set */ - - if ((!jrealm) || (!rc)) { - tr_err("tr_cfg_parse_one_idp_realm: Bad parameters."); - if (rc) - *rc=TR_CFG_BAD_PARAMS; - goto cleanup; - } - - if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) { - tr_err("tr_cfg_parse_one_idp_realm: could not allocate idp realm."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - - realm->origin=tr_cfg_realm_origin(jrealm); - if (realm->origin!=TR_REALM_LOCAL) { - tr_debug("tr_cfg_parse_one_idp_realm: realm is remote, should not have full IdP info."); - *rc=TR_CFG_NOPARSE; - goto cleanup; - } - - /* must have a name */ - realm->realm_id=tr_cfg_parse_name(realm, - json_object_get(jrealm, "realm"), - &call_rc); - if ((call_rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) { - tr_err("tr_cfg_parse_one_idp_realm: could not parse realm name"); - *rc=TR_CFG_NOPARSE; - goto cleanup; - } - tr_debug("tr_cfg_parse_one_idp_realm: realm_id=\"%.*s\"", - realm->realm_id->len, - realm->realm_id->buf); - - call_rc=tr_cfg_parse_idp(realm, json_object_get(jrealm, "identity_provider")); - if (call_rc!=TR_CFG_SUCCESS) { - tr_err("tr_cfg_parse_one_idp_realm: could not parse identity_provider."); - *rc=TR_CFG_NOPARSE; - goto cleanup; - } - - *rc=TR_CFG_SUCCESS; - -cleanup: - if (*rc==TR_CFG_SUCCESS) - talloc_steal(mem_ctx, realm); - else { - talloc_free(realm); - realm=NULL; - } - - talloc_free(tmp_ctx); - return realm; -} - - /* Determine whether the realm is an IDP realm */ -static int tr_cfg_is_idp_realm(json_t *jrealm) -{ - /* If a realm spec contains an identity_provider, it's an IDP realm. */ - if (NULL != json_object_get(jrealm, "identity_provider")) - return 1; - else - return 0; -} - -static TR_IDP_REALM *tr_cfg_parse_one_remote_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc) -{ - TALLOC_CTX *tmp_ctx=talloc_new(NULL); - TR_IDP_REALM *realm=talloc(mem_ctx, TR_IDP_REALM); - - *rc=TR_CFG_ERROR; /* default to error if not set */ - - if ((!jrealm) || (!rc)) { - tr_err("tr_cfg_parse_one_remote_realm: Bad parameters."); - if (rc) - *rc=TR_CFG_BAD_PARAMS; - goto cleanup; - } - - if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) { - tr_err("tr_cfg_parse_one_remote_realm: could not allocate idp realm."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - - /* must have a name */ - realm->realm_id=tr_cfg_parse_name(realm, - json_object_get(jrealm, "realm"), - rc); - if ((*rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) { - tr_err("tr_cfg_parse_one_remote_realm: could not parse realm name"); - *rc=TR_CFG_NOPARSE; - goto cleanup; - } - tr_debug("tr_cfg_parse_one_remote_realm: realm_id=\"%.*s\"", - realm->realm_id->len, - realm->realm_id->buf); - - realm->origin=tr_cfg_realm_origin(jrealm); - *rc=TR_CFG_SUCCESS; - -cleanup: - if (*rc==TR_CFG_SUCCESS) - talloc_steal(mem_ctx, realm); - else { - talloc_free(realm); - realm=NULL; - } - - talloc_free(tmp_ctx); - return realm; -} - -static int tr_cfg_is_remote_realm(json_t *jrealm) -{ - return (tr_cfg_realm_origin(jrealm)!=TR_REALM_LOCAL); -} - -/* Parse any idp realms in the j_realms object. Ignores other realm types. */ -static TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc) -{ - TALLOC_CTX *tmp_ctx=talloc_new(NULL); - TR_IDP_REALM *realms=NULL; - TR_IDP_REALM *new_realm=NULL; - json_t *this_jrealm=NULL; - int ii=0; - - *rc=TR_CFG_ERROR; - if ((jrealms==NULL) || (!json_is_array(jrealms))) { - tr_err("tr_cfg_parse_idp_realms: realms not an array"); - *rc=TR_CFG_BAD_PARAMS; - goto cleanup; - } - - for (ii=0; iilines[0]=tr_fline_new(filt); - if (filt->lines[0]==NULL) { - tr_debug("tr_cfg_default_filters: could not allocate filter line."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - - filt->lines[0]->action=TR_FILTER_ACTION_ACCEPT; - filt->lines[0]->specs[0]=tr_fspec_new(filt->lines[0]); - filt->lines[0]->specs[0]->field=n_rp_realm_1; - n_rp_realm_1=NULL; /* we don't own this name any more */ - - name=tr_dup_name(realm); - if (name==NULL) { - tr_debug("tr_cfg_default_filters: could not allocate realm name."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - tr_fspec_add_match(filt->lines[0]->specs[0], name); - name=NULL; /* we no longer own the name */ - - /* now do the wildcard name */ - filt->lines[0]->specs[1]=tr_fspec_new(filt->lines[0]); - filt->lines[0]->specs[1]->field=n_rp_realm_2; - n_rp_realm_2=NULL; /* we don't own this name any more */ - - if (NULL==(name=tr_name_cat(n_prefix, realm))) { - tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - - tr_fspec_add_match(filt->lines[0]->specs[1], name); - name=NULL; /* we no longer own the name */ - - /* domain constraint */ - if (NULL==(cons=tr_constraint_new(filt->lines[0]))) { - tr_debug("tr_cfg_default_filters: could not allocate domain constraint."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - - cons->type=n_domain; - n_domain=NULL; /* belongs to the constraint now */ - name=tr_dup_name(realm); - if (name==NULL) { - tr_debug("tr_cfg_default_filters: could not allocate realm name for domain constraint."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - cons->matches[0]=name; - name=tr_name_cat(n_prefix, realm); - if (name==NULL) { - tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name for domain constraint."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - cons->matches[1]=name; - name=NULL; - filt->lines[0]->domain_cons=cons; - - - /* realm constraint */ - if (NULL==(cons=tr_constraint_new(filt->lines[0]))) { - tr_debug("tr_cfg_default_filters: could not allocate realm constraint."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - - cons->type=n_realm; - n_realm=NULL; /* belongs to the constraint now */ - name=tr_dup_name(realm); - if (name==NULL) { - tr_debug("tr_cfg_default_filters: could not allocate realm name for realm constraint."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - cons->matches[0]=name; - name=tr_name_cat(n_prefix, realm); - if (name==NULL) { - tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name for realm constraint."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - cons->matches[1]=name; - name=NULL; - filt->lines[0]->realm_cons=cons; - - /* put the filter in a set */ - filt_set=tr_filter_set_new(tmp_ctx); - if ((filt_set==NULL)||(0!=tr_filter_set_add(filt_set, filt))) { - tr_debug("tr_cfg_default_filters: could not allocate filter set."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - talloc_steal(mem_ctx, filt_set); - -cleanup: - talloc_free(tmp_ctx); - - if (*rc!=TR_CFG_SUCCESS) - filt=NULL; - - if (n_prefix!=NULL) - tr_free_name(n_prefix); - if (n_rp_realm_1!=NULL) - tr_free_name(n_rp_realm_1); - if (n_rp_realm_2!=NULL) - tr_free_name(n_rp_realm_2); - if (n_realm!=NULL) - tr_free_name(n_realm); - if (n_domain!=NULL) - tr_free_name(n_domain); - if (name!=NULL) - tr_free_name(name); - - return filt_set; -} - -/* parses rp client */ -static TR_RP_CLIENT *tr_cfg_parse_one_rp_client(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc) -{ - TALLOC_CTX *tmp_ctx=talloc_new(NULL); - TR_RP_CLIENT *client=NULL; - TR_CFG_RC call_rc=TR_CFG_ERROR; - TR_FILTER_SET *new_filts=NULL; - TR_NAME *realm=NULL; - json_t *jfilt=NULL; - json_t *jrealm_id=NULL; - - *rc=TR_CFG_ERROR; /* default to error if not set */ - - if ((!jrealm) || (!rc)) { - tr_err("tr_cfg_parse_one_rp_client: Bad parameters."); - if (rc) - *rc=TR_CFG_BAD_PARAMS; - goto cleanup; - } - - if ((NULL==(jrealm_id=json_object_get(jrealm, "realm"))) || (!json_is_string(jrealm_id))) { - tr_debug("tr_cfg_parse_one_rp_client: no realm ID found."); - *rc=TR_CFG_BAD_PARAMS; - goto cleanup; - } - - tr_debug("tr_cfg_parse_one_rp_client: realm_id=\"%s\"", json_string_value(jrealm_id)); - realm=tr_new_name(json_string_value(jrealm_id)); - if (realm==NULL) { - tr_err("tr_cfg_parse_one_rp_client: could not allocate realm ID."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - - if (NULL==(client=tr_rp_client_new(tmp_ctx))) { - tr_err("tr_cfg_parse_one_rp_client: could not allocate rp client."); - *rc=TR_CFG_NOMEM; - goto cleanup; - } - - client->gss_names=tr_cfg_parse_gss_names(client, json_object_get(jrealm, "gss_names"), &call_rc); - - if (call_rc!=TR_CFG_SUCCESS) { - tr_err("tr_cfg_parse_one_rp_client: could not parse gss_names."); - *rc=TR_CFG_NOPARSE; - goto cleanup; - } - - /* parse filters */ - jfilt=json_object_get(jrealm, "filters"); - if (jfilt!=NULL) { - new_filts=tr_cfg_parse_filters(tmp_ctx, jfilt, &call_rc); - if (call_rc!=TR_CFG_SUCCESS) { - tr_err("tr_cfg_parse_one_rp_client: could not parse filters."); - *rc=TR_CFG_NOPARSE; - goto cleanup; - } - } else { - tr_debug("tr_cfg_parse_one_rp_client: no filters specified, using default filters."); - new_filts= tr_cfg_default_filters(tmp_ctx, realm, &call_rc); - if (call_rc!=TR_CFG_SUCCESS) { - tr_err("tr_cfg_parse_one_rp_client: could not set default filters."); - *rc=TR_CFG_NOPARSE; - goto cleanup; - } - } - - tr_rp_client_set_filters(client, new_filts); - *rc=TR_CFG_SUCCESS; - - cleanup: - if (realm!=NULL) - tr_free_name(realm); - - if (*rc==TR_CFG_SUCCESS) - talloc_steal(mem_ctx, client); - else { - talloc_free(client); - client=NULL; - } - - talloc_free(tmp_ctx); - return client; -} - - /* Determine whether the realm is an RP realm */ -static int tr_cfg_is_rp_realm(json_t *jrealm) -{ - /* Check that we have a gss name. */ - if (NULL != json_object_get(jrealm, "gss_names")) - return 1; - else - return 0; -} - -/* Parse any rp clients in the j_realms object. Ignores other realms. */ -static TR_RP_CLIENT *tr_cfg_parse_rp_clients(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc) -{ - TALLOC_CTX *tmp_ctx=talloc_new(NULL); - TR_RP_CLIENT *clients=NULL; - TR_RP_CLIENT *new_client=NULL; - json_t *this_jrealm=NULL; - int ii=0; - - *rc=TR_CFG_ERROR; - if ((jrealms==NULL) || (!json_is_array(jrealms))) { - tr_err("tr_cfg_parse_rp_clients: realms not an array"); - *rc=TR_CFG_BAD_PARAMS; - goto cleanup; - } - - for (ii=0; iilen, - org_name->buf); - /* we don't actually do anything with this, but we could */ - tr_free_name(org_name); - org_name=NULL; - } - } - - /* Now get realms. Allow this to be missing; even though that is a pointless organization entry, - * it's harmless. Report a warning because that might be unintentional. */ - if (NULL==(j_realms=json_object_get(jlorg, "realms"))) { - tr_warning("tr_cfg_parse_one_local_org: warning - no realms in this local organization"); - } else { - /* Allocate in the tmp_ctx so these will be cleaned up if we do not complete successfully. */ - new_idp_realms=tr_cfg_parse_idp_realms(tmp_ctx, j_realms, &rc); - if (rc!=TR_CFG_SUCCESS) - goto cleanup; - - new_rp_clients=tr_cfg_parse_rp_clients(tmp_ctx, j_realms, &rc); - if (rc!=TR_CFG_SUCCESS) - goto cleanup; - } - retval=TR_CFG_SUCCESS; - -cleanup: - /* if we succeeded, link things to the configuration and move out of tmp context */ - if (retval==TR_CFG_SUCCESS) { - if (new_idp_realms!=NULL) { - tr_idp_realm_add(trc->ctable->idp_realms, new_idp_realms); /* fixes talloc contexts except for head*/ - talloc_steal(trc, trc->ctable->idp_realms); /* make sure the head is in the right context */ - } - - if (new_rp_clients!=NULL) { - tr_rp_client_add(trc->rp_clients, new_rp_clients); /* fixes talloc contexts */ - talloc_steal(trc, trc->rp_clients); /* make sure head is in the right context */ - } - } - - talloc_free(tmp_ctx); - return rc; + TR_CFG *cfg = talloc_get_type_abort(object, TR_CFG); + if (cfg->files) + g_array_unref(cfg->files); + return 0; } - -/* Parse local organizations if present. Returns success if there are none. On failure, the configuration is unreliable. */ -static TR_CFG_RC tr_cfg_parse_local_orgs(TR_CFG *trc, json_t *jcfg) +TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx) { - json_t *jlocorgs=NULL; - size_t ii=0; - - jlocorgs=json_object_get(jcfg, "local_organizations"); - if (jlocorgs==NULL) - return TR_CFG_SUCCESS; - - if (!json_is_array(jlocorgs)) { - tr_err("tr_cfg_parse_local_orgs: local_organizations is not an array."); - return TR_CFG_NOPARSE; - } - - for (ii=0; iiinternal=NULL; + cfg->rp_clients=NULL; + cfg->peers=NULL; + cfg->default_servers=NULL; + cfg->ctable=tr_comm_table_new(cfg); + if (cfg->ctable==NULL) { + talloc_free(cfg); + return NULL; } - } - - return TR_CFG_SUCCESS; -} - -static TR_CFG_RC tr_cfg_parse_one_peer_org(TR_CFG *trc, json_t *jporg) -{ - TALLOC_CTX *tmp_ctx=talloc_new(NULL); - json_t *jhost=NULL; - json_t *jport=NULL; - json_t *jgss=NULL; - json_t *jfilt=NULL; - TRP_PEER *new_peer=NULL; - TR_GSS_NAMES *names=NULL; - TR_FILTER_SET *filt_set=NULL; - TR_CFG_RC rc=TR_CFG_ERROR; - - jhost=json_object_get(jporg, "hostname"); - jport=json_object_get(jporg, "port"); - jgss=json_object_get(jporg, "gss_names"); - jfilt=json_object_get(jporg, "filters"); - - if ((jhost==NULL) || (!json_is_string(jhost))) { - tr_err("tr_cfg_parse_one_peer_org: hostname not specified or not a string."); - rc=TR_CFG_NOPARSE; - goto cleanup; - } - - if ((jport!=NULL) && (!json_is_number(jport))) { - /* note that not specifying the port is allowed, but if set it must be a number */ - tr_err("tr_cfg_parse_one_peer_org: port is not a number."); - rc=TR_CFG_NOPARSE; - goto cleanup; - } - - if ((jgss==NULL) || (!json_is_array(jgss))) { - tr_err("tr_cfg_parse_one_peer_org: gss_names not specified or not an array."); - rc=TR_CFG_NOPARSE; - goto cleanup; - } - - if ((jfilt!=NULL) && (!json_is_object(jfilt))) { - tr_err("tr_cfg_parse_one_peer_org: filters is not an object."); - rc=TR_CFG_NOPARSE; - goto cleanup; - } - - new_peer=trp_peer_new(tmp_ctx); - if (new_peer==NULL) { - tr_err("tr_cfg_parse_one_peer_org: could not allocate new peer."); - rc=TR_CFG_NOMEM; - goto cleanup; - } - - trp_peer_set_server(new_peer, json_string_value(jhost)); /* string is strdup'ed in _set_server() */ - if (jport==NULL) - trp_peer_set_port(new_peer, TRP_PORT); - else - trp_peer_set_port(new_peer, json_integer_value(jport)); - - names=tr_cfg_parse_gss_names(tmp_ctx, jgss, &rc); - if (rc!=TR_CFG_SUCCESS) { - tr_err("tr_cfg_parse_one_peer_org: unable to parse gss names."); - rc=TR_CFG_NOPARSE; - goto cleanup; - } - trp_peer_set_gss_names(new_peer, names); - - if (jfilt) { - filt_set=tr_cfg_parse_filters(tmp_ctx, jfilt, &rc); - if (rc!=TR_CFG_SUCCESS) { - tr_err("tr_cfg_parse_one_peer_org: unable to parse filters."); - rc=TR_CFG_NOPARSE; - goto cleanup; + cfg->files = g_array_new(FALSE, FALSE, sizeof(TR_CFG_FILE)); + if (cfg->files == NULL) { + talloc_free(cfg); + return NULL; } - trp_peer_set_filters(new_peer, filt_set); + talloc_set_destructor((void *)cfg, tr_cfg_destructor); } - - /* success! */ - trp_ptable_add(trc->peers, new_peer); - rc=TR_CFG_SUCCESS; - - cleanup: - talloc_free(tmp_ctx); - return rc; + return cfg; } -/* Parse peer organizations, if present. Returns success if there are none. */ -static TR_CFG_RC tr_cfg_parse_peer_orgs(TR_CFG *trc, json_t *jcfg) -{ - json_t *jpeerorgs=NULL; - int ii=0; - - jpeerorgs=json_object_get(jcfg, "peer_organizations"); - if (jpeerorgs==NULL) - return TR_CFG_SUCCESS; - - if (!json_is_array(jpeerorgs)) { - tr_err("tr_cfg_parse_peer_orgs: peer_organizations is not an array."); - return TR_CFG_NOPARSE; - } - - for (ii=0; iihostname->buf); - ds->next = trc->default_servers; - trc->default_servers = ds; - } - } - - tr_debug("tr_cfg_parse_default_servers: Finished (rc=%d)", rc); - return rc; + talloc_free(cfg); } -static void tr_cfg_parse_comm_idps(TR_CFG *trc, json_t *jidps, TR_COMM *comm, TR_CFG_RC *rc) +TR_CFG_MGR *tr_cfg_mgr_new(TALLOC_CTX *mem_ctx) { - TR_IDP_REALM *found_idp=NULL; - json_t *jidp_name=NULL; - TR_NAME *idp_name=NULL; - size_t ii = 0; - - if ((!trc) || - (!jidps) || - (!json_is_array(jidps))) { - if (rc) - *rc = TR_CFG_BAD_PARAMS; - return; - } - - json_array_foreach(jidps, ii, jidp_name) { - idp_name=tr_new_name(json_string_value(jidp_name)); - if (idp_name==NULL) { - *rc = TR_CFG_NOMEM; - return; - } - found_idp=tr_cfg_find_idp(trc, idp_name, rc); - tr_free_name(idp_name); - - if ((found_idp==NULL) || (*rc!=TR_CFG_SUCCESS)) { - tr_debug("tr_cfg_parse_comm_idps: Unknown IDP %s.", json_string_value(jidp_name)); - *rc=TR_CFG_ERROR; - return; - } - tr_comm_add_idp_realm(trc->ctable, comm, found_idp, 0, NULL, NULL); /* no provenance, never expires */ - } - - *rc=TR_CFG_SUCCESS; - return; + return talloc_zero(mem_ctx, TR_CFG_MGR); } -static void tr_cfg_parse_comm_rps(TR_CFG *trc, json_t *jrps, TR_COMM *comm, TR_CFG_RC *rc) -{ - TR_RP_REALM *found_rp=NULL; - TR_RP_REALM *new_rp=NULL; - TR_NAME *rp_name=NULL; - const char *s=NULL; - int ii=0; - - if ((!trc) || - (!jrps) || - (!json_is_array(jrps))) { - if (rc) - *rc = TR_CFG_BAD_PARAMS; - return; - } - - for (ii=0; iilen, tr_comm_get_id(comm)->buf); - continue; - } - - /* convert string to TR_NAME */ - rp_name=tr_new_name(s); - if (rp_name==NULL) { - tr_err("tr_cfg_parse_comm_rps: unable to allocate RP name for %s in community %.*s.", - s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf); - } - - /* see if we already have this RP in this community */ - found_rp=tr_comm_find_rp(trc->ctable, comm, rp_name); - if (found_rp!=NULL) { - tr_notice("tr_cfg_parse_comm_rps: RP %s repeated in community %.*s.", - s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf); - tr_free_name(rp_name); - continue; - } - - /* Add the RP to the community, first see if we have the RP in any community */ - found_rp=tr_rp_realm_lookup(trc->ctable->rp_realms, rp_name); - if (found_rp!=NULL) { - tr_debug("tr_cfg_parse_comm_rps: RP realm %s already exists.", s); - new_rp=found_rp; /* use it rather than creating a new realm record */ - } else { - new_rp=tr_rp_realm_new(NULL); - if (new_rp==NULL) { - tr_err("tr_cfg_parse_comm_rps: unable to allocate RP record for %s in community %.*s.", - s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf); - } - tr_debug("tr_cfg_parse_comm_rps: setting name to %s", rp_name->buf); - tr_rp_realm_set_id(new_rp, rp_name); - rp_name=NULL; /* rp_name no longer belongs to us */ - tr_rp_realm_add(trc->ctable->rp_realms, new_rp); - talloc_steal(trc->ctable, trc->ctable->rp_realms); /* make sure head is in the right context */ - } - tr_comm_add_rp_realm(trc->ctable, comm, new_rp, 0, NULL, NULL); - } +void tr_cfg_mgr_free (TR_CFG_MGR *cfg_mgr) { + talloc_free(cfg_mgr); } -static TR_COMM *tr_cfg_parse_one_comm (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc) +TR_CFG_RC tr_apply_new_config (TR_CFG_MGR *cfg_mgr) { - TALLOC_CTX *tmp_ctx=talloc_new(NULL); - TR_COMM *comm = NULL; - json_t *jid = NULL; - json_t *jtype = NULL; - json_t *japcs = NULL; - json_t *jidps = NULL; - json_t *jrps = NULL; - - if ((!trc) || (!jcomm) || (!rc)) { - tr_debug("tr_cfg_parse_one_comm: Bad parameters."); - if (rc) - *rc = TR_CFG_BAD_PARAMS; - goto cleanup; - } - - comm=tr_comm_new(tmp_ctx); - if (comm==NULL) { - tr_crit("tr_cfg_parse_one_comm: Out of memory."); - *rc = TR_CFG_NOMEM; - goto cleanup; - } - - - if ((NULL == (jid = json_object_get(jcomm, "community_id"))) || - (!json_is_string(jid)) || - (NULL == (jtype = json_object_get(jcomm, "type"))) || - (!json_is_string(jtype)) || - (NULL == (japcs = json_object_get(jcomm, "apcs"))) || - (!json_is_array(japcs)) || - (NULL == (jidps = json_object_get(jcomm, "idp_realms"))) || - (!json_is_array(jidps)) || - (NULL == (jrps = json_object_get(jcomm, "rp_realms"))) || - (!json_is_array(jrps))) { - tr_debug("tr_cfg_parse_one_comm: Error parsing Communities configuration."); - *rc = TR_CFG_NOPARSE; - comm=NULL; - goto cleanup; - } - - tr_comm_set_id(comm, tr_new_name(json_string_value(jid))); - if (NULL == tr_comm_get_id(comm)) { - tr_debug("tr_cfg_parse_one_comm: No memory for community id."); - *rc = TR_CFG_NOMEM; - comm=NULL; - goto cleanup; - } - - if (0 == strcmp(json_string_value(jtype), "apc")) { - comm->type = TR_COMM_APC; - } else if (0 == strcmp(json_string_value(jtype), "coi")) { - comm->type = TR_COMM_COI; - if (NULL == (comm->apcs = tr_cfg_parse_apcs(trc, japcs, rc))) { - tr_debug("tr_cfg_parse_one_comm: Can't parse APCs for COI %s.", - tr_comm_get_id(comm)->buf); - comm=NULL; - goto cleanup; - } - } else { - tr_debug("tr_cfg_parse_one_comm: Invalid community type, comm = %s, type = %s", - tr_comm_get_id(comm)->buf, json_string_value(jtype)); - *rc = TR_CFG_NOPARSE; - comm=NULL; - goto cleanup; - } + /* cfg_mgr->active is allowed to be null, but new cannot be */ + if ((cfg_mgr==NULL) || (cfg_mgr->new==NULL)) + return TR_CFG_BAD_PARAMS; - tr_cfg_parse_comm_idps(trc, jidps, comm, rc); - if (TR_CFG_SUCCESS != *rc) { - tr_debug("tr_cfg_parse_one_comm: Can't parse IDP realms for comm %s.", - tr_comm_get_id(comm)->buf); - comm=NULL; - goto cleanup; - } + if (cfg_mgr->active != NULL) + tr_cfg_free(cfg_mgr->active); - tr_cfg_parse_comm_rps(trc, jrps, comm, rc); - if (TR_CFG_SUCCESS != *rc) { - tr_debug("tr_cfg_parse_one_comm: Can't parse RP realms for comm %s .", - tr_comm_get_id(comm)->buf); - comm=NULL; - goto cleanup; - } + cfg_mgr->active = cfg_mgr->new; + cfg_mgr->new=NULL; /* only keep a single handle on the new configuration */ - if (TR_COMM_APC == comm->type) { - json_t *jexpire = json_object_get(jcomm, "expiration_interval"); - comm->expiration_interval = 43200; /*30 days*/ - if (jexpire) { - if (!json_is_integer(jexpire)) { - tr_err("tr_parse_one_comm: expiration_interval is not an integer for comm %.*s", - tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf); - comm=NULL; - goto cleanup; - } - comm->expiration_interval = json_integer_value(jexpire); - if (comm->expiration_interval <= 10) { - comm->expiration_interval = 11; /* Freeradius waits 10 minutes between successful TR queries*/ - tr_notice( - "tr_parse_one_comm: expiration interval for %.*s less than minimum of 11 minutes; using 11 minutes instead.", - tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf); - } - if (comm->expiration_interval > 129600) { - /* > 90 days*/ - comm->expiration_interval = 129600; - tr_notice( - "tr_parse_one_comm: expiration interval for %.*s exceeds maximum of 90 days; using 90 days instead.", - tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf); - } - } - } + tr_log_threshold(cfg_mgr->active->internal->log_threshold); + tr_console_threshold(cfg_mgr->active->internal->console_threshold); -cleanup: - if (comm!=NULL) - talloc_steal(mem_ctx, comm); - talloc_free(tmp_ctx); - return comm; + return TR_CFG_SUCCESS; } -static TR_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg) -{ - json_t *jcomms = NULL; - TR_CFG_RC rc = TR_CFG_SUCCESS; - TR_COMM *comm = NULL; - int i = 0; - - if ((!trc) || (!jcfg)) { - tr_debug("tr_cfg_parse_comms: Bad Parameters."); - return TR_CFG_BAD_PARAMS; - } - - if (NULL != (jcomms = json_object_get(jcfg, "communities"))) { - if (!json_is_array(jcomms)) { - return TR_CFG_NOPARSE; - } - - for (i = 0; i < json_array_size(jcomms); i++) { - if (NULL == (comm = tr_cfg_parse_one_comm(NULL, /* TODO: use a talloc context */ - trc, - json_array_get(jcomms, i), - &rc))) { - return rc; - } - tr_debug("tr_cfg_parse_comms: Community configured: %s.", - tr_comm_get_id(comm)->buf); - - tr_comm_table_add_comm(trc->ctable, comm); - } - } - tr_debug("tr_cfg_parse_comms: Finished (rc=%d)", rc); - return rc; -} TR_CFG_RC tr_cfg_validate(TR_CFG *trc) { @@ -1983,7 +237,6 @@ static void tr_cfg_log_json_error(const char *label, json_error_t *rc) static json_t *tr_cfg_parse_one_config_file(const char *file_with_path) { json_t *jcfg=NULL; - json_t *jser=NULL; json_error_t rc; if (NULL==(jcfg=json_load_file(file_with_path, @@ -1994,16 +247,20 @@ static json_t *tr_cfg_parse_one_config_file(const char *file_with_path) return NULL; } - // Look for serial number and log it if it exists (borrowed reference, so no need to free it later) - if (NULL!=(jser=json_object_get(jcfg, "serial_number"))) { - if (json_is_number(jser)) { - tr_notice("tr_parse_one_config_file: Attempting to load revision %" JSON_INTEGER_FORMAT " of '%s'.", - json_integer_value(jser), - file_with_path); + return jcfg; +} + +/* extract serial number */ +static json_int_t get_cfg_serial(json_t *jcfg) +{ + json_t *jser=NULL; + + if (NULL != (jser = json_object_get(jcfg, "serial_number"))) { + if (json_is_integer(jser)) { + return json_integer_value(jser); } } - - return jcfg; + return TR_CFG_INVALID_SERIAL; } /** @@ -2028,11 +285,12 @@ static void tr_cfg_parse_free_jcfgs(unsigned int n_jcfgs, json_t **jcfgs) * @param cfg_files * @return */ -static json_t **tr_cfg_parse_config_files(TALLOC_CTX *mem_ctx, unsigned int n_files, char **files_with_paths) +static json_t **tr_cfg_parse_config_files(TALLOC_CTX *mem_ctx, unsigned int n_files, GArray *files) { TALLOC_CTX *tmp_ctx=talloc_new(NULL); unsigned int ii=0; json_t **jcfgs=NULL; + TR_CFG_FILE *this_file = NULL; /* first allocate the jcfgs */ jcfgs=talloc_array(NULL, json_t *, n_files); @@ -2041,14 +299,28 @@ static json_t **tr_cfg_parse_config_files(TALLOC_CTX *mem_ctx, unsigned int n_fi goto cleanup; } for (ii=0; iiname); if (jcfgs[ii]==NULL) { - tr_err("tr_parse_config: Error parsing JSON in %s", files_with_paths[ii]); + tr_err("tr_parse_config: Error parsing JSON in %s", this_file->name); tr_cfg_parse_free_jcfgs(ii, jcfgs); /* frees the JSON objects and the jcfgs array */ jcfgs=NULL; goto cleanup; } + + this_file->serial = get_cfg_serial(jcfgs[ii]); + if (this_file->serial != TR_CFG_INVALID_SERIAL) { + tr_notice("tr_parse_one_config_file: Attempting to load revision %" + JSON_INTEGER_FORMAT + " of '%s'.", + this_file->serial, + this_file->name); + } else { + tr_notice("tr_parse_one_config_file: Attempting to load '%s'.", + this_file->name); + } } + cleanup: if (jcfgs) talloc_steal(mem_ctx, jcfgs); /* give this to the caller's context if we succeeded */ @@ -2062,12 +334,17 @@ typedef TR_CFG_RC (TR_CFG_PARSE_FN)(TR_CFG *, json_t *); * Helper function to parse a collection of JSON structures using a generic parse function. * * @param cfg Config structure to receive results - * @param jcfgs Pointer to an array of decoded JSON structures - * @param n_jcfg Number of JSON structures in the array * @param parse_fn Function to apply + * @param n_jcfg Number of JSON structures in the array + * @param jcfgs Pointer to an array of decoded JSON structures + * @param key Key to extract from each jcfg before parsing, or NULL to use the object itself * @return TR_CFG_SUCCESS on success, _FAIL or an error code on failure */ -static TR_CFG_RC tr_cfg_parse_helper(TR_CFG *cfg, unsigned int n_jcfg, json_t **jcfgs, TR_CFG_PARSE_FN parse_fn) +static TR_CFG_RC tr_cfg_parse_helper(TR_CFG *cfg, + TR_CFG_PARSE_FN parse_fn, + unsigned int n_jcfg, + json_t **jcfgs, + const char *key) { size_t ii=0; json_t *this_jcfg=NULL; @@ -2077,7 +354,15 @@ static TR_CFG_RC tr_cfg_parse_helper(TR_CFG *cfg, unsigned int n_jcfg, json_t ** return TR_CFG_ERROR; for (ii=0; ii 0) { + frec.name = talloc_strdup(cfg, filenames[n]); + frec.serial = TR_CFG_INVALID_SERIAL; + + g_array_append_val(cfg->files, frec); + } +} /** - * Reads configuration files in config_dir ("" or "./" will use the current directory). + * Read a list of configuration files * * @param cfg_mgr Configuration manager * @param n_files Number of entries in cfg_files @@ -2114,8 +410,11 @@ TR_CFG_RC tr_parse_config(TR_CFG_MGR *cfg_mgr, unsigned int n_files, char **file goto cleanup; } + /* add the list of files to the config */ + add_files(cfg_mgr->new, n_files, files_with_paths); + /* first parse the json */ - jcfgs=tr_cfg_parse_config_files(tmp_ctx, n_files, files_with_paths); + jcfgs=tr_cfg_parse_config_files(tmp_ctx, n_files, cfg_mgr->new->files); if (jcfgs==NULL) { cfg_rc=TR_CFG_NOPARSE; goto cleanup; @@ -2124,11 +423,12 @@ TR_CFG_RC tr_parse_config(TR_CFG_MGR *cfg_mgr, unsigned int n_files, char **file cfg_mgr->new->peers=trp_ptable_new(cfg_mgr); /* not sure why this isn't in cfg_mgr->new's context */ /* now run through the parsers on the JSON */ - if ((TR_CFG_SUCCESS != (cfg_rc=tr_cfg_parse_helper(cfg_mgr->new, n_files, jcfgs, tr_cfg_parse_internal))) || - (TR_CFG_SUCCESS != (cfg_rc=tr_cfg_parse_helper(cfg_mgr->new, n_files, jcfgs, tr_cfg_parse_local_orgs))) || - (TR_CFG_SUCCESS != (cfg_rc=tr_cfg_parse_helper(cfg_mgr->new, n_files, jcfgs, tr_cfg_parse_peer_orgs))) || - (TR_CFG_SUCCESS != (cfg_rc=tr_cfg_parse_helper(cfg_mgr->new, n_files, jcfgs, tr_cfg_parse_default_servers))) || - (TR_CFG_SUCCESS != (cfg_rc=tr_cfg_parse_helper(cfg_mgr->new, n_files, jcfgs, tr_cfg_parse_comms)))) + if ((TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_internal, n_files, jcfgs, "tr_internal"))) || + (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_local_orgs, n_files, jcfgs, NULL))) || + (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_peer_orgs, n_files, jcfgs, NULL))) || + (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_default_servers, n_files, jcfgs, + NULL))) || + (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_comms, n_files, jcfgs, NULL)))) goto cleanup; /* cfg_rc was set above */ /* make sure we got a complete, consistent configuration */ @@ -2149,49 +449,8 @@ cleanup: return cfg_rc; } -TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc) -{ - - TR_IDP_REALM *cfg_idp; - - if ((!tr_cfg) || (!idp_id)) { - if (rc) - *rc = TR_CFG_BAD_PARAMS; - return NULL; - } - - for (cfg_idp = tr_cfg->ctable->idp_realms; NULL != cfg_idp; cfg_idp = cfg_idp->next) { - if (!tr_name_cmp (idp_id, cfg_idp->realm_id)) { - tr_debug("tr_cfg_find_idp: Found %s.", idp_id->buf); - return cfg_idp; - } - } - /* if we didn't find one, return NULL */ - return NULL; -} - -TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc) -{ - TR_RP_CLIENT *cfg_rp; - - if ((!tr_cfg) || (!rp_gss)) { - if (rc) - *rc = TR_CFG_BAD_PARAMS; - return NULL; - } - - for (cfg_rp = tr_cfg->rp_clients; NULL != cfg_rp; cfg_rp = cfg_rp->next) { - if (tr_gss_names_matches(cfg_rp->gss_names, rp_gss)) { - tr_debug("tr_cfg_find_rp: Found %s.", rp_gss->buf); - return cfg_rp; - } - } - /* if we didn't find one, return NULL */ - return NULL; -} - static int is_cfg_file(const struct dirent *dent) { - int n; + size_t n; /* Only accept filenames ending in ".cfg" and starting with a character * other than an ASCII '.' */ diff --git a/common/tr_config_comms.c b/common/tr_config_comms.c new file mode 100644 index 0000000..60b5f93 --- /dev/null +++ b/common/tr_config_comms.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2012-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if JANSSON_VERSION_HEX < 0x020500 +#include "jansson_iterators.h" +#endif + +TR_CFG_RC tr_cfg_parse_default_servers (TR_CFG *trc, json_t *jcfg) +{ + json_t *jdss = NULL; + TR_CFG_RC rc = TR_CFG_SUCCESS; + TR_AAA_SERVER *ds = NULL; + int i = 0; + + if ((!trc) || (!jcfg)) + return TR_CFG_BAD_PARAMS; + + /* If there are default servers, store them */ + if ((NULL != (jdss = json_object_get(jcfg, "default_servers"))) && + (json_is_array(jdss)) && + (0 < json_array_size(jdss))) { + + for (i = 0; i < json_array_size(jdss); i++) { + if (NULL == (ds = tr_cfg_parse_one_aaa_server(trc, + json_array_get(jdss, i), + &rc))) { + return rc; + } + tr_debug("tr_cfg_parse_default_servers: Default server configured: %s", ds->hostname->buf); + ds->next = trc->default_servers; + trc->default_servers = ds; + } + } + + tr_debug("tr_cfg_parse_default_servers: Finished (rc=%d)", rc); + return rc; +} + +static void tr_cfg_parse_comm_idps(TR_CFG *trc, json_t *jidps, TR_COMM *comm, TR_CFG_RC *rc) +{ + TR_IDP_REALM *found_idp=NULL; + json_t *jidp_name=NULL; + TR_NAME *idp_name=NULL; + size_t ii = 0; + + if ((!trc) || + (!jidps) || + (!json_is_array(jidps))) { + if (rc) + *rc = TR_CFG_BAD_PARAMS; + return; + } + + json_array_foreach(jidps, ii, jidp_name) { + idp_name=tr_new_name(json_string_value(jidp_name)); + if (idp_name==NULL) { + *rc = TR_CFG_NOMEM; + return; + } + found_idp=tr_cfg_find_idp(trc, idp_name, rc); + tr_free_name(idp_name); + + if ((found_idp==NULL) || (*rc!=TR_CFG_SUCCESS)) { + tr_debug("tr_cfg_parse_comm_idps: Unknown IDP %s.", json_string_value(jidp_name)); + *rc=TR_CFG_ERROR; + return; + } + tr_comm_add_idp_realm(trc->ctable, comm, found_idp, 0, NULL, NULL); /* no provenance, never expires */ + } + + *rc=TR_CFG_SUCCESS; + return; +} + +static void tr_cfg_parse_comm_rps(TR_CFG *trc, json_t *jrps, TR_COMM *comm, TR_CFG_RC *rc) +{ + TR_RP_REALM *found_rp=NULL; + TR_RP_REALM *new_rp=NULL; + TR_NAME *rp_name=NULL; + const char *s=NULL; + int ii=0; + + if ((!trc) || + (!jrps) || + (!json_is_array(jrps))) { + if (rc) + *rc = TR_CFG_BAD_PARAMS; + return; + } + + for (ii=0; iilen, tr_comm_get_id(comm)->buf); + continue; + } + + /* convert string to TR_NAME */ + rp_name=tr_new_name(s); + if (rp_name==NULL) { + tr_err("tr_cfg_parse_comm_rps: unable to allocate RP name for %s in community %.*s.", + s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf); + } + + /* see if we already have this RP in this community */ + found_rp=tr_comm_find_rp(trc->ctable, comm, rp_name); + if (found_rp!=NULL) { + tr_notice("tr_cfg_parse_comm_rps: RP %s repeated in community %.*s.", + s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf); + tr_free_name(rp_name); + continue; + } + + /* Add the RP to the community, first see if we have the RP in any community */ + found_rp=tr_rp_realm_lookup(trc->ctable->rp_realms, rp_name); + if (found_rp!=NULL) { + tr_debug("tr_cfg_parse_comm_rps: RP realm %s already exists.", s); + new_rp=found_rp; /* use it rather than creating a new realm record */ + } else { + new_rp=tr_rp_realm_new(NULL); + if (new_rp==NULL) { + tr_err("tr_cfg_parse_comm_rps: unable to allocate RP record for %s in community %.*s.", + s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf); + } + tr_debug("tr_cfg_parse_comm_rps: setting name to %s", rp_name->buf); + tr_rp_realm_set_id(new_rp, rp_name); + rp_name=NULL; /* rp_name no longer belongs to us */ + tr_rp_realm_add(trc->ctable->rp_realms, new_rp); + talloc_steal(trc->ctable, trc->ctable->rp_realms); /* make sure head is in the right context */ + } + tr_comm_add_rp_realm(trc->ctable, comm, new_rp, 0, NULL, NULL); + } +} + +static TR_COMM *tr_cfg_parse_one_comm (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_COMM *comm = NULL; + json_t *jid = NULL; + json_t *jtype = NULL; + json_t *japcs = NULL; + json_t *jidps = NULL; + json_t *jrps = NULL; + + if ((!trc) || (!jcomm) || (!rc)) { + tr_debug("tr_cfg_parse_one_comm: Bad parameters."); + if (rc) + *rc = TR_CFG_BAD_PARAMS; + goto cleanup; + } + + comm=tr_comm_new(tmp_ctx); + if (comm==NULL) { + tr_crit("tr_cfg_parse_one_comm: Out of memory."); + *rc = TR_CFG_NOMEM; + goto cleanup; + } + + + if ((NULL == (jid = json_object_get(jcomm, "community_id"))) || + (!json_is_string(jid)) || + (NULL == (jtype = json_object_get(jcomm, "type"))) || + (!json_is_string(jtype)) || + (NULL == (japcs = json_object_get(jcomm, "apcs"))) || + (!json_is_array(japcs)) || + (NULL == (jidps = json_object_get(jcomm, "idp_realms"))) || + (!json_is_array(jidps)) || + (NULL == (jrps = json_object_get(jcomm, "rp_realms"))) || + (!json_is_array(jrps))) { + tr_debug("tr_cfg_parse_one_comm: Error parsing Communities configuration."); + *rc = TR_CFG_NOPARSE; + comm=NULL; + goto cleanup; + } + + tr_comm_set_id(comm, tr_new_name(json_string_value(jid))); + if (NULL == tr_comm_get_id(comm)) { + tr_debug("tr_cfg_parse_one_comm: No memory for community id."); + *rc = TR_CFG_NOMEM; + comm=NULL; + goto cleanup; + } + + if (0 == strcmp(json_string_value(jtype), "apc")) { + comm->type = TR_COMM_APC; + } else if (0 == strcmp(json_string_value(jtype), "coi")) { + comm->type = TR_COMM_COI; + if (NULL == (comm->apcs = tr_cfg_parse_apcs(trc, japcs, rc))) { + tr_debug("tr_cfg_parse_one_comm: Can't parse APCs for COI %s.", + tr_comm_get_id(comm)->buf); + comm=NULL; + goto cleanup; + } + } else { + tr_debug("tr_cfg_parse_one_comm: Invalid community type, comm = %s, type = %s", + tr_comm_get_id(comm)->buf, json_string_value(jtype)); + *rc = TR_CFG_NOPARSE; + comm=NULL; + goto cleanup; + } + + tr_cfg_parse_comm_idps(trc, jidps, comm, rc); + if (TR_CFG_SUCCESS != *rc) { + tr_debug("tr_cfg_parse_one_comm: Can't parse IDP realms for comm %s.", + tr_comm_get_id(comm)->buf); + comm=NULL; + goto cleanup; + } + + tr_cfg_parse_comm_rps(trc, jrps, comm, rc); + if (TR_CFG_SUCCESS != *rc) { + tr_debug("tr_cfg_parse_one_comm: Can't parse RP realms for comm %s .", + tr_comm_get_id(comm)->buf); + comm=NULL; + goto cleanup; + } + + if (TR_COMM_APC == comm->type) { + json_t *jexpire = json_object_get(jcomm, "expiration_interval"); + comm->expiration_interval = 43200; /*30 days*/ + if (jexpire) { + if (!json_is_integer(jexpire)) { + tr_err("tr_parse_one_comm: expiration_interval is not an integer for comm %.*s", + tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf); + comm=NULL; + goto cleanup; + } + comm->expiration_interval = json_integer_value(jexpire); + if (comm->expiration_interval <= 10) { + comm->expiration_interval = 11; /* Freeradius waits 10 minutes between successful TR queries*/ + tr_notice( + "tr_parse_one_comm: expiration interval for %.*s less than minimum of 11 minutes; using 11 minutes instead.", + tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf); + } + if (comm->expiration_interval > 129600) { + /* > 90 days*/ + comm->expiration_interval = 129600; + tr_notice( + "tr_parse_one_comm: expiration interval for %.*s exceeds maximum of 90 days; using 90 days instead.", + tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf); + } + } + } + +cleanup: + if (comm!=NULL) + talloc_steal(mem_ctx, comm); + talloc_free(tmp_ctx); + return comm; +} + +TR_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg) +{ + json_t *jcomms = NULL; + TR_CFG_RC rc = TR_CFG_SUCCESS; + TR_COMM *comm = NULL; + int i = 0; + + if ((!trc) || (!jcfg)) { + tr_debug("tr_cfg_parse_comms: Bad Parameters."); + return TR_CFG_BAD_PARAMS; + } + + if (NULL != (jcomms = json_object_get(jcfg, "communities"))) { + if (!json_is_array(jcomms)) { + return TR_CFG_NOPARSE; + } + + for (i = 0; i < json_array_size(jcomms); i++) { + if (NULL == (comm = tr_cfg_parse_one_comm(NULL, /* TODO: use a talloc context */ + trc, + json_array_get(jcomms, i), + &rc))) { + return rc; + } + tr_debug("tr_cfg_parse_comms: Community configured: %s.", + tr_comm_get_id(comm)->buf); + + tr_comm_table_add_comm(trc->ctable, comm); + } + } + tr_debug("tr_cfg_parse_comms: Finished (rc=%d)", rc); + return rc; +} + +TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc) +{ + + TR_IDP_REALM *cfg_idp; + + if ((!tr_cfg) || (!idp_id)) { + if (rc) + *rc = TR_CFG_BAD_PARAMS; + return NULL; + } + + for (cfg_idp = tr_cfg->ctable->idp_realms; NULL != cfg_idp; cfg_idp = cfg_idp->next) { + if (!tr_name_cmp (idp_id, cfg_idp->realm_id)) { + tr_debug("tr_cfg_find_idp: Found %s.", idp_id->buf); + return cfg_idp; + } + } + /* if we didn't find one, return NULL */ + return NULL; +} + +TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc) +{ + TR_RP_CLIENT *cfg_rp; + + if ((!tr_cfg) || (!rp_gss)) { + if (rc) + *rc = TR_CFG_BAD_PARAMS; + return NULL; + } + + for (cfg_rp = tr_cfg->rp_clients; NULL != cfg_rp; cfg_rp = cfg_rp->next) { + if (tr_gss_names_matches(cfg_rp->gss_names, rp_gss)) { + tr_debug("tr_cfg_find_rp: Found %s.", rp_gss->buf); + return cfg_rp; + } + } + /* if we didn't find one, return NULL */ + return NULL; +} diff --git a/common/tr_config_encoders.c b/common/tr_config_encoders.c new file mode 100644 index 0000000..47331d9 --- /dev/null +++ b/common/tr_config_encoders.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2012, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +/* helper for below */ +#define OBJECT_SET_OR_FAIL(jobj, key, val) \ +do { \ + if (val) \ + json_object_set_new((jobj),(key),(val)); \ + else \ + goto cleanup; \ +} while (0) + +#define ARRAY_APPEND_OR_FAIL(jary, val) \ +do { \ + if (val) \ + json_array_append_new((jary),(val)); \ + else \ + goto cleanup; \ +} while (0) + +static json_t *tr_cfg_file_to_json(TR_CFG_FILE *cfg_file) +{ + json_t *file_json = NULL; + json_t *retval = NULL; + + file_json = json_object(); + if (file_json == NULL) + goto cleanup; + + OBJECT_SET_OR_FAIL(file_json, "name", json_string(cfg_file->name)); + if (cfg_file->serial != TR_CFG_INVALID_SERIAL) + OBJECT_SET_OR_FAIL(file_json, "serial", json_integer(cfg_file->serial)); + + /* succeeded - set the return value and increment the reference count */ + retval = file_json; + json_incref(retval); + +cleanup: + if (file_json) + json_decref(file_json); + + return retval; +} + +json_t *tr_cfg_files_to_json_array(TR_CFG *cfg) +{ + guint ii; + json_t *jarray = json_array(); + json_t *retval = NULL; + + if (jarray == NULL) + goto cleanup; + + for (ii=0; iifiles->len; ii++) { + ARRAY_APPEND_OR_FAIL(jarray, + tr_cfg_file_to_json( + &g_array_index(cfg->files, TR_CFG_FILE, ii))); + } + + /* success */ + retval = jarray; + json_incref(retval); + +cleanup: + if (jarray) + json_decref(jarray); + + return retval; +} diff --git a/common/tr_config_filters.c b/common/tr_config_filters.c new file mode 100644 index 0000000..4035e68 --- /dev/null +++ b/common/tr_config_filters.c @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2012-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if JANSSON_VERSION_HEX < 0x020500 +#include "jansson_iterators.h" +#endif + +static TR_CONSTRAINT *tr_cfg_parse_one_constraint(TALLOC_CTX *mem_ctx, char *ctype, json_t *jc, TR_CFG_RC *rc) +{ + TR_CONSTRAINT *cons=NULL; + int i=0; + + if ((!ctype) || (!jc) || (!rc) || + (!json_is_array(jc)) || + (0 >= json_array_size(jc)) || + (TR_MAX_CONST_MATCHES < json_array_size(jc)) || + (!json_is_string(json_array_get(jc, 0)))) { + tr_err("tr_cfg_parse_one_constraint: config error."); + *rc=TR_CFG_NOPARSE; + return NULL; + } + + if (NULL==(cons=tr_constraint_new(mem_ctx))) { + tr_debug("tr_cfg_parse_one_constraint: Out of memory (cons)."); + *rc=TR_CFG_NOMEM; + return NULL; + } + + if (NULL==(cons->type=tr_new_name(ctype))) { + tr_err("tr_cfg_parse_one_constraint: Out of memory (type)."); + *rc=TR_CFG_NOMEM; + tr_constraint_free(cons); + return NULL; + } + + for (i=0; i < json_array_size(jc); i++) { + cons->matches[i]=tr_new_name(json_string_value(json_array_get(jc, i))); + if (cons->matches[i]==NULL) { + tr_err("tr_cfg_parse_one_constraint: Out of memory (match %d).", i+1); + *rc=TR_CFG_NOMEM; + tr_constraint_free(cons); + return NULL; + } + } + + return cons; +} + +static TR_FILTER *tr_cfg_parse_one_filter(TALLOC_CTX *mem_ctx, json_t *jfilt, TR_FILTER_TYPE ftype, TR_CFG_RC *rc) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + TR_FILTER *filt = NULL; + json_t *jfaction = NULL; + json_t *jfline = NULL; + json_t *jfspecs = NULL; + json_t *this_jfspec = NULL; + json_t *jfield = NULL; + json_t *jmatch = NULL; + json_t *jrc = NULL; + json_t *jdc = NULL; + json_t *this_jmatch = NULL; + TR_NAME *name = NULL; + size_t i = 0, j = 0, k = 0; + + *rc = TR_CFG_ERROR; + + if ((jfilt == NULL) || (rc == NULL)) { + tr_err("tr_cfg_parse_one_filter: null argument"); + *rc = TR_CFG_BAD_PARAMS; + goto cleanup; + } + + if (NULL == (filt = tr_filter_new(tmp_ctx))) { + tr_err("tr_cfg_parse_one_filter: Out of memory."); + *rc = TR_CFG_NOMEM; + goto cleanup; + } + tr_filter_set_type(filt, ftype); + + /* make sure we have space to represent the filter */ + if (json_array_size(jfilt) > TR_MAX_FILTER_LINES) { + tr_err("tr_cfg_parse_one_filter: Filter has too many lines, maximum of %d.", TR_MAX_FILTER_LINES); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + + /* For each entry in the filter... */ + json_array_foreach(jfilt, i, jfline) { + if ((NULL == (jfaction = json_object_get(jfline, "action"))) || + (!json_is_string(jfaction))) { + tr_debug("tr_cfg_parse_one_filter: Error parsing filter action."); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + + if ((NULL == (jfspecs = json_object_get(jfline, "specs"))) || + (!json_is_array(jfspecs)) || + (0 == json_array_size(jfspecs))) { + tr_debug("tr_cfg_parse_one_filter: Error parsing filter specs."); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + + if (TR_MAX_FILTER_SPECS < json_array_size(jfspecs)) { + tr_debug("tr_cfg_parse_one_filter: Filter has too many specs, maximimum of %d.", TR_MAX_FILTER_SPECS); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + + if (NULL == (filt->lines[i] = tr_fline_new(filt))) { + tr_debug("tr_cfg_parse_one_filter: Out of memory allocating filter line %d.", i + 1); + *rc = TR_CFG_NOMEM; + goto cleanup; + } + + if (!strcmp(json_string_value(jfaction), "accept")) { + filt->lines[i]->action = TR_FILTER_ACTION_ACCEPT; + } else if (!strcmp(json_string_value(jfaction), "reject")) { + filt->lines[i]->action = TR_FILTER_ACTION_REJECT; + } else { + tr_debug("tr_cfg_parse_one_filter: Error parsing filter action, unknown action' %s'.", + json_string_value(jfaction)); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + + if (NULL != (jrc = json_object_get(jfline, "realm_constraints"))) { + if (!json_is_array(jrc)) { + tr_err("tr_cfg_parse_one_filter: cannot parse realm_constraints, not an array."); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } else if (json_array_size(jrc) > TR_MAX_CONST_MATCHES) { + tr_err("tr_cfg_parse_one_filter: realm_constraints has too many entries, maximum of %d.", + TR_MAX_CONST_MATCHES); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } else if (json_array_size(jrc) > 0) { + /* ok we actually have entries to process */ + if (NULL == (filt->lines[i]->realm_cons = tr_cfg_parse_one_constraint(filt->lines[i], "realm", jrc, rc))) { + tr_debug("tr_cfg_parse_one_filter: Error parsing realm constraint"); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + } + } + + if (NULL != (jdc = json_object_get(jfline, "domain_constraints"))) { + if (!json_is_array(jdc)) { + tr_err("tr_cfg_parse_one_filter: cannot parse domain_constraints, not an array."); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } else if (json_array_size(jdc) > TR_MAX_CONST_MATCHES) { + tr_err("tr_cfg_parse_one_filter: domain_constraints has too many entries, maximum of %d.", + TR_MAX_CONST_MATCHES); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } else if (json_array_size(jdc) > 0) { + if (NULL == (filt->lines[i]->domain_cons = tr_cfg_parse_one_constraint(filt->lines[i], "domain", jdc, rc))) { + tr_debug("tr_cfg_parse_one_filter: Error parsing domain constraint"); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + } + } + + /*For each filter spec within the filter line... */ + json_array_foreach(jfspecs, j, this_jfspec) { + if ((NULL == (jfield = json_object_get(this_jfspec, "field"))) || + (!json_is_string(jfield))) { + tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing field for filer spec %d, filter line %d.", i, + j); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + + /* check that we have a match attribute */ + if (NULL == (jmatch = json_object_get(this_jfspec, "match"))) { + tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing match for filer spec %d, filter line %d.", i, + j); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + + /* check that match is a string or an array */ + if ((!json_is_string(jmatch)) && (!json_is_array(jmatch))) { + tr_debug( + "tr_cfg_parse_one_filter: Error parsing filter: match not a string or array for filter spec %d, filter line %d.", + i, j); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + + /* allocate the filter spec */ + if (NULL == (filt->lines[i]->specs[j] = tr_fspec_new(filt->lines[i]))) { + tr_debug("tr_cfg_parse_one_filter: Out of memory."); + *rc = TR_CFG_NOMEM; + goto cleanup; + } + + /* fill in the field */ + if (NULL == (filt->lines[i]->specs[j]->field = tr_new_name(json_string_value(jfield)))) { + tr_debug("tr_cfg_parse_one_filter: Out of memory."); + *rc = TR_CFG_NOMEM; + goto cleanup; + } + + /* fill in the matches */ + if (json_is_string(jmatch)) { + if (NULL == (name = tr_new_name(json_string_value(jmatch)))) { + tr_debug("tr_cfg_parse_one_filter: Out of memory."); + *rc = TR_CFG_NOMEM; + goto cleanup; + } + tr_fspec_add_match(filt->lines[i]->specs[j], name); + } else { + /* jmatch is an array (we checked earlier) */ + json_array_foreach(jmatch, k, this_jmatch) { + if (NULL == (name = tr_new_name(json_string_value(this_jmatch)))) { + tr_debug("tr_cfg_parse_one_filter: Out of memory."); + *rc = TR_CFG_NOMEM; + goto cleanup; + } + tr_fspec_add_match(filt->lines[i]->specs[j], name); + } + } + if (!tr_filter_validate_spec_field(ftype, filt->lines[i]->specs[j])){ + tr_debug("tr_cfg_parse_one_filter: Invalid filter field \"%.*s\" for %s filter, spec %d, filter %d.", + filt->lines[i]->specs[j]->field->len, + filt->lines[i]->specs[j]->field->buf, + tr_filter_type_to_string(filt->type), + i, j); + *rc = TR_CFG_ERROR; + goto cleanup; + } + } + } + + /* check that the filter is valid */ + if (!tr_filter_validate(filt)) { + *rc = TR_CFG_ERROR; + } else { + *rc = TR_CFG_SUCCESS; + talloc_steal(mem_ctx, filt); + } + +cleanup: + talloc_free(tmp_ctx); + if (*rc!=TR_CFG_SUCCESS) + filt=NULL; + return filt; +} + +TR_FILTER_SET *tr_cfg_parse_filters(TALLOC_CTX *mem_ctx, json_t *jfilts, TR_CFG_RC *rc) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + json_t *jfilt; + const char *filt_label=NULL; + TR_FILTER *filt=NULL; + TR_FILTER_SET *filt_set=NULL; + TR_FILTER_TYPE filt_type=TR_FILTER_TYPE_UNKNOWN; + + *rc=TR_CFG_ERROR; + + /* no filters */ + if (jfilts==NULL) { + *rc=TR_CFG_SUCCESS; + goto cleanup; + } + + filt_set=tr_filter_set_new(tmp_ctx); + if (filt_set==NULL) { + tr_debug("tr_cfg_parse_filters: Unable to allocate filter set."); + *rc = TR_CFG_NOMEM; + goto cleanup; + } + + json_object_foreach(jfilts, filt_label, jfilt) { + /* check that we got a filter */ + if (jfilt == NULL) { + tr_debug("tr_cfg_parse_filters: Definition for %s filter is missing.", filt_label); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + + /* check that we recognize the filter type */ + filt_type=tr_filter_type_from_string(filt_label); + if (filt_type==TR_FILTER_TYPE_UNKNOWN) { + tr_debug("tr_cfg_parse_filters: Unrecognized filter (%s) defined.", filt_label); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + + /* finally, parse the filter */ + tr_debug("tr_cfg_parse_filters: Found %s filter.", filt_label); + filt = tr_cfg_parse_one_filter(tmp_ctx, jfilt, filt_type, rc); + tr_filter_set_add(filt_set, filt); + if (*rc != TR_CFG_SUCCESS) { + tr_debug("tr_cfg_parse_filters: Error parsing %s filter.", filt_label); + *rc = TR_CFG_NOPARSE; + goto cleanup; + } + } + + *rc=TR_CFG_SUCCESS; + +cleanup: + if (*rc==TR_CFG_SUCCESS) + talloc_steal(mem_ctx, filt_set); + else if (filt_set!=NULL) { + talloc_free(filt_set); + filt_set=NULL; + } + + talloc_free(tmp_ctx); + return filt_set; +} diff --git a/common/tr_config_internal.c b/common/tr_config_internal.c new file mode 100644 index 0000000..d06dc51 --- /dev/null +++ b/common/tr_config_internal.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2012-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include + +/** + * Parse a boolean + * + * If the key does not exist in the src object, returns success but does fill in *dest. + * + * @param src JSON object to pull a value from + * @param key key to pull + * @param dest (output) pointer to an allocated integer + * @return TR_CFG_SUCCESS or an error code + */ +static TR_CFG_RC tr_cfg_parse_boolean(json_t *src, const char *key, int *dest) +{ + json_t *jtmp; + + /* Validate parameters */ + if ((src == NULL) || (key == NULL) || (dest == NULL)) + return TR_CFG_BAD_PARAMS; + + /* See if we have a value for this key; do nothing if not */ + jtmp = json_object_get(src, key); + if (jtmp) { + if (json_is_boolean(jtmp)) { + *dest = json_boolean_value(jtmp); + } else { + tr_debug("tr_cfg_parse_unsigned: Parsing error, %s is not a boolean.", key); + return TR_CFG_NOPARSE; + } + } + + return TR_CFG_SUCCESS; +} + +/** + * Parse an unsigned integer + * + * If the key does not exist in the src object, returns success but does fill in *dest. + * + * @param src JSON object to pull a value from + * @param key key to pull + * @param dest (output) pointer to an allocated unsigned integer + * @return TR_CFG_SUCCESS or an error code + */ +static TR_CFG_RC tr_cfg_parse_unsigned(json_t *src, const char *key, unsigned int *dest) +{ + json_t *jtmp; + + /* Validate parameters */ + if ((src == NULL) || (key == NULL) || (dest == NULL)) + return TR_CFG_BAD_PARAMS; + + /* See if we have a value for this key; do nothing if not */ + jtmp = json_object_get(src, key); + if (jtmp) { + if (json_is_number(jtmp)) { + *dest = (unsigned int) json_integer_value(jtmp); + } else { + tr_debug("tr_cfg_parse_unsigned: Parsing error, %s is not a number.", key); + return TR_CFG_NOPARSE; + } + } + + return TR_CFG_SUCCESS; +} + +/** + * Parse a string + * + * If the key does not exist in the src object, returns success but does not allocate + * a return value in dest. Nulls the destination pointer if there is no return value. + * + * Return value is allocated in talloc's NULL context and must be freed with talloc_free() + * or put into a non-NULL context with talloc_steal() + * + * @param src JSON object to pull a value from + * @param key key to pull + * @param dest (output) pointer to a pointer that will hold the newly allocated return value + * @return TR_CFG_SUCCESS or an error code + */ +static TR_CFG_RC tr_cfg_parse_string(json_t *src, const char *key, const char **dest) +{ + json_t *jtmp; + + /* Validate parameters */ + if ((src == NULL) || (key == NULL) || (dest == NULL)) + return TR_CFG_BAD_PARAMS; + + /* See if we have a value for this key; do nothing if not */ + jtmp = json_object_get(src, key); + if (!jtmp) { + *dest = NULL; /* No return value, null this out */ + } else { + if (json_is_string(jtmp)) { + *dest = talloc_strdup(NULL, json_string_value(jtmp)); + } else { + tr_debug("tr_cfg_parse_string: Parsing error, %s is not a string.", key); + return TR_CFG_NOPARSE; + } + } + + return TR_CFG_SUCCESS; +} + +/** + * Set default values for settings that have them + * + * @param cfg configuration structure to fill in, not null + */ +static void set_defaults(TR_CFG_INTERNAL *cfg) +{ + cfg->max_tree_depth = TR_DEFAULT_MAX_TREE_DEPTH; + cfg->tids_port = TR_DEFAULT_TIDS_PORT; + cfg->trps_port = TR_DEFAULT_TRPS_PORT; + cfg->monitoring_port = TR_DEFAULT_MONITORING_PORT; + cfg->cfg_poll_interval = TR_CFGWATCH_DEFAULT_POLL; + cfg->cfg_settling_time = TR_CFGWATCH_DEFAULT_SETTLE; + cfg->trp_connect_interval = TR_DEFAULT_TRP_CONNECT_INTERVAL; + cfg->trp_sweep_interval = TR_DEFAULT_TRP_SWEEP_INTERVAL; + cfg->trp_update_interval = TR_DEFAULT_TRP_UPDATE_INTERVAL; + cfg->tid_req_timeout = TR_DEFAULT_TID_REQ_TIMEOUT; + cfg->tid_resp_numer = TR_DEFAULT_TID_RESP_NUMER; + cfg->tid_resp_denom = TR_DEFAULT_TID_RESP_DENOM; + cfg->log_threshold = TR_DEFAULT_LOG_THRESHOLD; + cfg->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD; + cfg->monitoring_credentials = NULL; +} + +/* Helper that checks return value of a parse fn and returns if it failed */ +#define NOPARSE_UNLESS(x) \ +do { \ + if ((x) != TR_CFG_SUCCESS) \ + return TR_CFG_NOPARSE; \ +} while(0) + +static TR_CFG_RC tr_cfg_parse_monitoring(TR_CFG *trc, json_t *jmon) +{ + int enabled = 1; /* assume we are enabled unless we are told not to be */ + + NOPARSE_UNLESS(tr_cfg_parse_boolean(jmon, "enabled", &enabled)); + if (enabled) { + NOPARSE_UNLESS(tr_cfg_parse_unsigned(jmon, "port", &(trc->internal->monitoring_port))); + NOPARSE_UNLESS(tr_cfg_parse_gss_names(trc->internal, + json_object_get(jmon, "authorized_credentials"), + &(trc->internal->monitoring_credentials))); + } + + return TR_CFG_SUCCESS; +} + +/** + * Parse internal configuration JSON + * + * @param trc configuration structure to fill in + * @param jint internal configuration JSON object + * @return TR_CFG_SUCCESS or an error code + */ +TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jint) +{ + json_t *jtmp = NULL; + const char *s = NULL; + + if ((!trc) || (!jint)) + return TR_CFG_BAD_PARAMS; + + /* If we don't yet have an internal config, allocate one and set defaults. If it + * already exists, do not disturb existing settings. */ + if (NULL == trc->internal) { + if (NULL == (trc->internal = talloc_zero(trc, TR_CFG_INTERNAL))) + return TR_CFG_NOMEM; + set_defaults(trc->internal); /* Install defaults for any unspecified settings */ + } + + NOPARSE_UNLESS(tr_cfg_parse_string(jint, "hostname", &(trc->internal->hostname))); + talloc_steal(trc->internal, trc->internal->hostname); + + NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "max_tree_depth", &(trc->internal->max_tree_depth))); + NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "tids_port", &(trc->internal->tids_port))); + NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "trps_port", &(trc->internal->trps_port))); + NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "cfg_poll_interval", &(trc->internal->cfg_poll_interval))); + NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "cfg_settling_time", &(trc->internal->cfg_settling_time))); + NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "trp_connect_interval", &(trc->internal->trp_connect_interval))); + NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "trp_sweep_interval", &(trc->internal->trp_sweep_interval))); + NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "trp_update_interval", &(trc->internal->trp_update_interval))); + NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "tid_request_timeout", &(trc->internal->tid_req_timeout))); + NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "tid_response_numerator", &(trc->internal->tid_resp_numer))); + NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "tid_response_denominator", &(trc->internal->tid_resp_denom))); + + /* Parse the logging section */ + if (NULL != (jtmp = json_object_get(jint, "logging"))) { + NOPARSE_UNLESS(tr_cfg_parse_string(jtmp, "log_threshold", &s)); + if (s) { + trc->internal->log_threshold = str2sev(s); + talloc_free((void *) s); + } + + NOPARSE_UNLESS(tr_cfg_parse_string(jtmp, "console_threshold", &s)); + if (s) { + trc->internal->console_threshold = str2sev(s); + talloc_free((void *) s); + } + } + + /* Parse the monitoring section */ + if (NULL != (jtmp = json_object_get(jint, "monitoring"))) { + NOPARSE_UNLESS(tr_cfg_parse_monitoring(trc, jtmp)); + } + + tr_debug("tr_cfg_parse_internal: Internal config parsed."); + return TR_CFG_SUCCESS; +} diff --git a/common/tr_config_orgs.c b/common/tr_config_orgs.c new file mode 100644 index 0000000..f6925ca --- /dev/null +++ b/common/tr_config_orgs.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2012-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if JANSSON_VERSION_HEX < 0x020500 +#include "jansson_iterators.h" +#endif + +/* takes a talloc context, but currently does not use it */ +static TR_NAME *tr_cfg_parse_org_name(TALLOC_CTX *mem_ctx, json_t *j_org, TR_CFG_RC *rc) +{ + TR_NAME *name=NULL; + + if ((j_org==NULL) || (rc==NULL) || (!json_is_string(j_org))) { + tr_debug("tr_cfg_parse_org_name: Bad parameters."); + if (rc!=NULL) + *rc = TR_CFG_BAD_PARAMS; /* fill in return value if we can */ + return NULL; + } + + name=tr_new_name(json_string_value(j_org)); + if (name==NULL) + *rc=TR_CFG_NOMEM; + else + *rc=TR_CFG_SUCCESS; + return name; +} + +static TR_CFG_RC tr_cfg_parse_one_local_org(TR_CFG *trc, json_t *jlorg) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_CFG_RC retval=TR_CFG_ERROR; /* our return code */ + TR_CFG_RC rc=TR_CFG_ERROR; /* return code from functions we call */ + TR_NAME *org_name=NULL; + json_t *j_org=NULL; + json_t *j_realms=NULL; + TR_IDP_REALM *new_idp_realms=NULL; + TR_RP_CLIENT *new_rp_clients=NULL; + + tr_debug("tr_cfg_parse_one_local_org: parsing local organization"); + + /* get organization_name (optional) */ + if (NULL==(j_org=json_object_get(jlorg, "organization_name"))) { + tr_debug("tr_cfg_parse_one_local_org: organization_name unspecified"); + } else { + org_name=tr_cfg_parse_org_name(tmp_ctx, j_org, &rc); + if (rc==TR_CFG_SUCCESS) { + tr_debug("tr_cfg_parse_one_local_org: organization_name=\"%.*s\"", + org_name->len, + org_name->buf); + /* we don't actually do anything with this, but we could */ + tr_free_name(org_name); + org_name=NULL; + } + } + + /* Now get realms. Allow this to be missing; even though that is a pointless organization entry, + * it's harmless. Report a warning because that might be unintentional. */ + if (NULL==(j_realms=json_object_get(jlorg, "realms"))) { + tr_warning("tr_cfg_parse_one_local_org: warning - no realms in this local organization"); + } else { + /* Allocate in the tmp_ctx so these will be cleaned up if we do not complete successfully. */ + new_idp_realms=tr_cfg_parse_idp_realms(tmp_ctx, j_realms, &rc); + if (rc!=TR_CFG_SUCCESS) + goto cleanup; + + new_rp_clients=tr_cfg_parse_rp_clients(tmp_ctx, j_realms, &rc); + if (rc!=TR_CFG_SUCCESS) + goto cleanup; + } + retval=TR_CFG_SUCCESS; + +cleanup: + /* if we succeeded, link things to the configuration and move out of tmp context */ + if (retval==TR_CFG_SUCCESS) { + if (new_idp_realms!=NULL) { + tr_idp_realm_add(trc->ctable->idp_realms, new_idp_realms); /* fixes talloc contexts except for head*/ + talloc_steal(trc, trc->ctable->idp_realms); /* make sure the head is in the right context */ + } + + if (new_rp_clients!=NULL) { + tr_rp_client_add(trc->rp_clients, new_rp_clients); /* fixes talloc contexts */ + talloc_steal(trc, trc->rp_clients); /* make sure head is in the right context */ + } + } + + talloc_free(tmp_ctx); + return rc; +} + +/* Parse local organizations if present. Returns success if there are none. On failure, the configuration is unreliable. */ +TR_CFG_RC tr_cfg_parse_local_orgs(TR_CFG *trc, json_t *jcfg) +{ + json_t *jlocorgs=NULL; + size_t ii=0; + + jlocorgs=json_object_get(jcfg, "local_organizations"); + if (jlocorgs==NULL) + return TR_CFG_SUCCESS; + + if (!json_is_array(jlocorgs)) { + tr_err("tr_cfg_parse_local_orgs: local_organizations is not an array."); + return TR_CFG_NOPARSE; + } + + for (ii=0; iipeers, new_peer); + rc=TR_CFG_SUCCESS; + +cleanup: + talloc_free(tmp_ctx); + return rc; +} + +/* Parse peer organizations, if present. Returns success if there are none. */ +TR_CFG_RC tr_cfg_parse_peer_orgs(TR_CFG *trc, json_t *jcfg) +{ + json_t *jpeerorgs=NULL; + int ii=0; + + jpeerorgs=json_object_get(jcfg, "peer_organizations"); + if (jpeerorgs==NULL) + return TR_CFG_SUCCESS; + + if (!json_is_array(jpeerorgs)) { + tr_err("tr_cfg_parse_peer_orgs: peer_organizations is not an array."); + return TR_CFG_NOPARSE; + } + + for (ii=0; ii +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if JANSSON_VERSION_HEX < 0x020500 +#include "jansson_iterators.h" +#endif + +TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc) +{ + TR_AAA_SERVER *aaa = NULL; + TR_NAME *name=NULL; + + if ((!jaddr) || (!json_is_string(jaddr))) { + tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters."); + *rc = TR_CFG_BAD_PARAMS; + return NULL; + } + + name=tr_new_name(json_string_value(jaddr)); + if (name==NULL) { + tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating hostname."); + *rc = TR_CFG_NOMEM; + return NULL; + } + + aaa=tr_aaa_server_new(mem_ctx, name); + if (aaa==NULL) { + tr_free_name(name); + tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server."); + *rc = TR_CFG_NOMEM; + return NULL; + } + + return aaa; +} + +static TR_AAA_SERVER *tr_cfg_parse_aaa_servers(TALLOC_CTX *mem_ctx, json_t *jaaas, TR_CFG_RC *rc) +{ + TALLOC_CTX *tmp_ctx=NULL; + TR_AAA_SERVER *aaa = NULL; + TR_AAA_SERVER *temp_aaa = NULL; + int i = 0; + + for (i = 0; i < json_array_size(jaaas); i++) { + /* rc gets set in here */ + if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(tmp_ctx, json_array_get(jaaas, i), rc))) { + talloc_free(tmp_ctx); + return NULL; + } + /* TBD -- IPv6 addresses */ + // tr_debug("tr_cfg_parse_aaa_servers: Configuring AAA Server: ip_addr = %s.", inet_ntoa(temp_aaa->aaa_server_addr)); + temp_aaa->next = aaa; + aaa = temp_aaa; + } + tr_debug("tr_cfg_parse_aaa_servers: Finished (rc=%d)", *rc); + + for (temp_aaa=aaa; temp_aaa!=NULL; temp_aaa=temp_aaa->next) + talloc_steal(mem_ctx, temp_aaa); + talloc_free(tmp_ctx); + return aaa; +} + +static TR_APC *tr_cfg_parse_one_apc(TALLOC_CTX *mem_ctx, json_t *japc, TR_CFG_RC *rc) +{ + TR_APC *apc=NULL; + TR_NAME *name=NULL; + + *rc = TR_CFG_SUCCESS; /* presume success */ + + if ((!japc) || (!rc) || (!json_is_string(japc))) { + tr_debug("tr_cfg_parse_one_apc: Bad parameters."); + if (rc) + *rc = TR_CFG_BAD_PARAMS; + return NULL; + } + + apc=tr_apc_new(mem_ctx); + if (apc==NULL) { + tr_debug("tr_cfg_parse_one_apc: Out of memory."); + *rc = TR_CFG_NOMEM; + return NULL; + } + + name=tr_new_name(json_string_value(japc)); + if (name==NULL) { + tr_debug("tr_cfg_parse_one_apc: No memory for APC name."); + tr_apc_free(apc); + *rc = TR_CFG_NOMEM; + return NULL; + } + tr_apc_set_id(apc, name); /* apc is now responsible for freeing the name */ + + return apc; +} + +TR_APC *tr_cfg_parse_apcs(TALLOC_CTX *mem_ctx, json_t *japcs, TR_CFG_RC *rc) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_APC *apcs=NULL; + TR_APC *new_apc=NULL; + int ii=0; + TR_CFG_RC call_rc=TR_CFG_ERROR; + + *rc = TR_CFG_SUCCESS; /* presume success */ + + if ((!japcs) || (!rc) || (!json_is_array(japcs))) { + tr_debug("tr_cfg_parse_one_apc: Bad parameters."); + if (rc) + *rc = TR_CFG_BAD_PARAMS; + return NULL; + } + + for (ii=0; iishared_config=tr_cfg_parse_shared_config(json_object_get(jidp, "shared_config"), &rc); + if (rc!=TR_CFG_SUCCESS) { + tr_err("tr_cfg_parse_idp: missing or malformed shared_config specification"); + rc=TR_CFG_NOPARSE; + goto cleanup; + } + + apcs=tr_cfg_parse_apcs(tmp_ctx, json_object_get(jidp, "apcs"), &rc); + if ((rc!=TR_CFG_SUCCESS) || (apcs==NULL)) { + tr_err("tr_cfg_parse_idp: unable to parse APC"); + rc=TR_CFG_NOPARSE; + goto cleanup; + } + + aaa=tr_cfg_parse_aaa_servers(idp, json_object_get(jidp, "aaa_servers"), &rc); + if (rc!=TR_CFG_SUCCESS) { + tr_err("tr_cfg_parse_idp: unable to parse AAA servers"); + rc=TR_CFG_NOPARSE; + goto cleanup; + } + + tr_debug("tr_cfg_parse_idp: APC=\"%.*s\"", + apcs->id->len, + apcs->id->buf); + + /* done, fill in the idp structures */ + idp->apcs=apcs; + talloc_steal(idp, apcs); + idp->aaa_servers=aaa; + rc=TR_CFG_SUCCESS; + +cleanup: + if (rc!=TR_CFG_SUCCESS) { + if (apcs!=NULL) + tr_apc_free(apcs); + if (aaa!=NULL) + tr_aaa_server_free(aaa); + } + + talloc_free(tmp_ctx); + return rc; +} + +/* parses idp realm */ +static TR_IDP_REALM *tr_cfg_parse_one_idp_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_IDP_REALM *realm=NULL; + TR_CFG_RC call_rc=TR_CFG_ERROR; + + *rc=TR_CFG_ERROR; /* default to error if not set */ + + if ((!jrealm) || (!rc)) { + tr_err("tr_cfg_parse_one_idp_realm: Bad parameters."); + if (rc) + *rc=TR_CFG_BAD_PARAMS; + goto cleanup; + } + + if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) { + tr_err("tr_cfg_parse_one_idp_realm: could not allocate idp realm."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + + realm->origin=tr_cfg_realm_origin(jrealm); + if (realm->origin!=TR_REALM_LOCAL) { + tr_debug("tr_cfg_parse_one_idp_realm: realm is remote, should not have full IdP info."); + *rc=TR_CFG_NOPARSE; + goto cleanup; + } + + /* must have a name */ + realm->realm_id=tr_cfg_parse_name(realm, + json_object_get(jrealm, "realm"), + &call_rc); + if ((call_rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) { + tr_err("tr_cfg_parse_one_idp_realm: could not parse realm name"); + *rc=TR_CFG_NOPARSE; + goto cleanup; + } + tr_debug("tr_cfg_parse_one_idp_realm: realm_id=\"%.*s\"", + realm->realm_id->len, + realm->realm_id->buf); + + call_rc=tr_cfg_parse_idp(realm, json_object_get(jrealm, "identity_provider")); + if (call_rc!=TR_CFG_SUCCESS) { + tr_err("tr_cfg_parse_one_idp_realm: could not parse identity_provider."); + *rc=TR_CFG_NOPARSE; + goto cleanup; + } + + *rc=TR_CFG_SUCCESS; + +cleanup: + if (*rc==TR_CFG_SUCCESS) + talloc_steal(mem_ctx, realm); + else { + talloc_free(realm); + realm=NULL; + } + + talloc_free(tmp_ctx); + return realm; +} + +/* Determine whether the realm is an IDP realm */ +static int tr_cfg_is_idp_realm(json_t *jrealm) +{ + /* If a realm spec contains an identity_provider, it's an IDP realm. */ + if (NULL != json_object_get(jrealm, "identity_provider")) + return 1; + else + return 0; +} + +static TR_IDP_REALM *tr_cfg_parse_one_remote_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_IDP_REALM *realm=talloc(mem_ctx, TR_IDP_REALM); + + *rc=TR_CFG_ERROR; /* default to error if not set */ + + if ((!jrealm) || (!rc)) { + tr_err("tr_cfg_parse_one_remote_realm: Bad parameters."); + if (rc) + *rc=TR_CFG_BAD_PARAMS; + goto cleanup; + } + + if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) { + tr_err("tr_cfg_parse_one_remote_realm: could not allocate idp realm."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + + /* must have a name */ + realm->realm_id=tr_cfg_parse_name(realm, + json_object_get(jrealm, "realm"), + rc); + if ((*rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) { + tr_err("tr_cfg_parse_one_remote_realm: could not parse realm name"); + *rc=TR_CFG_NOPARSE; + goto cleanup; + } + tr_debug("tr_cfg_parse_one_remote_realm: realm_id=\"%.*s\"", + realm->realm_id->len, + realm->realm_id->buf); + + realm->origin=tr_cfg_realm_origin(jrealm); + *rc=TR_CFG_SUCCESS; + +cleanup: + if (*rc==TR_CFG_SUCCESS) + talloc_steal(mem_ctx, realm); + else { + talloc_free(realm); + realm=NULL; + } + + talloc_free(tmp_ctx); + return realm; +} + +static int tr_cfg_is_remote_realm(json_t *jrealm) +{ + return (tr_cfg_realm_origin(jrealm)!=TR_REALM_LOCAL); +} + +/* Parse any idp realms in the j_realms object. Ignores other realm types. */ +TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_IDP_REALM *realms=NULL; + TR_IDP_REALM *new_realm=NULL; + json_t *this_jrealm=NULL; + int ii=0; + + *rc=TR_CFG_ERROR; + if ((jrealms==NULL) || (!json_is_array(jrealms))) { + tr_err("tr_cfg_parse_idp_realms: realms not an array"); + *rc=TR_CFG_BAD_PARAMS; + goto cleanup; + } + + for (ii=0; ii +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if JANSSON_VERSION_HEX < 0x020500 +#include "jansson_iterators.h" +#endif + + +TR_CFG_RC tr_cfg_parse_gss_names(TALLOC_CTX *mem_ctx, json_t *jgss_names, TR_GSS_NAMES **gssn_out) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_GSS_NAMES *gn=NULL; + json_t *jname=NULL; + size_t ii=0; + TR_NAME *name=NULL; + TR_CFG_RC rc = TR_CFG_ERROR; + + if (jgss_names==NULL) { + tr_err("tr_cfg_parse_gss_names: Bad parameters."); + rc=TR_CFG_BAD_PARAMS; + goto cleanup; + } + + if (!json_is_array(jgss_names)) { + tr_err("tr_cfg_parse_gss_names: gss_names not an array."); + rc=TR_CFG_NOPARSE; + goto cleanup; + } + + gn=tr_gss_names_new(tmp_ctx); + for (ii=0; iilines[0]=tr_fline_new(filt); + if (filt->lines[0]==NULL) { + tr_debug("tr_cfg_default_filters: could not allocate filter line."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + + filt->lines[0]->action=TR_FILTER_ACTION_ACCEPT; + filt->lines[0]->specs[0]=tr_fspec_new(filt->lines[0]); + filt->lines[0]->specs[0]->field=n_rp_realm_1; + n_rp_realm_1=NULL; /* we don't own this name any more */ + + name=tr_dup_name(realm); + if (name==NULL) { + tr_debug("tr_cfg_default_filters: could not allocate realm name."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + tr_fspec_add_match(filt->lines[0]->specs[0], name); + name=NULL; /* we no longer own the name */ + + /* now do the wildcard name */ + filt->lines[0]->specs[1]=tr_fspec_new(filt->lines[0]); + filt->lines[0]->specs[1]->field=n_rp_realm_2; + n_rp_realm_2=NULL; /* we don't own this name any more */ + + if (NULL==(name=tr_name_cat(n_prefix, realm))) { + tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + + tr_fspec_add_match(filt->lines[0]->specs[1], name); + name=NULL; /* we no longer own the name */ + + /* domain constraint */ + if (NULL==(cons=tr_constraint_new(filt->lines[0]))) { + tr_debug("tr_cfg_default_filters: could not allocate domain constraint."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + + cons->type=n_domain; + n_domain=NULL; /* belongs to the constraint now */ + name=tr_dup_name(realm); + if (name==NULL) { + tr_debug("tr_cfg_default_filters: could not allocate realm name for domain constraint."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + cons->matches[0]=name; + name=tr_name_cat(n_prefix, realm); + if (name==NULL) { + tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name for domain constraint."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + cons->matches[1]=name; + name=NULL; + filt->lines[0]->domain_cons=cons; + + + /* realm constraint */ + if (NULL==(cons=tr_constraint_new(filt->lines[0]))) { + tr_debug("tr_cfg_default_filters: could not allocate realm constraint."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + + cons->type=n_realm; + n_realm=NULL; /* belongs to the constraint now */ + name=tr_dup_name(realm); + if (name==NULL) { + tr_debug("tr_cfg_default_filters: could not allocate realm name for realm constraint."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + cons->matches[0]=name; + name=tr_name_cat(n_prefix, realm); + if (name==NULL) { + tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name for realm constraint."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + cons->matches[1]=name; + name=NULL; + filt->lines[0]->realm_cons=cons; + + /* put the filter in a set */ + filt_set=tr_filter_set_new(tmp_ctx); + if ((filt_set==NULL)||(0!=tr_filter_set_add(filt_set, filt))) { + tr_debug("tr_cfg_default_filters: could not allocate filter set."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + talloc_steal(mem_ctx, filt_set); + +cleanup: + talloc_free(tmp_ctx); + + if (*rc!=TR_CFG_SUCCESS) + filt=NULL; + + if (n_prefix!=NULL) + tr_free_name(n_prefix); + if (n_rp_realm_1!=NULL) + tr_free_name(n_rp_realm_1); + if (n_rp_realm_2!=NULL) + tr_free_name(n_rp_realm_2); + if (n_realm!=NULL) + tr_free_name(n_realm); + if (n_domain!=NULL) + tr_free_name(n_domain); + if (name!=NULL) + tr_free_name(name); + + return filt_set; +} + +/* parses rp client */ +static TR_RP_CLIENT *tr_cfg_parse_one_rp_client(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_RP_CLIENT *client=NULL; + TR_CFG_RC call_rc=TR_CFG_ERROR; + TR_FILTER_SET *new_filts=NULL; + TR_NAME *realm=NULL; + json_t *jfilt=NULL; + json_t *jrealm_id=NULL; + + *rc=TR_CFG_ERROR; /* default to error if not set */ + + if ((!jrealm) || (!rc)) { + tr_err("tr_cfg_parse_one_rp_client: Bad parameters."); + if (rc) + *rc=TR_CFG_BAD_PARAMS; + goto cleanup; + } + + if ((NULL==(jrealm_id=json_object_get(jrealm, "realm"))) || (!json_is_string(jrealm_id))) { + tr_debug("tr_cfg_parse_one_rp_client: no realm ID found."); + *rc=TR_CFG_BAD_PARAMS; + goto cleanup; + } + + tr_debug("tr_cfg_parse_one_rp_client: realm_id=\"%s\"", json_string_value(jrealm_id)); + realm=tr_new_name(json_string_value(jrealm_id)); + if (realm==NULL) { + tr_err("tr_cfg_parse_one_rp_client: could not allocate realm ID."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + + if (NULL==(client=tr_rp_client_new(tmp_ctx))) { + tr_err("tr_cfg_parse_one_rp_client: could not allocate rp client."); + *rc=TR_CFG_NOMEM; + goto cleanup; + } + + call_rc = tr_cfg_parse_gss_names(client, json_object_get(jrealm, "gss_names"), &(client->gss_names)); + + if (call_rc!=TR_CFG_SUCCESS) { + tr_err("tr_cfg_parse_one_rp_client: could not parse gss_names."); + *rc=TR_CFG_NOPARSE; + goto cleanup; + } + + /* parse filters */ + jfilt=json_object_get(jrealm, "filters"); + if (jfilt!=NULL) { + new_filts=tr_cfg_parse_filters(tmp_ctx, jfilt, &call_rc); + if (call_rc!=TR_CFG_SUCCESS) { + tr_err("tr_cfg_parse_one_rp_client: could not parse filters."); + *rc=TR_CFG_NOPARSE; + goto cleanup; + } + } else { + tr_debug("tr_cfg_parse_one_rp_client: no filters specified, using default filters."); + new_filts= tr_cfg_default_filters(tmp_ctx, realm, &call_rc); + if (call_rc!=TR_CFG_SUCCESS) { + tr_err("tr_cfg_parse_one_rp_client: could not set default filters."); + *rc=TR_CFG_NOPARSE; + goto cleanup; + } + } + + tr_rp_client_set_filters(client, new_filts); + *rc=TR_CFG_SUCCESS; + +cleanup: + if (realm!=NULL) + tr_free_name(realm); + + if (*rc==TR_CFG_SUCCESS) + talloc_steal(mem_ctx, client); + else { + talloc_free(client); + client=NULL; + } + + talloc_free(tmp_ctx); + return client; +} + +/* Determine whether the realm is an RP realm */ +static int tr_cfg_is_rp_realm(json_t *jrealm) +{ + /* Check that we have a gss name. */ + if (NULL != json_object_get(jrealm, "gss_names")) + return 1; + else + return 0; +} + +/* Parse any rp clients in the j_realms object. Ignores other realms. */ +TR_RP_CLIENT *tr_cfg_parse_rp_clients(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_RP_CLIENT *clients=NULL; + TR_RP_CLIENT *new_client=NULL; + json_t *this_jrealm=NULL; + int ii=0; + + *rc=TR_CFG_ERROR; + if ((jrealms==NULL) || (!json_is_array(jrealms))) { + tr_err("tr_cfg_parse_rp_clients: realms not an array"); + *rc=TR_CFG_BAD_PARAMS; + goto cleanup; + } + + for (ii=0; ii +#include + +#include + +/* helper for below */ +#define OBJECT_SET_OR_FAIL(jobj, key, val) \ +do { \ + if (val) \ + json_object_set_new((jobj),(key),(val)); \ + else \ + goto cleanup; \ +} while (0) + +#define ARRAY_APPEND_OR_FAIL(jary, val) \ +do { \ + if (val) \ + json_array_append_new((jary),(val)); \ + else \ + goto cleanup; \ +} while (0) + + +typedef json_t *(ITEM_ENCODER_FUNC)(void *); + +static json_t *items_to_json_array(void *items[], ITEM_ENCODER_FUNC *item_encoder, size_t max_items) +{ + size_t ii; + json_t *jarray = json_array(); + json_t *retval = NULL; + + if (jarray == NULL) + goto cleanup; + + for (ii=0; iifield)); + OBJECT_SET_OR_FAIL(fspec_json, "matches", + items_to_json_array((void **)fspec->match, + (ITEM_ENCODER_FUNC *) tr_name_to_json_string, + TR_MAX_FILTER_SPEC_MATCHES)); + + /* succeeded - set the return value and increment the reference count */ + retval = fspec_json; + json_incref(retval); + +cleanup: + if (fspec_json) + json_decref(fspec_json); + return retval; +} + +static json_t *tr_fline_to_json(TR_FLINE *fline) +{ + json_t *fline_json = NULL; + json_t *retval = NULL; + + fline_json = json_object(); + if (fline_json == NULL) + goto cleanup; + + OBJECT_SET_OR_FAIL(fline_json, "action", + json_string( (fline->action == TR_FILTER_ACTION_ACCEPT) ? "accept" : "reject")); + OBJECT_SET_OR_FAIL(fline_json, "specs", + items_to_json_array((void **)fline->specs, + (ITEM_ENCODER_FUNC *) tr_fspec_to_json, + TR_MAX_FILTER_SPECS)); + if (fline->realm_cons) { + OBJECT_SET_OR_FAIL(fline_json, "realm_constraints", + items_to_json_array((void **) fline->realm_cons->matches, + (ITEM_ENCODER_FUNC *) tr_name_to_json_string, + TR_MAX_CONST_MATCHES)); + } + if (fline->domain_cons) { + OBJECT_SET_OR_FAIL(fline_json, "domain_constraints", + items_to_json_array((void **) fline->domain_cons->matches, + (ITEM_ENCODER_FUNC *) tr_name_to_json_string, + TR_MAX_CONST_MATCHES)); + } + + /* succeeded - set the return value and increment the reference count */ + retval = fline_json; + json_incref(retval); + +cleanup: + if (fline_json) + json_decref(fline_json); + return retval; +} + +json_t *tr_filter_set_to_json(TR_FILTER_SET *filter_set) +{ + json_t *fset_json = NULL; + json_t *retval = NULL; + TR_FILTER *filt = NULL; + TR_FILTER_TYPE *filt_type = NULL; + TR_FILTER_TYPE types[] = { + TR_FILTER_TYPE_TID_INBOUND, + TR_FILTER_TYPE_TRP_INBOUND, + TR_FILTER_TYPE_TRP_OUTBOUND, + TR_FILTER_TYPE_UNKNOWN /* list terminator */ + }; + + fset_json = json_object(); + if (fset_json == NULL) + goto cleanup; + + for (filt_type = types; *filt_type != TR_FILTER_TYPE_UNKNOWN; filt_type++) { + filt = tr_filter_set_get(filter_set, *filt_type); + if (filt) { + OBJECT_SET_OR_FAIL(fset_json, tr_filter_type_to_string(*filt_type), + items_to_json_array((void **)filt->lines, + (ITEM_ENCODER_FUNC *) tr_fline_to_json, + TR_MAX_FILTER_LINES)); + } + } + + /* succeeded - set the return value and increment the reference count */ + retval = fset_json; + json_incref(retval); + +cleanup: + if (fset_json) + json_decref(fset_json); + return retval; +} + diff --git a/common/tr_gss.c b/common/tr_gss.c index 7fa6876..e7902ee 100644 --- a/common/tr_gss.c +++ b/common/tr_gss.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, JANET(UK) + * Copyright (c) 2018, JANET(UK) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,100 +33,267 @@ */ #include +#include +#include +#include +#include +#include #include -static int tr_gss_names_destructor(void *obj) +/** + * tr_gss.c - GSS connection handler + * + * The chief entry point to this module is tr_gss_handle_connection(). This + * function accepts an incoming socket connection, runs the GSS authorization + * and authentication process, accepts a request, processes it, then sends + * the reply and returns without closing the connection. + * + * Callers need to provide two callbacks, each with a cookie for passing + * custom data to the callback. + * + * * TR_GSS_AUTH_FN auth_cb: Authorization callback + * - This callback is used during the GSS auth process to determine whether + * a credential should be authorized to connect. + * + * * TR_GSS_HANDLE_REQ_FN req_cb: Request handler callback + * - After auth, this callback is passed the string form of the incoming request. + * It should process the request and return a string form of the outgoing + * response, if any. + */ + +typedef struct tr_gss_cookie { + TR_GSS_AUTH_FN *auth_cb; + void *auth_cookie; +} TR_GSS_COOKIE; + +static int tr_gss_auth_cb(gss_name_t clientName, gss_buffer_t displayName, void *data) { - TR_GSS_NAMES *gss_names=talloc_get_type_abort(obj, TR_GSS_NAMES); - int ii=0; + TR_GSS_COOKIE *cookie = talloc_get_type_abort(data, TR_GSS_COOKIE); + TR_NAME name ={(char *) displayName->value, (int) displayName->length}; + int result=0; - for (ii=0; iinames[ii]!=NULL) - tr_free_name(gss_names->names[ii]); + tr_debug("tr_gss_auth_cb: Calling auth handler for %.*s.", name.len, name.buf); + if (cookie->auth_cb(clientName, &name, cookie->auth_cookie)) { + tr_debug("tr_gss_auth_cb: client '%.*s' denied authorization.", name.len, name.buf); + result=EACCES; /* denied */ } - return 0; + + return result; } -TR_GSS_NAMES *tr_gss_names_new(TALLOC_CTX *mem_ctx) + + +/** + * Handle GSS authentication and authorization + * + * @param conn connection file descriptor + * @param acceptor_service name of acceptor to present to initiator + * @param acceptor_hostname hostname of acceptor to present to initiator + * @param gssctx GSS context + * @param auth_cb authorization callback + * @param auth_cookie generic data to pass to the authorization callback + * @return 0 on successful auth, 1 on disallowed auth, -1 on error + */ +static int tr_gss_auth_connection(int conn, + const char *acceptor_service, + const char *acceptor_hostname, + gss_ctx_id_t *gssctx, + TR_GSS_AUTH_FN auth_cb, + void *auth_cookie) { - TR_GSS_NAMES *gn=talloc(mem_ctx, TR_GSS_NAMES); - int ii=0; + int rc = 0; + int auth, autherr = 0; + gss_buffer_desc nameBuffer = {0, NULL}; + TR_GSS_COOKIE *cookie = NULL; - if (gn!=NULL) { - for (ii=0; iinames[ii]=NULL; - talloc_set_destructor((void *)gn, tr_gss_names_destructor); + nameBuffer.value = talloc_asprintf(NULL, "%s@%s", acceptor_service, acceptor_hostname); + if (nameBuffer.value == NULL) { + tr_err("tr_gss_auth_connection: Error allocating acceptor name."); + return -1; } - return gn; -} + nameBuffer.length = strlen(nameBuffer.value); -void tr_gss_names_free(TR_GSS_NAMES *gn) -{ - talloc_free(gn); + /* Set up for the auth callback. There are two layers of callbacks here: we + * use our own, which handles gsscon interfacing and calls the auth_cb parameter + * to do the actual auth. Store the auth_cb information in a metacookie. */ + cookie = talloc(NULL, TR_GSS_COOKIE); + cookie->auth_cb=auth_cb; + cookie->auth_cookie=auth_cookie; + + /* Now call gsscon with *our* auth callback and cookie */ + tr_debug("tr_gss_auth_connection: Beginning passive authentication as %.*s", + nameBuffer.length, nameBuffer.value); + rc = gsscon_passive_authenticate(conn, nameBuffer, gssctx, tr_gss_auth_cb, cookie); + talloc_free(cookie); + talloc_free(nameBuffer.value); + if (rc) { + tr_debug("tr_gss_auth_connection: Error from gsscon_passive_authenticate(), rc = %d.", rc); + return -1; + } + + tr_debug("tr_gss_auth_connection: Authentication succeeded, now authorizing."); + rc = gsscon_authorize(*gssctx, &auth, &autherr); + if (rc) { + tr_debug("tr_gss_auth_connection: Error from gsscon_authorize, rc = %d, autherr = %d.", + rc, autherr); + return -1; + } + + if (auth) + tr_debug("tr_gss_auth_connection: Connection authenticated, conn = %d.", conn); + else + tr_debug("tr_gss_auth_connection: Authentication failed, conn %d.", conn); + + return !auth; } -/* returns 0 on success */ -int tr_gss_names_add(TR_GSS_NAMES *gn, TR_NAME *new) +/** + * Read a request from the GSS connection + * + * @param mem_ctx talloc context for the result + * @param conn file descriptor for the connection + * @param gssctx GSS context + * @return talloc'ed string containing the request, or null on error + */ +static char *tr_gss_read_req(TALLOC_CTX *mem_ctx, int conn, gss_ctx_id_t gssctx) { - int ii=0; + int err; + char *retval = NULL; + char *buf = NULL; + size_t buflen = 0; - for (ii=0; iinames[ii]==NULL) - break; + err = gsscon_read_encrypted_token(conn, gssctx, &buf, &buflen); + if (err || (buf == NULL)) { + if (buf) + free(buf); + tr_debug("tr_gss_read_req: Error reading from connection, rc=%d", err); + return NULL; } - if (ii!=TR_MAX_GSS_NAMES) { - gn->names[ii]=new; - return 0; - } else - return -1; + + tr_debug("tr_gss_read_req: Read %u bytes.", (unsigned) buflen); + + // get a talloc'ed version, guaranteed to have a null termination + retval = talloc_asprintf(mem_ctx, "%.*s", (int) buflen, buf); + free(buf); + + return retval; } -int tr_gss_names_matches(TR_GSS_NAMES *gn, TR_NAME *name) +/** + * Write a response to the GSS connection + * + * @param conn file descriptor for the connection + * @param gssctx GSS context + * @param resp encoded response string to send + * @return 0 on success, -1 on error + */ +static int tr_gss_write_resp(int conn, gss_ctx_id_t gssctx, const char *resp) { - int ii=0; - - if (!gn) - return 0; + int err = 0; - for (ii=0; iinames[ii]!=NULL) && - (0==tr_name_cmp(gn->names[ii], name))) - return 1; + /* Send the response over the connection */ + err = gsscon_write_encrypted_token (conn, gssctx, resp, strlen(resp) + 1); + if (err) { + tr_debug("tr_gss_send_response: Error sending response over connection, rc=%d.", err); + return -1; } return 0; } -/* iterators */ -TR_GSS_NAMES_ITER *tr_gss_names_iter_new(TALLOC_CTX *mem_ctx) +/** + * Handle a request/response connection + * + * Authorizes/authenticates the connection, then reads a response, passes that to a + * callback to get a response, sends that, then returns. + * + * @param conn connection file descriptor + * @param acceptor_service acceptor name to present + * @param acceptor_hostname acceptor hostname to present + * @param auth_cb callback for authorization + * @param auth_cookie cookie for the auth_cb + * @param req_cb callback to handle the request and produce the response + * @param req_cookie cookie for the req_cb + */ +void tr_gss_handle_connection(int conn, + const char *acceptor_service, + const char *acceptor_hostname, + TR_GSS_AUTH_FN auth_cb, + void *auth_cookie, + TR_GSS_HANDLE_REQ_FN req_cb, + void *req_cookie) { - TR_GSS_NAMES_ITER *iter=talloc(mem_ctx, TR_GSS_NAMES_ITER); - if (iter!=NULL) { - iter->gn=NULL; - iter->ii=0; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT; + char *req_str = NULL; + size_t req_len = 0; + TR_MSG *req_msg = NULL; + TR_MSG *resp_msg = NULL; + char *resp_str = NULL; + + tr_debug("tr_gss_handle_connection: Attempting to accept %s connection on fd %d.", + acceptor_service, conn); + + if (tr_gss_auth_connection(conn, + acceptor_service, + acceptor_hostname, + &gssctx, + auth_cb, + auth_cookie)) { + tr_notice("tr_gss_handle_connection: Error authorizing connection."); + goto cleanup; } - return iter; -} -TR_NAME *tr_gss_names_iter_first(TR_GSS_NAMES_ITER *iter, TR_GSS_NAMES *gn) -{ - iter->gn=gn; - iter->ii=-1; - return tr_gss_names_iter_next(iter); -} + tr_debug("tr_gss_handle_connection: Connection authorized"); -TR_NAME *tr_gss_names_iter_next(TR_GSS_NAMES_ITER *iter) -{ - for (iter->ii++; - (iter->ii < TR_MAX_GSS_NAMES) && (iter->gn->names[iter->ii]==NULL); - iter->ii++) { } - - if (iter->iign->names[iter->ii]; - - return NULL; -} + // TODO: should there be a timeout on this? + do { + /* continue until an error breaks us out */ + // try to read a request + req_str = tr_gss_read_req(tmp_ctx, conn, gssctx); -void tr_gss_names_iter_free(TR_GSS_NAMES_ITER *iter) -{ - talloc_free(iter); + if (req_str == NULL) { + // an error occurred, give up + tr_notice("tr_gss_handle_connection: Error reading request"); + goto cleanup; + } + + req_len = strlen(req_str); + + /* If we got no characters, we will loop again. Free the empty response for the next loop. */ + if (req_len == 0) + talloc_free(req_str); + + } while (req_len == 0); + + /* Decode the request */ + req_msg = tr_msg_decode(tmp_ctx, req_str, req_len); + if (req_msg == NULL) { + tr_notice("tr_gss_handle_connection: Error decoding response"); + goto cleanup; + } + + /* Hand off the request for processing and get the response */ + resp_msg = req_cb(tmp_ctx, req_msg, req_cookie); + + if (resp_msg == NULL) { + // no response, clean up + goto cleanup; + } + + /* Encode the response */ + resp_str = tr_msg_encode(tmp_ctx, resp_msg); + if (resp_str == NULL) { + /* We apparently can't encode a response, so just return */ + tr_err("tr_gss_handle_connection: Error encoding response"); + goto cleanup; + } + + // send the response + if (tr_gss_write_resp(conn, gssctx, resp_str)) { + tr_err("tr_gss_handle_connection: Error writing response"); + goto cleanup; + } + +cleanup: + talloc_free(tmp_ctx); } diff --git a/common/tr_gss_client.c b/common/tr_gss_client.c new file mode 100644 index 0000000..db9f380 --- /dev/null +++ b/common/tr_gss_client.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2012, 2014-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include +#include +#include +#include +#include + +static int tr_gssc_destructor(void *obj) +{ + TR_GSSC_INSTANCE *tr_gssc=talloc_get_type_abort(obj, TR_GSSC_INSTANCE); + if (NULL!=tr_gssc) { + if (NULL!=tr_gssc->client_dh) + tr_destroy_dh_params(tr_gssc->client_dh); + } + return 0; +} + +TR_GSSC_INSTANCE *tr_gssc_instance_new(TALLOC_CTX *mem_ctx) +{ + TR_GSSC_INSTANCE *gssc=talloc(NULL, TR_GSSC_INSTANCE); + if (gssc != NULL) { + gssc->service_name = NULL; + gssc->client_dh = NULL; + gssc->conn = -1; + gssc->gss_ctx = talloc(gssc, gss_ctx_id_t); + if (gssc->gss_ctx == NULL) { + talloc_free(gssc); /* before the destructor is set */ + return NULL; + } + talloc_set_destructor((void *)gssc, tr_gssc_destructor); + } + return gssc; +} + +void tr_gssc_instance_free(TR_GSSC_INSTANCE *tr_gssc) +{ + talloc_free(tr_gssc); +} + +/** + * Open a connection to the requested server:port + * + * @param gssc client instance + * @param server server name/address + * @param port TCP port to connect + * @return 0 on success, -1 on failure + */ +int tr_gssc_open_connection(TR_GSSC_INSTANCE *gssc, const char *server, unsigned int port) +{ + tr_debug("tr_gssc_open_connection: opening connection to %s:%d", server, port); + if (0 != gsscon_connect(server, port, gssc->service_name, &(gssc->conn), gssc->gss_ctx)) + return -1; + + return 0; /* success */ +} + +/** + * Send a request message and retrieve a response message + * + * @param mem_ctx + * @param gssc + * @param req_msg + * @return decoded message, or null on error + */ +TR_MSG *tr_gssc_exchange_msgs(TALLOC_CTX *mem_ctx, TR_GSSC_INSTANCE *gssc, TR_MSG *req_msg) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + char *req_buf = NULL; + char *resp_buf = NULL; + size_t resp_buflen = 0; + TR_MSG *resp_msg = NULL; /* this is the return value */ + int err; + + /* Validate inputs */ + if ((gssc == NULL) || (req_msg == NULL)) + goto cleanup; + + /* Encode the request into a json string */ + if (!(req_buf = tr_msg_encode(tmp_ctx, req_msg))) { + tr_err("tr_gssc_exchange_msgs: Error encoding request message.\n"); + goto cleanup; + } + + tr_debug( "tr_gssc_exchange_msgs: Sending request message:\n%s\n", req_buf); + + /* Send the request over the connection */ + err = gsscon_write_encrypted_token(gssc->conn, *(gssc->gss_ctx), req_buf, strlen(req_buf)); + if (err) { + tr_err( "tr_gssc_exchange_msgs: Error sending request.\n"); + goto cleanup; + } + + /* Read the response from the connection */ + /* TBD -- timeout? */ + if (gsscon_read_encrypted_token(gssc->conn, *(gssc->gss_ctx), &resp_buf, &resp_buflen)) + goto cleanup; + + tr_debug( "tr_gssc_exchange_msgs: Response Received (%u bytes).\n%s\n", (unsigned) resp_buflen, resp_buf); + resp_msg = tr_msg_decode(mem_ctx, resp_buf, resp_buflen); + free(resp_buf); + + if (resp_msg == NULL) { + tr_err( "tr_gssc_exchange_msgs: Error decoding response.\n"); + goto cleanup; + } + + /* If we get here, then we decoded the message and resp_msg is not null. Nothing more to do. */ + +cleanup: + talloc_free(tmp_ctx); + return resp_msg; +} + +DH * tr_gssc_get_dh(TR_GSSC_INSTANCE *inst) +{ + return inst->client_dh; +} + +DH *tr_gssc_set_dh(TR_GSSC_INSTANCE *inst, DH *dh) +{ + inst->client_dh = dh; + return dh; +} diff --git a/common/tr_gss_names.c b/common/tr_gss_names.c new file mode 100644 index 0000000..df15202 --- /dev/null +++ b/common/tr_gss_names.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2016, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include +#include + +static int tr_gss_names_destructor(void *obj) +{ + TR_GSS_NAMES *gss_names=talloc_get_type_abort(obj, TR_GSS_NAMES); + int ii=0; + + for (ii=0; iinames[ii]!=NULL) + tr_free_name(gss_names->names[ii]); + } + return 0; +} +TR_GSS_NAMES *tr_gss_names_new(TALLOC_CTX *mem_ctx) +{ + TR_GSS_NAMES *gn=talloc(mem_ctx, TR_GSS_NAMES); + int ii=0; + + if (gn!=NULL) { + for (ii=0; iinames[ii]=NULL; + talloc_set_destructor((void *)gn, tr_gss_names_destructor); + } + return gn; +} + +void tr_gss_names_free(TR_GSS_NAMES *gn) +{ + talloc_free(gn); +} + +/* returns 0 on success */ +int tr_gss_names_add(TR_GSS_NAMES *gn, TR_NAME *new) +{ + int ii=0; + + for (ii=0; iinames[ii]==NULL) + break; + } + if (ii!=TR_MAX_GSS_NAMES) { + gn->names[ii]=new; + return 0; + } else + return -1; +} + +/** + * Create a duplicate GSS names struct + * + * @param mem_ctx + * @param orig + * @return + */ +TR_GSS_NAMES *tr_gss_names_dup(TALLOC_CTX *mem_ctx, TR_GSS_NAMES *orig) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + TR_GSS_NAMES *new = tr_gss_names_new(tmp_ctx); + TR_GSS_NAMES_ITER *iter = tr_gss_names_iter_new(tmp_ctx); + TR_NAME *this = NULL; + + if ( !orig || !new || !iter ) { + talloc_free(tmp_ctx); + return NULL; + } + this = tr_gss_names_iter_first(iter, orig); + while (this) { + if (tr_gss_names_add(new, tr_dup_name(this)) != 0) { + talloc_free(tmp_ctx); + return NULL; + } + this = tr_gss_names_iter_next(iter); + } + /* success */ + talloc_steal(mem_ctx, new); + return new; +} +int tr_gss_names_matches(TR_GSS_NAMES *gn, TR_NAME *name) +{ + int ii=0; + + if (!gn) + return 0; + + for (ii=0; iinames[ii]!=NULL) && + (0==tr_name_cmp(gn->names[ii], name))) + return 1; + } + return 0; +} + +/* iterators */ +TR_GSS_NAMES_ITER *tr_gss_names_iter_new(TALLOC_CTX *mem_ctx) +{ + TR_GSS_NAMES_ITER *iter=talloc(mem_ctx, TR_GSS_NAMES_ITER); + if (iter!=NULL) { + iter->gn=NULL; + iter->ii=0; + } + return iter; +} + +TR_NAME *tr_gss_names_iter_first(TR_GSS_NAMES_ITER *iter, TR_GSS_NAMES *gn) +{ + iter->gn=gn; + iter->ii=-1; + return tr_gss_names_iter_next(iter); +} + +TR_NAME *tr_gss_names_iter_next(TR_GSS_NAMES_ITER *iter) +{ + for (iter->ii++; + (iter->ii < TR_MAX_GSS_NAMES) && (iter->gn->names[iter->ii]==NULL); + iter->ii++) { } + + if (iter->iign->names[iter->ii]; + + return NULL; +} + +void tr_gss_names_iter_free(TR_GSS_NAMES_ITER *iter) +{ + talloc_free(iter); +} + +json_t *tr_gss_names_to_json_array(TR_GSS_NAMES *gss_names) +{ + TR_GSS_NAMES_ITER *iter = tr_gss_names_iter_new(NULL); + json_t *jarray = json_array(); + TR_NAME *name = tr_gss_names_iter_first(iter, gss_names); + while (name) { + json_array_append_new(jarray, tr_name_to_json_string(name)); + name = tr_gss_names_iter_next(iter); + } + tr_gss_names_iter_free(iter); + return jarray; +} + diff --git a/common/tr_idp.c b/common/tr_idp.c index 747397b..50a9330 100644 --- a/common/tr_idp.c +++ b/common/tr_idp.c @@ -259,7 +259,7 @@ TR_IDP_REALM *tr_idp_realm_remove_func(TR_IDP_REALM *head, TR_IDP_REALM *remove) return head; } -static int tr_idp_realm_apc_count(TR_IDP_REALM *idp) +int tr_idp_realm_apc_count(TR_IDP_REALM *idp) { int ii=0; TR_APC *apc=idp->apcs; @@ -270,7 +270,7 @@ static int tr_idp_realm_apc_count(TR_IDP_REALM *idp) return ii; } -static int tr_idp_realm_aaa_server_count(TR_IDP_REALM *idp) +int tr_idp_realm_aaa_server_count(TR_IDP_REALM *idp) { int ii=0; TR_AAA_SERVER *aaa=idp->aaa_servers; @@ -281,84 +281,6 @@ static int tr_idp_realm_aaa_server_count(TR_IDP_REALM *idp) return ii; } -static char *tr_aaa_server_to_str(TALLOC_CTX *mem_ctx, TR_AAA_SERVER *aaa) -{ - return talloc_strndup(mem_ctx, aaa->hostname->buf, aaa->hostname->len); -} - -char *tr_idp_realm_to_str(TALLOC_CTX *mem_ctx, TR_IDP_REALM *idp) -{ - TALLOC_CTX *tmp_ctx=talloc_new(NULL); - char **s_aaa=NULL, *aaa_servers=NULL; - char **s_apc=NULL, *apcs=NULL; - int ii=0, aaa_servers_strlen=0, apcs_strlen=0; - int n_aaa_servers=tr_idp_realm_aaa_server_count(idp); - int n_apcs=tr_idp_realm_apc_count(idp); - TR_AAA_SERVER *aaa=NULL; - TR_APC *apc=NULL; - char *result=NULL; - - /* get the AAA servers */ - if (n_aaa_servers<=0) - aaa_servers=talloc_strdup(tmp_ctx, ""); - else { - s_aaa=talloc_array(tmp_ctx, char *, n_aaa_servers); - for (aaa=idp->aaa_servers,ii=0; aaa!=NULL; aaa=aaa->next,ii++) { - s_aaa[ii]=tr_aaa_server_to_str(s_aaa, aaa); - aaa_servers_strlen+=strlen(s_aaa[ii]); - } - - /* add space for comma-space separators */ - aaa_servers_strlen+=2*(n_aaa_servers-1); - - aaa_servers=talloc_array(tmp_ctx, char, aaa_servers_strlen+1); - aaa_servers[0]='\0'; - for (ii=0; iiapcs,ii=0; apc!=NULL; apc=apc->next,ii++) { - s_apc[ii]=tr_apc_to_str(s_apc, apc); - apcs_strlen+=strlen(s_apc[ii]); - } - - /* add space for comma-space separators */ - apcs_strlen+=2*(n_apcs-1); - - apcs=talloc_array(tmp_ctx, char, apcs_strlen+1); - apcs[0]='\0'; - for (ii=0; iirealm_id->len, idp->realm_id->buf, - (idp->shared_config)?"yes":"no", - (idp->origin==TR_REALM_LOCAL)?"yes":"no", - aaa_servers, - apcs); - talloc_free(tmp_ctx); - return result; -} - void tr_idp_realm_incref(TR_IDP_REALM *realm) { realm->refcount++; diff --git a/common/tr_idp_encoders.c b/common/tr_idp_encoders.c new file mode 100644 index 0000000..fec129a --- /dev/null +++ b/common/tr_idp_encoders.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2012-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include +#include +#include +#include + +static char *tr_aaa_server_to_str(TALLOC_CTX *mem_ctx, TR_AAA_SERVER *aaa) +{ + return talloc_strndup(mem_ctx, aaa->hostname->buf, aaa->hostname->len); +} + + +char *tr_idp_realm_to_str(TALLOC_CTX *mem_ctx, TR_IDP_REALM *idp) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + char **s_aaa=NULL, *aaa_servers=NULL; + char **s_apc=NULL, *apcs=NULL; + int ii=0, aaa_servers_strlen=0, apcs_strlen=0; + int n_aaa_servers=tr_idp_realm_aaa_server_count(idp); + int n_apcs=tr_idp_realm_apc_count(idp); + TR_AAA_SERVER *aaa=NULL; + TR_APC *apc=NULL; + char *result=NULL; + + /* get the AAA servers */ + if (n_aaa_servers<=0) + aaa_servers=talloc_strdup(tmp_ctx, ""); + else { + s_aaa=talloc_array(tmp_ctx, char *, n_aaa_servers); + for (aaa=idp->aaa_servers,ii=0; aaa!=NULL; aaa=aaa->next,ii++) { + s_aaa[ii]=tr_aaa_server_to_str(s_aaa, aaa); + aaa_servers_strlen+=strlen(s_aaa[ii]); + } + + /* add space for comma-space separators */ + aaa_servers_strlen+=2*(n_aaa_servers-1); + + aaa_servers=talloc_array(tmp_ctx, char, aaa_servers_strlen+1); + aaa_servers[0]='\0'; + for (ii=0; iiapcs,ii=0; apc!=NULL; apc=apc->next,ii++) { + s_apc[ii]=tr_apc_to_str(s_apc, apc); + apcs_strlen+=strlen(s_apc[ii]); + } + + /* add space for comma-space separators */ + apcs_strlen+=2*(n_apcs-1); + + apcs=talloc_array(tmp_ctx, char, apcs_strlen+1); + apcs[0]='\0'; + for (ii=0; iirealm_id->len, idp->realm_id->buf, + (idp->shared_config)?"yes":"no", + (idp->origin==TR_REALM_LOCAL)?"yes":"no", + aaa_servers, + apcs); + talloc_free(tmp_ctx); + return result; +} + + +/* helper for below */ +#define OBJECT_SET_OR_FAIL(jobj, key, val) \ +do { \ + if (val) \ + json_object_set_new((jobj),(key),(val)); \ + else \ + goto cleanup; \ +} while (0) + +#define ARRAY_APPEND_OR_FAIL(jary, val) \ +do { \ + if (val) \ + json_array_append_new((jary),(val)); \ + else \ + goto cleanup; \ +} while (0) + +static json_t *tr_apcs_to_json(TR_APC *apcs) +{ + json_t *jarray = json_array(); + json_t *retval = NULL; + TR_APC_ITER *iter = tr_apc_iter_new(NULL); + TR_APC *apc = NULL; + + if ((jarray == NULL) || (iter == NULL)) + goto cleanup; + + apc = tr_apc_iter_first(iter, apcs); + while (apc) { + ARRAY_APPEND_OR_FAIL(jarray, tr_name_to_json_string(tr_apc_get_id(apc))); + apc = tr_apc_iter_next(iter); + } + + /* success */ + retval = jarray; + json_incref(retval); + +cleanup: + if (jarray) + json_decref(jarray); + + return retval; +} + +static json_t *tr_aaa_server_to_json(TR_AAA_SERVER *aaa) +{ + char *hostname = tr_name_strdup(aaa->hostname); + char *s = NULL; + json_t *jstr = NULL; + + if (hostname == NULL) + return NULL; + + s = talloc_asprintf(NULL, "%s:%d", hostname, TID_PORT); + if (s) { + jstr = json_string(s); + talloc_free(s); + } + return jstr; +} + +static json_t *tr_aaa_servers_to_json(TR_AAA_SERVER *aaas) +{ + json_t *jarray = json_array(); + json_t *retval = NULL; + TR_AAA_SERVER_ITER *iter = tr_aaa_server_iter_new(NULL); + TR_AAA_SERVER *aaa = NULL; + + if ((jarray == NULL) || (iter == NULL)) + goto cleanup; + + aaa = tr_aaa_server_iter_first(iter, aaas); + while (aaa) { + ARRAY_APPEND_OR_FAIL(jarray, tr_aaa_server_to_json(aaa)); + aaa = tr_aaa_server_iter_next(iter); + } + + /* success */ + retval = jarray; + json_incref(retval); + +cleanup: + if (jarray) + json_decref(jarray); + + return retval; +} + +static json_t *tr_idp_realm_to_json(TR_IDP_REALM *idp) +{ + json_t *idp_json = json_object(); + json_t *retval = NULL; + + if (idp_json == NULL) + goto cleanup; + + + /* success */ + retval = idp_json; + json_incref(retval); + + OBJECT_SET_OR_FAIL(idp_json, "realm", + tr_name_to_json_string(tr_idp_realm_get_id(idp))); + OBJECT_SET_OR_FAIL(idp_json, "discovered", + json_boolean(idp->origin == TR_REALM_DISCOVERED)); + OBJECT_SET_OR_FAIL(idp_json, "apcs", + tr_apcs_to_json(tr_idp_realm_get_apcs(idp))); + OBJECT_SET_OR_FAIL(idp_json, "aaa_servers", + tr_aaa_servers_to_json(idp->aaa_servers)); + OBJECT_SET_OR_FAIL(idp_json, "shared_config", + json_boolean(idp->shared_config)); +cleanup: + if (idp_json) + json_decref(idp_json); + + return retval; +} + +json_t *tr_idp_realms_to_json(TR_IDP_REALM *idps) +{ + { + json_t *jarray = json_array(); + json_t *retval = NULL; + TR_IDP_REALM *this = NULL; + + if (jarray == NULL) + goto cleanup; + + for (this=idps; this != NULL; this=this->next) + ARRAY_APPEND_OR_FAIL(jarray, tr_idp_realm_to_json(this)); + + /* success */ + retval = jarray; + json_incref(retval); + + cleanup: + if (jarray) + json_decref(jarray); + + return retval; + } + +} \ No newline at end of file diff --git a/common/tr_msg.c b/common/tr_msg.c index 66972e0..50e8bc9 100644 --- a/common/tr_msg.c +++ b/common/tr_msg.c @@ -41,12 +41,12 @@ #include #include - #include #include +#include +#include #include #include -#include #include #include #include @@ -101,6 +101,17 @@ void tr_msg_set_msg_type(TR_MSG *msg, enum msg_type type) msg->msg_type = type; } +/* NOTE: If you are manipulating messages with these getters/setters, the msg_rep + * objects are *not* put in the talloc context of the msg. If you are allocating + * the message directly with talloc, then you can talloc_steal() the rep into the + * message's context, but this is not handled automatically. */ + +/** + * Get a TID_REQ message payload + * + * @param msg + * @return the message payload, or null if it is not a TID_REQUEST message + */ TID_REQ *tr_msg_get_req(TR_MSG *msg) { if (msg->msg_type == TID_REQUEST) @@ -108,12 +119,24 @@ TID_REQ *tr_msg_get_req(TR_MSG *msg) return NULL; } +/** + * Set message's payload + * + * Does not manage talloc contexts, works with any means of allocating + * the objects. + */ void tr_msg_set_req(TR_MSG *msg, TID_REQ *req) { msg->msg_rep = req; msg->msg_type = TID_REQUEST; } +/** + * Get a TID_RESP message payload + * + * @param msg + * @return the message payload, or null if it is not a TID_RESPONSE message + */ TID_RESP *tr_msg_get_resp(TR_MSG *msg) { if (msg->msg_type == TID_RESPONSE) @@ -121,12 +144,74 @@ TID_RESP *tr_msg_get_resp(TR_MSG *msg) return NULL; } +/** + * Set message's payload + * + * Does not manage talloc contexts, works with any means of allocating + * the objects. + */ void tr_msg_set_resp(TR_MSG *msg, TID_RESP *resp) { msg->msg_rep = resp; msg->msg_type = TID_RESPONSE; } +/** + * Get a MON_REQ message payload + * + * @param msg + * @return the message payload, or null if it is not a MON_REQUEST message + */ +MON_REQ *tr_msg_get_mon_req(TR_MSG *msg) +{ + if (msg->msg_type == MON_REQUEST) + return (MON_REQ *)msg->msg_rep; + return NULL; +} + +/** + * Set message's payload + * + * Does not manage talloc contexts, works with any means of allocating + * the objects. + */ +void tr_msg_set_mon_req(TR_MSG *msg, MON_REQ *req) +{ + msg->msg_rep = req; + msg->msg_type = MON_REQUEST; +} + +/** + * Get a MON_RESP message payload + * + * @param msg + * @return the message payload, or null if it is not a MON_RESPONSE message + */ +MON_RESP *tr_msg_get_mon_resp(TR_MSG *msg) +{ + if (msg->msg_type == MON_RESPONSE) + return (MON_RESP *)msg->msg_rep; + return NULL; +} + +/** + * Set message's payload + * + * Does not manage talloc contexts, works with any means of allocating + * the objects. + */ +void tr_msg_set_mon_resp(TR_MSG *msg, MON_RESP *resp) +{ + msg->msg_rep = resp; + msg->msg_type = MON_RESPONSE; +} + +/** + * Get a TRP_UPD message payload + * + * @param msg + * @return the message payload, or null if it is not a TRP_UPDATE message + */ TRP_UPD *tr_msg_get_trp_upd(TR_MSG *msg) { if (msg->msg_type == TRP_UPDATE) @@ -134,13 +219,24 @@ TRP_UPD *tr_msg_get_trp_upd(TR_MSG *msg) return NULL; } +/** + * Set message's payload + * + * Does not manage talloc contexts, works with any means of allocating + * the objects. + */ void tr_msg_set_trp_upd(TR_MSG *msg, TRP_UPD *update) { msg->msg_rep=update; - talloc_steal(NULL, update); /* should attach to msg, but TR_MSG not usually talloc'ed */ msg->msg_type=TRP_UPDATE; } +/** + * Get a TRP_REQ message payload + * + * @param msg + * @return the message payload, or null if it is not a TRP_REQUEST message + */ TRP_REQ *tr_msg_get_trp_req(TR_MSG *msg) { if (msg->msg_type == TRP_REQUEST) @@ -148,6 +244,12 @@ TRP_REQ *tr_msg_get_trp_req(TR_MSG *msg) return NULL; } +/** + * Set message's payload + * + * Does not manage talloc contexts, works with any means of allocating + * the objects. + */ void tr_msg_set_trp_req(TR_MSG *msg, TRP_REQ *req) { msg->msg_rep=req; @@ -216,7 +318,8 @@ static json_t * tr_msg_encode_tidreq(TID_REQ *req) if ((!req) || (!req->rp_realm) || (!req->realm) || !(req->comm)) return NULL; - assert(jreq = json_object()); + jreq = json_object(); + assert(jreq); jstr = tr_name_to_json_string(req->rp_realm); json_object_set_new(jreq, "rp_realm", jstr); @@ -251,7 +354,7 @@ static json_t * tr_msg_encode_tidreq(TID_REQ *req) return jreq; } -static TID_REQ *tr_msg_decode_tidreq(json_t *jreq) +static TID_REQ *tr_msg_decode_tidreq(TALLOC_CTX *mem_ctx, json_t *jreq) { TID_REQ *treq = NULL; json_t *jrp_realm = NULL; @@ -267,7 +370,8 @@ static TID_REQ *tr_msg_decode_tidreq(json_t *jreq) tr_crit("tr_msg_decode_tidreq(): Error allocating TID_REQ structure."); return NULL; } - + talloc_steal(mem_ctx, treq); + /* store required fields from request */ if ((NULL == (jrp_realm = json_object_get(jreq, "rp_realm"))) || (NULL == (jrealm = json_object_get(jreq, "target_realm"))) || @@ -499,7 +603,7 @@ static json_t * tr_msg_encode_tidresp(TID_RESP *resp) return jresp; } -static TID_RESP *tr_msg_decode_tidresp(json_t *jresp) +static TID_RESP *tr_msg_decode_tidresp(TALLOC_CTX *mem_ctx, json_t *jresp) { TID_RESP *tresp = NULL; json_t *jresult = NULL; @@ -511,11 +615,10 @@ static TID_RESP *tr_msg_decode_tidresp(json_t *jresp) json_t *jservers = NULL; json_t *jerr_msg = NULL; - if (!(tresp=tid_resp_new(NULL))) { + if (!(tresp=tid_resp_new(mem_ctx))) { tr_crit("tr_msg_decode_tidresp(): Error allocating TID_RESP structure."); return NULL; } - /* store required fields from response */ if ((NULL == (jresult = json_object_get(jresp, "result"))) || @@ -1167,21 +1270,23 @@ cleanup: return req; } -char *tr_msg_encode(TR_MSG *msg) +char *tr_msg_encode(TALLOC_CTX *mem_ctx, TR_MSG *msg) { json_t *jmsg=NULL; json_t *jmsg_type=NULL; + char *encoded_tmp=NULL; char *encoded=NULL; TID_RESP *tidresp=NULL; TID_REQ *tidreq=NULL; TRP_UPD *trpupd=NULL; TRP_REQ *trpreq=NULL; + MON_REQ *monreq=NULL; + MON_RESP *monresp=NULL; /* TBD -- add error handling */ jmsg = json_object(); - switch (msg->msg_type) - { + switch (msg->msg_type) { case TID_REQUEST: jmsg_type = json_string("tid_request"); json_object_set_new(jmsg, "msg_type", jmsg_type); @@ -1210,18 +1315,37 @@ char *tr_msg_encode(TR_MSG *msg) json_object_set_new(jmsg, "msg_body", tr_msg_encode_trp_req(trpreq)); break; + case MON_REQUEST: + jmsg_type = json_string("mon_request"); + json_object_set_new(jmsg, "msg_type", jmsg_type); + monreq=tr_msg_get_mon_req(msg); + json_object_set_new(jmsg, "msg_body", mon_req_encode(monreq)); + break; + + case MON_RESPONSE: + jmsg_type = json_string("mon_response"); + json_object_set_new(jmsg, "msg_type", jmsg_type); + monresp=tr_msg_get_mon_resp(msg); + json_object_set_new(jmsg, "msg_body", mon_resp_encode(monresp)); + break; + default: json_decref(jmsg); return NULL; - } + } + + /* We should perhaps use json_set_alloc_funcs to automatically use talloc, but for + * now, we'll encode to a malloc'ed buffer, then copy that to a talloc'ed buffer. */ + encoded_tmp=json_dumps(jmsg, 0); // malloc'ed version + json_decref(jmsg); // free the JSON structure + encoded = talloc_strdup(mem_ctx, encoded_tmp); // get the talloc'ed version + free(encoded_tmp); // free the malloc'ed version - encoded=json_dumps(jmsg, 0); tr_debug("tr_msg_encode: outgoing msg=%s", encoded); - json_decref(jmsg); return encoded; } -TR_MSG *tr_msg_decode(const char *jbuf, size_t buflen) +TR_MSG *tr_msg_decode(TALLOC_CTX *mem_ctx, const char *jbuf, size_t buflen) { TR_MSG *msg=NULL; json_t *jmsg = NULL; @@ -1235,14 +1359,12 @@ TR_MSG *tr_msg_decode(const char *jbuf, size_t buflen) return NULL; } - if (!(msg = malloc(sizeof(TR_MSG)))) { + if (!(msg = talloc_zero(mem_ctx, TR_MSG))) { tr_debug("tr_msg_decode(): Error allocating TR_MSG structure."); json_decref(jmsg); return NULL; } - memset(msg, 0, sizeof(TR_MSG)); - if ((NULL == (jtype = json_object_get(jmsg, "msg_type"))) || (NULL == (jbody = json_object_get(jmsg, "msg_body")))) { tr_debug("tr_msg_decode(): Error parsing message header."); @@ -1255,19 +1377,28 @@ TR_MSG *tr_msg_decode(const char *jbuf, size_t buflen) if (0 == strcmp(mtype, "tid_request")) { msg->msg_type = TID_REQUEST; - tr_msg_set_req(msg, tr_msg_decode_tidreq(jbody)); + tr_msg_set_req(msg, tr_msg_decode_tidreq(msg, jbody)); } else if (0 == strcmp(mtype, "tid_response")) { msg->msg_type = TID_RESPONSE; - tr_msg_set_resp(msg, tr_msg_decode_tidresp(jbody)); + tr_msg_set_resp(msg, tr_msg_decode_tidresp(msg, jbody)); } else if (0 == strcmp(mtype, "trp_update")) { msg->msg_type = TRP_UPDATE; - tr_msg_set_trp_upd(msg, tr_msg_decode_trp_upd(NULL, jbody)); /* null talloc context for now */ + tr_msg_set_trp_upd(msg, tr_msg_decode_trp_upd(msg, jbody)); } else if (0 == strcmp(mtype, "trp_request")) { msg->msg_type = TRP_UPDATE; - tr_msg_set_trp_req(msg, tr_msg_decode_trp_req(NULL, jbody)); /* null talloc context for now */ + tr_msg_set_trp_req(msg, tr_msg_decode_trp_req(msg, jbody)); + } + else if (0 == strcmp(mtype, "mon_request")) { + msg->msg_type = MON_REQUEST; + tr_msg_set_mon_req(msg, mon_req_decode(msg, jbody)); + } + /* We do not currently handle monitoring responses */ + else if (0 == strcmp(mtype, "mon_response")) { + msg->msg_type = MON_RESPONSE; + tr_msg_set_mon_resp(msg, mon_resp_decode(msg, jbody)); } else { msg->msg_type = TR_UNKNOWN; @@ -1282,29 +1413,11 @@ TR_MSG *tr_msg_decode(const char *jbuf, size_t buflen) void tr_msg_free_encoded(char *jmsg) { if (jmsg) - free (jmsg); + talloc_free(jmsg); } void tr_msg_free_decoded(TR_MSG *msg) { - if (msg) { - if (msg->msg_rep!=NULL) { - switch (msg->msg_type) { - case TID_REQUEST: - tid_req_free(tr_msg_get_req(msg)); - break; - case TID_RESPONSE: - tid_resp_free(tr_msg_get_resp(msg)); - break; - case TRP_UPDATE: - trp_upd_free(tr_msg_get_trp_upd(msg)); - break; - case TRP_REQUEST: - trp_req_free(tr_msg_get_trp_req(msg)); - default: - break; - } - } - free (msg); - } + if (msg) + talloc_free(msg); } diff --git a/common/tr_rp.c b/common/tr_rp.c index 4acdc4b..0b7d08e 100644 --- a/common/tr_rp.c +++ b/common/tr_rp.c @@ -36,126 +36,11 @@ #include #include -#include +#include #include #include #include -static int tr_rp_client_destructor(void *obj) -{ - return 0; -} - -TR_RP_CLIENT *tr_rp_client_new(TALLOC_CTX *mem_ctx) -{ - TR_RP_CLIENT *client=talloc(mem_ctx, TR_RP_CLIENT); - - if (client!=NULL) { - client->next=NULL; - client->comm_next=NULL; - client->gss_names=NULL; - client->filters=NULL; - talloc_set_destructor((void *)client, tr_rp_client_destructor); - } - return client; -} - -void tr_rp_client_free(TR_RP_CLIENT *client) -{ - talloc_free(client); -} - -static TR_RP_CLIENT *tr_rp_client_tail(TR_RP_CLIENT *client) -{ - if (client==NULL) - return NULL; - - while (client->next!=NULL) - client=client->next; - return client; -} - -/* do not call directly, use the tr_rp_client_add() macro */ -TR_RP_CLIENT *tr_rp_client_add_func(TR_RP_CLIENT *clients, TR_RP_CLIENT *new) -{ - if (clients==NULL) - clients=new; - else { - tr_rp_client_tail(clients)->next=new; - while (new!=NULL) { - talloc_steal(clients, new); /* put it in the right context */ - new=new->next; - } - } - return clients; -} - - -int tr_rp_client_add_gss_name(TR_RP_CLIENT *rp_client, TR_NAME *gss_name) -{ - return tr_gss_names_add(rp_client->gss_names, gss_name); -} - -int tr_rp_client_set_filters(TR_RP_CLIENT *client, TR_FILTER_SET *filts) -{ - if (client->filters!=NULL) - tr_filter_set_free(client->filters); - client->filters=filts; - talloc_steal(client, filts); - return 0; /* success */ -} - -TR_RP_CLIENT_ITER *tr_rp_client_iter_new(TALLOC_CTX *memctx) -{ - return talloc(memctx, TR_RP_CLIENT_ITER); -} - -void tr_rp_client_iter_free(TR_RP_CLIENT_ITER *iter) -{ - talloc_free(iter); -} - -TR_RP_CLIENT *tr_rp_client_iter_first(TR_RP_CLIENT_ITER *iter, TR_RP_CLIENT *rp_clients) -{ - if (!iter) { - tr_err("tr_rp_client_iter_first: Iterator is null, failing."); - return NULL; - } - *iter=rp_clients; - return *iter; -} - -TR_RP_CLIENT *tr_rp_client_iter_next(TR_RP_CLIENT_ITER *iter) -{ - if (*iter) - *iter=(*iter)->next; - return *iter; -} - -/** - * Find a client associated with a GSS name. It's possible there are other clients that match as well. - * - * @param rp_clients List of RP clients to search - * @param gss_name GSS name to search for - * @return Borrowed reference to an RP client linked to the GSS name - */ -TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name) -{ - TR_RP_CLIENT_ITER *iter=tr_rp_client_iter_new(NULL); - TR_RP_CLIENT *client=NULL; - - if (iter==NULL) { - tr_err("tr_rp_client_lookup: Unable to allocate iterator"); - return NULL; - } - for (client=tr_rp_client_iter_first(iter, rp_clients); client != NULL; client=tr_rp_client_iter_next(iter)) { - if (tr_gss_names_matches(client->gss_names, gss_name)) - break; - } - tr_rp_client_iter_free(iter); - return client; -} - TR_RP_REALM *tr_rp_realm_lookup(TR_RP_REALM *rp_realms, TR_NAME *rp_name) { TR_RP_REALM *rp = NULL; @@ -330,3 +215,4 @@ char *tr_rp_realm_to_str(TALLOC_CTX *mem_ctx, TR_RP_REALM *rp) "RP realm: \"%.*s\"\n", rp->realm_id->len, rp->realm_id->buf); } + diff --git a/common/tr_rp_client.c b/common/tr_rp_client.c new file mode 100644 index 0000000..b5e657e --- /dev/null +++ b/common/tr_rp_client.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2012-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +static int tr_rp_client_destructor(void *obj) +{ + return 0; +} + +TR_RP_CLIENT *tr_rp_client_new(TALLOC_CTX *mem_ctx) +{ + TR_RP_CLIENT *client=talloc(mem_ctx, TR_RP_CLIENT); + + if (client!=NULL) { + client->next=NULL; + client->comm_next=NULL; + client->gss_names=NULL; + client->filters=NULL; + talloc_set_destructor((void *)client, tr_rp_client_destructor); + } + return client; +} + +void tr_rp_client_free(TR_RP_CLIENT *client) +{ + talloc_free(client); +} + +static TR_RP_CLIENT *tr_rp_client_tail(TR_RP_CLIENT *client) +{ + if (client==NULL) + return NULL; + + while (client->next!=NULL) + client=client->next; + return client; +} + +/* do not call directly, use the tr_rp_client_add() macro */ +TR_RP_CLIENT *tr_rp_client_add_func(TR_RP_CLIENT *clients, TR_RP_CLIENT *new) +{ + if (clients==NULL) + clients=new; + else { + tr_rp_client_tail(clients)->next=new; + while (new!=NULL) { + talloc_steal(clients, new); /* put it in the right context */ + new=new->next; + } + } + return clients; +} + + +int tr_rp_client_add_gss_name(TR_RP_CLIENT *rp_client, TR_NAME *gss_name) +{ + return tr_gss_names_add(rp_client->gss_names, gss_name); +} + +int tr_rp_client_set_filters(TR_RP_CLIENT *client, TR_FILTER_SET *filts) +{ + if (client->filters!=NULL) + tr_filter_set_free(client->filters); + client->filters=filts; + talloc_steal(client, filts); + return 0; /* success */ +} + +TR_RP_CLIENT_ITER *tr_rp_client_iter_new(TALLOC_CTX *memctx) +{ + return talloc(memctx, TR_RP_CLIENT_ITER); +} + +void tr_rp_client_iter_free(TR_RP_CLIENT_ITER *iter) +{ + talloc_free(iter); +} + +TR_RP_CLIENT *tr_rp_client_iter_first(TR_RP_CLIENT_ITER *iter, TR_RP_CLIENT *rp_clients) +{ + if (!iter) { + tr_err("tr_rp_client_iter_first: Iterator is null, failing."); + return NULL; + } + *iter=rp_clients; + return *iter; +} + +TR_RP_CLIENT *tr_rp_client_iter_next(TR_RP_CLIENT_ITER *iter) +{ + if (*iter) + *iter=(*iter)->next; + return *iter; +} + +/** + * Find a client associated with a GSS name. It's possible there are other clients that match as well. + * + * @param rp_clients List of RP clients to search + * @param gss_name GSS name to search for + * @return Borrowed reference to an RP client linked to the GSS name + */ +TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name) +{ + TR_RP_CLIENT_ITER *iter=tr_rp_client_iter_new(NULL); + TR_RP_CLIENT *client=NULL; + + if (iter==NULL) { + tr_err("tr_rp_client_lookup: Unable to allocate iterator"); + return NULL; + } + for (client=tr_rp_client_iter_first(iter, rp_clients); client != NULL; client=tr_rp_client_iter_next(iter)) { + if (tr_gss_names_matches(client->gss_names, gss_name)) + break; + } + tr_rp_client_iter_free(iter); + return client; +} + diff --git a/common/tr_rp_client_encoders.c b/common/tr_rp_client_encoders.c new file mode 100644 index 0000000..d0a5cd0 --- /dev/null +++ b/common/tr_rp_client_encoders.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include +#include + +/* helper for below */ +#define OBJECT_SET_OR_FAIL(jobj, key, val) \ +do { \ + if (val) \ + json_object_set_new((jobj),(key),(val)); \ + else \ + goto cleanup; \ +} while (0) + +#define ARRAY_APPEND_OR_FAIL(jary, val) \ +do { \ + if (val) \ + json_array_append_new((jary),(val)); \ + else \ + goto cleanup; \ +} while (0) + +static json_t *tr_rp_client_to_json(TR_RP_CLIENT *rp_client) +{ + json_t *client_json = NULL; + json_t *retval = NULL; + + client_json = json_object(); + if (client_json == NULL) + goto cleanup; + + OBJECT_SET_OR_FAIL(client_json, "gss_names", tr_gss_names_to_json_array(rp_client->gss_names)); + OBJECT_SET_OR_FAIL(client_json, "filters", tr_filter_set_to_json(rp_client->filters)); + + /* succeeded - set the return value and increment the reference count */ + retval = client_json; + json_incref(retval); + +cleanup: + if (client_json) + json_decref(client_json); + return retval; +} + +json_t *tr_rp_clients_to_json(TR_RP_CLIENT *rp_clients) +{ + json_t *jarray = json_array(); + json_t *retval = NULL; + TR_RP_CLIENT_ITER *iter = tr_rp_client_iter_new(NULL); + TR_RP_CLIENT *rp_client = NULL; + + if ((jarray == NULL) || (iter == NULL)) + goto cleanup; + + rp_client = tr_rp_client_iter_first(iter, rp_clients); + while (rp_client) { + ARRAY_APPEND_OR_FAIL(jarray, tr_rp_client_to_json(rp_client)); + rp_client = tr_rp_client_iter_next(iter); + } + + /* succeeded - set the return value and increment the reference count */ + retval = jarray; + json_incref(retval); + +cleanup: + if (jarray) + json_decref(jarray); + + if (iter) + tr_rp_client_iter_free(iter); + + return retval; +} diff --git a/common/tr_socket.c b/common/tr_socket.c new file mode 100644 index 0000000..7f1c917 --- /dev/null +++ b/common/tr_socket.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2016-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include // for nfds_t + +#include +#include + +/** + * Open sockets on all interface addresses + * + * Uses getaddrinfo() to find all TCP addresses and opens sockets in + * non-blocking modes. Binds to most max_fd sockets and stores file descriptors + * in fd_out. Unused entries in fd_out are not modified. Returns the actual + * number of sockets opened. + * + * @param port port to listen on + * @param fd_out output array, at least max_fd long + * @param max_fd maximum number of file descriptors to write + * @return number of file descriptors written into the output array + */ +nfds_t tr_sock_listen_all(unsigned int port, int *fd_out, nfds_t max_fd) +{ + int rc = 0; + int conn = -1; + int optval = 1; + int gai_retval = 0; + struct addrinfo *ai=NULL; + struct addrinfo *ai_head=NULL; + struct addrinfo hints={ + .ai_flags=AI_PASSIVE, + .ai_family=AF_UNSPEC, + .ai_socktype=SOCK_STREAM, + .ai_protocol=IPPROTO_TCP + }; + char *port_str=NULL; + nfds_t n_opened=0; + + port_str=talloc_asprintf(NULL, "%d", port); + if (port_str==NULL) { + tr_err("tr_sock_listen_all: unable to allocate port"); + return 0; + } + + gai_retval = getaddrinfo(NULL, port_str, &hints, &ai_head); + talloc_free(port_str); + if (gai_retval != 0) { + tr_err("tr_sock_listen_all: getaddrinfo() failed (%s)", gai_strerror(gai_retval)); + return 0; + } + tr_debug("tr_sock_listen_all: got address info"); + + /* TODO: listen on all ports - I don't recall what this means (jlr, 4/11/2018) */ + for (ai=ai_head,n_opened=0; (ai!=NULL)&&(n_openedai_next) { + if (0 > (conn = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))) { + tr_debug("tr_sock_listen_all: unable to open socket"); + continue; + } + + optval=1; + if (0!=setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) + tr_debug("tids_listen: unable to set SO_REUSEADDR"); /* not fatal? */ + + if (ai->ai_family==AF_INET6) { + /* don't allow IPv4-mapped IPv6 addresses (per RFC4942, not sure + * if still relevant) */ + if (0!=setsockopt(conn, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval))) { + tr_debug("tr_sock_listen_all: unable to set IPV6_V6ONLY, skipping interface"); + close(conn); + continue; + } + } + + rc=bind(conn, ai->ai_addr, ai->ai_addrlen); + if (rc<0) { + tr_debug("tr_sock_listen_all: unable to bind to socket"); + close(conn); + continue; + } + + if (0>listen(conn, 512)) { + tr_debug("tr_sock_listen_all: unable to listen on bound socket"); + close(conn); + continue; + } + + /* ok, this one worked. Save it */ + fd_out[n_opened++]=conn; + } + freeaddrinfo(ai_head); + + if (n_opened==0) { + tr_debug("tr_sock_listen_all: no addresses available for listening."); + return 0; + } + + tr_debug("tr_sock_listen_all: listening on port %d on %d socket%s", + port, + n_opened, + (n_opened==1)?"":"s"); + + return n_opened; +} + diff --git a/common/tr_util.c b/common/tr_util.c index 2ce3c82..ef85776 100644 --- a/common/tr_util.c +++ b/common/tr_util.c @@ -36,8 +36,8 @@ #include #include #include -#include #include +#include void tr_bin_to_hex(const unsigned char * bin, size_t bin_len, char * hex_out, size_t hex_len) @@ -71,3 +71,28 @@ int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2) return 0; } + +/** + * Convert a struct timespec to a string representation + * @param ts + * @return + */ +char *timespec_to_str(struct timespec *ts) +{ + struct tm tm; + char *s=NULL; + + if (localtime_r(&(ts->tv_sec), &tm)==NULL) + return NULL; + + s=malloc(40); /* long enough to contain strftime result */ + if (s==NULL) + return NULL; + + if (strftime(s, 40, "%F %T", &tm)==0) { + free(s); + return NULL; + } + return s; +} + diff --git a/configure.ac b/configure.ac index 47c962b..6b4639c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.63) -AC_INIT([trust_router],[3.3.0], +AC_INIT([trust_router],[3.4.0~1], [bugs@project-moonshot.org]) AC_CONFIG_MACRO_DIR(m4) AC_CONFIG_AUX_DIR(build-aux) diff --git a/include/mon.h b/include/mon.h new file mode 100644 index 0000000..33b8f4b --- /dev/null +++ b/include/mon.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef TRUST_ROUTER_MON_H +#define TRUST_ROUTER_MON_H + +#include +#include + +/* Typedefs */ +typedef struct mon_req MON_REQ; +typedef struct mon_resp MON_RESP; + +typedef enum mon_cmd MON_CMD; +typedef enum mon_resp_code MON_RESP_CODE; + +typedef struct mon_opt MON_OPT; +typedef enum mon_opt_type MON_OPT_TYPE; + +typedef enum mon_rc MON_RC; + +typedef struct mons_instance MONS_INSTANCE; +typedef struct monc_instance MONC_INSTANCE; + +typedef struct mons_dispatch_table_entry MONS_DISPATCH_TABLE_ENTRY; + +typedef int (MONS_REQ_FUNC)(MONS_INSTANCE *, MON_REQ *, MON_RESP *, void *); +typedef int (MONS_AUTH_FUNC)(gss_name_t client_name, TR_NAME *display_name, void *cookie); +typedef int (MONC_RESP_FUNC)(MONS_INSTANCE *, MON_REQ *, MON_RESP *, void *); + +#endif //TRUST_ROUTER_MON_H diff --git a/include/mon_internal.h b/include/mon_internal.h new file mode 100644 index 0000000..29ea514 --- /dev/null +++ b/include/mon_internal.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef TRUST_ROUTER_MON_REQ_H +#define TRUST_ROUTER_MON_REQ_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Typedefs */ +typedef struct mon_req MON_REQ; +typedef struct mon_resp MON_RESP; + +typedef enum mon_cmd MON_CMD; +typedef enum mon_resp_code MON_RESP_CODE; + +typedef struct mon_opt MON_OPT; +typedef enum mon_opt_type MON_OPT_TYPE; + +typedef enum mon_rc MON_RC; + +typedef struct mons_instance MONS_INSTANCE; +typedef struct monc_instance MONC_INSTANCE; + +typedef int (MONS_REQ_FUNC)(MONS_INSTANCE *, MON_REQ *, MON_RESP *, void *); +typedef int (MONS_AUTH_FUNC)(gss_name_t client_name, TR_NAME *display_name, void *cookie); +typedef int (MONC_RESP_FUNC)(MONS_INSTANCE *, MON_REQ *, MON_RESP *, void *); + +/* Struct and enum definitions */ +enum mon_rc { + MON_SUCCESS=0, + MON_ERROR, /* generic error */ + MON_BADARG, /* problem with the arguments */ + MON_NOMEM, /* out of memory */ + MON_NOPARSE, /* parsing failed */ +}; + +enum mon_cmd { + MON_CMD_UNKNOWN=0, + MON_CMD_RECONFIGURE, + MON_CMD_SHOW +}; + +/* These should be explicitly numbered because they form part of the public API */ +enum mon_resp_code { + MON_RESP_SUCCESS=0, + MON_RESP_ERROR=1, // generic error +}; + +enum mon_opt_type { + OPT_TYPE_UNKNOWN=0, + OPT_TYPE_ANY, + + // System information + OPT_TYPE_SHOW_VERSION, + OPT_TYPE_SHOW_CONFIG_FILES, + + // System statistics + OPT_TYPE_SHOW_UPTIME, + OPT_TYPE_SHOW_TID_REQ_COUNT, + OPT_TYPE_SHOW_TID_REQ_ERR_COUNT, + OPT_TYPE_SHOW_TID_REQ_PENDING, + + // Dynamic trust router state + OPT_TYPE_SHOW_ROUTES, + OPT_TYPE_SHOW_PEERS, + OPT_TYPE_SHOW_COMMUNITIES, + OPT_TYPE_SHOW_REALMS, + OPT_TYPE_SHOW_RP_CLIENTS +}; + +struct mon_opt { + MON_OPT_TYPE type; +}; + +struct mon_req { + MON_CMD command; + GArray *options; +}; + +struct mon_resp { + MON_RESP_CODE code; + TR_NAME *message; + json_t *payload; +}; + +/* Monitoring server instance */ +struct mons_instance { + const char *hostname; + unsigned int port; + TR_GSS_NAMES *authorized_gss_names; + TIDS_INSTANCE *tids; + TRPS_INSTANCE *trps; + MONS_REQ_FUNC *req_handler; + MONS_AUTH_FUNC *auth_handler; + void *cookie; + GPtrArray *handlers; + GArray *pids; /* PIDs of active mons processes */ +}; + +/* Client instance */ +struct monc_instance { + TR_GSSC_INSTANCE *gssc; +}; + +/* Prototypes */ +/* tr_mon.c */ +const char *mon_cmd_to_string(MON_CMD cmd); +MON_CMD mon_cmd_from_string(const char *s); +const char *mon_opt_type_to_string(MON_OPT_TYPE opt_type); +MON_OPT_TYPE mon_opt_type_from_string(const char *s); + +/* mon_req.c */ +MON_REQ *mon_req_new(TALLOC_CTX *mem_ctx, MON_CMD cmd); +void mon_req_free(MON_REQ *req); +MON_RC mon_req_add_option(MON_REQ *req, MON_OPT_TYPE opt_type); +size_t mon_req_opt_count(MON_REQ *req); +MON_OPT *mon_req_opt_index(MON_REQ *req, size_t index); + +/* mon_req_encode.c */ +json_t *mon_req_encode(MON_REQ *req); + +/* mon_req_decode.c */ +MON_REQ *mon_req_decode(TALLOC_CTX *mem_ctx, json_t *req_json); +MON_REQ *mon_req_parse(TALLOC_CTX *mem_ctx, const char *input); + +/* mon_resp.c */ +MON_RESP *mon_resp_new(TALLOC_CTX *mem_ctx, MON_RESP_CODE code, const char *msg, json_t *payload); +void mon_resp_free(MON_RESP *resp); +int mon_resp_set_message(MON_RESP *resp, const char *new_msg); +void mon_resp_set_payload(MON_RESP *resp, json_t *new_payload); + +/* mon_resp_encode.c */ +json_t *mon_resp_encode(MON_RESP *resp); + +/* mon_resp_decode.c */ +MON_RESP * mon_resp_decode(TALLOC_CTX *mem_ctx, json_t *resp_json); + +/* mons.c */ +MONS_INSTANCE *mons_new(TALLOC_CTX *mem_ctx); +int mons_get_listener(MONS_INSTANCE *mons, MONS_REQ_FUNC *req_handler, MONS_AUTH_FUNC *auth_handler, const char *hostname, + unsigned int port, void *cookie, int *fd_out, size_t max_fd); +int mons_accept(MONS_INSTANCE *mons, int listen); + +/* monc.c */ +MONC_INSTANCE *monc_new(TALLOC_CTX *mem_ctx); +void monc_free(MONC_INSTANCE *monc); +DH *monc_get_dh(MONC_INSTANCE *inst); +DH *monc_set_dh(MONC_INSTANCE *inst, DH *dh); +int monc_open_connection(MONC_INSTANCE *monc, const char *server, unsigned int port); +MON_RESP *monc_send_request(TALLOC_CTX *mem_ctx, MONC_INSTANCE *monc, MON_REQ *req); + +#endif //TRUST_ROUTER_MON_REQ_H diff --git a/include/mons_handlers.h b/include/mons_handlers.h new file mode 100644 index 0000000..48a01b3 --- /dev/null +++ b/include/mons_handlers.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef TRUST_ROUTER_MONS_HANDLERS_H +#define TRUST_ROUTER_MONS_HANDLERS_H + +typedef MON_RC (MONS_HANDLER_FUNC)(void *cookie, json_t **result_ptr); + +struct mons_dispatch_table_entry { + MON_CMD command; + MON_OPT_TYPE opt_type; + MONS_HANDLER_FUNC *handler; + void *cookie; +}; + +/* mons_handlers.c */ +MON_RESP *mons_handle_request(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ *req); +MON_RC mons_register_handler(MONS_INSTANCE *mons, MON_CMD cmd, MON_OPT_TYPE opt_type, MONS_HANDLER_FUNC *f, void *cookie); + +#endif //TRUST_ROUTER_MONS_HANDLERS_H diff --git a/include/tid_internal.h b/include/tid_internal.h index 855df0a..8613eb8 100644 --- a/include/tid_internal.h +++ b/include/tid_internal.h @@ -39,6 +39,7 @@ #include #include #include +#include "tr_gss_client.h" struct tid_srvr_blk { TID_SRVR_BLK *next; @@ -84,23 +85,26 @@ struct tid_req { }; struct tidc_instance { - // TID_REQ *req_list; - // TBD -- Do we still need a separate private key */ - // char *priv_key; - // int priv_len; - DH *client_dh; /* Client's DH struct with priv and pub keys */ + TR_GSSC_INSTANCE *gssc; +}; + +struct tid_process { + pid_t pid; + int read_fd; }; struct tids_instance { int req_count; + int error_count; char *priv_key; char *ipaddr; const char *hostname; TIDS_REQ_FUNC *req_handler; tids_auth_func *auth_handler; void *cookie; - uint16_t tids_port; + unsigned int tids_port; TR_NAME *gss_name; /* GSS name client used for authentication */ + GArray *pids; /* PIDs of active tids processes */ }; /** Decrement a reference to #json when this tid_req is cleaned up. A @@ -117,7 +121,10 @@ TID_SRVR_BLK *tid_srvr_blk_add_func(TID_SRVR_BLK *head, TID_SRVR_BLK *new); #define tid_srvr_blk_add(head, new) ((head)=tid_srvr_blk_add_func((head),(new))) void tid_srvr_blk_set_path(TID_SRVR_BLK *block, TID_PATH *path); +TID_RC tid_resp_cpy(TID_RESP *dst, TID_RESP *src); void tid_resp_set_cons(TID_RESP *resp, TR_CONSTRAINT_SET *cons); void tid_resp_set_error_path(TID_RESP *resp, json_t *ep); +void tids_sweep_procs(TIDS_INSTANCE *tids); + #endif diff --git a/include/tr_comm.h b/include/tr_comm.h index 5a770b8..6228c1f 100644 --- a/include/tr_comm.h +++ b/include/tr_comm.h @@ -226,4 +226,7 @@ TR_NAME *tr_realm_dup_id(TR_REALM *realm); const char *tr_realm_role_to_str(TR_REALM_ROLE role); TR_REALM_ROLE tr_realm_role_from_str(const char *s); +/* tr_comm_encoders.c */ +json_t *tr_comm_table_to_json(TR_COMM_TABLE *ctable); + #endif diff --git a/include/tr_config.h b/include/tr_config.h index 89fd328..30317de 100644 --- a/include/tr_config.h +++ b/include/tr_config.h @@ -41,9 +41,11 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -51,6 +53,7 @@ #define TR_DEFAULT_MAX_TREE_DEPTH 12 #define TR_DEFAULT_TRPS_PORT 12308 #define TR_DEFAULT_TIDS_PORT 12309 +#define TR_DEFAULT_MONITORING_PORT 0 /* defaults to being turned off */ #define TR_DEFAULT_LOG_THRESHOLD LOG_INFO #define TR_DEFAULT_CONSOLE_THRESHOLD LOG_NOTICE #define TR_DEFAULT_APC_EXPIRATION_INTERVAL 43200 @@ -61,6 +64,8 @@ #define TR_DEFAULT_TID_RESP_NUMER 2 #define TR_DEFAULT_TID_RESP_DENOM 3 +#define TR_CFG_INVALID_SERIAL -1 + typedef enum tr_cfg_rc { TR_CFG_SUCCESS = 0, /* No error */ TR_CFG_ERROR, /* General processing error */ @@ -73,6 +78,7 @@ typedef struct tr_cfg_internal { unsigned int max_tree_depth; unsigned int tids_port; unsigned int trps_port; + unsigned int monitoring_port; const char *hostname; int log_threshold; int console_threshold; @@ -84,15 +90,23 @@ typedef struct tr_cfg_internal { unsigned int tid_req_timeout; unsigned int tid_resp_numer; /* numerator of fraction of AAA servers to wait for in unshared mode */ unsigned int tid_resp_denom; /* denominator of fraction of AAA servers to wait for in unshared mode */ + TR_GSS_NAMES *monitoring_credentials; } TR_CFG_INTERNAL; +/* record of files loaded for this configuration */ +typedef struct tr_cfg_file { + const char *name; + json_int_t serial; +} TR_CFG_FILE; + typedef struct tr_cfg { TR_CFG_INTERNAL *internal; /* internal trust router config */ TR_RP_CLIENT *rp_clients; /* locally associated RP Clients */ TRP_PTABLE *peers; /* TRP peer table */ TR_COMM_TABLE *ctable; /* communities/realms */ TR_AAA_SERVER *default_servers; /* default server list */ - /* TBD -- Global Filters */ + + GArray *files; /* files loaded to make this configuration */ } TR_CFG; typedef struct tr_cfg_mgr { @@ -115,7 +129,32 @@ void tr_print_comms(TR_COMM_TABLE *ctab); void tr_print_comm_idps(TR_COMM_TABLE *ctab, TR_COMM *comm); void tr_print_comm_rps(TR_COMM_TABLE *ctab, TR_COMM *comm); -TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *cfg, TR_NAME *idp_id, TR_CFG_RC *rc); -TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *cfg, TR_NAME *rp_gss, TR_CFG_RC *rc); +/* tr_config_internal.c */ +TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jint); + +/* tr_config_comms.c */ +TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc); +TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc); +TR_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg); +TR_CFG_RC tr_cfg_parse_default_servers (TR_CFG *trc, json_t *jcfg); + +/* tr_config_filters.c */ +TR_FILTER_SET *tr_cfg_parse_filters(TALLOC_CTX *mem_ctx, json_t *jfilts, TR_CFG_RC *rc); + +/* tr_config_orgs.c */ +TR_CFG_RC tr_cfg_parse_local_orgs(TR_CFG *trc, json_t *jcfg); +TR_CFG_RC tr_cfg_parse_peer_orgs(TR_CFG *trc, json_t *jcfg); + +/* tr_config_realms.c */ +TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc); +TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc); +TR_APC *tr_cfg_parse_apcs(TALLOC_CTX *mem_ctx, json_t *japcs, TR_CFG_RC *rc); + +/* tr_config_rp_clients.c */ +TR_RP_CLIENT *tr_cfg_parse_rp_clients(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc); +TR_CFG_RC tr_cfg_parse_gss_names(TALLOC_CTX *mem_ctx, json_t *jgss_names, TR_GSS_NAMES **gssn_out); + +/* tr_config_encoders.c */ +json_t *tr_cfg_files_to_json_array(TR_CFG *cfg); #endif diff --git a/include/tr_event.h b/include/tr_event.h index dc93860..55cc272 100644 --- a/include/tr_event.h +++ b/include/tr_event.h @@ -41,7 +41,7 @@ /* struct for hanging on to a socket listener event */ struct tr_socket_event { - size_t n_sock_fd; /* how many of those are filled in? */ + int n_sock_fd; /* how many of those are filled in? */ int sock_fd[TR_MAX_SOCKETS]; /* the fd for the socket */ struct event *ev[TR_MAX_SOCKETS]; /* its events */ }; diff --git a/include/tr_filter.h b/include/tr_filter.h index a7704d7..ece3650 100644 --- a/include/tr_filter.h +++ b/include/tr_filter.h @@ -144,4 +144,7 @@ int tr_filter_validate_spec_field(TR_FILTER_TYPE ftype, TR_FSPEC *fspec); const char *tr_filter_type_to_string(TR_FILTER_TYPE ftype); TR_FILTER_TYPE tr_filter_type_from_string(const char *s); +/* tr_filter_encoders.c */ +json_t *tr_filter_set_to_json(TR_FILTER_SET *filter_set); + #endif diff --git a/include/tr_gss.h b/include/tr_gss.h index 676c8e4..850f51b 100644 --- a/include/tr_gss.h +++ b/include/tr_gss.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, JANET(UK) + * Copyright (c) 2018, JANET(UK) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,31 +32,15 @@ * */ -#ifndef __TR_GSS_H__ -#define __TR_GSS_H__ +#ifndef TRUST_ROUTER_TR_GSS_H +#define TRUST_ROUTER_TR_GSS_H -#include -#include +#include -#define TR_MAX_GSS_NAMES 5 +typedef int (TR_GSS_AUTH_FN)(gss_name_t, TR_NAME *, void *); +typedef TR_MSG *(TR_GSS_HANDLE_REQ_FN)(TALLOC_CTX *, TR_MSG *, void *); -typedef struct tr_gss_names { - TR_NAME *names[TR_MAX_GSS_NAMES]; -} TR_GSS_NAMES; +void tr_gss_handle_connection(int conn, const char *acceptor_service, const char *acceptor_hostname, TR_GSS_AUTH_FN auth_cb, + void *auth_cookie, TR_GSS_HANDLE_REQ_FN req_cb, void *req_cookie); -typedef struct tr_gss_names_iter { - TR_GSS_NAMES *gn; - int ii; /* which entry did we last output? */ -} TR_GSS_NAMES_ITER; - -TR_GSS_NAMES *tr_gss_names_new(TALLOC_CTX *mem_ctx); -void tr_gss_names_free(TR_GSS_NAMES *gn); -int tr_gss_names_add(TR_GSS_NAMES *gn, TR_NAME *new); -int tr_gss_names_matches(TR_GSS_NAMES *gn, TR_NAME *name); - -TR_GSS_NAMES_ITER *tr_gss_names_iter_new(TALLOC_CTX *mem_ctx); -TR_NAME *tr_gss_names_iter_first(TR_GSS_NAMES_ITER *iter, TR_GSS_NAMES *gn); -TR_NAME *tr_gss_names_iter_next(TR_GSS_NAMES_ITER *iter); -void tr_gss_names_iter_free(TR_GSS_NAMES_ITER *iter); - -#endif /* __TR_GSS_H__ */ +#endif //TRUST_ROUTER_TR_GSS_H diff --git a/include/tr_gss_client.h b/include/tr_gss_client.h new file mode 100644 index 0000000..25024a6 --- /dev/null +++ b/include/tr_gss_client.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TRUST_ROUTER_TR_GSS_CLIENT_H +#define TRUST_ROUTER_TR_GSS_CLIENT_H + +#include +#include +#include + +typedef struct tr_gssc_instance TR_GSSC_INSTANCE; + +/* Client instance */ +struct tr_gssc_instance { + const char *service_name; + DH *client_dh; + gss_ctx_id_t *gss_ctx; + int conn; +}; + +/* tr_gss_client.c */ +TR_GSSC_INSTANCE *tr_gssc_instance_new(TALLOC_CTX *mem_ctx); +void tr_gssc_instance_free(TR_GSSC_INSTANCE *tr_gssc); +int tr_gssc_open_connection(TR_GSSC_INSTANCE *gssc, const char *server, unsigned int port); +TR_MSG *tr_gssc_exchange_msgs(TALLOC_CTX *mem_ctx, TR_GSSC_INSTANCE *gssc, TR_MSG *req_msg); +DH * tr_gssc_get_dh(TR_GSSC_INSTANCE *inst); +DH *tr_gssc_set_dh(TR_GSSC_INSTANCE *inst, DH *dh); + +#endif //TRUST_ROUTER_TR_GSS_CLIENT_H diff --git a/include/tr_gss_names.h b/include/tr_gss_names.h new file mode 100644 index 0000000..f8f97a2 --- /dev/null +++ b/include/tr_gss_names.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __TR_GSS_H__ +#define __TR_GSS_H__ + +#include +#include + +#define TR_MAX_GSS_NAMES 5 + +typedef struct tr_gss_names { + TR_NAME *names[TR_MAX_GSS_NAMES]; +} TR_GSS_NAMES; + +typedef struct tr_gss_names_iter { + TR_GSS_NAMES *gn; + int ii; /* which entry did we last output? */ +} TR_GSS_NAMES_ITER; + +TR_GSS_NAMES *tr_gss_names_new(TALLOC_CTX *mem_ctx); +void tr_gss_names_free(TR_GSS_NAMES *gn); +int tr_gss_names_add(TR_GSS_NAMES *gn, TR_NAME *new); +TR_GSS_NAMES *tr_gss_names_dup(TALLOC_CTX *mem_ctx, TR_GSS_NAMES *orig); +int tr_gss_names_matches(TR_GSS_NAMES *gn, TR_NAME *name); + +TR_GSS_NAMES_ITER *tr_gss_names_iter_new(TALLOC_CTX *mem_ctx); +TR_NAME *tr_gss_names_iter_first(TR_GSS_NAMES_ITER *iter, TR_GSS_NAMES *gn); +TR_NAME *tr_gss_names_iter_next(TR_GSS_NAMES_ITER *iter); +void tr_gss_names_iter_free(TR_GSS_NAMES_ITER *iter); + +json_t *tr_gss_names_to_json_array(TR_GSS_NAMES *gss_names); + +#endif /* __TR_GSS_H__ */ diff --git a/include/tr_idp.h b/include/tr_idp.h index 951f7d7..68d669e 100644 --- a/include/tr_idp.h +++ b/include/tr_idp.h @@ -83,7 +83,8 @@ TR_IDP_REALM *tr_idp_realm_remove_func(TR_IDP_REALM *head, TR_IDP_REALM *remove) #define tr_idp_realm_remove(head,remove) ((head)=tr_idp_realm_remove_func((head),(remove))) TR_IDP_REALM *tr_idp_realm_sweep_func(TR_IDP_REALM *head); #define tr_idp_realm_sweep(head) ((head)=tr_idp_realm_sweep_func((head))) -char *tr_idp_realm_to_str(TALLOC_CTX *mem_ctx, TR_IDP_REALM *idp); +int tr_idp_realm_aaa_server_count(TR_IDP_REALM *idp); +int tr_idp_realm_apc_count(TR_IDP_REALM *idp); void tr_idp_realm_incref(TR_IDP_REALM *realm); void tr_idp_realm_decref(TR_IDP_REALM *realm); @@ -98,4 +99,8 @@ TR_AAA_SERVER *tr_aaa_server_iter_next(TR_AAA_SERVER_ITER *iter); TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_realm_name, TR_NAME *comm, int *shared_out); TR_AAA_SERVER *tr_default_server_lookup(TR_AAA_SERVER *default_servers, TR_NAME *comm); +/* tr_idp_encoders.c */ +char *tr_idp_realm_to_str(TALLOC_CTX *mem_ctx, TR_IDP_REALM *idp); +json_t *tr_idp_realms_to_json(TR_IDP_REALM *idp); + #endif diff --git a/include/tr_mon.h b/include/tr_mon.h new file mode 100644 index 0000000..15ab640 --- /dev/null +++ b/include/tr_mon.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TR_MON_H +#define TR_MON_H + +#include +#include +#include +#include + +int tr_mons_event_init(struct event_base *base, + MONS_INSTANCE *mons, + TR_CFG_MGR *cfg_mgr, + struct tr_socket_event *mons_ev); + +#endif /* TR_MON_H */ diff --git a/include/tr_msg.h b/include/tr_msg.h index e10fd1a..6605cbd 100644 --- a/include/tr_msg.h +++ b/include/tr_msg.h @@ -38,6 +38,8 @@ #include #include #include +#include + typedef struct tr_msg TR_MSG; enum msg_type { @@ -45,7 +47,9 @@ enum msg_type { TID_REQUEST, TID_RESPONSE, TRP_UPDATE, - TRP_REQUEST + TRP_REQUEST, + MON_REQUEST, + MON_RESPONSE }; /* Union of TR message types to hold message of any type. */ @@ -65,11 +69,15 @@ TRP_UPD *tr_msg_get_trp_upd(TR_MSG *msg); void tr_msg_set_trp_upd(TR_MSG *msg, TRP_UPD *req); TRP_REQ *tr_msg_get_trp_req(TR_MSG *msg); void tr_msg_set_trp_req(TR_MSG *msg, TRP_REQ *req); +MON_REQ *tr_msg_get_mon_req(TR_MSG *msg); +void tr_msg_set_mon_req(TR_MSG *msg, MON_REQ *req); +MON_RESP *tr_msg_get_mon_resp(TR_MSG *msg); +void tr_msg_set_mon_resp(TR_MSG *msg, MON_RESP *resp); /* Encoders/Decoders */ -char *tr_msg_encode(TR_MSG *msg); -TR_MSG *tr_msg_decode(const char *jmsg, size_t len); +char *tr_msg_encode(TALLOC_CTX *mem_ctx, TR_MSG *msg); +TR_MSG *tr_msg_decode(TALLOC_CTX *mem_ctx, const char *jmsg, size_t len); void tr_msg_free_encoded(char *jmsg); void tr_msg_free_decoded(TR_MSG *msg); diff --git a/include/tr_rp.h b/include/tr_rp.h index fa6b84e..4424c73 100644 --- a/include/tr_rp.h +++ b/include/tr_rp.h @@ -36,18 +36,7 @@ #define TR_RP_H #include - -#include -#include - -typedef struct tr_rp_client { - struct tr_rp_client *next; - struct tr_rp_client *comm_next; - TR_GSS_NAMES *gss_names; - TR_FILTER_SET *filters; -} TR_RP_CLIENT; - -typedef struct tr_rp_client *TR_RP_CLIENT_ITER; +#include /* Structure to make a linked list of RP realms by name for community config */ typedef struct tr_rp_realm { @@ -57,18 +46,6 @@ typedef struct tr_rp_realm { } TR_RP_REALM; /* prototypes */ -TR_RP_CLIENT *tr_rp_client_new(TALLOC_CTX *mem_ctx); -void tr_rp_client_free(TR_RP_CLIENT *client); -TR_RP_CLIENT *tr_rp_client_add_func(TR_RP_CLIENT *clients, TR_RP_CLIENT *new); -#define tr_rp_client_add(clients,new) ((clients)=tr_rp_client_add_func((clients),(new))) -int tr_rp_client_add_gss_name(TR_RP_CLIENT *client, TR_NAME *name); -int tr_rp_client_set_filters(TR_RP_CLIENT *client, TR_FILTER_SET *filts); -TR_RP_CLIENT_ITER *tr_rp_client_iter_new(TALLOC_CTX *memctx); -void tr_rp_client_iter_free(TR_RP_CLIENT_ITER *iter); -TR_RP_CLIENT *tr_rp_client_iter_first(TR_RP_CLIENT_ITER *iter, TR_RP_CLIENT *rp_clients); -TR_RP_CLIENT *tr_rp_client_iter_next(TR_RP_CLIENT_ITER *iter); -TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name); - TR_RP_REALM *tr_rp_realm_new(TALLOC_CTX *mem_ctx); void tr_rp_realm_free(TR_RP_REALM *rp); TR_NAME *tr_rp_realm_get_id(TR_RP_REALM *rp); diff --git a/include/tr_rp_client.h b/include/tr_rp_client.h new file mode 100644 index 0000000..1ff4b29 --- /dev/null +++ b/include/tr_rp_client.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TRUST_ROUTER_TR_RP_CLIENT_H +#define TRUST_ROUTER_TR_RP_CLIENT_H + +#include + +#include +#include + +typedef struct tr_rp_client { + struct tr_rp_client *next; + struct tr_rp_client *comm_next; + TR_GSS_NAMES *gss_names; + TR_FILTER_SET *filters; +} TR_RP_CLIENT; + +typedef struct tr_rp_client *TR_RP_CLIENT_ITER; + +/* tr_rp_client.c */ +TR_RP_CLIENT *tr_rp_client_new(TALLOC_CTX *mem_ctx); +void tr_rp_client_free(TR_RP_CLIENT *client); +TR_RP_CLIENT *tr_rp_client_add_func(TR_RP_CLIENT *clients, TR_RP_CLIENT *new); +#define tr_rp_client_add(clients,new) ((clients)=tr_rp_client_add_func((clients),(new))) +int tr_rp_client_add_gss_name(TR_RP_CLIENT *client, TR_NAME *name); +int tr_rp_client_set_filters(TR_RP_CLIENT *client, TR_FILTER_SET *filts); +TR_RP_CLIENT_ITER *tr_rp_client_iter_new(TALLOC_CTX *memctx); +void tr_rp_client_iter_free(TR_RP_CLIENT_ITER *iter); +TR_RP_CLIENT *tr_rp_client_iter_first(TR_RP_CLIENT_ITER *iter, TR_RP_CLIENT *rp_clients); +TR_RP_CLIENT *tr_rp_client_iter_next(TR_RP_CLIENT_ITER *iter); +TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name); + +/* tr_rp_client_encoders.c */ +json_t *tr_rp_clients_to_json(TR_RP_CLIENT *rp_clients); + +#endif //TRUST_ROUTER_TR_RP_CLIENT_H diff --git a/include/tr_socket.h b/include/tr_socket.h new file mode 100644 index 0000000..064c6fc --- /dev/null +++ b/include/tr_socket.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TRUST_ROUTER_TR_SOCKET_H +#define TRUST_ROUTER_TR_SOCKET_H + +#include +#include // for nfds_t + +nfds_t tr_sock_listen_all(unsigned int port, int *fd_out, nfds_t max_fd); + +#endif //TRUST_ROUTER_TR_SOCKET_H diff --git a/include/tr_tid.h b/include/tr_tid.h index 924293d..d8285b0 100644 --- a/include/tr_tid.h +++ b/include/tr_tid.h @@ -38,13 +38,14 @@ #include #include #include +#include #define TR_TID_MAX_AAA_SERVERS 10 -int tr_tids_event_init(struct event_base *base, - TIDS_INSTANCE *tids, - TR_CFG_MGR *cfg_mgr, - TRPS_INSTANCE *trps, - struct tr_socket_event *tids_ev); +int tr_tids_event_init(struct event_base *base, TIDS_INSTANCE *tids, TR_CFG_MGR *cfg_mgr, TRPS_INSTANCE *trps, + struct tr_socket_event *tids_ev, struct event **sweep_ev); + +/* tr_tid_mons.c */ +void tr_tid_register_mons_handlers(TIDS_INSTANCE *tids, MONS_INSTANCE *mons); #endif /* TR_TID_H */ diff --git a/include/tr_trp.h b/include/tr_trp.h index 297295f..dea20e4 100644 --- a/include/tr_trp.h +++ b/include/tr_trp.h @@ -44,6 +44,7 @@ #include #include #include +#include typedef struct tr_trps_events { struct event *trps_ev; @@ -59,6 +60,7 @@ struct tr_instance { TR_CFG_MGR *cfg_mgr; TIDS_INSTANCE *tids; TRPS_INSTANCE *trps; + MONS_INSTANCE *mons; TR_CFGWATCH *cfgwatch; TR_TRPS_EVENTS *events; }; @@ -78,4 +80,8 @@ TRP_RC tr_trpc_initiate(TRPS_INSTANCE *trps, TRP_PEER *peer, struct event *ev); void tr_config_changed(TR_CFG *new_cfg, void *cookie); TRP_RC tr_connect_to_peers(TRPS_INSTANCE *trps, struct event *ev); void tr_peer_status_change(TRP_PEER *peer, void *cookie); + +/* tr_trp_mons.h */ +void tr_trp_register_mons_handlers(TRPS_INSTANCE *trps, MONS_INSTANCE *mons); + #endif /* TR_TRP_H */ diff --git a/include/tr_util.h b/include/tr_util.h index 3a3f7bc..bed0482 100644 --- a/include/tr_util.h +++ b/include/tr_util.h @@ -37,6 +37,10 @@ #include +/* NB, tr_bin_to_hex() is also prototyped in trust_router/tr_dh.h */ +TR_EXPORT void tr_bin_to_hex(const unsigned char * bin, size_t binlen, + char * hex_out, size_t hex_len); TR_EXPORT int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2); +char *timespec_to_str(struct timespec *ts); #endif /* TR_UTIL_H */ diff --git a/include/trp_internal.h b/include/trp_internal.h index d6828d5..a35a043 100644 --- a/include/trp_internal.h +++ b/include/trp_internal.h @@ -43,7 +43,9 @@ #include #include #include +#include #include +#include #include #include #include diff --git a/include/trp_peer.h b/include/trp_peer.h new file mode 100644 index 0000000..8886bef --- /dev/null +++ b/include/trp_peer.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TRUST_ROUTER_TRP_PEER_H +#define TRUST_ROUTER_TRP_PEER_H + +#include +#include + +typedef enum trp_peer_conn_status { + PEER_DISCONNECTED=0, + PEER_CONNECTED +} TRP_PEER_CONN_STATUS; + +typedef struct trp_peer TRP_PEER; +struct trp_peer { + TRP_PEER *next; /* for making a linked list */ + TR_NAME *label; /* often null, set on first call to trp_peer_get_label or dup_label */ + char *server; + TR_GSS_NAMES *gss_names; + TR_NAME *servicename; + unsigned int port; + unsigned int linkcost; + struct timespec last_conn_attempt; + TRP_PEER_CONN_STATUS outgoing_status; + TRP_PEER_CONN_STATUS incoming_status; + void (*conn_status_cb)(TRP_PEER *, void *); /* callback for connected status change */ + void *conn_status_cookie; + TR_FILTER_SET *filters; +}; + + +TRP_PEER *trp_peer_new(TALLOC_CTX *memctx); +void trp_peer_free(TRP_PEER *peer); +TRP_PEER *trp_peer_tail(TRP_PEER *peer); +TR_NAME *trp_peer_get_label(TRP_PEER *peer); +TR_NAME *trp_peer_dup_label(TRP_PEER *peer); +char *trp_peer_get_server(TRP_PEER *peer); +void trp_peer_set_server(TRP_PEER *peer, const char *server); +void trp_peer_add_gss_name(TRP_PEER *peer, TR_NAME *gssname); +void trp_peer_set_gss_names(TRP_PEER *peer, TR_GSS_NAMES *gss_names); +TR_GSS_NAMES *trp_peer_get_gss_names(TRP_PEER *peer); +TR_NAME *trp_peer_get_servicename(TRP_PEER *peer); +TR_NAME *trp_peer_dup_servicename(TRP_PEER *peer); +unsigned int trp_peer_get_port(TRP_PEER *peer); +void trp_peer_set_port(TRP_PEER *peer, unsigned int port); +unsigned int trp_peer_get_linkcost(TRP_PEER *peer); +struct timespec *trp_peer_get_last_conn_attempt(TRP_PEER *peer); +void trp_peer_set_last_conn_attempt(TRP_PEER *peer, struct timespec *time); +TRP_PEER_CONN_STATUS trp_peer_get_outgoing_status(TRP_PEER *peer); +void trp_peer_set_outgoing_status(TRP_PEER *peer, TRP_PEER_CONN_STATUS status); +TRP_PEER_CONN_STATUS trp_peer_get_incoming_status(TRP_PEER *peer); +void trp_peer_set_incoming_status(TRP_PEER *peer, TRP_PEER_CONN_STATUS status); +int trp_peer_is_connected(TRP_PEER *peer); +void trp_peer_set_linkcost(TRP_PEER *peer, unsigned int linkcost); +void trp_peer_set_conn_status_cb(TRP_PEER *peer, void (*cb)(TRP_PEER *, void *), void *cookie); +void trp_peer_set_filters(TRP_PEER *peer, TR_FILTER_SET *filts); +TR_FILTER *trp_peer_get_filter(TRP_PEER *peer, TR_FILTER_TYPE ftype); + +/* trp_peer_encoders.c */ +char *trp_peer_to_str(TALLOC_CTX *memctx, TRP_PEER *peer, const char *sep); +json_t *trp_peer_to_json(TRP_PEER *peer); + +#endif //TRUST_ROUTER_TRP_PEER_H diff --git a/include/trp_ptable.h b/include/trp_ptable.h index 186f77a..b26fac1 100644 --- a/include/trp_ptable.h +++ b/include/trp_ptable.h @@ -39,31 +39,10 @@ #include #include -#include +#include #include #include - -typedef enum trp_peer_conn_status { - PEER_DISCONNECTED=0, - PEER_CONNECTED -} TRP_PEER_CONN_STATUS; - -typedef struct trp_peer TRP_PEER; -struct trp_peer { - TRP_PEER *next; /* for making a linked list */ - TR_NAME *label; /* often null, set on first call to trp_peer_get_label or dup_label */ - char *server; - TR_GSS_NAMES *gss_names; - TR_NAME *servicename; - unsigned int port; - unsigned int linkcost; - struct timespec last_conn_attempt; - TRP_PEER_CONN_STATUS outgoing_status; - TRP_PEER_CONN_STATUS incoming_status; - void (*conn_status_cb)(TRP_PEER *, void *); /* callback for connected status change */ - void *conn_status_cookie; - TR_FILTER_SET *filters; -}; +#include typedef struct trp_ptable { TRP_PEER *head; /* head of a peer table list */ @@ -78,38 +57,14 @@ TRP_RC trp_ptable_add(TRP_PTABLE *ptbl, TRP_PEER *newpeer); TRP_RC trp_ptable_remove(TRP_PTABLE *ptbl, TRP_PEER *peer); TRP_PEER *trp_ptable_find_gss_name(TRP_PTABLE *ptbl, TR_NAME *gssname); TRP_PEER *trp_ptable_find_servicename(TRP_PTABLE *ptbl, TR_NAME *servicename); -char *trp_ptable_to_str(TALLOC_CTX *memctx, TRP_PTABLE *ptbl, const char *sep, const char *lineterm); TRP_PTABLE_ITER *trp_ptable_iter_new(TALLOC_CTX *mem_ctx); TRP_PEER *trp_ptable_iter_first(TRP_PTABLE_ITER *iter, TRP_PTABLE *ptbl); TRP_PEER *trp_ptable_iter_next(TRP_PTABLE_ITER *iter); void trp_ptable_iter_free(TRP_PTABLE_ITER *iter); -TRP_PEER *trp_peer_new(TALLOC_CTX *memctx); -void trp_peer_free(TRP_PEER *peer); -TR_NAME *trp_peer_get_label(TRP_PEER *peer); -TR_NAME *trp_peer_dup_label(TRP_PEER *peer); -char *trp_peer_get_server(TRP_PEER *peer); -void trp_peer_set_server(TRP_PEER *peer, const char *server); -void trp_peer_add_gss_name(TRP_PEER *peer, TR_NAME *gssname); -void trp_peer_set_gss_names(TRP_PEER *peer, TR_GSS_NAMES *gss_names); -TR_GSS_NAMES *trp_peer_get_gss_names(TRP_PEER *peer); -TR_NAME *trp_peer_get_servicename(TRP_PEER *peer); -TR_NAME *trp_peer_dup_servicename(TRP_PEER *peer); -unsigned int trp_peer_get_port(TRP_PEER *peer); -void trp_peer_set_port(TRP_PEER *peer, unsigned int port); -unsigned int trp_peer_get_linkcost(TRP_PEER *peer); -struct timespec *trp_peer_get_last_conn_attempt(TRP_PEER *peer); -void trp_peer_set_last_conn_attempt(TRP_PEER *peer, struct timespec *time); -TRP_PEER_CONN_STATUS trp_peer_get_outgoing_status(TRP_PEER *peer); -void trp_peer_set_outgoing_status(TRP_PEER *peer, TRP_PEER_CONN_STATUS status); -TRP_PEER_CONN_STATUS trp_peer_get_incoming_status(TRP_PEER *peer); -void trp_peer_set_incoming_status(TRP_PEER *peer, TRP_PEER_CONN_STATUS status); -int trp_peer_is_connected(TRP_PEER *peer); -void trp_peer_set_linkcost(TRP_PEER *peer, unsigned int linkcost); -void trp_peer_set_conn_status_cb(TRP_PEER *peer, void (*cb)(TRP_PEER *, void *), void *cookie); -void trp_peer_set_filters(TRP_PEER *peer, TR_FILTER_SET *filts); -TR_FILTER *trp_peer_get_filter(TRP_PEER *peer, TR_FILTER_TYPE ftype); -char *trp_peer_to_str(TALLOC_CTX *memctx, TRP_PEER *peer, const char *sep); +/* trp_ptable_encoders.c */ +char *trp_ptable_to_str(TALLOC_CTX *memctx, TRP_PTABLE *ptbl, const char *sep, const char *lineterm); +json_t *trp_ptable_to_json(TRP_PTABLE *ptbl); #endif /* _TRP_PTABLE_H_ */ diff --git a/include/trp_route.h b/include/trp_route.h new file mode 100644 index 0000000..d159861 --- /dev/null +++ b/include/trp_route.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef TRUST_ROUTER_TRP_ROUTE_H +#define TRUST_ROUTER_TRP_ROUTE_H + +typedef struct trp_route { + TR_NAME *comm; + TR_NAME *realm; + TR_NAME *peer; + unsigned int metric; + TR_NAME *trust_router; /* hostname */ + unsigned int trp_port; + unsigned int tid_port; + TR_NAME *next_hop; + int selected; + unsigned int interval; /* interval from route update */ + struct timespec *expiry; + int local; /* is this a local route? */ + int triggered; +} TRP_ROUTE; + +/* trp_route.c */ +TRP_ROUTE *trp_route_new(TALLOC_CTX *mem_ctx); +void trp_route_free(TRP_ROUTE *entry); +void trp_route_set_comm(TRP_ROUTE *entry, TR_NAME *comm); +TR_NAME *trp_route_get_comm(TRP_ROUTE *entry); +TR_NAME *trp_route_dup_comm(TRP_ROUTE *entry); +void trp_route_set_realm(TRP_ROUTE *entry, TR_NAME *realm); +TR_NAME *trp_route_get_realm(TRP_ROUTE *entry); +TR_NAME *trp_route_dup_realm(TRP_ROUTE *entry); +void trp_route_set_trust_router(TRP_ROUTE *entry, TR_NAME *tr); +TR_NAME *trp_route_get_trust_router(TRP_ROUTE *entry); +TR_NAME *trp_route_dup_trust_router(TRP_ROUTE *entry); +void trp_route_set_peer(TRP_ROUTE *entry, TR_NAME *peer); +TR_NAME *trp_route_get_peer(TRP_ROUTE *entry); +TR_NAME *trp_route_dup_peer(TRP_ROUTE *entry); +void trp_route_set_metric(TRP_ROUTE *entry, unsigned int metric); +unsigned int trp_route_get_metric(TRP_ROUTE *entry); +void trp_route_set_next_hop(TRP_ROUTE *entry, TR_NAME *next_hop); +TR_NAME *trp_route_get_next_hop(TRP_ROUTE *entry); +TR_NAME *trp_route_dup_next_hop(TRP_ROUTE *entry); +void trp_route_set_selected(TRP_ROUTE *entry, int sel); +int trp_route_is_selected(TRP_ROUTE *entry); +void trp_route_set_interval(TRP_ROUTE *entry, int interval); +int trp_route_get_interval(TRP_ROUTE *entry); +void trp_route_set_expiry(TRP_ROUTE *entry, struct timespec *exp); +struct timespec *trp_route_get_expiry(TRP_ROUTE *entry); +void trp_route_set_local(TRP_ROUTE *entry, int local); +int trp_route_is_local(TRP_ROUTE *entry); +void trp_route_set_triggered(TRP_ROUTE *entry, int trig); +int trp_route_is_triggered(TRP_ROUTE *entry); + +/* trp_route_encoders.c */ +char *trp_route_to_str(TALLOC_CTX *mem_ctx, TRP_ROUTE *entry, const char *sep); +json_t *trp_route_to_json(TRP_ROUTE *route); + +#endif //TRUST_ROUTER_TRP_ROUTE_H diff --git a/include/trp_rtable.h b/include/trp_rtable.h index 583b5a7..0e0639d 100644 --- a/include/trp_rtable.h +++ b/include/trp_rtable.h @@ -39,26 +39,13 @@ #include #include +#include #include -typedef struct trp_route { - TR_NAME *comm; - TR_NAME *realm; - TR_NAME *peer; - unsigned int metric; - TR_NAME *trust_router; /* hostname */ - unsigned int trp_port; - unsigned int tid_port; - TR_NAME *next_hop; - int selected; - unsigned int interval; /* interval from route update */ - struct timespec *expiry; - int local; /* is this a local route? */ - int triggered; -} TRP_ROUTE; typedef GHashTable TRP_RTABLE; +/* trp_rtable.c */ TRP_RTABLE *trp_rtable_new(void); void trp_rtable_free(TRP_RTABLE *rtbl); void trp_rtable_add(TRP_RTABLE *rtbl, TRP_ROUTE *entry); /* adds or updates */ @@ -67,7 +54,7 @@ void trp_rtable_clear(TRP_RTABLE *rtbl); size_t trp_rtable_size(TRP_RTABLE *rtbl); size_t trp_rtable_comm_size(TRP_RTABLE *rtbl, TR_NAME *comm); size_t trp_rtable_realm_size(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm); -TRP_ROUTE **trp_rtable_get_entries(TRP_RTABLE *rtbl, size_t *n_out); +TRP_ROUTE **trp_rtable_get_entries(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, size_t *n_out); TR_NAME **trp_rtable_get_comms(TRP_RTABLE *rtbl, size_t *n_out); TRP_ROUTE **trp_rtable_get_comm_entries(TRP_RTABLE *rtbl, TR_NAME *comm, size_t *n_out); TR_NAME **trp_rtable_get_comm_realms(TRP_RTABLE *rtbl, TR_NAME *comm, size_t *n_out); @@ -76,37 +63,9 @@ TR_NAME **trp_rtable_get_comm_realm_peers(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NA TRP_ROUTE *trp_rtable_get_entry(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer); TRP_ROUTE *trp_rtable_get_selected_entry(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm); void trp_rtable_clear_triggered(TRP_RTABLE *rtbl); -char *trp_rtable_to_str(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, const char *sep, const char *lineterm); -TRP_ROUTE *trp_route_new(TALLOC_CTX *mem_ctx); -void trp_route_free(TRP_ROUTE *entry); -void trp_route_set_comm(TRP_ROUTE *entry, TR_NAME *comm); -TR_NAME *trp_route_get_comm(TRP_ROUTE *entry); -TR_NAME *trp_route_dup_comm(TRP_ROUTE *entry); -void trp_route_set_realm(TRP_ROUTE *entry, TR_NAME *realm); -TR_NAME *trp_route_get_realm(TRP_ROUTE *entry); -TR_NAME *trp_route_dup_realm(TRP_ROUTE *entry); -void trp_route_set_trust_router(TRP_ROUTE *entry, TR_NAME *tr); -TR_NAME *trp_route_get_trust_router(TRP_ROUTE *entry); -TR_NAME *trp_route_dup_trust_router(TRP_ROUTE *entry); -void trp_route_set_peer(TRP_ROUTE *entry, TR_NAME *peer); -TR_NAME *trp_route_get_peer(TRP_ROUTE *entry); -TR_NAME *trp_route_dup_peer(TRP_ROUTE *entry); -void trp_route_set_metric(TRP_ROUTE *entry, unsigned int metric); -unsigned int trp_route_get_metric(TRP_ROUTE *entry); -void trp_route_set_next_hop(TRP_ROUTE *entry, TR_NAME *next_hop); -TR_NAME *trp_route_get_next_hop(TRP_ROUTE *entry); -TR_NAME *trp_route_dup_next_hop(TRP_ROUTE *entry); -void trp_route_set_selected(TRP_ROUTE *entry, int sel); -int trp_route_is_selected(TRP_ROUTE *entry); -void trp_route_set_interval(TRP_ROUTE *entry, int interval); -int trp_route_get_interval(TRP_ROUTE *entry); -void trp_route_set_expiry(TRP_ROUTE *entry, struct timespec *exp); -struct timespec *trp_route_get_expiry(TRP_ROUTE *entry); -void trp_route_set_local(TRP_ROUTE *entry, int local); -int trp_route_is_local(TRP_ROUTE *entry); -void trp_route_set_triggered(TRP_ROUTE *entry, int trig); -int trp_route_is_triggered(TRP_ROUTE *entry); -char *trp_route_to_str(TALLOC_CTX *mem_ctx, TRP_ROUTE *entry, const char *sep); +/* trp_rtable_encoders.c */ +char *trp_rtable_to_str(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, const char *sep, const char *lineterm); +json_t *trp_rtable_to_json(TRP_RTABLE *rtbl); #endif /* _TRP_RTABLE_H_ */ diff --git a/include/trust_router/tid.h b/include/trust_router/tid.h index 42b97f3..38833f1 100644 --- a/include/trust_router/tid.h +++ b/include/trust_router/tid.h @@ -44,6 +44,7 @@ #include #include +#include #define TID_PORT 12309 @@ -94,7 +95,7 @@ void tid_req_set_realm(TID_REQ *req, TR_NAME *realm); TR_EXPORT TR_NAME *tid_req_get_comm(TID_REQ *req); void tid_req_set_comm(TID_REQ *req, TR_NAME *comm); TR_EXPORT TR_NAME *tid_req_get_orig_coi(TID_REQ *req); -void tid_req_set_rp_orig_coi(TID_REQ *req, TR_NAME *orig_coi); +void tid_req_set_orig_coi(TID_REQ *req, TR_NAME *orig_coi); TR_EXPORT TR_NAME *tid_req_get_request_id(TID_REQ *req); void tid_req_set_request_id(TID_REQ *req, TR_NAME *request_id); TR_EXPORT TIDC_RESP_FUNC *tid_req_get_resp_func(TID_REQ *req); @@ -140,9 +141,9 @@ TR_EXPORT const TID_PATH *tid_srvr_get_path(const TID_SRVR_BLK *); TR_EXPORT int tid_srvr_get_key_expiration(const TID_SRVR_BLK *, struct timeval *tv_out); #define tid_resp_servers_foreach(RESP, SERVER, INDEX) \ - for (INDEX=0,SERVER=NULL; \ - ((INDEX < tid_resp_get_num_servers(RESP))&&(SERVER = tid_resp_get_server(resp, INDEX))); \ - INDEX++) + for ((INDEX)=0,(SERVER)=NULL; \ + (((INDEX) < tid_resp_get_num_servers(RESP))&&((SERVER) = tid_resp_get_server(resp, (INDEX)))); \ + (INDEX)++) /* TID Client functions, in tid/tidc.c */ @@ -155,13 +156,14 @@ TR_EXPORT DH *tidc_set_dh(TIDC_INSTANCE *, DH *); TR_EXPORT void tidc_destroy(TIDC_INSTANCE *tidc); /* TID Server functions, in tid/tids.c */ +TIDS_INSTANCE *tids_new(TALLOC_CTX *mem_ctx); TR_EXPORT TIDS_INSTANCE *tids_create (void); TR_EXPORT int tids_start (TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler, tids_auth_func *auth_handler, const char *hostname, unsigned int port, void *cookie); -TR_EXPORT int tids_get_listener (TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler, - tids_auth_func *auth_handler, const char *hostname, - unsigned int port, void *cookie, int *fd_out, size_t max_fd); +TR_EXPORT nfds_t tids_get_listener(TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler, + tids_auth_func *auth_handler, const char *hostname, + unsigned int port, void *cookie, int *fd_out, size_t max_fd); TR_EXPORT int tids_accept(TIDS_INSTANCE *tids, int listen); TR_EXPORT int tids_send_response (TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp); TR_EXPORT int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_msg); diff --git a/mon/mon_common.c b/mon/mon_common.c new file mode 100644 index 0000000..a237b5d --- /dev/null +++ b/mon/mon_common.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include +#include + +#include + +// Monitoring common code + +/** + * This method defines the command strings + */ +const char *mon_cmd_to_string(MON_CMD cmd) +{ + switch(cmd) { + case MON_CMD_UNKNOWN: + return NULL; + + case MON_CMD_RECONFIGURE: + return "reconfigure"; + + case MON_CMD_SHOW: + return "show"; + } + return NULL; +} + +// Helper macro for the mon_cmd_from_string method +#define return_if_matches(s, cmd) \ + do { \ + if (strcmp((s), mon_cmd_to_string(cmd))==0) \ + return (cmd); \ + } while(0) + +MON_CMD mon_cmd_from_string(const char *s) +{ + return_if_matches(s, MON_CMD_RECONFIGURE); + return_if_matches(s, MON_CMD_SHOW); + return MON_CMD_UNKNOWN; +} +#undef return_if_matches + +/** + * This method defines the option type strings + */ +const char *mon_opt_type_to_string(MON_OPT_TYPE opt_type) +{ + switch(opt_type) { + case OPT_TYPE_UNKNOWN: + case OPT_TYPE_ANY: + return NULL; + + case OPT_TYPE_SHOW_VERSION: + return "version"; + + case OPT_TYPE_SHOW_CONFIG_FILES: + return "config_files"; + + case OPT_TYPE_SHOW_UPTIME: + return "uptime"; + + case OPT_TYPE_SHOW_TID_REQ_COUNT: + return "tid_req_count"; + + case OPT_TYPE_SHOW_TID_REQ_ERR_COUNT: + return "tid_req_error_count"; + + case OPT_TYPE_SHOW_TID_REQ_PENDING: + return "tid_req_pending"; + + case OPT_TYPE_SHOW_ROUTES: + return "routes"; + + case OPT_TYPE_SHOW_PEERS: + return "peers"; + + case OPT_TYPE_SHOW_COMMUNITIES: + return "communities"; + + case OPT_TYPE_SHOW_REALMS: + return "realms"; + + case OPT_TYPE_SHOW_RP_CLIENTS: + return "rp_clients"; + } + return NULL; +} + +// Helper macro for the mon_opt_type_from_string method +#define return_if_matches(s, cmd) \ + do { \ + if (strcmp((s), mon_opt_type_to_string(cmd))==0) \ + return (cmd); \ + } while(0) + +MON_OPT_TYPE mon_opt_type_from_string(const char *s) +{ + return_if_matches(s, OPT_TYPE_SHOW_VERSION); + return_if_matches(s, OPT_TYPE_SHOW_CONFIG_FILES); + return_if_matches(s, OPT_TYPE_SHOW_UPTIME); + return_if_matches(s, OPT_TYPE_SHOW_TID_REQ_COUNT); + return_if_matches(s, OPT_TYPE_SHOW_TID_REQ_ERR_COUNT); + return_if_matches(s, OPT_TYPE_SHOW_TID_REQ_PENDING); + return_if_matches(s, OPT_TYPE_SHOW_ROUTES); + return_if_matches(s, OPT_TYPE_SHOW_PEERS); + return_if_matches(s, OPT_TYPE_SHOW_COMMUNITIES); + return_if_matches(s, OPT_TYPE_SHOW_REALMS); + return_if_matches(s, OPT_TYPE_SHOW_RP_CLIENTS); + return OPT_TYPE_UNKNOWN; +} +#undef return_if_matches diff --git a/mon/mon_req.c b/mon/mon_req.c new file mode 100644 index 0000000..bffa92e --- /dev/null +++ b/mon/mon_req.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include + +#include + +// Monitoring request message common code + +/** + * Destructor used by talloc to ensure proper cleanup + */ +static int mon_req_destructor(void *object) +{ + MON_REQ *req = talloc_get_type_abort(object, MON_REQ); + if (req->options) { + g_array_unref(req->options); + } + return 0; +} + +/** + * Allocate a new monitoring request + * + * @param mem_ctx talloc context for the new request + * @param cmd command for the request + * @return newly allocated request, or null on error + */ +MON_REQ *mon_req_new(TALLOC_CTX *mem_ctx, MON_CMD cmd) +{ + MON_REQ *req=talloc(mem_ctx, MON_REQ); + if (req) { + req->command = cmd; + req->options = g_array_new(FALSE, FALSE, sizeof(MON_OPT)); + talloc_set_destructor((void *)req, mon_req_destructor); + } + return req; +} + +/** + * Free a monitoring request + * + * @param req request to free, must not be null + */ +void mon_req_free(MON_REQ *req) +{ + talloc_free(req); +} + +/** + * Add an option to a MON_REQ + * @param req request to operate on, not null + * @param opt_type type of option + * @return MON_SUCCESS on success, error code on error + */ +MON_RC mon_req_add_option(MON_REQ *req, MON_OPT_TYPE opt_type) +{ + MON_OPT new_opt; // not a pointer + + /* Validate parameters */ + if ((req == NULL) || (opt_type == OPT_TYPE_UNKNOWN)) { + return MON_BADARG; + } + + new_opt.type = opt_type; + + /* Add the new option to the list */ + g_array_append_val(req->options, new_opt); + return MON_SUCCESS; +} + +size_t mon_req_opt_count(MON_REQ *req) +{ + return req->options->len; +} + +MON_OPT *mon_req_opt_index(MON_REQ *req, size_t index) +{ + MON_OPT *result = &g_array_index(req->options, MON_OPT, index); + return result; +} + diff --git a/mon/mon_req_decode.c b/mon/mon_req_decode.c new file mode 100644 index 0000000..21bb64b --- /dev/null +++ b/mon/mon_req_decode.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include + +#include + +// Monitoring request decoders + +/** + * Decode a single option + * + * Format: + * { "type": "some_tpye" } + * + * @param opt_json JSON object reference + * @param dest allocated memory for the result + * @return MON_SUCCESS on success, error on error + */ +static MON_RC mon_decode_one_opt(json_t *opt_json, MON_OPT *dest) +{ + json_t *jstr = NULL; + MON_OPT_TYPE opt_type = OPT_TYPE_UNKNOWN; + + if ( (opt_json == NULL) || (dest == NULL)) + return MON_BADARG; + + if (! json_is_object(opt_json)) + return MON_NOPARSE; + + jstr = json_object_get(opt_json, "type"); + if ( (jstr == NULL) || (! json_is_string(jstr)) ) + return MON_NOPARSE; + + opt_type = mon_opt_type_from_string(json_string_value(jstr)); + if (opt_type == OPT_TYPE_UNKNOWN) + return MON_NOPARSE; + + dest->type = opt_type; + return MON_SUCCESS; +} + +/** + * Decode options array + * + * Format: + * [{option}, {option}, ...] + * + */ +static MON_RC mon_options_decode(json_t *opts_json, MON_REQ *req) +{ + MON_OPT opt; // not a pointer + size_t n_opts=0; + size_t ii=0; + + if ( (opts_json == NULL) || (req == NULL)) + return MON_BADARG; + + if (! json_is_array(opts_json)) + return MON_NOPARSE; + + n_opts = json_array_size(opts_json); + for (ii=0; ii < n_opts; ii++) { + if (mon_decode_one_opt(json_array_get(opts_json, ii), + &opt) != MON_SUCCESS) { + return MON_NOPARSE; + } + mon_req_add_option(req, opt.type); + } + return MON_SUCCESS; +} + +/** + * Parse a JSON string into a request + */ +MON_REQ *mon_req_parse(TALLOC_CTX *mem_ctx, const char *input) +{ + json_t *parsed_json = NULL; + json_error_t json_error; + + parsed_json = json_loads(input, JSON_REJECT_DUPLICATES, &json_error); + return mon_req_decode(mem_ctx, parsed_json); +} + +/** + * Decode a JSON request + * + * Expected format: + * { + * "command": "some_command_name", + * "options": [{option1}, ...] + * } + * + * (options are optional) + * + * Caller must free the return value with mon_req_free(). + * + * @param mem_ctx talloc context for the returned struct + * @param req_json reference to JSON request object + * @return decoded request struct or NULL on failure + */ +MON_REQ *mon_req_decode(TALLOC_CTX *mem_ctx, json_t *req_json) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + MON_REQ *req = NULL; + json_t *jval = NULL; + json_t *opts_json = NULL; + MON_CMD cmd = MON_CMD_UNKNOWN; + + if (! json_is_object(req_json)) + goto cleanup; + + // Get the command and verify that it is a string value + jval = json_object_get(req_json, "command"); + if (! json_is_string(jval)) + goto cleanup; + + cmd = mon_cmd_from_string(json_string_value(jval)); + if (cmd == MON_CMD_UNKNOWN) + goto cleanup; + + /* Command is good. Allocate the request in the tmp context */ + req = mon_req_new(tmp_ctx, cmd); + if (req == NULL) + goto cleanup; + + /* Parse options if we have any */ + opts_json = json_object_get(req_json, "options"); + if (opts_json) { + if (mon_options_decode(opts_json, req) != MON_SUCCESS) { + req = NULL; // memory still in tmp_ctx, so it will be cleaned up + goto cleanup; + } + } + + /* Success! Put the request in the caller's talloc context */ + talloc_steal(mem_ctx, req); + +cleanup: + talloc_free(tmp_ctx); + + return req; +} diff --git a/mon/mon_req_encode.c b/mon/mon_req_encode.c new file mode 100644 index 0000000..5bcf8b2 --- /dev/null +++ b/mon/mon_req_encode.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include +#include + +#include + +// Monitoring request encoders + +/** + * Encode options array as a JSON array + * + * Format: + * [ + * { "type": "first_type" }, + * { "type": "second_type"}, + * ... + * ] + * + * @param opts array of options + * @return reference to a JSON array of options + */ +static json_t *mon_opts_decode(GArray *opts) +{ + json_t *array_json = json_array(); // the array of options + json_t *opt_json = NULL; // individual option JSON object + json_t *type_json = NULL; + guint ii = 0; + MON_OPT this_opt; + + if (array_json == NULL) + return NULL; // failed + + /* Iterate over the options */ + for (ii=0; ii < opts->len; ii++) { + this_opt = g_array_index(opts, MON_OPT, ii); + + /* Create the JSON object for this option */ + opt_json = json_object(); + if (opt_json == NULL) { + json_decref(array_json); + return NULL; + } + + /* Add to the array, making opt_json a borrowed ref if we succeed */ + if (json_array_append_new(array_json, opt_json) == -1) { + json_decref(array_json); + json_decref(opt_json); // handle ourselves because the set failed + } + + /* Create the type string for this option */ + type_json = json_string(mon_opt_type_to_string(this_opt.type)); + if (type_json == NULL) { + json_decref(array_json); + return NULL; + } + + /* Add the type string to the JSON object, making type_json a borrowed ref */ + if (json_object_set_new(opt_json, "type", type_json) == -1) { + json_decref(array_json); + json_decref(type_json); // must handle ourselves because the set failed + return NULL; + } + } + + return array_json; +} + +/** + * Encode a request as a JSON object + * + * Caller must free the return value using json_decref() + * + * Format: + * { + * "command": "some_command", + * "options": [...see mon_opts_to_json()...] + * } + * + * @param req request to encode + * @return reference to a JSON object + */ +json_t *mon_req_encode(MON_REQ *req) +{ + json_t *req_json = NULL; + json_t *cmd_json = NULL; + json_t *opts_json = NULL; + + /* Allocate the base JSON object */ + req_json = json_object(); + if (req_json == NULL) + return NULL; + + /* Allocate the JSON string for the command */ + cmd_json = json_string(mon_cmd_to_string(req->command)); + if (cmd_json == NULL) { + json_decref(req_json); + return NULL; + } + + /* Add the command string to the base object. Steals the reference to + * the string if successful. */ + if (json_object_set_new(req_json, "command", cmd_json) == -1) { + json_decref(cmd_json); // must clean this up ourselves because the set failed + json_decref(req_json); + return NULL; + } + + /* If we have options, add them to the object */ + if (req->options->len > 0) { + opts_json = mon_opts_decode(req->options); + if (opts_json == NULL) { + json_decref(req_json); + return NULL; + } + + if (json_object_set_new(req_json, "options", opts_json) == -1) { + json_decref(req_json); + json_decref(opts_json); // must clean this up ourselves because set failed + return NULL; + } + } + + /* That's it, we succeeded */ + return req_json; +} + diff --git a/mon/mon_resp.c b/mon/mon_resp.c new file mode 100644 index 0000000..f0729ad --- /dev/null +++ b/mon/mon_resp.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include + +#include + +// Monitoring request message common code + +/** + * Destructor used by talloc to ensure proper cleanup + */ +static int mon_resp_destructor(void *object) +{ + MON_RESP *resp = talloc_get_type_abort(object, MON_RESP); + /* free the message */ + if (resp->message) { + tr_free_name(resp->message); + } + /* free the payload */ + if (resp->payload) { + json_decref(resp->payload); + } + return 0; +} + +/** + * Allocate a new monitoring response + * + * Caller must free using mon_resp_free(). + * + * Makes its own copy of the message, so caller can dispose of + * that after allocating the response. + * + * Increments the reference count of the payload if it is not null. + * + * @param mem_ctx talloc context for allocation + * @param req MON_REQ this response corresponds to + * @param code numeric response code + * @param msg string description of response code + * @param payload JSON object to be send as payload, or null for no payload + * @return response allocated in the requested talloc context, null on failure + */ +MON_RESP *mon_resp_new(TALLOC_CTX *mem_ctx, MON_RESP_CODE code, const char *msg, json_t *payload) +{ + MON_RESP *resp = talloc(mem_ctx, MON_RESP); + if (resp) { + resp->code = code; + resp->message = tr_new_name(msg); + + resp->payload = payload; + if (resp->payload) + json_incref(resp->payload); + + talloc_set_destructor((void *)resp, mon_resp_destructor); + if (resp->message == NULL) { + talloc_free(resp); // destructor will be called + resp = NULL; + } + } + return resp; +} + +/** + * Set or replace the response message + * + * Does not change the message if it fails + * + * @param resp + * @param new_msg + * @return 1 on success, 0 on error + */ +int mon_resp_set_message(MON_RESP *resp, const char *new_msg) +{ + TR_NAME *n = tr_new_name(new_msg); + + if (n == NULL) + return 0; /* failed */ + + if (resp->message) + tr_free_name(resp->message); + resp->message = n; + return 1; /* succeeded */ +} + +/** + * Set or replace the payload + * + * Manages JSON reference counts + * + * @param resp + * @param new_payload + */ +void mon_resp_set_payload(MON_RESP *resp, json_t *new_payload) +{ + if (resp->payload) + json_decref(resp->payload); + resp->payload = new_payload; + if (resp->payload) + json_incref(new_payload); +} + +/** + * Free a monitoring response + * + * @param resp request to free, must not be null + */ +void mon_resp_free(MON_RESP *resp) +{ + talloc_free(resp); +} diff --git a/mon/mon_resp_decode.c b/mon/mon_resp_decode.c new file mode 100644 index 0000000..83b0ac3 --- /dev/null +++ b/mon/mon_resp_decode.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include + +#include + +// Monitoring response decoder + +/** + * Decode a JSON response + * + * Expected format: + * { + * "code": 0, + * "message": "success", + * "payload": { + * "serial": 12345, + * ... + * } + * } + * + * Caller must free the return value with MON_REQ_free(). + * + * @param mem_ctx talloc context for the returned struct + * @param resp_json reference to JSON request object + * @return decoded request struct or NULL on failure + */ +MON_RESP *mon_resp_decode(TALLOC_CTX *mem_ctx, json_t *resp_json) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + MON_RESP *resp = NULL; + json_t *jcode = NULL; + json_t *jmessage = NULL; + json_t *jpayload = NULL; + + if (! json_is_object(resp_json)) + goto cleanup; + + /* Get the response code, which is an integer */ + jcode = json_object_get(resp_json, "code"); + if (! json_is_integer(jcode)) + goto cleanup; + + /* Get the response message, which is a string */ + jmessage = json_object_get(resp_json, "message"); + if (! json_is_string(jmessage)) + goto cleanup; + + /* Get the payload if we have one */ + jpayload = json_object_get(resp_json, "payload"); + + /* Get a response in the tmp_ctx context. The payload may be null. */ + resp = mon_resp_new(tmp_ctx, + (MON_RESP_CODE) json_integer_value(jcode), + json_string_value(jmessage), + jpayload); + if (resp == NULL) + goto cleanup; + + /* Success! Put the request in the caller's talloc context */ + talloc_steal(mem_ctx, resp); + +cleanup: + talloc_free(tmp_ctx); + if (resp_json) + json_decref(resp_json); + + return resp; +} diff --git a/mon/mon_resp_encode.c b/mon/mon_resp_encode.c new file mode 100644 index 0000000..7e41c65 --- /dev/null +++ b/mon/mon_resp_encode.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include + +/* Helper for encoding. Adds a newly allocated JSON object to + * jobj. If the allocation or setting fails, returns NULL after + * cleaning up. */ +#define object_set_or_free_and_return(jobj, tmp_jval, key, jval) \ + do { \ + (tmp_jval) = (jval); \ + if ( (tmp_jval) == NULL) { \ + json_decref(jobj); \ + return NULL; \ + } \ + if (json_object_set_new((jobj), (key), (tmp_jval)) == -1) { \ + json_decref(tmp_jval); \ + json_decref(jobj); \ + return NULL; \ + } \ + } while(0) + +/** + * Encode a monitoring response as a JSON object + * + * Caller must ensure json_decref() is used to free the return value. + * + * @param resp response to encode + * @return response as a newly allocated JSON object + */ +json_t *mon_resp_encode(MON_RESP *resp) +{ + json_t *resp_json = NULL; + json_t *jval = NULL; + + /* Get a JSON object */ + resp_json = json_object(); + if (resp_json == NULL) + return NULL; + + /* Add properties, cleaning up and returning NULL on failure */ + object_set_or_free_and_return(resp_json, jval, "code", json_integer(resp->code)); + object_set_or_free_and_return(resp_json, jval, "message", tr_name_to_json_string(resp->message)); + + /* If we have a payload, add it */ + if (resp->payload) { + object_set_or_free_and_return(resp_json, jval, "payload", resp->payload); + json_incref(resp->payload); /* we just created a second reference to the payload */ + } + + return resp_json; +} diff --git a/mon/monc.c b/mon/monc.c new file mode 100644 index 0000000..426b447 --- /dev/null +++ b/mon/monc.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2012, 2014-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + + +MONC_INSTANCE *monc_new(TALLOC_CTX *mem_ctx) +{ + MONC_INSTANCE *monc=talloc(mem_ctx, MONC_INSTANCE); + if (monc!=NULL) { + monc->gssc = tr_gssc_instance_new(monc); + if (monc->gssc == NULL) { + talloc_free(monc); + return NULL; + } + + monc->gssc->service_name = "trustmonitor"; + } + return monc; +} + +void monc_free(MONC_INSTANCE *monc) +{ + talloc_free(monc); +} + +int monc_open_connection(MONC_INSTANCE *monc, + const char *server, + unsigned int port) +{ + return tr_gssc_open_connection(monc->gssc, server, port); +} + +MON_RESP *monc_send_request(TALLOC_CTX *mem_ctx, MONC_INSTANCE *monc, MON_REQ *req) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + TR_MSG *msg = NULL; + TR_MSG *resp_msg = NULL; + MON_RESP *resp = NULL; + + /* Create and populate a msg structure */ + if (!(msg = talloc_zero(tmp_ctx, TR_MSG))) + goto cleanup; + + msg->msg_type = MON_REQUEST; + tr_msg_set_mon_req(msg, req); + + resp_msg = tr_gssc_exchange_msgs(tmp_ctx, monc->gssc, msg); + if (resp_msg == NULL) + goto cleanup; + + resp = tr_msg_get_mon_resp(resp_msg); + + /* if we got a response, steal it from resp_msg's context so we can return it */ + if (resp) + talloc_steal(mem_ctx, resp); + +cleanup: + talloc_free(tmp_ctx); + return resp; +} + +DH *monc_get_dh(MONC_INSTANCE *inst) +{ + return tr_gssc_get_dh(inst->gssc); +} + +DH *monc_set_dh(MONC_INSTANCE *inst, DH *dh) +{ + return tr_gssc_set_dh(inst->gssc, dh); +} diff --git a/mon/mons.c b/mon/mons.c new file mode 100644 index 0000000..f2e69c5 --- /dev/null +++ b/mon/mons.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "mons_handlers.h" + +static void mons_sweep_procs(MONS_INSTANCE *mons); + +static int mons_destructor(void *object) +{ + MONS_INSTANCE *mons = talloc_get_type_abort(object, MONS_INSTANCE); + if (mons->handlers) + g_ptr_array_unref(mons->handlers); + + if (mons->pids) + g_array_unref(mons->pids); + + return 0; +} + +/** + * Allocate a new MONS_INSTANCE + * + * @param mem_ctx talloc context for allocation + * @return new MONS_INSTANCE or null on failure + */ +MONS_INSTANCE *mons_new(TALLOC_CTX *mem_ctx) +{ + MONS_INSTANCE *mons = talloc(mem_ctx, MONS_INSTANCE); + + if (mons) { + mons->hostname = NULL; + mons->port = 0; + mons->tids = NULL; + mons->trps = NULL; + mons->req_handler = NULL; + mons->auth_handler = NULL; + mons->cookie = NULL; + + /* Before any steps that may fail, install the destructor */ + talloc_set_destructor((void *)mons, mons_destructor); + + mons->authorized_gss_names = tr_gss_names_new(mons); + if (mons->authorized_gss_names == NULL) { + talloc_free(mons); + return NULL; + } + + mons->handlers = g_ptr_array_new(); + if (mons->handlers == NULL) { + talloc_free(mons); + return NULL; + } + + mons->pids = g_array_new(FALSE, FALSE, sizeof(pid_t)); + if (mons->pids == NULL) { + talloc_free(mons); + return NULL; + } + } + return mons; +} + +/** + * Callback to process a request and produce a response + * + * @param req_str JSON-encoded request + * @param data pointer to a MONS_INSTANCE + * @return pointer to the response string or null to send no response + */ +static TR_MSG *mons_req_cb(TALLOC_CTX *mem_ctx, TR_MSG *req_msg, void *data) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + MONS_INSTANCE *mons = talloc_get_type_abort(data, MONS_INSTANCE); + MON_REQ *req = NULL; + MON_RESP *resp = NULL; + TR_MSG *resp_msg = NULL; /* This is the response value */ + + /* Validate inputs */ + if (req_msg == NULL) + goto cleanup; + + req = tr_msg_get_mon_req(req_msg); + if (req == NULL) { + /* this is an internal error */ + tr_err("mons_req_cb: Received incorrect message type (was %d, expected %d)", + tr_msg_get_msg_type(req_msg), + MON_REQUEST); + /* TODO send an error response */ + goto cleanup; + } + + /* Allocate a response message */ + resp_msg = talloc(tmp_ctx, TR_MSG); + if (resp_msg == NULL) { + /* can't return a message, just emit an error */ + tr_crit("mons_req_cb: Error allocating response message."); + goto cleanup; + } + + /* Handle the request */ + resp = mons_handle_request(resp_msg, mons, req); + if (resp == NULL) { + /* error processing the request */ + /* TODO send back an error */ + goto cleanup; + } + + /* Set the response message payload */ + tr_msg_set_mon_resp(resp_msg, resp); + + /* Put the response message in the caller's context so it does not get freed when we exit */ + talloc_steal(mem_ctx, resp_msg); + +cleanup: + talloc_free(tmp_ctx); + return resp_msg; +} + +/** + * Create a listener for monitoring requests + * + * Accept connections with mons_accept() + * + * @param mons monitoring server instance + * @param req_handler + * @param auth_handler + * @param hostname + * @param port + * @param cookie + * @param fd_out + * @param max_fd + * @return + */ +int mons_get_listener(MONS_INSTANCE *mons, MONS_REQ_FUNC *req_handler, MONS_AUTH_FUNC *auth_handler, const char *hostname, + unsigned int port, void *cookie, int *fd_out, size_t max_fd) +{ + size_t n_fd=0; + size_t ii=0; + + mons->port = port; + n_fd = tr_sock_listen_all(port, fd_out, max_fd); + if (n_fd<=0) + tr_err("mons_get_listener: Error opening port %d"); + else { + /* opening port succeeded */ + tr_info("mons_get_listener: Opened port %d.", port); + + /* make this socket non-blocking */ + for (ii=0; ii0) { + /* store the caller's request handler & cookie */ + mons->req_handler = req_handler; + mons->auth_handler = auth_handler; + mons->hostname = hostname; + mons->cookie = cookie; + } + + return (int) n_fd; +} + +/** + * Accept and process a connection on a port opened with mons_get_listener() + * + * @param mons monitoring interface instance + * @param listen FD of the connection socket + * @return 0 on success + */ +int mons_accept(MONS_INSTANCE *mons, int listen) +{ + int conn=-1; + int pid=-1; + + if (0 > (conn = accept(listen, NULL, NULL))) { + perror("Error from monitoring interface accept()"); + return 1; + } + + if (0 > (pid = fork())) { + perror("Error on fork()"); + return 1; + } + + if (pid == 0) { + close(listen); + tr_gss_handle_connection(conn, + "trustmonitor", mons->hostname, /* acceptor name */ + mons->auth_handler, mons->cookie, /* auth callback and cookie */ + mons_req_cb, mons /* req callback and cookie */ + ); + close(conn); + exit(0); /* exit to kill forked child process */ + } + + /* Only the parent process gets here */ + close(conn); + g_array_append_val(mons->pids, pid); + + /* clean up any processes that have completed */ + mons_sweep_procs(mons); + + return 0; +} + +void mons_sweep_procs(MONS_INSTANCE *mons) +{ + guint ii; + pid_t pid; + int status; + + /* loop backwards over the array so we can remove elements as we go */ + for (ii=mons->pids->len; ii > 0; ii--) { + /* ii-1 is the current index */ + pid = g_array_index(mons->pids, pid_t, ii-1); + if (waitpid(pid, &status, WNOHANG) > 0) { + /* the process exited */ + tr_debug("mons_sweep_procs: monitoring process %d terminated.", pid); + + g_array_remove_index_fast(mons->pids, ii-1); /* disturbs only indices >= ii-1 which we've already handled */ + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) == 0) + tr_debug("mons_sweep_procs: monitoring process %d succeeded.", pid); + else + tr_debug("mons_sweep_procs: monitoring process %d exited with status %d.", pid, WTERMSIG(status)); + } else if (WIFSIGNALED(status)) { + tr_debug("mons_sweep_procs: monitoring process %d terminated by signal %d.", pid, WTERMSIG(status)); + } + } + } +} diff --git a/mon/mons_handlers.c b/mon/mons_handlers.c new file mode 100644 index 0000000..5e19db0 --- /dev/null +++ b/mon/mons_handlers.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* Handlers for monitoring requests */ + +#include + +#include +#include +#include + + +/* Static Prototypes */ +static int dispatch_entry_matches(MONS_DISPATCH_TABLE_ENTRY *e, MON_CMD command, MON_OPT_TYPE opt_type); +static MONS_HANDLER_FUNC *mons_find_handler(MONS_INSTANCE *mons, MON_CMD cmd, MON_OPT_TYPE opt_type); +static void request_helper(void *element, void *data); + +struct request_helper_data { + MON_CMD command; + MON_OPT_TYPE opt_type; + json_t *payload; /* json object to add responses to */ + GArray *results; +}; + +struct handler_result { + MON_OPT_TYPE opt_type; /* what opt type set this? */ + MON_RC rc; /* what was its result code? */ + json_t *json_data; /* what data, if any, is it returning? */ +}; + +/** + * Call the appropriate handler for a request + * + * TODO: report errors from handlers + * + * @return a MON_RESP structure or null if there was a processing error + */ +MON_RESP *mons_handle_request(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ *req) +{ + MON_RESP *resp = NULL; + json_t *payload = NULL; + struct request_helper_data cookie = {0}; + size_t ii = 0; + + tr_debug("mons_handle_request: Handling a request"); + + /* Start off by allocating our response with a generic error message */ + resp = mon_resp_new(mem_ctx, + MON_RESP_ERROR, + "error processing request", + NULL); + if (resp == NULL) { + /* we can't respond, just return */ + tr_crit("mons_handle_request: Error allocating response structure."); + goto cleanup; + } + + /* Now get a JSON object for our return payload */ + payload = json_object(); + if (payload == NULL) { + tr_crit("mons_handle_request: Error allocating response payload."); + goto cleanup; /* This will return the generic error message set earlier */ + } + + /* Now call handlers */ + cookie.command = req->command; + cookie.results = g_array_new(FALSE, TRUE, sizeof(struct handler_result)); + + if (mon_req_opt_count(req) == 0) { + /* call every handler that matches the command */ + cookie.opt_type = OPT_TYPE_ANY; + g_ptr_array_foreach(mons->handlers, request_helper, &cookie); + } else { + /* call only those handlers that match an option */ + for (ii=0; ii < mon_req_opt_count(req); ii++) { + cookie.opt_type = mon_req_opt_index(req, ii)->type; + /* Loop over all handlers - we know we can only have one match for each opt type */ + g_ptr_array_foreach(mons->handlers, request_helper, &cookie); + } + } + + /* We now have an array of results in cookie.results. If any of these failed, return an error. */ + tr_debug("mons_handle_request: Examining %d handler results", cookie.results->len); + resp->code = MON_RESP_SUCCESS; /* tentatively set this to success */ + for (ii=0; ii < cookie.results->len; ii++) { + struct handler_result *this = &g_array_index(cookie.results, struct handler_result, ii); + if (this->rc != MON_SUCCESS) { + tr_debug("mons_handle_request: Result %d was an error.", ii); + resp->code = MON_RESP_ERROR; + } + + /* add the JSON response even if there was an error */ + if (this->json_data) { + tr_debug("mons_handle_request: Result %d returned JSON data.", ii); + json_object_set_new(payload, mon_opt_type_to_string(this->opt_type), this->json_data); + } + } + + if (resp->code == MON_RESP_SUCCESS) { + if (mon_resp_set_message(resp, "success") == 0) { + /* Failed to set the response message to success - fail ironically, don't send + * an inconsistent response. */ + tr_crit("mons_handle_request: Error setting response message to 'success'."); + goto cleanup; + } + } else { + /* Failed - send a response indicating that the overall command succeeded */ + if (mon_resp_set_message(resp, "request processed but an error occurred") == 0) { + tr_crit("mons_handle_request: Error setting response message after a handler error."); + goto cleanup; + } + } + + /* Attach the accumulated payload to the response */ + if (json_object_size(payload) > 0) { + tr_debug("mons_handle_request: Attaching payload to response."); + mon_resp_set_payload(resp, payload); + } + + tr_debug("mons_handle_request: Successfully processed request."); + +cleanup: + if (payload) + json_decref(payload); + if (cookie.results) + g_array_free(cookie.results, TRUE); + return resp; +} + +/** + * Register a handler for a command/option combination + * + * @param mons + * @param cmd + * @param opt_type + * @param f + * @param cookie + * @return + */ +MON_RC mons_register_handler(MONS_INSTANCE *mons, + MON_CMD cmd, + MON_OPT_TYPE opt_type, + MONS_HANDLER_FUNC *f, + void *cookie) +{ + MONS_DISPATCH_TABLE_ENTRY *entry = NULL; + + if (mons_find_handler(mons, cmd, opt_type) != NULL) { + return MON_ERROR; + } + + /* Put these in the mons talloc context so we don't have to muck about with + * a free function for the GPtrArray */ + entry = talloc(mons, MONS_DISPATCH_TABLE_ENTRY); + if (entry == NULL) { + return MON_NOMEM; + } + entry->command = cmd; + entry->opt_type = opt_type; + entry->handler = f; + entry->cookie = cookie; + + g_ptr_array_add(mons->handlers, entry); + return MON_SUCCESS; +} + +/** + * Two table entries match if none of the commands or opt_types are unknown, + * if the commands match, and if the opt types either match or at least one is + * OPT_TYPE_ANY. + * + * No comparison of the handler pointer is included. + * + * @return 1 if the two match, 0 if not + */ +static int dispatch_entry_matches(MONS_DISPATCH_TABLE_ENTRY *e, + MON_CMD command, + MON_OPT_TYPE opt_type) +{ + if ((command == MON_CMD_UNKNOWN) || (opt_type == OPT_TYPE_UNKNOWN)) + return 0; /* request is invalid */ + + if ((e->command == MON_CMD_UNKNOWN) || (e->opt_type == OPT_TYPE_UNKNOWN)) + return 0; /* e1 is invalid */ + + if (e->command != command) + return 0; /* commands do not match */ + + if (e->opt_type == opt_type) + return 1; /* exact match */ + + if ( (e->opt_type == OPT_TYPE_ANY) || (opt_type == OPT_TYPE_ANY) ) + return 1; /* one is a wildcard */ + + return 0; /* commands matched but opt_types did not */ +} + +static MONS_HANDLER_FUNC *mons_find_handler(MONS_INSTANCE *mons, MON_CMD cmd, MON_OPT_TYPE opt_type) +{ + guint index; + + for (index=0; index < mons->handlers->len; index++) { + if (dispatch_entry_matches(g_ptr_array_index(mons->handlers, index), cmd, opt_type)) + return g_ptr_array_index(mons->handlers, index); + } + return NULL; +} + +/** + * This calls every request handler that matches a command/opt_type, + * gathering their results. + * + * @param element + * @param data + */ +static void request_helper(void *element, void *data) +{ + MONS_DISPATCH_TABLE_ENTRY *entry = talloc_get_type_abort(element, MONS_DISPATCH_TABLE_ENTRY); + struct request_helper_data *helper_data = data; + struct handler_result result = {0}; + + if (dispatch_entry_matches(entry, helper_data->command, helper_data->opt_type)) { + result.rc = entry->handler(entry->cookie, &(result.json_data)); + result.opt_type = entry->opt_type; + g_array_append_val(helper_data->results, result); + } +} + diff --git a/mon/tests/req_reconfigure.test b/mon/tests/req_reconfigure.test new file mode 100644 index 0000000..75f1690 --- /dev/null +++ b/mon/tests/req_reconfigure.test @@ -0,0 +1 @@ +{"command": "reconfigure"} diff --git a/mon/tests/req_show_all_options.test b/mon/tests/req_show_all_options.test new file mode 100644 index 0000000..799be44 --- /dev/null +++ b/mon/tests/req_show_all_options.test @@ -0,0 +1 @@ +{"command": "show", "options": [{"type": "serial"}, {"type": "version"}, {"type": "uptime"}, {"type": "tid_req_count"}, {"type": "tid_req_pending"}, {"type": "routes"}, {"type": "communities"}]} diff --git a/mon/tests/req_show_no_options.test b/mon/tests/req_show_no_options.test new file mode 100644 index 0000000..af1cdce --- /dev/null +++ b/mon/tests/req_show_no_options.test @@ -0,0 +1 @@ +{"command": "show"} diff --git a/mon/tests/resp_reconfigure_error.test b/mon/tests/resp_reconfigure_error.test new file mode 100644 index 0000000..3344bb1 --- /dev/null +++ b/mon/tests/resp_reconfigure_error.test @@ -0,0 +1 @@ +{"code": 1, "message": "error"} diff --git a/mon/tests/resp_reconfigure_success.test b/mon/tests/resp_reconfigure_success.test new file mode 100644 index 0000000..392c2e0 --- /dev/null +++ b/mon/tests/resp_reconfigure_success.test @@ -0,0 +1 @@ +{"code": 0, "message": "success"} diff --git a/mon/tests/resp_show_success.test b/mon/tests/resp_show_success.test new file mode 100644 index 0000000..bfbec19 --- /dev/null +++ b/mon/tests/resp_show_success.test @@ -0,0 +1 @@ +{"code": 0, "message": "success", "show": {"version": "1.2.3-4", "serial": 86400, "tid_req_pending": 13, "tid_req_count": 1432}} diff --git a/mon/tests/test_mon_req_decode.c b/mon/tests/test_mon_req_decode.c new file mode 100644 index 0000000..19ad64d --- /dev/null +++ b/mon/tests/test_mon_req_decode.c @@ -0,0 +1,137 @@ +// +// Created by jlr on 4/9/18. +// + +#include +#include +#include +#include +#include + +#include + +/** + * @return reconfigure command + */ +static MON_REQ *reconfigure() +{ + MON_REQ *req = mon_req_new(NULL, MON_CMD_RECONFIGURE); + assert(req); + return req; +} + +/** + * @return show command with no options + */ +static MON_REQ *show_plain() +{ + MON_REQ *req = mon_req_new(NULL, MON_CMD_SHOW); + assert(req); + return req; +} + +/** + * @param opts array of option types, terminated with OPT_TYPE_UNKNOWN + * @return show command with the requested options, excluding the terminator + */ +static MON_REQ *show_options(const MON_OPT_TYPE *opts) +{ + MON_REQ *req = mon_req_new(NULL, MON_CMD_SHOW); + assert(req); + + while (*opts != OPT_TYPE_UNKNOWN) { + assert(MON_SUCCESS == mon_req_add_option(req, *opts)); + opts++; + } + return req; +} + +/** + * @return show command with every option + */ +static MON_REQ *show_all_options() +{ + MON_OPT_TYPE opts[] = { + OPT_TYPE_SHOW_CONFIG_FILES, + OPT_TYPE_SHOW_VERSION, + OPT_TYPE_SHOW_UPTIME, + OPT_TYPE_SHOW_TID_REQ_COUNT, + OPT_TYPE_SHOW_TID_REQ_PENDING, + OPT_TYPE_SHOW_ROUTES, + OPT_TYPE_SHOW_COMMUNITIES, + OPT_TYPE_UNKNOWN // terminator + }; + + return show_options(opts); +} + +static char *read_file(const char *filename) +{ + FILE *f = fopen(filename, "r"); + char *s = NULL; + size_t nn = 0; + ssize_t n = getline(&s, &nn, f); + + fclose(f); + + if( (n > 0) && (s[n-1] == '\n')) + s[n-1] = 0; + + return s; +} + +static int equal(MON_REQ *r1, MON_REQ *r2) +{ + size_t ii; + + if (r1->command != r2->command) + return 0; + + if (mon_req_opt_count(r1) != mon_req_opt_count(r2)) + return 0; + + for (ii=0; ii < mon_req_opt_count(r1); ii++) { + if (mon_req_opt_index(r1, ii)->type != mon_req_opt_index(r2, ii)->type) + return 0; + } + + return 1; +} + +static int run_test(const char *filename, MON_REQ *(generator)()) +{ + MON_REQ *req = NULL; + MON_REQ *expected = NULL; + char *req_json_str = NULL; + + expected = generator(); + assert(expected); + + req_json_str = read_file(filename); + assert(req_json_str); + + req = mon_req_parse(NULL, req_json_str); + assert(req); + assert(equal(req, expected)); + + free(req_json_str); + mon_req_free(req); + mon_req_free(expected); + + return 1; +} + +int main(void) +{ + + // Test reconfigure command + assert(run_test("req_reconfigure.test", reconfigure)); + + // Test show command with no options + assert(run_test("req_show_no_options.test", show_plain)); + + // Test show command with all the options + assert(run_test("req_show_all_options.test", show_all_options)); + + return 0; +} \ No newline at end of file diff --git a/mon/tests/test_mon_req_encode.c b/mon/tests/test_mon_req_encode.c new file mode 100644 index 0000000..d3ae49c --- /dev/null +++ b/mon/tests/test_mon_req_encode.c @@ -0,0 +1,126 @@ +// +// Created by jlr on 4/9/18. +// + +#include +#include +#include +#include +#include + +#include + +#define JSON_DUMP_OPTS 0 + +static char *reconfigure() +{ + MON_REQ *req = mon_req_new(NULL, MON_CMD_RECONFIGURE); + json_t *req_json = mon_req_encode(req); + char *result = json_dumps(req_json, JSON_DUMP_OPTS); + assert(req); + assert(req_json); + assert(result); + json_decref(req_json); + mon_req_free(req); + return result; +} + +static char *show_plain() +{ + MON_REQ *req = mon_req_new(NULL, MON_CMD_SHOW); + json_t *req_json = mon_req_encode(req); + char *result = json_dumps(req_json, JSON_DUMP_OPTS); + assert(req); + assert(req_json); + assert(result); + json_decref(req_json); + mon_req_free(req); + return result; +} + +static char *show_options(const MON_OPT_TYPE *opts) +{ + MON_REQ *req = mon_req_new(NULL, MON_CMD_SHOW); + json_t *req_json = NULL; + char *result = NULL; + + assert(req); + + while (*opts != OPT_TYPE_UNKNOWN) { + assert(MON_SUCCESS == mon_req_add_option(req, *opts)); + opts++; + } + + req_json = mon_req_encode(req); + assert(req_json); + + result = json_dumps(req_json, JSON_DUMP_OPTS); + assert(result); + + json_decref(req_json); + mon_req_free(req); + return result; +} + +static char *read_file(const char *filename) +{ + FILE *f = fopen(filename, "r"); + char *s = NULL; + size_t nn = 0; + ssize_t n = getline(&s, &nn, f); + fclose(f); + + if( (n > 0) && (s[n-1] == '\n')) + s[n-1] = 0; + + return s; +} +int main(void) +{ + char *s = NULL; + MON_OPT_TYPE opts[10]; + char *expected = NULL; + + // Test reconfigure command + s = reconfigure(); + expected = read_file("req_reconfigure.test"); + assert(expected); + assert(strcmp(expected, s) == 0); + free(s); + free(expected); + + // Test show without options + s = show_plain(); + expected = read_file("req_show_no_options.test"); + assert(expected); + assert(strcmp(expected, s) == 0); + free(s); + free(expected); + + // Test show with empty options (this mostly tests the test) + opts[0] = OPT_TYPE_UNKNOWN; + s = show_options(opts); + expected = read_file("req_show_no_options.test"); + assert(expected); + assert(strcmp(expected, s) == 0); + free(s); + free(expected); + + // Test show with many options + opts[0] = OPT_TYPE_SHOW_CONFIG_FILES; + opts[1] = OPT_TYPE_SHOW_VERSION; + opts[2] = OPT_TYPE_SHOW_UPTIME; + opts[3] = OPT_TYPE_SHOW_TID_REQ_COUNT; + opts[4] = OPT_TYPE_SHOW_TID_REQ_PENDING; + opts[5] = OPT_TYPE_SHOW_ROUTES; + opts[6] = OPT_TYPE_SHOW_COMMUNITIES; + opts[7] = OPT_TYPE_UNKNOWN; + s = show_options(opts); + expected = read_file("req_show_all_options.test"); + assert(expected); + assert(strcmp(expected, s) == 0); + free(s); + free(expected); + + return 0; +} \ No newline at end of file diff --git a/mon/tests/test_mon_resp_encode.c b/mon/tests/test_mon_resp_encode.c new file mode 100644 index 0000000..6945b76 --- /dev/null +++ b/mon/tests/test_mon_resp_encode.c @@ -0,0 +1,130 @@ +// +// Created by jlr on 4/9/18. +// + +#include +#include +#include +#include + +#include + +#define JSON_DUMP_OPTS 0 + +static char *reconfigure(MON_RESP_CODE code, const char *message) +{ + MON_REQ *req = NULL; + MON_RESP *resp = NULL; + json_t *resp_json = NULL; + char *result = NULL; + + req = mon_req_new(NULL, MON_CMD_RECONFIGURE); + assert(req); + + resp = mon_resp_new(NULL, code, message, NULL); + assert(resp); + + resp_json = mon_resp_encode(resp); + assert(resp_json); + + result = json_dumps(resp_json, JSON_DUMP_OPTS); + assert(result); + + json_decref(resp_json); + mon_resp_free(resp); + mon_req_free(req); + return result; +} + +static char *reconfigure_success() +{ + return reconfigure(MON_RESP_SUCCESS, "success"); +} + +static char *reconfigure_error() +{ + return reconfigure(MON_RESP_ERROR, "error"); +} + +static char *show_success() +{ + MON_REQ *req = NULL; + MON_RESP *resp = NULL; + json_t *resp_json = NULL; + json_t *payload = NULL; + char *result = NULL; + + req = mon_req_new(NULL, MON_CMD_SHOW); + // Only need the command to be set in req, don't actually need the options + assert(req); + + payload = json_object(); + assert(payload); + assert(! json_object_set_new(payload, + mon_opt_type_to_string(OPT_TYPE_SHOW_VERSION), + json_string("1.2.3-4"))); + assert(! json_object_set_new(payload, + mon_opt_type_to_string(OPT_TYPE_SHOW_CONFIG_FILES), + json_integer(1234567890))); + assert(! json_object_set_new(payload, + mon_opt_type_to_string(OPT_TYPE_SHOW_CONFIG_FILES), + json_integer(86400))); + assert(! json_object_set_new(payload, + mon_opt_type_to_string(OPT_TYPE_SHOW_TID_REQ_PENDING), + json_integer(13))); + assert(! json_object_set_new(payload, + mon_opt_type_to_string(OPT_TYPE_SHOW_TID_REQ_COUNT), + json_integer(1432))); + + resp = mon_resp_new(NULL, MON_RESP_SUCCESS, "success", payload); + assert(resp); + + resp_json = mon_resp_encode(resp); + assert(resp_json); + + result = json_dumps(resp_json, JSON_DUMP_OPTS); + assert(result); + + json_decref(resp_json); + mon_resp_free(resp); + mon_req_free(req); + return result; +} + +static char *read_file(const char *filename) +{ + FILE *f = fopen(filename, "r"); + char *s = NULL; + size_t nn = 0; + ssize_t n = getline(&s, &nn, f); + fclose(f); + + if( (n > 0) && (s[n-1] == '\n')) + s[n-1] = 0; + + return s; +} + +int run_test(const char *filename, char *(generator)()) +{ + char *s = NULL; + char *expected = NULL; + + // Test reconfigure command + s = generator(); + expected = read_file(filename); + assert(expected); + assert(strcmp(expected, s) == 0); + free(s); + free(expected); + + return 1; +} + +int main(void) +{ + assert(run_test("resp_reconfigure_success.test", reconfigure_success)); + assert(run_test("resp_reconfigure_error.test", reconfigure_error)); + assert(run_test("resp_show_success.test", show_success)); + return 0; +} diff --git a/tid/example/tidc_main.c b/tid/example/tidc_main.c index 26fb994..02cd1db 100644 --- a/tid/example/tidc_main.c +++ b/tid/example/tidc_main.c @@ -210,7 +210,8 @@ int main (int argc, /* Create a TID client instance & the client DH */ tidc = tidc_create(); - if (NULL == (tidc->client_dh = tr_create_dh_params(NULL, 0))) { + tidc_set_dh(tidc, tr_create_dh_params(NULL, 0)); + if (tidc_get_dh(tidc) == NULL) { printf("Error creating client DH params.\n"); return 1; } diff --git a/tid/example/tids_main.c b/tid/example/tids_main.c index 8673cdd..1ecc494 100644 --- a/tid/example/tids_main.c +++ b/tid/example/tids_main.c @@ -167,9 +167,6 @@ static int tids_req_handler (TIDS_INSTANCE *tids, tr_debug("tids_req_handler: Request received! target_realm = %s, community = %s", req->realm->buf, req->comm->buf); - if (tids) - tids->req_count++; - if (!(resp) || !resp) { tr_debug("tids_req_handler: No response structure."); return -1; diff --git a/tid/tid_req.c b/tid/tid_req.c index 967b1fc..002b720 100644 --- a/tid/tid_req.c +++ b/tid/tid_req.c @@ -167,7 +167,7 @@ TR_NAME *tid_req_get_orig_coi(TID_REQ *req) return(req->orig_coi); } -void tid_req_set_rp_orig_coi(TID_REQ *req, TR_NAME *orig_coi) +void tid_req_set_orig_coi(TID_REQ *req, TR_NAME *orig_coi) { req->orig_coi = orig_coi; } diff --git a/tid/tid_resp.c b/tid/tid_resp.c index 4f46b12..3ff3d02 100644 --- a/tid/tid_resp.c +++ b/tid/tid_resp.c @@ -83,6 +83,13 @@ void tid_resp_free(TID_RESP *resp) talloc_free(resp); } +/** + * Allocate a new copy of a TID_RESP + * + * @param mem_ctx + * @param resp + * @return + */ TID_RESP *tid_resp_dup(TALLOC_CTX *mem_ctx, TID_RESP *resp) { TID_RESP *newresp=NULL; @@ -93,19 +100,38 @@ TID_RESP *tid_resp_dup(TALLOC_CTX *mem_ctx, TID_RESP *resp) newresp=tid_resp_new(mem_ctx); if (NULL!=newresp) { - newresp->result=resp->result; - newresp->err_msg=tr_dup_name(resp->err_msg); - newresp->rp_realm=tr_dup_name(resp->rp_realm); - newresp->realm=tr_dup_name(resp->realm); - newresp->comm=tr_dup_name(resp->comm); - newresp->orig_coi=tr_dup_name(resp->orig_coi); - newresp->servers=tid_srvr_blk_dup(newresp, resp->servers); - tid_resp_set_cons(newresp, resp->cons); - tid_resp_set_error_path(newresp, resp->error_path); + tid_resp_cpy(newresp, resp); } return newresp; } +/** + * Copy contents of one TID_RESP to an existing TID_RESP + * + * @param dst + * @param src + * @return TID_SUCCESS on success, error code on error + */ +TID_RC tid_resp_cpy(TID_RESP *dst, TID_RESP *src) +{ + tid_resp_set_result(dst, tid_resp_get_result(src)); + tid_resp_set_err_msg(dst, + tr_dup_name(tid_resp_get_err_msg(src))); + tid_resp_set_rp_realm(dst, + tr_dup_name(tid_resp_get_rp_realm(src))); + tid_resp_set_realm(dst, + tr_dup_name(tid_resp_get_realm(src))); + tid_resp_set_comm(dst, + tr_dup_name(tid_resp_get_comm(src))); + tid_resp_set_cons(dst, src->cons); + tid_resp_set_orig_coi(dst, + tr_dup_name(tid_resp_get_orig_coi(src))); + dst->servers = tid_srvr_blk_dup(dst, src->servers); + tid_resp_set_error_path(dst, src->error_path); + + return TID_SUCCESS; +} + TR_EXPORT int tid_resp_get_result(TID_RESP *resp) { return(resp->result); diff --git a/tid/tidc.c b/tid/tidc.c index 320ded6..90335f0 100644 --- a/tid/tidc.c +++ b/tid/tidc.c @@ -36,33 +36,28 @@ #include #include +#include #include #include #include -#include #include #include int tmp_len = 32; -static int tidc_destructor(void *obj) -{ - TIDC_INSTANCE *tidc=talloc_get_type_abort(obj, TIDC_INSTANCE); - if (NULL!=tidc) { - if (NULL!=tidc->client_dh) - tr_destroy_dh_params(tidc->client_dh); - } - return 0; -} - /* creates struct in talloc null context */ TIDC_INSTANCE *tidc_create(void) { TIDC_INSTANCE *tidc=talloc(NULL, TIDC_INSTANCE); if (tidc!=NULL) { - tidc->client_dh=NULL; - talloc_set_destructor((void *)tidc, tidc_destructor); + tidc->gssc = tr_gssc_instance_new(tidc); + if (tidc->gssc == NULL) { + talloc_free(tidc); + return NULL; + } + + tidc->gssc->service_name = "trustidentity"; } return tidc; } @@ -73,44 +68,56 @@ void tidc_destroy(TIDC_INSTANCE *tidc) } int tidc_open_connection (TIDC_INSTANCE *tidc, - const char *server, - unsigned int port, - gss_ctx_id_t *gssctx) + const char *server, + unsigned int port, + gss_ctx_id_t *gssctx) { - int err = 0; - int conn = -1; unsigned int use_port = 0; + tidc->gssc->gss_ctx = gssctx; if (0 == port) use_port = TID_PORT; else use_port = port; - tr_debug("tidc_open_connection: opening tidc connection to %s:%d", server, port); - err = gsscon_connect(server, use_port, "trustidentity", &conn, gssctx); - - if (!err) - return conn; + tr_debug("tidc_open_connection: opening tidc connection to %s:%d", server, use_port); + if (0 == tr_gssc_open_connection(tidc->gssc, server, use_port)) + return tidc->gssc->conn; else return -1; } int tidc_send_request (TIDC_INSTANCE *tidc, - int conn, - gss_ctx_id_t gssctx, - const char *rp_realm, - const char *realm, - const char *comm, - TIDC_RESP_FUNC *resp_handler, - void *cookie) + int conn, + gss_ctx_id_t gssctx, + const char *rp_realm, + const char *realm, + const char *comm, + TIDC_RESP_FUNC *resp_handler, + void *cookie) { TID_REQ *tid_req = NULL; char *request_id = NULL; int rc; + int orig_conn = 0; + gss_ctx_id_t *orig_gss_ctx = NULL; + + /* For ABI compatibility, replace the generic GSS client parameters + * with the arguments we were passed. */ + orig_conn = tidc->gssc->conn; /* save to restore later */ + if (conn != tidc->gssc->conn) { + tr_warning("tidc_send_request: WARNING: socket connection FD does not match FD opened by tidc_open_connection()"); + tidc->gssc->conn = conn; + } + orig_gss_ctx = tidc->gssc->gss_ctx; /* save to restore later */ + if (gssctx != *(tidc->gssc->gss_ctx)) { + tr_warning("tidc_send_request: WARNING: sending request with different GSS context than used for tidc_open_connection()"); + *tidc->gssc->gss_ctx = gssctx; + } /* Create and populate a TID req structure */ if (!(tid_req = tid_req_new())) - return -1; + goto error; tid_req->conn = conn; tid_req->gssctx = gssctx; @@ -122,7 +129,7 @@ int tidc_send_request (TIDC_INSTANCE *tidc, goto error; } - tid_req->tidc_dh = tr_dh_dup(tidc->client_dh); + tid_req->tidc_dh = tr_dh_dup(tidc->gssc->client_dh); /* generate an ID */ request_id = tr_random_id(NULL); @@ -140,7 +147,11 @@ int tidc_send_request (TIDC_INSTANCE *tidc, error: rc = -1; cleanup: - tid_req_free(tid_req); + if (tid_req) + tid_req_free(tid_req); + + tidc->gssc->conn = orig_conn; + tidc->gssc->gss_ctx = orig_gss_ctx; return rc; } @@ -149,60 +160,26 @@ int tidc_fwd_request(TIDC_INSTANCE *tidc, TIDC_RESP_FUNC *resp_handler, void *cookie) { - char *req_buf = NULL; - char *resp_buf = NULL; - size_t resp_buflen = 0; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); TR_MSG *msg = NULL; TR_MSG *resp_msg = NULL; TID_RESP *tid_resp = NULL; - int err; int rc = 0; /* Create and populate a TID msg structure */ - if (!(msg = talloc_zero(tid_req, TR_MSG))) + if (!(msg = talloc_zero(tmp_ctx, TR_MSG))) goto error; msg->msg_type = TID_REQUEST; tr_msg_set_req(msg, tid_req); - /* store the response function and cookie */ - // tid_req->resp_func = resp_handler; - // tid_req->cookie = cookie; - - /* Encode the request into a json string */ - if (!(req_buf = tr_msg_encode(msg))) { - tr_err("tidc_fwd_request: Error encoding TID request.\n"); - goto error; - } - - tr_debug( "tidc_fwd_request: Sending TID request:\n"); - tr_debug( "%s\n", req_buf); + tr_debug( "tidc_fwd_request: Sending TID request\n"); /* Send the request over the connection */ - if (err = gsscon_write_encrypted_token (tid_req->conn, tid_req->gssctx, req_buf, - strlen(req_buf))) { - tr_err( "tidc_fwd_request: Error sending request over connection.\n"); + resp_msg = tr_gssc_exchange_msgs(tmp_ctx, tidc->gssc, msg); + if (resp_msg == NULL) goto error; - } - - /* TBD -- queue request on instance, read resps in separate thread */ - - /* Read the response from the connection */ - /* TBD -- timeout? */ - if (err = gsscon_read_encrypted_token(tid_req->conn, tid_req->gssctx, &resp_buf, &resp_buflen)) { - if (resp_buf) - free(resp_buf); - goto error; - } - - tr_debug( "tidc_fwd_request: Response Received (%u bytes).\n", (unsigned) resp_buflen); - tr_debug( "%s\n", resp_buf); - - if (NULL == (resp_msg = tr_msg_decode(resp_buf, resp_buflen))) { - tr_err( "tidc_fwd_request: Error decoding response.\n"); - goto error; - } /* TBD -- Check if this is actually a valid response */ tid_resp = tr_msg_get_resp(resp_msg); @@ -237,25 +214,17 @@ int tidc_fwd_request(TIDC_INSTANCE *tidc, error: rc = -1; cleanup: - if (msg) - talloc_free(msg); - if (req_buf) - free(req_buf); - if (resp_buf) - free(resp_buf); - if (resp_msg) - tr_msg_free_decoded(resp_msg); + talloc_free(tmp_ctx); return rc; } DH * tidc_get_dh(TIDC_INSTANCE *inst) { - return inst->client_dh; + return tr_gssc_get_dh(inst->gssc); } DH *tidc_set_dh(TIDC_INSTANCE *inst, DH *dh) { - inst->client_dh = dh; - return dh; + return tr_gssc_set_dh(inst->gssc, dh); } diff --git a/tid/tids.c b/tid/tids.c index 8d22749..9b9d51f 100644 --- a/tid/tids.c +++ b/tid/tids.c @@ -32,16 +32,13 @@ * */ -#include #include #include #include #include #include -#include #include #include -#include #include #include #include @@ -49,13 +46,23 @@ #include #include #include +#include +#include +#include -static TID_RESP *tids_create_response (TIDS_INSTANCE *tids, TID_REQ *req) +/** + * Create a response with minimal fields filled in + * + * @param mem_ctx talloc context for the return value + * @param req request to respond to + * @return new response structure allocated in the mem_ctx context + */ +static TID_RESP *tids_create_response(TALLOC_CTX *mem_ctx, TID_REQ *req) { TID_RESP *resp=NULL; int success=0; - if (NULL == (resp = tid_resp_new(req))) { + if (NULL == (resp = tid_resp_new(mem_ctx))) { tr_crit("tids_create_response: Error allocating response structure."); return NULL; } @@ -90,250 +97,137 @@ cleanup: return resp; } -static int tids_listen(TIDS_INSTANCE *tids, int port, int *fd_out, size_t max_fd) -{ - int rc = 0; - int conn = -1; - int optval = 1; - struct addrinfo *ai=NULL; - struct addrinfo *ai_head=NULL; - struct addrinfo hints={.ai_flags=AI_PASSIVE, - .ai_family=AF_UNSPEC, - .ai_socktype=SOCK_STREAM, - .ai_protocol=IPPROTO_TCP}; - char *port_str=NULL; - size_t n_opened=0; - - tr_debug("tids_listen: started!"); - port_str=talloc_asprintf(NULL, "%d", port); - if (port_str==NULL) { - tr_debug("tids_listen: unable to allocate port."); - return -1; - } - - tr_debug("getaddrinfo()=%d", getaddrinfo(NULL, port_str, &hints, &ai_head)); - talloc_free(port_str); - tr_debug("tids_listen: got address info"); - - /* TODO: listen on all ports */ - for (ai=ai_head,n_opened=0; (ai!=NULL)&&(n_openedai_next) { - if (0 > (conn = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))) { - tr_debug("tids_listen: unable to open socket."); - continue; - } - - optval=1; - if (0!=setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) - tr_debug("tids_listen: unable to set SO_REUSEADDR."); /* not fatal? */ - - if (ai->ai_family==AF_INET6) { - /* don't allow IPv4-mapped IPv6 addresses (per RFC4942, not sure - * if still relevant) */ - if (0!=setsockopt(conn, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval))) { - tr_debug("tids_listen: unable to set IPV6_V6ONLY. Skipping interface."); - close(conn); - continue; - } - } - - rc=bind(conn, ai->ai_addr, ai->ai_addrlen); - if (rc<0) { - tr_debug("tids_listen: unable to bind to socket."); - close(conn); - continue; - } - - if (0>listen(conn, 512)) { - tr_debug("tids_listen: unable to listen on bound socket."); - close(conn); - continue; - } - - /* ok, this one worked. Save it */ - fd_out[n_opened++]=conn; - } - freeaddrinfo(ai_head); - - if (n_opened==0) { - tr_debug("tids_listen: no addresses available for listening."); - return -1; - } - - tr_debug("tids_listen: TRP Server listening on port %d on %d socket%s", - port, - n_opened, - (n_opened==1)?"":"s"); - - return n_opened; -} - -/* returns EACCES if authorization is denied */ -static int tids_auth_cb(gss_name_t clientName, gss_buffer_t displayName, - void *data) -{ - struct tids_instance *inst = (struct tids_instance *) data; - TR_NAME name ={(char *) displayName->value, - displayName->length}; - int result=0; - - if (0!=inst->auth_handler(clientName, &name, inst->cookie)) { - tr_debug("tids_auth_cb: client '%.*s' denied authorization.", name.len, name.buf); - result=EACCES; /* denied */ - } - - return result; -} - -/* returns 0 on authorization success, 1 on failure, or -1 in case of error */ -static int tids_auth_connection (TIDS_INSTANCE *inst, - int conn, - gss_ctx_id_t *gssctx) -{ - int rc = 0; - int auth, autherr = 0; - gss_buffer_desc nameBuffer = {0, NULL}; - char *name = 0; - int nameLen = 0; - - nameLen = asprintf(&name, "trustidentity@%s", inst->hostname); - nameBuffer.length = nameLen; - nameBuffer.value = name; - - if (rc = gsscon_passive_authenticate(conn, nameBuffer, gssctx, tids_auth_cb, inst)) { - tr_debug("tids_auth_connection: Error from gsscon_passive_authenticate(), rc = %d.", rc); - free(name); - return -1; - } - free(name); - nameBuffer.value=NULL; nameBuffer.length=0; - - if (rc = gsscon_authorize(*gssctx, &auth, &autherr)) { - tr_debug("tids_auth_connection: Error from gsscon_authorize, rc = %d, autherr = %d.", - rc, autherr); - return -1; - } - - if (auth) - tr_debug("tids_auth_connection: Connection authenticated, conn = %d.", conn); - else - tr_debug("tids_auth_connection: Authentication failed, conn %d.", conn); - - return !auth; -} - -static int tids_read_request (TIDS_INSTANCE *tids, int conn, gss_ctx_id_t *gssctx, TR_MSG **mreq) -{ - int err; - char *buf; - size_t buflen = 0; - - if (err = gsscon_read_encrypted_token(conn, *gssctx, &buf, &buflen)) { - if (buf) - free(buf); - return -1; - } - - tr_debug("tids_read_request():Request Received, %u bytes.", (unsigned) buflen); - - /* Parse request */ - if (NULL == ((*mreq) = tr_msg_decode(buf, buflen))) { - tr_debug("tids_read_request():Error decoding request."); - free (buf); - return -1; - } - - /* If this isn't a TID Request, just drop it. */ - if (TID_REQUEST != (*mreq)->msg_type) { - tr_debug("tids_read_request(): Not a TID Request, dropped."); - return -1; - } - - free (buf); - return buflen; -} - -static int tids_handle_request (TIDS_INSTANCE *tids, TR_MSG *mreq, TID_RESP *resp) +static int tids_handle_request(TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp) { int rc=-1; /* Check that this is a valid TID Request. If not, send an error return. */ - if ((!tr_msg_get_req(mreq)) || - (!tr_msg_get_req(mreq)->rp_realm) || - (!tr_msg_get_req(mreq)->realm) || - (!tr_msg_get_req(mreq)->comm)) { + if ((!req) || + (!(req->rp_realm)) || + (!(req->realm)) || + (!(req->comm))) { tr_notice("tids_handle_request(): Not a valid TID Request."); - resp->result = TID_ERROR; - resp->err_msg = tr_new_name("Bad request format"); + tid_resp_set_result(resp, TID_ERROR); + tid_resp_set_err_msg(resp, tr_new_name("Bad request format")); return -1; } tr_debug("tids_handle_request: adding self to req path."); - tid_req_add_path(tr_msg_get_req(mreq), tids->hostname, tids->tids_port); + tid_req_add_path(req, tids->hostname, tids->tids_port); /* Call the caller's request handler */ /* TBD -- Handle different error returns/msgs */ - if (0 > (rc = (*tids->req_handler)(tids, tr_msg_get_req(mreq), resp, tids->cookie))) { + if (0 > (rc = (*tids->req_handler)(tids, req, resp, tids->cookie))) { /* set-up an error response */ tr_debug("tids_handle_request: req_handler returned error."); - resp->result = TID_ERROR; - if (!resp->err_msg) /* Use msg set by handler, if any */ - resp->err_msg = tr_new_name("Internal processing error"); - } - else { + tid_resp_set_result(resp, TID_ERROR); + if (!tid_resp_get_err_msg(resp)) /* Use msg set by handler, if any */ + tid_resp_set_err_msg(resp, tr_new_name("Internal processing error")); + } else { /* set-up a success response */ tr_debug("tids_handle_request: req_handler returned success."); - resp->result = TID_SUCCESS; + tid_resp_set_result(resp, TID_SUCCESS); resp->err_msg = NULL; /* No error msg on successful return */ } return rc; } +/** + * Produces a JSON-encoded msg containing the TID response + * + * @param mem_ctx talloc context for the return value + * @param resp outgoing response + * @return JSON-encoded message containing the TID response + */ +static char *tids_encode_response(TALLOC_CTX *mem_ctx, TID_RESP *resp) +{ + TR_MSG mresp; + char *resp_buf = NULL; + + /* Construct the response message */ + mresp.msg_type = TID_RESPONSE; + tr_msg_set_resp(&mresp, resp); + + /* Encode the message to JSON */ + resp_buf = tr_msg_encode(mem_ctx, &mresp); + if (resp_buf == NULL) { + tr_err("tids_encode_response: Error encoding json response."); + return NULL; + } + tr_debug("tids_encode_response: Encoded response: %s", resp_buf); + + /* Success */ + return resp_buf; +} + +/** + * Encode/send an error response + * + * Part of the public interface + * + * @param tids + * @param req + * @param err_msg + * @return + */ int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_msg) { TID_RESP *resp = NULL; int rc = 0; + if ((!tids) || (!req) || (!err_msg)) { + tr_debug("tids_send_err_response: Invalid parameters."); + return -1; + } + /* If we already sent a response, don't send another no matter what. */ if (req->resp_sent) return 0; - if (NULL == (resp = tids_create_response(tids, req))) { + if (NULL == (resp = tids_create_response(req, req))) { tr_crit("tids_send_err_response: Can't create response."); return -1; } - + /* mark this as an error response, and include the error message */ resp->result = TID_ERROR; resp->err_msg = tr_new_name((char *)err_msg); resp->error_path = req->path; rc = tids_send_response(tids, req, resp); - + tid_resp_free(resp); return rc; } +/** + * Encode/send a response + * + * Part of the public interface + * + * @param tids not actually used, but kept for ABI compatibility + * @param req + * @param resp + * @return + */ int tids_send_response (TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp) { int err; - TR_MSG mresp; char *resp_buf; - if ((!tids) || (!req) || (!resp)) + if ((!tids) || (!req) || (!resp)) { tr_debug("tids_send_response: Invalid parameters."); + return -1; + } /* Never send a second response if we already sent one. */ if (req->resp_sent) return 0; - mresp.msg_type = TID_RESPONSE; - tr_msg_set_resp(&mresp, resp); - - if (NULL == (resp_buf = tr_msg_encode(&mresp))) { - + resp_buf = tids_encode_response(NULL, NULL); + if (resp_buf == NULL) { tr_err("tids_send_response: Error encoding json response."); tr_audit_req(req); - return -1; } @@ -344,12 +238,11 @@ int tids_send_response (TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp) tr_audit_resp(resp); /* Send the response over the connection */ - if (err = gsscon_write_encrypted_token (req->conn, req->gssctx, resp_buf, - strlen(resp_buf) + 1)) { + err = gsscon_write_encrypted_token (req->conn, req->gssctx, resp_buf, + strlen(resp_buf) + 1); + if (err) { tr_notice("tids_send_response: Error sending response over connection."); - tr_audit_req(req); - return -1; } @@ -361,83 +254,119 @@ int tids_send_response (TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp) return 0; } -static void tids_handle_connection (TIDS_INSTANCE *tids, int conn) +/** + * Callback to process a request and produce a response + * + * @param req_str JSON-encoded request + * @param data pointer to a TIDS_INSTANCE + * @return pointer to the response string or null to send no response + */ +static TR_MSG *tids_req_cb(TALLOC_CTX *mem_ctx, TR_MSG *mreq, void *data) { - TR_MSG *mreq = NULL; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + TIDS_INSTANCE *tids = talloc_get_type_abort(data, TIDS_INSTANCE); + TID_REQ *req = NULL; TID_RESP *resp = NULL; + TR_MSG *resp_msg = NULL; /* this is the return value */ int rc = 0; - gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT; - if (tids_auth_connection(tids, conn, &gssctx)) { - tr_notice("tids_handle_connection: Error authorizing TID Server connection."); - close(conn); - return; + /* If this isn't a TID Request, just drop it. */ + if (mreq->msg_type != TID_REQUEST) { + tr_debug("tids_req_cb: Not a TID request, dropped."); + goto cleanup; } - tr_debug("tids_handle_connection: Connection authorized!"); + /* Get a handle on the request itself. Don't free req - it belongs to mreq */ + req = tr_msg_get_req(mreq); - while (1) { /* continue until an error breaks us out */ + /* Allocate a response message */ + resp_msg = talloc(tmp_ctx, TR_MSG); + if (resp_msg == NULL) { + /* We cannot create a response message, so all we can really do is emit + * an error message and return. */ + tr_crit("tids_req_cb: Error allocating response message."); + goto cleanup; + } - if (0 > (rc = tids_read_request(tids, conn, &gssctx, &mreq))) { - tr_debug("tids_handle_connection: Error from tids_read_request(), rc = %d.", rc); - return; - } else if (0 == rc) { - continue; - } + /* Allocate a response structure and populate common fields. Put it in the + * response message's talloc context. */ + resp = tids_create_response(resp_msg, req); + if (resp == NULL) { + /* If we were unable to create a response, we cannot reply. Log an + * error if we can, then drop the request. */ + tr_crit("tids_req_cb: Error creating response structure."); + resp_msg = NULL; /* the contents are in tmp_ctx, so they will still be cleaned up */ + goto cleanup; + } + /* Now officially assign the response to the message. */ + tr_msg_set_resp(resp_msg, resp); + + /* Handle the request and fill in resp */ + rc = tids_handle_request(tids, req, resp); + if (rc < 0) { + tr_debug("tids_req_cb: Error from tids_handle_request(), rc = %d.", rc); + /* Fall through, to send the response, either way */ + } - /* Put connection information into the request structure */ - tr_msg_get_req(mreq)->conn = conn; - tr_msg_get_req(mreq)->gssctx = gssctx; - - /* Allocate a response structure and populate common fields */ - if (NULL == (resp = tids_create_response (tids, tr_msg_get_req(mreq)))) { - tr_crit("tids_handle_connection: Error creating response structure."); - /* try to send an error */ - tids_send_err_response(tids, tr_msg_get_req(mreq), "Error creating response."); - tr_msg_free_decoded(mreq); - return; - } + /* put the response message in the caller's context */ + talloc_steal(mem_ctx, resp_msg); - if (0 > (rc = tids_handle_request(tids, mreq, resp))) { - tr_debug("tids_handle_connection: Error from tids_handle_request(), rc = %d.", rc); - /* Fall through, to send the response, either way */ - } +cleanup: + talloc_free(tmp_ctx); + return resp_msg; +} - if (0 > (rc = tids_send_response(tids, tr_msg_get_req(mreq), resp))) { - tr_debug("tids_handle_connection: Error from tids_send_response(), rc = %d.", rc); - /* if we didn't already send a response, try to send a generic error. */ - if (!tr_msg_get_req(mreq)->resp_sent) - tids_send_err_response(tids, tr_msg_get_req(mreq), "Error sending response."); - /* Fall through to free the response, either way. */ +static int tids_destructor(void *object) +{ + TIDS_INSTANCE *tids = talloc_get_type_abort(object, TIDS_INSTANCE); + if (tids->pids) + g_array_unref(tids->pids); + return 0; +} + +TIDS_INSTANCE *tids_new(TALLOC_CTX *mem_ctx) +{ + TIDS_INSTANCE *tids = talloc_zero(mem_ctx, TIDS_INSTANCE); + if (tids) { + tids->pids = g_array_new(FALSE, FALSE, sizeof(struct tid_process)); + if (tids->pids == NULL) { + talloc_free(tids); + return NULL; } - - tr_msg_free_decoded(mreq); /* takes resp with it */ - return; - } + talloc_set_destructor((void *)tids, tids_destructor); + } + return tids; } -TIDS_INSTANCE *tids_create (void) +/** + * Create a new TIDS instance + * + * Deprecated: exists for ABI compatibility, but tids_new() should be used instead + * + */ +TIDS_INSTANCE *tids_create(void) { - return talloc_zero(NULL, TIDS_INSTANCE); + return tids_new(NULL); } /* Get a listener for tids requests, returns its socket fd. Accept * connections with tids_accept() */ -int tids_get_listener(TIDS_INSTANCE *tids, - TIDS_REQ_FUNC *req_handler, - tids_auth_func *auth_handler, - const char *hostname, - unsigned int port, - void *cookie, - int *fd_out, - size_t max_fd) +nfds_t tids_get_listener(TIDS_INSTANCE *tids, + TIDS_REQ_FUNC *req_handler, + tids_auth_func *auth_handler, + const char *hostname, + unsigned int port, + void *cookie, + int *fd_out, + size_t max_fd) { - size_t n_fd=0; - size_t ii=0; + nfds_t n_fd = 0; + nfds_t ii = 0; tids->tids_port = port; - n_fd=tids_listen(tids, port, fd_out, max_fd); - if (n_fd<=0) + n_fd = tr_sock_listen_all(port, fd_out, max_fd); + + if (n_fd == 0) tr_err("tids_get_listener: Error opening port %d"); else { /* opening port succeeded */ @@ -451,13 +380,13 @@ int tids_get_listener(TIDS_INSTANCE *tids, close(fd_out[ii]); fd_out[ii]=-1; } - n_fd=0; + n_fd = 0; break; } } } - if (n_fd>0) { + if (n_fd > 0) { /* store the caller's request handler & cookie */ tids->req_handler = req_handler; tids->auth_handler = auth_handler; @@ -465,7 +394,7 @@ int tids_get_listener(TIDS_INSTANCE *tids, tids->cookie = cookie; } - return n_fd; + return (int)n_fd; } /* Accept and process a connection on a port opened with tids_get_listener() */ @@ -473,47 +402,137 @@ int tids_accept(TIDS_INSTANCE *tids, int listen) { int conn=-1; int pid=-1; + int pipe_fd[2]; + struct tid_process tp = {0}; if (0 > (conn = accept(listen, NULL, NULL))) { perror("Error from TIDS Server accept()"); return 1; } + if (0 > pipe(pipe_fd)) { + perror("Error on pipe()"); + return 1; + } + /* pipe_fd[0] is for reading, pipe_fd[1] is for writing */ + if (0 > (pid = fork())) { perror("Error on fork()"); return 1; } if (pid == 0) { + close(pipe_fd[0]); /* child only writes */ close(listen); - tids_handle_connection(tids, conn); + tr_gss_handle_connection(conn, + "trustidentity", tids->hostname, /* acceptor name */ + tids->auth_handler, tids->cookie, /* auth callback and cookie */ + tids_req_cb, tids /* req callback and cookie */ + ); + if (write(pipe_fd[1], "OK\0", 3) < 0) + tr_crit("tids_accept: child process unable to write to pipe"); + close(pipe_fd[1]); close(conn); exit(0); /* exit to kill forked child process */ - } else { - close(conn); } - /* clean up any processes that have completed (TBD: move to main loop?) */ - while (waitpid(-1, 0, WNOHANG) > 0); + /* Only the parent process gets here */ + close(pipe_fd[1]); /* parent only listens */ + close(conn); /* connection belongs to the child */ + tp.pid = pid; + tp.read_fd = pipe_fd[0]; + g_array_append_val(tids->pids, tp); /* remember the PID of our child process */ + /* clean up any processes that have completed */ + tids_sweep_procs(tids); return 0; } +/** + * Clean up any finished TID request processes + * + * This is called by the main process after forking each TID request. If you want to be + * sure finished processes are cleaned up promptly even during a lull in TID requests, + * this can be called from the main thread of the main process. It is not thread-safe, + * so should not be used from sub-threads. It should not be called by child processes - + * this would probably be harmless but ineffective. + * + * @param tids + */ +void tids_sweep_procs(TIDS_INSTANCE *tids) +{ + guint ii; + struct tid_process tp = {0}; + char result[10] = {0}; + ssize_t result_len; + int status; + int wait_rc; + + /* loop backwards over the array so we can remove elements as we go */ + for (ii=tids->pids->len; ii > 0; ii--) { + /* ii-1 is the current index - get our own copy, we may destroy the list's copy */ + tp = g_array_index(tids->pids, struct tid_process, ii-1); + + wait_rc = waitpid(tp.pid, &status, WNOHANG); + if (wait_rc == 0) + continue; /* process still running */ + + if (wait_rc < 0) { + /* invalid options will probably keep being invalid, report that condition */ + if(errno == EINVAL) + tr_crit("tids_sweep_procs: waitpid called with invalid options"); + + /* If we got ECHILD, that means the PID was invalid; we'll assume the process was + * terminated and we missed it. For all other errors, move on + * to the next PID to check. */ + if (errno != ECHILD) + continue; + + tr_warning("tid_sweep_procs: TID process %d disappeared", tp.pid); + } + + /* remove the item (we still have a copy of the data) */ + g_array_remove_index_fast(tids->pids, ii-1); /* disturbs only indices >= ii-1 which we've already handled */ + + /* Report exit status unless we got ECHILD above or somehow waitpid returned the wrong pid */ + if (wait_rc == tp.pid) { + if (WIFEXITED(status)) { + tr_debug("tids_sweep_procs: TID process %d exited with status %d.", tp.pid, WTERMSIG(status)); + } else if (WIFSIGNALED(status)) { + tr_debug("tids_sweep_procs: TID process %d terminated by signal %d.", tp.pid, WTERMSIG(status)); + } + } else if (wait_rc > 0) { + tr_err("tids_sweep_procs: waitpid returned pid %d, expected %d", wait_rc, tp.pid); + } + + /* read the pipe - if the TID request worked, it will have written status before terminating */ + result_len = read(tp.read_fd, result, sizeof(result)/sizeof(result[0])); + close(tp.read_fd); + + if ((result_len > 0) && (strcmp(result, "OK") == 0)) { + tids->req_count++; + tr_debug("tids_sweep_procs: TID process %d succeeded", tp.pid); + } else { + tids->error_count++; + tr_debug("tids_sweep_procs: TID process %d failed", tp.pid); + } + } +} + /* Process tids requests forever. Should not return except on error. */ -#define MAX_SOCKETS 10 -int tids_start (TIDS_INSTANCE *tids, +int tids_start (TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler, tids_auth_func *auth_handler, const char *hostname, unsigned int port, void *cookie) { - int fd[MAX_SOCKETS]={0}; - size_t n_fd=0; - struct pollfd poll_fd[MAX_SOCKETS]={{0}}; + int fd[TR_MAX_SOCKETS]={0}; + nfds_t n_fd=0; + struct pollfd poll_fd[TR_MAX_SOCKETS]={{0}}; int ii=0; - n_fd=tids_get_listener(tids, req_handler, auth_handler, hostname, port, cookie, fd, MAX_SOCKETS); + n_fd = tids_get_listener(tids, req_handler, auth_handler, hostname, port, cookie, fd, TR_MAX_SOCKETS); if (n_fd <= 0) { perror ("Error from tids_listen()"); return 1; @@ -557,7 +576,6 @@ int tids_start (TIDS_INSTANCE *tids, return 1; /* should never get here, loops "forever" */ } -#undef MAX_SOCKETS void tids_destroy (TIDS_INSTANCE *tids) { diff --git a/tr/tr_main.c b/tr/tr_main.c index 4f09d12..ba738c7 100644 --- a/tr/tr_main.c +++ b/tr/tr_main.c @@ -34,15 +34,15 @@ #include #include -#include #include #include #include -#include #include -#include +#include #include +#include +#include #include #include #include @@ -151,6 +151,38 @@ static void configure_signals(void) pthread_sigmask(SIG_BLOCK, &signals, NULL); } +/* Monitoring handlers */ +static MON_RC tr_handle_version(void *cookie, json_t **result_ptr) +{ + *result_ptr = json_string(PACKAGE_VERSION); + return (*result_ptr == NULL) ? MON_NOMEM : MON_SUCCESS; +} + +static MON_RC tr_handle_uptime(void *cookie, json_t **result_ptr) +{ + time_t *start_time = cookie; + *result_ptr = json_integer(time(NULL) - (*start_time)); + return (*result_ptr == NULL) ? MON_NOMEM : MON_SUCCESS; +} + +static MON_RC tr_handle_show_rp_clients(void *cookie, json_t **response_ptr) +{ + TR_CFG_MGR *cfg_mgr = talloc_get_type_abort(cookie, TR_CFG_MGR); + + *response_ptr = tr_rp_clients_to_json(cfg_mgr->active->rp_clients); + return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS; +} + +static MON_RC tr_handle_show_cfg_serial(void *cookie, json_t **response_ptr) +{ + TR_CFG_MGR *cfg_mgr = talloc_get_type_abort(cookie, TR_CFG_MGR); + + *response_ptr = tr_cfg_files_to_json_array(cfg_mgr->active); + return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS; +} + + + int main(int argc, char *argv[]) { TALLOC_CTX *main_ctx=NULL; @@ -159,8 +191,12 @@ int main(int argc, char *argv[]) struct cmdline_args opts; struct event_base *ev_base; struct tr_socket_event tids_ev = {0}; + struct event *tids_sweep_ev; + struct tr_socket_event mon_ev = {0}; struct event *cfgwatch_ev; + time_t start_time = time(NULL); /* TODO move this? */ + configure_signals(); /* we're going to be multithreaded, so disable null context tracking */ @@ -199,11 +235,10 @@ int main(int argc, char *argv[]) } /***** initialize the trust path query server instance *****/ - if (NULL == (tr->tids = tids_create())) { + if (NULL == (tr->tids = tids_new(tr))) { tr_crit("Error initializing Trust Path Query Server instance."); return 1; } - talloc_steal(tr, tr->tids); /***** initialize the trust router protocol server instance *****/ if (NULL == (tr->trps = trps_new(tr))) { @@ -211,6 +246,23 @@ int main(int argc, char *argv[]) return 1; } + /***** initialize the monitoring interface instance *****/ + if (NULL == (tr->mons = mons_new(tr))) { + tr_crit("Error initializing monitoring interface instance."); + return 1; + } + /* Monitor our tids/trps instances */ + tr->mons->tids = tr->tids; + tr->mons->trps = tr->trps; + + /* Register monitoring handlers */ + mons_register_handler(tr->mons, MON_CMD_SHOW, OPT_TYPE_SHOW_VERSION, tr_handle_version, NULL); + mons_register_handler(tr->mons, MON_CMD_SHOW, OPT_TYPE_SHOW_CONFIG_FILES, tr_handle_show_cfg_serial, tr->cfg_mgr); + mons_register_handler(tr->mons, MON_CMD_SHOW, OPT_TYPE_SHOW_UPTIME, tr_handle_uptime, &start_time); + mons_register_handler(tr->mons, MON_CMD_SHOW, OPT_TYPE_SHOW_RP_CLIENTS, tr_handle_show_rp_clients, tr->cfg_mgr); + tr_tid_register_mons_handlers(tr->tids, tr->mons); + tr_trp_register_mons_handlers(tr->trps, tr->mons); + /***** process configuration *****/ tr->cfgwatch=tr_cfgwatch_create(tr); if (tr->cfgwatch == NULL) { @@ -244,15 +296,16 @@ int main(int argc, char *argv[]) return 1; } - /*tr_status_event_init();*/ /* install status reporting events */ + /* install monitoring interface events */ + tr_debug("Initializing monitoring interface events."); + if (0 != tr_mons_event_init(ev_base, tr->mons, tr->cfg_mgr, &mon_ev)) { + tr_crit("Error initializing monitoring interface."); + return 1; + } /* install TID server events */ tr_debug("Initializing TID server events."); - if (0 != tr_tids_event_init(ev_base, - tr->tids, - tr->cfg_mgr, - tr->trps, - &tids_ev)) { + if (0 != tr_tids_event_init(ev_base, tr->tids, tr->cfg_mgr, tr->trps, &tids_ev, &tids_sweep_ev)) { tr_crit("Error initializing Trust Path Query Server instance."); return 1; } diff --git a/tr/tr_mon.c b/tr/tr_mon.c new file mode 100644 index 0000000..84ab353 --- /dev/null +++ b/tr/tr_mon.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include +#include +#include +#include + +/* + * Cookie for the event handling callback + */ +struct tr_mons_event_cookie { + MONS_INSTANCE *mons; + TR_CFG_MGR *cfg_mgr; +}; + + +/** + * Callback to handle a triggered event + * + * @param listener file descriptor of the socket that triggered the event + * @param event libevent2 event + * @param arg pointer to our MONS_INSTANCE + */ +static void tr_mons_event_cb(int listener, short event, void *arg) +{ + MONS_INSTANCE *mons = talloc_get_type_abort(arg, MONS_INSTANCE); + + // check that we were not accidentally triggered + if (0==(event & EV_READ)) + tr_debug("tr_mons_event_cb: unexpected event on monitoring interface socket (event=0x%X)", event); + else + mons_accept(mons, listener); +} + + +/** + * Callback to handle an incoming monitoring request + * + * @param mons monitoring interface instance + * @param orig_req incoming request + * @param resp destination for outgoing response + * @param cookie_in cookie from the event handling system + * @return 0 on success + */ +static int tr_mons_req_handler(MONS_INSTANCE *mons, + MON_REQ *orig_req, + MON_RESP *resp, + void *cookie_in) +{ + return -1; +} + +/** + * Callback to authorize a GSS client + * + * @param client_name ? + * @param gss_name GSS name of credential attempting to authorize + * @param cookie_in event cookie + * @return 0 if authorization is successful, -1 if not + */ +static int tr_mons_auth_handler(gss_name_t client_name, TR_NAME *gss_name, void *cookie_in) +{ + struct tr_mons_event_cookie *cookie=talloc_get_type_abort(cookie_in, struct tr_mons_event_cookie); + MONS_INSTANCE *mons = cookie->mons; + + if ((!client_name) || (!gss_name) || (!mons)) { + tr_debug("tr_mons_gss_handler: Bad parameters."); + return -1; + } + + /* Ensure at least one client exists using this GSS name */ + if (! tr_gss_names_matches(mons->authorized_gss_names, gss_name)) { + tr_info("tr_mons_gss_handler: Unauthorized request from %.*s", gss_name->len, gss_name->buf); + return -1; + } + + /* Credential was valid, authorize it */ + tr_info("tr_mons_gss_handler: Authorized request from %.*s", gss_name->len, gss_name->buf); + return 0; +} + + +/* + * + * Get a listener for monitoring requests, returns its socket fd. Accept + * connections with tids_accept() */ + +/** + * Configure the monitoring service instance and set up its event handler + * + * @param base libevent2 event base + * @param mons MONS_INSTANCE for this monitoring interface + * @param cfg_mgr configuration manager instance + * @param mons_ev monitoring interface event instance + * @return 0 on success, nonzero on failure. + * */ +int tr_mons_event_init(struct event_base *base, + MONS_INSTANCE *mons, + TR_CFG_MGR *cfg_mgr, + struct tr_socket_event *mons_ev) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + struct tr_mons_event_cookie *cookie=NULL; + int retval=0; + int ii=0; + + if (mons_ev == NULL) { + tr_debug("tr_mon_event_init: Null mons_ev."); + retval=1; + goto cleanup; + } + + if (cfg_mgr->active->internal->monitoring_port == 0) { + tr_notice("tr_mons_event_init: monitoring is disabled, not enabling events or opening sockets"); + retval = 0; + goto cleanup; + } + + /* Create the cookie for callbacks. We'll put it in the mons context, so it will + * be cleaned up when mons is freed by talloc_free. */ + cookie=talloc(tmp_ctx, struct tr_mons_event_cookie); + if (cookie == NULL) { + tr_debug("tr_mons_event_init: Unable to allocate cookie."); + retval=1; + goto cleanup; + } + cookie->mons=mons; + cookie->cfg_mgr=cfg_mgr; + talloc_steal(mons, cookie); + + /* get a monitoring interface listener */ + mons_ev->n_sock_fd = mons_get_listener(mons, tr_mons_req_handler, + tr_mons_auth_handler, + cfg_mgr->active->internal->hostname, + cfg_mgr->active->internal->monitoring_port, + (void *) cookie, mons_ev->sock_fd, + TR_MAX_SOCKETS); + if (mons_ev->n_sock_fd==0) { + tr_crit("Error opening monitoring interface socket."); + retval=1; + goto cleanup; + } + + /* Set up events */ + for (ii=0; iin_sock_fd; ii++) { + mons_ev->ev[ii]=event_new(base, + mons_ev->sock_fd[ii], + EV_READ|EV_PERSIST, + tr_mons_event_cb, + (void *)mons); + event_add(mons_ev->ev[ii], NULL); + } + +cleanup: + talloc_free(tmp_ctx); + return retval; +} diff --git a/tr/tr_tid.c b/tr/tr_tid.c index c4ac9fb..62722e8 100644 --- a/tr/tr_tid.c +++ b/tr/tr_tid.c @@ -40,14 +40,17 @@ #include #include #include +#include #include #include #include +#include #include #include #include #include #include +#include /* Structure to hold data for the tid response callback */ typedef struct tr_resp_cookie { @@ -183,7 +186,7 @@ static void *tr_tids_req_fwd_thread(void *arg) } /* cookie->resp should now contain our copy of the response */ success=1; - tr_debug("tr_tids_req_fwd_thread: thread %d received response."); + tr_debug("tr_tids_req_fwd_thread: thread %d received response.", cookie->thread_id); cleanup: /* Notify parent thread of the response, if it's still listening. */ @@ -221,15 +224,84 @@ static TID_RC tr_tids_merge_resps(TID_RESP *r1, TID_RESP *r2) if ((r1->result!=TID_SUCCESS) || (r2->result!=TID_SUCCESS)) return TID_ERROR; - if ((0!=tr_name_cmp(r1->rp_realm, r2->rp_realm)) || - (0!=tr_name_cmp(r1->realm, r2->realm)) || - (0!=tr_name_cmp(r1->comm, r2->comm))) - return TID_ERROR; + if ((0 == tr_name_cmp(r1->rp_realm, r2->rp_realm)) + && (0 == tr_name_cmp(r1->realm, r2->realm)) + && ( (0 == tr_name_cmp(r1->comm, r2->comm)) + || (0 == tr_name_cmp(r1->comm, r2->orig_coi)) + || (0 == tr_name_cmp(r1->orig_coi, r2->comm)))) { + + tid_srvr_blk_add(r1->servers, tid_srvr_blk_dup(r1, r2->servers)); + return TID_SUCCESS; + } - tid_srvr_blk_add(r1->servers, tid_srvr_blk_dup(r1, r2->servers)); - return TID_SUCCESS; + return TID_ERROR; } +enum map_coi_result { + MAP_COI_SUCCESS = 0, + MAP_COI_MAP_NOT_REQUIRED, + MAP_COI_ALREADY_MAPPED, + MAP_COI_NO_APC, + MAP_COI_INVALID_APC, + MAP_COI_UNKNOWN_COMM, + MAP_COI_ERROR +}; + +static enum map_coi_result map_coi(TR_COMM_TABLE *ctable, TID_REQ *req) +{ + TR_COMM *orig_comm; + TR_NAME *apc_name; + TR_COMM *apc; + TR_APC *apcs; + + if (tid_req_get_orig_coi(req) != NULL) + return MAP_COI_ALREADY_MAPPED; + + /* look up the community */ + orig_comm = tr_comm_table_find_comm(ctable, tid_req_get_comm(req)); + if (orig_comm == NULL) + return MAP_COI_UNKNOWN_COMM; + + if (tr_comm_get_type(orig_comm) == TR_COMM_APC) + return MAP_COI_MAP_NOT_REQUIRED; /* it was already an APC, no mapping to do */ + + /* use first (only) APC. These are just APC names */ + apcs = tr_comm_get_apcs(orig_comm); + if ((!apcs) || (!tr_apc_get_id(apcs))) + return MAP_COI_NO_APC; + + /* get our own copy of the APC name */ + apc_name = tr_dup_name(tr_apc_get_id(apcs)); + if (apc_name == NULL) { + tr_err("map_coi: Error allocating apc_name"); + return MAP_COI_ERROR; + } + + /* Check that the APC is configured */ + apc = tr_comm_table_find_comm(ctable, apc_name); + if (apc == NULL) { + tr_free_name(apc_name); + return MAP_COI_INVALID_APC; + } + + tid_req_set_orig_coi(req, tid_req_get_comm(req)); /* was null, so no need to free anything */ + tid_req_set_comm(req, apc_name); /* original contents will be freed via orig_coi */ + + return MAP_COI_SUCCESS; /* successfully mapped */ +} + +/** + * Process a TID request + * + * Return value of -1 means to send a TID_ERROR response. Fill in resp->err_msg or it will + * be returned as a generic error. + * + * @param tids + * @param orig_req + * @param resp + * @param cookie_in + * @return + */ static int tr_tids_req_handler(TIDS_INSTANCE *tids, TID_REQ *orig_req, TID_RESP *resp, @@ -245,7 +317,6 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids, TID_RESP *aaa_resp[TR_TID_MAX_AAA_SERVERS]={NULL}; TR_RP_CLIENT *rp_client=NULL; TR_RP_CLIENT_ITER *rpc_iter=NULL; - TR_NAME *apc = NULL; TID_REQ *fwd_req = NULL; TR_COMM *cfg_comm = NULL; TR_COMM *cfg_apc = NULL; @@ -273,7 +344,7 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids, goto cleanup; } - tr_debug("tr_tids_req_handler: Request received (conn = %d)! Realm = %s, Comm = %s", orig_req->conn, + tr_debug("tr_tids_req_handler: Request received (conn = %d)! Realm = %s, Comm = %s", orig_req->conn, orig_req->realm->buf, orig_req->comm->buf); if (orig_req->request_id) tr_debug("tr_tids_req_handler: TID request ID: %.*s", orig_req->request_id->len, orig_req->request_id->buf); @@ -285,14 +356,15 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids, /* Duplicate the request, so we can modify and forward it */ if (NULL == (fwd_req=tid_dup_req(orig_req))) { tr_debug("tr_tids_req_handler: Unable to duplicate request."); - retval=-1; + retval=-1; /* response will be a generic internal error */ goto cleanup; } talloc_steal(tmp_ctx, fwd_req); + /* cfg_comm is now the community (APC or CoI) of the incoming request */ if (NULL == (cfg_comm=tr_comm_table_find_comm(cfg_mgr->active->ctable, orig_req->comm))) { tr_notice("tr_tids_req_hander: Request for unknown comm: %s.", orig_req->comm->buf); - tids_send_err_response(tids, orig_req, "Unknown community"); + tid_resp_set_err_msg(resp, tr_new_name("Unknown community")); retval=-1; goto cleanup; } @@ -305,7 +377,7 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids, if (!tids->gss_name) { tr_notice("tr_tids_req_handler: No GSS name for incoming request."); - tids_send_err_response(tids, orig_req, "No GSS name for request"); + tid_resp_set_err_msg(resp, tr_new_name("No GSS name for request")); retval=-1; goto cleanup; } @@ -317,7 +389,7 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids, target=tr_filter_target_tid_req(tmp_ctx, orig_req); if (target==NULL) { tr_crit("tid_req_handler: Unable to allocate filter target, cannot apply filter!"); - tids_send_err_response(tids, orig_req, "Incoming TID request filter error"); + tid_resp_set_err_msg(resp, tr_new_name("Incoming TID request filter error")); retval=-1; goto cleanup; } @@ -346,82 +418,96 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids, /* We get here whether or not a filter matched. If tr_filter_apply() doesn't match, it returns * a default action of reject, so we don't have to check why we exited the loop. */ if (oaction != TR_FILTER_ACTION_ACCEPT) { - tr_notice("tr_tids_req_handler: Incoming TID request rejected by filter for GSS name", orig_req->rp_realm->buf); - tids_send_err_response(tids, orig_req, "Incoming TID request filter error"); + tr_notice("tr_tids_req_handler: Incoming TID request rejected by RP client filter for GSS name %.*s", + tids->gss_name->len, tids->gss_name->buf); + tid_resp_set_err_msg(resp, tr_new_name("Incoming TID request filter error")); retval = -1; goto cleanup; } /* Check that the rp_realm is a member of the community in the request */ if (NULL == tr_comm_find_rp(cfg_mgr->active->ctable, cfg_comm, orig_req->rp_realm)) { - tr_notice("tr_tids_req_handler: RP Realm (%s) not member of community (%s).", orig_req->rp_realm->buf, orig_req->comm->buf); - tids_send_err_response(tids, orig_req, "RP COI membership error"); + tr_notice("tr_tids_req_handler: RP Realm (%s) not member of community (%s).", + orig_req->rp_realm->buf, orig_req->comm->buf); + tid_resp_set_err_msg(resp, tr_new_name("RP community membership error")); retval=-1; goto cleanup; } - /* Map the comm in the request from a COI to an APC, if needed */ - if (TR_COMM_COI == cfg_comm->type) { - if (orig_req->orig_coi!=NULL) { - tr_notice("tr_tids_req_handler: community %s is COI but COI to APC mapping already occurred. Dropping request.", - orig_req->comm->buf); - tids_send_err_response(tids, orig_req, "Second COI to APC mapping would result, permitted only once."); - retval=-1; - goto cleanup; - } - - tr_debug("tr_tids_req_handler: Community was a COI, switching."); - /* TBD -- In theory there can be more than one? How would that work? */ - if ((!cfg_comm->apcs) || (!cfg_comm->apcs->id)) { - tr_notice("No valid APC for COI %s.", orig_req->comm->buf); - tids_send_err_response(tids, orig_req, "No valid APC for community"); - retval=-1; + switch(map_coi(cfg_mgr->active->ctable, fwd_req)) { + case MAP_COI_MAP_NOT_REQUIRED: + cfg_apc = cfg_comm; + break; + + case MAP_COI_SUCCESS: + cfg_apc = tr_comm_table_find_comm(cfg_mgr->active->ctable, tid_req_get_comm(fwd_req)); + tr_debug("tr_tids_req_handler: Community %.*s is a COI, mapping to APC %.*s.", + tid_req_get_orig_coi(fwd_req)->len, tid_req_get_orig_coi(fwd_req)->buf, + tr_comm_get_id(cfg_apc)->len, tr_comm_get_id(cfg_apc)->buf); + break; + + case MAP_COI_ALREADY_MAPPED: + tr_notice("tr_tids_req_handler: community %.*s is COI but COI to APC mapping already occurred. Dropping request.", + tid_req_get_comm(orig_req)->len, tid_req_get_comm(orig_req)->buf); + tid_resp_set_err_msg(resp, tr_new_name("Second COI to APC mapping would result, permitted only once.")); + retval = -1; goto cleanup; - } - apc = tr_dup_name(cfg_comm->apcs->id); - /* Check that the APC is configured */ - if (NULL == (cfg_apc = tr_comm_table_find_comm(cfg_mgr->active->ctable, apc))) { - tr_notice("tr_tids_req_hander: Request for unknown comm: %s.", apc->buf); - tids_send_err_response(tids, orig_req, "Unknown APC"); - retval=-1; + case MAP_COI_NO_APC: + tr_notice("No valid APC for COI %.*s.", + tid_req_get_comm(orig_req)->len, tid_req_get_comm(orig_req)->buf); + tid_resp_set_err_msg(resp, tr_new_name("No valid APC for community")); + retval = -1; goto cleanup; - } - fwd_req->comm = apc; - fwd_req->orig_coi = orig_req->comm; + case MAP_COI_INVALID_APC: + tr_notice("tr_tids_req_hander: Request for unknown APC."); + tid_resp_set_err_msg(resp, tr_new_name("Unknown APC")); + retval = -1; + goto cleanup; - /* Check that rp_realm is a member of this APC */ - if (NULL == (tr_comm_find_rp(cfg_mgr->active->ctable, cfg_apc, orig_req->rp_realm))) { - tr_notice("tr_tids_req_hander: RP Realm (%s) not member of community (%s).", orig_req->rp_realm->buf, orig_req->comm->buf); - tids_send_err_response(tids, orig_req, "RP APC membership error"); - retval=-1; + default: + tr_notice("tr_tids_req_hander: Unexpected error mapping COI to APC."); + retval = -1; goto cleanup; - } } - /* Look up the route for this community/realm. */ + /* cfg_comm is now the original community, and cfg_apc is the APC it belongs to. These + * may both be the same. If not, check that rp_realm is a member of the mapped APC */ + if ((cfg_apc != cfg_comm) + && (NULL == tr_comm_find_rp(cfg_mgr->active->ctable, + cfg_apc, + tid_req_get_rp_realm(fwd_req)))) { + tr_notice("tr_tids_req_hander: RP Realm (%.*s) not member of mapped APC (%.*s).", + tid_req_get_rp_realm(fwd_req)->len, tid_req_get_rp_realm(fwd_req)->buf, + tr_comm_get_id(cfg_apc)->len, tr_comm_get_id(cfg_apc)->buf); + tid_resp_set_err_msg(resp, tr_new_name("RP community membership error")); + retval=-1; + goto cleanup; + } + + /* Look up the route for forwarding request's community/realm. */ tr_debug("tr_tids_req_handler: looking up route."); - route=trps_get_selected_route(trps, orig_req->comm, orig_req->realm); + route=trps_get_selected_route(trps, fwd_req->comm, fwd_req->realm); if (route==NULL) { /* No route. Use default AAA servers if we have them. */ - tr_debug("tr_tids_req_handler: No route for realm %s, defaulting.", orig_req->realm->buf); + tr_debug("tr_tids_req_handler: No route for realm %s, defaulting.", fwd_req->realm->buf); if (NULL == (aaa_servers = tr_default_server_lookup(cfg_mgr->active->default_servers, - orig_req->comm))) { + fwd_req->comm))) { tr_notice("tr_tids_req_handler: No default AAA servers, discarded."); - tids_send_err_response(tids, orig_req, "No path to AAA Server(s) for realm"); + tid_resp_set_err_msg(resp, tr_new_name("No path to AAA Server(s) for realm")); retval = -1; goto cleanup; } idp_shared = 0; } else { - /* Found a route. Determine the AAA servers or next hop address. */ + /* Found a route. Determine the AAA servers or next hop address for the request we are forwarding. */ tr_debug("tr_tids_req_handler: found route."); if (trp_route_is_local(route)) { tr_debug("tr_tids_req_handler: route is local."); aaa_servers = tr_idp_aaa_server_lookup(cfg_mgr->active->ctable->idp_realms, - orig_req->realm, - orig_req->comm, + fwd_req->realm, + fwd_req->comm, &idp_shared); } else { tr_debug("tr_tids_req_handler: route not local."); @@ -429,38 +515,39 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids, idp_shared = 0; } - /* Since we aren't defaulting, check idp coi and apc membership */ - if (NULL == (tr_comm_find_idp(cfg_mgr->active->ctable, cfg_comm, fwd_req->realm))) { - tr_notice("tr_tids_req_handler: IDP Realm (%s) not member of community (%s).", orig_req->realm->buf, orig_req->comm->buf); - tids_send_err_response(tids, orig_req, "IDP community membership error"); + /* Since we aren't defaulting, check idp coi and apc membership of the original request */ + if (NULL == (tr_comm_find_idp(cfg_mgr->active->ctable, cfg_comm, orig_req->realm))) { + tr_notice("tr_tids_req_handler: IDP Realm (%s) not member of community (%s).", orig_req->realm->buf, cfg_comm->id->buf); + tid_resp_set_err_msg(resp, tr_new_name("IDP community membership error")); retval=-1; goto cleanup; } - if ( cfg_apc && (NULL == (tr_comm_find_idp(cfg_mgr->active->ctable, cfg_apc, fwd_req->realm)))) { - tr_notice("tr_tids_req_handler: IDP Realm (%s) not member of APC (%s).", orig_req->realm->buf, orig_req->comm->buf); - tids_send_err_response(tids, orig_req, "IDP APC membership error"); + if ( cfg_apc && (NULL == (tr_comm_find_idp(cfg_mgr->active->ctable, cfg_apc, orig_req->realm)))) { + tr_notice("tr_tids_req_handler: IDP Realm (%s) not member of APC (%s).", orig_req->realm->buf, cfg_apc->id->buf); + tid_resp_set_err_msg(resp, tr_new_name("IDP APC membership error")); retval=-1; goto cleanup; } } - /* Make sure we came through with a AAA server. If not, we can't handle the request. */ + /* Make sure we came through with a AAA server. If not, we can't handle the request. + * Report using the original request, not translated values. */ if (NULL == aaa_servers) { tr_notice("tr_tids_req_handler: no route or AAA server for realm (%s) in community (%s).", orig_req->realm->buf, orig_req->comm->buf); - tids_send_err_response(tids, orig_req, "Missing trust route error"); + tid_resp_set_err_msg(resp, tr_new_name("Missing trust route error")); retval = -1; goto cleanup; } /* send a TID request to the AAA server(s), and get the answer(s) */ tr_debug("tr_tids_req_handler: sending TID request(s)."); - if (cfg_apc) - expiration_interval = cfg_apc->expiration_interval; - else expiration_interval = cfg_comm->expiration_interval; + /* Use the smaller of the APC's expiration interval and the expiration interval of the incoming request */ + expiration_interval = cfg_apc->expiration_interval; if (fwd_req->expiration_interval) fwd_req->expiration_interval = (expiration_interval < fwd_req->expiration_interval) ? expiration_interval : fwd_req->expiration_interval; - else fwd_req->expiration_interval = expiration_interval; + else + fwd_req->expiration_interval = expiration_interval; /* Set up message queue for replies from req forwarding threads */ mq=tr_mq_new(tmp_ctx); @@ -613,13 +700,19 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids, } if (n_responses==0) { - /* No requests succeeded. Forward an error if we got any error responses. */ + /* No requests succeeded, so this will be an error */ + retval = -1; + + /* If we got any error responses, send an arbitrarily chosen one. */ for (ii=0; iin_sock_fd=tids_get_listener(tids, - tr_tids_req_handler, - tr_tids_gss_handler, - cfg_mgr->active->internal->hostname, - cfg_mgr->active->internal->tids_port, - (void *)cookie, - tids_ev->sock_fd, - TR_MAX_SOCKETS); + tids_ev->n_sock_fd = (int)tids_get_listener(tids, + tr_tids_req_handler, + tr_tids_gss_handler, + cfg_mgr->active->internal->hostname, + cfg_mgr->active->internal->tids_port, + (void *)cookie, + tids_ev->sock_fd, + TR_MAX_SOCKETS); if (tids_ev->n_sock_fd==0) { tr_crit("Error opening TID server socket."); retval=1; goto cleanup; } - /* Set up events */ + /* Set up listener events */ for (ii=0; iin_sock_fd; ii++) { tids_ev->ev[ii]=event_new(base, tids_ev->sock_fd[ii], @@ -727,6 +835,12 @@ int tr_tids_event_init(struct event_base *base, event_add(tids_ev->ev[ii], NULL); } + /* Set up a periodic check for completed TID handler processes */ + *sweep_ev = event_new(base, -1, EV_TIMEOUT|EV_PERSIST, tr_tids_sweep_cb, tids); + sweep_interval.tv_sec = 10; + sweep_interval.tv_usec = 0; + event_add(*sweep_ev, &sweep_interval); + cleanup: talloc_free(tmp_ctx); return retval; diff --git a/tr/tr_tid_mons.c b/tr/tr_tid_mons.c new file mode 100644 index 0000000..0a18d2d --- /dev/null +++ b/tr/tr_tid_mons.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* Monitoring handlers for trust router TID server */ + +#include +#include +#include +#include +#include + +/** + * Get the count of completed TID requests + */ +static MON_RC handle_show_req_count(void *cookie, json_t **response_ptr) +{ + TIDS_INSTANCE *tids = talloc_get_type_abort(cookie, TIDS_INSTANCE); + *response_ptr = json_integer(tids->req_count); + return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS; +} + +static MON_RC handle_show_req_err_count(void *cookie, json_t **response_ptr) +{ + TIDS_INSTANCE *tids = talloc_get_type_abort(cookie, TIDS_INSTANCE); + *response_ptr = json_integer(tids->error_count); + return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS; +} + +static MON_RC handle_show_req_pending(void *cookie, json_t **response_ptr) +{ + TIDS_INSTANCE *tids = talloc_get_type_abort(cookie, TIDS_INSTANCE); + *response_ptr = json_integer(tids->pids->len); + return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS; +} + +void tr_tid_register_mons_handlers(TIDS_INSTANCE *tids, MONS_INSTANCE *mons) +{ + mons_register_handler(mons, + MON_CMD_SHOW, OPT_TYPE_SHOW_TID_REQ_COUNT, + handle_show_req_count, tids); + mons_register_handler(mons, + MON_CMD_SHOW, OPT_TYPE_SHOW_TID_REQ_ERR_COUNT, + handle_show_req_err_count, tids); + mons_register_handler(mons, + MON_CMD_SHOW, OPT_TYPE_SHOW_TID_REQ_PENDING, + handle_show_req_pending, tids); +} diff --git a/tr/tr_trp.c b/tr/tr_trp.c index 298299e..e075d58 100644 --- a/tr/tr_trp.c +++ b/tr/tr_trp.c @@ -47,7 +47,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -870,6 +872,20 @@ void tr_config_changed(TR_CFG *new_cfg, void *cookie) tr->cfgwatch->settling_time.tv_sec=new_cfg->internal->cfg_settling_time; tr->cfgwatch->settling_time.tv_usec=0; + /* These need to be updated */ + tr->tids->hostname = new_cfg->internal->hostname; + tr->mons->hostname = new_cfg->internal->hostname; + + /* Update the authorized monitoring gss names */ + if (tr->mons->authorized_gss_names) { + tr_debug("tr_config_changed: freeing tr->mons->authorized_gss_names"); + tr_gss_names_free(tr->mons->authorized_gss_names); + } + tr->mons->authorized_gss_names = tr_gss_names_dup(tr->mons, new_cfg->internal->monitoring_credentials); + if (tr->mons->authorized_gss_names == NULL) { + tr_err("tr_config_changed: Error configuring monitoring credentials"); + } + trps_set_connect_interval(trps, new_cfg->internal->trp_connect_interval); trps_set_update_interval(trps, new_cfg->internal->trp_update_interval); trps_set_sweep_interval(trps, new_cfg->internal->trp_sweep_interval); diff --git a/tr/tr_trp_mons.c b/tr/tr_trp_mons.c new file mode 100644 index 0000000..354d1c8 --- /dev/null +++ b/tr/tr_trp_mons.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* Monitoring handlers for trust router TID server */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static MON_RC handle_show_routes(void *cookie, json_t **response_ptr) +{ + TRPS_INSTANCE *trps = talloc_get_type_abort(cookie, TRPS_INSTANCE); + + *response_ptr = trp_rtable_to_json(trps->rtable); + return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS; +} + +static MON_RC handle_show_peers(void *cookie, json_t **response_ptr) +{ + TRPS_INSTANCE *trps = talloc_get_type_abort(cookie, TRPS_INSTANCE); + + *response_ptr = trp_ptable_to_json(trps->ptable); + return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS; +} + +static MON_RC handle_show_communities(void *cookie, json_t **response_ptr) +{ + TRPS_INSTANCE *trps = talloc_get_type_abort(cookie, TRPS_INSTANCE); + + *response_ptr = tr_comm_table_to_json(trps->ctable); + return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS; +} + +static MON_RC handle_show_realms(void *cookie, json_t **response_ptr) +{ + TRPS_INSTANCE *trps = talloc_get_type_abort(cookie, TRPS_INSTANCE); + + *response_ptr = tr_idp_realms_to_json(trps->ctable->idp_realms); + return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS; +} + +void tr_trp_register_mons_handlers(TRPS_INSTANCE *trps, MONS_INSTANCE *mons) +{ + mons_register_handler(mons, + MON_CMD_SHOW, OPT_TYPE_SHOW_ROUTES, + handle_show_routes, trps); + mons_register_handler(mons, + MON_CMD_SHOW, OPT_TYPE_SHOW_PEERS, + handle_show_peers, trps); + mons_register_handler(mons, + MON_CMD_SHOW, OPT_TYPE_SHOW_COMMUNITIES, + handle_show_communities, trps); + mons_register_handler(mons, + MON_CMD_SHOW, OPT_TYPE_SHOW_REALMS, + handle_show_realms, trps); +} diff --git a/tr/trmon_main.c b/tr/trmon_main.c new file mode 100644 index 0000000..e6d4253 --- /dev/null +++ b/tr/trmon_main.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2012-2018, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include + +#include +#include + + +/* command-line option setup */ +static void print_version_info(void) +{ + printf("Moonshot TR Monitoring Client %s\n\n", PACKAGE_VERSION); +} + + +/* argp global parameters */ +const char *argp_program_bug_address=PACKAGE_BUGREPORT; /* bug reporting address */ + +/* doc strings */ +static const char doc[]=PACKAGE_NAME " - Moonshot TR Monitoring Client"; +static const char arg_doc[]=" [