Handle community updates when received. Builds, not tested.
authorJennifer Richards <jennifer@painless-security.com>
Wed, 26 Oct 2016 20:53:59 +0000 (16:53 -0400)
committerJennifer Richards <jennifer@painless-security.com>
Wed, 26 Oct 2016 20:53:59 +0000 (16:53 -0400)
common/tr_comm.c
common/tr_config.c
common/tr_idp.c
include/tr_comm.h
include/tr_config.h
include/tr_idp.h
include/trp_internal.h
tr/tr_tid.c
tr/tr_trp.c
trp/trp_upd.c
trp/trps.c

index 5fe33b6..6222aaf 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <jansson.h>
 #include <talloc.h>
+#include <sys/time.h>
 
 #include <tr_rp.h>
 #include <tr_idp.h>
@@ -98,6 +99,14 @@ void tr_comm_decref(TR_COMM *comm)
     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;
@@ -268,18 +277,33 @@ static TR_COMM *tr_comm_add_func(TR_COMM *comms, TR_COMM *new)
   return comms;
 }
 
-/* guarantees comm is not in the list, not an error if it was't there */
+/* 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)
 {
-  TR_COMM *this=comms;
+  TALLOC_CTX *list_ctx=talloc_parent(comms); /* in case we need to remove the head */
+  TR_COMM *this=NULL;
 
-  if ((this==NULL) || (this==remove))
+  if (comms==NULL)
     return NULL;
-  
-  for (; this->next!=NULL; this=this->next) {
-    if (this->next==remove)
-      this->next=remove->next;
+
+  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->next);
+      /* 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;
+    }
   }
   return comms;
 }
@@ -698,7 +722,12 @@ TR_COMM_MEMB *tr_comm_memb_new(TALLOC_CTX *mem_ctx)
     memb->origin=NULL;
     memb->provenance=NULL;
     memb->interval=0;
-    memb->expiry=NULL;
+    memb->expiry=talloc(memb, struct timespec);
+    if (memb->expiry==NULL) {
+      talloc_free(memb);
+      return NULL;
+    }
+    *(memb->expiry)=(struct timespec){0,0};
     talloc_set_destructor(memb, tr_comm_memb_destructor);
   }
   return memb;
@@ -851,7 +880,8 @@ unsigned int tr_comm_memb_get_interval(TR_COMM_MEMB *memb)
 
 void tr_comm_memb_set_expiry(TR_COMM_MEMB *memb, struct timespec *time)
 {
-  memb->expiry=time;
+  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)
@@ -872,6 +902,8 @@ TR_COMM_TABLE *tr_comm_table_new(TALLOC_CTX *mem_ctx)
   if (ctab!=NULL) {
     ctab->comms=NULL;
     ctab->memberships=NULL;
+    ctab->idp_realms=NULL;
+    ctab->rp_realms=NULL;
   }
   return ctab;
 }
@@ -895,6 +927,8 @@ void tr_comm_table_add_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *new)
 {
   TR_COMM_MEMB *cur=NULL;
 
+  /* TODO: validate the member (must have valid comm and realm) */
+
   /* handle the empty list case */
   if (ctab->memberships==NULL) {
     ctab->memberships=new;
index 5ada21d..b07c8dd 100644 (file)
@@ -51,7 +51,7 @@
 
 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_TABLE *ctab)
@@ -126,13 +126,11 @@ TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx)
   TR_CFG *cfg=talloc(mem_ctx, TR_CFG);
   if (cfg!=NULL) {
     cfg->internal=NULL;
-    cfg->idp_realms=NULL;
-    cfg->rp_realms=NULL;
     cfg->rp_clients=NULL;
     cfg->peers=NULL;
     cfg->default_servers=NULL;
-    cfg->comms=tr_comm_table_new(cfg);
-    if (cfg->comms==NULL) {
+    cfg->ctable=tr_comm_table_new(cfg);
+    if (cfg->ctable==NULL) {
       talloc_free(cfg);
       cfg=NULL;
     }
@@ -1456,8 +1454,8 @@ cleanup:
   /* if we succeeded, link things to the configuration and move out of tmp context */
   if (retval==TR_CFG_SUCCESS) {
     if (new_idp_realms!=NULL) {
-      tr_idp_realm_add(trc->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) {
@@ -1637,7 +1635,7 @@ static void tr_cfg_parse_comm_idps(TR_CFG *trc, json_t *jidps, TR_COMM *comm, TR
       *rc=TR_CFG_ERROR;
       return;
     }
-    tr_comm_add_idp_realm(trc->comms, comm, found_idp, NULL, NULL); /* no provenance, never expires */
+    tr_comm_add_idp_realm(trc->ctable, comm, found_idp, NULL, NULL); /* no provenance, never expires */
   }
 
   *rc=TR_CFG_SUCCESS;
@@ -1677,7 +1675,7 @@ static void tr_cfg_parse_comm_rps(TR_CFG *trc, json_t *jrps, TR_COMM *comm, TR_C
     }
 
     /* see if we already have this RP in this community */
-    found_rp=tr_comm_find_rp(trc->comms, comm, rp_name);
+    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);
@@ -1686,7 +1684,7 @@ static void tr_cfg_parse_comm_rps(TR_CFG *trc, json_t *jrps, TR_COMM *comm, TR_C
     }
 
     /* Add the RP to the community, first see if we have the RP in any community */
-    found_rp=tr_rp_realm_lookup(trc->rp_realms, rp_name);
+    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 */
@@ -1699,9 +1697,9 @@ static void tr_cfg_parse_comm_rps(TR_CFG *trc, json_t *jrps, TR_COMM *comm, TR_C
       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->rp_realms, new_rp);
+      tr_rp_realm_add(trc->ctable->rp_realms, new_rp);
     }
-    tr_comm_add_rp_realm(trc->comms, comm, new_rp, NULL, NULL);
+    tr_comm_add_rp_realm(trc->ctable, comm, new_rp, NULL, NULL);
   }
 }
 
@@ -1839,7 +1837,7 @@ static TR_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg)
       tr_debug("tr_cfg_parse_comms: Community configured: %s.",
                tr_comm_get_id(comm)->buf);
 
-      tr_comm_table_add_comm(trc->comms, comm);
+      tr_comm_table_add_comm(trc->ctable, comm);
     }
   }
   tr_debug("tr_cfg_parse_comms: Finished (rc=%d)", rc);
@@ -1864,12 +1862,12 @@ TR_CFG_RC tr_cfg_validate(TR_CFG *trc)
     rc = TR_CFG_ERROR;
   }
 
-  if (0==tr_comm_table_size(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;
   }
@@ -1992,7 +1990,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;
index 0c2a0c6..3019ef6 100644 (file)
@@ -38,6 +38,7 @@
 #include <trust_router/tr_name.h>
 #include <tr_idp.h>
 #include <tr_config.h>
+#include <tr_debug.h>
 
 static int tr_aaa_server_destructor(void *obj)
 {
@@ -109,12 +110,6 @@ TR_IDP_REALM *tr_idp_realm_new(TALLOC_CTX *mem_ctx)
     idp->aaa_servers=NULL;
     idp->apcs=NULL;
     idp->origin=TR_REALM_LOCAL;
-    idp->peer=NULL;
-    idp->expiry=talloc(idp, struct timespec);
-    if (idp->expiry==NULL) {
-      talloc_free(idp);
-      return NULL;
-    }
     talloc_set_destructor((void *)idp, tr_idp_realm_destructor);
   }
   return idp;
@@ -148,6 +143,36 @@ void tr_idp_realm_set_id(TR_IDP_REALM *idp, TR_NAME *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)
index fae2c82..2d91e6a 100644 (file)
@@ -77,6 +77,8 @@ typedef struct 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 */
 }; 
 
@@ -143,9 +145,10 @@ int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime);
 TR_COMM *tr_comm_new(TALLOC_CTX *mem_ctx);
 void tr_comm_free(TR_COMM *comm);
 void tr_comm_set_id(TR_COMM *comm, TR_NAME *id);
-TR_APC *tr_comm_get_apcs(TR_COMM *comm);
 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);
index 30a45d7..743ff12 100644 (file)
@@ -83,11 +83,9 @@ typedef struct 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_REALM *rp_realms;
   TR_RP_CLIENT *rp_clients;            /* locally associated RP Clients */
   TRP_PTABLE *peers; /* TRP peer table */
-  TR_COMM_TABLE *comms; /* communities */
+  TR_COMM_TABLE *ctable; /* communities/realms */
   TR_AAA_SERVER *default_servers;      /* default server list */
   /* TBD -- Global Filters */
 } TR_CFG;
index 1a5dfff..aa407b8 100644 (file)
@@ -62,8 +62,6 @@ typedef struct tr_idp_realm {
   TR_AAA_SERVER *aaa_servers;
   TR_APC *apcs;
   TR_REALM_ORIGIN origin; /* how did we learn about this realm? */
-  TR_NAME *peer; /* for remote/discovered, who told us about this realm? */
-  struct timespec *expiry; /* when does this realm entry expire? */
   unsigned int refcount; /* how many TR_COMM_MEMBs refer to this realm */
 } TR_IDP_REALM;
   
@@ -72,6 +70,9 @@ 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)))
 char *tr_idp_realm_to_str(TALLOC_CTX *mem_ctx, TR_IDP_REALM *idp);
index a0a77e3..ac7e3ee 100644 (file)
@@ -64,6 +64,7 @@ typedef struct trp_inforec_comm {
   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;
@@ -213,6 +214,7 @@ 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);
@@ -281,10 +283,14 @@ 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 */
index b0715ac..30d9ee1 100644 (file)
@@ -115,7 +115,7 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids,
     goto cleanup;
   }
 
-  if (NULL == (cfg_comm = tr_comm_table_find_comm(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;
@@ -145,7 +145,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_comm_find_rp(cfg_mgr->active->comms, 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;
@@ -165,7 +165,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_table_find_comm(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 +176,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_comm_find_rp(cfg_mgr->active->comms, 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;
@@ -197,7 +197,7 @@ 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, 
+    aaa_servers = tr_idp_aaa_server_lookup(cfg_mgr->active->ctable->idp_realms, 
                                            orig_req->realm, 
                                            orig_req->comm);
   } else {
@@ -217,13 +217,13 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids,
     }
   } else {
     /* if we aren't defaulting, check idp coi and apc membership */
-    if (NULL == (tr_comm_find_idp(cfg_mgr->active->comms, 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_comm_find_idp(cfg_mgr->active->comms, 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;
index 9ca0b2f..fc8661e 100644 (file)
@@ -754,7 +754,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; ii<n_routes; ii++)
       trps_add_route(trps, local_routes[ii]);
@@ -833,7 +833,7 @@ void tr_config_changed(TR_CFG *new_cfg, void *cookie)
   trps_set_connect_interval(trps, new_cfg->internal->trp_connect_interval);
   trps_set_update_interval(trps, new_cfg->internal->trp_update_interval);
   trps_set_sweep_interval(trps, new_cfg->internal->trp_sweep_interval);
-  trps_set_ctable(trps, new_cfg->comms);
+  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??? */
index 1623b84..ee627a9 100644 (file)
@@ -162,6 +162,7 @@ static TRP_INFOREC_DATA *trp_inforec_comm_new(TALLOC_CTX *mem_ctx)
     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);
@@ -340,6 +341,34 @@ TRP_RC trp_inforec_set_interval(TRP_INFOREC *rec, unsigned int interval)
   return TRP_ERROR;
 }
 
+time_t trp_inforec_get_exp_interval(TRP_INFOREC *rec)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL)
+      return rec->data->comm->expiration_interval;
+    break;
+  default:
+    break;
+  }
+  return 0;
+}
+
+TRP_RC trp_inforec_set_exp_interval(TRP_INFOREC *rec, time_t expint)
+{
+  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;
+}
+
 TR_COMM_TYPE trp_inforec_get_comm_type(TRP_INFOREC *rec)
 {
   switch (rec->type) {
@@ -545,6 +574,24 @@ static TRP_RC trp_inforec_add_to_provenance(TRP_INFOREC *rec, TR_NAME *peer)
   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)
 {
index e181dc9..69c6bde 100644 (file)
@@ -186,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;
@@ -524,7 +540,8 @@ static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
     break;
 
   case TRP_INFOREC_TYPE_COMMUNITY:
-    /* TODO: handle community updates -jlr*/
+    /* TODO: validate community updates */
+    break;
     
   default:
     tr_notice("trps_validate_inforec: unsupported record type.");
@@ -653,11 +670,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; ii<json_array_size(prov); ii++) {
+    s=json_string_value(json_array_get(prov, ii));
+    if (s==NULL) {
+      tr_debug("trps_name_in_provenance: empty entry in provenance list.");
+      continue;
+    }
+
+    this_name=tr_new_name(s);
+    if (this_name==NULL) {
+      tr_debug("trps_name_in_provenance: unable to allocate name.");
+      return -1;
+    }
+    if (0==tr_name_cmp(name, this_name)) {
+      tr_free_name(this_name);
+      return 1;
+    }
+    tr_free_name(this_name);
+  }
+  return 0;
+}
+
+static TR_COMM *trps_create_new_comm(TALLOC_CTX *mem_ctx, TR_NAME *comm_id, TRP_INFOREC *rec)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_COMM *comm=tr_comm_new(tmp_ctx);
+  
+  if (comm==NULL) {
+    tr_debug("trps_create_new_comm: unable to allocate new community.");
+    goto cleanup;
+  }
+  /* fill in the community with info */
+  tr_comm_set_id(comm, tr_dup_name(comm_id));
+  if (tr_comm_get_id(comm)==NULL) {
+    tr_debug("trps_create_new_comm: unable to allocate community name.");
+    comm=NULL;
+    goto cleanup;
+  }
+  tr_comm_set_type(comm, trp_inforec_get_comm_type(rec));
+  if (trp_inforec_get_apcs(rec)!=NULL) {
+    tr_comm_set_apcs(comm, tr_apc_dup(tmp_ctx, trp_inforec_get_apcs(rec)));
+    if (tr_comm_get_apcs(comm)==NULL) {
+      tr_debug("trps_create_new_comm: unable to allocate APC list.");
+      comm=NULL;
+      goto cleanup;
+    }
+  }
+  if (trp_inforec_get_owner_realm(rec)!=NULL) {
+    tr_comm_set_owner_realm(comm, tr_dup_name(trp_inforec_get_owner_realm(rec)));
+    if (tr_comm_get_owner_realm(comm)==NULL) {
+      tr_debug("trps_create_new_comm: unable to allocate owner realm name.");
+      comm=NULL;
+      goto cleanup;
+    }
+  }
+  if (trp_inforec_get_owner_contact(rec)!=NULL) {
+    tr_comm_set_owner_contact(comm, tr_dup_name(trp_inforec_get_owner_contact(rec)));
+    if (tr_comm_get_owner_contact(comm)==NULL) {
+      tr_debug("trps_create_new_comm: unable to allocate owner contact.");
+      comm=NULL;
+      goto cleanup;
+    }
+  }
+  comm->expiration_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 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_rp_realm_add(trps->ctable->rp_realms, 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_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_idp_realm_add(trps->ctable->idp_realms, 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_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.");
@@ -667,40 +965,24 @@ 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, 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);
+    switch (trp_inforec_get_type(rec)) {
+    case TRP_INFOREC_TYPE_ROUTE:
+      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:
+      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;