From: Jennifer Richards Date: Thu, 12 Jan 2017 21:53:15 +0000 (-0500) Subject: Merge branch 'master' into debian X-Git-Url: http://www.project-moonshot.org/gitweb/?a=commitdiff_plain;h=af8bf19f43ae56252a02d67a60d0ac0d847802fb;hp=ec6ee7b405b20f7d7f67be1c725f336a4106426f;p=trust_router.git Merge branch 'master' into debian --- diff --git a/Makefile.am b/Makefile.am index 3b434c3..4c32c24 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ DISTCHECK_CONFIGURE_FLAGS = \ --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) -bin_PROGRAMS= tr/trust_router tr/trpc tid/example/tidc tid/example/tids common/dh_test/tr_dh_test common/mq_test/mq_test common/mq_test/thread_test trp/msgtst trp/test/rtbl_test trp/test/ptbl_test common/cfg_test/cfg_test +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 AM_CPPFLAGS=-I$(srcdir)/include $(GLIB_CFLAGS) AM_CFLAGS = -Wall -Werror=missing-prototypes -Werror -Wno-parentheses $(GLIB_CFLAGS) SUBDIRS = gsscon @@ -10,7 +10,13 @@ common_srcs = common/tr_name.c \ common/tr_msg.c \ common/tr_dh.c \ common/tr_debug.c \ - common/tr_util.c + common/tr_util.c \ + common/tr_apc.c \ + common/tr_comm.c \ + common/tr_rp.c \ + common/tr_idp.c \ + common/tr_filter.c \ + common/tr_gss.c tid_srcs = tid/tid_resp.c \ tid/tid_req.c \ @@ -24,18 +30,7 @@ trp/trp_ptable.c \ trp/trp_rtable.c \ trp/trp_req.c \ trp/trp_upd.c \ -tid/tid_resp.c \ -tid/tid_req.c \ -common/tr_gss.c \ common/tr_config.c \ -common/tr_idp.c \ -common/tr_apc.c \ -common/tr_comm.c \ -common/tr_filter.c \ -common/tr_rp.c \ -common/tr_msg.c \ -common/tr_constraint.c \ -common/tr_name.c \ common/tr_mq.c check_PROGRAMS = common/t_constraint @@ -50,7 +45,7 @@ trp/trp_upd.c libtr_tid_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden libtr_tid_la_LIBADD = gsscon/libgsscon.la $(GLIB_LIBS) -libtr_tid_la_LDFLAGS = $(AM_LDFLAGS) -version-info 2 -no-undefined +libtr_tid_la_LDFLAGS = $(AM_LDFLAGS) -version-info 3:0:1 -no-undefined common_t_constraint_SOURCES = common/t_constraint.c \ common/tr_debug.c \ @@ -68,26 +63,28 @@ tr/tr_event.c \ tr/tr_cfgwatch.c \ tr/tr_tid.c \ tr/tr_trp.c \ -$(trp_srcs) +$(tid_srcs) \ +$(trp_srcs) \ +$(common_srcs) -tr_trust_router_CFLAGS = $(AM_CFLAGS) -pthread tr_trust_router_LDFLAGS = $(AM_LDFLAGS) -levent_pthreads -pthread -tr_trust_router_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS) - +tr_trust_router_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) tr_trpc_SOURCES =tr/trpc_main.c \ tr/tr_trp.c \ -$(trp_srcs) -tr_trpc_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS) +$(trp_srcs) \ +$(tid_srcs) \ +$(common_srcs) + +tr_trpc_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) tr_trpc_LDFLAGS = $(AM_LDFLAGS) -pthread -tr_trpc_CFLAGS = $(AM_CFLAGS) -pthread trp_msgtst_SOURCES = trp/msgtst.c \ -common/tr_msg.c \ +$(common_srcs) \ trp/trp_req.c \ trp/trp_upd.c \ tid/tid_resp.c \ tid/tid_req.c -trp_msgtst_LDADD = libtr_tid.la $(GLIB_LIBS) +trp_msgtst_LDADD = $(GLIB_LIBS) trp_test_rtbl_test_SOURCES = trp/test/rtbl_test.c \ common/tr_name.c \ @@ -97,41 +94,58 @@ trp/trp_rtable.c trp_test_rtbl_test_LDADD = $(GLIB_LIBS) trp_test_ptbl_test_SOURCES = trp/test/ptbl_test.c \ -$(trp_srcs) -trp_test_ptbl_test_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS) - -tid_example_tidc_SOURCES = tid/example/tidc_main.c -tid_example_tidc_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS) - -tid_example_tids_SOURCES = tid/example/tids_main.c -tid_example_tids_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS) - -common_dh_test_tr_dh_test_SOURCES = common/tr_dh.c \ +$(tid_srcs) \ +$(trp_srcs) \ +$(common_srcs) +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 \ +$(tid_srcs) \ +$(trp_srcs) \ +$(common_srcs) +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 \ +$(tid_srcs) \ +$(trp_srcs) \ +$(common_srcs) +tid_example_tids_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) +tid_example_tids_LDFLAGS = $(AM_LDFLAGS) -pthread + +common_tests_tr_dh_test_SOURCES = common/tr_dh.c \ common/tr_debug.c \ -common/dh_test/dh_test.c +common/tests/dh_test.c -common_mq_test_mq_test_SOURCES = common/tr_mq.c \ -common/mq_test/mq_test.c +common_tests_mq_test_SOURCES = common/tr_mq.c \ +common/tests/mq_test.c \ +common/tr_debug.c -common_mq_test_mq_test_CFLAGS = -pthread -common_mq_test_mq_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc +common_tests_mq_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread -common_cfg_test_cfg_test_SOURCES = common/cfg_test/cfg_test.c \ +common_tests_cfg_test_SOURCES = common/tests/cfg_test.c \ +$(common_srcs) \ +$(tid_srcs) \ $(trp_srcs) +common_tests_cfg_test_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) +common_tests_cfg_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread -common_cfg_test_cfg_test_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS) -common_cfg_test_cfg_test_CFLAGS = -pthread -common_cfg_test_cfg_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc - -common_mq_test_thread_test_SOURCES = common/tr_mq.c \ +common_tests_thread_test_SOURCES = common/tr_mq.c \ common/tr_debug.c \ -common/mq_test/thread_test.c +common/tests/thread_test.c + +common_tests_commtest_SOURCES = common/tests/commtest.c \ +$(common_srcs) \ +$(tid_srcs) \ +$(trp_srcs) +common_tests_commtest_LDADD = gsscon/libgsscon.la $(GLIB_LIBS) +common_tests_commtest_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread -common_mq_test_thread_test_CFLAGS = -pthread -common_mq_test_thread_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc +common_tests_thread_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread pkginclude_HEADERS = include/trust_router/tid.h include/trust_router/tr_name.h \ - include/tr_debug.h \ + include/tr_debug.h include/trust_router/trp.h \ include/trust_router/tr_dh.h \ include/trust_router/tr_constraint.h \ include/trust_router/tr_versioning.h @@ -141,16 +155,19 @@ noinst_HEADERS = include/gsscon.h include/tr_config.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/tid_internal.h + include/tr_filter.h include/tr_gss.h \ + include/tid_internal.h include/trp_internal.h \ + include/tr_cfgwatch.h include/tr_event.h \ + include/tr_mq.h include/trp_ptable.h \ + include/trp_rtable.h include/tr_util.h pkgdata_DATA=schema.sql -nobase_dist_pkgdata_DATA=redhat/init redhat/sysconfig redhat/trusts.cfg redhat/tidc-wrapper redhat/trust_router-wrapper redhat/tr-test-main.cfg redhat/default-main.cfg redhat/tids-wrapper redhat/sysconfig.tids +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 if HAVE_SYSTEMD systemdsystemunit_DATA = tids.service endif EXTRA_DIST = trust_router.spec common/tests.json schema.sql tids.service \ - tr/manual.cfg tr/portal.cfg \ + tr/internal.cfg tr/organizations.cfg \ redhat/tids.init diff --git a/common/cfg_test/idp.cfg b/common/cfg_test/idp.cfg deleted file mode 100644 index 0bfc87f..0000000 --- a/common/cfg_test/idp.cfg +++ /dev/null @@ -1,17 +0,0 @@ -{ - "local_organizations": [ - {"organization_name": "idp.cfg test org", - "realms": [ - {"realm": "A.idp.cfg", - "identity_provider": { - "aaa_servers": ["rad1.A.idp.cfg", - "rad2.A.idp.cfg"], - "apc": "apc.example.com", - "shared_config": "no" - }, - "gss_names": ["gss@example.com"] - } - ] - } - ] -} diff --git a/common/cfg_test/cfg_test.c b/common/tests/cfg_test.c similarity index 91% rename from common/cfg_test/cfg_test.c rename to common/tests/cfg_test.c index d8a0117..f487c53 100644 --- a/common/cfg_test/cfg_test.c +++ b/common/tests/cfg_test.c @@ -51,16 +51,15 @@ static int verify_idp_cfg(TR_CFG *cfg) { TR_COMM *comm=NULL; TR_NAME *name=NULL; - TR_APC *apc=NULL; TR_IDP_REALM *idp_realm=NULL; TR_AAA_SERVER *aaa=NULL; assert(cfg!=NULL); /* test the comms attribute */ - assert(cfg->comms!=NULL); + assert(cfg->ctable!=NULL); name=tr_new_name("apc.example.com"); - comm=tr_comm_lookup(cfg->comms, name); + comm=tr_comm_table_find_comm(cfg->ctable, name); tr_free_name(name); assert(comm!=NULL); @@ -69,9 +68,8 @@ static int verify_idp_cfg(TR_CFG *cfg) assert(comm->apcs==NULL); name=tr_new_name("A.idp.cfg"); - for (idp_realm=comm->idp_realms; - (idp_realm!=NULL) && (tr_name_cmp(name, idp_realm->realm_id)!=0); - idp_realm=idp_realm->comm_next) { } + assert(name!=NULL); + idp_realm=tr_comm_find_idp(cfg->ctable, comm, name); assert(idp_realm!=NULL); assert(idp_realm->shared_config==0); assert(idp_realm->origin==TR_REALM_LOCAL); @@ -107,14 +105,13 @@ static int verify_rp_cfg(TR_CFG *cfg) assert(cfg->rp_clients!=NULL); assert(cfg->rp_clients->next==NULL); assert(cfg->rp_clients->comm_next==NULL); - /* need to update next test to use TR_GSS_NAMES structure */ -#if 0 + + assert(cfg->rp_clients->gss_names!=NULL); for (ii=1; iirp_clients->gss_names[ii]==NULL); - assert(cfg->rp_clients->gss_names[0]!=NULL); + assert(cfg->rp_clients->gss_names->names[ii]==NULL); + assert(cfg->rp_clients->gss_names->names[0]!=NULL); name=tr_new_name("gss@example.com"); - assert(tr_name_cmp(name, cfg->rp_clients->gss_names[0])==0); -#endif + assert(tr_name_cmp(name, cfg->rp_clients->gss_names->names[0])==0); return 0; } diff --git a/common/tests/commtest.c b/common/tests/commtest.c new file mode 100644 index 0000000..34153cb --- /dev/null +++ b/common/tests/commtest.c @@ -0,0 +1,838 @@ +#include +#include +#include + +#include +#include +#include +#include + +/**********************************************************************/ +/* APC test stuff */ +struct apc_entry { + const char *id; /* only allows a single entry for now */ +}; + +static TR_APC *apc_entry_to_apc(TALLOC_CTX *mem_ctx, struct apc_entry *ae) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_APC *apcs=NULL; + + apcs=tr_apc_new(tmp_ctx); + if (apcs!=NULL) { + tr_apc_set_id(apcs, tr_new_name(ae->id)); + talloc_steal(mem_ctx, apcs); + } + + talloc_free(tmp_ctx); + return apcs; +} + +/**********************************************************************/ +/* TR_COMM test stuff */ + +struct comm_entry { + const char *id; + TR_COMM_TYPE type; + struct apc_entry *apcs; +}; + +static TR_COMM *comm_entry_to_comm(TALLOC_CTX *mem_ctx, struct comm_entry *ce) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_COMM *comm=tr_comm_new(tmp_ctx); + + if (comm!=NULL) { + tr_comm_set_id(comm, tr_new_name(ce->id)); + tr_comm_set_type(comm, ce->type); + if (ce->apcs!=NULL) + tr_comm_set_apcs(comm, apc_entry_to_apc(tmp_ctx, ce->apcs)); + + if ((tr_comm_get_id(comm)==NULL) || + ((ce->apcs!=NULL)&&(tr_comm_get_apcs(comm)==NULL))) + comm=NULL; /* it's in tmp_ctx, so will be freed */ + else + talloc_steal(mem_ctx, comm); + } + + talloc_free(tmp_ctx); + return comm; +} + +static int add_comm_set(TR_COMM_TABLE *ctab, struct comm_entry *entries) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + struct comm_entry *this=NULL; + size_t ii=0; + TR_COMM *new=NULL; + int rc=-1; + + for (this=entries,ii=0; this->id!=NULL; this++, ii++) { + new=comm_entry_to_comm(tmp_ctx, this); + if (new==NULL) { + printf("Error creating community %u.\n", (unsigned int)ii+1); + rc=1; + goto cleanup; + } + tr_comm_table_add_comm(ctab, new); + } + /* success */ + rc=0; + +cleanup: + talloc_free(tmp_ctx); + return rc; +} + +static int verify_comm_set(TR_COMM_TABLE *ctab, struct comm_entry *entries) +{ + struct comm_entry *this=NULL; + TR_COMM *comm=NULL; + TR_NAME *this_id=NULL; + + for (this=entries; this->id!=NULL; this++) { + this_id=tr_new_name(this->id); + comm=tr_comm_table_find_comm(ctab, this_id); + tr_free_name(this_id); this_id=NULL; + + if (comm==NULL) { + printf("Error, community %s missing from community table.\n", this->id); + return -1; + } + if (tr_comm_get_type(comm)!=this->type) { + printf("Error, community %s has wrong type (was %s, expected %s).\n", + this->id, + tr_comm_type_to_str(tr_comm_get_type(comm)), + tr_comm_type_to_str(this->type)); + return -1; + } + /* TODO: verify apcs */ + } + return 0; +} + +/* removes entry n from ctab */ +static int remove_comm_set_member(TR_COMM_TABLE *ctab, struct comm_entry *entries, size_t n) +{ + TR_NAME *comm_name=tr_new_name(entries[n].id); + TR_COMM *comm=tr_comm_table_find_comm(ctab, comm_name); + TR_COMM *comm2=NULL; + + if (comm==NULL) { + printf("Can't remove community %s, not in table.\n", entries[n].id); + tr_free_name(comm_name); + return 1; + } + + tr_comm_table_remove_comm(ctab, comm); + comm2=tr_comm_table_find_comm(ctab, comm_name); + if (comm2!=NULL) { + printf("Community %s still in table after removal.\n", entries[n].id); + tr_comm_free(comm); + tr_free_name(comm_name); + return 2; + } + + tr_comm_free(comm); + tr_free_name(comm_name); + return 0; +} + +/**********************************************************************/ +/* TR_RP_REALM test stuff */ + +struct rp_realm_entry { + const char *id; +}; + +static TR_RP_REALM *rp_realm_entry_to_rp_realm(TALLOC_CTX *mem_ctx, struct rp_realm_entry *re) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_RP_REALM *realm=NULL; + + realm=tr_rp_realm_new(tmp_ctx); + if (realm!=NULL) { + tr_rp_realm_set_id(realm, tr_new_name(re->id)); + talloc_steal(mem_ctx, realm); + } + + talloc_free(tmp_ctx); + return realm; +} + +static int add_rp_realm_set(TR_COMM_TABLE *ctab, struct rp_realm_entry *entries) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + struct rp_realm_entry *this=NULL; + TR_RP_REALM *realm=NULL; + int rc=-1; + + for (this=entries; this->id!=NULL; this++) { + realm=rp_realm_entry_to_rp_realm(tmp_ctx, this); + if (realm==NULL) { + printf("Error creating RP realm %s.\n", this->id); + rc=1; + goto cleanup; + } + tr_comm_table_add_rp_realm(ctab, realm); + } + rc=0; + +cleanup: + talloc_free(tmp_ctx); + return rc; +} + +static int verify_rp_realm_set(TR_COMM_TABLE *ctab, struct rp_realm_entry *entries) +{ + struct rp_realm_entry *this=NULL; + TR_RP_REALM *rp_realm=NULL; + TR_NAME *this_id=NULL; + + for (this=entries; this->id!=NULL; this++) { + this_id=tr_new_name(this->id); + rp_realm=tr_comm_table_find_rp_realm(ctab, this_id); + tr_free_name(this_id); this_id=NULL; + + if (rp_realm==NULL) { + printf("Error, RP realm %s missing from community table.\n", this->id); + return -1; + } + } + return 0; +} + +/* removes entry n from ctab */ +static int remove_rp_realm_set_member(TR_COMM_TABLE *ctab, struct rp_realm_entry *entries, size_t n) +{ + TR_NAME *rp_realm_name=tr_new_name(entries[n].id); + TR_RP_REALM *rp_realm=tr_comm_table_find_rp_realm(ctab, rp_realm_name); + TR_RP_REALM *rp_realm2=NULL; + + if (rp_realm==NULL) { + printf("Can't remove RP realm %s, not in table.\n", entries[n].id); + tr_free_name(rp_realm_name); + return 1; + } + + tr_comm_table_remove_rp_realm(ctab, rp_realm); + rp_realm2=tr_comm_table_find_rp_realm(ctab, rp_realm_name); + if (rp_realm2!=NULL) { + printf("RP realm %s still in table after removal.\n", entries[n].id); + tr_rp_realm_free(rp_realm); + tr_free_name(rp_realm_name); + return 2; + } + + tr_rp_realm_free(rp_realm); + tr_free_name(rp_realm_name); + return 0; +} + +/**********************************************************************/ +/* TR_AAA_SERVER test stuff */ + +struct aaa_entry { + const char *hostname; /* only supports one for testing right now */ +}; + +static TR_AAA_SERVER *aaa_entry_to_aaa_server(TALLOC_CTX *mem_ctx, struct aaa_entry *ae) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_AAA_SERVER *aaa=tr_aaa_server_new(tmp_ctx, tr_new_name(ae->hostname)); + + if ((aaa==NULL) || (aaa->hostname==NULL)) + aaa=NULL; + else + talloc_steal(mem_ctx, aaa); + + talloc_free(tmp_ctx); + return aaa; +} + + +/**********************************************************************/ +/* TR_IDP_REALM test stuff */ + +struct idp_realm_entry { + const char *id; + struct aaa_entry *aaa_servers; + struct apc_entry *apcs; +}; + +static TR_IDP_REALM *idp_realm_entry_to_idp_realm(TALLOC_CTX *mem_ctx, struct idp_realm_entry *re) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_IDP_REALM *realm=NULL; + + realm=tr_idp_realm_new(tmp_ctx); + if (realm!=NULL) { + tr_idp_realm_set_id(realm, tr_new_name(re->id)); + realm->aaa_servers=aaa_entry_to_aaa_server(realm, re->aaa_servers); + if (realm->aaa_servers==NULL) + realm=NULL; /* still in tmp_ctx so will be freed */ + else { + tr_idp_realm_set_apcs(realm, apc_entry_to_apc(tmp_ctx, re->apcs)); + if (tr_idp_realm_get_apcs(realm)==NULL) + realm=NULL; + } + } + + if (realm!=NULL) + talloc_steal(mem_ctx, realm); + + talloc_free(tmp_ctx); + return realm; +} + +static int add_idp_realm_set(TR_COMM_TABLE *ctab, struct idp_realm_entry *entries) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + struct idp_realm_entry *this=NULL; + TR_IDP_REALM *realm=NULL; + int rc=-1; + + for (this=entries; this->id!=NULL; this++) { + realm=idp_realm_entry_to_idp_realm(tmp_ctx, this); + if (realm==NULL) { + printf("Error creating IDP realm %s.\n", this->id); + rc=1; + goto cleanup; + } + tr_comm_table_add_idp_realm(ctab, realm); + } + rc=0; + +cleanup: + talloc_free(tmp_ctx); + return rc; +} + +static int verify_idp_realm_set(TR_COMM_TABLE *ctab, struct idp_realm_entry *entries) +{ + struct idp_realm_entry *this=NULL; + TR_IDP_REALM *idp_realm=NULL; + TR_NAME *this_id=NULL; + + for (this=entries; this->id!=NULL; this++) { + this_id=tr_new_name(this->id); + idp_realm=tr_comm_table_find_idp_realm(ctab, this_id); + tr_free_name(this_id); this_id=NULL; + + if (idp_realm==NULL) { + printf("Error, IDP realm %s missing from community table.\n", this->id); + return -1; + } + } + return 0; +} + +/* removes entry n from ctab */ +static int remove_idp_realm_set_member(TR_COMM_TABLE *ctab, struct idp_realm_entry *entries, size_t n) +{ + TR_NAME *idp_realm_name=tr_new_name(entries[n].id); + TR_IDP_REALM *idp_realm=tr_comm_table_find_idp_realm(ctab, idp_realm_name); + TR_IDP_REALM *idp_realm2=NULL; + + if (idp_realm==NULL) { + printf("Can't remove IDP realm %s, not in table.\n", entries[n].id); + tr_free_name(idp_realm_name); + return 1; + } + + tr_comm_table_remove_idp_realm(ctab, idp_realm); + idp_realm2=tr_comm_table_find_idp_realm(ctab, idp_realm_name); + if (idp_realm2!=NULL) { + printf("IDP realm %s still in table after removal.\n", entries[n].id); + tr_idp_realm_free(idp_realm); + tr_free_name(idp_realm_name); + return 2; + } + + tr_idp_realm_free(idp_realm); + tr_free_name(idp_realm_name); + return 0; +} + +/**********************************************************************/ +/* Community Membership test stuff */ + +struct comm_memb_entry { + TR_REALM_ROLE role; + const char *realm_name; + const char *comm_name; + const char *origin; + /* TODO: test provenance */ +}; + +/* add an existing realm to an existing community (these must + * exist in the community table lists) */ +static int add_comm_membership(TR_COMM_TABLE *ctab, struct comm_memb_entry *entry) +{ + TR_NAME *comm_name=tr_new_name(entry->comm_name); + TR_NAME *realm_name=tr_new_name(entry->realm_name); + TR_COMM *comm=tr_comm_table_find_comm(ctab, comm_name); + TR_RP_REALM *rp_realm=(entry->role==TR_ROLE_RP)?(tr_comm_table_find_rp_realm(ctab, realm_name)):(NULL); + TR_IDP_REALM *idp_realm=(entry->role==TR_ROLE_IDP)?(tr_comm_table_find_idp_realm(ctab, realm_name)):(NULL); + json_t *prov=NULL; + + if ((comm==NULL) || ((rp_realm==NULL)&&(idp_realm==NULL))) + return 1; + + prov=json_array(); + if (entry->origin!=NULL) + json_array_append(prov, json_string(entry->origin)); + + switch (entry->role) { + case TR_ROLE_IDP: + tr_comm_add_idp_realm(ctab, comm, idp_realm, 0, prov, NULL); /* Expiry!? */ + break; + case TR_ROLE_RP: + tr_comm_add_rp_realm(ctab, comm, rp_realm, 0, prov, NULL); /* Expiry!? */ + break; + default: + return 2; + } + + return 0; +} + +static int add_member_set(TR_COMM_TABLE *ctab, struct comm_memb_entry *entries) +{ + struct comm_memb_entry *this=NULL; + + for (this=entries; this->role!=TR_ROLE_UNKNOWN; this++) { + if (0!=add_comm_membership(ctab, this)) { + printf("Error adding %s realm %s to community %s (origin %s).\n", + (this->role==TR_ROLE_RP)?"RP":"IDP", + this->realm_name, + this->comm_name, + (this->origin!=NULL)?(this->origin):"null"); + return 1; + } + } + return 0; +} + +static int remove_membership(TR_COMM_TABLE *ctab, struct comm_memb_entry *entries, size_t n) +{ + TR_NAME *realm_name=tr_new_name(entries[n].realm_name); + TR_NAME *comm_name=tr_new_name(entries[n].comm_name); + TR_NAME *origin=(entries[n].origin!=NULL)?(tr_new_name(entries[n].origin)):NULL; + TR_COMM_MEMB *memb=NULL; + int rc=-1; + + switch (entries[n].role) { + case TR_ROLE_IDP: + memb=tr_comm_table_find_idp_memb_origin(ctab, realm_name, comm_name, origin); + break; + case TR_ROLE_RP: + memb=tr_comm_table_find_rp_memb_origin(ctab, realm_name, comm_name, origin); + break; + default: + rc=1; + goto cleanup; + } + + if (memb==NULL) { + printf("%s realm %s not in comm %s from origin %s, can't remove membership.\n", + (entries[n].role==TR_ROLE_RP)?"RP":"IDP", + entries[n].realm_name, + entries[n].comm_name, + (entries[n].origin!=NULL)?(entries[n].origin):"null"); + rc=2; + goto cleanup; + } + tr_comm_table_remove_memb(ctab, memb); + tr_comm_memb_free(memb); + rc=0; + +cleanup: + tr_free_name(realm_name); + tr_free_name(comm_name); + if (origin!=NULL) + tr_free_name(origin); + return rc; +} + +/**********************************************************************/ +/* Test data */ + +struct apc_entry apc_1={ "apc" }; + +struct comm_entry comm_set_1[]={ + { "apc", TR_COMM_APC, NULL }, + { "comm 1", TR_COMM_COI, &apc_1 }, + { "comm 2", TR_COMM_COI, &apc_1 }, + { NULL } +}; + +struct rp_realm_entry rp_realm_set_1[]={ + { "rp 1" }, + { "rp 2" }, + { "rp 3" }, + { NULL } +}; + +struct aaa_entry aaa_1= { "aaa 1" }; +struct aaa_entry aaa_2= { "aaa 2" }; +struct aaa_entry aaa_3= { "aaa 3" }; + +struct idp_realm_entry idp_realm_set_1[]={ + { "idp 1", &aaa_1, &apc_1 }, + { "idp 2", &aaa_2, &apc_1 }, + { "idp 3", &aaa_3, &apc_1 }, + { NULL } +}; + +struct comm_memb_entry member_set_1[]={ + { TR_ROLE_RP, "rp 1", "apc", NULL }, + { TR_ROLE_RP, "rp 2", "apc", NULL }, + { TR_ROLE_RP, "rp 3", "apc", NULL }, + { TR_ROLE_IDP, "idp 1", "apc", NULL }, + { TR_ROLE_IDP, "idp 2", "apc", NULL }, + { TR_ROLE_IDP, "idp 3", "apc", NULL }, + { TR_ROLE_RP, "rp 1", "comm 1", NULL }, + { TR_ROLE_RP, "rp 2", "comm 1", NULL }, + { TR_ROLE_RP, "rp 2", "comm 1", "peer 1" }, + { TR_ROLE_RP, "rp 2", "comm 1", "peer 2" }, + { TR_ROLE_IDP, "idp 1", "comm 1", NULL }, + { TR_ROLE_IDP, "idp 1", "comm 1", "peer 1" }, + { TR_ROLE_IDP, "idp 1", "comm 1", "peer 2" }, + { TR_ROLE_IDP, "idp 2", "comm 1", NULL }, + { TR_ROLE_RP, "rp 1", "comm 2", NULL }, + { TR_ROLE_RP, "rp 2", "comm 2", NULL }, + { TR_ROLE_RP, "rp 2", "comm 2", "peer 1" }, + { TR_ROLE_RP, "rp 2", "comm 2", "peer 2" }, + { TR_ROLE_IDP, "idp 1", "comm 2", NULL }, + { TR_ROLE_IDP, "idp 1", "comm 2", "peer 1" }, + { TR_ROLE_IDP, "idp 1", "comm 2", "peer 2" }, + { TR_ROLE_IDP, "idp 2", "comm 2", NULL }, + { TR_ROLE_UNKNOWN } +}; + + +/**********************************************************************/ +/* Test routines */ + +/* the first few tests here insert a few things into the community table (comms, + * rp_realms, or idp_realms), then verify that they're all there. They + * then remove them in various orders, put them back, try removing + * things that are not present, etc. */ + +static int community_test(void) +{ + TALLOC_CTX *mem_ctx=talloc_new(NULL); + TR_COMM_TABLE *ctab=tr_comm_table_new(mem_ctx); + + assert(0==tr_comm_table_size(ctab)); + + /* add communities */ + assert(ctab!=NULL); + assert(0==add_comm_set(ctab, comm_set_1)); + assert(3==tr_comm_table_size(ctab)); + assert(0==verify_comm_set(ctab, comm_set_1)); + + /* remove */ + assert(0==remove_comm_set_member(ctab, comm_set_1, 0)); + assert(2==tr_comm_table_size(ctab)); + assert(0==remove_comm_set_member(ctab, comm_set_1, 1)); + assert(1==tr_comm_table_size(ctab)); + assert(0==remove_comm_set_member(ctab, comm_set_1, 2)); + assert(0==tr_comm_table_size(ctab)); + + /* add communities */ + assert(ctab!=NULL); + assert(0==add_comm_set(ctab, comm_set_1)); + assert(3==tr_comm_table_size(ctab)); + assert(0==verify_comm_set(ctab, comm_set_1)); + + /* remove */ + assert(0==remove_comm_set_member(ctab, comm_set_1, 0)); + assert(2==tr_comm_table_size(ctab)); + assert(0==remove_comm_set_member(ctab, comm_set_1, 2)); + assert(1==tr_comm_table_size(ctab)); + assert(0==remove_comm_set_member(ctab, comm_set_1, 1)); + assert(0==tr_comm_table_size(ctab)); + + /* add communities */ + assert(ctab!=NULL); + assert(0==add_comm_set(ctab, comm_set_1)); + assert(3==tr_comm_table_size(ctab)); + assert(0==verify_comm_set(ctab, comm_set_1)); + + /* remove */ + assert(0==remove_comm_set_member(ctab, comm_set_1, 1)); + assert(2==tr_comm_table_size(ctab)); + assert(0==remove_comm_set_member(ctab, comm_set_1, 0)); + assert(1==tr_comm_table_size(ctab)); + assert(0==remove_comm_set_member(ctab, comm_set_1, 2)); + assert(0==tr_comm_table_size(ctab)); + + assert(1==remove_comm_set_member(ctab, comm_set_1, 1)); /* should not be in the table */ + assert(0==tr_comm_table_size(ctab)); + + /* add communities */ + assert(ctab!=NULL); + assert(0==add_comm_set(ctab, comm_set_1)); + assert(3==tr_comm_table_size(ctab)); + assert(0==verify_comm_set(ctab, comm_set_1)); + + /* remove */ + assert(0==remove_comm_set_member(ctab, comm_set_1, 1)); + assert(2==tr_comm_table_size(ctab)); + assert(1==remove_comm_set_member(ctab, comm_set_1, 1)); /* should not be in the table */ + assert(2==tr_comm_table_size(ctab)); + assert(0==remove_comm_set_member(ctab, comm_set_1, 2)); + assert(1==tr_comm_table_size(ctab)); + assert(0==remove_comm_set_member(ctab, comm_set_1, 0)); + assert(0==tr_comm_table_size(ctab)); + + /* add communities */ + assert(ctab!=NULL); + assert(0==add_comm_set(ctab, comm_set_1)); + assert(3==tr_comm_table_size(ctab)); + assert(0==verify_comm_set(ctab, comm_set_1)); + + /* remove */ + assert(0==remove_comm_set_member(ctab, comm_set_1, 2)); + assert(2==tr_comm_table_size(ctab)); + assert(0==remove_comm_set_member(ctab, comm_set_1, 0)); + assert(1==tr_comm_table_size(ctab)); + assert(0==remove_comm_set_member(ctab, comm_set_1, 1)); + assert(0==tr_comm_table_size(ctab)); + assert(1==remove_comm_set_member(ctab, comm_set_1, 1)); /* should not be in the table */ + assert(0==tr_comm_table_size(ctab)); + + /* add communities */ + assert(ctab!=NULL); + assert(0==add_comm_set(ctab, comm_set_1)); + assert(3==tr_comm_table_size(ctab)); + assert(0==verify_comm_set(ctab, comm_set_1)); + + /* remove */ + assert(0==remove_comm_set_member(ctab, comm_set_1, 2)); + assert(2==tr_comm_table_size(ctab)); + assert(0==remove_comm_set_member(ctab, comm_set_1, 1)); + assert(1==tr_comm_table_size(ctab)); + assert(1==remove_comm_set_member(ctab, comm_set_1, 1)); /* should not be in the table */ + assert(1==tr_comm_table_size(ctab)); + assert(0==remove_comm_set_member(ctab, comm_set_1, 0)); + assert(0==tr_comm_table_size(ctab)); + + talloc_free(mem_ctx); + return 0; +} + +static int rp_realm_test(void) +{ + TALLOC_CTX *mem_ctx=talloc_new(NULL); + TR_COMM_TABLE *ctab=tr_comm_table_new(mem_ctx); + + /* add realms */ + assert(ctab!=NULL); + assert(0==add_rp_realm_set(ctab, rp_realm_set_1)); + assert(0==verify_rp_realm_set(ctab, rp_realm_set_1)); + + /* remove */ + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 0)); + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 2)); + + /* add realms */ + assert(ctab!=NULL); + assert(0==add_rp_realm_set(ctab, rp_realm_set_1)); + assert(0==verify_rp_realm_set(ctab, rp_realm_set_1)); + + /* remove */ + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 0)); + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 2)); + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); + + /* add realms */ + assert(ctab!=NULL); + assert(0==add_rp_realm_set(ctab, rp_realm_set_1)); + assert(0==verify_rp_realm_set(ctab, rp_realm_set_1)); + + /* remove */ + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 0)); + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 2)); + + assert(1==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); /* should not be in the table */ + + /* add realms */ + assert(ctab!=NULL); + assert(0==add_rp_realm_set(ctab, rp_realm_set_1)); + assert(0==verify_rp_realm_set(ctab, rp_realm_set_1)); + + /* remove */ + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); + assert(1==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); /* should not be in the table */ + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 2)); + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 0)); + + /* add realms */ + assert(ctab!=NULL); + assert(0==add_rp_realm_set(ctab, rp_realm_set_1)); + assert(0==verify_rp_realm_set(ctab, rp_realm_set_1)); + + /* remove */ + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 2)); + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 0)); + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); + assert(1==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); /* should not be in the table */ + + /* add realms */ + assert(ctab!=NULL); + assert(0==add_rp_realm_set(ctab, rp_realm_set_1)); + assert(0==verify_rp_realm_set(ctab, rp_realm_set_1)); + + /* remove */ + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 2)); + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); + assert(1==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); /* should not be in the table */ + assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 0)); + + talloc_free(mem_ctx); + return 0; +} + +static int idp_realm_test(void) +{ + TALLOC_CTX *mem_ctx=talloc_new(NULL); + TR_COMM_TABLE *ctab=tr_comm_table_new(mem_ctx); + + /* add realms */ + assert(ctab!=NULL); + assert(0==add_idp_realm_set(ctab, idp_realm_set_1)); + assert(0==verify_idp_realm_set(ctab, idp_realm_set_1)); + + /* remove */ + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 0)); + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 2)); + + /* add realms */ + assert(ctab!=NULL); + assert(0==add_idp_realm_set(ctab, idp_realm_set_1)); + assert(0==verify_idp_realm_set(ctab, idp_realm_set_1)); + + /* remove */ + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 0)); + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 2)); + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); + + /* add realms */ + assert(ctab!=NULL); + assert(0==add_idp_realm_set(ctab, idp_realm_set_1)); + assert(0==verify_idp_realm_set(ctab, idp_realm_set_1)); + + /* remove */ + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 0)); + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 2)); + + assert(1==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); /* should not be in the table */ + + /* add realms */ + assert(ctab!=NULL); + assert(0==add_idp_realm_set(ctab, idp_realm_set_1)); + assert(0==verify_idp_realm_set(ctab, idp_realm_set_1)); + + /* remove */ + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); + assert(1==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); /* should not be in the table */ + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 2)); + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 0)); + + /* add realms */ + assert(ctab!=NULL); + assert(0==add_idp_realm_set(ctab, idp_realm_set_1)); + assert(0==verify_idp_realm_set(ctab, idp_realm_set_1)); + + /* remove */ + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 2)); + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 0)); + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); + assert(1==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); /* should not be in the table */ + + /* add realms */ + assert(ctab!=NULL); + assert(0==add_idp_realm_set(ctab, idp_realm_set_1)); + assert(0==verify_idp_realm_set(ctab, idp_realm_set_1)); + + /* remove */ + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 2)); + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); + assert(1==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); /* should not be in the table */ + assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 0)); + + talloc_free(mem_ctx); + return 0; +} + +static int membership_test(void) +{ + TALLOC_CTX *mem_ctx=talloc_new(NULL); + TR_COMM_TABLE *ctab=tr_comm_table_new(mem_ctx); + size_t ii=0; + size_t size=0; + + assert(ctab!=NULL); + assert(0==add_comm_set(ctab, comm_set_1)); + assert(0==add_rp_realm_set(ctab, rp_realm_set_1)); + assert(0==add_idp_realm_set(ctab, idp_realm_set_1)); + assert(0==add_member_set(ctab, member_set_1)); + + size=tr_comm_table_size(ctab); + tr_comm_table_sweep(ctab); + assert(size==tr_comm_table_size(ctab)); + + /* now remove memberships */ + for (ii=0; member_set_1[ii].role!=TR_ROLE_UNKNOWN; ii++) { + assert(0==remove_membership(ctab, member_set_1, ii)); + assert(2==remove_membership(ctab, member_set_1, ii)); /* should not be in the table */ + } + + assert(NULL==ctab->memberships); + + /* put them back */ + assert(0==add_member_set(ctab, member_set_1)); + /* tr_comm_table_print(stdout, ctab); */ + + tr_comm_table_sweep(ctab); + assert(size==tr_comm_table_size(ctab)); + + /* remove in the reverse order */ + for(; ii>0; ii--) { + assert(0==remove_membership(ctab, member_set_1, ii-1)); + assert(2==remove_membership(ctab, member_set_1, ii-1)); /* should not be in the table */ + /* tr_comm_table_print(stdout, ctab); */ + } + + assert(NULL==ctab->memberships); + + assert(size==tr_comm_table_size(ctab)); + tr_comm_table_sweep(ctab); + assert(0==tr_comm_table_size(ctab)); + + talloc_free(mem_ctx); + return 0; +} + + +/**********************************************************************/ +/* main */ +int main(void) +{ + assert(0==community_test()); + printf("Community tests passed.\n"); + assert(0==rp_realm_test()); + printf("RP realm tests passed.\n"); + assert(0==idp_realm_test()); + printf("IDP realm tests passed.\n"); + assert(0==membership_test()); + printf("Membership tests passed.\n"); + return 0; +} diff --git a/common/dh_test/dh_test.c b/common/tests/dh_test.c similarity index 100% rename from common/dh_test/dh_test.c rename to common/tests/dh_test.c diff --git a/common/tests/idp.cfg b/common/tests/idp.cfg new file mode 100644 index 0000000..cfb1729 --- /dev/null +++ b/common/tests/idp.cfg @@ -0,0 +1,27 @@ +{ + "communities": [ + { + "apcs": [], + "community_id": "apc.example.com", + "idp_realms": ["A.idp.cfg"], + "rp_realms": ["A.idp.cfg"], + "type": "apc" + } + ], + "local_organizations": [ + {"organization_name": "idp.cfg test org", + "realms": [ + { + "realm": "A.idp.cfg", + "identity_provider": { + "aaa_servers": ["rad1.A.idp.cfg", + "rad2.A.idp.cfg"], + "apcs": ["apc.example.com"], + "shared_config": "no" + }, + "gss_names": ["gss@example.com"] + } + ] + } + ] +} diff --git a/common/mq_test/mq_test.c b/common/tests/mq_test.c similarity index 91% rename from common/mq_test/mq_test.c rename to common/tests/mq_test.c index b1c4be7..5c04b37 100644 --- a/common/mq_test/mq_test.c +++ b/common/tests/mq_test.c @@ -60,7 +60,7 @@ int main(void) mq->notify_cb_arg=mq_name; msg1=tr_mq_msg_new(NULL,"Message 1", TR_MQ_PRIO_NORMAL); - assert(asprintf((char **)&(msg2->p), "First message.\n")!=-1); + assert(asprintf((char **)&(msg1->p), "First message.\n")!=-1); msg1->p_free=free; tr_mq_add(mq, msg1); assert(mq->head==msg1); @@ -76,7 +76,7 @@ int main(void) assert(mq->tail==msg2); assert(msg2->next==NULL); - msg=tr_mq_pop(mq); + msg=tr_mq_pop(mq, NULL); assert(msg==msg1); assert(mq->head==msg2); assert(mq->tail==msg2); @@ -88,7 +88,7 @@ int main(void) printf("no message to pop\n"); msg3=tr_mq_msg_new(NULL, "Message 3", TR_MQ_PRIO_NORMAL); - assert(asprintf("%s",(char **)&(msg3->p), "Third message.\n")!=-1); + assert(asprintf((char **)&(msg3->p), "%s", "Third message.\n")!=-1); msg3->p_free=free; tr_mq_add(mq, msg3); assert(mq->head==msg2); @@ -96,7 +96,7 @@ int main(void) assert(msg2->next==msg3); assert(msg3->next==NULL); - msg=tr_mq_pop(mq); + msg=tr_mq_pop(mq, NULL); assert(msg==msg2); assert(mq->head==msg3); assert(mq->tail==msg3); @@ -107,7 +107,7 @@ int main(void) } else printf("no message to pop\n"); - msg=tr_mq_pop(mq); + msg=tr_mq_pop(mq, NULL); assert(msg==msg3); assert(mq->head==NULL); assert(mq->tail==NULL); @@ -117,7 +117,7 @@ int main(void) } else printf("no message to pop\n"); - msg=tr_mq_pop(mq); + msg=tr_mq_pop(mq, NULL); assert(msg==NULL); assert(mq->head==NULL); assert(mq->tail==NULL); @@ -128,14 +128,14 @@ int main(void) printf("no message to pop\n"); msg4=tr_mq_msg_new(NULL, "Message 4", TR_MQ_PRIO_NORMAL); - assert(asprintf("%s",(char **)&(msg4->p), "Fourth message.\n")!=-1); + assert(asprintf((char **)&(msg4->p), "%s", "Fourth message.\n")!=-1); msg4->p_free=free; tr_mq_add(mq, msg4); assert(mq->head==msg4); assert(mq->tail==msg4); assert(msg4->next==NULL); - msg=tr_mq_pop(mq); + msg=tr_mq_pop(mq, NULL); assert(msg==msg4); assert(mq->head==NULL); assert(mq->tail==NULL); @@ -145,7 +145,7 @@ int main(void) } else printf("no message to pop\n"); - msg=tr_mq_pop(mq); + msg=tr_mq_pop(mq, NULL); assert(msg==NULL); assert(mq->head==NULL); assert(mq->tail==NULL); @@ -157,5 +157,6 @@ int main(void) tr_mq_free(mq); + printf("success\n"); return 0; } diff --git a/common/mq_test/thread_test.c b/common/tests/thread_test.c similarity index 89% rename from common/mq_test/thread_test.c rename to common/tests/thread_test.c index 0e5aeb4..14aac6f 100644 --- a/common/mq_test/thread_test.c +++ b/common/tests/thread_test.c @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -49,21 +50,21 @@ struct thread_data { char *label; }; -TR_MQ_MSG *make_msg(label, n) +static TR_MQ_MSG *make_msg(char *label, int n) { TR_MQ_MSG *msg=NULL; msg=tr_mq_msg_new(NULL, "Message", TR_MQ_PRIO_NORMAL); - asprintf((char **)&(msg->p), "%s: %d messages to go...", label, n); + assert(-1!=asprintf((char **)&(msg->p), "%s: %d messages to go...", label, n)); msg->p_free=free; return msg; } -void *thread_start(void *arg) +static void *thread_start(void *arg) { TR_MQ *mq=((struct thread_data *)arg)->mq; int n_msgs=((struct thread_data *)arg)->n_msgs; useconds_t msg_dly=((struct thread_data *)arg)->msg_dly; - const char *label=((struct thread_data *)arg)->label; + char *label=((struct thread_data *)arg)->label; while (n_msgs>=0) { usleep(msg_dly); @@ -80,7 +81,7 @@ struct message_data { int ready; }; -void handle_messages(TR_MQ *mq, void *arg) +static void handle_messages(TR_MQ *mq, void *arg) { struct message_data *status=(struct message_data *)arg; pthread_mutex_lock(&(status->lock)); @@ -89,12 +90,12 @@ void handle_messages(TR_MQ *mq, void *arg) pthread_mutex_unlock(&(status->lock)); } -void output_messages(TR_MQ *mq) +static void output_messages(TR_MQ *mq) { TR_MQ_MSG *msg=NULL; printf("\n* handle_messages notified of new messages in queue.\n"); - for (msg=tr_mq_pop(mq); msg!=NULL; msg=tr_mq_pop(mq)) { + for (msg=tr_mq_pop(mq, NULL); msg!=NULL; msg=tr_mq_pop(mq, NULL)) { printf(" > %s\n", (char *)msg->p); tr_mq_msg_free(msg); } @@ -128,7 +129,7 @@ int main(void) thread_data[ii].mq=mq; thread_data[ii].msg_dly=dly[ii]; thread_data[ii].n_msgs=10; - asprintf(&(thread_data[ii].label), "thread %d", ii+1); + assert(-1!=asprintf(&(thread_data[ii].label), "thread %d", ii+1)); pthread_create(&(thread[ii]), NULL, thread_start, &thread_data[ii]); printf("%s started.\n", thread_data[ii].label); } @@ -153,5 +154,6 @@ int main(void) for (ii=0; ii #include +#include static int tr_apc_destructor(void *obj) { @@ -71,7 +72,8 @@ static TR_APC *tr_apc_tail(TR_APC *apc) return apc; } -TR_APC *tr_apc_add(TR_APC *head, TR_APC *new) +/* do not call this directly, use the tr_apc_add() macro */ +TR_APC *tr_apc_add_func(TR_APC *head, TR_APC *new) { if (head==NULL) head=new; @@ -86,10 +88,39 @@ TR_APC *tr_apc_add(TR_APC *head, TR_APC *new) } /* does not copy next pointer */ -TR_APC *tr_apc_dup(TALLOC_CTX *mem_ctx, TR_APC *apc) +TR_APC *tr_apc_dup_one(TALLOC_CTX *mem_ctx, TR_APC *apc) { TR_APC *new=tr_apc_new(mem_ctx); - tr_apc_set_id(new, tr_apc_dup_id(apc)); + if (new!=NULL) + tr_apc_set_id(new, tr_apc_dup_id(apc)); + return new; +} + +/* copies next pointer */ +TR_APC *tr_apc_dup(TALLOC_CTX *mem_ctx, TR_APC *apc) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_APC *cur=NULL; + TR_APC *new=NULL; + + if (apc==NULL) + return NULL; + + if (NULL==(new=tr_apc_dup_one(tmp_ctx, apc))) + return NULL; + + for (cur=new,apc=apc->next; apc!=NULL; cur=cur->next,apc=apc->next) { + cur->next=tr_apc_dup_one(new, apc); + if (cur->next==NULL) { + new=NULL; + goto cleanup; + } + } + + talloc_steal(mem_ctx, new); + +cleanup: + talloc_free(tmp_ctx); return new; } @@ -107,7 +138,7 @@ TR_NAME *tr_apc_get_id(TR_APC *apc) TR_NAME *tr_apc_dup_id(TR_APC *apc) { - return tr_dup_name(apc->id);; + return tr_dup_name(apc->id); } @@ -115,3 +146,48 @@ char *tr_apc_to_str(TALLOC_CTX *mem_ctx, TR_APC *apc) { return talloc_strndup(mem_ctx, apc->id->buf, apc->id->len); } + +TR_APC_ITER *tr_apc_iter_new(TALLOC_CTX *mem_ctx) +{ + return talloc(mem_ctx, TR_APC_ITER); +} + +TR_APC *tr_apc_iter_first(TR_APC_ITER *iter, TR_APC *apc) +{ + *iter=apc; + return *iter; +} + +TR_APC *tr_apc_iter_next(TR_APC_ITER *iter) +{ + (*iter)=(*iter)->next; + return *iter; +} + +void tr_apc_iter_free(TR_APC_ITER *iter) +{ + talloc_free(iter); +} + +/* 1 on match, 0 on no match, -1 on error */ +int tr_apc_in_common(TR_APC *one, TR_APC *two) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_APC_ITER *i_one=tr_apc_iter_new(tmp_ctx); + TR_APC_ITER *i_two=tr_apc_iter_new(tmp_ctx); + TR_APC *cur_one=NULL; + TR_APC *cur_two=NULL; + + if ((i_one==NULL) || (i_two==NULL)) { + tr_err("tr_apc_in_common: unable to allocate iterators."); + talloc_free(tmp_ctx); + return -1; + } + for (cur_one=tr_apc_iter_first(i_one, one); cur_one!=NULL; cur_one=tr_apc_iter_next(i_one)) { + for (cur_two=tr_apc_iter_first(i_two, two); cur_two!=NULL; cur_two=tr_apc_iter_next(i_two)) { + if (cur_one==cur_two) + return 1; + } + } + return 0; +} diff --git a/common/tr_comm.c b/common/tr_comm.c index fe4476a..73c8023 100644 --- a/common/tr_comm.c +++ b/common/tr_comm.c @@ -32,18 +32,27 @@ * */ +#include +#include #include +#include #include +#include #include #include #include + static int tr_comm_destructor(void *obj) { TR_COMM *comm=talloc_get_type_abort(obj, TR_COMM); if (comm->id!=NULL) tr_free_name(comm->id); + if (comm->owner_realm!=NULL) + tr_free_name(comm->owner_realm); + if (comm->owner_contact!=NULL) + tr_free_name(comm->owner_contact); return 0; } @@ -55,8 +64,10 @@ TR_COMM *tr_comm_new(TALLOC_CTX *mem_ctx) comm->id=NULL; comm->type=TR_COMM_UNKNOWN; comm->apcs=NULL; - comm->idp_realms=NULL; - comm->rp_realms=NULL; + comm->owner_realm=NULL; + comm->owner_contact=NULL; + comm->expiration_interval=0; + comm->refcount=0; talloc_set_destructor((void *)comm, tr_comm_destructor); } return comm; @@ -67,30 +78,215 @@ void tr_comm_free(TR_COMM *comm) talloc_free(comm); } +void tr_comm_set_id(TR_COMM *comm, TR_NAME *id) +{ + if (comm->id != NULL) + tr_free_name(comm->id); + comm->id=id; +} + +void tr_comm_incref(TR_COMM *comm) +{ + comm->refcount++; +} + +void tr_comm_decref(TR_COMM *comm) +{ + if (comm->refcount>0) + comm->refcount--; +} + +void tr_comm_set_apcs(TR_COMM *comm, TR_APC *apc) +{ + if (comm->apcs!=NULL) + tr_apc_free(comm->apcs); + comm->apcs=apc; + talloc_steal(comm, apc); +} + +TR_APC *tr_comm_get_apcs(TR_COMM *comm) +{ + return comm->apcs; +} + +TR_NAME *tr_comm_get_id(TR_COMM *comm) +{ + return comm->id; +} + +TR_NAME *tr_comm_dup_id(TR_COMM *comm) +{ + return tr_dup_name(comm->id); +} + +void tr_comm_set_type(TR_COMM *comm, TR_COMM_TYPE type) +{ + comm->type=type; +} + +TR_COMM_TYPE tr_comm_get_type(TR_COMM *comm) +{ + return comm->type; +} + +void tr_comm_set_owner_realm(TR_COMM *comm, TR_NAME *realm) +{ + if (comm->owner_realm!=NULL) + tr_free_name(comm->owner_realm); + comm->owner_realm=realm; +} + +TR_NAME *tr_comm_get_owner_realm(TR_COMM *comm) +{ + return comm->owner_realm; +} + +TR_NAME *tr_comm_dup_owner_realm(TR_COMM *comm) +{ + return tr_dup_name(comm->owner_realm); +} + +void tr_comm_set_owner_contact(TR_COMM *comm, TR_NAME *contact) +{ + if (comm->owner_contact != NULL) + tr_free_name(comm->owner_contact); + comm->owner_contact=contact; +} + +TR_NAME *tr_comm_get_owner_contact(TR_COMM *comm) +{ + return comm->owner_contact; +} + +TR_NAME *tr_comm_dup_owner_contact(TR_COMM *comm) +{ + return tr_dup_name(comm->owner_contact); +} + +unsigned int tr_comm_get_refcount(TR_COMM *comm) +{ + return comm->refcount; +} + +/* 0 if equivalent, nonzero if different, only considers + * nhops last hops (nhops==0 means consider all, nhops==1 + * only considers last hop) */ +static int tr_comm_memb_provenance_cmp(TR_COMM_MEMB *m1, TR_COMM_MEMB *m2, int nhops) +{ + size_t ii; + + if ((m1->provenance==NULL) || (m2->provenance==NULL)) + return m1->provenance!=m2->provenance; /* return 0 if both null, 1 if only one null */ + + if (json_array_size(m1->provenance)!=json_array_size(m2->provenance)) + return 1; + + if (nhops==0) + nhops=json_array_size(m1->provenance); /* same as size(m2->provenance) */ + + for (ii=0; iiprovenance); ii++) { + if (0==strcmp(json_string_value(json_array_get(m1->provenance, ii)), + json_string_value(json_array_get(m2->provenance, ii)))) { + return 0; + } + } + return 1; +} + +/* Accepts an update that either came from the same peer as the previous + * origin, has a shorter provenance list, or can replace an expired + * membership. Otherwise keeps the existing one. + * On replacement, frees the old member and moves new member to ctab's + * context. Caller should not free newmemb except by freeing its original + * context. */ +static void tr_comm_add_if_shorter(TR_COMM_TABLE *ctab, TR_COMM_MEMB *existing, TR_COMM_MEMB *newmemb) +{ + int accept=0; + + if (existing==NULL) { + /* not in the table */ + tr_comm_table_add_memb(ctab, newmemb); + } else { + if (0==tr_comm_memb_provenance_cmp(existing, newmemb, 1)) + accept=1; /* always accept a replacement from the same peer */ + else if (tr_comm_memb_provenance_len(newmemb) < tr_comm_memb_provenance_len(existing)) + accept=1; /* accept a shorter provenance */ + else if (existing->times_expired>0) + accept=1; + else + accept=0; + + if (accept) { + tr_comm_table_remove_memb(ctab, existing); + tr_comm_memb_free(existing); + tr_comm_table_add_memb(ctab, newmemb); + } + } +} + /* does not take responsibility for freeing IDP realm */ -void tr_comm_add_idp_realm(TR_COMM *comm, TR_IDP_REALM *realm) +void tr_comm_add_idp_realm(TR_COMM_TABLE *ctab, + TR_COMM *comm, + TR_IDP_REALM *realm, + unsigned int interval, + json_t *provenance, + struct timespec *expiry) { - TR_IDP_REALM *cur=NULL; + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_COMM_MEMB *newmemb=tr_comm_memb_new(tmp_ctx); + TR_COMM_MEMB *existing=NULL; - if (comm->idp_realms==NULL) - comm->idp_realms=realm; - else { - for (cur=comm->idp_realms; cur->comm_next!=NULL; cur=cur->comm_next) { } - cur->comm_next=realm; + if (newmemb==NULL) { + tr_err("tr_comm_add_idp_realm: unable to allocate new membership record."); + talloc_free(tmp_ctx); + return; } + + tr_comm_memb_set_idp_realm(newmemb, realm); + tr_comm_memb_set_comm(newmemb, comm); + tr_comm_memb_set_interval(newmemb, interval); + tr_comm_memb_set_provenance(newmemb, provenance); + tr_comm_memb_set_expiry(newmemb, expiry); + + existing=tr_comm_table_find_idp_memb_origin(ctab, + tr_idp_realm_get_id(realm), + tr_comm_get_id(comm), + tr_comm_memb_get_origin(newmemb)); + tr_comm_add_if_shorter(ctab, existing, newmemb); /* takes newmemb out of tmp_ctx if needed */ + + talloc_free(tmp_ctx); } /* does not take responsibility for freeing RP realm */ -void tr_comm_add_rp_realm(TR_COMM *comm, TR_RP_REALM *realm) +void tr_comm_add_rp_realm(TR_COMM_TABLE *ctab, + TR_COMM *comm, + TR_RP_REALM *realm, + unsigned int interval, + json_t *provenance, + struct timespec *expiry) { - TR_RP_REALM *cur=NULL; + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_COMM_MEMB *newmemb=tr_comm_memb_new(tmp_ctx); + TR_COMM_MEMB *existing=NULL; - if (comm->rp_realms==NULL) - comm->rp_realms=realm; - else { - for (cur=comm->rp_realms; cur->next!=NULL; cur=cur->next) { } - cur->next=realm; + if (newmemb==NULL) { + tr_err("tr_comm_add_idp_realm: unable to allocate new membership record."); + talloc_free(tmp_ctx); + return; } + + tr_comm_memb_set_rp_realm(newmemb, realm); + tr_comm_memb_set_comm(newmemb, comm); + tr_comm_memb_set_interval(newmemb, interval); + tr_comm_memb_set_provenance(newmemb, provenance); + tr_comm_memb_set_expiry(newmemb, expiry); + + existing=tr_comm_table_find_rp_memb_origin(ctab, + tr_rp_realm_get_id(realm), + tr_comm_get_id(comm), + tr_comm_memb_get_origin(newmemb)); + tr_comm_add_if_shorter(ctab, existing, newmemb); /* takes newmemb out of tmp_ctx if needed */ + talloc_free(tmp_ctx); } static TR_COMM *tr_comm_tail(TR_COMM *comm) @@ -105,9 +301,10 @@ static TR_COMM *tr_comm_tail(TR_COMM *comm) /* All list members are in the talloc context of the head. * This will require careful thought if entries are ever removed - * or shuffled between lists. - * Call like comms=tr_comm_add(comms, new_comm); */ -TR_COMM *tr_comm_add(TR_COMM *comms, TR_COMM *new) + * Call like comms=tr_comm_add_func(comms, new_comm); + * or just use the tr_comm_add(comms, new) macro. */ +#define tr_comm_add(comms, new) ((comms)=tr_comm_add_func((comms), (new))) +static TR_COMM *tr_comm_add_func(TR_COMM *comms, TR_COMM *new) { if (comms==NULL) comms=new; @@ -121,50 +318,1133 @@ TR_COMM *tr_comm_add(TR_COMM *comms, TR_COMM *new) return comms; } -TR_IDP_REALM *tr_find_comm_idp (TR_COMM *comm, TR_NAME *idp_realm) +/* Guarantees comm is not in the list, not an error if it was't there. + * Does not free the removed element, nor change its talloc context. */ +#define tr_comm_remove(comms, c) ((comms)=tr_comm_remove_func((comms), (c))) +static TR_COMM *tr_comm_remove_func(TR_COMM *comms, TR_COMM *remove) +{ + TALLOC_CTX *list_ctx=talloc_parent(comms); /* in case we need to remove the head */ + TR_COMM *this=NULL; + + if (comms==NULL) + return NULL; + + if (comms==remove) { + /* if we're removing the head, put the next element (if present) into the context + * the list head was in. */ + comms=comms->next; + if (comms!=NULL) { + talloc_steal(list_ctx, comms); + /* now put all the other elements in the context of the list head */ + for (this=comms->next; this!=NULL; this=this->next) + talloc_steal(comms, this); + } + } else { + /* not removing the head; no need to play with contexts */ + for (this=comms; this->next!=NULL; this=this->next) { + if (this->next==remove) { + this->next=remove->next; + break; + } + } + } + return comms; +} + +/* remove any with zero refcount + * Call via macro. */ +#define tr_comm_sweep(head) ((head)=tr_comm_sweep_func((head))) +static TR_COMM *tr_comm_sweep_func(TR_COMM *head) { - TR_IDP_REALM *idp; + TR_COMM *comm=NULL; + TR_COMM *old_next=NULL; - if ((!comm) || (!idp_realm)) { + if (head==NULL) return NULL; + + while ((head!=NULL) && (head->refcount==0)) { + comm=head; /* keep a pointer so we can remove it */ + tr_comm_remove(head, comm); /* use this to get talloc contexts right */ + tr_comm_free(comm); } - for (idp = comm->idp_realms; NULL != idp; idp = idp->comm_next) { - if (!tr_name_cmp (idp_realm, idp->realm_id)) { - tr_debug("tr_find_comm_idp: Found IdP %s in community %s.", idp_realm->buf, comm->id->buf); - return idp; + if (head==NULL) + return NULL; + + /* will not remove the head here, that has already been done */ + for (comm=head; comm->next!=NULL; comm=comm->next) { + if (comm->next->refcount==0) { + old_next=comm->next; + tr_comm_remove(head, comm->next); /* changes comm->next */ + tr_comm_free(old_next); } } - /* if we didn't find one, return NULL */ + + return head; +} + +TR_IDP_REALM *tr_comm_find_idp(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *idp_realm) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_COMM_ITER *iter=NULL; + TR_IDP_REALM *this_idp=NULL; + + if ((NULL==ctab) || (NULL==comm) || (NULL==idp_realm)) { + talloc_free(tmp_ctx); + return NULL; + } + + iter=tr_comm_iter_new(tmp_ctx); + for (this_idp=tr_idp_realm_iter_first(iter, ctab, tr_comm_get_id(comm)); + this_idp!=NULL; + this_idp=tr_idp_realm_iter_next(iter)) { + if (0==tr_name_cmp(idp_realm, tr_idp_realm_get_id(this_idp))) { + tr_debug("tr_comm_find_idp: Found IdP %s in community %s.", idp_realm->buf, tr_comm_get_id(comm)->buf); + talloc_free(tmp_ctx); + return this_idp; + } + } + tr_debug("tr_comm_find_idp: Unable to find IdP %s in community %s.", idp_realm->buf, tr_comm_get_id(comm)->buf); + talloc_free(tmp_ctx); return NULL; } -TR_RP_REALM *tr_find_comm_rp (TR_COMM *comm, TR_NAME *rp_realm) +TR_RP_REALM *tr_comm_find_rp (TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *rp_realm) { - TR_RP_REALM *rp; + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_COMM_ITER *iter=NULL; + TR_RP_REALM *this_rp=NULL; - if ((!comm) || (!rp_realm)) { + if ((NULL==ctab) || (NULL==comm) || (NULL==rp_realm)) { + talloc_free(tmp_ctx); return NULL; } - for (rp = comm->rp_realms; NULL != rp; rp = rp->next) { - if (!tr_name_cmp (rp_realm, rp->realm_name)) { - tr_debug("tr_find_comm_rp: Found RP %s in community %s.", rp_realm->buf, comm->id->buf); - return rp; + iter=tr_comm_iter_new(tmp_ctx); + for (this_rp=tr_rp_realm_iter_first(iter, ctab, tr_comm_get_id(comm)); + this_rp!=NULL; + this_rp=tr_rp_realm_iter_next(iter)) { + if (0==tr_name_cmp(rp_realm, tr_rp_realm_get_id(this_rp))) { + tr_debug("tr_comm_find_rp: Found RP %s in community %s.", rp_realm->buf, tr_comm_get_id(comm)->buf); + talloc_free(tmp_ctx); + return this_rp; } } - /* if we didn't find one, return NULL */ + tr_debug("tr_comm_find_rp: Unable to find RP %s in community %s.", rp_realm->buf, tr_comm_get_id(comm)->buf); + talloc_free(tmp_ctx); return NULL; } -TR_COMM *tr_comm_lookup(TR_COMM *comms, TR_NAME *comm_name) +static TR_COMM *tr_comm_lookup(TR_COMM *comms, TR_NAME *comm_name) { TR_COMM *cfg_comm = NULL; for (cfg_comm = comms; NULL != cfg_comm; cfg_comm = cfg_comm->next) { - if ((cfg_comm->id->len == comm_name->len) && - (!strncmp(cfg_comm->id->buf, comm_name->buf, comm_name->len))) + if (0==tr_name_cmp(cfg_comm->id, comm_name)) return cfg_comm; } return NULL; } + +TR_COMM_ITER *tr_comm_iter_new(TALLOC_CTX *mem_ctx) +{ + TR_COMM_ITER *iter=talloc(mem_ctx, TR_COMM_ITER); + if (iter!=NULL) { + iter->cur_comm=NULL; + iter->cur_memb=NULL; + iter->cur_orig_head=NULL; + iter->match=NULL; + iter->realm=NULL; + } + return iter; +} + +void tr_comm_iter_free(TR_COMM_ITER *iter) +{ + talloc_free(iter); +} + + +TR_COMM *tr_comm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm) +{ + iter->match=realm; + + /* find memberships for this realm */ + for (iter->cur_memb=ctab->memberships; + iter->cur_memb!=NULL; + iter->cur_memb=iter->cur_memb->next) { + if (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))) + return tr_comm_memb_get_comm(iter->cur_memb); + } + return NULL; +} + +TR_COMM *tr_comm_iter_next(TR_COMM_ITER *iter) +{ + for (iter->cur_memb=iter->cur_memb->next; + iter->cur_memb!=NULL; + iter->cur_memb=iter->cur_memb->next) { + if (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))) + return tr_comm_memb_get_comm(iter->cur_memb); + } + return NULL; +} + +/* iterate only over RPs */ +TR_COMM *tr_comm_iter_first_rp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm) +{ + iter->match=realm; + + /* find memberships for this realm */ + for (iter->cur_memb=ctab->memberships; + iter->cur_memb!=NULL; + iter->cur_memb=iter->cur_memb->next) { + if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) && + (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb)))) + return tr_comm_memb_get_comm(iter->cur_memb); + } + return NULL; +} + +TR_COMM *tr_comm_iter_next_rp(TR_COMM_ITER *iter) +{ + for (iter->cur_memb=iter->cur_memb->next; + iter->cur_memb!=NULL; + iter->cur_memb=iter->cur_memb->next) { + if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) && + (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb)))) + return tr_comm_memb_get_comm(iter->cur_memb); + } + return NULL; +} + +/* iterate only over IDPs */ +TR_COMM *tr_comm_iter_first_idp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm) +{ + iter->match=realm; + + /* find memberships for this realm */ + for (iter->cur_memb=ctab->memberships; + iter->cur_memb!=NULL; + iter->cur_memb=iter->cur_memb->next) { + if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) && + (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb)))) + return tr_comm_memb_get_comm(iter->cur_memb); + } + return NULL; +} + +TR_COMM *tr_comm_iter_next_idp(TR_COMM_ITER *iter) +{ + for (iter->cur_memb=iter->cur_memb->next; + iter->cur_memb!=NULL; + iter->cur_memb=iter->cur_memb->next) { + if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) && + (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb)))) + return tr_comm_memb_get_comm(iter->cur_memb); + } + return NULL; +} + +static TR_REALM *tr_realm_new(TALLOC_CTX *mem_ctx) +{ + TR_REALM *realm=talloc(mem_ctx, TR_REALM); + if (realm!=NULL) { + realm->role=TR_ROLE_UNKNOWN; + realm->rp=NULL; + realm->idp=NULL; + } + return realm; +} + +static void tr_realm_free(TR_REALM *realm) +{ + talloc_free(realm); +} + +static void tr_realm_set_rp(TR_REALM *realm, TR_RP_REALM *rp) +{ + if (realm->idp!=NULL) + realm->idp=NULL; + realm->role=TR_ROLE_RP; + realm->rp=rp; +} + +static void tr_realm_set_idp(TR_REALM *realm, TR_IDP_REALM *idp) +{ + if (realm->rp!=NULL) + realm->rp=NULL; + realm->role=TR_ROLE_IDP; + realm->idp=idp; +} + +TR_NAME *tr_realm_get_id(TR_REALM *realm) +{ + switch (realm->role) { + case TR_ROLE_RP: + return tr_rp_realm_get_id(realm->rp); + case TR_ROLE_IDP: + return tr_idp_realm_get_id(realm->idp); + default: + break; + } + return NULL; +} + +TR_NAME *tr_realm_dup_id(TR_REALM *realm) +{ + return tr_dup_name(tr_realm_get_id(realm)); +} + +/* Iterate over either sort of realm. Do not free the TR_REALM returned. It becomes + * undefined/invalid after the next operation affecting the iterator. */ +TR_REALM *tr_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm) +{ + iter->match=comm; + if (iter->realm==NULL) + iter->realm=tr_realm_new(iter); + if (iter->realm==NULL) + return NULL; + + /* find memberships for this comm */ + for (iter->cur_memb=ctab->memberships; + iter->cur_memb!=NULL; + iter->cur_memb=iter->cur_memb->next) { + if (0==tr_name_cmp(iter->match, + tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))) { + /* found a match, determine whether it's an rp realm or an idp realm */ + if (tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) + tr_realm_set_rp(iter->realm, tr_comm_memb_get_rp_realm(iter->cur_memb)); + else if (tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) + tr_realm_set_idp(iter->realm, tr_comm_memb_get_idp_realm(iter->cur_memb)); + else { + if (iter->realm!=NULL) + tr_realm_free(iter->realm); + iter->realm=NULL; + } + return iter->realm; + } + } + if (iter->realm!=NULL) + tr_realm_free(iter->realm); + iter->realm=NULL; + return NULL; +} + +TR_REALM *tr_realm_iter_next(TR_COMM_ITER *iter) +{ + if (iter->realm==NULL) + return NULL; + + /* find memberships for this comm */ + for (iter->cur_memb=iter->cur_memb->next; + iter->cur_memb!=NULL; + iter->cur_memb=iter->cur_memb->next) { + if (0==tr_name_cmp(iter->match, + tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))) { + /* found a match, determine whether it's an rp realm or an idp realm */ + if (tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) + tr_realm_set_rp(iter->realm, tr_comm_memb_get_rp_realm(iter->cur_memb)); + else if (tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) + tr_realm_set_idp(iter->realm, tr_comm_memb_get_idp_realm(iter->cur_memb)); + else { + if (iter->realm!=NULL) + tr_realm_free(iter->realm); + iter->realm=NULL; + } + return iter->realm; + } + } + if (iter->realm!=NULL) + tr_realm_free(iter->realm); + iter->realm=NULL; + return NULL; +} + +TR_RP_REALM *tr_rp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm) +{ + iter->match=comm; + + /* find memberships for this comm */ + for (iter->cur_memb=ctab->memberships; + iter->cur_memb!=NULL; + iter->cur_memb=iter->cur_memb->next) { + if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) && + (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb))))) + return tr_comm_memb_get_rp_realm(iter->cur_memb); + } + return NULL; +} + +TR_RP_REALM *tr_rp_realm_iter_next(TR_COMM_ITER *iter) +{ + for (iter->cur_memb=iter->cur_memb->next; + iter->cur_memb!=NULL; + iter->cur_memb=iter->cur_memb->next) { + if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) && + (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb))))) + return tr_comm_memb_get_rp_realm(iter->cur_memb); + } + return NULL; +} + +TR_IDP_REALM *tr_idp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm) +{ + iter->match=comm; + + /* find memberships for this comm */ + for (iter->cur_memb=ctab->memberships; + iter->cur_memb!=NULL; + iter->cur_memb=iter->cur_memb->next) { + if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) && + (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb))))) + return tr_comm_memb_get_idp_realm(iter->cur_memb); + } + return NULL; +} + +TR_IDP_REALM *tr_idp_realm_iter_next(TR_COMM_ITER *iter) +{ + for (iter->cur_memb=iter->cur_memb->next; + iter->cur_memb!=NULL; + iter->cur_memb=iter->cur_memb->next) { + if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) && + (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb))))) + return tr_comm_memb_get_idp_realm(iter->cur_memb); + } + return NULL; +} + +/* iterators for all communities in a table */ +TR_COMM *tr_comm_table_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab) +{ + iter->cur_comm=ctab->comms; + return iter->cur_comm; +} + +TR_COMM *tr_comm_table_iter_next(TR_COMM_ITER *iter) +{ + return iter->cur_comm=iter->cur_comm->next; +} + +const char *tr_comm_type_to_str(TR_COMM_TYPE type) +{ + const char *s=NULL; + switch(type) { + case TR_COMM_UNKNOWN: + s="unknown"; + break; + case TR_COMM_APC: + s="apc"; + break; + case TR_COMM_COI: + s="coi"; + break; + default: + s="invalid"; + } + return s; +} + +/* iterate along the origin list for this member */ +TR_COMM_MEMB *tr_comm_memb_iter_first(TR_COMM_ITER *iter, TR_COMM_MEMB *memb) +{ + iter->cur_memb=memb; + return iter->cur_memb; +} + +TR_COMM_MEMB *tr_comm_memb_iter_next(TR_COMM_ITER *iter) +{ + if (iter->cur_memb!=NULL) + iter->cur_memb=iter->cur_memb->origin_next; + return iter->cur_memb; +} + + +/* iterate over all memberships in the table */ +TR_COMM_MEMB *tr_comm_memb_iter_all_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab) +{ + iter->cur_memb=ctab->memberships; + iter->cur_orig_head=ctab->memberships; + return iter->cur_memb; +} + +TR_COMM_MEMB *tr_comm_memb_iter_all_next(TR_COMM_ITER *iter) +{ + if (iter->cur_memb->next==NULL) { + if (iter->cur_orig_head->next==NULL) { + /* we're done */ + return NULL; + } else { + iter->cur_memb=iter->cur_orig_head->next; + iter->cur_orig_head=iter->cur_orig_head->next; + } + } else { + iter->cur_memb=iter->cur_memb->origin_next; + } + return iter->cur_memb; +} + + +TR_COMM_TYPE tr_comm_type_from_str(const char *s) +{ + if (strcmp(s, "apc")==0) + return TR_COMM_APC; + if (strcmp(s,"coi")==0) + return TR_COMM_COI; + return TR_COMM_UNKNOWN; +} + + +static int tr_comm_memb_destructor(void *obj) +{ + TR_COMM_MEMB *memb=talloc_get_type_abort(obj, TR_COMM_MEMB); + if (memb->origin!=NULL) + tr_free_name(memb->origin); + + if (memb->rp!=NULL) + tr_rp_realm_decref(memb->rp); + if (memb->idp!=NULL) + tr_idp_realm_decref(memb->idp); + if (memb->comm!=NULL) + tr_comm_decref(memb->comm); + if (memb->provenance!=NULL) + json_decref(memb->provenance); + return 0; +} + +TR_COMM_MEMB *tr_comm_memb_new(TALLOC_CTX *mem_ctx) +{ + TR_COMM_MEMB *memb=talloc(mem_ctx, TR_COMM_MEMB); + if (memb!=NULL) { + memb->next=NULL; + memb->origin_next=NULL; + memb->idp=NULL; + memb->rp=NULL; + memb->comm=NULL; + memb->origin=NULL; + memb->provenance=NULL; + memb->interval=0; + memb->triggered=0; + memb->times_expired=0; + memb->expiry=talloc(memb, struct timespec); + if (memb->expiry==NULL) { + talloc_free(memb); + return NULL; + } + *(memb->expiry)=(struct timespec){0,0}; + talloc_set_destructor((void *)memb, tr_comm_memb_destructor); + } + return memb; +} + +void tr_comm_memb_free(TR_COMM_MEMB *memb) +{ + talloc_free(memb); +} + +/* Returns 0 if they are the same, nonzero if they differ. + * Ignores expiry, triggered, and times_expired, next pointers */ +int tr_comm_memb_cmp(TR_COMM_MEMB *m1, TR_COMM_MEMB *m2) +{ + if ((m1->idp==m2->idp) && + (m1->rp==m2->rp) && + (m1->comm==m2->comm) && + (tr_comm_memb_provenance_cmp(m1, m2, 0)==0) && + (m1->interval==m2->interval)) + return 0; + return 1; +} + +TR_REALM_ROLE tr_comm_memb_get_role(TR_COMM_MEMB *memb) +{ + if (memb->rp!=NULL) + return TR_ROLE_RP; + if (memb->idp!=NULL) + return TR_ROLE_IDP; + return TR_ROLE_UNKNOWN; +} + +void tr_comm_memb_set_rp_realm(TR_COMM_MEMB *memb, TR_RP_REALM *realm) +{ + if (memb->idp!=NULL) { + tr_idp_realm_decref(memb->idp); + memb->idp=NULL; + } + if (memb->rp!=NULL) + tr_rp_realm_decref(memb->rp); + + + memb->rp=realm; + tr_rp_realm_incref(realm); +} + +TR_RP_REALM *tr_comm_memb_get_rp_realm(TR_COMM_MEMB *memb) +{ + return memb->rp; +} + +void tr_comm_memb_set_idp_realm(TR_COMM_MEMB *memb, TR_IDP_REALM *realm) +{ + if (memb->rp!=NULL) { + tr_rp_realm_decref(memb->rp); + memb->rp=NULL; + } + if (memb->idp!=NULL) + tr_idp_realm_decref(memb->idp); + + memb->idp=realm; + tr_idp_realm_incref(realm); +} + +TR_IDP_REALM *tr_comm_memb_get_idp_realm(TR_COMM_MEMB *memb) +{ + return memb->idp; +} + +void tr_comm_memb_set_comm(TR_COMM_MEMB *memb, TR_COMM *comm) +{ + if (memb->comm!=NULL) + tr_comm_decref(memb->comm); + memb->comm=comm; + tr_comm_incref(comm); +} + +TR_COMM *tr_comm_memb_get_comm(TR_COMM_MEMB *memb) +{ + return memb->comm; +} + +static void tr_comm_memb_set_origin(TR_COMM_MEMB *memb, TR_NAME *origin) +{ + if (memb->origin!=NULL) + tr_free_name(memb->origin); + memb->origin=origin; +} + +TR_NAME *tr_comm_memb_get_origin(TR_COMM_MEMB *memb) +{ + return memb->origin; +} + +TR_NAME *tr_comm_memb_dup_origin(TR_COMM_MEMB *memb) +{ + if (memb->origin!=NULL) + return tr_dup_name(memb->origin); + return NULL; +} + +json_t *tr_comm_memb_get_provenance(TR_COMM_MEMB *memb) +{ + if (memb!=NULL) + return memb->provenance; + return NULL; +} + +void tr_comm_memb_set_provenance(TR_COMM_MEMB *memb, json_t *prov) +{ + const char *s=NULL; + + if (memb->provenance) + json_decref(memb->provenance); + + memb->provenance=prov; + if (prov!=NULL) { + json_incref(prov); + + /* next line sets origin to NULL if provenance is empty because jansson + * routines return NULL on error */ + s=json_string_value(json_array_get(prov, 0)); + if (s==NULL) + tr_comm_memb_set_origin(memb, NULL); + else + memb->origin=tr_new_name(s); + } else { + tr_comm_memb_set_origin(memb, NULL); + } +} + +void tr_comm_memb_add_to_provenance(TR_COMM_MEMB *memb, TR_NAME *hop) +{ + if (memb->provenance==NULL) { + memb->provenance=json_array(); + if (memb->provenance==NULL) { + tr_err("tr_comm_memb_add_to_provenance: unable to allocate provenance list."); + return; + } + /* this is the first entry in the provenance, so it is the origin */ + tr_comm_memb_set_origin(memb,tr_dup_name(hop)); + if (memb->origin==NULL) { + tr_err("tr_comm_memb_add_to_provenance: unable to allocate origin."); + json_decref(memb->provenance); + memb->provenance=NULL; + return; + } + } + if (0!=json_array_append_new(memb->provenance, tr_name_to_json_string(hop))) + tr_err("tr_comm_memb_add_to_provenance: unable to extend provenance list."); +} + +size_t tr_comm_memb_provenance_len(TR_COMM_MEMB *memb) +{ + if (memb->provenance==NULL) + return 0; + return json_array_size(memb->provenance); +} + +void tr_comm_memb_set_interval(TR_COMM_MEMB *memb, unsigned int interval) +{ + memb->interval=interval; +} + +unsigned int tr_comm_memb_get_interval(TR_COMM_MEMB *memb) +{ + return memb->interval; +} + +void tr_comm_memb_set_expiry(TR_COMM_MEMB *memb, struct timespec *time) +{ + if (time==NULL) + *(memb->expiry)=(struct timespec){0,0}; + else { + memb->expiry->tv_sec=time->tv_sec; + memb->expiry->tv_nsec=time->tv_nsec; + } +} + +struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb) +{ + return memb->expiry; +} + +int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime) +{ + tr_debug("tr_comm_memb_is_expired: (cur->tv_sec>memb->expiry->tv_sec)=(%u > %u)=%s", + curtime->tv_sec, + memb->expiry->tv_sec, + (curtime->tv_sec > memb->expiry->tv_sec)?"true":"false"); + + return ((curtime->tv_sec > memb->expiry->tv_sec) + || ((curtime->tv_sec == memb->expiry->tv_sec) + &&(curtime->tv_nsec >= memb->expiry->tv_nsec))); +} + +void tr_comm_memb_set_triggered(TR_COMM_MEMB *memb, int trig) +{ + memb->triggered=trig; +} + +int tr_comm_memb_is_triggered(TR_COMM_MEMB *memb) +{ + return memb->triggered; +} + +void tr_comm_memb_reset_times_expired(TR_COMM_MEMB *memb) +{ + memb->times_expired=0; +} + +/* bumps the expiration count */ +void tr_comm_memb_expire(TR_COMM_MEMB *memb) +{ + /* avoid overflow */ + if (memb->times_expired+1>memb->times_expired) + memb->times_expired++; +} + +unsigned int tr_comm_memb_get_times_expired(TR_COMM_MEMB *memb) +{ + return memb->times_expired; +} + +TR_COMM_TABLE *tr_comm_table_new(TALLOC_CTX *mem_ctx) +{ + TR_COMM_TABLE *ctab=talloc(mem_ctx, TR_COMM_TABLE); + if (ctab!=NULL) { + ctab->comms=NULL; + ctab->memberships=NULL; + ctab->idp_realms=NULL; + ctab->rp_realms=NULL; + } + return ctab; +} + +void tr_comm_table_free(TR_COMM_TABLE *ctab) +{ + talloc_free(ctab); +} + +static TR_REALM_ROLE tr_comm_memb_role(TR_COMM_MEMB *memb) +{ + if (memb->rp!=NULL) + return TR_ROLE_RP; + if (memb->idp!=NULL) + return TR_ROLE_IDP; + + return TR_ROLE_UNKNOWN; +} + +void tr_comm_table_add_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *new) +{ + TR_COMM_MEMB *cur=NULL; + + /* TODO: further validate the member (must have valid comm and realm) */ + if ((new->next!=NULL) || (new->origin_next!=NULL)) { + tr_debug("tr_comm_table_add_memb: attempting to add member already in a list."); + } + + /* handle the empty list case */ + if (ctab->memberships==NULL) { + ctab->memberships=new; + talloc_steal(ctab, new); + return; + } + + /* The list was not empty. See if we already have a membership for this realm/comm/role */ + switch (tr_comm_memb_role(new)) { + case TR_ROLE_RP: + cur=tr_comm_table_find_rp_memb(ctab, + tr_rp_realm_get_id(tr_comm_memb_get_rp_realm(new)), + tr_comm_get_id(tr_comm_memb_get_comm(new))); + break; + case TR_ROLE_IDP: + cur=tr_comm_table_find_idp_memb(ctab, + tr_idp_realm_get_id(tr_comm_memb_get_idp_realm(new)), + tr_comm_get_id(tr_comm_memb_get_comm(new))); + break; + case TR_ROLE_UNKNOWN: + default: + tr_err("tr_comm_table_add_memb: realm with unknown role added."); + cur=NULL; + } + + if (cur==NULL) { + /* no entry for this realm/comm/role, tack it on the end */ + for (cur=ctab->memberships; cur->next!=NULL; cur=cur->next) { } + cur->next=new; + } else { + /* Found an entry. Add to the end of its same-origin list. */ + while (cur->origin_next!=NULL) { + cur=cur->origin_next; + } + cur->origin_next=new; + } + + talloc_steal(ctab, new); +} + +/* Remove memb from ctab. Do not free anything. Do nothing if memb not in ctab. */ +void tr_comm_table_remove_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *memb) +{ + TR_COMM_MEMB *cur=NULL; /* for walking the main list */ + TR_COMM_MEMB *orig_cur=NULL; /* for walking the origin list */ + + if ((memb==NULL) || (ctab->memberships==NULL)) + return; + + /* see if it's the first member */ + if (ctab->memberships==memb) { + if (memb->origin_next!=NULL) { + memb->origin_next->next=memb->next; + ctab->memberships=memb->origin_next; + } else + ctab->memberships=memb->next; + + return; + } + + /* see if it's in first member's origin list */ + for (orig_cur=ctab->memberships; + orig_cur->origin_next!=NULL; + orig_cur=orig_cur->origin_next) { + if (orig_cur->origin_next==memb) { + orig_cur->origin_next=memb->origin_next; + return; + } + } + + /* now we have to walk the rest of the tree */ + for (cur=ctab->memberships; cur->next!=NULL; cur=cur->next) { + if (cur->next==memb) { + /* it matched an entry on the main list */ + if (memb->origin_next==NULL) + cur->next=memb->next; /* no origin list, just drop memb */ + else { + /* replace the entry in the main list with the next element on the origin list */ + memb->origin_next->next=memb->next; + cur->next=memb->origin_next; + } + return; + } else { + /* it was not on the main list, walk the origin list */ + for (orig_cur=cur; orig_cur->origin_next!=NULL; orig_cur=orig_cur->origin_next) { + if (orig_cur->origin_next==memb) { + orig_cur->origin_next=memb->origin_next; + return; /* just drop the element from the origin list */ + } + } + } + } + /* if we got here, cur->next was null. Still have to check the origin_next list */ + for (orig_cur=cur; orig_cur->origin_next!=NULL; orig_cur=orig_cur->origin_next) { + if (orig_cur->origin_next==memb) { + orig_cur->origin_next=memb->origin_next; + return; /* just drop the element from the origin list */ + } + } +} + +TR_NAME *tr_comm_memb_get_realm_id(TR_COMM_MEMB *memb) +{ + if (memb->rp!=NULL) + return tr_rp_realm_get_id(memb->rp); + else + return tr_idp_realm_get_id(memb->idp); +} + +/* find a membership from any origin */ +TR_COMM_MEMB *tr_comm_table_find_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm) +{ + TR_COMM_MEMB *cur=NULL; + TR_NAME *cur_realm_name=NULL; + + for (cur=ctab->memberships; cur!=NULL; cur=cur->next) { + cur_realm_name=tr_comm_memb_get_realm_id(cur); + if (cur_realm_name==NULL) { + tr_warning("tr_comm_table_find: encountered realm with no name."); + continue; + } + if ((0==tr_name_cmp(realm, cur_realm_name)) && + (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) { + return cur; + } + } + return NULL; +} + +/* find a membership from a particular origin */ +TR_COMM_MEMB *tr_comm_table_find_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin) +{ + TR_NAME *cur_orig=NULL; + TR_COMM_MEMB *cur=tr_comm_table_find_memb(ctab, realm, comm); + if (cur==NULL) + return NULL; /* no match */ + + /* had a match for comm/realm; find origin match */ + while (cur!=NULL) { + if (((origin==NULL) && (cur_orig==NULL)) || + ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig)))) + return cur; /* found a match */ + cur=cur->origin_next; + } + return NULL; /* no match */ +} + + +/* find an idp membership regardless of its origin */ +TR_COMM_MEMB *tr_comm_table_find_idp_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm) +{ + TR_COMM_MEMB *cur=NULL; + TR_IDP_REALM *idp_realm=NULL; + + for (cur=ctab->memberships; cur!=NULL; cur=cur->next) { + idp_realm=tr_comm_memb_get_idp_realm(cur); + if (idp_realm==NULL) + continue; /* was not an idp */ + + if ((0==tr_name_cmp(realm, idp_realm->realm_id)) && + (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) { + return cur; + } + } + return NULL; +} + +/* find an idp membership from a particular origin */ +TR_COMM_MEMB *tr_comm_table_find_idp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin) +{ + TR_NAME *cur_orig=NULL; + TR_COMM_MEMB *cur=tr_comm_table_find_idp_memb(ctab, realm, comm); + if (cur==NULL) + return NULL; /* no match */ + + /* had a match for comm/realm; find origin match */ + while (cur!=NULL) { + cur_orig=tr_comm_memb_get_origin(cur); + if (((origin==NULL) && (cur_orig==NULL)) || + ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig)))) + return cur; /* found a match */ + cur=cur->origin_next; + } + return NULL; /* no match */ +} + +/* find an rp membership from any origin */ +TR_COMM_MEMB *tr_comm_table_find_rp_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm) +{ + TR_COMM_MEMB *cur=NULL; + TR_RP_REALM *rp_realm=NULL; + + for (cur=ctab->memberships; cur!=NULL; cur=cur->next) { + rp_realm=tr_comm_memb_get_rp_realm(cur); + if (rp_realm==NULL) + continue; /* was not an rp */ + + if ((0==tr_name_cmp(realm, tr_rp_realm_get_id(rp_realm))) && + (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) { + return cur; + } + } + return NULL; +} + +/* find an rp membership from a particular origin */ +TR_COMM_MEMB *tr_comm_table_find_rp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin) +{ + TR_NAME *cur_orig=NULL; + TR_COMM_MEMB *cur=tr_comm_table_find_rp_memb(ctab, realm, comm); + if (cur==NULL) + return NULL; /* no match */ + + /* had a match for comm/realm; find origin match */ + while (cur!=NULL) { + cur_orig=tr_comm_memb_get_origin(cur); + if (((origin==NULL) && (cur_orig==NULL)) || + ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig)))) + return cur; /* found a match */ + cur=cur->origin_next; + } + return NULL; /* no match */ +} + +TR_COMM *tr_comm_table_find_comm(TR_COMM_TABLE *ctab, TR_NAME *comm_id) +{ + return tr_comm_lookup(ctab->comms, comm_id); +} + +void tr_comm_table_add_comm(TR_COMM_TABLE *ctab, TR_COMM *new) +{ + tr_comm_add(ctab->comms, new); + if (ctab->comms!=NULL) + talloc_steal(ctab, ctab->comms); /* make sure it's in the right context */ +} + +void tr_comm_table_remove_comm(TR_COMM_TABLE *ctab, TR_COMM *comm) +{ + tr_comm_remove(ctab->comms, comm); +} + +TR_RP_REALM *tr_comm_table_find_rp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id) +{ + return tr_rp_realm_lookup(ctab->rp_realms, realm_id); +} + +void tr_comm_table_add_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *new) +{ + tr_rp_realm_add(ctab->rp_realms, new); + if (ctab->rp_realms!=NULL) + talloc_steal(ctab, ctab->rp_realms); /* make sure it's in the right context */ +} + +void tr_comm_table_remove_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *realm) +{ + tr_rp_realm_remove(ctab->rp_realms, realm); +} + +TR_IDP_REALM *tr_comm_table_find_idp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id) +{ + return tr_idp_realm_lookup(ctab->idp_realms, realm_id); +} + +void tr_comm_table_add_idp_realm(TR_COMM_TABLE *ctab, TR_IDP_REALM *new) +{ + tr_idp_realm_add(ctab->idp_realms, new); + if (ctab->idp_realms!=NULL) + talloc_steal(ctab, ctab->idp_realms); /* make sure it's in the right context */ +} + +void tr_comm_table_remove_idp_realm(TR_COMM_TABLE *ctab, TR_IDP_REALM *realm) +{ + tr_idp_realm_remove(ctab->idp_realms, realm); +} + + +/* how many communities in the table? */ +size_t tr_comm_table_size(TR_COMM_TABLE *ctab) +{ + size_t count=0; + TR_COMM *this=ctab->comms; + while(this!=NULL) { + this=this->next; + count++; + } + return count; +} + +/* clean up unreferenced realms, etc */ +void tr_comm_table_sweep(TR_COMM_TABLE *ctab) +{ + tr_rp_realm_sweep(ctab->rp_realms); + tr_idp_realm_sweep(ctab->idp_realms); + tr_comm_sweep(ctab->comms); +} + + +const char *tr_realm_role_to_str(TR_REALM_ROLE role) +{ + switch(role) { + case TR_ROLE_IDP: + return "idp"; + case TR_ROLE_RP: + return "rp"; + default: + return NULL; + } +} + +TR_REALM_ROLE tr_realm_role_from_str(const char *s) +{ + if (strcmp(s, "idp")==0) + return TR_ROLE_IDP; + if (strcmp(s, "rp")==0) + return TR_ROLE_RP; + return TR_ROLE_UNKNOWN; +} + +static void tr_comm_table_print_provenance(FILE *f, json_t *prov) +{ + const char *s=NULL; + size_t ii=0; + + for (ii=0; ii> Membership table start <<\n"); + for (p1=ctab->memberships; p1!=NULL; p1=p1->next) { + fprintf(f, "* %s %s/%s\n %s (%p) - prov: ", + tr_realm_role_to_str(tr_comm_memb_get_role(p1)), + tr_comm_memb_get_realm_id(p1)->buf, + tr_comm_get_id(tr_comm_memb_get_comm(p1))->buf, + (tr_comm_memb_get_origin(p1)==NULL)?"null origin":(tr_comm_memb_get_origin(p1)->buf), + p1); + tr_comm_table_print_provenance(f, p1->provenance); + fprintf(f, "\n"); + for (p2=p1->origin_next; p2!=NULL; p2=p2->origin_next) { + fprintf(f, " %s (%p) - prov: ", + (tr_comm_memb_get_origin(p2)==NULL)?"null origin":(tr_comm_memb_get_origin(p2)->buf), + p2); + tr_comm_table_print_provenance(f, p2->provenance); + fprintf(f, "\n"); + } + fprintf(f, "\n"); + } +} diff --git a/common/tr_config.c b/common/tr_config.c index 0bdaab6..b451860 100644 --- a/common/tr_config.c +++ b/common/tr_config.c @@ -51,50 +51,95 @@ void tr_print_config (TR_CFG *cfg) { tr_notice("tr_print_config: Logging running trust router configuration."); - tr_print_comms(cfg->comms); + tr_print_comms(cfg->ctable); } -void tr_print_comms (TR_COMM *comm_list) { +void tr_print_comms (TR_COMM_TABLE *ctab) +{ TR_COMM *comm = NULL; - for (comm = comm_list; NULL != comm; comm = comm->next) { + for (comm = ctab->comms; NULL != comm; comm = comm->next) { tr_notice("tr_print_config: Community %s:", comm->id->buf); tr_notice("tr_print_config: - Member IdPs:"); - tr_print_comm_idps(comm->idp_realms); + tr_print_comm_idps(ctab, comm); tr_notice("tr_print_config: - Member RPs:"); - tr_print_comm_rps(comm->rp_realms); + tr_print_comm_rps(ctab, comm); } } -void tr_print_comm_idps (TR_IDP_REALM *idp_list) { +void tr_print_comm_idps(TR_COMM_TABLE *ctab, TR_COMM *comm) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_COMM_ITER *iter=NULL; TR_IDP_REALM *idp = NULL; char *s=NULL; - for (idp = idp_list; NULL != idp; idp = idp->comm_next) { - s=tr_idp_realm_to_str(NULL, idp); + iter=tr_comm_iter_new(tmp_ctx); + if (iter==NULL) { + tr_notice("tr_print_config: unable to allocate IdP iterator."); + talloc_free(tmp_ctx); + return; + } + + for (idp=tr_idp_realm_iter_first(iter, ctab, tr_comm_get_id(comm)); + NULL!=idp; + idp=tr_idp_realm_iter_next(iter)) { + s=tr_idp_realm_to_str(tmp_ctx, idp); if (s!=NULL) tr_notice("tr_print_config: - @%s", s); else - tr_notice("tr_print_config: unable to allocate idp output string."); + tr_notice("tr_print_config: unable to allocate IdP output string."); } + talloc_free(tmp_ctx); } -void tr_print_comm_rps(TR_RP_REALM *rp_list) { +void tr_print_comm_rps(TR_COMM_TABLE *ctab, TR_COMM *comm) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_COMM_ITER *iter=NULL; TR_RP_REALM *rp = NULL; + char *s=NULL; - for (rp = rp_list; NULL != rp; rp = rp->next) { - tr_notice("tr_print_config: - %s", rp->realm_name->buf); + iter=tr_comm_iter_new(tmp_ctx); + if (iter==NULL) { + tr_notice("tr_print_config: unable to allocate RP iterator."); + talloc_free(tmp_ctx); + return; + } + + for (rp=tr_rp_realm_iter_first(iter, ctab, tr_comm_get_id(comm)); + NULL!=rp; + rp=tr_rp_realm_iter_next(iter)) { + s=tr_rp_realm_to_str(tmp_ctx, rp); + if (s!=NULL) + tr_notice("tr_print_config: - @%s", s); + else + tr_notice("tr_print_config: unable to allocate RP output string."); } + talloc_free(tmp_ctx); } TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx) { - return talloc_zero(mem_ctx, TR_CFG); + 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) { +void tr_cfg_free (TR_CFG *cfg) +{ talloc_free(cfg); } @@ -139,6 +184,9 @@ static TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jcfg) 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)) @@ -249,6 +297,42 @@ static TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jcfg) 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)) { @@ -651,7 +735,7 @@ static TR_APC *tr_cfg_parse_apcs(TALLOC_CTX *mem_ctx, json_t *japcs, TR_CFG_RC * *rc=TR_CFG_NOPARSE; goto cleanup; } - apcs=tr_apc_add(apcs, new_apc); + tr_apc_add(apcs, new_apc); } talloc_steal(mem_ctx, apcs); @@ -932,7 +1016,7 @@ static TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealm *rc=TR_CFG_NOPARSE; goto cleanup; } - realms=tr_idp_realm_add(realms, new_realm); + tr_idp_realm_add(realms, new_realm); } else if (tr_cfg_is_remote_realm(this_jrealm)) { new_realm=tr_cfg_parse_one_remote_realm(tmp_ctx, this_jrealm, rc); if ((*rc)!=TR_CFG_SUCCESS) { @@ -940,7 +1024,7 @@ static TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealm *rc=TR_CFG_NOPARSE; goto cleanup; } - realms=tr_idp_realm_add(realms, new_realm); + tr_idp_realm_add(realms, new_realm); } } @@ -952,44 +1036,6 @@ cleanup: return realms; } -#if 0 -static TR_IDP_REALM *tr_cfg_parse_remote_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_remote_realms: realms not an array"); - *rc=TR_CFG_BAD_PARAMS; - goto cleanup; - } - - for (ii=0; iitype=TR_COMM_APC; /* realms added this way are in APCs */ comm->expiration_interval=TR_DEFAULT_APC_EXPIRATION_INTERVAL; - comm->id=tr_dup_name(apc->id); + tr_comm_set_id(comm, tr_dup_name(apc->id)); tr_comm_add_idp_realm(comm, realm); - new_comms=tr_comm_add(new_comms, comm); + tr_comm_add(new_comms, comm); } else { /* add this realm to the comm */ tr_comm_add_idp_realm(comm, realm); @@ -1387,7 +1437,7 @@ static TR_COMM *tr_cfg_comm_idp_update(TALLOC_CTX *mem_ctx, TR_COMM *comms, TR_I } /* we successfully built a list, add it to the other list */ - comms=tr_comm_add(comms, new_comms); + tr_comm_add(comms, new_comms); talloc_steal(mem_ctx, comms); cleanup: talloc_free(tmp_ctx); @@ -1443,12 +1493,12 @@ 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) { - trc->idp_realms=tr_idp_realm_add(trc->idp_realms, new_idp_realms); /* fixes talloc contexts except for head*/ - talloc_steal(trc, trc->idp_realms); /* make sure the head is in the right context */ + 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) { - trc->rp_clients=tr_rp_client_add(trc->rp_clients, new_rp_clients); /* fixes talloc contexts */ + 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 */ } } @@ -1601,11 +1651,9 @@ static TR_CFG_RC tr_cfg_parse_default_servers (TR_CFG *trc, json_t *jcfg) return rc; } -static TR_IDP_REALM *tr_cfg_parse_comm_idps (TR_CFG *trc, json_t *jidps, TR_CFG_RC *rc) +static void tr_cfg_parse_comm_idps(TR_CFG *trc, json_t *jidps, TR_COMM *comm, TR_CFG_RC *rc) { - TR_IDP_REALM *idp = NULL; - TR_IDP_REALM *found_idp = NULL; - TR_IDP_REALM *temp_idp = NULL; + TR_IDP_REALM *found_idp=NULL; int i = 0; if ((!trc) || @@ -1613,73 +1661,91 @@ static TR_IDP_REALM *tr_cfg_parse_comm_idps (TR_CFG *trc, json_t *jidps, TR_CFG_ (!json_is_array(jidps))) { if (rc) *rc = TR_CFG_BAD_PARAMS; - return NULL; + return; } - for (i = 0; i < json_array_size(jidps); i++) { - if (NULL == (temp_idp = talloc(trc, TR_IDP_REALM))) { - tr_debug("tr_cfg_parse_comm_idps: Can't allocate memory for IdP Realm."); - if (rc) - *rc = TR_CFG_NOMEM; - return NULL; - } - memset (temp_idp, 0, sizeof(TR_IDP_REALM)); - - if (NULL == (found_idp = (tr_cfg_find_idp(trc, - tr_new_name((char *)json_string_value(json_array_get(jidps, i))), - rc)))) { + for (i=0; i < json_array_size(jidps); i++) { + found_idp=tr_cfg_find_idp(trc, + tr_new_name((char *)json_string_value(json_array_get(jidps, i))), + rc); + if ((found_idp==NULL) || (*rc!=TR_CFG_SUCCESS)) { tr_debug("tr_cfg_parse_comm_idps: Unknown IDP %s.", (char *)json_string_value(json_array_get(jidps, i))); - return NULL; + *rc=TR_CFG_ERROR; + return; } - - // We *MUST* do a dereferenced copy here or the second community will corrupt the linked list we create here. - *temp_idp = *found_idp; - - temp_idp->comm_next = idp; - idp = temp_idp; + tr_comm_add_idp_realm(trc->ctable, comm, found_idp, 0, NULL, NULL); /* no provenance, never expires */ } - return idp; + *rc=TR_CFG_SUCCESS; + return; } -static TR_RP_REALM *tr_cfg_parse_comm_rps (TR_CFG *trc, json_t *jrps, TR_CFG_RC *rc) +static void tr_cfg_parse_comm_rps(TR_CFG *trc, json_t *jrps, TR_COMM *comm, TR_CFG_RC *rc) { - TR_RP_REALM *rp = NULL; - TR_RP_REALM *temp_rp = NULL; - int i = 0; + 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 NULL; + return; } - for (i = (json_array_size(jrps)-1); i >= 0; i--) { - if (NULL == (temp_rp = talloc_zero(trc, TR_RP_REALM))) { - tr_debug("tr_cfg_parse_comm_rps: Can't allocate memory for RP Realm."); - if (rc) - *rc = TR_CFG_NOMEM; - return NULL; + for (ii=0; iilen, tr_comm_get_id(comm)->buf); + continue; } - if (NULL == (temp_rp->realm_name = tr_new_name((char *)json_string_value(json_array_get(jrps, i))))) { - tr_debug("tr_cfg_parse_comm_rps: No memory for RP Realm Name."); - if (rc) - *rc = TR_CFG_NOMEM; - return NULL; + /* 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); } - temp_rp->next = rp; - rp = temp_rp; - } + /* 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; + } - return rp; + /* 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 (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc) { +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; @@ -1691,13 +1757,14 @@ static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc tr_debug("tr_cfg_parse_one_comm: Bad parameters."); if (rc) *rc = TR_CFG_BAD_PARAMS; - return NULL; + goto cleanup; } - if (NULL == (comm = talloc_zero(trc, TR_COMM))) { + comm=tr_comm_new(tmp_ctx); + if (comm==NULL) { tr_crit("tr_cfg_parse_one_comm: Out of memory."); *rc = TR_CFG_NOMEM; - return NULL; + goto cleanup; } @@ -1713,13 +1780,16 @@ static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc (!json_is_array(jrps))) { tr_debug("tr_cfg_parse_one_comm: Error parsing Communities configuration."); *rc = TR_CFG_NOPARSE; - return NULL; + comm=NULL; + goto cleanup; } - if (NULL == (comm->id = tr_new_name((char *)json_string_value(jid)))) { + 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; - return NULL; + comm=NULL; + goto cleanup; } if (0 == strcmp(json_string_value(jtype), "apc")) { @@ -1727,29 +1797,33 @@ static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc } 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.", comm->id->buf); - tr_free_name(comm->id); - return NULL; + 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", comm->id->buf, json_string_value(jtype)); - tr_free_name(comm->id); + 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; - return NULL; + comm=NULL; + goto cleanup; } - comm->idp_realms = tr_cfg_parse_comm_idps(trc, jidps, rc); + 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.", comm->id->buf); - tr_free_name(comm->id); - return NULL; + 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; } - comm->rp_realms = tr_cfg_parse_comm_rps(trc, jrps, rc); + tr_cfg_parse_comm_rps(trc, jrps, comm, rc); if (TR_CFG_SUCCESS != *rc) { - tr_debug("tr_cfg_parse_comm: Can't parse RP realms for comm %s .", comm->id->buf); - tr_free_name(comm->id); - return NULL; + 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) { @@ -1757,8 +1831,9 @@ static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc comm->expiration_interval = 43200; /*30 days*/ if (jexpire) { if (!json_is_integer(jexpire)) { - fprintf(stderr, "tr_parse_comm: expirae_interval is not an integer\n"); - return NULL; + fprintf(stderr, "tr_parse_one_comm: expiration_interval is not an integer\n"); + comm=NULL; + goto cleanup; } comm->expiration_interval = json_integer_value(jexpire); if (comm->expiration_interval <= 10) @@ -1767,7 +1842,11 @@ static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc comm->expiration_interval = 129600; } } - + +cleanup: + if (comm!=NULL) + talloc_steal(mem_ctx, comm); + talloc_free(tmp_ctx); return comm; } @@ -1789,14 +1868,16 @@ static TR_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg) } for (i = 0; i < json_array_size(jcomms); i++) { - if (NULL == (comm = tr_cfg_parse_one_comm(trc, - json_array_get(jcomms, i), - &rc))) { - return rc; + 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.", comm->id->buf); - comm->next = trc->comms; - trc->comms = comm; + 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); @@ -1821,12 +1902,12 @@ TR_CFG_RC tr_cfg_validate(TR_CFG *trc) rc = TR_CFG_ERROR; } - if (NULL == trc->comms) { + if (0==tr_comm_table_size(trc->ctable)) { tr_debug("tr_cfg_validate: Error: No Communities configured"); rc = TR_CFG_ERROR; } - if ((NULL == trc->default_servers) && (NULL == trc->idp_realms)) { + if ((NULL == trc->default_servers) && (NULL == trc->ctable->idp_realms)) { tr_debug("tr_cfg_validate: Error: No default servers or IDPs configured."); rc = TR_CFG_ERROR; } @@ -1949,7 +2030,7 @@ TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc) return NULL; } - for (cfg_idp = tr_cfg->idp_realms; NULL != cfg_idp; cfg_idp = cfg_idp->next) { + 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; diff --git a/common/tr_constraint.c b/common/tr_constraint.c index 44a60ba..a155566 100644 --- a/common/tr_constraint.c +++ b/common/tr_constraint.c @@ -76,6 +76,29 @@ void tr_constraint_free(TR_CONSTRAINT *cons) talloc_free(cons); } +TR_CONSTRAINT *tr_constraint_dup(TALLOC_CTX *mem_ctx, TR_CONSTRAINT *cons) +{ + TALLOC_CTX *tmp_ctx=NULL; + TR_CONSTRAINT *new=NULL; + int ii=0; + + if (cons==NULL) + return NULL; + + tmp_ctx=talloc_new(NULL); + new=tr_constraint_new(tmp_ctx); + + if (new!=NULL) { + new->type=tr_dup_name(cons->type); + for (ii=0; iimatches[ii]=tr_dup_name(cons->matches[ii]); + talloc_steal(mem_ctx, new); + } + + talloc_free(tmp_ctx); + return new; +} + /* Returns TRUE (1) if the the string (str) matchs the wildcard string (wc_str), FALSE (0) if not. */ int tr_prefix_wildcard_match (const char *str, const char *wc_str) { diff --git a/common/tr_dh.c b/common/tr_dh.c index b1d1e17..031b404 100644 --- a/common/tr_dh.c +++ b/common/tr_dh.c @@ -77,6 +77,11 @@ unsigned char tr_2048_dhprime[2048/8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +DH *tr_dh_new(void) +{ + return DH_new(); +} + DH *tr_create_dh_params(unsigned char *priv_key, size_t keylen) { @@ -170,6 +175,66 @@ void tr_destroy_dh_params(DH *dh) { } } +DH *tr_dh_dup(DH *in) +{ + DH *out=DH_new(); + + if (out==NULL) + return NULL; + + if (in->g==NULL) + out->g=NULL; + else { + out->g=BN_dup(in->g); + if (out->g==NULL) { + DH_free(out); + return NULL; + } + } + + if (in->p==NULL) + out->p=NULL; + else { + out->p=BN_dup(in->p); + if (out->p==NULL) { + DH_free(out); + return NULL; + } + } + + if (in->q==NULL) + out->q=NULL; + else { + out->q=BN_dup(in->q); + if (out->q==NULL) { + DH_free(out); + return NULL; + } + } + + if (in->priv_key==NULL) + out->priv_key=NULL; + else { + out->priv_key=BN_dup(in->priv_key); + if (out->priv_key==NULL) { + DH_free(out); + return NULL; + } + } + + if (in->pub_key==NULL) + out->pub_key=NULL; + else { + out->pub_key=BN_dup(in->pub_key); + if (out->g==NULL) { + DH_free(out); + return NULL; + } + } + + return out; +} + int tr_compute_dh_key(unsigned char **pbuf, BIGNUM *pub_key, DH *priv_dh) { @@ -212,14 +277,21 @@ int tr_dh_pub_hash(TID_REQ *request, unsigned char *bn_bytes = talloc_zero_size(request, BN_num_bytes(pub)); unsigned char *digest = talloc_zero_size(request, SHA_DIGEST_LENGTH+1); assert(bn_bytes && digest); - BN_bn2bin(pub, bn_bytes); - SHA1(bn_bytes, BN_num_bytes(pub), digest); - *out_digest = digest; - *out_len = SHA_DIGEST_LENGTH; - return 0; + BN_bn2bin(pub, bn_bytes); + SHA1(bn_bytes, BN_num_bytes(pub), digest); + *out_digest = digest; + *out_len = SHA_DIGEST_LENGTH; + + talloc_free(bn_bytes); + return 0; } void tr_dh_free(unsigned char *dh_buf) { free(dh_buf); } + +void tr_dh_destroy(DH *dh) +{ + DH_free(dh); +} diff --git a/common/tr_idp.c b/common/tr_idp.c index 2ef9e93..10bb2c3 100644 --- a/common/tr_idp.c +++ b/common/tr_idp.c @@ -33,10 +33,12 @@ */ #include +#include #include #include #include +#include static int tr_aaa_server_destructor(void *obj) { @@ -50,6 +52,7 @@ TR_AAA_SERVER *tr_aaa_server_new(TALLOC_CTX *mem_ctx, TR_NAME *hostname) { TR_AAA_SERVER *aaa=talloc(mem_ctx, TR_AAA_SERVER); if (aaa!=NULL) { + aaa->next=NULL; aaa->hostname=hostname; talloc_set_destructor((void *)aaa, tr_aaa_server_destructor); } @@ -61,7 +64,33 @@ void tr_aaa_server_free(TR_AAA_SERVER *aaa) talloc_free(aaa); } -TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_realm_name, TR_NAME *comm) +TR_AAA_SERVER_ITER *tr_aaa_server_iter_new(TALLOC_CTX *mem_ctx) +{ + return talloc(mem_ctx, TR_AAA_SERVER_ITER); +} + +void tr_aaa_server_iter_free(TR_AAA_SERVER_ITER *iter) +{ + talloc_free(iter); +} + +TR_AAA_SERVER *tr_aaa_server_iter_first(TR_AAA_SERVER_ITER *iter, TR_AAA_SERVER *aaa) +{ + iter->this=aaa; + return iter->this; +} + +TR_AAA_SERVER *tr_aaa_server_iter_next(TR_AAA_SERVER_ITER *iter) +{ + if (iter->this!=NULL) { + iter->this=iter->this->next; + } + return iter->this; +} + + +/* fills in shared if pointer not null */ +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_IDP_REALM *idp = NULL; @@ -71,9 +100,11 @@ TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_r break; } } - if (idp) + if (idp) { + if (shared_out!=NULL) + *shared_out=idp->shared_config; return idp->aaa_servers; - else + } else return NULL; } @@ -113,6 +144,64 @@ TR_IDP_REALM *tr_idp_realm_new(TALLOC_CTX *mem_ctx) return idp; } +void tr_idp_realm_free(TR_IDP_REALM *idp) +{ + talloc_free(idp); +} + +TR_NAME *tr_idp_realm_get_id(TR_IDP_REALM *idp) +{ + if (idp==NULL) + return NULL; + + return idp->realm_id; +} + +TR_NAME *tr_idp_realm_dup_id(TR_IDP_REALM *idp) +{ + if (idp==NULL) + return NULL; + + return tr_dup_name(tr_idp_realm_get_id(idp)); +} + +void tr_idp_realm_set_id(TR_IDP_REALM *idp, TR_NAME *id) +{ + if (idp->realm_id!=NULL) + tr_free_name(idp->realm_id); + idp->realm_id=id; +} + +void tr_idp_realm_set_apcs(TR_IDP_REALM *idp, TR_APC *apc) +{ + if (idp->apcs!=NULL) + tr_apc_free(idp->apcs); + idp->apcs=apc; + talloc_steal(idp, apc); +} + +TR_APC *tr_idp_realm_get_apcs(TR_IDP_REALM *idp) +{ + return idp->apcs; +} + +TR_IDP_REALM *tr_idp_realm_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_name) +{ + TR_IDP_REALM *idp = NULL; + + if (!idp_name) { + tr_debug("tr_idp_realm_lookup: Bad parameters."); + return NULL; + } + + for (idp=idp_realms; NULL!=idp; idp=idp->next) { + if (0==tr_name_cmp(tr_idp_realm_get_id(idp), idp_name)) + return idp; + } + return NULL; +} + + static TR_IDP_REALM *tr_idp_realm_tail(TR_IDP_REALM *idp) { if (idp==NULL) @@ -123,8 +212,8 @@ static TR_IDP_REALM *tr_idp_realm_tail(TR_IDP_REALM *idp) return idp; } -/* for correct behavior, call like: idp_realms=tr_idp_realm_add(idp_realms, new_realm); */ -TR_IDP_REALM *tr_idp_realm_add(TR_IDP_REALM *head, TR_IDP_REALM *new) +/* do not call directly, use the tr_idp_realm_add() macro */ +TR_IDP_REALM *tr_idp_realm_add_func(TR_IDP_REALM *head, TR_IDP_REALM *new) { if (head==NULL) head=new; @@ -138,6 +227,37 @@ TR_IDP_REALM *tr_idp_realm_add(TR_IDP_REALM *head, TR_IDP_REALM *new) return head; } +/* use the macro */ +TR_IDP_REALM *tr_idp_realm_remove_func(TR_IDP_REALM *head, TR_IDP_REALM *remove) +{ + TALLOC_CTX *list_ctx=talloc_parent(head); + TR_IDP_REALM *this=NULL; + + if (head==NULL) + return NULL; + + if (head==remove) { + /* if we're removing the head, put the next element (if present) into the context + * the list head was in. */ + head=head->next; + if (head!=NULL) { + talloc_steal(list_ctx, head); + /* now put all the other elements in the context of the list head */ + for (this=head->next; this!=NULL; this=this->next) + talloc_steal(head, this); + } + } else { + /* not removing the head; no need to play with contexts */ + for (this=head; this->next!=NULL; this=this->next) { + if (this->next==remove) { + this->next=remove->next; + break; + } + } + } + return head; +} + static int tr_idp_realm_apc_count(TR_IDP_REALM *idp) { int ii=0; @@ -237,3 +357,46 @@ char *tr_idp_realm_to_str(TALLOC_CTX *mem_ctx, TR_IDP_REALM *idp) talloc_free(tmp_ctx); return result; } + +void tr_idp_realm_incref(TR_IDP_REALM *realm) +{ + realm->refcount++; +} + +void tr_idp_realm_decref(TR_IDP_REALM *realm) +{ + if (realm->refcount>0) + realm->refcount--; +} + +/* remove any with zero refcount + * Call via macro. */ +TR_IDP_REALM *tr_idp_realm_sweep_func(TR_IDP_REALM *head) +{ + TR_IDP_REALM *idp=NULL; + TR_IDP_REALM *old_next=NULL; + + if (head==NULL) + return NULL; + + while ((head!=NULL) && (head->refcount==0)) { + idp=head; /* keep a pointer so we can remove it */ + tr_idp_realm_remove(head, idp); /* use this to get talloc contexts right */ + tr_idp_realm_free(idp); + } + + if (head==NULL) + return NULL; + + /* will not remove the head here, that has already been done */ + for (idp=head; idp->next!=NULL; idp=idp->next) { + if (idp->next->refcount==0) { + old_next=idp->next; + tr_idp_realm_remove(head, idp->next); /* changes idp->next */ + tr_idp_realm_free(old_next); + } + } + + return head; +} + diff --git a/common/tr_mq.c b/common/tr_mq.c index 60313c6..07080b6 100644 --- a/common/tr_mq.c +++ b/common/tr_mq.c @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include @@ -107,11 +109,22 @@ static void tr_mq_msg_set_next(TR_MQ_MSG *msg, TR_MQ_MSG *next) TR_MQ *tr_mq_new(TALLOC_CTX *mem_ctx) { TR_MQ *mq=talloc(mem_ctx, TR_MQ); + pthread_condattr_t cattr; + if (mq!=NULL) { pthread_mutex_init(&(mq->mutex), 0); + pthread_condattr_init(&cattr); + + pthread_condattr_setclock(&cattr, CLOCK_MONOTONIC); /* use the monotonic clock for timeouts */ + pthread_cond_init(&(mq->have_msg_cond), &cattr); + pthread_condattr_destroy(&cattr); + mq->head=NULL; mq->tail=NULL; mq->last_hi_prio=NULL; + + mq->notify_cb=NULL; + mq->notify_cb_arg=NULL; } return mq; } @@ -252,6 +265,12 @@ void tr_mq_add(TR_MQ *mq, TR_MQ_MSG *msg) tr_mq_print(mq); #endif + /* Before releasing the lock, signal any waiting threads that there's now + * something in the queue. Used for blocking tr_mq_pop() call. */ + + if (was_empty) + pthread_cond_broadcast(&(mq->have_msg_cond)); + tr_mq_unlock(mq); /* see if we need to tell someone we became non-empty */ @@ -259,12 +278,44 @@ void tr_mq_add(TR_MQ *mq, TR_MQ_MSG *msg) notify_cb(mq, notify_cb_arg); } -/* caller must free msg via tr_mq_msg_free */ -TR_MQ_MSG *tr_mq_pop(TR_MQ *mq) +/* Compute an absolute time from a desired timeout interval for use with tr_mq_pop(). + * Fills in *ts and returns 0 on success. */ +int tr_mq_pop_timeout(time_t seconds, struct timespec *ts) { - TR_MQ_MSG *popped=NULL; + if (0!=clock_gettime(CLOCK_MONOTONIC, ts)) + return -1; + ts->tv_sec+=seconds; + return 0; +} + +/* Caller must free msg via tr_mq_msg_free, waiting until absolute + * time ts_abort before giving up (using CLOCK_MONOTONIC). If ts_abort + * has passed, returns an existing message but will not wait if one is + * not already available. If ts_abort is null, no blocking. Not + * guaranteed to wait if an error occurs - immediately returns without + * a message. Use tr_mq_pop_timeout() to get an absolute time that + * is guaranteed compatible with this function. */ +TR_MQ_MSG *tr_mq_pop(TR_MQ *mq, struct timespec *ts_abort) +{ + TR_MQ_MSG *popped=NULL; + int wait_err=0; + tr_mq_lock(mq); + if ((tr_mq_get_head(mq)==NULL) && (ts_abort!=NULL)) { + /* No msgs yet, and blocking was requested */ + while ((wait_err==0) && (NULL==tr_mq_get_head(mq))) + wait_err=pthread_cond_timedwait(&(mq->have_msg_cond), + &(mq->mutex), + ts_abort); + + if ((wait_err!=0) && (wait_err!=ETIMEDOUT)) { + tr_notice("tr_mq_pop: error waiting for message."); + return NULL; + } + /* if it timed out, ok to go ahead and check once more for a message, so no special exit */ + } + if (tr_mq_get_head(mq)!=NULL) { popped=tr_mq_get_head(mq); tr_mq_set_head(mq, tr_mq_msg_get_next(popped)); /* popped is the old head */ diff --git a/common/tr_msg.c b/common/tr_msg.c index 9591cd4..9b205d3 100644 --- a/common/tr_msg.c +++ b/common/tr_msg.c @@ -36,15 +36,19 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include #include +#include #include /* JSON helpers */ @@ -68,23 +72,23 @@ static int tr_msg_get_json_integer(json_t *jmsg, const char *attr, int *dest) /* Read attribute attr from msg as a string. Copies string into mem_ctx context so jmsg can * be destroyed safely. Returns nonzero on error. */ -static int tr_msg_get_json_string(json_t *jmsg, const char *attr, char **dest, TALLOC_CTX *mem_ctx) +static TRP_RC tr_msg_get_json_string(json_t *jmsg, const char *attr, char **dest, TALLOC_CTX *mem_ctx) { json_t *obj; obj=json_object_get(jmsg, attr); if (obj == NULL) - return -1; + return TRP_ERROR; /* check type */ if (!json_is_string(obj)) - return -1; + return TRP_ERROR; *dest=talloc_strdup(mem_ctx, json_string_value(obj)); if (*dest==NULL) - return -1; + return TRP_ERROR; - return 0; + return TRP_SUCCESS; } enum msg_type tr_msg_get_msg_type(TR_MSG *msg) @@ -133,6 +137,7 @@ TRP_UPD *tr_msg_get_trp_upd(TR_MSG *msg) 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; } @@ -153,19 +158,23 @@ static json_t *tr_msg_encode_dh(DH *dh) { json_t *jdh = NULL; json_t *jbn = NULL; + char *s=NULL; if ((!dh) || (!dh->p) || (!dh->g) || (!dh->pub_key)) return NULL; jdh = json_object(); - jbn = json_string(BN_bn2hex(dh->p)); + jbn = json_string(s=BN_bn2hex(dh->p)); + OPENSSL_free(s); json_object_set_new(jdh, "dh_p", jbn); - jbn = json_string(BN_bn2hex(dh->g)); + jbn = json_string(s=BN_bn2hex(dh->g)); + OPENSSL_free(s); json_object_set_new(jdh, "dh_g", jbn); - jbn = json_string(BN_bn2hex(dh->pub_key)); + jbn = json_string(s=BN_bn2hex(dh->pub_key)); + OPENSSL_free(s); json_object_set_new(jdh, "dh_pub_key", jbn); return jdh; @@ -178,19 +187,17 @@ static DH *tr_msg_decode_dh(json_t *jdh) json_t *jg = NULL; json_t *jpub_key = NULL; - if (!(dh = malloc(sizeof(DH)))) { + if (!(dh=tr_dh_new())) { tr_crit("tr_msg_decode_dh(): Error allocating DH structure."); return NULL; } - memset(dh, 0, sizeof(DH)); - /* store required fields from dh object */ if ((NULL == (jp = json_object_get(jdh, "dh_p"))) || (NULL == (jg = json_object_get(jdh, "dh_g"))) || (NULL == (jpub_key = json_object_get(jdh, "dh_pub_key")))) { tr_debug("tr_msg_decode_dh(): Error parsing dh_info."); - free(dh); + tr_dh_destroy(dh); return NULL; } @@ -315,8 +322,7 @@ static json_t *tr_msg_encode_one_server(TID_SRVR_BLK *srvr) jsrvr = json_object(); - /* Server IP Address -- TBD handle IPv6 */ - jstr = json_string(inet_ntoa(srvr->aaa_server_addr)); + jstr = json_string(srvr->aaa_server_addr); json_object_set_new(jsrvr, "server_addr", jstr); json_object_set_new(jsrvr, @@ -328,7 +334,7 @@ static json_t *tr_msg_encode_one_server(TID_SRVR_BLK *srvr) json_object_set_new(jsrvr, "server_dh", tr_msg_encode_dh(srvr->aaa_server_dh)); if (srvr->path) /* The path is owned by the srvr, so grab an extra ref*/ - json_object_set(jsrvr, "path", srvr->path); + json_object_set(jsrvr, "path", (json_t *)(srvr->path)); return jsrvr; } @@ -349,12 +355,11 @@ static int tr_msg_decode_one_server(json_t *jsrvr, TID_SRVR_BLK *srvr) tr_notice("tr_msg_decode_one_server(): Error parsing required fields."); return -1; } - - /* TBD -- handle IPv6 Addresses */ - inet_aton(json_string_value(jsrvr_addr), &(srvr->aaa_server_addr)); + + srvr->aaa_server_addr=talloc_strdup(srvr, json_string_value(jsrvr_addr)); srvr->key_name = tr_new_name((char *)json_string_value(jsrvr_kn)); srvr->aaa_server_dh = tr_msg_decode_dh(jsrvr_dh); - srvr->path = json_object_get(jsrvr, "path"); + tid_srvr_blk_set_path(srvr, (TID_PATH *) json_object_get(jsrvr, "path")); jsrvr_expire = json_object_get(jsrvr, "key_expiration"); if (jsrvr_expire && json_is_string(jsrvr_expire)) { if (!g_time_val_from_iso8601(json_string_value(jsrvr_expire), @@ -386,9 +391,11 @@ static json_t *tr_msg_encode_servers(TID_RESP *resp) return jservers; } -static TID_SRVR_BLK *tr_msg_decode_servers(void * ctx, json_t *jservers, size_t *out_len) +static TID_SRVR_BLK *tr_msg_decode_servers(TALLOC_CTX *mem_ctx, json_t *jservers) { - TID_SRVR_BLK *servers = NULL; + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TID_SRVR_BLK *servers=NULL; + TID_SRVR_BLK *new_srvr=NULL; json_t *jsrvr; size_t i, num_servers; @@ -397,20 +404,30 @@ static TID_SRVR_BLK *tr_msg_decode_servers(void * ctx, json_t *jservers, size_t if (0 == num_servers) { tr_debug("tr_msg_decode_servers(): Server array is empty."); - return NULL; + goto cleanup; } - servers = talloc_zero_array(ctx, TID_SRVR_BLK, num_servers); for (i = 0; i < num_servers; i++) { jsrvr = json_array_get(jservers, i); - if (0 != tr_msg_decode_one_server(jsrvr, &servers[i])) { - talloc_free(servers); - return NULL; - } + new_srvr=tid_srvr_blk_new(tmp_ctx); + if (new_srvr==NULL) { + servers=NULL; /* it's all in tmp_ctx, so we can just let go */ + goto cleanup; + } + + if (0 != tr_msg_decode_one_server(jsrvr, new_srvr)) { + servers=NULL; /* it's all in tmp_ctx, so we can just let go */ + goto cleanup; + } + tid_srvr_blk_add(servers, new_srvr); } - *out_len = num_servers; + + talloc_steal(mem_ctx, servers); + +cleanup: + talloc_free(tmp_ctx); return servers; } @@ -501,7 +518,7 @@ static TID_RESP *tr_msg_decode_tidresp(json_t *jresp) tr_debug("tr_msg_decode_tidresp(): Success! result = %s.", json_string_value(jresult)); if ((NULL != (jservers = json_object_get(jresp, "servers"))) || (!json_is_array(jservers))) { - tresp->servers = tr_msg_decode_servers(tresp, jservers, &tresp->num_servers); + tresp->servers = tr_msg_decode_servers(tresp, jservers); } else { talloc_free(tresp); @@ -514,18 +531,22 @@ static TID_RESP *tr_msg_decode_tidresp(json_t *jresp) tr_debug("tr_msg_decode_tidresp(): Error! result = %s.", json_string_value(jresult)); if ((NULL != (jerr_msg = json_object_get(jresp, "err_msg"))) || (!json_is_string(jerr_msg))) { - tresp->err_msg = tr_new_name((char *)json_string_value(jerr_msg)); - } + tresp->err_msg = tr_new_name(json_string_value(jerr_msg)); + } else + tresp->err_msg = tr_new_name("No error message set."); + + if (NULL !=(tresp->error_path = json_object_get(jresp, "error_path"))) + json_incref(tresp->error_path); } - tresp->rp_realm = tr_new_name((char *)json_string_value(jrp_realm)); - tresp->realm = tr_new_name((char *)json_string_value(jrealm)); - tresp->comm = tr_new_name((char *)json_string_value(jcomm)); + tresp->rp_realm = tr_new_name(json_string_value(jrp_realm)); + tresp->realm = tr_new_name(json_string_value(jrealm)); + tresp->comm = tr_new_name(json_string_value(jcomm)); /* store optional "orig_coi" field */ if ((NULL != (jorig_coi = json_object_get(jresp, "orig_coi"))) && (!json_is_object(jorig_coi))) { - tresp->orig_coi = tr_new_name((char *)json_string_value(jorig_coi)); + tresp->orig_coi = tr_new_name(json_string_value(jorig_coi)); } return tresp; @@ -534,7 +555,7 @@ static TID_RESP *tr_msg_decode_tidresp(json_t *jresp) /* Information records for TRP update msg * requires that jrec already be allocated */ -static TRP_RC tr_msg_encode_inforec_route(json_t *jrec, TRP_INFOREC *rec ) +static TRP_RC tr_msg_encode_inforec_route(json_t *jrec, TRP_INFOREC *rec) { json_t *jstr=NULL; json_t *jint=NULL; @@ -543,43 +564,155 @@ static TRP_RC tr_msg_encode_inforec_route(json_t *jrec, TRP_INFOREC *rec ) if (rec==NULL) return TRP_BADTYPE; - if ((trp_inforec_get_comm(rec)==NULL) - || (trp_inforec_get_realm(rec)==NULL) - || (trp_inforec_get_trust_router(rec)==NULL)) { + if (trp_inforec_get_trust_router(rec)==NULL) return TRP_ERROR; - } - s=tr_name_strdup(trp_inforec_get_comm(rec)); + s=tr_name_strdup(trp_inforec_get_trust_router(rec)); if (s==NULL) return TRP_NOMEM; jstr=json_string(s); free(s);s=NULL; if(jstr==NULL) return TRP_ERROR; - json_object_set_new(jrec, "community", jstr); + json_object_set_new(jrec, "trust_router", jstr); + + jint=json_integer(trp_inforec_get_metric(rec)); + if(jint==NULL) + return TRP_ERROR; + json_object_set_new(jrec, "metric", jint); - s=tr_name_strdup(trp_inforec_get_realm(rec)); - if (s==NULL) - return TRP_NOMEM; - jstr=json_string(s); - free(s);s=NULL; + jint=json_integer(trp_inforec_get_interval(rec)); + if(jint==NULL) + return TRP_ERROR; + json_object_set_new(jrec, "interval", jint); + + return TRP_SUCCESS; +} + +/* returns a json array */ +static json_t *tr_msg_encode_apcs(TR_APC *apcs) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_APC_ITER *iter=tr_apc_iter_new(tmp_ctx); + TR_APC *apc=NULL; + json_t *jarray=NULL; + json_t *jid=NULL; + + if (iter==NULL) + goto cleanup; + + jarray=json_array(); + if (jarray==NULL) + goto cleanup; + + for (apc=tr_apc_iter_first(iter, apcs); apc!=NULL; apc=tr_apc_iter_next(iter)) { + jid=tr_name_to_json_string(tr_apc_get_id(apc)); + if ((jid==NULL) || (json_array_append_new(jarray, jid)!=0)) { + json_decref(jarray); + jarray=NULL; + goto cleanup; + } + } + +cleanup: + talloc_free(tmp_ctx); + return jarray; +} + +static TR_APC *tr_msg_decode_apcs(TALLOC_CTX *mem_ctx, json_t *jarray, TRP_RC *rc) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + size_t ii=0; + TR_APC *apc_list=NULL; + TR_APC *new=NULL; + json_t *jstr=NULL; + + *rc=TRP_ERROR; + + for (ii=0; iitype)); +cleanup: + talloc_free(tmp_ctx); + return rc; +} + +static TRP_RC tr_msg_decode_trp_inforec_comm(json_t *jrecord, TRP_INFOREC *rec) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TRP_RC rc=TRP_ERROR; + char *s=NULL; + int num=0; + TR_APC *apcs=NULL; - rc=tr_msg_get_json_string(jrecord, "community", &s, tmp_ctx); + rc=tr_msg_get_json_string(jrecord, "type", &s, tmp_ctx); if (rc != TRP_SUCCESS) goto cleanup; - if (TRP_SUCCESS!=trp_inforec_set_comm(rec, tr_new_name(s))) + if (TRP_SUCCESS!=trp_inforec_set_comm_type(rec, tr_comm_type_from_str(s))) { + rc=TRP_ERROR; goto cleanup; + } talloc_free(s); s=NULL; - rc=tr_msg_get_json_string(jrecord, "realm", &s, tmp_ctx); + rc=tr_msg_get_json_string(jrecord, "role", &s, tmp_ctx); if (rc != TRP_SUCCESS) goto cleanup; - if (TRP_SUCCESS!=trp_inforec_set_realm(rec, tr_new_name(s))) + if (TRP_SUCCESS!=trp_inforec_set_role(rec, tr_realm_role_from_str(s))) { + rc=TRP_ERROR; goto cleanup; + } talloc_free(s); s=NULL; - rc=tr_msg_get_json_string(jrecord, "trust_router", &s, tmp_ctx); - if (rc != TRP_SUCCESS) + apcs=tr_msg_decode_apcs(rec, json_object_get(jrecord, "apcs"), &rc); + if (rc!=TRP_SUCCESS) { + rc=TRP_ERROR; goto cleanup; - if (TRP_SUCCESS!=trp_inforec_set_trust_router(rec, tr_new_name(s))) + } + trp_inforec_set_apcs(rec, apcs); + + rc=tr_msg_get_json_integer(jrecord, "interval", &num); + tr_debug("tr_msg_decode_trp_inforec_comm: interval=%u", num); + if ((rc != TRP_SUCCESS) || (TRP_SUCCESS!=trp_inforec_set_interval(rec,num))) goto cleanup; - talloc_free(s); s=NULL; - trp_inforec_set_next_hop(rec, NULL); /* make sure this is null (filled in later) */ + trp_inforec_set_provenance(rec, json_object_get(jrecord, "provenance")); - rc=tr_msg_get_json_integer(jrecord, "metric", &num); - if ((rc != TRP_SUCCESS) || (TRP_SUCCESS!=trp_inforec_set_metric(rec,num))) + /* optional */ + rc=tr_msg_get_json_string(jrecord, "owner_realm", &s, tmp_ctx); + if (rc == TRP_SUCCESS) { + if (TRP_SUCCESS!=trp_inforec_set_owner_realm(rec, tr_new_name(s))) { + rc=TRP_ERROR; + goto cleanup; + } + if (s!=NULL) { + talloc_free(s); + s=NULL; + } + } + + rc=tr_msg_get_json_string(jrecord, "owner_contact", &s, tmp_ctx); + if (rc == TRP_SUCCESS) { + if (TRP_SUCCESS!=trp_inforec_set_owner_contact(rec, tr_new_name(s))) { + rc=TRP_ERROR; + goto cleanup; + } + if (s!=NULL) { + talloc_free(s); + s=NULL; + } + } + +cleanup: + talloc_free(tmp_ctx); + return rc; +} + +/* decode a single record */ +static TRP_INFOREC *tr_msg_decode_trp_inforec(TALLOC_CTX *mem_ctx, json_t *jrecord) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TRP_INFOREC_TYPE rectype; + TRP_INFOREC *rec=NULL; + TRP_RC rc=TRP_ERROR; + char *s=NULL; + + if (TRP_SUCCESS!=tr_msg_get_json_string(jrecord, "record_type", &s, tmp_ctx)) goto cleanup; - rc=tr_msg_get_json_integer(jrecord, "interval", &num); - if ((rc != TRP_SUCCESS) || (TRP_SUCCESS!=trp_inforec_set_interval(rec,num))) + rectype=trp_inforec_type_from_string(s); + talloc_free(s); s=NULL; + + rec=trp_inforec_new(tmp_ctx, rectype); + if (rec==NULL) { + rc=TRP_NOMEM; goto cleanup; + } + + tr_debug("tr_msg_decode_trp_inforec: '%s' record found.", trp_inforec_type_to_string(rec->type)); + + switch(trp_inforec_get_type(rec)) { + case TRP_INFOREC_TYPE_ROUTE: + rc=tr_msg_decode_trp_inforec_route(jrecord, rec); + break; + case TRP_INFOREC_TYPE_COMMUNITY: + rc=tr_msg_decode_trp_inforec_comm(jrecord, rec); + break; + default: + rc=TRP_UNSUPPORTED; + goto cleanup; + } talloc_steal(mem_ctx, rec); rc=TRP_SUCCESS; @@ -701,7 +915,9 @@ static json_t *tr_msg_encode_trp_upd(TRP_UPD *update) json_t *jupdate=NULL; json_t *jrecords=NULL; json_t *jrec=NULL; + json_t *jstr=NULL; TRP_INFOREC *rec; + char *s=NULL; if (update==NULL) return NULL; @@ -710,6 +926,32 @@ static json_t *tr_msg_encode_trp_upd(TRP_UPD *update) if (jupdate==NULL) return NULL; + s=tr_name_strdup(trp_upd_get_comm(update)); + if (s==NULL) { + json_decref(jupdate); + return NULL; + } + jstr=json_string(s); + free(s);s=NULL; + if(jstr==NULL) { + json_decref(jupdate); + return NULL; + } + json_object_set_new(jupdate, "community", jstr); + + s=tr_name_strdup(trp_upd_get_realm(update)); + if (s==NULL) { + json_decref(jupdate); + return NULL; + } + jstr=json_string(s); + free(s);s=NULL; + if(jstr==NULL) { + json_decref(jupdate); + return NULL; + } + json_object_set_new(jupdate, "realm", jstr); + jrecords=json_array(); if (jrecords==NULL) { json_decref(jupdate); @@ -733,7 +975,7 @@ static json_t *tr_msg_encode_trp_upd(TRP_UPD *update) return jupdate; } -/*Creates a linked list of records in the msg->body talloc context. +/* Creates a linked list of records in the msg->body talloc context. * An error will be returned if any unparseable records are encountered. */ static TRP_UPD *tr_msg_decode_trp_upd(TALLOC_CTX *mem_ctx, json_t *jupdate) @@ -744,6 +986,8 @@ static TRP_UPD *tr_msg_decode_trp_upd(TALLOC_CTX *mem_ctx, json_t *jupdate) TRP_UPD *update=NULL; TRP_INFOREC *new_rec=NULL; TRP_INFOREC *list_tail=NULL; + char *s=NULL; + TR_NAME *name; TRP_RC rc=TRP_ERROR; update=trp_upd_new(tmp_ctx); @@ -752,6 +996,36 @@ static TRP_UPD *tr_msg_decode_trp_upd(TALLOC_CTX *mem_ctx, json_t *jupdate) goto cleanup; } + rc=tr_msg_get_json_string(jupdate, "community", &s, tmp_ctx); + if (rc != TRP_SUCCESS) { + tr_debug("tr_msg_decode_trp_upd: no community in TRP update message."); + rc=TRP_NOPARSE; + goto cleanup; + } + name=tr_new_name(s); + if (name==NULL) { + tr_debug("tr_msg_decode_trp_upd: could not allocate community name."); + rc=TRP_NOMEM; + goto cleanup; + } + talloc_free(s); s=NULL; + trp_upd_set_comm(update, name); + + rc=tr_msg_get_json_string(jupdate, "realm", &s, tmp_ctx); + if (rc != TRP_SUCCESS) { + tr_debug("tr_msg_decode_trp_upd: no realm in TRP update message."); + rc=TRP_NOPARSE; + goto cleanup; + } + name=tr_new_name(s); + if (name==NULL) { + tr_debug("tr_msg_decode_trp_upd: could not allocate realm name."); + rc=TRP_NOMEM; + goto cleanup; + } + talloc_free(s); s=NULL; + trp_upd_set_realm(update, name); + jrecords=json_object_get(jupdate, "records"); if ((jrecords==NULL) || (!json_is_array(jrecords))) { rc=TRP_NOPARSE; @@ -919,6 +1193,7 @@ char *tr_msg_encode(TR_MSG *msg) } encoded=json_dumps(jmsg, 0); + tr_debug("tr_msg_encode: outgoing msg=%s", encoded); json_decref(jmsg); return encoded; } @@ -975,6 +1250,9 @@ TR_MSG *tr_msg_decode(char *jbuf, size_t buflen) msg->msg_type = TR_UNKNOWN; msg->msg_rep = NULL; } + + json_decref(jmsg); + return msg; } diff --git a/common/tr_name.c b/common/tr_name.c index 4480a6a..eacef63 100644 --- a/common/tr_name.c +++ b/common/tr_name.c @@ -34,6 +34,7 @@ #include #include +#include #include @@ -123,6 +124,15 @@ char * tr_name_strdup(TR_NAME *src) return s; } +json_t *tr_name_to_json_string(TR_NAME *src) +{ + char *s=tr_name_strdup(src); + json_t *js=json_string(s); + if (s!=NULL) + free(s); + return js; +} + TR_NAME *tr_name_cat(TR_NAME *n1, TR_NAME *n2) { char *s=malloc(n1->len+n2->len+1); diff --git a/common/tr_rp.c b/common/tr_rp.c index 96c7254..94a6360 100644 --- a/common/tr_rp.c +++ b/common/tr_rp.c @@ -75,7 +75,8 @@ static TR_RP_CLIENT *tr_rp_client_tail(TR_RP_CLIENT *client) return client; } -TR_RP_CLIENT *tr_rp_client_add(TR_RP_CLIENT *clients, TR_RP_CLIENT *new) +/* 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; @@ -120,6 +121,47 @@ TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name) return NULL; } +TR_RP_REALM *tr_rp_realm_lookup(TR_RP_REALM *rp_realms, TR_NAME *rp_name) +{ + TR_RP_REALM *rp = NULL; + + if (!rp_name) { + tr_debug("tr_rp_realm_lookup: Bad parameters."); + return NULL; + } + + for (rp=rp_realms; NULL!=rp; rp=rp->next) { + if (0==tr_name_cmp(tr_rp_realm_get_id(rp), rp_name)) + return rp; + } + return NULL; +} + +static int tr_rp_realm_destructor(void *obj) +{ + TR_RP_REALM *rp=talloc_get_type_abort(obj, TR_RP_REALM); + if (rp->realm_id!=NULL) + tr_free_name(rp->realm_id); + return 0; +} + +TR_RP_REALM *tr_rp_realm_new(TALLOC_CTX *mem_ctx) +{ + TR_RP_REALM *rp=talloc(mem_ctx, TR_RP_REALM); + if (rp!=NULL) { + rp->next=NULL; + rp->realm_id=NULL; + rp->refcount=0; + talloc_set_destructor((void *)rp, tr_rp_realm_destructor); + } + return rp; +} + +void tr_rp_realm_free(TR_RP_REALM *rp) +{ + talloc_free(rp); +} + /* talloc note: lists of idp realms should be assembled using * tr_idp_realm_add(). This will put all of the elements in the * list, other than the head, as children of the head context. @@ -127,13 +169,17 @@ TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name) static TR_RP_REALM *tr_rp_realm_tail(TR_RP_REALM *realm) { - while (realm!=NULL) + if (realm==NULL) + return NULL; + + while (realm->next!=NULL) realm=realm->next; return realm; } -/* for correct behavior, call like: rp_realms=tr_rp_realm_add(rp_realms, new_realm); */ -TR_RP_REALM *tr_rp_realm_add(TR_RP_REALM *head, TR_RP_REALM *new) +/* for correct behavior, call like: rp_realms=tr_rp_realm_add_func(rp_realms, new_realm); + * or better yet, use the macro */ +TR_RP_REALM *tr_rp_realm_add_func(TR_RP_REALM *head, TR_RP_REALM *new) { if (head==NULL) head=new; @@ -146,3 +192,106 @@ TR_RP_REALM *tr_rp_realm_add(TR_RP_REALM *head, TR_RP_REALM *new) } return head; } + +/* use the macro */ +TR_RP_REALM *tr_rp_realm_remove_func(TR_RP_REALM *head, TR_RP_REALM *remove) +{ + TALLOC_CTX *list_ctx=talloc_parent(head); + TR_RP_REALM *this=NULL; + + if (head==NULL) + return NULL; + + if (head==remove) { + /* if we're removing the head, put the next element (if present) into the context + * the list head was in. */ + head=head->next; + if (head!=NULL) { + talloc_steal(list_ctx, head); + /* now put all the other elements in the context of the list head */ + for (this=head->next; this!=NULL; this=this->next) + talloc_steal(head, this); + } + } else { + /* not removing the head; no need to play with contexts */ + for (this=head; this->next!=NULL; this=this->next) { + if (this->next==remove) { + this->next=remove->next; + break; + } + } + } + return head; +} + +void tr_rp_realm_incref(TR_RP_REALM *realm) +{ + realm->refcount++; +} + +void tr_rp_realm_decref(TR_RP_REALM *realm) +{ + if (realm->refcount>0) + realm->refcount--; +} + +/* remove any with zero refcount + * Call via macro. */ +TR_RP_REALM *tr_rp_realm_sweep_func(TR_RP_REALM *head) +{ + TR_RP_REALM *rp=NULL; + TR_RP_REALM *old_next=NULL; + + if (head==NULL) + return NULL; + + while ((head!=NULL) && (head->refcount==0)) { + rp=head; /* keep a pointer so we can remove it */ + tr_rp_realm_remove(head, rp); /* use this to get talloc contexts right */ + tr_rp_realm_free(rp); + } + + if (head==NULL) + return NULL; + + /* will not remove the head here, that has already been done */ + for (rp=head; rp->next!=NULL; rp=rp->next) { + if (rp->next->refcount==0) { + old_next=rp->next; + tr_rp_realm_remove(head, rp->next); /* changes rp->next */ + tr_rp_realm_free(old_next); + } + } + + return head; +} + +TR_NAME *tr_rp_realm_get_id(TR_RP_REALM *rp) +{ + if (rp==NULL) + return NULL; + + return rp->realm_id; +} + +TR_NAME *tr_rp_realm_dup_id(TR_RP_REALM *rp) +{ + if (rp==NULL) + return NULL; + + return tr_dup_name(tr_rp_realm_get_id(rp)); +} + +void tr_rp_realm_set_id(TR_RP_REALM *rp, TR_NAME *id) +{ + if (rp->realm_id!=NULL) + tr_free_name(rp->realm_id); + rp->realm_id=id; +} + +char *tr_rp_realm_to_str(TALLOC_CTX *mem_ctx, TR_RP_REALM *rp) +{ + return talloc_asprintf(mem_ctx, + "RP realm: \"%.*s\"\n", + rp->realm_id->len, rp->realm_id->buf); +} diff --git a/common/tr_util.c b/common/tr_util.c index 9562d9c..2ce3c82 100644 --- a/common/tr_util.c +++ b/common/tr_util.c @@ -35,7 +35,9 @@ #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) @@ -49,3 +51,23 @@ void tr_bin_to_hex(const unsigned char * bin, size_t bin_len, } } +/* Returns 0 if ts1==ts2, <0 if ts1= if ts1>ts2. + * Assumes that tv_nsec <= 1e9. */ +int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2) +{ + if (ts1->tv_sec > ts2->tv_sec) + return 1; + + if (ts1->tv_sec < ts2->tv_sec) + return -1; + + /* ts1->tv_sec==ts2->tv_sec */ + + if (ts1->tv_nsec > ts2->tv_nsec) + return 1; + + if (ts1->tv_nsec < ts2->tv_nsec) + return -1; + + return 0; +} diff --git a/configure.ac b/configure.ac index 2a89f00..dabfd1e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.63) -AC_INIT([trust_router],[1.5.2], +AC_INIT([trust_router],[2.1.1], [bugs@project-moonshot.org]) AC_CONFIG_MACRO_DIR(m4) AC_CONFIG_AUX_DIR(build-aux) diff --git a/gsscon/gsscon_active.c b/gsscon/gsscon_active.c index 1f4d91f..1227c2b 100755 --- a/gsscon/gsscon_active.c +++ b/gsscon/gsscon_active.c @@ -59,211 +59,226 @@ #include /* --------------------------------------------------------------------------- -*/ + */ int gsscon_connect (const char *inHost, unsigned int inPort, const char *inServiceName, int *outFD, gss_ctx_id_t *outGSSContext) { - int err = 0; - int fd = -1; - OM_uint32 majorStatus; - OM_uint32 minorStatus = 0, minorStatusToo = 0; - struct hostent *hp = NULL; - struct sockaddr_in saddr; - gss_name_t serviceName = NULL; - gss_name_t clientName = NULL; - gss_cred_id_t clientCredentials = GSS_C_NO_CREDENTIAL; - gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT; - OM_uint32 actualFlags = 0; - char *inputTokenBuffer = NULL; - size_t inputTokenBufferLength = 0; - gss_buffer_desc inputToken; /* buffer received from the server */ - gss_buffer_desc nameBuffer; - gss_buffer_t inputTokenPtr = GSS_C_NO_BUFFER; - char *name; + int err = 0; + int fd = -1; + OM_uint32 majorStatus; + OM_uint32 minorStatus = 0, minorStatusToo = 0; + struct addrinfo *ai=NULL; + struct addrinfo *ai_head=NULL; + struct addrinfo hints={.ai_family=AF_UNSPEC, .ai_socktype=SOCK_STREAM, .ai_protocol=IPPROTO_TCP}; + struct sockaddr_in saddr; + char *port=NULL; + gss_name_t serviceName = NULL; + gss_name_t clientName = NULL; + gss_cred_id_t clientCredentials = GSS_C_NO_CREDENTIAL; + gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT; + OM_uint32 actualFlags = 0; + char *inputTokenBuffer = NULL; + size_t inputTokenBufferLength = 0; + gss_buffer_desc inputToken; /* buffer received from the server */ + gss_buffer_desc nameBuffer; + gss_buffer_t inputTokenPtr = GSS_C_NO_BUFFER; + char *name; - if (!inServiceName) { err = EINVAL; } - if (!outGSSContext) { err = EINVAL; } - - if (!err) { - hp = gethostbyname (inHost); - if (hp == NULL) { err = h_errno; } - } + if (!inServiceName) { err = EINVAL; } + if (!outGSSContext) { err = EINVAL; } - if (!err) { - saddr.sin_family = hp->h_addrtype; - memcpy ((char *) &saddr.sin_addr, hp->h_addr, sizeof (saddr.sin_addr)); - saddr.sin_port = htons(inPort); - - fd = socket (AF_INET, SOCK_STREAM, 0); - if (fd < 0) { err = errno; } - } + if (!err) { + /* get a string for getaddrinfo */ + if (asprintf(&port, "%d", inPort)>0) { + err=getaddrinfo(inHost, port, &hints, &ai_head); + free(port); + } else + err=1; + } - if (!err) { - fprintf (stderr, "gss_connect: Connecting to host '%s' on port %d\n", inHost, inPort); - err = connect (fd, (struct sockaddr *) &saddr, sizeof (saddr)); - if (err < 0) { err = errno; } + if (!err) { + /* try all options returned until one works */ + for (ai=ai_head,fd=-1; (ai!=NULL) && (fd==-1); ai=ai->ai_next) { + fd=socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd < 0) { + fd=-1; + continue; + } + + fprintf(stderr, "gss_connect: Connecting to host '%s' on port %d\n", inHost, inPort); + err=connect(fd, ai->ai_addr, ai->ai_addrlen); + if (err!=0) { + close(fd); + fd=-1; + continue; + } } + + if (fd==-1) + err=1; + } - if (!err) { - *outFD = fd; - fd = -1; /* takes ownership */ - } else { - gsscon_print_error (err, "OpenConnection failed"); - } + if (!err) { + *outFD = fd; + fd = -1; /* takes ownership */ + } else { + gsscon_print_error (err, "OpenConnection failed"); + } - if (fd >= 0) { close (fd); } + if (fd >= 0) { close (fd); } - if (!err) { - majorStatus = gss_acquire_cred (&minorStatus, clientName, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, - GSS_C_INITIATE, &clientCredentials, NULL, NULL); - if (majorStatus != GSS_S_COMPLETE) { - gsscon_print_gss_errors ("gss_acquire_cred", majorStatus, minorStatus); - err = minorStatus ? minorStatus : majorStatus; - } + if (!err) { + majorStatus = gss_acquire_cred (&minorStatus, clientName, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, + GSS_C_INITIATE, &clientCredentials, NULL, NULL); + if (majorStatus != GSS_S_COMPLETE) { + gsscon_print_gss_errors ("gss_acquire_cred", majorStatus, minorStatus); + err = minorStatus ? minorStatus : majorStatus; } + } - /* - * Here is where the client picks the service principal it will - * try to use to connect to the server. In the case of the - * gssClientSample, the service principal is passed in on the - * command line, however, in a real world example, this would be - * unacceptable from a user interface standpoint since the user - * shouldn't need to know the server's service principal. - * - * In traditional Kerberos setups, the service principal would be - * constructed from the type of the service (eg: "imap"), the DNS - * hostname of the server (eg: "mailserver.domain.com") and the - * client's local realm (eg: "DOMAIN.COM") to form a full - * principal string (eg: "imap/mailserver.domain.com@DOMAIN.COM"). - * - * Now that many sites do not have DNS, this setup is becoming - * less common. However you decide to generate the service - * principal, you need to adhere to the following constraint: The - * service principal must be constructed by the client, typed in - * by the user or administrator, or transmitted to the client in a - * secure manner from a trusted third party -- such as through an - * encrypted connection to a directory server. You should not - * have the server send the client the service principal name as - * part of the authentication negotiation. - * - * The reason you can't let the server tell the client which - * principal to use is that many machines at a site will have - * their own service principal and keytab which identifies the - * machine -- in a Windows Active Directory environment all - * machines have a service principal and keytab. Some of these - * machines (such as a financial services server) will be more - * trustworthy than others (such as a random machine on a - * coworker's desk). If the owner of one of these untrustworthy - * machines can trick the client into using the untrustworthy - * machine's principal instead of the financial services server's - * principal, then he can trick the client into authenticating and - * connecting to the untrustworthy machine. The untrustworthy - * machine can then harvest any confidential information the - * client sends to it, such as credit card information or social - * security numbers. - * - * If your protocol already involves sending the service principal - * as part of your authentication negotiation, your client should - * cache the name it gets after the first successful - * authentication so that the problem above can only happen on the - * first connection attempt -- similar to what ssh does with host - * keys. - */ + /* + * Here is where the client picks the service principal it will + * try to use to connect to the server. In the case of the + * gssClientSample, the service principal is passed in on the + * command line, however, in a real world example, this would be + * unacceptable from a user interface standpoint since the user + * shouldn't need to know the server's service principal. + * + * In traditional Kerberos setups, the service principal would be + * constructed from the type of the service (eg: "imap"), the DNS + * hostname of the server (eg: "mailserver.domain.com") and the + * client's local realm (eg: "DOMAIN.COM") to form a full + * principal string (eg: "imap/mailserver.domain.com@DOMAIN.COM"). + * + * Now that many sites do not have DNS, this setup is becoming + * less common. However you decide to generate the service + * principal, you need to adhere to the following constraint: The + * service principal must be constructed by the client, typed in + * by the user or administrator, or transmitted to the client in a + * secure manner from a trusted third party -- such as through an + * encrypted connection to a directory server. You should not + * have the server send the client the service principal name as + * part of the authentication negotiation. + * + * The reason you can't let the server tell the client which + * principal to use is that many machines at a site will have + * their own service principal and keytab which identifies the + * machine -- in a Windows Active Directory environment all + * machines have a service principal and keytab. Some of these + * machines (such as a financial services server) will be more + * trustworthy than others (such as a random machine on a + * coworker's desk). If the owner of one of these untrustworthy + * machines can trick the client into using the untrustworthy + * machine's principal instead of the financial services server's + * principal, then he can trick the client into authenticating and + * connecting to the untrustworthy machine. The untrustworthy + * machine can then harvest any confidential information the + * client sends to it, such as credit card information or social + * security numbers. + * + * If your protocol already involves sending the service principal + * as part of your authentication negotiation, your client should + * cache the name it gets after the first successful + * authentication so that the problem above can only happen on the + * first connection attempt -- similar to what ssh does with host + * keys. + */ - if (!err) { - nameBuffer.length = asprintf(&name, "%s@%s", inServiceName, inHost); - nameBuffer.value = name; + if (!err) { + nameBuffer.length = asprintf(&name, "%s@%s", inServiceName, inHost); + nameBuffer.value = name; - majorStatus = gss_import_name (&minorStatus, &nameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); - if (majorStatus != GSS_S_COMPLETE) { - gsscon_print_gss_errors ("gss_import_name(inServiceName)", majorStatus, minorStatus); - err = minorStatus ? minorStatus : majorStatus; - } + majorStatus = gss_import_name (&minorStatus, &nameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); + if (majorStatus != GSS_S_COMPLETE) { + gsscon_print_gss_errors ("gss_import_name(inServiceName)", majorStatus, minorStatus); + err = minorStatus ? minorStatus : majorStatus; } + } - /* - * The main authentication loop: - * - * GSS is a multimechanism API. Because the number of packet - * exchanges required to authenticate can vary between mechanisms, - * we need to loop calling gss_init_sec_context, passing the - * "input tokens" received from the server and send the resulting - * "output tokens" back until we get GSS_S_COMPLETE or an error. - */ + /* + * The main authentication loop: + * + * GSS is a multimechanism API. Because the number of packet + * exchanges required to authenticate can vary between mechanisms, + * we need to loop calling gss_init_sec_context, passing the + * "input tokens" received from the server and send the resulting + * "output tokens" back until we get GSS_S_COMPLETE or an error. + */ - majorStatus = GSS_S_CONTINUE_NEEDED; + majorStatus = GSS_S_CONTINUE_NEEDED; - gss_OID_desc EAP_OID = { 9, "\x2B\x06\x01\x05\x05\x0F\x01\x01\x11" }; + gss_OID_desc EAP_OID = { 9, "\x2B\x06\x01\x05\x05\x0F\x01\x01\x11" }; - while (!err && (majorStatus != GSS_S_COMPLETE)) { - gss_buffer_desc outputToken = { 0, NULL }; /* buffer to send to the server */ - OM_uint32 requestedFlags = (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | - GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG); + while (!err && (majorStatus != GSS_S_COMPLETE)) { + gss_buffer_desc outputToken = { 0, NULL }; /* buffer to send to the server */ + OM_uint32 requestedFlags = (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | + GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG); - majorStatus = gss_init_sec_context (&minorStatus, - clientCredentials, - &gssContext, - serviceName, - &EAP_OID /* mech_type */, - requestedFlags, - GSS_C_INDEFINITE, - GSS_C_NO_CHANNEL_BINDINGS, - inputTokenPtr, - NULL /* actual_mech_type */, - &outputToken, - &actualFlags, - NULL /* time_rec */); + majorStatus = gss_init_sec_context (&minorStatus, + clientCredentials, + &gssContext, + serviceName, + &EAP_OID /* mech_type */, + requestedFlags, + GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, + inputTokenPtr, + NULL /* actual_mech_type */, + &outputToken, + &actualFlags, + NULL /* time_rec */); - /* Send the output token to the server (even on error) */ - if ((outputToken.length > 0) && (outputToken.value != NULL)) { - err = gsscon_write_token (*outFD, outputToken.value, outputToken.length); + /* Send the output token to the server (even on error) */ + if ((outputToken.length > 0) && (outputToken.value != NULL)) { + err = gsscon_write_token (*outFD, outputToken.value, outputToken.length); - /* free the output token */ - gss_release_buffer (&minorStatusToo, &outputToken); - } + /* free the output token */ + gss_release_buffer (&minorStatusToo, &outputToken); + } - if (!err) { - if (majorStatus == GSS_S_CONTINUE_NEEDED) { - /* Protocol requires another packet exchange */ + if (!err) { + if (majorStatus == GSS_S_CONTINUE_NEEDED) { + /* Protocol requires another packet exchange */ - /* Clean up old input buffer */ - if (inputTokenBuffer) { - free (inputTokenBuffer); - inputTokenBuffer = NULL; /* don't double-free */ - } + /* Clean up old input buffer */ + if (inputTokenBuffer) { + free (inputTokenBuffer); + inputTokenBuffer = NULL; /* don't double-free */ + } - /* Read another input token from the server */ - err = gsscon_read_token (*outFD, &inputTokenBuffer, &inputTokenBufferLength); + /* Read another input token from the server */ + err = gsscon_read_token (*outFD, &inputTokenBuffer, &inputTokenBufferLength); - if (!err) { - /* Set up input buffers for the next run through the loop */ - inputToken.value = inputTokenBuffer; - inputToken.length = inputTokenBufferLength; - inputTokenPtr = &inputToken; - } - } else if (majorStatus != GSS_S_COMPLETE) { - gsscon_print_gss_errors ("gss_init_sec_context", majorStatus, minorStatus); - err = minorStatus ? minorStatus : majorStatus; - } + if (!err) { + /* Set up input buffers for the next run through the loop */ + inputToken.value = inputTokenBuffer; + inputToken.length = inputTokenBufferLength; + inputTokenPtr = &inputToken; } + } else if (majorStatus != GSS_S_COMPLETE) { + gsscon_print_gss_errors ("gss_init_sec_context", majorStatus, minorStatus); + err = minorStatus ? minorStatus : majorStatus; + } } + } - if (!err) { - *outGSSContext = gssContext; - gssContext = NULL; - } else { - gsscon_print_error (err, "AuthenticateToServer failed"); - } + if (!err) { + *outGSSContext = gssContext; + gssContext = NULL; + } else { + gsscon_print_error (err, "AuthenticateToServer failed"); + } - if (inputTokenBuffer) { free (inputTokenBuffer); } - if (serviceName ) { gss_release_name (&minorStatus, &serviceName); } - if (clientName ) { gss_release_name (&minorStatus, &clientName); } - - if (clientCredentials != GSS_C_NO_CREDENTIAL) { - gss_release_cred (&minorStatus, &clientCredentials); } - if (gssContext != GSS_C_NO_CONTEXT) { - gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); } + if (inputTokenBuffer) { free (inputTokenBuffer); } + if (serviceName ) { gss_release_name (&minorStatus, &serviceName); } + if (clientName ) { gss_release_name (&minorStatus, &clientName); } + if (ai_head ) { freeaddrinfo(ai_head); } + + if (clientCredentials != GSS_C_NO_CREDENTIAL) { + gss_release_cred (&minorStatus, &clientCredentials); } + if (gssContext != GSS_C_NO_CONTEXT) { + gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); } - return err; + return err; } diff --git a/gsscon/gsscon_passive.c b/gsscon/gsscon_passive.c index 1ede6d3..7cdff69 100755 --- a/gsscon/gsscon_passive.c +++ b/gsscon/gsscon_passive.c @@ -56,136 +56,136 @@ const char *gServiceName = NULL; -int gsscon_passive_authenticate (int inSocket, - gss_buffer_desc inNameBuffer, - gss_ctx_id_t *outGSSContext, - client_cb_fn clientCb, - void *clientCbData) +int gsscon_passive_authenticate (int inSocket, + gss_buffer_desc inNameBuffer, + gss_ctx_id_t *outGSSContext, + client_cb_fn clientCb, + void *clientCbData) { - int err = 0; - OM_uint32 majorStatus; - OM_uint32 minorStatus = 0, minorStatusToo = 0; - gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT; - gss_name_t clientName = GSS_C_NO_NAME, serviceName = GSS_C_NO_NAME; - gss_cred_id_t acceptorCredentials = NULL; - gss_buffer_desc clientDisplayName = {0, NULL}; - char *inputTokenBuffer = NULL; - size_t inputTokenBufferLength = 0; - gss_buffer_desc inputToken; /* buffer received from the server */ + int err = 0; + OM_uint32 majorStatus; + OM_uint32 minorStatus = 0, minorStatusToo = 0; + gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT; + gss_name_t clientName = GSS_C_NO_NAME, serviceName = GSS_C_NO_NAME; + gss_cred_id_t acceptorCredentials = NULL; + gss_buffer_desc clientDisplayName = {0, NULL}; + char *inputTokenBuffer = NULL; + size_t inputTokenBufferLength = 0; + gss_buffer_desc inputToken; /* buffer received from the server */ - if (inSocket < 0 ) { err = EINVAL; } - if (!outGSSContext) { err = EINVAL; } + if (inSocket < 0 ) { err = EINVAL; } + if (!outGSSContext) { err = EINVAL; } - if (!err) { - majorStatus = gss_import_name (&minorStatus, &inNameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); - if (majorStatus != GSS_S_COMPLETE) { - gsscon_print_gss_errors ("gss_import_name(serviceName)", majorStatus, minorStatus); - err = minorStatus ? minorStatus : majorStatus; - } + if (!err) { + majorStatus = gss_import_name (&minorStatus, &inNameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); + if (majorStatus != GSS_S_COMPLETE) { + gsscon_print_gss_errors ("gss_import_name(serviceName)", majorStatus, minorStatus); + err = minorStatus ? minorStatus : majorStatus; } + } - if (!err) { - majorStatus = gss_acquire_cred ( &minorStatus, serviceName, - GSS_C_INDEFINITE, GSS_C_NO_OID_SET, - GSS_C_ACCEPT, &acceptorCredentials, - NULL /*mechs out*/, NULL /*time out*/); - if (majorStatus != GSS_S_COMPLETE) { - gsscon_print_gss_errors ("gss_acquire_cred", majorStatus, minorStatus); - err = minorStatus ? minorStatus : majorStatus; - } + if (!err) { + majorStatus = gss_acquire_cred ( &minorStatus, serviceName, + GSS_C_INDEFINITE, GSS_C_NO_OID_SET, + GSS_C_ACCEPT, &acceptorCredentials, + NULL /*mechs out*/, NULL /*time out*/); + if (majorStatus != GSS_S_COMPLETE) { + gsscon_print_gss_errors ("gss_acquire_cred", majorStatus, minorStatus); + err = minorStatus ? minorStatus : majorStatus; } + } - /* - * The main authentication loop: - * - * GSS is a multimechanism API. The number of packet exchanges required to - * authenticatevaries between mechanisms. As a result, we need to loop reading - * input tokens from the client, calling gss_accept_sec_context on the input - * tokens and send the resulting output tokens back to the client until we - * get GSS_S_COMPLETE or an error. - * - * When we are done, save the client principal so we can make authorization - * checks. - */ + /* + * The main authentication loop: + * + * GSS is a multimechanism API. The number of packet exchanges required to + * authenticatevaries between mechanisms. As a result, we need to loop reading + * input tokens from the client, calling gss_accept_sec_context on the input + * tokens and send the resulting output tokens back to the client until we + * get GSS_S_COMPLETE or an error. + * + * When we are done, save the client principal so we can make authorization + * checks. + */ - majorStatus = GSS_S_CONTINUE_NEEDED; - while (!err && (majorStatus != GSS_S_COMPLETE)) { - /* Clean up old input buffer */ - if (inputTokenBuffer != NULL) { - free (inputTokenBuffer); - inputTokenBuffer = NULL; /* don't double-free */ - } + majorStatus = GSS_S_CONTINUE_NEEDED; + while (!err && (majorStatus != GSS_S_COMPLETE)) { + /* Clean up old input buffer */ + if (inputTokenBuffer != NULL) { + free (inputTokenBuffer); + inputTokenBuffer = NULL; /* don't double-free */ + } - err = gsscon_read_token (inSocket, &inputTokenBuffer, &inputTokenBufferLength); + err = gsscon_read_token (inSocket, &inputTokenBuffer, &inputTokenBufferLength); - if (!err) { - /* Set up input buffers for the next run through the loop */ - inputToken.value = inputTokenBuffer; - inputToken.length = inputTokenBufferLength; - } + if (!err) { + /* Set up input buffers for the next run through the loop */ + inputToken.value = inputTokenBuffer; + inputToken.length = inputTokenBufferLength; + } - if (!err) { - /* buffer to send to the server */ - gss_buffer_desc outputToken = { 0, NULL }; + if (!err) { + /* buffer to send to the server */ + gss_buffer_desc outputToken = { 0, NULL }; - /* - * accept_sec_context does the actual work of taking the client's - * request and generating an appropriate reply. */ - majorStatus = gss_accept_sec_context (&minorStatus, - &gssContext, - acceptorCredentials, - &inputToken, - GSS_C_NO_CHANNEL_BINDINGS, - &clientName, - NULL /* actual_mech_type */, - &outputToken, - NULL /* req_flags */, - NULL /* time_rec */, - NULL /* delegated_cred_handle */); + /* + * accept_sec_context does the actual work of taking the client's + * request and generating an appropriate reply. */ + majorStatus = gss_accept_sec_context (&minorStatus, + &gssContext, + acceptorCredentials, + &inputToken, + GSS_C_NO_CHANNEL_BINDINGS, + &clientName, + NULL /* actual_mech_type */, + &outputToken, + NULL /* req_flags */, + NULL /* time_rec */, + NULL /* delegated_cred_handle */); - if ((outputToken.length > 0) && (outputToken.value != NULL)) { - /* Send the output token to the client (even on error) */ - err = gsscon_write_token (inSocket, outputToken.value, outputToken.length); + if ((outputToken.length > 0) && (outputToken.value != NULL)) { + /* Send the output token to the client (even on error) */ + err = gsscon_write_token (inSocket, outputToken.value, outputToken.length); - /* free the output token */ - gss_release_buffer (&minorStatusToo, &outputToken); - } - } + /* free the output token */ + gss_release_buffer (&minorStatusToo, &outputToken); + } + } - if ((majorStatus != GSS_S_COMPLETE) && (majorStatus != GSS_S_CONTINUE_NEEDED)) { - gsscon_print_gss_errors ("gss_accept_sec_context", majorStatus, minorStatus); - err = minorStatus ? minorStatus : majorStatus; - } + if ((majorStatus != GSS_S_COMPLETE) && (majorStatus != GSS_S_CONTINUE_NEEDED)) { + gsscon_print_gss_errors ("gss_accept_sec_context", majorStatus, minorStatus); + err = minorStatus ? minorStatus : majorStatus; } + } - if (!err) { - majorStatus = gss_display_name(&minorStatus, clientName, &clientDisplayName, NULL); - if (GSS_ERROR(majorStatus)) { - gsscon_print_gss_errors("gss_display_name", majorStatus, minorStatus); - err = EINVAL; - } - if (!err) - err = clientCb(clientName, &clientDisplayName, clientCbData); + if (!err) { + majorStatus = gss_display_name(&minorStatus, clientName, &clientDisplayName, NULL); + if (GSS_ERROR(majorStatus)) { + gsscon_print_gss_errors("gss_display_name", majorStatus, minorStatus); + err = EINVAL; } + if (!err) + err = clientCb(clientName, &clientDisplayName, clientCbData); + } - if (!err) { - *outGSSContext = gssContext; - gssContext = NULL; - } else { - gsscon_print_error (err, "Authenticate failed"); - } + if (!err) { + *outGSSContext = gssContext; + gssContext = NULL; + } else { + gsscon_print_error (err, "Authenticate failed"); + } - if (inputTokenBuffer) { free (inputTokenBuffer); } - if (gssContext != GSS_C_NO_CONTEXT) { - gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); } -if (clientName != GSS_C_NO_NAME) - gss_release_name(&minorStatus, &clientName); -if (clientDisplayName.value != NULL) - gss_release_buffer(&minorStatus, &clientDisplayName); - gss_release_name( &minorStatus, &serviceName); - gss_release_cred( &minorStatus, &acceptorCredentials); + if (inputTokenBuffer) { free (inputTokenBuffer); } + if (gssContext != GSS_C_NO_CONTEXT) { + gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); } + if (clientName != GSS_C_NO_NAME) + gss_release_name(&minorStatus, &clientName); + if (clientDisplayName.value != NULL) + gss_release_buffer(&minorStatus, &clientDisplayName); + gss_release_name( &minorStatus, &serviceName); + gss_release_cred( &minorStatus, &acceptorCredentials); - return err; + return err; } @@ -195,18 +195,18 @@ if (clientDisplayName.value != NULL) static int ClientPrincipalIsAuthorizedForService (const char *inClientPrincipal) { - int err = 0; - /* - * Here is where the server checks to see if the client principal should - * be allowed to use your service. Typically it should check both the name - * and the realm, since with cross-realm shared keys, a user at another - * realm may be trying to contact your service. - */ - err = 0; + int err = 0; + /* + * Here is where the server checks to see if the client principal should + * be allowed to use your service. Typically it should check both the name + * and the realm, since with cross-realm shared keys, a user at another + * realm may be trying to contact your service. + */ + err = 0; - return err; + return err; } /* --------------------------------------------------------------------------- */ @@ -215,56 +215,56 @@ int gsscon_authorize (gss_ctx_id_t inContext, int *outAuthorized, int *outAuthorizationError) { - int err = 0; - OM_uint32 majorStatus; - OM_uint32 minorStatus = 0; - gss_name_t clientName = NULL; - gss_name_t serviceName = NULL; - char *clientPrincipal = NULL; - char *servicePrincipal = NULL; + int err = 0; + OM_uint32 majorStatus; + OM_uint32 minorStatus = 0; + gss_name_t clientName = NULL; + gss_name_t serviceName = NULL; + char *clientPrincipal = NULL; + char *servicePrincipal = NULL; - if (!inContext ) { err = EINVAL; } - if (!outAuthorized ) { err = EINVAL; } - if (!outAuthorizationError) { err = EINVAL; } + if (!inContext ) { err = EINVAL; } + if (!outAuthorized ) { err = EINVAL; } + if (!outAuthorizationError) { err = EINVAL; } - if (!err) { - /* Get the client and service principals used to authenticate */ - majorStatus = gss_inquire_context (&minorStatus, - inContext, - &clientName, - &serviceName, - NULL, NULL, NULL, NULL, NULL); - if (majorStatus != GSS_S_COMPLETE) { - err = minorStatus ? minorStatus : majorStatus; - } + if (!err) { + /* Get the client and service principals used to authenticate */ + majorStatus = gss_inquire_context (&minorStatus, + inContext, + &clientName, + &serviceName, + NULL, NULL, NULL, NULL, NULL); + if (majorStatus != GSS_S_COMPLETE) { + err = minorStatus ? minorStatus : majorStatus; } + } - if (!err) { - /* Pull the client principal string out of the gss name */ - gss_buffer_desc nameToken; + if (!err) { + /* Pull the client principal string out of the gss name */ + gss_buffer_desc nameToken; - majorStatus = gss_display_name (&minorStatus, - clientName, - &nameToken, - NULL); - if (majorStatus != GSS_S_COMPLETE) { - err = minorStatus ? minorStatus : majorStatus; - } + majorStatus = gss_display_name (&minorStatus, + clientName, + &nameToken, + NULL); + if (majorStatus != GSS_S_COMPLETE) { + err = minorStatus ? minorStatus : majorStatus; + } - if (!err) { - clientPrincipal = malloc (nameToken.length + 1); - if (clientPrincipal == NULL) { err = ENOMEM; } - } + if (!err) { + clientPrincipal = malloc (nameToken.length + 1); + if (clientPrincipal == NULL) { err = ENOMEM; } + } - if (!err) { - memcpy (clientPrincipal, nameToken.value, nameToken.length); - clientPrincipal[nameToken.length] = '\0'; - } + if (!err) { + memcpy (clientPrincipal, nameToken.value, nameToken.length); + clientPrincipal[nameToken.length] = '\0'; + } - if (nameToken.value) { gss_release_buffer (&minorStatus, &nameToken); } - } + if (nameToken.value) { gss_release_buffer (&minorStatus, &nameToken); } + } - if (!err) { + if (!err) { // /* Pull the service principal string out of the gss name */ // gss_buffer_desc nameToken; // @@ -290,24 +290,24 @@ int gsscon_authorize (gss_ctx_id_t inContext, // } - int authorizationErr = 0; - authorizationErr = ClientPrincipalIsAuthorizedForService (clientPrincipal); + int authorizationErr = 0; + authorizationErr = ClientPrincipalIsAuthorizedForService (clientPrincipal); // printf ("'%s' is%s authorized for service '%s'\n", // clientPrincipal, authorizationErr ? " NOT" : "", servicePrincipal); // - *outAuthorized = !authorizationErr; - *outAuthorizationError = authorizationErr; - } + *outAuthorized = !authorizationErr; + *outAuthorizationError = authorizationErr; + } - if (serviceName ) { gss_release_name (&minorStatus, &serviceName); } - if (clientName ) { gss_release_name (&minorStatus, &clientName); } - if (clientPrincipal ) { free (clientPrincipal); } - if (servicePrincipal) { free (servicePrincipal); } + if (serviceName ) { gss_release_name (&minorStatus, &serviceName); } + if (clientName ) { gss_release_name (&minorStatus, &clientName); } + if (clientPrincipal ) { free (clientPrincipal); } + if (servicePrincipal) { free (servicePrincipal); } - return err; + return err; } diff --git a/include/tid_internal.h b/include/tid_internal.h index f415da6..10c6eac 100644 --- a/include/tid_internal.h +++ b/include/tid_internal.h @@ -41,11 +41,12 @@ #include struct tid_srvr_blk { - struct in_addr aaa_server_addr; + TID_SRVR_BLK *next; + char *aaa_server_addr; TR_NAME *key_name; DH *aaa_server_dh; /* AAA server's public dh information */ GTimeVal key_expiration; /**< absolute time at which key expires*/ - json_t *path;/**< Path of trust routers that the request traversed*/ + TID_PATH *path;/**< Path of trust routers that the request traversed*/ }; struct tid_resp { @@ -57,7 +58,6 @@ struct tid_resp { TR_CONSTRAINT_SET *cons; TR_NAME *orig_coi; TID_SRVR_BLK *servers; /* array of servers */ - size_t num_servers; json_t *error_path; /**< Path that a request generating an error traveled*/ }; @@ -82,7 +82,7 @@ struct tid_req { }; struct tidc_instance { - TID_REQ *req_list; + // TID_REQ *req_list; // TBD -- Do we still need a separate private key */ // char *priv_key; // int priv_len; @@ -95,17 +95,27 @@ struct tids_instance { char *ipaddr; const char *hostname; TIDS_REQ_FUNC *req_handler; - TIDS_AUTH_FUNC *auth_handler; + tids_auth_func *auth_handler; void *cookie; uint16_t tids_port; struct tr_rp_client *rp_gss; /* Client matching GSS name */ }; - /** Decrement a reference to #json when this tid_req is cleaned up. A new reference is not created; in effect the caller is handing a reference they already hold to the TID_REQ.*/ void tid_req_cleanup_json(TID_REQ *, json_t *json); int tid_req_add_path(TID_REQ *, const char *this_system, unsigned port); + +TID_SRVR_BLK *tid_srvr_blk_new(TALLOC_CTX *mem_ctx); +void tid_srvr_blk_free(TID_SRVR_BLK *srvr); +TID_SRVR_BLK *tid_srvr_blk_dup(TALLOC_CTX *mem_ctx, TID_SRVR_BLK *srvr); +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); + +void tid_resp_set_cons(TID_RESP *resp, TR_CONSTRAINT_SET *cons); +void tid_resp_set_error_path(TID_RESP *resp, json_t *ep); + #endif diff --git a/include/tr_apc.h b/include/tr_apc.h index 80b35b3..2919f86 100644 --- a/include/tr_apc.h +++ b/include/tr_apc.h @@ -45,9 +45,14 @@ typedef struct tr_apc { TR_NAME *id; } TR_APC; +/* iterator is just a pointer to a TR_APC */ +typedef TR_APC *TR_APC_ITER; + TR_APC *tr_apc_new(TALLOC_CTX *mem_ctx); void tr_apc_free(TR_APC *apc); -TR_APC *tr_apc_add(TR_APC *apcs, TR_APC *new); +TR_APC *tr_apc_add_func(TR_APC *apcs, TR_APC *new); +#define tr_apc_add(apcs,new) ((apcs)=tr_apc_add_func((apcs),(new))) +TR_APC *tr_apc_dup_one(TALLOC_CTX *mem_ctx, TR_APC *apc); TR_APC *tr_apc_dup(TALLOC_CTX *mem_ctx, TR_APC *apc); void tr_apc_set_id(TR_APC *apc, TR_NAME *id); @@ -56,6 +61,13 @@ TR_NAME *tr_apc_dup_id(TR_APC *apc); char *tr_apc_to_str(TALLOC_CTX *mem_ctx, TR_APC *apc); +TR_APC_ITER *tr_apc_iter_new(TALLOC_CTX *mem_ctx); +TR_APC *tr_apc_iter_first(TR_APC_ITER *iter, TR_APC *apc); +TR_APC *tr_apc_iter_next(TR_APC_ITER *iter); +void tr_apc_iter_free(TR_APC_ITER *iter); + +int tr_apc_in_common(TR_APC *one, TR_APC *two); + #endif diff --git a/include/tr_comm.h b/include/tr_comm.h index 2d753b6..5f4b4ae 100644 --- a/include/tr_comm.h +++ b/include/tr_comm.h @@ -35,10 +35,16 @@ #ifndef TR_COMM_H #define TR_COMM_H +#include +#include +#include + #include #include #include +typedef struct tr_comm_table TR_COMM_TABLE; + typedef enum tr_comm_type { TR_COMM_UNKNOWN, TR_COMM_APC, @@ -50,18 +56,173 @@ typedef struct tr_comm { TR_NAME *id; TR_COMM_TYPE type; TR_APC *apcs; - TR_IDP_REALM *idp_realms; - TR_RP_REALM *rp_realms; + TR_NAME *owner_realm; /* what realm owns this community? */ + TR_NAME *owner_contact; /* contact email */ time_t expiration_interval; /*Minutes to key expiration; only valid for an APC*/ + unsigned int refcount; /* how many TR_COMM_MEMBs refer to this community? */ } TR_COMM; +/* community membership - link realms to their communities */ +typedef struct tr_comm_memb { + struct tr_comm_memb *next; + struct tr_comm_memb *origin_next; /* for multiple copies from different origins */ + TR_IDP_REALM *idp; /* only set one of idp and rp, other null */ + TR_RP_REALM *rp; /* only set one of idp and rp, other null */ + TR_COMM *comm; + TR_NAME *origin; + json_t *provenance; /* array of names of systems traversed */ + unsigned int interval; + struct timespec *expiry; + unsigned int times_expired; /* how many times has this expired? */ + int triggered; /* do we need to send this with triggered updates? */ +} TR_COMM_MEMB; + +/* table of communities/memberships */ +struct tr_comm_table { + TR_COMM *comms; /* all communities */ + TR_IDP_REALM *idp_realms; /* all idp realms */ + TR_RP_REALM *rp_realms; /* all rp realms */ + TR_COMM_MEMB *memberships; /* head of the linked list of membership records */ +}; + +typedef enum tr_realm_role { + TR_ROLE_UNKNOWN=0, + TR_ROLE_IDP, + TR_ROLE_RP +} TR_REALM_ROLE; + +typedef struct tr_realm { + TR_REALM_ROLE role; + TR_RP_REALM *rp; + TR_IDP_REALM *idp; +} TR_REALM; + +/* nb, not all iterator routines use all members */ +typedef struct tr_comm_iter { + TR_COMM *cur_comm; + TR_COMM_MEMB *cur_memb; + TR_COMM_MEMB *cur_orig_head; /* for iterating along orig_next list */ + TR_NAME *match; /* realm or comm to match */ + TR_REALM *realm; /* handle so caller does not have to manage memory, private */ +} TR_COMM_ITER; + + +TR_COMM_TABLE *tr_comm_table_new(TALLOC_CTX *mem_ctx); +void tr_comm_table_free(TR_COMM_TABLE *ctab); + +TR_COMM_TABLE *tr_comm_table_new(TALLOC_CTX *mem_ctx); +void tr_comm_table_free(TR_COMM_TABLE *ctab); +void tr_comm_table_sweep(TR_COMM_TABLE *ctab); +void tr_comm_table_add_comm(TR_COMM_TABLE *ctab, TR_COMM *new); +void tr_comm_table_remove_comm(TR_COMM_TABLE *ctab, TR_COMM *comm); +TR_RP_REALM *tr_comm_table_find_rp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id); +void tr_comm_table_add_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *new); +void tr_comm_table_remove_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *realm); +TR_IDP_REALM *tr_comm_table_find_idp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id); +void tr_comm_table_add_idp_realm(TR_COMM_TABLE *ctab, TR_IDP_REALM *new); +void tr_comm_table_remove_idp_realm(TR_COMM_TABLE *ctab, TR_IDP_REALM *realm); +void tr_comm_table_add_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *new); +void tr_comm_table_remove_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *memb); +TR_COMM_MEMB *tr_comm_table_find_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin); +TR_COMM_MEMB *tr_comm_table_find_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm); +TR_COMM_MEMB *tr_comm_table_find_rp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *rp_realm, TR_NAME *comm, TR_NAME *origin); +TR_COMM_MEMB *tr_comm_table_find_rp_memb(TR_COMM_TABLE *ctab, TR_NAME *rp_realm, TR_NAME *comm); +TR_COMM_MEMB *tr_comm_table_find_idp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *idp_realm, TR_NAME *comm, TR_NAME *origin); +TR_COMM_MEMB *tr_comm_table_find_idp_memb(TR_COMM_TABLE *ctab, TR_NAME *idp_realm, TR_NAME *comm); +TR_COMM *tr_comm_table_find_comm(TR_COMM_TABLE *ctab, TR_NAME *comm_id); +size_t tr_comm_table_size(TR_COMM_TABLE *ctab); +void tr_comm_table_print(FILE *f, TR_COMM_TABLE *ctab); + +TR_COMM_MEMB *tr_comm_memb_new(TALLOC_CTX *mem_ctx); +void tr_comm_memb_free(TR_COMM_MEMB *memb); +int tr_comm_memb_cmp(TR_COMM_MEMB *m1, TR_COMM_MEMB *m2); +TR_REALM_ROLE tr_comm_memb_get_role(TR_COMM_MEMB *memb); +void tr_comm_memb_set_rp_realm(TR_COMM_MEMB *memb, TR_RP_REALM *realm); +TR_RP_REALM *tr_comm_memb_get_rp_realm(TR_COMM_MEMB *memb); +void tr_comm_memb_set_idp_realm(TR_COMM_MEMB *memb, TR_IDP_REALM *realm); +TR_IDP_REALM *tr_comm_memb_get_idp_realm(TR_COMM_MEMB *memb); +TR_NAME *tr_comm_memb_get_realm_id(TR_COMM_MEMB *memb); +void tr_comm_memb_set_comm(TR_COMM_MEMB *memb, TR_COMM *comm); +TR_COMM *tr_comm_memb_get_comm(TR_COMM_MEMB *memb); +TR_NAME *tr_comm_memb_get_origin(TR_COMM_MEMB *memb); +TR_NAME *tr_comm_memb_dup_origin(TR_COMM_MEMB *memb); +json_t *tr_comm_memb_get_provenance(TR_COMM_MEMB *memb); +void tr_comm_memb_set_provenance(TR_COMM_MEMB *memb, json_t *prov); +void tr_comm_memb_add_to_provenance(TR_COMM_MEMB *memb, TR_NAME *hop); +size_t tr_comm_memb_provenance_len(TR_COMM_MEMB *memb); +void tr_comm_memb_set_interval(TR_COMM_MEMB *memb, unsigned int interval); +unsigned int tr_comm_memb_get_interval(TR_COMM_MEMB *memb); +void tr_comm_memb_set_expiry(TR_COMM_MEMB *memb, struct timespec *time); +struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb); +int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime); +void tr_comm_memb_set_triggered(TR_COMM_MEMB *memb, int trig); +int tr_comm_memb_is_triggered(TR_COMM_MEMB *memb); +void tr_comm_memb_reset_times_expired(TR_COMM_MEMB *memb); +void tr_comm_memb_expire(TR_COMM_MEMB *memb); +unsigned int tr_comm_memb_get_times_expired(TR_COMM_MEMB *memb); + TR_COMM *tr_comm_new(TALLOC_CTX *mem_ctx); void tr_comm_free(TR_COMM *comm); -TR_COMM *tr_comm_add(TR_COMM *comms, TR_COMM *new); -void tr_comm_add_idp_realm(TR_COMM *comm, TR_IDP_REALM *realm); -void tr_comm_add_rp_realm(TR_COMM *comm, TR_RP_REALM *realm); -TR_COMM *tr_comm_lookup(TR_COMM *comms, TR_NAME *comm_name); -TR_RP_REALM *tr_find_comm_rp (TR_COMM *comm, TR_NAME *rp_realm); -TR_IDP_REALM *tr_find_comm_idp (TR_COMM *comm, TR_NAME *idp_realm); +void tr_comm_set_id(TR_COMM *comm, TR_NAME *id); +TR_NAME *tr_comm_get_id(TR_COMM *comm); +TR_NAME *tr_comm_dup_id(TR_COMM *comm); +void tr_comm_set_apcs(TR_COMM *comm, TR_APC *apc); +TR_APC *tr_comm_get_apcs(TR_COMM *comm); +void tr_comm_set_type(TR_COMM *comm, TR_COMM_TYPE type); +TR_COMM_TYPE tr_comm_get_type(TR_COMM *comm); +void tr_comm_set_owner_realm(TR_COMM *comm, TR_NAME *realm); +TR_NAME *tr_comm_get_owner_realm(TR_COMM *comm); +TR_NAME *tr_comm_dup_owner_realm(TR_COMM *comm); +void tr_comm_set_owner_contact(TR_COMM *comm, TR_NAME *contact); +TR_NAME *tr_comm_get_owner_contact(TR_COMM *comm); +TR_NAME *tr_comm_dup_owner_contact(TR_COMM *comm); +void tr_comm_add_idp_realm(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_IDP_REALM *realm, unsigned int interval, json_t *provenance, struct timespec *expiry); +void tr_comm_add_rp_realm(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_RP_REALM *realm, unsigned int interval, json_t *provenance, struct timespec *expiry); +TR_RP_REALM *tr_comm_find_rp(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *rp_realm); +TR_IDP_REALM *tr_comm_find_idp(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *idp_realm); +const char *tr_comm_type_to_str(TR_COMM_TYPE type); +TR_COMM_TYPE tr_comm_type_from_str(const char *s); +void tr_comm_incref(TR_COMM *comm); +void tr_comm_decref(TR_COMM *comm); +unsigned int tr_comm_get_refcount(TR_COMM *comm); + +/* for iterating over communities within a realm or realms within a community */ +TR_COMM_ITER *tr_comm_iter_new(TALLOC_CTX *mem_ctx); +void tr_comm_iter_free(TR_COMM_ITER *iter); + +/* iterate over all communities in a table */ +TR_COMM *tr_comm_table_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab); +TR_COMM *tr_comm_table_iter_next(TR_COMM_ITER *); + +/* these iterate over communities for a realm */ +TR_COMM *tr_comm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm); +TR_COMM *tr_comm_iter_next(TR_COMM_ITER *iter); +TR_COMM *tr_comm_iter_first_rp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm); +TR_COMM *tr_comm_iter_next_rp(TR_COMM_ITER *iter); +TR_COMM *tr_comm_iter_first_idp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm); +TR_COMM *tr_comm_iter_next_idp(TR_COMM_ITER *iter); + +/* iterate over realms for a community */ +TR_REALM *tr_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm); +TR_REALM *tr_realm_iter_next(TR_COMM_ITER *iter); +TR_RP_REALM *tr_rp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm); +TR_RP_REALM *tr_rp_realm_iter_next(TR_COMM_ITER *iter); +TR_IDP_REALM *tr_idp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm); +TR_IDP_REALM *tr_idp_realm_iter_next(TR_COMM_ITER *iter); + +/* iterate over members with different origins */ +TR_COMM_MEMB *tr_comm_memb_iter_first(TR_COMM_ITER *iter, TR_COMM_MEMB *memb); +TR_COMM_MEMB *tr_comm_memb_iter_next(TR_COMM_ITER *iter); + +/* iterate over all members */ +TR_COMM_MEMB *tr_comm_memb_iter_all_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab); +TR_COMM_MEMB *tr_comm_memb_iter_all_next(TR_COMM_ITER *iter); + +/* general realm stuff, should probably move */ +TR_NAME *tr_realm_get_id(TR_REALM *realm); +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); #endif diff --git a/include/tr_config.h b/include/tr_config.h index 8bd9e5b..62c5fa1 100644 --- a/include/tr_config.h +++ b/include/tr_config.h @@ -49,15 +49,17 @@ #include #define TR_DEFAULT_MAX_TREE_DEPTH 12 -#define TR_DEFAULT_TR_PORT 12308 +#define TR_DEFAULT_TRPS_PORT 12308 #define TR_DEFAULT_TIDS_PORT 12309 -#define TR_DEFAULT_TRPS_PORT 12310 #define TR_DEFAULT_LOG_THRESHOLD LOG_INFO #define TR_DEFAULT_CONSOLE_THRESHOLD LOG_NOTICE #define TR_DEFAULT_APC_EXPIRATION_INTERVAL 43200 #define TR_DEFAULT_TRP_CONNECT_INTERVAL 10 -#define TR_DEFAULT_TRP_UPDATE_INTERVAL 120 +#define TR_DEFAULT_TRP_UPDATE_INTERVAL 30 #define TR_DEFAULT_TRP_SWEEP_INTERVAL 30 +#define TR_DEFAULT_TID_REQ_TIMEOUT 5 +#define TR_DEFAULT_TID_RESP_NUMER 2 +#define TR_DEFAULT_TID_RESP_DENOM 3 typedef enum tr_cfg_rc { TR_CFG_SUCCESS = 0, /* No error */ @@ -79,14 +81,16 @@ typedef struct tr_cfg_internal { unsigned int trp_sweep_interval; unsigned int trp_update_interval; unsigned int trp_connect_interval; + 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_CFG_INTERNAL; typedef struct tr_cfg { TR_CFG_INTERNAL *internal; /* internal trust router config */ - TR_IDP_REALM *idp_realms; /* locally associated IDP Realms */ TR_RP_CLIENT *rp_clients; /* locally associated RP Clients */ TRP_PTABLE *peers; /* TRP peer table */ - TR_COMM *comms; /* locally-known communities */ + TR_COMM_TABLE *ctable; /* communities/realms */ TR_AAA_SERVER *default_servers; /* default server list */ /* TBD -- Global Filters */ } TR_CFG; @@ -108,9 +112,9 @@ void tr_cfg_free(TR_CFG *cfg); void tr_cfg_mgr_free(TR_CFG_MGR *cfg); void tr_print_config(TR_CFG *cfg); -void tr_print_comms(TR_COMM *comm_list); -void tr_print_comm_idps(TR_IDP_REALM *idp_list); -void tr_print_comm_rps(TR_RP_REALM *rp_list); +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); diff --git a/include/tr_event.h b/include/tr_event.h index 16a1067..dc93860 100644 --- a/include/tr_event.h +++ b/include/tr_event.h @@ -37,10 +37,13 @@ #include +#define TR_MAX_SOCKETS 10 + /* struct for hanging on to a socket listener event */ struct tr_socket_event { - int sock_fd; /* the fd for the socket */ - struct event *ev; /* its event */ + size_t 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 */ }; /* prototypes */ diff --git a/include/tr_idp.h b/include/tr_idp.h index 9af36d2..dbcb472 100644 --- a/include/tr_idp.h +++ b/include/tr_idp.h @@ -36,6 +36,7 @@ #define TR_IDP_H #include +#include #include #include @@ -45,6 +46,10 @@ typedef struct tr_aaa_server { TR_NAME *hostname; } TR_AAA_SERVER; +typedef struct tr_aaa_server_iter { + TR_AAA_SERVER *this; +} TR_AAA_SERVER_ITER; + /* may also want to use in tr_rp.h */ typedef enum tr_realm_origin { TR_REALM_LOCAL=0, /* realm we were configured to contact */ @@ -61,16 +66,36 @@ typedef struct tr_idp_realm { TR_AAA_SERVER *aaa_servers; TR_APC *apcs; TR_REALM_ORIGIN origin; /* how did we learn about this realm? */ + unsigned int refcount; /* how many TR_COMM_MEMBs refer to this realm */ } TR_IDP_REALM; TR_IDP_REALM *tr_idp_realm_new(TALLOC_CTX *mem_ctx); -TR_IDP_REALM *tr_idp_realm_add(TR_IDP_REALM *head, TR_IDP_REALM *new); +void tr_idp_realm_free(TR_IDP_REALM *idp); +TR_NAME *tr_idp_realm_get_id(TR_IDP_REALM *idp); +TR_NAME *tr_idp_realm_dup_id(TR_IDP_REALM *idp); +void tr_idp_realm_set_id(TR_IDP_REALM *idp, TR_NAME *id); +void tr_idp_realm_set_apcs(TR_IDP_REALM *idp, TR_APC *apc); +TR_APC *tr_idp_realm_get_apcs(TR_IDP_REALM *idp); +TR_IDP_REALM *tr_idp_realm_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_name); +TR_IDP_REALM *tr_idp_realm_add_func(TR_IDP_REALM *head, TR_IDP_REALM *new); +#define tr_idp_realm_add(head,new) ((head)=tr_idp_realm_add_func((head),(new))) +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); +void tr_idp_realm_incref(TR_IDP_REALM *realm); +void tr_idp_realm_decref(TR_IDP_REALM *realm); TR_AAA_SERVER *tr_aaa_server_new(TALLOC_CTX *mem_ctx, TR_NAME *hostname); void tr_aaa_server_free(TR_AAA_SERVER *aaa); -TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_realm_name, TR_NAME *comm); +TR_AAA_SERVER_ITER *tr_aaa_server_iter_new(TALLOC_CTX *mem_ctx); +void tr_aaa_server_iter_free(TR_AAA_SERVER_ITER *iter); +TR_AAA_SERVER *tr_aaa_server_iter_first(TR_AAA_SERVER_ITER *iter, TR_AAA_SERVER *aaa); +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); #endif diff --git a/include/tr_mq.h b/include/tr_mq.h index 81cb325..f60c7af 100644 --- a/include/tr_mq.h +++ b/include/tr_mq.h @@ -37,6 +37,7 @@ #include #include +#include /* Note on mq priorities: High priority messages are guaranteed to be * processed before any normal priority messages. Otherwise, messages @@ -63,6 +64,7 @@ typedef struct tr_mq TR_MQ; typedef void (*TR_MQ_NOTIFY_FN)(TR_MQ *, void *); struct tr_mq { pthread_mutex_t mutex; + pthread_cond_t have_msg_cond; TR_MQ_MSG *head; TR_MQ_MSG *tail; TR_MQ_MSG *last_hi_prio; @@ -87,7 +89,8 @@ int tr_mq_lock(TR_MQ *mq); int tr_mq_unlock(TR_MQ *mq); void tr_mq_set_notify_cb(TR_MQ *mq, TR_MQ_NOTIFY_FN cb, void *arg); void tr_mq_add(TR_MQ *mq, TR_MQ_MSG *msg); -TR_MQ_MSG *tr_mq_pop(TR_MQ *mq); +int tr_mq_pop_timeout(time_t seconds, struct timespec *ts); +TR_MQ_MSG *tr_mq_pop(TR_MQ *mq, struct timespec *ts_abort); void tr_mq_clear(TR_MQ *mq); #endif /*_TR_MQ_H_ */ diff --git a/include/tr_rp.h b/include/tr_rp.h index 9db8c15..7e8f8f0 100644 --- a/include/tr_rp.h +++ b/include/tr_rp.h @@ -50,16 +50,34 @@ typedef struct tr_rp_client { /* Structure to make a linked list of RP realms by name for community config */ typedef struct tr_rp_realm { struct tr_rp_realm *next; - TR_NAME *realm_name; + TR_NAME *realm_id; + unsigned int refcount; /* how many TR_COMM_MEMBs refer to this 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(TR_RP_CLIENT *clients, TR_RP_CLIENT *new); +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_filter(TR_RP_CLIENT *client, TR_FILTER *filt); - TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name); -TR_RP_REALM *tr_rp_realm_add(TR_RP_REALM *head, TR_RP_REALM *new); + +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); +TR_NAME *tr_rp_realm_dup_id(TR_RP_REALM *rp); +void tr_rp_realm_set_id(TR_RP_REALM *rp, TR_NAME *id); +TR_RP_REALM *tr_rp_realm_lookup(TR_RP_REALM *rp_realms, TR_NAME *rp_name); +TR_RP_REALM *tr_rp_realm_add_func(TR_RP_REALM *head, TR_RP_REALM *new); +#define tr_rp_realm_add(head,new) ((head)=tr_rp_realm_add_func((head),(new))) +TR_RP_REALM *tr_rp_realm_remove_func(TR_RP_REALM *head, TR_RP_REALM *remove); +#define tr_rp_realm_remove(head,remove) ((head)=tr_rp_realm_remove_func((head),(remove))) +TR_RP_REALM *tr_rp_realm_sweep_func(TR_RP_REALM *head); +#define tr_rp_realm_sweep(head) ((head)=tr_rp_realm_sweep_func((head))) +void tr_rp_realm_incref(TR_RP_REALM *realm); +void tr_rp_realm_decref(TR_RP_REALM *realm); + +char *tr_rp_realm_to_str(TALLOC_CTX *mem_ctx, TR_RP_REALM *rp); + #endif diff --git a/include/tr_tid.h b/include/tr_tid.h index 5db395e..924293d 100644 --- a/include/tr_tid.h +++ b/include/tr_tid.h @@ -39,6 +39,8 @@ #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, diff --git a/include/tr_util.h b/include/tr_util.h new file mode 100644 index 0000000..3a3f7bc --- /dev/null +++ b/include/tr_util.h @@ -0,0 +1,42 @@ +/* + * 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_UTIL_H +#define TR_UTIL_H + +#include + +TR_EXPORT int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2); + +#endif /* TR_UTIL_H */ diff --git a/include/trp_internal.h b/include/trp_internal.h index 09decc1..d6828d5 100644 --- a/include/trp_internal.h +++ b/include/trp_internal.h @@ -35,21 +35,26 @@ #ifndef TRP_INTERNAL_H #define TRP_INTERNAL_H +#include #include #include +#include #include #include #include #include #include +#include +#include #include +/* what clock do we use with clock_gettime() ? */ +#define TRP_CLOCK CLOCK_MONOTONIC + /* info records */ /* TRP update record types */ typedef struct trp_inforec_route { - TR_NAME *comm; - TR_NAME *realm; TR_NAME *trust_router; TR_NAME *next_hop; unsigned int next_hop_port; @@ -57,20 +62,31 @@ typedef struct trp_inforec_route { unsigned int interval; } TRP_INFOREC_ROUTE; -/* TODO: define struct trp_msg_info_community */ +typedef struct trp_inforec_comm { + TR_COMM_TYPE comm_type; + TR_REALM_ROLE role; + TR_APC *apcs; + TR_NAME *owner_realm; + TR_NAME *owner_contact; + time_t expiration_interval; /* Minutes to key expiration; only valid for an APC */ + json_t *provenance; + unsigned int interval; +} TRP_INFOREC_COMM; typedef union trp_inforec_data { TRP_INFOREC_ROUTE *route; - /* TRP_INFOREC_COMM *comm; */ + TRP_INFOREC_COMM *comm; } TRP_INFOREC_DATA; struct trp_inforec { TRP_INFOREC *next; TRP_INFOREC_TYPE type; - TRP_INFOREC_DATA data; /* contains pointer to one of the record types */ + TRP_INFOREC_DATA *data; /* contains pointer to one of the record types */ }; struct trp_update { + TR_NAME *realm; + TR_NAME *comm; TRP_INFOREC *records; TR_NAME *peer; /* who did this update come from? */ }; @@ -138,6 +154,7 @@ struct trps_instance { TR_MQ *mq; /* incoming message queue */ TRP_PTABLE *ptable; /* peer table */ TRP_RTABLE *rtable; /* route table */ + TR_COMM_TABLE *ctable; /* community table */ struct timeval connect_interval; /* interval between connection refreshes */ struct timeval update_interval; /* interval between scheduled updates */ struct timeval sweep_interval; /* interval between route table sweeps */ @@ -198,8 +215,10 @@ TRP_RC trpc_send_msg(TRPC_INSTANCE *trpc, const char *msg_content); TRPS_INSTANCE *trps_new (TALLOC_CTX *mem_ctx); void trps_free (TRPS_INSTANCE *trps); +void trps_set_ctable(TRPS_INSTANCE *trps, TR_COMM_TABLE *comm); void trps_set_ptable(TRPS_INSTANCE *trps, TRP_PTABLE *ptable); void trps_set_peer_status_callback(TRPS_INSTANCE *trps, void (*cb)(TRP_PEER *, void *), void *cookie); +TR_NAME *trps_dup_label(TRPS_INSTANCE *trps); TRP_RC trps_init_rtable(TRPS_INSTANCE *trps); void trps_clear_rtable(TRPS_INSTANCE *trps); void trps_set_connect_interval(TRPS_INSTANCE *trps, unsigned int interval); @@ -219,7 +238,9 @@ int trps_get_listener(TRPS_INSTANCE *trps, TRP_AUTH_FUNC auth_handler, const char *hostname, unsigned int port, - void *cookie); + void *cookie, + int *fd_out, + size_t max_fd); TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps); void trps_mq_add(TRPS_INSTANCE *trps, TR_MQ_MSG *msg); TRP_RC trps_authorize_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn); @@ -230,6 +251,7 @@ TRP_ROUTE *trps_get_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR TRP_ROUTE *trps_get_selected_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm); TR_NAME *trps_get_next_hop(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm); TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps); +TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps); TRP_RC trps_add_route(TRPS_INSTANCE *trps, TRP_ROUTE *route); TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer); TRP_PEER *trps_get_peer_by_gssname(TRPS_INSTANCE *trps, TR_NAME *gssname); @@ -237,4 +259,45 @@ TRP_PEER *trps_get_peer_by_servicename(TRPS_INSTANCE *trps, TR_NAME *servicename TRP_RC trps_update(TRPS_INSTANCE *trps, TRP_UPDATE_TYPE type); int trps_peer_connected(TRPS_INSTANCE *trps, TRP_PEER *peer); TRP_RC trps_wildcard_route_req(TRPS_INSTANCE *trps, TR_NAME *peer_gssname); + +TRP_INFOREC *trp_inforec_new(TALLOC_CTX *mem_ctx, TRP_INFOREC_TYPE type); +void trp_inforec_free(TRP_INFOREC *rec); +TRP_INFOREC *trp_inforec_get_next(TRP_INFOREC *rec); +void trp_inforec_set_next(TRP_INFOREC *rec, TRP_INFOREC *next_rec); +TRP_INFOREC_TYPE trp_inforec_get_type(TRP_INFOREC *rec); +void trp_inforec_set_type(TRP_INFOREC *rec, TRP_INFOREC_TYPE type); +TR_NAME *trp_inforec_get_comm(TRP_INFOREC *rec); +TR_NAME *trp_inforec_dup_comm(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_comm(TRP_INFOREC *rec, TR_NAME *comm); +TR_NAME *trp_inforec_get_realm(TRP_INFOREC *rec); +TR_NAME *trp_inforec_dup_realm(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_realm(TRP_INFOREC *rec, TR_NAME *realm); +TR_NAME *trp_inforec_get_trust_router(TRP_INFOREC *rec); +TR_NAME *trp_inforec_dup_trust_router(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router); +TR_NAME *trp_inforec_get_next_hop(TRP_INFOREC *rec); +TR_NAME *trp_inforec_dup_next_hop(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop); +unsigned int trp_inforec_get_metric(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_metric(TRP_INFOREC *rec, unsigned int metric); +unsigned int trp_inforec_get_interval(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_interval(TRP_INFOREC *rec, unsigned int interval); +TR_NAME *trp_inforec_get_owner_realm(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_owner_realm(TRP_INFOREC *rec, TR_NAME *name); +TR_NAME *trp_inforec_get_owner_contact(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_owner_contact(TRP_INFOREC *rec, TR_NAME *name); +json_t *trp_inforec_get_provenance(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_provenance(TRP_INFOREC *rec, json_t *prov); +TRP_INFOREC_TYPE trp_inforec_type_from_string(const char *s); +const char *trp_inforec_type_to_string(TRP_INFOREC_TYPE msgtype); +time_t trp_inforec_get_exp_interval(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_exp_interval(TRP_INFOREC *rec, time_t expint); +TR_COMM_TYPE trp_inforec_get_comm_type(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_comm_type(TRP_INFOREC *rec, TR_COMM_TYPE type); +TR_REALM_ROLE trp_inforec_get_role(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_role(TRP_INFOREC *rec, TR_REALM_ROLE role); +TR_APC *trp_inforec_get_apcs(TRP_INFOREC *rec); +TRP_RC trp_inforec_set_apcs(TRP_INFOREC *rec, TR_APC *apcs); +TR_NAME *trp_inforec_dup_origin(TRP_INFOREC *rec); + #endif /* TRP_INTERNAL_H */ diff --git a/include/trp_ptable.h b/include/trp_ptable.h index 42c9688..a0d17b3 100644 --- a/include/trp_ptable.h +++ b/include/trp_ptable.h @@ -50,6 +50,7 @@ typedef enum 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; diff --git a/include/trust_router/tid.h b/include/trust_router/tid.h index 6aa3336..0475d4d 100644 --- a/include/trust_router/tid.h +++ b/include/trust_router/tid.h @@ -71,7 +71,7 @@ typedef void (TIDC_RESP_FUNC)(TIDC_INSTANCE *, TID_REQ *, TID_RESP *, void *); typedef int (TIDS_REQ_FUNC)(TIDS_INSTANCE *, TID_REQ *, TID_RESP *, void *); -typedef int (TIDS_AUTH_FUNC)(gss_name_t client_name, TR_NAME *display_name, void *cookie); +typedef int (tids_auth_func)(gss_name_t client_name, TR_NAME *display_name, void *cookie); @@ -106,6 +106,7 @@ TR_EXPORT void tid_req_free( TID_REQ *req); TID_RESP *tid_resp_new(TALLOC_CTX *mem_ctx); void tid_resp_free(TID_RESP *resp); +TID_RESP *tid_resp_dup(TALLOC_CTX *mem_ctx, TID_RESP *resp); TR_EXPORT int tid_resp_get_result(TID_RESP *resp); void tid_resp_set_result(TID_RESP *resp, int result); TR_EXPORT TR_NAME *tid_resp_get_err_msg(TID_RESP *resp); @@ -146,16 +147,16 @@ TR_EXPORT int tidc_send_request (TIDC_INSTANCE *tidc, int conn, gss_ctx_id_t gss TR_EXPORT int tidc_fwd_request (TIDC_INSTANCE *tidc, TID_REQ *req, TIDC_RESP_FUNC *resp_handler, void *cookie); TR_EXPORT DH *tidc_get_dh(TIDC_INSTANCE *); TR_EXPORT DH *tidc_set_dh(TIDC_INSTANCE *, DH *); -TR_EXPORT void tidc_destroy (TIDC_INSTANCE *tidc); +TR_EXPORT void tidc_destroy(TIDC_INSTANCE *tidc); /* TID Server functions, in tid/tids.c */ -TR_EXPORT TIDS_INSTANCE *tids_create (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); + 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); + 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/include/trust_router/tr_constraint.h b/include/trust_router/tr_constraint.h index 58fbb67..9f76047 100644 --- a/include/trust_router/tr_constraint.h +++ b/include/trust_router/tr_constraint.h @@ -51,6 +51,7 @@ typedef struct tr_constraint { TR_CONSTRAINT *tr_constraint_new(TALLOC_CTX *mem_ctx); void tr_constraint_free(TR_CONSTRAINT *cons); +TR_CONSTRAINT *tr_constraint_dup(TALLOC_CTX *mem_ctx, TR_CONSTRAINT *cons); void TR_EXPORT tr_constraint_add_to_set (TR_CONSTRAINT_SET **cs, TR_CONSTRAINT *c); diff --git a/include/trust_router/tr_dh.h b/include/trust_router/tr_dh.h index 7760155..f4ec7fb 100644 --- a/include/trust_router/tr_dh.h +++ b/include/trust_router/tr_dh.h @@ -40,10 +40,12 @@ #include #include - +TR_EXPORT DH *tr_dh_new(void); +TR_EXPORT void tr_dh_destroy(DH *dh); /* called destroy because free is already used */ TR_EXPORT DH *tr_create_dh_params(unsigned char *key, size_t len); TR_EXPORT DH *tr_create_matching_dh(unsigned char *key, size_t len, DH *in_dh); TR_EXPORT void tr_destroy_dh_params(DH *dh); +TR_EXPORT DH *tr_dh_dup(DH *in); TR_EXPORT int tr_compute_dh_key(unsigned char **pbuf, BIGNUM *pub_key, DH *priv_dh); TR_EXPORT void tr_dh_free(unsigned char *dh_buf); @@ -51,8 +53,8 @@ int TR_EXPORT tr_dh_pub_hash(TID_REQ *request, unsigned char **out_digest, size_t *out_llen); - TR_EXPORT void tr_bin_to_hex(const unsigned char * bin, size_t binlen, - char * hex_out, size_t hex_len); + char * hex_out, size_t hex_len); + #endif diff --git a/include/trust_router/tr_name.h b/include/trust_router/tr_name.h index 5509f87..7e86003 100644 --- a/include/trust_router/tr_name.h +++ b/include/trust_router/tr_name.h @@ -35,6 +35,7 @@ #ifndef TR_NAME_H #define TR_NAME_H #include +#include #include typedef const char *tr_const_string; @@ -50,6 +51,7 @@ TR_EXPORT void tr_free_name (TR_NAME *name); TR_EXPORT int tr_name_cmp (TR_NAME *one, TR_NAME *two); TR_EXPORT void tr_name_strlcat(char *dest, const TR_NAME *src, size_t len); TR_EXPORT char *tr_name_strdup(TR_NAME *); -TR_NAME *tr_name_cat(TR_NAME *n1, TR_NAME *n2); +TR_EXPORT json_t *tr_name_to_json_string(TR_NAME *src); +TR_EXPORT TR_NAME *tr_name_cat(TR_NAME *n1, TR_NAME *n2); #endif diff --git a/include/trust_router/trp.h b/include/trust_router/trp.h index e683649..7e524af 100644 --- a/include/trust_router/trp.h +++ b/include/trust_router/trp.h @@ -65,7 +65,7 @@ typedef enum trp_rc { typedef enum trp_inforec_type { TRP_INFOREC_TYPE_UNKNOWN=0, /* conveniently, JSON parser returns 0 if a non-integer number is specified */ TRP_INFOREC_TYPE_ROUTE, - TRP_INFOREC_TYPE_COMMUNITY, /* not yet implemented (2016-06-14) */ + TRP_INFOREC_TYPE_COMMUNITY } TRP_INFOREC_TYPE; typedef struct trp_inforec TRP_INFOREC; @@ -79,34 +79,17 @@ void trp_upd_free(TRP_UPD *update); TR_EXPORT TRP_INFOREC *trp_upd_get_inforec(TRP_UPD *upd); void trp_upd_set_inforec(TRP_UPD *upd, TRP_INFOREC *rec); void trp_upd_add_inforec(TRP_UPD *upd, TRP_INFOREC *rec); +TR_EXPORT TR_NAME *trp_upd_get_realm(TRP_UPD *upd); +TR_NAME *trp_upd_dup_realm(TRP_UPD *upd); +void trp_upd_set_realm(TRP_UPD *upd, TR_NAME *realm); +TR_EXPORT TR_NAME *trp_upd_get_comm(TRP_UPD *upd); +TR_NAME *trp_upd_dup_comm(TRP_UPD *upd); +void trp_upd_set_comm(TRP_UPD *upd, TR_NAME *comm); TR_EXPORT TR_NAME *trp_upd_get_peer(TRP_UPD *upd); TR_NAME *trp_upd_dup_peer(TRP_UPD *upd); void trp_upd_set_peer(TRP_UPD *upd, TR_NAME *peer); void trp_upd_set_next_hop(TRP_UPD *upd, const char *hostname, unsigned int port); -TR_EXPORT TRP_INFOREC *trp_inforec_new(TALLOC_CTX *mem_ctx, TRP_INFOREC_TYPE type); -void trp_inforec_free(TRP_INFOREC *rec); -TR_EXPORT TRP_INFOREC *trp_inforec_get_next(TRP_INFOREC *rec); -void trp_inforec_set_next(TRP_INFOREC *rec, TRP_INFOREC *next_rec); -TR_EXPORT TRP_INFOREC_TYPE trp_inforec_get_type(TRP_INFOREC *rec); -void trp_inforec_set_type(TRP_INFOREC *rec, TRP_INFOREC_TYPE type); -TR_EXPORT TR_NAME *trp_inforec_get_comm(TRP_INFOREC *rec); -TR_EXPORT TR_NAME *trp_inforec_dup_comm(TRP_INFOREC *rec); -TRP_RC trp_inforec_set_comm(TRP_INFOREC *rec, TR_NAME *comm); -TR_EXPORT TR_NAME *trp_inforec_get_realm(TRP_INFOREC *rec); -TR_EXPORT TR_NAME *trp_inforec_dup_realm(TRP_INFOREC *rec); -TRP_RC trp_inforec_set_realm(TRP_INFOREC *rec, TR_NAME *realm); -TR_EXPORT TR_NAME *trp_inforec_get_trust_router(TRP_INFOREC *rec); -TR_EXPORT TR_NAME *trp_inforec_dup_trust_router(TRP_INFOREC *rec); -TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router); -TR_EXPORT TR_NAME *trp_inforec_get_next_hop(TRP_INFOREC *rec); -TR_EXPORT TR_NAME *trp_inforec_dup_next_hop(TRP_INFOREC *rec); -TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop); -TR_EXPORT unsigned int trp_inforec_get_metric(TRP_INFOREC *rec); -TRP_RC trp_inforec_set_metric(TRP_INFOREC *rec, unsigned int metric); -TR_EXPORT unsigned int trp_inforec_get_interval(TRP_INFOREC *rec); -TRP_RC trp_inforec_set_interval(TRP_INFOREC *rec, unsigned int interval); -TR_EXPORT TRP_INFOREC_TYPE trp_inforec_type_from_string(const char *s); -TR_EXPORT const char *trp_inforec_type_to_string(TRP_INFOREC_TYPE msgtype); +void trp_upd_add_to_provenance(TRP_UPD *upd, TR_NAME *name); /* Functions for TRP_REQ structures */ TR_EXPORT TRP_REQ *trp_req_new(TALLOC_CTX *mem_ctx); diff --git a/redhat/default-internal.cfg b/redhat/default-internal.cfg new file mode 100644 index 0000000..7bfe0f5 --- /dev/null +++ b/redhat/default-internal.cfg @@ -0,0 +1,20 @@ +{ + "tr_internal": { + "max_tree_depth": 12, + "hostname":"beta.example.com", + "trps_port":25308, + "tids_port":25309, + "cfg_poll_interval": 1, + "cfg_settling_time": 5, + "trp_sweep_interval": 30, + "trp_update_interval": 30, + "trp_connect_interval": 10, + "tid_request_timeout": 5, + "tid_response_numerator": 2, + "tid_response_denominator": 3, + "logging": { + "log_threshold": "info", + "console_threshold":"notice" + } + } +} diff --git a/redhat/default-main.cfg b/redhat/default-main.cfg deleted file mode 100644 index 1dca690..0000000 --- a/redhat/default-main.cfg +++ /dev/null @@ -1,9 +0,0 @@ -{"tr_internal": {"max_tree_depth": 4, - "hostname":"tr.moonshot.local", - "tids_port": 12309, - - "logging": { "console_threshold": "debug", - "log_threshold": "info" - } - } -} diff --git a/redhat/organizations.cfg b/redhat/organizations.cfg new file mode 100644 index 0000000..5c190b8 --- /dev/null +++ b/redhat/organizations.cfg @@ -0,0 +1,84 @@ +{ + "communities": [ + { + "apcs": [], + "community_id": "apc.x", + "idp_realms": ["idp.x", "other.idp.x"], + "rp_realms": ["rp.x", "other.rp.x"], + "type": "apc", + "expiration_interval": 10 + }, + { + "apcs": ["apc."], + "community_id": "coi.x", + "idp_realms": ["idp.x"], + "rp_realms": ["rp.x"], + "type": "coi" + } + ], + "local_organizations": [ + { + "organization_name": "Demo Organization", + "realms": [ + { + "realm": "rp.x", + "gss_names": ["alpha-cred@apc.x", + "beta-cred@apc.x", + "gamma-cred@apc.x"], + "filters": { + "tid_inbound": [ + { + "action": "accept", + "domain_constraints": [ + "*.local" + ], + "specs": [ + { + "field": "rp_realm", + "match": "rp.x" + }, + { + "field": "rp_realm", + "match": "*.rp.x" + } + ], + "realm_constraints": [ + "rp.x", "*.rp.x" + ] + } + ] + } + }, + { + "realm": "other.rp.x", + "gss_names": ["something@apc.x"] + }, + { + "realm": "idp.x", + "gss_names": ["alpha-cred@apc.x"], + "identity_provider": { + "aaa_servers": ["alpha.local"], + "apcs": ["apc.x"], + "shared_config": "no" + } + }, + { + "realm": "other.idp.x", + "gss_names": ["beta-cred@apc.x"], + "identity_provider": { + "aaa_servers": ["alpha.local"], + "apcs": ["apc.x"], + "shared_config": "no" + } + } + ] + } + ], + "peer_organizations": [ + { + "hostname": "gamma.local", + "port": 12310, + "gss_names": ["gamma-cred@apc.x"] + } + ] +} diff --git a/redhat/tr-test-main.cfg b/redhat/tr-test-internal.cfg similarity index 100% rename from redhat/tr-test-main.cfg rename to redhat/tr-test-internal.cfg diff --git a/redhat/trusts.cfg b/redhat/trusts.cfg deleted file mode 100644 index 0998e1f..0000000 --- a/redhat/trusts.cfg +++ /dev/null @@ -1,128 +0,0 @@ -{ - "communities": [ - { - "apcs": [ - "pci-community.ja.net" - ], - "community_id": "comm.offcenter.org", - "idp_realms": [ - "idr2.offcenter.org" - ], - "rp_realms": [ - "sr3.offcenter.org" - ], - "type": "coi" - }, - { - "apcs": [ - - ], - "community_id": "pci-community.ja.net", - "idp_realms": [ - "idr1.offcenter.org", - "idr2.offcenter.org", - "ja.net", - "no-longer-untitled.offcenter.org" - ], - "rp_realms": [ - "exchange.ja.net", - "sr3.offcenter.org" - ], - "type": "apc" - } - ], - "idp_realms": [ - { - "aaa_servers": [ - "127.0.0.1" - ], - "apcs": [ - "pci-community.ja.net" - ], - "realm_id": "idr1.offcenter.org", - "shared_config": "yes" - }, - { - "aaa_servers": [ - "127.0.0.1" - ], - "apcs": [ - "pci-community.ja.net" - ], - "realm_id": "idr2.offcenter.org", - "shared_config": "no" - }, - { - "aaa_servers": [ - "10.1.10.90" - ], - "apcs": [ - "pci-community.ja.net" - ], - "realm_id": "ja.net", - "shared_config": "no" - }, - { - "aaa_servers": [ - "127.0.0.1" - ], - "apcs": [ - "pci-community.ja.net" - ], - "realm_id": "no-longer-untitled.offcenter.org", - "shared_config": "yes" - } - ], - "rp_clients": [ - { - "filter": { - "filter_lines": [ - { - "action": "accept", - "domain_constraints": ["*.exchange.ja.net"], - "filter_specs": [ - { - "field": "rp_realm", - "match": "exchange.ja.net" - }, - { - "field": "rp_realm", - "match": "*.exchange.ja.net" - } - ], - "realm_constraints": ["*.exchange.ja.net", "a.com"] - } - ], - "type": "rp_permitted" - }, - "gss_names": [ - "01b80aa9-8753-4691-8f8a-f49f7793546f@portal-realm.ja.net" - ] - }, - { - "filter": { - "filter_lines": [ - { - "action": "accept", - "domain_constraints": ["*.bob.sr3.offcenter.org"], - "filter_specs": [ - { - "field": "rp_realm", - "match": "sr3.offcenter.org" - }, - { - "field": "rp_realm", - "match": "*.sr3.offcenter.org" - } - ], - "realm_constraints": ["*.sr3.offcenter.org" ] - } - ], - "type": "rp_permitted" - }, - "gss_names": [ - "895c308a-5624-4055-bb4f-ea24b77e6637@portal-realm.ja.net" - ] - } - ] -} diff --git a/tid/example/tids_main.c b/tid/example/tids_main.c index c334f41..7b58840 100644 --- a/tid/example/tids_main.c +++ b/tid/example/tids_main.c @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -147,6 +148,7 @@ static int handle_authorizations(TID_REQ *req, const unsigned char *dh_hash, if (SQLITE_DONE != sqlite3_result) tr_crit("sqlite3: failed to write to database"); sqlite3_reset(authorization_insert); + sqlite3_clear_bindings(authorization_insert); } return 0; } @@ -160,7 +162,7 @@ static int tids_req_handler (TIDS_INSTANCE *tids, unsigned char *s_keybuf = NULL; int s_keylen = 0; char key_id[12]; - unsigned char *pub_digest; + unsigned char *pub_digest=NULL; size_t pub_digest_len; @@ -175,13 +177,12 @@ static int tids_req_handler (TIDS_INSTANCE *tids, /* Allocate a new server block */ - if (NULL == (resp->servers = talloc_zero(resp, TID_SRVR_BLK))){ - tr_crit("tids_req_handler(): malloc failed."); + tid_srvr_blk_add(resp->servers, tid_srvr_blk_new(resp)); + if (NULL==resp->servers) { + tr_crit("tids_req_handler(): unable to allocate server block."); return -1; } - resp->num_servers = 1; - /* TBD -- Set up the server IP Address */ if (!(req) || !(req->tidc_dh)) { @@ -203,10 +204,7 @@ static int tids_req_handler (TIDS_INSTANCE *tids, return -1; } - if (0 == inet_aton(tids->ipaddr, &(resp->servers->aaa_server_addr))) { - tr_debug("tids_req_handler: inet_aton() failed."); - return -1; - } + resp->servers->aaa_server_addr=talloc_strdup(resp->servers, tids->ipaddr); /* Set the key name */ if (-1 == create_key_id(key_id, sizeof(key_id))) @@ -229,7 +227,8 @@ static int tids_req_handler (TIDS_INSTANCE *tids, } if (0 != handle_authorizations(req, pub_digest, pub_digest_len)) return -1; - resp->servers->path = req->path; + tid_srvr_blk_set_path(resp->servers, (TID_PATH *)(req->path)); + if (req->expiration_interval < 1) req->expiration_interval = 1; g_get_current_time(&resp->servers->key_expiration); @@ -238,14 +237,16 @@ static int tids_req_handler (TIDS_INSTANCE *tids, if (NULL != insert_stmt) { int sqlite3_result; gchar *expiration_str = g_time_val_to_iso8601(&resp->servers->key_expiration); - sqlite3_bind_text(insert_stmt, 1, key_id, -1, SQLITE_TRANSIENT); + sqlite3_bind_text(insert_stmt, 1, key_id, -1, SQLITE_TRANSIENT); sqlite3_bind_blob(insert_stmt, 2, s_keybuf, s_keylen, SQLITE_TRANSIENT); sqlite3_bind_blob(insert_stmt, 3, pub_digest, pub_digest_len, SQLITE_TRANSIENT); - sqlite3_bind_text(insert_stmt, 4, expiration_str, -1, SQLITE_TRANSIENT); + sqlite3_bind_text(insert_stmt, 4, expiration_str, -1, SQLITE_TRANSIENT); + g_free(expiration_str); /* bind_text already made its own copy */ sqlite3_result = sqlite3_step(insert_stmt); if (SQLITE_DONE != sqlite3_result) tr_crit("sqlite3: failed to write to database"); sqlite3_reset(insert_stmt); + sqlite3_clear_bindings(insert_stmt); } /* Print out the key. */ @@ -255,6 +256,12 @@ static int tids_req_handler (TIDS_INSTANCE *tids, // } // fprintf(stderr, "\n"); + if (s_keybuf!=NULL) + free(s_keybuf); + + if (pub_digest!=NULL) + talloc_free(pub_digest); + return s_keylen; } @@ -348,8 +355,6 @@ int main (int argc, TIDS_INSTANCE *tids; TR_NAME *gssname = NULL; struct cmdline_args opts={NULL}; - int tids_socket=-1; - struct pollfd *poll_fds=NULL; /* parse the command line*/ argp_parse(&argp, argc, argv, 0, 0, &opts); @@ -375,38 +380,13 @@ int main (int argc, -1, &authorization_insert, NULL); /* Create a TID server instance */ - if (NULL == (tids = tids_create(NULL))) { + if (NULL == (tids = tids_create())) { tr_crit("Unable to create TIDS instance, exiting."); return 1; } tids->ipaddr = opts.ip_address; - - /* get listener for tids port */ - tids_socket = tids_get_listener(tids, &tids_req_handler , auth_handler, opts.hostname, TID_PORT, gssname); - - poll_fds=malloc(sizeof(*poll_fds)); - if (poll_fds == NULL) { - tr_crit("Could not allocate event polling list, exiting."); - return 1; - } - - poll_fds[0].fd=tids_socket; - poll_fds[0].events=POLLIN; /* poll on ready for reading */ - poll_fds[0].revents=0; - - /* main event loop */ - while (1) { - /* wait up to 100 ms for an event, then handle any idle work */ - if(poll(poll_fds, 1, 100) > 0) { - if (poll_fds[0].revents & POLLIN) { - if (0 != tids_accept(tids, tids_socket)) { - tr_err("Error handling tids request."); - } - } - } - /* idle loop stuff here */ - } + (void) tids_start(tids, &tids_req_handler, auth_handler, opts.hostname, TID_PORT, gssname); /* Clean-up the TID server instance */ tids_destroy(tids); diff --git a/tid/tid_req.c b/tid/tid_req.c index a068c40..c3f1a27 100644 --- a/tid/tid_req.c +++ b/tid/tid_req.c @@ -189,11 +189,12 @@ void tid_req_set_cookie(TID_REQ *req, void *cookie) req->cookie = cookie; } +/* struct is allocated in talloc null context */ TID_REQ *tid_dup_req (TID_REQ *orig_req) { TID_REQ *new_req = NULL; - if (NULL == (new_req = talloc_zero(orig_req, TID_REQ))) { + if (NULL == (new_req = talloc_zero(NULL, TID_REQ))) { tr_crit("tid_dup_req: Can't allocated duplicate request."); return NULL; } @@ -253,8 +254,8 @@ void tid_srvr_get_address(const TID_SRVR_BLK *blk, assert(blk); sa = talloc_zero(blk, struct sockaddr_in); sa->sin_family = AF_INET; - sa->sin_addr = blk->aaa_server_addr; - sa->sin_port = htons(2083); + inet_aton(blk->aaa_server_addr, &(sa->sin_addr)); + sa->sin_port = htons(2083); /* radsec port */ *out_addr = (struct sockaddr *) sa; *out_len = sizeof( struct sockaddr_in); } diff --git a/tid/tid_resp.c b/tid/tid_resp.c index 338c972..c6ee2e8 100644 --- a/tid/tid_resp.c +++ b/tid/tid_resp.c @@ -37,6 +37,7 @@ #include #include +#include #include static int tid_resp_destructor(void *obj) @@ -67,7 +68,6 @@ TID_RESP *tid_resp_new(TALLOC_CTX *mem_ctx) resp->cons=NULL; resp->orig_coi=NULL; resp->servers=NULL; - resp->num_servers=0; resp->error_path=NULL; talloc_set_destructor((void *)resp, tid_resp_destructor); } @@ -80,6 +80,29 @@ void tid_resp_free(TID_RESP *resp) talloc_free(resp); } +TID_RESP *tid_resp_dup(TALLOC_CTX *mem_ctx, TID_RESP *resp) +{ + TID_RESP *newresp=NULL; + + if (resp==NULL) + return NULL; + + 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); + } + return newresp; +} + TR_EXPORT int tid_resp_get_result(TID_RESP *resp) { return(resp->result); @@ -97,6 +120,9 @@ TR_EXPORT TR_NAME *tid_resp_get_err_msg(TID_RESP *resp) void tid_resp_set_err_msg(TID_RESP *resp, TR_NAME *err_msg) { + if (resp->err_msg!=NULL) + tr_free_name(resp->err_msg); + resp->err_msg = err_msg; } @@ -143,33 +169,144 @@ void tid_resp_set_orig_coi(TID_RESP *resp, TR_NAME *orig_coi) TR_EXPORT TID_SRVR_BLK *tid_resp_get_server(TID_RESP *resp, size_t index) { + TID_SRVR_BLK *this=NULL; assert(resp); - assert(index < resp->num_servers); - return(&(resp->servers[index])); + + for (this=resp->servers; index>0; index--, this=this->next) {} + + return this; } size_t tid_resp_get_num_servers(const TID_RESP *resp) { - assert(resp); - return resp->num_servers; + size_t count=0; + TID_SRVR_BLK *this=NULL; + + assert(resp!=NULL); + for (count=0, this=resp->servers; this!=NULL; count++, this=this->next) {} + return count; +} + +static int tid_srvr_blk_destructor(void *obj) +{ + TID_SRVR_BLK *srvr=talloc_get_type_abort(obj, TID_SRVR_BLK); + + if (srvr->key_name!=NULL) + tr_free_name(srvr->key_name); + if (srvr->aaa_server_dh!=NULL) + tr_destroy_dh_params(srvr->aaa_server_dh); + if (srvr->path!=NULL) + json_decref((json_t *)(srvr->path)); + return 0; +} + +TR_EXPORT TID_SRVR_BLK *tid_srvr_blk_new(TALLOC_CTX *mem_ctx) +{ + TID_SRVR_BLK *srvr=talloc(mem_ctx, TID_SRVR_BLK); + + if (srvr!=NULL) { + srvr->next=NULL; + srvr->aaa_server_addr=NULL; + srvr->key_name=NULL; + srvr->aaa_server_dh=NULL; + srvr->key_expiration=(GTimeVal){0}; + srvr->path=NULL; + talloc_set_destructor((void *)srvr, tid_srvr_blk_destructor); + } + return srvr; +} + +TR_EXPORT void tid_srvr_blk_free(TID_SRVR_BLK *srvr) +{ + talloc_free(srvr); } +TR_EXPORT TID_SRVR_BLK *tid_srvr_blk_dup(TALLOC_CTX *mem_ctx, TID_SRVR_BLK *srvr) +{ + TID_SRVR_BLK *new=NULL; + + if (srvr==NULL) + return NULL; + + new=tid_srvr_blk_new(mem_ctx); + if (new!=NULL) { + if (srvr->aaa_server_addr!=NULL) + new->aaa_server_addr=talloc_strdup(new, srvr->aaa_server_addr); + new->key_name=tr_dup_name(srvr->key_name); + new->aaa_server_dh=tr_dh_dup(srvr->aaa_server_dh); + new->key_expiration=srvr->key_expiration; + + tid_srvr_blk_set_path(new, srvr->path); + + tid_srvr_blk_add(new->next, tid_srvr_blk_dup(mem_ctx, srvr->next)); + } + return new; +} -const TID_PATH *tid_srvr_get_path( const TID_SRVR_BLK *block) +/* use the macro */ +TR_EXPORT TID_SRVR_BLK *tid_srvr_blk_add_func(TID_SRVR_BLK *head, TID_SRVR_BLK *new) +{ + TID_SRVR_BLK *this=head; + + if (head==NULL) + return new; + + while (this->next!=NULL) + this=this->next; + + this->next=new; + while (this!=NULL) { + talloc_steal(head, this); + this=this->next; + } + return head; +} + +TR_EXPORT void tid_srvr_blk_set_path(TID_SRVR_BLK *block, TID_PATH *path) +{ + if (block->path!=NULL) + json_decref((json_t *)(block->path)); + block->path=path; + if (block->path!=NULL) + json_incref((json_t *)(block->path)); +} + +TR_EXPORT const TID_PATH *tid_srvr_get_path( const TID_SRVR_BLK *block) { if (!block) return NULL; - return (const TID_PATH *) block->path; + return block->path; } -const TID_PATH *tid_resp_get_error_path( const TID_RESP *resp) +TR_EXPORT void tid_resp_set_cons(TID_RESP *resp, TR_CONSTRAINT_SET *cons) +{ + json_t *jc=(json_t *)cons; + + if (resp->cons!=NULL) + json_decref((json_t *) (resp->cons)); + + resp->cons=(TR_CONSTRAINT_SET *)jc; + if (jc!=NULL) + json_incref(jc); +} + +TR_EXPORT void tid_resp_set_error_path(TID_RESP *resp, json_t *ep) +{ + if (resp->error_path!=NULL) + json_decref(resp->error_path); + resp->error_path=ep; + if (resp->error_path!=NULL) + json_incref(resp->error_path); +} + +TR_EXPORT const TID_PATH *tid_resp_get_error_path(const TID_RESP *resp) { if (!resp) return NULL; - return (const TID_PATH *) resp->error_path; + return (const TID_PATH *)(resp->error_path); } -const TID_PATH *tid_resp_get_a_path( const TID_RESP *const_resp) +TR_EXPORT const TID_PATH *tid_resp_get_a_path(const TID_RESP *const_resp) { size_t index; TID_SRVR_BLK *server; @@ -177,12 +314,11 @@ const TID_PATH *tid_resp_get_a_path( const TID_RESP *const_resp) if (!resp) return NULL; - if (resp->error_path) - return (const TID_PATH *) resp->error_path; + return (const TID_PATH *)(resp->error_path); tid_resp_servers_foreach( resp, server, index) { if (server->path) - return (const TID_PATH *) server->path; + return server->path; } return NULL; diff --git a/tid/tidc.c b/tid/tidc.c index 37a3712..d724322 100644 --- a/tid/tidc.c +++ b/tid/tidc.c @@ -45,17 +45,28 @@ int tmp_len = 32; -TIDC_INSTANCE *tidc_create () +static int tidc_destructor(void *obj) { - TIDC_INSTANCE *tidc = NULL; - - if (NULL == (tidc = talloc_zero(NULL, TIDC_INSTANCE))) - return NULL; + 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); + } return tidc; } -void tidc_destroy (TIDC_INSTANCE *tidc) +void tidc_destroy(TIDC_INSTANCE *tidc) { talloc_free(tidc); } @@ -109,7 +120,7 @@ int tidc_send_request (TIDC_INSTANCE *tidc, goto error; } - tid_req->tidc_dh = tidc->client_dh; + tid_req->tidc_dh = tr_dh_dup(tidc->client_dh); rc = tidc_fwd_request(tidc, tid_req, resp_handler, cookie); goto cleanup; @@ -120,10 +131,10 @@ int tidc_send_request (TIDC_INSTANCE *tidc, return rc; } -int tidc_fwd_request (TIDC_INSTANCE *tidc, - TID_REQ *tid_req, - TIDC_RESP_FUNC *resp_handler, - void *cookie) +int tidc_fwd_request(TIDC_INSTANCE *tidc, + TID_REQ *tid_req, + TIDC_RESP_FUNC *resp_handler, + void *cookie) { char *req_buf = NULL; char *resp_buf = NULL; @@ -186,10 +197,11 @@ int tidc_fwd_request (TIDC_INSTANCE *tidc, } if (resp_handler) { - /* Call the caller's response function */ + /* Call the caller's response function. It must copy any data it needs before returning. */ tr_debug("tidc_fwd_request: calling response callback function."); (*resp_handler)(tidc, tid_req, tr_msg_get_resp(resp_msg), cookie); } + goto cleanup; error: @@ -201,9 +213,8 @@ int tidc_fwd_request (TIDC_INSTANCE *tidc, free(req_buf); if (resp_buf) free(resp_buf); - - /* TBD -- free the decoded response */ - + if (resp_msg) + tr_msg_free_decoded(resp_msg); return rc; } diff --git a/tid/tids.c b/tid/tids.c index 47ecfcb..a08182d 100644 --- a/tid/tids.c +++ b/tid/tids.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -54,7 +55,7 @@ static TID_RESP *tids_create_response (TIDS_INSTANCE *tids, TID_REQ *req) TID_RESP *resp=NULL; int success=0; - if ((NULL == (resp = talloc_zero(req, TID_RESP)))) { + if (NULL == (resp = tid_resp_new(req))) { tr_crit("tids_create_response: Error allocating response structure."); return NULL; } @@ -77,67 +78,87 @@ static TID_RESP *tids_create_response (TIDS_INSTANCE *tids, TID_REQ *req) cleanup: if ((!success) && (resp!=NULL)) { - if (resp->rp_realm!=NULL) - tr_free_name(resp->rp_realm); - if (resp->realm!=NULL) - tr_free_name(resp->realm); - if (resp->comm!=NULL) - tr_free_name(resp->comm); - if (resp->orig_coi!=NULL) - tr_free_name(resp->orig_coi); talloc_free(resp); resp=NULL; } return resp; } -static void tids_destroy_response(TIDS_INSTANCE *tids, TID_RESP *resp) +static int tids_listen(TIDS_INSTANCE *tids, int port, int *fd_out, size_t max_fd) { - if (resp) { - if (resp->err_msg) - tr_free_name(resp->err_msg); - if (resp->rp_realm) - tr_free_name(resp->rp_realm); - if (resp->realm) - tr_free_name(resp->realm); - if (resp->comm) - tr_free_name(resp->comm); - if (resp->orig_coi) - tr_free_name(resp->orig_coi); - talloc_free(resp); + 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; } -} -static int tids_listen (TIDS_INSTANCE *tids, int port) -{ - int rc = 0; - int conn = -1; - int optval = 1; + tr_debug("getaddrinfo()=%d", getaddrinfo(NULL, port_str, &hints, &ai_head)); + talloc_free(port_str); + tr_debug("tids_listen: got address info"); - union { - struct sockaddr_storage storage; - struct sockaddr_in in4; - } addr; + /* 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; + } - struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4; + 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; + } + } - saddr->sin_port = htons (port); - saddr->sin_family = AF_INET; - saddr->sin_addr.s_addr = INADDR_ANY; + 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 > (conn = socket (AF_INET, SOCK_STREAM, 0))) - return conn; + if (0>listen(conn, 512)) { + tr_debug("tids_listen: unable to listen on bound socket."); + close(conn); + continue; + } - setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + /* ok, this one worked. Save it */ + fd_out[n_opened++]=conn; + } + freeaddrinfo(ai_head); - if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in)))) - return rc; + if (n_opened==0) { + tr_debug("tids_listen: no addresses available for listening."); + return -1; + } - if (0 > (rc = listen(conn, 512))) - return rc; + tr_debug("tids_listen: TRP Server listening on port %d on %d socket%s", + port, + n_opened, + (n_opened==1)?"":"s"); - tr_debug("tids_listen: TID Server listening on port %d", port); - return conn; + return n_opened; } /* returns EACCES if authorization is denied */ @@ -174,8 +195,11 @@ static int tids_auth_connection (TIDS_INSTANCE *inst, 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.", @@ -271,7 +295,6 @@ int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_m 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; @@ -280,7 +303,7 @@ int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_m rc = tids_send_response(tids, req, resp); - tids_destroy_response(tids, resp); + tid_resp_free(resp); return rc; } @@ -378,55 +401,57 @@ static void tids_handle_connection (TIDS_INSTANCE *tids, int conn) 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."); + tids_send_err_response(tids, tr_msg_get_req(mreq), "Error sending response."); /* Fall through to free the response, either way. */ } - tids_destroy_response(tids, resp); - tr_msg_free_decoded(mreq); + tr_msg_free_decoded(mreq); /* takes resp with it */ return; } } -TIDS_INSTANCE *tids_create (TALLOC_CTX *mem_ctx) +TIDS_INSTANCE *tids_create (void) { - return talloc_zero(mem_ctx, TIDS_INSTANCE); + return talloc_zero(NULL, TIDS_INSTANCE); } /* 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, + tids_auth_func *auth_handler, const char *hostname, unsigned int port, - void *cookie) + void *cookie, + int *fd_out, + size_t max_fd) { - int listen = -1; + size_t n_fd=0; + size_t ii=0; tids->tids_port = port; - if (0 > (listen = tids_listen(tids, port))) { - char errbuf[256]; - if (0 == strerror_r(errno, errbuf, 256)) { - tr_debug("tids_get_listener: Error opening port %d: %s.", port, errbuf); - } else { - tr_debug("tids_get_listener: Unknown error openining port %d.", port); - } - } - - if (listen > 0) { + n_fd=tids_listen(tids, port, fd_out, max_fd); + if (n_fd<=0) + tr_debug("tids_get_listener: Error opening port %d"); + else { /* opening port succeeded */ tr_debug("tids_get_listener: Opened port %d.", port); /* make this socket non-blocking */ - if (0 != fcntl(listen, F_SETFL, O_NONBLOCK)) { - tr_debug("tids_get_listener: Error setting O_NONBLOCK."); - close(listen); - listen=-1; + for (ii=0; ii 0) { + if (n_fd>0) { /* store the caller's request handler & cookie */ tids->req_handler = req_handler; tids->auth_handler = auth_handler; @@ -434,7 +459,7 @@ int tids_get_listener(TIDS_INSTANCE *tids, tids->cookie = cookie; } - return listen; + return n_fd; } /* Accept and process a connection on a port opened with tids_get_listener() */ @@ -469,56 +494,64 @@ int tids_accept(TIDS_INSTANCE *tids, int listen) } /* Process tids requests forever. Should not return except on error. */ +#define MAX_SOCKETS 10 int tids_start (TIDS_INSTANCE *tids, - TIDS_REQ_FUNC *req_handler, - TIDS_AUTH_FUNC *auth_handler, - const char *hostname, - unsigned int port, - void *cookie) + TIDS_REQ_FUNC *req_handler, + tids_auth_func *auth_handler, + const char *hostname, + unsigned int port, + void *cookie) { - int listen = -1; - int conn = -1; - pid_t pid; + int fd[MAX_SOCKETS]={0}; + size_t n_fd=0; + struct pollfd poll_fd[MAX_SOCKETS]={{0}}; + int ii=0; - tids->tids_port = port; - if (0 > (listen = tids_listen(tids, port))) + n_fd=tids_get_listener(tids, req_handler, auth_handler, hostname, port, cookie, fd, MAX_SOCKETS); + if (n_fd <= 0) { perror ("Error from tids_listen()"); - - /* store the caller's request handler & cookie */ - tids->req_handler = req_handler; - tids->auth_handler = auth_handler; - tids->hostname = hostname; - tids->cookie = cookie; + return 1; + } tr_info("Trust Path Query Server starting on host %s:%d.", hostname, port); + /* set up the poll structs */ + for (ii=0; ii (conn = accept(listen, NULL, NULL))) { - perror("Error from TIDS Server accept()"); + /* wait indefinitely for a connection */ + if (poll(poll_fd, n_fd, -1) < 0) { + perror("Error from poll()"); return 1; } - if (0 > (pid = fork())) { - perror("Error on fork()"); - return 1; - } + /* fork handlers for any sockets that have data */ + for (ii=0; ii 0); + if (poll_fd[ii].revents & POLLIN) { + if (tids_accept(tids, poll_fd[ii].fd)) + tr_err("tids_start: error in tids_accept()."); + } + } } return 1; /* should never get here, loops "forever" */ } +#undef MAX_SOCKETS void tids_destroy (TIDS_INSTANCE *tids) { diff --git a/tr/internal.cfg b/tr/internal.cfg new file mode 100644 index 0000000..7bfe0f5 --- /dev/null +++ b/tr/internal.cfg @@ -0,0 +1,20 @@ +{ + "tr_internal": { + "max_tree_depth": 12, + "hostname":"beta.example.com", + "trps_port":25308, + "tids_port":25309, + "cfg_poll_interval": 1, + "cfg_settling_time": 5, + "trp_sweep_interval": 30, + "trp_update_interval": 30, + "trp_connect_interval": 10, + "tid_request_timeout": 5, + "tid_response_numerator": 2, + "tid_response_denominator": 3, + "logging": { + "log_threshold": "info", + "console_threshold":"notice" + } + } +} diff --git a/tr/manual.cfg b/tr/manual.cfg deleted file mode 100644 index 5d33638..0000000 --- a/tr/manual.cfg +++ /dev/null @@ -1,2 +0,0 @@ -{"tr_internal":{"max_tree_depth": 4, - "hostname":"margaret-moonshot3.local"}} \ No newline at end of file diff --git a/tr/organizations.cfg b/tr/organizations.cfg new file mode 100644 index 0000000..5c190b8 --- /dev/null +++ b/tr/organizations.cfg @@ -0,0 +1,84 @@ +{ + "communities": [ + { + "apcs": [], + "community_id": "apc.x", + "idp_realms": ["idp.x", "other.idp.x"], + "rp_realms": ["rp.x", "other.rp.x"], + "type": "apc", + "expiration_interval": 10 + }, + { + "apcs": ["apc."], + "community_id": "coi.x", + "idp_realms": ["idp.x"], + "rp_realms": ["rp.x"], + "type": "coi" + } + ], + "local_organizations": [ + { + "organization_name": "Demo Organization", + "realms": [ + { + "realm": "rp.x", + "gss_names": ["alpha-cred@apc.x", + "beta-cred@apc.x", + "gamma-cred@apc.x"], + "filters": { + "tid_inbound": [ + { + "action": "accept", + "domain_constraints": [ + "*.local" + ], + "specs": [ + { + "field": "rp_realm", + "match": "rp.x" + }, + { + "field": "rp_realm", + "match": "*.rp.x" + } + ], + "realm_constraints": [ + "rp.x", "*.rp.x" + ] + } + ] + } + }, + { + "realm": "other.rp.x", + "gss_names": ["something@apc.x"] + }, + { + "realm": "idp.x", + "gss_names": ["alpha-cred@apc.x"], + "identity_provider": { + "aaa_servers": ["alpha.local"], + "apcs": ["apc.x"], + "shared_config": "no" + } + }, + { + "realm": "other.idp.x", + "gss_names": ["beta-cred@apc.x"], + "identity_provider": { + "aaa_servers": ["alpha.local"], + "apcs": ["apc.x"], + "shared_config": "no" + } + } + ] + } + ], + "peer_organizations": [ + { + "hostname": "gamma.local", + "port": 12310, + "gss_names": ["gamma-cred@apc.x"] + } + ] +} diff --git a/tr/portal.cfg b/tr/portal.cfg deleted file mode 100644 index 6ead29d..0000000 --- a/tr/portal.cfg +++ /dev/null @@ -1,84 +0,0 @@ -{ - "communities": [ - { - "apcs": [ - ], - "community_id": "apc.painless-security.com", - "idp_realms": [ - ], - "rp_realms": [ - "margaret-2.painless-security.com" - ], - "type": "apc" - } - ], - "idp_realms": [ - { - "aaa_servers": [ - "127.0.0.1" - ], - "apcs": [ - "pci-community.ja.net" - ], - "realm_id": "idr1.offcenter.org", - "shared_config": "yes" - }, - { - "aaa_servers": [ - "127.0.0.1" - ], - "apcs": [ - "pci-community.ja.net" - ], - "realm_id": "idr2.offcenter.org", - "shared_config": "no" - }, - { - "aaa_servers": [ - "10.1.10.90" - ], - "apcs": [ - "pci-community.ja.net" - ], - "realm_id": "ja.net", - "shared_config": "no" - }, - { - "aaa_servers": [ - "127.0.0.1" - ], - "apcs": [ - "pci-community.ja.net" - ], - "realm_id": "no-longer-untitled.offcenter.org", - "shared_config": "yes" - } - ], - "rp_clients": [ - { - "filter": { - "filter_lines": [ - { - "action": "accept", - "domain_constraints": ["*.painless-security.com"], - "filter_specs": [ - { - "field": "rp_realm", - "match": "margaret-2.painless-security.com" - }, - { - "field": "rp_realm", - "match": "*.margaret-2.painless-security.com" - } - ], - "realm_constraints": ["*.painless-security.com", "a.com"] - } - ], - "type": "rp_permitted" - }, - "gss_names": [ - "4b95cd61-616f-48f2-9713-7d2cbe19ce69@apc.painless-security.com" - ] - } - ] -} diff --git a/tr/tr_main.c b/tr/tr_main.c index 5eb825e..1f2e2de 100644 --- a/tr/tr_main.c +++ b/tr/tr_main.c @@ -141,7 +141,7 @@ int main(int argc, char *argv[]) TR_INSTANCE *tr = NULL; struct cmdline_args opts; struct event_base *ev_base; - struct tr_socket_event tids_ev; + struct tr_socket_event tids_ev = {0}; struct event *cfgwatch_ev; configure_signals(); @@ -174,10 +174,11 @@ int main(int argc, char *argv[]) } /***** initialize the trust path query server instance *****/ - if (NULL == (tr->tids = tids_create (tr))) { + if (NULL == (tr->tids = tids_create())) { 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))) { @@ -216,6 +217,7 @@ int main(int argc, char *argv[]) /*tr_status_event_init();*/ /* install status reporting events */ /* install TID server events */ + tr_debug("Initializing TID server events."); if (0 != tr_tids_event_init(ev_base, tr->tids, tr->cfg_mgr, @@ -226,11 +228,13 @@ int main(int argc, char *argv[]) } /* install TRP handler events */ + tr_debug("Initializing Dynamic Trust Router Protocol events."); if (TRP_SUCCESS != tr_trps_event_init(ev_base, tr)) { tr_crit("Error initializing Trust Path Query Server instance."); return 1; } + tr_debug("Starting event loop."); tr_event_loop_run(ev_base); /* does not return until we are done */ tr_destroy(tr); /* thanks to talloc, should destroy everything */ diff --git a/tr/tr_tid.c b/tr/tr_tid.c index 289ffba..64c0a16 100644 --- a/tr/tr_tid.c +++ b/tr/tr_tid.c @@ -34,6 +34,7 @@ #include +#include #include #include #include @@ -44,12 +45,14 @@ #include #include #include +#include +#include #include -/* Structure to hold TR instance and original request in one cookie */ +/* Structure to hold data for the tid response callback */ typedef struct tr_resp_cookie { - TIDS_INSTANCE *tids; - TID_REQ *orig_req; + int thread_id; + TID_RESP *resp; } TR_RESP_COOKIE; /* hold a tids instance and a config manager */ @@ -59,43 +62,206 @@ struct tr_tids_event_cookie { TRPS_INSTANCE *trps; }; +static void tr_tidc_resp_handler(TIDC_INSTANCE *tidc, + TID_REQ *req, + TID_RESP *resp, + void *resp_cookie) +{ + TR_RESP_COOKIE *cookie=talloc_get_type_abort(resp_cookie, TR_RESP_COOKIE); + + tr_debug("tr_tidc_resp_handler: Response received! Realm = %s, Community = %s, result = %s.", + resp->realm->buf, + resp->comm->buf, + (TID_SUCCESS==resp->result)?"success":"error"); + + if (resp->error_path!=NULL) + tr_debug("tr_tids_resp_handler: error_path is set."); + cookie->resp=tid_resp_dup(cookie, resp); +} + +/* data for AAA req forwarding threads */ +struct tr_tids_fwd_cookie { + int thread_id; + pthread_mutex_t mutex; /* lock on the mq (separate from the locking within the mq, see below) */ + TR_MQ *mq; /* messages from thread to main process; set to NULL to disable response */ + TR_NAME *aaa_hostname; + DH *dh_params; + TID_REQ *fwd_req; /* the req to duplicate */ +}; -static void tr_tidc_resp_handler (TIDC_INSTANCE *tidc, - TID_REQ *req, - TID_RESP *resp, - void *resp_cookie) +static int tr_tids_fwd_cookie_destructor(void *obj) { - tr_debug("tr_tidc_resp_handler: Response received (conn = %d)! Realm = %s, Community = %s.", ((TR_RESP_COOKIE *)resp_cookie)->orig_req->conn, resp->realm->buf, resp->comm->buf); - req->resp_rcvd = 1; - - /* TBD -- handle concatentation of multiple responses to single req */ - tids_send_response(((TR_RESP_COOKIE *)resp_cookie)->tids, - ((TR_RESP_COOKIE *)resp_cookie)->orig_req, - resp); - - return; + struct tr_tids_fwd_cookie *c=talloc_get_type_abort(obj, struct tr_tids_fwd_cookie); + if (c->aaa_hostname!=NULL) + tr_free_name(c->aaa_hostname); + if (c->dh_params!=NULL) + tr_destroy_dh_params(c->dh_params); + return 0; } -static int tr_tids_req_handler (TIDS_INSTANCE *tids, - TID_REQ *orig_req, - TID_RESP *resp, - void *cookie_in) +/* Block until we get the lock, returns 0 on success. + * The mutex is used to protect changes to the mq pointer in + * a thread's cookie. The master thread sets this to null to indicate + * that it has abandoned the thread and the message queue is no longer + * valid. This is unrelated to the locking in the message queue + * implementation itself. */ +static int tr_tids_fwd_get_mutex(struct tr_tids_fwd_cookie *cookie) { - TALLOC_CTX *tmp_ctx=talloc_new(NULL);; - TIDC_INSTANCE *tidc = NULL; - TR_RESP_COOKIE resp_cookie; - TR_AAA_SERVER *aaa_servers = NULL; + if (cookie==NULL) + return -1; + + return (pthread_mutex_lock(&(cookie->mutex))); +} + +static int tr_tids_fwd_release_mutex(struct tr_tids_fwd_cookie *cookie) +{ + if (cookie==NULL) + return -1; + + return (pthread_mutex_unlock(&(cookie->mutex))); +} + +/* values for messages */ +#define TR_TID_MQMSG_SUCCESS "tid success" +#define TR_TID_MQMSG_FAILURE "tid failure" + +/* Thread main for sending and receiving a request to a single AAA server */ +static void *tr_tids_req_fwd_thread(void *arg) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + struct tr_tids_fwd_cookie *args=talloc_get_type_abort(arg, struct tr_tids_fwd_cookie); + TIDC_INSTANCE *tidc=tidc_create(); + TR_MQ_MSG *msg=NULL; + TR_RESP_COOKIE *cookie=NULL; + int rc=0; + int success=0; + + talloc_steal(tmp_ctx, args); /* take responsibility for the cookie */ + + if (tidc!=NULL) + talloc_steal(tmp_ctx, tidc); + + /* create the cookie we will use for our response */ + cookie=talloc(tmp_ctx, TR_RESP_COOKIE); + if (cookie==NULL) { + tr_notice("tr_tids_req_fwd_thread: unable to allocate response cookie."); + success=0; + goto cleanup; + } + cookie->thread_id=args->thread_id; + tr_debug("tr_tids_req_fwd_thread: thread %d started.", cookie->thread_id); + + /* Create a TID client instance */ + if (tidc==NULL) { + tr_crit("tr_tids_req_fwd_thread: Unable to allocate TIDC instance."); + /*tids_send_err_response(tids, orig_req, "Memory allocation failure");*/ + /* TODO: encode reason for failure */ + success=0; + goto cleanup; + } + + /* Set-up TID connection */ + if (-1==(args->fwd_req->conn = tidc_open_connection(tidc, + args->aaa_hostname->buf, + TID_PORT, /* TODO: make this configurable */ + &(args->fwd_req->gssctx)))) { + tr_notice("tr_tids_req_fwd_thread: Error in tidc_open_connection."); + /* tids_send_err_response(tids, orig_req, "Can't open connection to next hop TIDS"); */ + /* TODO: encode reason for failure */ + success=0; + goto cleanup; + }; + tr_debug("tr_tids_req_fwd_thread: thread %d opened TID connection to %s.", + cookie->thread_id, + args->aaa_hostname->buf); + + /* Send a TID request. */ + if (0 > (rc = tidc_fwd_request(tidc, args->fwd_req, tr_tidc_resp_handler, (void *)cookie))) { + tr_notice("Error from tidc_fwd_request, rc = %d.", rc); + success=0; + goto cleanup; + } + /* cookie->resp should now contain our copy of the response */ + success=1; + tr_debug("tr_tids_req_fwd_thread: thread %d received response."); + +cleanup: + /* Notify parent thread of the response, if it's still listening. */ + if (0!=tr_tids_fwd_get_mutex(args)) { + tr_notice("tr_tids_req_fwd_thread: thread %d unable to acquire mutex.", cookie->thread_id); + } else if (NULL!=args->mq) { + /* mq is still valid, so we can queue our response */ + tr_debug("tr_tids_req_fwd_thread: thread %d using valid msg queue.", cookie->thread_id); + if (success) + msg=tr_mq_msg_new(tmp_ctx, TR_TID_MQMSG_SUCCESS, TR_MQ_PRIO_NORMAL); + else + msg=tr_mq_msg_new(tmp_ctx, TR_TID_MQMSG_FAILURE, TR_MQ_PRIO_NORMAL); + + if (msg==NULL) + tr_notice("tr_tids_req_fwd_thread: thread %d unable to allocate response msg.", cookie->thread_id); + + tr_mq_msg_set_payload(msg, (void *)cookie, NULL); + if (NULL!=cookie) + talloc_steal(msg, cookie); /* attach this to the msg so we can forget about it */ + tr_mq_add(args->mq, msg); + talloc_steal(NULL, args); /* take out of our tmp_ctx; master thread now responsible for freeing */ + tr_debug("tr_tids_req_fwd_thread: thread %d queued response message.", cookie->thread_id); + if (0!=tr_tids_fwd_release_mutex(args)) + tr_notice("tr_tids_req_fwd_thread: Error releasing mutex."); + } + + talloc_free(tmp_ctx); + return NULL; +} + +/* Merges r2 into r1 if they are compatible. */ +static TID_RC tr_tids_merge_resps(TID_RESP *r1, TID_RESP *r2) +{ + /* ensure these are compatible replies */ + 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; + + tid_srvr_blk_add(r1->servers, tid_srvr_blk_dup(r1, r2->servers)); + return TID_SUCCESS; +} + +static int tr_tids_req_handler(TIDS_INSTANCE *tids, + TID_REQ *orig_req, + TID_RESP *resp, + void *cookie_in) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_AAA_SERVER *aaa_servers=NULL, *this_aaa=NULL; + int n_aaa=0; + int idp_shared=0; + TR_AAA_SERVER_ITER *aaa_iter=NULL; + pthread_t aaa_thread[TR_TID_MAX_AAA_SERVERS]; + struct tr_tids_fwd_cookie *aaa_cookie[TR_TID_MAX_AAA_SERVERS]={NULL}; + TID_RESP *aaa_resp[TR_TID_MAX_AAA_SERVERS]={NULL}; TR_NAME *apc = NULL; TID_REQ *fwd_req = NULL; TR_COMM *cfg_comm = NULL; TR_COMM *cfg_apc = NULL; int oaction = TR_FILTER_ACTION_REJECT; - int rc = 0; time_t expiration_interval=0; struct tr_tids_event_cookie *cookie=talloc_get_type_abort(cookie_in, struct tr_tids_event_cookie); TR_CFG_MGR *cfg_mgr=cookie->cfg_mgr; TRPS_INSTANCE *trps=cookie->trps; TRP_ROUTE *route=NULL; + TR_MQ *mq=NULL; + TR_MQ_MSG *msg=NULL; + unsigned int n_responses=0; + unsigned int n_failed=0; + struct timespec ts_abort={0}; + unsigned int resp_frac_numer=cfg_mgr->active->internal->tid_resp_numer; + unsigned int resp_frac_denom=cfg_mgr->active->internal->tid_resp_denom; + TR_RESP_COOKIE *payload=NULL; + int ii=0; int retval=-1; if ((!tids) || (!orig_req) || (!resp)) { @@ -109,13 +275,14 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids, tids->req_count++; /* Duplicate the request, so we can modify and forward it */ - if (NULL == (fwd_req = tid_dup_req(orig_req))) { + if (NULL == (fwd_req=tid_dup_req(orig_req))) { tr_debug("tr_tids_req_handler: Unable to duplicate request."); retval=-1; goto cleanup; } + talloc_steal(tmp_ctx, fwd_req); - if (NULL == (cfg_comm = tr_comm_lookup(cfg_mgr->active->comms, orig_req->comm))) { + 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"); retval=-1; @@ -123,7 +290,9 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids, } /* Check that the rp_realm matches the filter for the GSS name that - * was received. */ + * was received. N.B. that tids->rp_gss was pointed at the correct + * rp_client when we received its GSS name. It is only set within + * the TIDS handler subprocess. */ if ((!tids->rp_gss) || (!tids->rp_gss->filter)) { @@ -145,7 +314,7 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids, goto cleanup; } /* Check that the rp_realm is a member of the community in the request */ - if (NULL == (tr_find_comm_rp(cfg_comm, orig_req->rp_realm))) { + 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"); retval=-1; @@ -154,6 +323,14 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids, /* 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)) { @@ -165,7 +342,7 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids, apc = tr_dup_name(cfg_comm->apcs->id); /* Check that the APC is configured */ - if (NULL == (cfg_apc = tr_comm_lookup(cfg_mgr->active->comms, apc))) { + 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; @@ -176,7 +353,7 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids, fwd_req->orig_coi = orig_req->comm; /* Check that rp_realm is a member of this APC */ - if (NULL == (tr_find_comm_rp(cfg_apc, orig_req->rp_realm))) { + 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; @@ -196,13 +373,15 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids, } 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->idp_realms, + 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); + orig_req->comm, + &idp_shared); } else { tr_debug("tr_tids_req_handler: route not local."); aaa_servers = tr_aaa_server_new(tmp_ctx, trp_route_get_next_hop(route)); + idp_shared=0; } /* Find the AAA server(s) for this request */ @@ -215,15 +394,16 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids, retval=-1; goto cleanup; } + idp_shared=0; } else { /* if we aren't defaulting, check idp coi and apc membership */ - if (NULL == (tr_find_comm_idp(cfg_comm, fwd_req->realm))) { + 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"); retval=-1; goto cleanup; } - if ( cfg_apc && (NULL == (tr_find_comm_idp(cfg_apc, fwd_req->realm)))) { + 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"); retval=-1; @@ -232,48 +412,174 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids, } /* send a TID request to the AAA server(s), and get the answer(s) */ - /* TBD -- Handle multiple servers */ - + 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; 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; - /* Create a TID client instance */ - if (NULL == (tidc = tidc_create())) { - tr_crit("tr_tids_req_hander: Unable to allocate TIDC instance."); - tids_send_err_response(tids, orig_req, "Memory allocation failure"); + + /* Set up message queue for replies from req forwarding threads */ + mq=tr_mq_new(tmp_ctx); + if (mq==NULL) { + tr_notice("tr_tids_req_handler: unable to allocate message queue."); retval=-1; goto cleanup; } - /* Use the DH parameters from the original request */ - /* TBD -- this needs to be fixed when we handle more than one req per conn */ - tidc->client_dh = orig_req->tidc_dh; - - /* Save information about this request for the response */ - resp_cookie.tids = tids; - resp_cookie.orig_req = orig_req; + tr_debug("tr_tids_req_handler: message queue allocated."); - /* Set-up TID connection */ - if (-1 == (fwd_req->conn = tidc_open_connection(tidc, - aaa_servers->hostname->buf, - TID_PORT, - &(fwd_req->gssctx)))) { - tr_notice("tr_tids_req_handler: Error in tidc_open_connection."); - tids_send_err_response(tids, orig_req, "Can't open connection to next hop TIDS"); + /* start threads */ + aaa_iter=tr_aaa_server_iter_new(tmp_ctx); + if (aaa_iter==NULL) { + tr_notice("tr_tids_req_handler: unable to allocate AAA server iterator."); retval=-1; goto cleanup; - }; + } + for (n_aaa=0, this_aaa=tr_aaa_server_iter_first(aaa_iter, aaa_servers); + this_aaa!=NULL; + n_aaa++, this_aaa=tr_aaa_server_iter_next(aaa_iter)) { + tr_debug("tr_tids_req_handler: Preparing to start thread %d.", n_aaa); + + aaa_cookie[n_aaa]=talloc(tmp_ctx, struct tr_tids_fwd_cookie); + if (aaa_cookie[n_aaa]==NULL) { + tr_notice("tr_tids_req_handler: unable to allocate cookie for AAA thread %d.", n_aaa); + retval=-1; + goto cleanup; + } + talloc_set_destructor((void *)(aaa_cookie[n_aaa]), tr_tids_fwd_cookie_destructor); + /* fill in the cookie. To ensure the thread has valid data even if we exit first and + * abandon it, duplicate anything pointed to (except the mq). */ + aaa_cookie[n_aaa]->thread_id=n_aaa; + if (0!=pthread_mutex_init(&(aaa_cookie[n_aaa]->mutex), NULL)) { + tr_notice("tr_tids_req_handler: unable to init mutex for AAA thread %d.", n_aaa); + retval=-1; + goto cleanup; + } + aaa_cookie[n_aaa]->mq=mq; + aaa_cookie[n_aaa]->aaa_hostname=tr_dup_name(this_aaa->hostname); + aaa_cookie[n_aaa]->dh_params=tr_dh_dup(orig_req->tidc_dh); + aaa_cookie[n_aaa]->fwd_req=tid_dup_req(fwd_req); + talloc_steal(aaa_cookie[n_aaa], aaa_cookie[n_aaa]->fwd_req); + tr_debug("tr_tids_req_handler: cookie %d initialized.", n_aaa); + + /* Take the cookie out of tmp_ctx before starting thread. If thread starts, it becomes + * responsible for freeing it until it queues a response. If we did not do this, the possibility + * exists that this function exits, freeing the cookie, before the thread takes the cookie + * out of our tmp_ctx. This would cause a segfault or talloc error in the thread. */ + talloc_steal(NULL, aaa_cookie[n_aaa]); + if (0!=pthread_create(&(aaa_thread[n_aaa]), NULL, tr_tids_req_fwd_thread, aaa_cookie[n_aaa])) { + talloc_steal(tmp_ctx, aaa_cookie[n_aaa]); /* thread start failed; steal this back */ + tr_notice("tr_tids_req_handler: unable to start AAA thread %d.", n_aaa); + retval=-1; + goto cleanup; + } + tr_debug("tr_tids_req_handler: thread %d started.", n_aaa); + } - /* Send a TID request */ - if (0 > (rc = tidc_fwd_request(tidc, fwd_req, &tr_tidc_resp_handler, (void *)&resp_cookie))) { - tr_notice("Error from tidc_fwd_request, rc = %d.", rc); - tids_send_err_response(tids, orig_req, "Can't forward request to next hop TIDS"); + /* determine expiration time */ + if (0!=tr_mq_pop_timeout(cfg_mgr->active->internal->tid_req_timeout, &ts_abort)) { + tr_notice("tr_tids_req_handler: unable to read clock for timeout."); retval=-1; goto cleanup; } + /* wait for responses */ + tr_debug("tr_tids_req_handler: waiting for response(s)."); + n_responses=0; + n_failed=0; + while (((n_responses+n_failed)thread_id]=payload->resp; /* save pointers to these */ + + if (payload->resp->result==TID_SUCCESS) { + tr_tids_merge_resps(resp, payload->resp); + n_responses++; + } else { + n_failed++; + tr_notice("tr_tids_req_handler: TID error received from AAA server %d: %.*s", + payload->thread_id, + payload->resp->err_msg->len, + payload->resp->err_msg->buf); + } + } else if (0==strcmp(tr_mq_msg_get_message(msg), TR_TID_MQMSG_FAILURE)) { + /* failure */ + n_failed++; + payload=talloc_get_type(tr_mq_msg_get_payload(msg), TR_RESP_COOKIE); + if (payload!=NULL) + talloc_steal(tmp_ctx, payload); /* put this back in our context */ + else { + /* this means the thread was unable to allocate a response cookie, and we thus cannot determine which thread it was. This is bad and should never happen in a working system.. Give up. */ + tr_notice("tr_tids_req_handler: TID request thread sent invalid reply. Aborting!"); + retval=-1; + goto cleanup; + } + tr_notice("tr_tids_req_handler: TID request for AAA server %d failed.", + payload->thread_id); + } else { + /* unexpected message */ + tr_err("tr_tids_req_handler: Unexpected message received. Aborting!"); + retval=-1; + goto cleanup; + } + + /* Set the cookie pointer to NULL so we know we've dealt with this one. The + * cookie itself is in our tmp_ctx, which we'll free before exiting. Let it hang + * around in case we are still using pointers to elements of the cookie. */ + aaa_cookie[payload->thread_id]=NULL; + + tr_mq_msg_free(msg); + + /* check whether we've received enough responses to exit */ + if ((idp_shared && (n_responses>0)) || + (resp_frac_denom*n_responses>=resp_frac_numer*n_aaa)) + break; + } + + tr_debug("tr_tids_req_handler: done waiting for responses. %d responses, %d failures.", + n_responses, n_failed); + /* Inform any remaining threads that we will no longer handle their responses. */ + for (ii=0; iimq=NULL; /* threads will not try to respond through a null mq */ + + if (0!=tr_tids_fwd_release_mutex(aaa_cookie[ii])) + tr_notice("tr_tids_req_handler: unable to release mutex for AAA thread %d.", ii); + } + } + + /* Now all threads have either replied (and aaa_cookie[ii] is null) or have been told not to + * reply (by setting their mq pointer to null). However, some may have responded by placing + * a message on the mq after we last checked but before we set their mq pointer to null. These + * will not know that we gave up on them, so we must free their cookies for them. We can just + * go through any remaining messages on the mq to identify these threads. By putting them in + * our context instead of freeing them directly, we ensure we don't accidentally invalidate + * any of our own pointers into the structure before this function exits. */ + while (NULL!=(msg=tr_mq_pop(mq, NULL))) { + payload=(TR_RESP_COOKIE *)tr_mq_msg_get_payload(msg); + if (aaa_cookie[payload->thread_id]!=NULL) + talloc_steal(tmp_ctx, aaa_cookie[payload->thread_id]); + + tr_mq_msg_free(msg); + } + + if (n_responses==0) { + /* No requests succeeded. Forward an error if we got any error responses. */ + for (ii=0; iisock_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); - if (tids_ev->sock_fd < 0) { + tids_ev->n_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); + if (tids_ev->n_sock_fd==0) { tr_crit("Error opening TID server socket."); retval=1; goto cleanup; } - /* and its event */ - tids_ev->ev=event_new(base, - tids_ev->sock_fd, - EV_READ|EV_PERSIST, - tr_tids_event_cb, - (void *)tids); - event_add(tids_ev->ev, NULL); + /* Set up events */ + for (ii=0; iin_sock_fd; ii++) { + tids_ev->ev[ii]=event_new(base, + tids_ev->sock_fd[ii], + EV_READ|EV_PERSIST, + tr_tids_event_cb, + (void *)tids); + event_add(tids_ev->ev[ii], NULL); + } cleanup: talloc_free(tmp_ctx); diff --git a/tr/tr_trp.c b/tr/tr_trp.c index ef44e89..2b93969 100644 --- a/tr/tr_trp.c +++ b/tr/tr_trp.c @@ -214,7 +214,6 @@ static void tr_trps_cleanup_conn(TRPS_INSTANCE *trps, TRP_CONNECTION *conn) tr_debug("tr_trps_cleanup_conn: freeing %p", conn); pthread_join(*trp_connection_get_thread(conn), NULL); trps_remove_connection(trps, conn); - talloc_report_full(conn, stderr); trp_connection_free(conn); tr_debug("tr_trps_cleanup_conn: deleted connection"); } @@ -244,7 +243,6 @@ static void tr_trps_process_mq(int socket, short event, void *arg) TR_MQ_MSG *msg=NULL; const char *s=NULL; - talloc_report_full(trps->mq, stderr); msg=trps_mq_pop(trps); while (msg!=NULL) { s=tr_mq_msg_get_message(msg); @@ -316,9 +314,10 @@ static void tr_trps_update(int listener, short event, void *arg) TRPS_INSTANCE *trps=cookie->trps; struct event *ev=cookie->ev; - tr_debug("tr_trps_update: sending scheduled route updates."); + tr_debug("tr_trps_update: sending scheduled route/community updates."); trps_update(trps, TRP_UPDATE_SCHEDULED); event_add(ev, &(trps->update_interval)); + tr_debug("tr_trps_update: update interval=%d", trps->update_interval.tv_sec); } static void tr_trps_sweep(int listener, short event, void *arg) @@ -329,6 +328,8 @@ static void tr_trps_sweep(int listener, short event, void *arg) tr_debug("tr_trps_sweep: sweeping routes."); trps_sweep_routes(trps); + tr_debug("tr_trps_sweep: sweeping communities."); + trps_sweep_ctable(trps); tr_trps_print_route_table(trps, stderr); /* schedule the event to run again */ event_add(ev, &(trps->sweep_interval)); @@ -395,6 +396,7 @@ TRP_RC tr_trps_event_init(struct event_base *base, TR_INSTANCE *tr) struct tr_trps_event_cookie *sweep_cookie=NULL; struct timeval zero_time={0,0}; TRP_RC retval=TRP_ERROR; + size_t ii=0; if (tr->events != NULL) { tr_notice("tr_trps_event_init: tr->events was not null. Freeing before reallocating.."); @@ -425,28 +427,31 @@ TRP_RC tr_trps_event_init(struct event_base *base, TR_INSTANCE *tr) trps_cookie->cfg_mgr=tr->cfg_mgr; /* get a trps listener */ - listen_ev->sock_fd=trps_get_listener(tr->trps, - tr_trps_msg_handler, - tr_trps_gss_handler, - tr->cfg_mgr->active->internal->hostname, - tr->cfg_mgr->active->internal->trps_port, - (void *)trps_cookie); - if (listen_ev->sock_fd < 0) { + listen_ev->n_sock_fd=trps_get_listener(tr->trps, + tr_trps_msg_handler, + tr_trps_gss_handler, + tr->cfg_mgr->active->internal->hostname, + tr->cfg_mgr->active->internal->trps_port, + (void *)trps_cookie, + listen_ev->sock_fd, + TR_MAX_SOCKETS); + if (listen_ev->n_sock_fd==0) { tr_crit("Error opening TRP server socket."); retval=TRP_ERROR; tr_trps_events_free(tr->events); tr->events=NULL; goto cleanup; } - trps_cookie->ev=listen_ev->ev; /* in case it needs to frob the event */ - - /* and its event */ - listen_ev->ev=event_new(base, - listen_ev->sock_fd, - EV_READ|EV_PERSIST, - tr_trps_event_cb, - (void *)(tr->trps)); - event_add(listen_ev->ev, NULL); + + /* Set up events for the sockets */ + for (ii=0; iin_sock_fd; ii++) { + listen_ev->ev[ii]=event_new(base, + listen_ev->sock_fd[ii], + EV_READ|EV_PERSIST, + tr_trps_event_cb, + (void *)(tr->trps)); + event_add(listen_ev->ev[ii], NULL); + } /* now set up message queue processing event, only triggered by * tr_trps_mq_cb() */ @@ -545,6 +550,8 @@ static void *tr_trpc_thread(void *arg) const char *msg_type=NULL; char *encoded_msg=NULL; TR_NAME *peer_gssname=NULL; + int n_sent=0; + int exit_loop=0; struct trpc_notify_cb_data cb_data={0, PTHREAD_COND_INITIALIZER, @@ -583,41 +590,40 @@ static void *tr_trpc_thread(void *arg) trps_mq_add(trps, msg); /* steals msg context */ msg=NULL; - while(1) { + while(!exit_loop) { cb_data.msg_ready=0; pthread_cond_wait(&(cb_data.cond), &(cb_data.mutex)); /* verify the condition */ if (cb_data.msg_ready) { - msg=trpc_mq_pop(trpc); - if (msg==NULL) { - /* no message in the queue */ - tr_err("tr_trpc_thread: notified of msg, but queue empty"); - break; - } - - msg_type=tr_mq_msg_get_message(msg); + for (msg=trpc_mq_pop(trpc),n_sent=0; msg!=NULL; msg=trpc_mq_pop(trpc),n_sent++) { + msg_type=tr_mq_msg_get_message(msg); - if (0==strcmp(msg_type, TR_MQMSG_ABORT)) { - tr_mq_msg_free(msg); - break; /* exit loop */ - } - else if (0==strcmp(msg_type, TR_MQMSG_TRPC_SEND)) { - encoded_msg=tr_mq_msg_get_payload(msg); - if (encoded_msg==NULL) - tr_notice("tr_trpc_thread: null outgoing TRP message."); - else { - rc = trpc_send_msg(trpc, encoded_msg); - if (rc!=TRP_SUCCESS) { - tr_notice("tr_trpc_thread: trpc_send_msg failed."); - tr_mq_msg_free(msg); - break; + if (0==strcmp(msg_type, TR_MQMSG_ABORT)) { + exit_loop=1; + break; + } + else if (0==strcmp(msg_type, TR_MQMSG_TRPC_SEND)) { + encoded_msg=tr_mq_msg_get_payload(msg); + if (encoded_msg==NULL) + tr_notice("tr_trpc_thread: null outgoing TRP message."); + else { + rc = trpc_send_msg(trpc, encoded_msg); + if (rc!=TRP_SUCCESS) { + tr_notice("tr_trpc_thread: trpc_send_msg failed."); + exit_loop=1; + break; + } } } - } - else - tr_notice("tr_trpc_thread: unknown message '%s' received.", msg_type); + else + tr_notice("tr_trpc_thread: unknown message '%s' received.", msg_type); - tr_mq_msg_free(msg); + tr_mq_msg_free(msg); + } + if (n_sent==0) + tr_err("tr_trpc_thread: notified of msg, but queue empty"); + else + tr_debug("tr_trpc_thread: sent %d messages.", n_sent); } } } @@ -755,7 +761,7 @@ TRP_RC tr_add_local_routes(TRPS_INSTANCE *trps, TR_CFG *cfg) if (trust_router_name==NULL) return TRP_NOMEM; - for (cur=cfg->idp_realms; cur!=NULL; cur=cur->next) { + for (cur=cfg->ctable->idp_realms; cur!=NULL; cur=cur->next) { local_routes=tr_make_local_routes(tmp_ctx, cur, trust_router_name, &n_routes); for (ii=0; iiinternal->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); + trps_set_ctable(trps, new_cfg->ctable); trps_set_ptable(trps, new_cfg->peers); trps_set_peer_status_callback(trps, tr_peer_status_change, (void *)trps); trps_clear_rtable(trps); /* should we do this every time??? */ diff --git a/trp/trp_ptable.c b/trp/trp_ptable.c index 8b16abd..f5ea1fb 100644 --- a/trp/trp_ptable.c +++ b/trp/trp_ptable.c @@ -44,6 +44,8 @@ static int trp_peer_destructor(void *object) { TRP_PEER *peer=talloc_get_type_abort(object, TRP_PEER); + if (peer->label!=NULL) + tr_free_name(peer->label); if (peer->servicename!=NULL) tr_free_name(peer->servicename); return 0; @@ -53,6 +55,7 @@ TRP_PEER *trp_peer_new(TALLOC_CTX *memctx) TRP_PEER *peer=talloc(memctx, TRP_PEER); if (peer!=NULL) { peer->next=NULL; + peer->label=NULL; peer->server=NULL; peer->servicename=NULL; peer->gss_names=NULL; @@ -86,15 +89,16 @@ static TRP_PEER *trp_peer_tail(TRP_PEER *peer) * Do not modify or free the label. */ TR_NAME *trp_peer_get_label(TRP_PEER *peer) { - TR_GSS_NAMES_ITER *iter=tr_gss_names_iter_new(NULL); - TR_NAME *name=NULL; + char *s=NULL; - /* for now, use the first gss name */ - if (iter!=NULL) { - name=tr_gss_names_iter_first(iter, peer->gss_names); - talloc_free(iter); + if (peer->label==NULL) { + s=talloc_asprintf(NULL, "%s:%u", peer->server, peer->port); + if (s!=NULL) { + peer->label=tr_new_name(s); + talloc_free(s); + } } - return name; + return peer->label; } /* Get a name that identifies this peer for display to the user, etc. diff --git a/trp/trp_upd.c b/trp/trp_upd.c index 927c993..98898ff 100644 --- a/trp/trp_upd.c +++ b/trp/trp_upd.c @@ -37,23 +37,27 @@ #include #include +#include +#include #include /* static prototypes */ -static void *trp_inforec_route_new(TALLOC_CTX *mem_ctx); -static void trp_inforec_route_print(TRP_INFOREC_DATA); +static TRP_INFOREC_DATA *trp_inforec_route_new(TALLOC_CTX *mem_ctx); +static void trp_inforec_route_print(TRP_INFOREC_DATA *); +static TRP_INFOREC_DATA *trp_inforec_comm_new(TALLOC_CTX *mem_ctx); +static void trp_inforec_comm_print(TRP_INFOREC_DATA *); struct trp_inforec_type_entry { const char *name; TRP_INFOREC_TYPE type; - void *(*allocate)(TALLOC_CTX *); - void (*print)(TRP_INFOREC_DATA); + TRP_INFOREC_DATA *(*allocate)(TALLOC_CTX *); + void (*print)(TRP_INFOREC_DATA *); }; static struct trp_inforec_type_entry trp_inforec_type_table[] = { { "route", TRP_INFOREC_TYPE_ROUTE, trp_inforec_route_new, trp_inforec_route_print }, - { "comm", TRP_INFOREC_TYPE_COMMUNITY, NULL, NULL }, + { "comm", TRP_INFOREC_TYPE_COMMUNITY, trp_inforec_comm_new, trp_inforec_comm_print }, { NULL, TRP_INFOREC_TYPE_UNKNOWN, NULL, NULL } /* must be the last entry */ }; @@ -95,45 +99,77 @@ static int trp_inforec_route_destructor(void *object) TRP_INFOREC_ROUTE *body=talloc_get_type_abort(object, TRP_INFOREC_ROUTE); /* clean up TR_NAME data, which are not managed by talloc */ - if (body->comm != NULL) { - tr_free_name(body->comm); - body->comm=NULL; - tr_debug("trp_inforec_route_destructor: freed community"); - } - if (body->realm != NULL) { - tr_free_name(body->realm); - body->realm=NULL; - tr_debug("trp_inforec_route_destructor: freed realm"); - } - if (body->trust_router != NULL) { + if (body->trust_router != NULL) tr_free_name(body->trust_router); - body->trust_router=NULL; - tr_debug("trp_inforec_route_destructor: freed trust_router"); - } - if (body->next_hop != NULL) { + if (body->next_hop != NULL) tr_free_name(body->next_hop); - body->next_hop=NULL; - tr_debug("trp_inforec_route_destructor: freed next_hop"); - } - return 0; } -static void *trp_inforec_route_new(TALLOC_CTX *mem_ctx) +static TRP_INFOREC_DATA *trp_inforec_route_new(TALLOC_CTX *mem_ctx) { - TRP_INFOREC_ROUTE *new_rec=talloc(mem_ctx, TRP_INFOREC_ROUTE); + TRP_INFOREC_DATA *new_data=talloc(mem_ctx, TRP_INFOREC_DATA); + TRP_INFOREC_ROUTE *new_rec=NULL; + + if (new_data==NULL) + return NULL; - if (new_rec != NULL) { - new_rec->comm=NULL; - new_rec->realm=NULL; + new_rec=talloc(new_data, TRP_INFOREC_ROUTE); + if (new_rec == NULL) { + talloc_free(new_data); + new_data=NULL; + } else { new_rec->trust_router=NULL; new_rec->next_hop=NULL; new_rec->next_hop_port=0; new_rec->metric=TRP_METRIC_INFINITY; new_rec->interval=0; talloc_set_destructor((void *)new_rec, trp_inforec_route_destructor); + new_data->route=new_rec; } - return new_rec; + + return new_data; +} + + +static int trp_inforec_comm_destructor(void *obj) +{ + TRP_INFOREC_COMM *rec=talloc_get_type_abort(obj, TRP_INFOREC_COMM); + if (rec->owner_realm!=NULL) + tr_free_name(rec->owner_realm); + if (rec->owner_contact!=NULL) + tr_free_name(rec->owner_contact); + if (rec->provenance!=NULL) + json_decref(rec->provenance); + return 0; +} + +static TRP_INFOREC_DATA *trp_inforec_comm_new(TALLOC_CTX *mem_ctx) +{ + TRP_INFOREC_DATA *new_data=talloc(mem_ctx, TRP_INFOREC_DATA); + TRP_INFOREC_COMM *new_rec=NULL; + + if (new_data==NULL) + return NULL; + + new_rec=talloc(new_data, TRP_INFOREC_COMM); + if (new_rec==NULL) { + talloc_free(new_data); + new_data=NULL; + } else { + new_rec->comm_type=TR_COMM_UNKNOWN; + new_rec->role=TR_ROLE_UNKNOWN; + new_rec->apcs=NULL; + new_rec->owner_realm=NULL; + new_rec->owner_contact=NULL; + new_rec->expiration_interval=0; + new_rec->provenance=NULL; + new_rec->interval=0; + talloc_set_destructor((void *)new_rec, trp_inforec_comm_destructor); + new_data->comm=new_rec; + } + + return new_data; } TRP_INFOREC *trp_inforec_get_next(TRP_INFOREC *rec) @@ -159,7 +195,7 @@ void trp_inforec_set_next(TRP_INFOREC *rec, TRP_INFOREC *next_rec) TRP_INFOREC_TYPE trp_inforec_get_type(TRP_INFOREC *rec) { - if (rec) + if (rec!=NULL) return rec->type; else return TRP_INFOREC_TYPE_UNKNOWN; @@ -171,12 +207,12 @@ void trp_inforec_set_type(TRP_INFOREC *rec, TRP_INFOREC_TYPE type) rec->type=type; } -TR_NAME *trp_inforec_get_comm(TRP_INFOREC *rec) +TR_NAME *trp_inforec_get_trust_router(TRP_INFOREC *rec) { switch (rec->type) { case TRP_INFOREC_TYPE_ROUTE: - if (rec->data.route!=NULL) - return rec->data.route->comm; + if (rec->data->route!=NULL) + return rec->data->route->trust_router; break; default: break; @@ -184,17 +220,17 @@ TR_NAME *trp_inforec_get_comm(TRP_INFOREC *rec) return NULL; } -TR_NAME *trp_inforec_dup_comm(TRP_INFOREC *rec) +TR_NAME *trp_inforec_dup_trust_router(TRP_INFOREC *rec) { - return tr_dup_name(trp_inforec_get_comm(rec)); + return tr_dup_name(trp_inforec_get_trust_router(rec)); } -TRP_RC trp_inforec_set_comm(TRP_INFOREC *rec, TR_NAME *comm) +TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router) { switch (rec->type) { case TRP_INFOREC_TYPE_ROUTE: - if (rec->data.route!=NULL) { - rec->data.route->comm=comm; + if (rec->data->route!=NULL) { + rec->data->route->trust_router=trust_router; return TRP_SUCCESS; } break; @@ -204,12 +240,13 @@ TRP_RC trp_inforec_set_comm(TRP_INFOREC *rec, TR_NAME *comm) return TRP_ERROR; } -TR_NAME *trp_inforec_get_realm(TRP_INFOREC *rec) +/* TODO: need to return hostname/port --jlr */ +TR_NAME *trp_inforec_get_next_hop(TRP_INFOREC *rec) { switch (rec->type) { case TRP_INFOREC_TYPE_ROUTE: - if (rec->data.route!=NULL) - return rec->data.route->realm; + if (rec->data->route!=NULL) + return rec->data->route->next_hop; break; default: break; @@ -217,84 +254,140 @@ TR_NAME *trp_inforec_get_realm(TRP_INFOREC *rec) return NULL; } -TR_NAME *trp_inforec_dup_realm(TRP_INFOREC *rec) +TR_NAME *trp_inforec_dup_next_hop(TRP_INFOREC *rec) { - return tr_dup_name(trp_inforec_get_realm(rec)); + return tr_dup_name(trp_inforec_get_next_hop(rec)); } -TRP_RC trp_inforec_set_realm(TRP_INFOREC *rec, TR_NAME *realm) +TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop) { switch (rec->type) { case TRP_INFOREC_TYPE_ROUTE: - if (rec->data.route!=NULL) { - rec->data.route->realm=realm; - return TRP_SUCCESS; - } + if (rec->data->route==NULL) + return TRP_ERROR; + rec->data->route->next_hop=next_hop; + break; + case TRP_INFOREC_TYPE_COMMUNITY: + /* next hop not used for community records */ break; default: break; } - return TRP_ERROR; + return TRP_SUCCESS; } -TR_NAME *trp_inforec_get_trust_router(TRP_INFOREC *rec) +unsigned int trp_inforec_get_metric(TRP_INFOREC *rec) { switch (rec->type) { case TRP_INFOREC_TYPE_ROUTE: - if (rec->data.route!=NULL) - return rec->data.route->trust_router; + if (rec->data->route!=NULL) + return rec->data->route->metric; break; default: break; } - return NULL; + return TRP_METRIC_INVALID; } -TR_NAME *trp_inforec_dup_trust_router(TRP_INFOREC *rec) +TRP_RC trp_inforec_set_metric(TRP_INFOREC *rec, unsigned int metric) { - return tr_dup_name(trp_inforec_get_trust_router(rec)); + switch (rec->type) { + case TRP_INFOREC_TYPE_ROUTE: + if (rec->data->route!=NULL) { + rec->data->route->metric=metric; + return TRP_SUCCESS; + } + break; + default: + break; + } + return TRP_ERROR; } -TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router) +unsigned int trp_inforec_get_interval(TRP_INFOREC *rec) +{ + switch (rec->type) { + case TRP_INFOREC_TYPE_ROUTE: + if (rec->data->route!=NULL) + return rec->data->route->interval; + break; + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) + return rec->data->comm->interval; + break; + default: + break; + } + return TRP_INTERVAL_INVALID; +} + +TRP_RC trp_inforec_set_interval(TRP_INFOREC *rec, unsigned int interval) { switch (rec->type) { case TRP_INFOREC_TYPE_ROUTE: - if (rec->data.route!=NULL) { - rec->data.route->trust_router=trust_router; + if (rec->data->route!=NULL) { + rec->data->route->interval=interval; return TRP_SUCCESS; } break; + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) { + rec->data->comm->interval=interval; + return TRP_SUCCESS; + } default: break; } return TRP_ERROR; } -/* TODO: need to return hostname/port --jlr */ -TR_NAME *trp_inforec_get_next_hop(TRP_INFOREC *rec) +time_t trp_inforec_get_exp_interval(TRP_INFOREC *rec) { switch (rec->type) { - case TRP_INFOREC_TYPE_ROUTE: - if (rec->data.route!=NULL) - return rec->data.route->next_hop; + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) + return rec->data->comm->expiration_interval; break; default: break; } - return NULL; + return 0; } -TR_NAME *trp_inforec_dup_next_hop(TRP_INFOREC *rec) +TRP_RC trp_inforec_set_exp_interval(TRP_INFOREC *rec, time_t expint) { - return tr_dup_name(trp_inforec_get_next_hop(rec)); + switch (rec->type) { + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) { + rec->data->comm->expiration_interval=expint; + return TRP_SUCCESS; + } + break; + default: + break; + } + return TRP_ERROR; } -TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop) +TR_COMM_TYPE trp_inforec_get_comm_type(TRP_INFOREC *rec) { switch (rec->type) { - case TRP_INFOREC_TYPE_ROUTE: - if (rec->data.route!=NULL) { - rec->data.route->next_hop=next_hop; + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) + return rec->data->comm->comm_type; + break; + default: + break; + } + return TR_COMM_UNKNOWN; +} + +TRP_RC trp_inforec_set_comm_type(TRP_INFOREC *rec, TR_COMM_TYPE type) +{ + switch (rec->type) { + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) { + rec->data->comm->comm_type=type; return TRP_SUCCESS; } break; @@ -304,53 +397,83 @@ TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop) return TRP_ERROR; } -unsigned int trp_inforec_get_metric(TRP_INFOREC *rec) +TR_REALM_ROLE trp_inforec_get_role(TRP_INFOREC *rec) { switch (rec->type) { - case TRP_INFOREC_TYPE_ROUTE: - if (rec->data.route!=NULL) - return rec->data.route->metric; + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) + return rec->data->comm->role; break; default: break; } - return TRP_METRIC_INVALID; + return TR_ROLE_UNKNOWN; } -TRP_RC trp_inforec_set_metric(TRP_INFOREC *rec, unsigned int metric) +TRP_RC trp_inforec_set_role(TRP_INFOREC *rec, TR_REALM_ROLE role) { switch (rec->type) { - case TRP_INFOREC_TYPE_ROUTE: - if (rec->data.route!=NULL) { - rec->data.route->metric=metric; + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) { + rec->data->comm->role=role; return TRP_SUCCESS; + break; } + default: break; + } + return TRP_ERROR; +} + +TR_APC *trp_inforec_get_apcs(TRP_INFOREC *rec) +{ + switch (rec->type) { + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) + return rec->data->comm->apcs; + break; + default: + break; + } + return NULL; +} + +TRP_RC trp_inforec_set_apcs(TRP_INFOREC *rec, TR_APC *apcs) +{ + switch (rec->type) { + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) { + rec->data->comm->apcs=apcs; + talloc_steal(rec, apcs); + return TRP_SUCCESS; + } + break; + default: break; } return TRP_ERROR; } -unsigned int trp_inforec_get_interval(TRP_INFOREC *rec) +TR_NAME *trp_inforec_get_owner_realm(TRP_INFOREC *rec) { switch (rec->type) { - case TRP_INFOREC_TYPE_ROUTE: - if (rec->data.route!=NULL) - return rec->data.route->interval; + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) + return rec->data->comm->owner_realm; break; default: break; } - return TRP_INTERVAL_INVALID; + return NULL; } -TRP_RC trp_inforec_set_interval(TRP_INFOREC *rec, unsigned int interval) +TRP_RC trp_inforec_set_owner_realm(TRP_INFOREC *rec, TR_NAME *name) { switch (rec->type) { - case TRP_INFOREC_TYPE_ROUTE: - if (rec->data.route!=NULL) { - rec->data.route->interval=interval; + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) { + rec->data->comm->owner_realm=name; return TRP_SUCCESS; default: break; @@ -360,35 +483,132 @@ TRP_RC trp_inforec_set_interval(TRP_INFOREC *rec, unsigned int interval) return TRP_ERROR; } -/* for internal use only; must set rec->type before calling this */ -static TRP_RC trp_inforec_set_data(TRP_INFOREC *rec, void *data) +TR_NAME *trp_inforec_get_owner_contact(TRP_INFOREC *rec) +{ + switch (rec->type) { + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) + return rec->data->comm->owner_contact; + break; + default: + break; + } + return NULL; +} + +TRP_RC trp_inforec_set_owner_contact(TRP_INFOREC *rec, TR_NAME *name) +{ + switch (rec->type) { + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) { + rec->data->comm->owner_contact=name; + return TRP_SUCCESS; + } + break; + default: + break; + } + return TRP_ERROR; +} + +/* caller needs to incref the output if they're going to hang on to it */ +json_t *trp_inforec_get_provenance(TRP_INFOREC *rec) +{ + switch (rec->type) { + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) + return rec->data->comm->provenance; + break; + default: + break; + } + return NULL; +} + +/* increments the reference count */ +TRP_RC trp_inforec_set_provenance(TRP_INFOREC *rec, json_t *prov) +{ + switch (rec->type) { + case TRP_INFOREC_TYPE_COMMUNITY: + if (rec->data->comm!=NULL) { + if (rec->data->comm->provenance!=NULL) + json_decref(rec->data->comm->provenance); + rec->data->comm->provenance=prov; + json_incref(prov); + return TRP_SUCCESS; + } + break; + default: + break; + } + return TRP_ERROR; +} + +static TRP_RC trp_inforec_add_to_provenance(TRP_INFOREC *rec, TR_NAME *name) { - if (data==NULL) - return TRP_ERROR; + json_t *jname=NULL; switch (rec->type) { case TRP_INFOREC_TYPE_ROUTE: - rec->data.route=talloc_get_type(data, TRP_INFOREC_ROUTE); + /* no provenance list */ + break; + case TRP_INFOREC_TYPE_COMMUNITY: + jname=tr_name_to_json_string(name); + if (jname==NULL) + return TRP_ERROR; + if (rec->data->comm->provenance==NULL) { + rec->data->comm->provenance=json_array(); + if (rec->data->comm->provenance==NULL) { + json_decref(jname); + return TRP_ERROR; + } + } + if (0!=json_array_append_new(rec->data->comm->provenance, jname)) { + json_decref(jname); + return TRP_ERROR; + } break; default: - return TRP_BADTYPE; + break; } return TRP_SUCCESS; } +TR_NAME *trp_inforec_dup_origin(TRP_INFOREC *rec) +{ + TR_NAME *origin=NULL; + json_t *prov=trp_inforec_get_provenance(rec); + const char *s=NULL; + + if (prov==NULL) + return NULL; + + s=json_string_value(json_array_get(prov, 0)); + if (s==NULL) { + tr_debug("trp_inforec_dup_origin: empty origin in provenance list."); + return NULL; + } + origin=tr_new_name(s); + return origin; +} + /* generic record type */ TRP_INFOREC *trp_inforec_new(TALLOC_CTX *mem_ctx, TRP_INFOREC_TYPE type) { TRP_INFOREC *new_rec=talloc(mem_ctx, TRP_INFOREC); + TRP_INFOREC_DATA *data=NULL; struct trp_inforec_type_entry *dtype=get_trp_inforec_type_entry(type); if ((new_rec != NULL) && (dtype->type != TRP_INFOREC_TYPE_UNKNOWN)) { trp_inforec_set_type(new_rec, type); trp_inforec_set_next(new_rec, NULL); if (dtype->allocate!=NULL) { - if (TRP_SUCCESS!=trp_inforec_set_data(new_rec, dtype->allocate(new_rec))) { + data=dtype->allocate(new_rec); + if (data!=NULL) + new_rec->data=data; + else { talloc_free(new_rec); - new_rec=NULL; + return NULL; } } } @@ -404,6 +624,10 @@ void trp_inforec_free(TRP_INFOREC *rec) static int trp_upd_destructor(void *object) { TRP_UPD *upd=talloc_get_type_abort(object, TRP_UPD); + if (upd->realm!=NULL) + tr_free_name(upd->realm); + if (upd->comm!=NULL) + tr_free_name(upd->comm); if (upd->peer!=NULL) tr_free_name(upd->peer); return 0; @@ -414,6 +638,8 @@ TRP_UPD *trp_upd_new(TALLOC_CTX *mem_ctx) TRP_UPD *new_body=talloc(mem_ctx, TRP_UPD); if (new_body!=NULL) { + new_body->realm=NULL; + new_body->comm=NULL; new_body->records=NULL; new_body->peer=NULL; talloc_set_destructor((void *)new_body, trp_upd_destructor); @@ -451,6 +677,40 @@ void trp_upd_add_inforec(TRP_UPD *upd, TRP_INFOREC *rec) talloc_steal(upd, rec); } +TR_NAME *trp_upd_get_realm(TRP_UPD *upd) +{ + return upd->realm; +} + +TR_NAME *trp_upd_dup_realm(TRP_UPD *upd) +{ + return tr_dup_name(upd->realm); +} + +void trp_upd_set_realm(TRP_UPD *upd, TR_NAME *realm) +{ + if (upd->realm!=NULL) + tr_free_name(upd->realm); + upd->realm=realm; +} + +TR_NAME *trp_upd_get_comm(TRP_UPD *upd) +{ + return upd->comm; +} + +TR_NAME *trp_upd_dup_comm(TRP_UPD *upd) +{ + return tr_dup_name(upd->comm); +} + +void trp_upd_set_comm(TRP_UPD *upd, TR_NAME *comm) +{ + if (upd->comm!=NULL) + tr_free_name(upd->comm); + upd->comm=comm; +} + TR_NAME *trp_upd_get_peer(TRP_UPD *upd) { return upd->peer; @@ -473,21 +733,41 @@ void trp_upd_set_next_hop(TRP_UPD *upd, const char *hostname, unsigned int port) for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) { if (trp_inforec_set_next_hop(rec, cpy=tr_new_name(hostname)) != TRP_SUCCESS) { - tr_err("trp_upd_set_peer: error setting peer."); + tr_err("trp_upd_set_next_hop: error setting next hop."); tr_free_name(cpy); } } } +void trp_upd_add_to_provenance(TRP_UPD *upd, TR_NAME *name) +{ + TRP_INFOREC *rec=NULL; + + /* add it to all inforecs */ + for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) { + if (TRP_SUCCESS!=trp_inforec_add_to_provenance(rec, name)) + tr_err("trp_upd_set_peer: error adding peer to provenance list."); + } +} + /* pretty print */ -static void trp_inforec_route_print(TRP_INFOREC_DATA data) +static void trp_inforec_route_print(TRP_INFOREC_DATA *data) { - if (data.route!=NULL) { - printf(" community=%.*s\n realm=%.*s\n trust_router=%.*s\n metric=%d\n interval=%d]\n", - data.route->comm->len, data.route->comm->buf, - data.route->realm->len, data.route->realm->buf, - data.route->trust_router->len, data.route->trust_router->buf, - data.route->metric, data.route->interval); + if (data->route!=NULL) { + printf(" trust_router=%.*s\n metric=%d\n interval=%d]\n", + data->route->trust_router->len, data->route->trust_router->buf, + data->route->metric, data->route->interval); } } +static void trp_inforec_comm_print(TRP_INFOREC_DATA *data) +{ + if (data->comm!=NULL) { + printf(" type=%s\n role=%s\n owner=%.*s\n contact=%.*s]\n", + tr_comm_type_to_str(data->comm->comm_type), + tr_realm_role_to_str(data->comm->role), + data->comm->owner_realm->len, data->comm->owner_realm->buf, + data->comm->owner_contact->len, data->comm->owner_contact->buf); + /* TODO: print apcs */ + } +} diff --git a/trp/trpc.c b/trp/trpc.c index 4f32584..b6a2cea 100644 --- a/trp/trpc.c +++ b/trp/trpc.c @@ -191,7 +191,7 @@ void trpc_mq_add(TRPC_INSTANCE *trpc, TR_MQ_MSG *msg) TR_MQ_MSG *trpc_mq_pop(TRPC_INSTANCE *trpc) { - return tr_mq_pop(trpc->mq); + return tr_mq_pop(trpc->mq, 0); } void trpc_mq_clear(TRPC_INSTANCE *trpc) diff --git a/trp/trps.c b/trp/trps.c index 47479c7..c706f72 100644 --- a/trp/trps.c +++ b/trp/trps.c @@ -37,8 +37,12 @@ #include #include #include +#include +#include #include +#include +#include #include #include #include @@ -46,7 +50,7 @@ #include #include #include - +#include static int trps_destructor(void *object) { @@ -116,7 +120,7 @@ void trps_free (TRPS_INSTANCE *trps) TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps) { - return tr_mq_pop(trps->mq); + return tr_mq_pop(trps->mq, 0); } void trps_mq_add(TRPS_INSTANCE *trps, TR_MQ_MSG *msg) @@ -157,6 +161,11 @@ void trps_set_sweep_interval(TRPS_INSTANCE *trps, unsigned int interval) trps->sweep_interval.tv_usec=0; } +void trps_set_ctable(TRPS_INSTANCE *trps, TR_COMM_TABLE *comm) +{ + trps->ctable=comm; +} + void trps_set_ptable(TRPS_INSTANCE *trps, TRP_PTABLE *ptable) { if (trps->ptable!=NULL) @@ -177,6 +186,22 @@ void trps_set_peer_status_callback(TRPS_INSTANCE *trps, void (*cb)(TRP_PEER *, v trp_ptable_iter_free(iter); } +/* Get the label peers will know us by - needs to match trp_peer_get_label() output. + * There is no get, only dup, because we don't store the label except when requested. */ +TR_NAME *trps_dup_label(TRPS_INSTANCE *trps) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_NAME *label=NULL; + char *s=talloc_asprintf(tmp_ctx, "%s:%u", trps->hostname, trps->port); + if (s==NULL) + goto cleanup; + label=tr_new_name(s); + +cleanup: + talloc_free(tmp_ctx); + return label; +} + TRPC_INSTANCE *trps_find_trpc(TRPS_INSTANCE *trps, TRP_PEER *peer) { TRPC_INSTANCE *cur=NULL; @@ -251,36 +276,80 @@ TRP_RC trps_send_msg(TRPS_INSTANCE *trps, TRP_PEER *peer, const char *msg) return rc; } -static int trps_listen (TRPS_INSTANCE *trps, int port) +/* Listens on all interfaces. Returns number of sockets opened. Their + * descriptors are stored in *fd_out, which should point to space for + * up to max_fd of them. */ +static size_t trps_listen(TRPS_INSTANCE *trps, int port, int *fd_out, size_t max_fd) { int rc = 0; int conn = -1; - int optval = 1; - - union { - struct sockaddr_storage storage; - struct sockaddr_in in4; - } addr; + int optval=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; + size_t n_opened=0; + + port_str=talloc_asprintf(NULL, "%d", port); + if (port_str==NULL) { + tr_debug("trps_listen: unable to allocate port."); + return -1; + } + getaddrinfo(NULL, port_str, &hints, &ai_head); + talloc_free(port_str); - struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4; + 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("trps_listen: unable to open socket."); + continue; + } - saddr->sin_port = htons (port); - saddr->sin_family = AF_INET; - saddr->sin_addr.s_addr = INADDR_ANY; + optval=1; + if (0!=setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) + tr_debug("trps_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("trps_listen: unable to set IPV6_V6ONLY. Skipping interface."); + close(conn); + continue; + } + } - if (0 > (conn = socket (AF_INET, SOCK_STREAM, 0))) - return conn; + rc=bind(conn, ai->ai_addr, ai->ai_addrlen); + if (rc<0) { + tr_debug("trps_listen: unable to bind to socket."); + close(conn); + continue; + } - setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + if (0>listen(conn, 512)) { + tr_debug("trps_listen: unable to listen on bound socket."); + close(conn); + continue; + } - if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in)))) - return rc; + /* ok, this one worked. Save it */ + fd_out[n_opened++]=conn; + } + freeaddrinfo(ai_head); - if (0 > (rc = listen(conn, 512))) - return rc; + if (n_opened==0) { + tr_debug("trps_listen: no addresses available for listening."); + return -1; + } + + tr_debug("trps_listen: TRP Server listening on port %d on %d socket%s", + port, + n_opened, + (n_opened==1)?"":"s"); - tr_debug("trps_listen: TRP Server listening on port %d", port); - return conn; + return n_opened; } /* get the currently selected route if available */ @@ -363,6 +432,8 @@ static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MS case TRP_UPDATE: trp_upd_set_peer(tr_msg_get_trp_upd(*msg), tr_dup_name(conn_peer)); trp_upd_set_next_hop(tr_msg_get_trp_upd(*msg), trp_peer_get_server(peer), 0); /* TODO: 0 should be the configured TID port */ + /* update provenance if necessary */ + trp_upd_add_to_provenance(tr_msg_get_trp_upd(*msg), trp_peer_get_label(peer)); break; case TRP_REQUEST: @@ -384,32 +455,35 @@ int trps_get_listener(TRPS_INSTANCE *trps, TRP_AUTH_FUNC auth_handler, const char *hostname, unsigned int port, - void *cookie) + void *cookie, + int *fd_out, + size_t max_fd) { - int listen = -1; - - if (0 > (listen = trps_listen(trps, port))) { - char errbuf[256]; - if (0 == strerror_r(errno, errbuf, 256)) { - tr_debug("trps_get_listener: Error opening port %d: %s.", port, errbuf); - } else { - tr_debug("trps_get_listener: Unknown error openining port %d.", port); - } - } + size_t n_fd=0; + size_t ii=0; - if (listen > 0) { + n_fd=trps_listen(trps, port, fd_out, max_fd); + if (n_fd==0) + tr_debug("trps_get_listener: Error opening port %d."); + else { /* opening port succeeded */ tr_debug("trps_get_listener: Opened port %d.", port); - /* make this socket non-blocking */ - if (0 != fcntl(listen, F_SETFL, O_NONBLOCK)) { - tr_debug("trps_get_listener: Error setting O_NONBLOCK."); - close(listen); - listen=-1; + /* make the sockets non-blocking */ + for (ii=0; ii 0) { + if (n_fd>0) { /* store the caller's request handler & cookie */ trps->msg_handler = msg_handler; trps->auth_handler = auth_handler; @@ -418,7 +492,7 @@ int trps_get_listener(TRPS_INSTANCE *trps, trps->cookie = cookie; } - return listen; + return n_fd; } TRP_RC trps_authorize_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn) @@ -458,6 +532,7 @@ void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn) tr_debug("trps_handle_connection: connection closed."); } +/* TODO: check realm/comm, now part of the update instead of inforec */ static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd) { if (upd==NULL) { @@ -465,6 +540,16 @@ static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd) return TRP_BADARG; } + if (trp_upd_get_realm(upd)==NULL) { + tr_notice("trps_validate_update: received TRP update without realm."); + return TRP_ERROR; + } + + if (trp_upd_get_comm(upd)==NULL) { + tr_notice("trps_validate_update: received TRP update without community."); + return TRP_ERROR; + } + if (trp_upd_get_inforec(upd)==NULL) { tr_notice("trps_validate_update: received TRP update with no info records."); return TRP_ERROR; @@ -474,6 +559,7 @@ static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd) tr_notice("trps_validate_update: received TRP update without origin peer information."); return TRP_ERROR; } + return TRP_SUCCESS; } @@ -483,9 +569,7 @@ static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec) { switch(trp_inforec_get_type(rec)) { case TRP_INFOREC_TYPE_ROUTE: - if ((trp_inforec_get_comm(rec)==NULL) - || (trp_inforec_get_realm(rec)==NULL) - || (trp_inforec_get_trust_router(rec)==NULL) + if ((trp_inforec_get_trust_router(rec)==NULL) || (trp_inforec_get_next_hop(rec)==NULL)) { tr_debug("trps_validate_inforec: missing record info."); return TRP_ERROR; @@ -504,6 +588,10 @@ static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec) } break; + case TRP_INFOREC_TYPE_COMMUNITY: + /* TODO: validate community updates */ + break; + default: tr_notice("trps_validate_inforec: unsupported record type."); return TRP_UNSUPPORTED; @@ -526,7 +614,7 @@ static unsigned int trps_advertised_metric(TRPS_INSTANCE *trps, TR_NAME *comm, T return trp_route_get_metric(entry) + trps_cost(trps, peer); } -static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec) +static int trps_check_feasibility(TRPS_INSTANCE *trps, TR_NAME *realm, TR_NAME *comm, TRP_INFOREC *rec) { unsigned int rec_metric=trp_inforec_get_metric(rec); unsigned int new_metric=0; @@ -542,9 +630,7 @@ static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec) return 1; /* updates from our current next hop are always feasible*/ - next_hop=trps_get_next_hop(trps, - trp_inforec_get_comm(rec), - trp_inforec_get_realm(rec));; + next_hop=trps_get_next_hop(trps, comm, realm); if ((next_hop!=NULL) && (0==tr_name_cmp(next_hop,trp_inforec_get_next_hop(rec)))) { return 1; @@ -553,10 +639,7 @@ static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec) /* compare the existing metric we advertise to what we would advertise * if we accept this update */ - current_metric=trps_advertised_metric(trps, - trp_inforec_get_comm(rec), - trp_inforec_get_realm(rec), - trp_inforec_get_next_hop(rec)); + current_metric=trps_advertised_metric(trps, comm, realm, trp_inforec_get_next_hop(rec)); new_metric=rec_metric + trps_cost(trps, trp_inforec_get_next_hop(rec)); if (new_metric <= current_metric) return 1; @@ -568,11 +651,12 @@ static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec) static struct timespec *trps_compute_expiry(TRPS_INSTANCE *trps, unsigned int interval, struct timespec *ts) { const unsigned int small_factor=3; /* how many intervals we wait before expiring */ - if (0!=clock_gettime(CLOCK_REALTIME, ts)) { + if (0!=clock_gettime(TRP_CLOCK, ts)) { tr_err("trps_compute_expiry: could not read realtime clock."); ts->tv_sec=0; ts->tv_nsec=0; } + tr_debug("trps_compute_expiry: tv_sec=%u, interval=%u, small_factor*interval=%u", ts->tv_sec, interval, small_factor*interval); ts->tv_sec += small_factor*interval; return ts; } @@ -582,8 +666,8 @@ static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC TRP_ROUTE *entry=NULL; entry=trp_rtable_get_entry(trps->rtable, - trp_inforec_get_comm(rec), - trp_inforec_get_realm(rec), + trp_upd_get_comm(upd), + trp_upd_get_realm(upd), trp_inforec_get_next_hop(rec)); if (entry==NULL) { entry=trp_route_new(NULL); @@ -592,8 +676,8 @@ static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC return TRP_NOMEM; } - trp_route_set_comm(entry, trp_inforec_dup_comm(rec)); - trp_route_set_realm(entry, trp_inforec_dup_realm(rec)); + trp_route_set_comm(entry, trp_upd_dup_comm(upd)); + trp_route_set_realm(entry, trp_upd_dup_realm(upd)); trp_route_set_peer(entry, trp_upd_dup_peer(upd)); trp_route_set_trust_router(entry, trp_inforec_dup_trust_router(rec)); trp_route_set_next_hop(entry, trp_inforec_dup_next_hop(rec)); @@ -636,11 +720,292 @@ static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC return TRP_SUCCESS; } -static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd) + +static TRP_RC trps_handle_inforec_route(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec) { + TRP_ROUTE *route=NULL; unsigned int feas=0; + + /* determine feasibility */ + feas=trps_check_feasibility(trps, trp_upd_get_realm(upd), trp_upd_get_comm(upd), rec); + tr_debug("trps_handle_update: record feasibility=%d", feas); + + /* do we have an existing route? */ + route=trps_get_route(trps, + trp_upd_get_comm(upd), + trp_upd_get_realm(upd), + trp_upd_get_peer(upd)); + if (route!=NULL) { + /* there was a route table entry already */ + tr_debug("trps_handle_updates: route entry already exists."); + if (feas) { + /* Update is feasible. Accept it. */ + trps_accept_update(trps, upd, rec); + } else { + /* Update is infeasible. Ignore it unless the trust router has changed. */ + if (0!=tr_name_cmp(trp_route_get_trust_router(route), + trp_inforec_get_trust_router(rec))) { + /* the trust router associated with the route has changed, treat update as a retraction */ + trps_retract_route(trps, route); + } + } + } else { + /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */ + tr_debug("trps_handle_update: no route entry exists yet."); + if (feas && trp_metric_is_finite(trp_inforec_get_metric(rec))) + trps_accept_update(trps, upd, rec); + } + + return TRP_SUCCESS; +} + +static int trps_name_in_provenance(TR_NAME *name, json_t *prov) +{ + size_t ii=0; + TR_NAME *this_name=NULL; + const char *s=NULL; + + if (prov==NULL) + return 0; /* no provenance list, so it has no names in it */ + + /* now check to see if name is in the provenance */ + for (ii=0; iiexpiration_interval=trp_inforec_get_exp_interval(rec); + talloc_steal(mem_ctx, comm); + +cleanup: + talloc_free(tmp_ctx); + return comm; +} + +static TR_RP_REALM *trps_create_new_rp_realm(TALLOC_CTX *mem_ctx, TR_NAME *realm_id, TRP_INFOREC *rec) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_RP_REALM *rp=tr_rp_realm_new(tmp_ctx); + + if (rp==NULL) { + tr_debug("trps_create_new_rp_realm: unable to allocate new realm."); + goto cleanup; + } + /* fill in the realm */ + tr_rp_realm_set_id(rp, tr_dup_name(realm_id)); + if (tr_rp_realm_get_id(rp)==NULL) { + tr_debug("trps_create_new_rp_realm: unable to allocate realm name."); + rp=NULL; + goto cleanup; + } + talloc_steal(mem_ctx, rp); + +cleanup: + talloc_free(tmp_ctx); + return rp; +} + +static TR_IDP_REALM *trps_create_new_idp_realm(TALLOC_CTX *mem_ctx, TR_NAME *realm_id, TRP_INFOREC *rec) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_IDP_REALM *idp=tr_idp_realm_new(tmp_ctx); + + if (idp==NULL) { + tr_debug("trps_create_new_idp_realm: unable to allocate new realm."); + goto cleanup; + } + /* fill in the realm */ + tr_idp_realm_set_id(idp, tr_dup_name(realm_id)); + if (tr_idp_realm_get_id(idp)==NULL) { + tr_debug("trps_create_new_idp_realm: unable to allocate realm name."); + idp=NULL; + goto cleanup; + } + if (trp_inforec_get_apcs(rec)!=NULL) { + tr_idp_realm_set_apcs(idp, tr_apc_dup(tmp_ctx, trp_inforec_get_apcs(rec))); + if (tr_idp_realm_get_apcs(idp)==NULL) { + tr_debug("trps_create_new_idp_realm: unable to allocate APC list."); + idp=NULL; + goto cleanup; + } + } + idp->origin=TR_REALM_DISCOVERED; + + talloc_steal(mem_ctx, idp); + +cleanup: + talloc_free(tmp_ctx); + return idp; +} + +static TRP_RC trps_handle_inforec_comm(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_NAME *comm_id=trp_upd_get_comm(upd); + TR_NAME *realm_id=trp_upd_get_realm(upd); + TR_NAME *origin_id=NULL; + TR_NAME *our_peer_label=NULL; + TR_COMM *comm=NULL; + TR_RP_REALM *rp_realm=NULL; + TR_IDP_REALM *idp_realm=NULL; + struct timespec expiry={0,0}; + TRP_RC rc=TRP_ERROR; + + if ((comm_id==NULL) || (realm_id==NULL)) + goto cleanup; + + origin_id=trp_inforec_dup_origin(rec); + if (origin_id==NULL) + goto cleanup; + + /* see whether we want to add this */ + our_peer_label=trps_dup_label(trps); + if (our_peer_label==NULL) { + tr_debug("trps_handle_inforec_comm: unable to allocate peer label."); + goto cleanup; + } + + if (trps_name_in_provenance(our_peer_label, trp_inforec_get_provenance(rec))) + tr_debug("trps_handle_inforec_comm: rejecting community inforec to avoid provenance loop."); + else { + /* no loop occurring, accept the update */ + comm=tr_comm_table_find_comm(trps->ctable, comm_id); + if (comm==NULL) { + tr_debug("trps_handle_inforec_comm: unknown community %.*s in inforec, creating it.", + comm_id->len, comm_id->buf); + comm=trps_create_new_comm(tmp_ctx, comm_id, rec); + if (comm==NULL) { + tr_debug("trps_handle_inforec_comm: unable to create new community."); + goto cleanup; + } + tr_comm_table_add_comm(trps->ctable, comm); + } + /* TODO: see if other comm data match the new inforec and update or complain */ + + trps_compute_expiry(trps, trp_inforec_get_interval(rec), &expiry); + if ((expiry.tv_sec==0)&&(expiry.tv_nsec==0)) + goto cleanup; + + switch (trp_inforec_get_role(rec)) { + case TR_ROLE_RP: + rp_realm=tr_rp_realm_lookup(trps->ctable->rp_realms, realm_id); + if (rp_realm==NULL) { + tr_debug("trps_handle_inforec_comm: unknown RP realm %.*s in inforec, creating it.", + realm_id->len, realm_id->buf); + rp_realm=trps_create_new_rp_realm(tmp_ctx, realm_id, rec); + if (rp_realm==NULL) { + tr_debug("trps_handle_inforec_comm: unable to create new RP realm."); + /* we may leave an unused community in the table, but it will only last until + * the next table sweep if it does not get any realms before that happens */ + goto cleanup; + } + tr_comm_table_add_rp_realm(trps->ctable, rp_realm); + } + /* TODO: if realm existed, see if data match the new inforec and update or complain */ + tr_comm_add_rp_realm(trps->ctable, comm, rp_realm, trp_inforec_get_interval(rec), trp_inforec_get_provenance(rec), &expiry); + tr_debug("trps_handle_inforec_comm: added RP realm %.*s to comm %.*s (origin %.*s).", + realm_id->len, realm_id->buf, + comm_id->len, comm_id->buf, + origin_id->len, origin_id->buf); + break; + case TR_ROLE_IDP: + idp_realm=tr_idp_realm_lookup(trps->ctable->idp_realms, realm_id); + if (idp_realm==NULL) { + tr_debug("trps_handle_inforec_comm: unknown IDP realm %.*s in inforec, creating it.", + realm_id->len, realm_id->buf); + idp_realm=trps_create_new_idp_realm(tmp_ctx, realm_id, rec); + if (idp_realm==NULL) { + tr_debug("trps_handle_inforec_comm: unable to create new IDP realm."); + /* we may leave an unused community in the table, but it will only last until + * the next table sweep if it does not get any realms before that happens */ + goto cleanup; + } + tr_comm_table_add_idp_realm(trps->ctable, idp_realm); + } + /* TODO: if realm existed, see if data match the new inforec and update or complain */ + tr_comm_add_idp_realm(trps->ctable, comm, idp_realm, trp_inforec_get_interval(rec), trp_inforec_get_provenance(rec), &expiry); + tr_debug("trps_handle_inforec_comm: added IDP realm %.*s to comm %.*s (origin %.*s).", + realm_id->len, realm_id->buf, + comm_id->len, comm_id->buf, + origin_id->len, origin_id->buf); + break; + default: + tr_debug("trps_handle_inforec_comm: unable to add realm."); + goto cleanup; + } + } + + rc=TRP_SUCCESS; + +cleanup: + if (our_peer_label!=NULL) + tr_free_name(our_peer_label); + if (origin_id!=NULL) + tr_free_name(origin_id); + talloc_free(tmp_ctx); + return rc; +} + +static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd) +{ TRP_INFOREC *rec=NULL; - TRP_ROUTE *route=NULL; if (trps_validate_update(trps, upd) != TRP_SUCCESS) { tr_notice("trps_handle_update: received invalid TRP update."); @@ -650,40 +1015,27 @@ static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd) for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) { /* validate/sanity check the record update */ if (trps_validate_inforec(trps, rec) != TRP_SUCCESS) { - tr_notice("trps_handle_update: invalid record in TRP update, discarding entire update."); + tr_notice("trps_handle_update: invalid inforec in TRP update, discarding entire update."); return TRP_ERROR; } } for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) { - /* determine feasibility */ - feas=trps_check_feasibility(trps, rec); - tr_debug("trps_handle_update: record feasibility=%d", feas); - - /* do we have an existing route? */ - route=trps_get_route(trps, - trp_inforec_get_comm(rec), - trp_inforec_get_realm(rec), - trp_upd_get_peer(upd)); - if (route!=NULL) { - /* there was a route table entry already */ - tr_debug("trps_handle_updates: route entry already exists."); - if (feas) { - /* Update is feasible. Accept it. */ - trps_accept_update(trps, upd, rec); - } else { - /* Update is infeasible. Ignore it unless the trust router has changed. */ - if (0!=tr_name_cmp(trp_route_get_trust_router(route), - trp_inforec_get_trust_router(rec))) { - /* the trust router associated with the route has changed, treat update as a retraction */ - trps_retract_route(trps, route); - } - } - } else { - /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */ - tr_debug("trps_handle_update: no route entry exists yet."); - if (feas && trp_metric_is_finite(trp_inforec_get_metric(rec))) - trps_accept_update(trps, upd, rec); + switch (trp_inforec_get_type(rec)) { + case TRP_INFOREC_TYPE_ROUTE: + tr_debug("trps_handle_update: handling route inforec."); + if (TRP_SUCCESS!=trps_handle_inforec_route(trps, upd, rec)) + tr_notice("trps_handle_update: error handling route inforec."); + break; + case TRP_INFOREC_TYPE_COMMUNITY: + tr_debug("trps_handle_update: handling community inforec."); + if (TRP_SUCCESS!=trps_handle_inforec_comm(trps, upd, rec)) + tr_notice("trps_handle_update: error handling community inforec."); + + break; + default: + tr_notice("trps_handle_update: unsupported inforec in TRP update."); + break; } } return TRP_SUCCESS; @@ -791,9 +1143,7 @@ TRP_RC trps_update_active_routes(TRPS_INSTANCE *trps) /* true if curtime >= expiry */ static int trps_expired(struct timespec *expiry, struct timespec *curtime) { - return ((curtime->tv_sec > expiry->tv_sec) - || ((curtime->tv_sec == expiry->tv_sec) - &&(curtime->tv_nsec > expiry->tv_nsec))); + return (tr_cmp_timespec(curtime, expiry) >= 0); } /* Sweep for expired routes. For each expired route, if its metric is infinite, the route is flushed. @@ -806,7 +1156,7 @@ TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps) size_t ii=0; /* use a single time for the entire sweep */ - if (0!=clock_gettime(CLOCK_REALTIME, &sweep_time)) { + if (0!=clock_gettime(TRP_CLOCK, &sweep_time)) { tr_err("trps_sweep_routes: could not read realtime clock."); sweep_time.tv_sec=0; sweep_time.tv_nsec=0; @@ -839,6 +1189,173 @@ TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps) return TRP_SUCCESS; } + +static 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; +} + + +/* Sweep for expired communities/realms/memberships. */ +TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + struct timespec sweep_time={0,0}; + TR_COMM_MEMB *memb=NULL; + TR_COMM_ITER *iter=NULL; + TRP_RC rc=TRP_ERROR; + + /* use a single time for the entire sweep */ + if (0!=clock_gettime(TRP_CLOCK, &sweep_time)) { + tr_err("trps_sweep_ctable: could not read realtime clock."); + sweep_time.tv_sec=0; + sweep_time.tv_nsec=0; + goto cleanup; + } + + /* iterate all memberships */ + iter=tr_comm_iter_new(tmp_ctx); + if (iter==NULL) { + tr_err("trps_sweep_ctable: unable to allocate iterator."); + rc=TRP_NOMEM; + goto cleanup; + } + for (memb=tr_comm_memb_iter_all_first(iter, trps->ctable); + memb!=NULL; + memb=tr_comm_memb_iter_all_next(iter)) { + if (tr_comm_memb_get_origin(memb)==NULL) + continue; /* do not expire local entries */ + + if (tr_comm_memb_is_expired(memb, &sweep_time)) { + if (tr_comm_memb_get_times_expired(memb)>0) { + /* Already expired once; flush. */ + tr_debug("trps_sweep_ctable: flushing expired community membership (%.*s in %.*s, origin %.*s, expired %s).", + tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf, + tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf, + tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf, + timespec_to_str(tr_comm_memb_get_expiry(memb))); + tr_comm_table_remove_memb(trps->ctable, memb); + tr_comm_memb_free(memb); + } else { + /* This is the first expiration. Note this and reset the expiry time. */ + tr_comm_memb_expire(memb); + trps_compute_expiry(trps, tr_comm_memb_get_interval(memb), tr_comm_memb_get_expiry(memb)); + tr_debug("trps_sweep_ctable: community membership expired at %s, resetting expiry to %s (%.*s in %.*s, origin %.*s).", + timespec_to_str(&sweep_time), + timespec_to_str(tr_comm_memb_get_expiry(memb)), + tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf, + tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf, + tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf); + } + } + } + + /* get rid of any unreferenced realms, etc */ + tr_comm_table_sweep(trps->ctable); + +cleanup: + talloc_free(tmp_ctx); + return rc; +} + +/* add metrics */ +static unsigned int trps_metric_add(unsigned int m1, unsigned int m2) +{ + if (trp_metric_is_invalid(m1) || trp_metric_is_invalid(m2)) + return TRP_METRIC_INVALID; + + if (trp_metric_is_infinite(m1) || trp_metric_is_infinite(m2)) + return TRP_METRIC_INFINITY; + + if (trp_metric_is_finite(m1+m2)) + return m1+m2; + else + return TRP_METRIC_INFINITY; +} + +/* convert an rentry into a new trp update info record */ +static TRP_INFOREC *trps_route_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_ROUTE *route) +{ + TRP_INFOREC *rec=trp_inforec_new(mem_ctx, TRP_INFOREC_TYPE_ROUTE); + unsigned int linkcost=0; + + if (rec!=NULL) { + if (trp_route_is_local(route)) + linkcost=0; + else { + linkcost=trp_peer_get_linkcost(trps_get_peer_by_gssname(trps, + trp_route_get_peer(route))); + } + + /* Note that we leave the next hop empty since the recipient fills that in. + * This is where we add the link cost (currently always 1) to the next peer. */ + if ((trp_inforec_set_trust_router(rec, trp_route_dup_trust_router(route)) != TRP_SUCCESS) + ||(trp_inforec_set_metric(rec, + trps_metric_add(trp_route_get_metric(route), + linkcost)) != TRP_SUCCESS) + ||(trp_inforec_set_interval(rec, trps_get_update_interval(trps)) != TRP_SUCCESS)) { + tr_err("trps_route_to_inforec: error creating route update."); + talloc_free(rec); + rec=NULL; + } + } + return rec; +} + +static TRP_UPD *trps_route_to_upd(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_ROUTE *route) +{ + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TRP_UPD *upd=trp_upd_new(tmp_ctx); + TRP_INFOREC *rec=NULL; + + if (upd==NULL) { + tr_err("trps_route_to_upd: could not create update message."); + goto cleanup; + } + trp_upd_set_realm(upd, trp_route_dup_realm(route)); + if (trp_upd_get_realm(upd)==NULL) { + tr_err("trps_route_to_upd: could not copy realm."); + upd=NULL; /* it's still in tmp_ctx, so it will be freed */ + goto cleanup; + } + trp_upd_set_comm(upd, trp_route_dup_comm(route)); + if (trp_upd_get_comm(upd)==NULL) { + tr_err("trps_route_to_upd: could not copy comm."); + upd=NULL; /* it's still in tmp_ctx, so it will be freed */ + goto cleanup; + } + rec=trps_route_to_inforec(tmp_ctx, trps, route); + if (rec==NULL) { + tr_err("trps_route_to_upd: could not create route info record for realm %.*s in comm %.*s.", + trp_route_get_realm(route)->len, trp_route_get_realm(route)->buf, + trp_route_get_comm(route)->len, trp_route_get_comm(route)->buf); + upd=NULL; /* it's till in tmp_ctx, so it will be freed */ + goto cleanup; + } + trp_upd_add_inforec(upd, rec); + + /* sucess */ + talloc_steal(mem_ctx, upd); + +cleanup: + talloc_free(tmp_ctx); + return upd; +} + /* select the correct route to comm/realm to be announced to peer */ static TRP_ROUTE *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer_gssname) { @@ -864,14 +1381,12 @@ static TRP_ROUTE *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, T return route; } -/* returns an array of pointers to updates (*not* an array of updates). Returns number of entries - * via n_update parameter. (The allocated space will generally be larger than required, see note in - * the code.) If triggered is set, sends only triggered updates. */ -static TRP_ROUTE **trps_select_updates_for_peer(TALLOC_CTX *memctx, +/* Add TRP_UPD msgs to the updates GPtrArray. Caller needs to arrange for these to be freed. */ +static TRP_RC trps_select_route_updates_for_peer(TALLOC_CTX *mem_ctx, + GPtrArray *updates, TRPS_INSTANCE *trps, TR_NAME *peer_gssname, - int triggered, - size_t *n_update) + int triggered) { size_t n_comm=0; TR_NAME **comm=trp_rtable_get_comms(trps->rtable, &n_comm); @@ -879,88 +1394,230 @@ static TRP_ROUTE **trps_select_updates_for_peer(TALLOC_CTX *memctx, size_t n_realm=0; size_t ii=0, jj=0; TRP_ROUTE *best=NULL; - TRP_ROUTE **result=NULL; - size_t n_used=0; - - /* Need to allocate space for the results. For simplicity, we just allocate a block - * with space for every route table entry to be returned. This is guaranteed to be large - * enough. If the routing table gets very large, this may be wasteful, but that seems - * unlikely to be significant in the near future. */ - result=talloc_array(memctx, TRP_ROUTE *, trp_rtable_size(trps->rtable)); - if (result==NULL) { - talloc_free(comm); - *n_update=0; - return NULL; - } - + TRP_UPD *upd=NULL; + + if (updates==NULL) + return TRP_BADARG; + for (ii=0; iirtable, comm[ii], &n_realm); for (jj=0; jjrole) { + case TR_ROLE_IDP: + memb=tr_comm_table_find_idp_memb(trps->ctable, + tr_realm_get_id(realm), + tr_comm_get_id(comm)); + break; + case TR_ROLE_RP: + memb=tr_comm_table_find_rp_memb(trps->ctable, + tr_realm_get_id(realm), + tr_comm_get_id(comm)); + break; + default: + break; + } + if (memb!=NULL) { + for (memb=tr_comm_memb_iter_first(iter, memb); + memb!=NULL; + memb=tr_comm_memb_iter_next(iter)) { + rec=trps_memb_to_inforec(tmp_ctx, trps, memb); + if (rec==NULL) { + tr_err("trps_comm_update: unable to allocate inforec."); + upd=NULL; + goto cleanup; + } + trp_upd_add_inforec(upd, rec); + } + } + + if (trp_upd_get_inforec(upd)==NULL) + upd=NULL; /* no inforecs, no reason to send the update */ else - return TRP_METRIC_INFINITY; + talloc_steal(mem_ctx, upd); /* success! */ + +cleanup: + talloc_free(tmp_ctx); + return upd; } -/* convert an rentry into a new trp update info record */ -static TRP_INFOREC *trps_route_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_ROUTE *route) +/* Find all community updates to send to a peer and add these as TR_UPD records + * to the updates GPtrArray. */ +static TRP_RC trps_select_comm_updates_for_peer(TALLOC_CTX *mem_ctx, + GPtrArray *updates, + TRPS_INSTANCE *trps, + TR_NAME *peer_gssname, + int triggered) { - TRP_INFOREC *rec=trp_inforec_new(mem_ctx, TRP_INFOREC_TYPE_ROUTE); - unsigned int linkcost=0; + TALLOC_CTX *tmp_ctx=talloc_new(NULL); + TR_COMM_ITER *comm_iter=NULL; + TR_COMM *comm=NULL; + TR_COMM_ITER *realm_iter=NULL; + TR_REALM *realm=NULL; + TRP_UPD *upd=NULL; + TRP_RC rc=TRP_ERROR; - if (rec!=NULL) { - if (trp_route_is_local(route)) - linkcost=0; - else { - linkcost=trp_peer_get_linkcost(trps_get_peer_by_gssname(trps, - trp_route_get_peer(route))); - } + /* currently do not send any communities on triggered updates */ + if (triggered) { + rc=TRP_SUCCESS; + goto cleanup; + } - /* Note that we leave the next hop empty since the recipient fills that in. - * This is where we add the link cost (currently always 1) to the next peer. */ - if ((trp_inforec_set_comm(rec, trp_route_dup_comm(route)) != TRP_SUCCESS) - ||(trp_inforec_set_realm(rec, trp_route_dup_realm(route)) != TRP_SUCCESS) - ||(trp_inforec_set_trust_router(rec, trp_route_dup_trust_router(route)) != TRP_SUCCESS) - ||(trp_inforec_set_metric(rec, - trps_metric_add(trp_route_get_metric(route), - linkcost)) != TRP_SUCCESS) - ||(trp_inforec_set_interval(rec, trps_get_update_interval(trps)) != TRP_SUCCESS)) { - tr_err("trps_route_to_inforec: error creating route update."); - talloc_free(rec); - rec=NULL; + comm_iter=tr_comm_iter_new(tmp_ctx); + realm_iter=tr_comm_iter_new(tmp_ctx); + if ((comm_iter==NULL) || (realm_iter==NULL)) { + tr_err("trps_select_comm_updates_for_peer: unable to allocate iterator."); + rc=TRP_NOMEM; + goto cleanup; + } + + /* do every community */ + for (comm=tr_comm_table_iter_first(comm_iter, trps->ctable); + comm!=NULL; + comm=tr_comm_table_iter_next(comm_iter)) { + /* do every realm in this community */ + tr_debug("trps_select_comm_updates_for_peer: looking through community %.*s", + tr_comm_get_id(comm)->len, + tr_comm_get_id(comm)->buf); + for (realm=tr_realm_iter_first(realm_iter, trps->ctable, tr_comm_get_id(comm)); + realm!=NULL; + realm=tr_realm_iter_next(realm_iter)) { + /* get the update for this comm/realm */ + tr_debug("trps_select_comm_updates_for_peer: adding realm %.*s", + tr_realm_get_id(realm)->len, + tr_realm_get_id(realm)->buf); + upd=trps_comm_update(mem_ctx, trps, peer_gssname, comm, realm); + if (upd!=NULL) + g_ptr_array_add(updates, upd); } } - return rec; + +cleanup: + talloc_free(tmp_ctx); + return rc; } -/* all routes to a single peer, unless comm/realm are specified (both or neither must be NULL) */ + +/* helper for trps_update_one_peer. Frees the TRP_UPD pointed to by a GPtrArray element */ +static void trps_trp_upd_destroy(gpointer data) +{ + trp_upd_free((TRP_UPD *)data); +} + +/* all routes/communities to a single peer, unless comm/realm are specified (both or neither must be NULL) */ static TRP_RC trps_update_one_peer(TRPS_INSTANCE *trps, TRP_PEER *peer, TRP_UPDATE_TYPE update_type, @@ -970,107 +1627,115 @@ static TRP_RC trps_update_one_peer(TRPS_INSTANCE *trps, TALLOC_CTX *tmp_ctx=talloc_new(NULL); TR_MSG msg; /* not a pointer! */ TRP_UPD *upd=NULL; - TRP_ROUTE **update_list=NULL; - TRP_INFOREC *rec=NULL; - size_t n_updates=0, ii=0; + TRP_ROUTE *route=NULL; + size_t ii=0; char *encoded=NULL; TRP_RC rc=TRP_ERROR; TR_NAME *peer_label=trp_peer_get_label(peer); + GPtrArray *updates=g_ptr_array_new_with_free_func(trps_trp_upd_destroy); + + if (updates==NULL) { + tr_err("trps_update_one_peer: unable to allocate updates array."); + rc=TRP_NOMEM; + goto cleanup; + } switch (update_type) { case TRP_UPDATE_TRIGGERED: - tr_debug("trps_update_one_peer: preparing triggered route update for %.*s", + tr_debug("trps_update_one_peer: preparing triggered update for %.*s", peer_label->len, peer_label->buf); break; case TRP_UPDATE_SCHEDULED: - tr_debug("trps_update_one_peer: preparing scheduled route update for %.*s", + tr_debug("trps_update_one_peer: preparing scheduled update for %.*s", peer_label->len, peer_label->buf); break; case TRP_UPDATE_REQUESTED: - tr_debug("trps_update_one_peer: preparing requested route update for %.*s", + tr_debug("trps_update_one_peer: preparing requested update for %.*s", peer_label->len, peer_label->buf); + break; + default: + tr_err("trps_update_one_peer: invalid update type requested."); + rc=TRP_BADARG; + goto cleanup; } - /* do not fill in peer, recipient does that */ + /* First, gather route updates. */ + tr_debug("trps_update_one_peer: selecting route updates for %.*s.", peer_label->len, peer_label->buf); if ((comm==NULL) && (realm==NULL)) { /* do all realms */ - update_list=trps_select_updates_for_peer(tmp_ctx, - trps, - peer_label, - update_type==TRP_UPDATE_TRIGGERED, - &n_updates); + rc=trps_select_route_updates_for_peer(tmp_ctx, + updates, + trps, + peer_label, + update_type==TRP_UPDATE_TRIGGERED); } else if ((comm!=NULL) && (realm!=NULL)) { /* a single community/realm was requested */ - update_list=talloc(tmp_ctx, TRP_ROUTE *); - if (update_list==NULL) { - tr_err("trps_update_one_peer: could not allocate update_list."); - rc=TRP_NOMEM; - goto cleanup; - } - *update_list=trps_select_realm_update(trps, comm, realm, peer_label); - if (*update_list==NULL) { + route=trps_select_realm_update(trps, comm, realm, peer_label); + if (route==NULL) { /* we have no actual update to send back, MUST send a retraction */ tr_debug("trps_update_one_peer: community/realm without route requested, sending mandatory retraction."); - *update_list=trp_route_new(update_list); - trp_route_set_comm(*update_list, tr_dup_name(comm)); - trp_route_set_realm(*update_list, tr_dup_name(realm)); - trp_route_set_peer(*update_list, tr_new_name("")); - trp_route_set_metric(*update_list, TRP_METRIC_INFINITY); - trp_route_set_trust_router(*update_list, tr_new_name("")); - trp_route_set_next_hop(*update_list, tr_new_name("")); + route=trp_route_new(tmp_ctx); + trp_route_set_comm(route, tr_dup_name(comm)); + trp_route_set_realm(route, tr_dup_name(realm)); + trp_route_set_peer(route, tr_new_name("")); + trp_route_set_metric(route, TRP_METRIC_INFINITY); + trp_route_set_trust_router(route, tr_new_name("")); + trp_route_set_next_hop(route, tr_new_name("")); + } + upd=trps_route_to_upd(tmp_ctx, trps, route); + if (upd==NULL) { + tr_err("trps_update_one_peer: unable to allocate route update."); + rc=TRP_NOMEM; + goto cleanup; } - n_updates=1; + g_ptr_array_add(updates, upd); } else { - tr_err("trps_update_one_peer: error: only comm or realm was specified."); + tr_err("trps_update_one_peer: error: only comm or realm was specified. Need both or neither."); rc=TRP_ERROR; goto cleanup; } - if ((n_updates>0) && (update_list!=NULL)) { - tr_debug("trps_update_one_peer: sending %u update records.", (unsigned int)n_updates); - upd=trp_upd_new(tmp_ctx); - for (ii=0; iilen, peer_label->buf); + rc=trps_select_comm_updates_for_peer(tmp_ctx, updates, trps, peer_label, update_type==TRP_UPDATE_TRIGGERED); + + /* see if we have anything to send */ + if (updates->len<=0) + tr_debug("trps_update_one_peer: no updates for %.*s", peer_label->len, peer_label->buf); + else { + tr_debug("trps_update_one_peer: sending %d update messages.", updates->len); + for (ii=0; iilen; ii++) { + upd=(TRP_UPD *)g_ptr_array_index(updates, ii); + /* now encode the update message */ + tr_msg_set_trp_upd(&msg, upd); + encoded=tr_msg_encode(&msg); + if (encoded==NULL) { + tr_err("trps_update_one_peer: error encoding update."); rc=TRP_ERROR; goto cleanup; } - trp_upd_add_inforec(upd, rec); - } - talloc_free(update_list); - update_list=NULL; - - /* now encode the update message */ - tr_msg_set_trp_upd(&msg, upd); - encoded=tr_msg_encode(&msg); - if (encoded==NULL) { - tr_err("trps_update_one_peer: error encoding update."); - rc=TRP_ERROR; - goto cleanup; - } - tr_debug("trps_update_one_peer: adding message to queue."); - if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS) - tr_err("trps_update_one_peer: error queueing update."); - else - tr_debug("trps_update_one_peer: update queued successfully."); + tr_debug("trps_update_one_peer: adding message to queue."); + if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS) + tr_err("trps_update_one_peer: error queueing update."); + else + tr_debug("trps_update_one_peer: update queued successfully."); - tr_msg_free_encoded(encoded); - encoded=NULL; - trp_upd_free(upd); - upd=NULL; - } else if (n_updates==0) - tr_debug("trps_update_one_peer: no updates for %.*s", peer_label->len, peer_label->buf); + tr_msg_free_encoded(encoded); + encoded=NULL; + } + } rc=TRP_SUCCESS; cleanup: + if (updates!=NULL) + g_ptr_array_free(updates, TRUE); /* frees any TRP_UPD records */ talloc_free(tmp_ctx); return rc; } -/* all routes to all peers */ +/* all routes/communities to all peers */ TRP_RC trps_update(TRPS_INSTANCE *trps, TRP_UPDATE_TYPE update_type) { TALLOC_CTX *tmp_ctx=talloc_new(NULL); @@ -1088,7 +1753,7 @@ TRP_RC trps_update(TRPS_INSTANCE *trps, TRP_UPDATE_TYPE update_type) } for (peer=trp_ptable_iter_first(iter, trps->ptable); - peer!=NULL && rc==TRP_SUCCESS; + (peer!=NULL) && (rc==TRP_SUCCESS); peer=trp_ptable_iter_next(iter)) { if (!trps_peer_connected(trps, peer)) { diff --git a/trust_router.spec b/trust_router.spec index a71aa32..340e628 100644 --- a/trust_router.spec +++ b/trust_router.spec @@ -1,6 +1,6 @@ %global optflags %{optflags} -Wno-parentheses Name: trust_router -Version: 1.5.2 +Version: 2.1.1 Release: 1%{?dist} Summary: Moonshot Trust Router @@ -12,7 +12,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: krb5-devel, glib2-devel BuildRequires: jansson-devel >= 2.4 -BuildRequires: sqlite-devel, openssl-devel, libtalloc-devel +BuildRequires: sqlite-devel, openssl-devel, libtalloc-devel, libevent-devel %{?el7:BuildRequires: systemd} Requires: moonshot-gss-eap >= 0.9.3, sqlite @@ -43,7 +43,7 @@ packages that wish trust_router functionality. %prep %setup -q - +autoreconf -f -i %build %configure --disable-static @@ -57,16 +57,16 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' # Install config files install -D -m 755 redhat/init $RPM_BUILD_ROOT/%{_initrddir}/trust_router -install -D -m 640 redhat/trusts.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/trusts.cfg -install -D -m 640 redhat/default-main.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/default/main.cfg -install -D -m 640 redhat/tr-test-main.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/tr-test/main.cfg +install -D -m 640 redhat/organizations.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/organizations.cfg +install -D -m 640 redhat/default-internal.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/default/internal.cfg +install -D -m 640 redhat/tr-test-internal.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/tr-test/internal.cfg install -D -m 640 redhat/sysconfig $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/trust_router install -D -m 640 redhat/sysconfig.tids $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/tids install -D -m 755 redhat/tids.init $RPM_BUILD_ROOT/%{_initrddir}/tids # Link shared config -ln -s ../../trusts.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/default/trusts.cfg -ln -s ../../trusts.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/tr-test/trusts.cfg +ln -s ../../organizations.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/default/organizations.cfg +ln -s ../../organizations.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/tr-test/organizations.cfg # Install wrapper scripts install -D -m 755 redhat/tidc-wrapper $RPM_BUILD_ROOT/%{_bindir}/tidc-wrapper @@ -113,6 +113,12 @@ chmod 770 /var/log/trust_router %files %defattr(-,root,root,-) %doc README +#%{_bindir}/tidc +#%{_bindir}/tidc-wrapper +#%{_bindir}/tids +#%{_bindir}/tids-wrapper +#%{_bindir}/trust_router +#%{_bindir}/trust_router-wrapper %{_bindir}/* %{_datadir}/trust_router/schema.sql @@ -129,11 +135,11 @@ chmod 770 /var/log/trust_router %dir %attr(755,root,trustrouter) %{_sysconfdir}/trust_router/conf.d/default %dir %attr(755,root,trustrouter) %{_sysconfdir}/trust_router/conf.d/tr-test -%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/trusts.cfg -%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/default/main.cfg -%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/tr-test/main.cfg -%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/default/trusts.cfg -%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/tr-test/trusts.cfg +%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/organizations.cfg +%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/default/internal.cfg +%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/tr-test/internal.cfg +%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/default/organizations.cfg +%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/tr-test/organizations.cfg %files libs %defattr(-,root,root,-)