Beispiel #1
0
TID_REQ *tid_dup_req (TID_REQ *orig_req) 
{
  TID_REQ *new_req = NULL;

  if (NULL == (new_req = talloc_zero(orig_req, TID_REQ))) {
    tr_crit("tid_dup_req: Can't allocated duplicate request.");
    return NULL;
  }

  /* Memcpy for flat fields, not valid until names are duped. */
  memcpy(new_req, orig_req, sizeof(TID_REQ));
  json_incref(new_req->json_references);
  new_req->free_conn = 0;
  
  if ((NULL == (new_req->rp_realm = tr_dup_name(orig_req->rp_realm))) ||
      (NULL == (new_req->realm = tr_dup_name(orig_req->realm))) ||
      (NULL == (new_req->comm = tr_dup_name(orig_req->comm)))) {
	tr_crit("tid_dup_req: Can't duplicate request (names).");
  }

  if (orig_req->orig_coi) {
    if (NULL == (new_req->orig_coi = tr_dup_name(orig_req->orig_coi))) {
      tr_crit("tid_dup_req: Can't duplicate request (orig_coi).");
    }
  }
  
  return new_req;
}
Beispiel #2
0
/**
 * Callback to process a request and produce a response
 *
 * @param req_str JSON-encoded request
 * @param data pointer to a TIDS_INSTANCE
 * @return pointer to the response string or null to send no response
 */
static TR_GSS_RC tids_req_cb(TALLOC_CTX *mem_ctx, TR_MSG *mreq, TR_MSG **mresp, void *data)
{
  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
  TIDS_INSTANCE *tids = talloc_get_type_abort(data, TIDS_INSTANCE);
  TID_REQ *req = NULL;
  TID_RESP *resp = NULL;
  TR_GSS_RC rc = TR_GSS_ERROR;

  /* Get a handle on the request itself. Don't free req - it belongs to mreq */
  req = tid_get_tr_msg_req(mreq);
  if (NULL == req) {
    /* This isn't a TID Request, just drop it. */
    tr_debug("tids_req_cb: Not a TID request, dropped.");
    rc = TR_GSS_INTERNAL_ERROR;
    goto cleanup;
  }

  /* Allocate a response message */
  *mresp = talloc(tmp_ctx, TR_MSG);
  if (*mresp == NULL) {
    /* We cannot create a response message, so all we can really do is emit
     * an error message and return. */
    tr_crit("tids_req_cb: Error allocating response message.");
    rc = TR_GSS_INTERNAL_ERROR;
    goto cleanup;
  }

  /* Allocate a response structure and populate common fields. Put it in the
   * response message's talloc context. */
  resp = tids_create_response(*mresp, req);
  if (resp == NULL) {
    /* If we were unable to create a response, we cannot reply. Log an
     * error if we can, then drop the request. */
    tr_crit("tids_req_cb: Error creating response structure.");
    *mresp = NULL; /* the contents are in tmp_ctx, so they will still be cleaned up */
    rc = TR_GSS_INTERNAL_ERROR;
    goto cleanup;
  }
  /* Now officially assign the response to the message. */
  tid_set_tr_msg_resp(*mresp, resp);

  /* Handle the request and fill in resp */
  if (tids_handle_request(tids, req, resp) >= 0)
    rc = TR_GSS_SUCCESS;
  else {
    /* The TID request was an error response */
    tr_debug("tids_req_cb: Error from tids_handle_request");
    rc = TR_GSS_REQUEST_FAILED;
    /* Fall through, to send the response, either way */
  }

  /* put the response message in the caller's context */
  talloc_steal(mem_ctx, *mresp);

cleanup:
  talloc_free(tmp_ctx);
  return rc;
}
Beispiel #3
0
static DH *tr_msg_decode_dh(json_t *jdh)
{
  DH *dh = NULL;
  json_t *jp = NULL;
  json_t *jg = NULL;
  json_t *jpub_key = NULL;

  if (!(dh = malloc(sizeof(DH)))) {
    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);
    return NULL;
  }

  BN_hex2bn(&(dh->p), json_string_value(jp));
  BN_hex2bn(&(dh->g), json_string_value(jg));
  BN_hex2bn(&(dh->pub_key), json_string_value(jpub_key));

  return dh;
}
Beispiel #4
0
/**
 * Encode/send an error response
 *
 * Part of the public interface
 *
 * @param tids
 * @param req
 * @param err_msg
 * @return
 */
int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_msg) {
  TID_RESP *resp = NULL;
  int rc = 0;

  if ((!tids) || (!req) || (!err_msg)) {
    tr_debug("tids_send_err_response: Invalid parameters.");
    return -1;
  }

  /* If we already sent a response, don't send another no matter what. */
  if (req->resp_sent)
    return 0;

  if (NULL == (resp = tids_create_response(req, req))) {
    tr_crit("tids_send_err_response: Can't create response.");
    return -1;
  }

  /* mark this as an error response, and include the error message */
  resp->result = TID_ERROR;
  resp->err_msg = tr_new_name((char *)err_msg);
  resp->error_path = req->path;

  rc = tids_send_response(tids, req, resp);

  tid_resp_free(resp);
  return rc;
}
Beispiel #5
0
int tr_compute_dh_key(unsigned char **pbuf,
		      BIGNUM *pub_key,
		      DH *priv_dh) {
  size_t buflen;
  unsigned char *buf = NULL;;
  int rc = 0;

  if ((!pbuf) ||
      (!pub_key) ||
      (!priv_dh)) {
    tr_debug("tr_compute_dh_key: Invalid parameters.");
    return(-1);
  }
  *pbuf = NULL;
  buflen = DH_size(priv_dh);
  buf = malloc(buflen);
  if (buf == NULL) {
    tr_crit("tr_compute_dh_key: out of memory");
    return -1;
  }


  rc = DH_compute_key(buf, pub_key, priv_dh);
  if (0 <= rc) {
    *pbuf = buf;
  }else {
    free(buf);
  }
  return rc;
}
Beispiel #6
0
void tid_srvr_get_address(const TID_SRVR_BLK *blk,
			  const struct sockaddr **out_addr,
			  size_t *out_len)
{
    char *colon = NULL;
    assert(blk);
    char *hostname = NULL, *port = NULL;
    int s, len;
    struct addrinfo *result;

    // make sure we don't return garbage
    *out_len = 0;
    *out_addr = NULL;

    /* get a copy of the address */
    hostname = talloc_strdup(blk, blk->aaa_server_addr);

    /* address might contain AAA port number. If so, process it */
    colon = strrchr(hostname, ':');

    /* If there are more than one colon, and the last one is not preceeded by ],
       this is not a port separator, but an IPv6 address (likely) */
    if (strchr(hostname, ':') != colon && *(colon - 1) != ']')
      colon = NULL;

    /* we get two strings, the hostname without the colon, and the port number */
    if (colon != NULL) {
      *colon = '\0';
      port = talloc_strdup(blk, colon + 1);
    }

    /* IPv6 addresses might be surrounded by square brackets */
    len = strlen(hostname);
    if (hostname[0] == '[' && hostname[len - 1] == ']') {
        char *copy = talloc_strndup(NULL, hostname + 1, len - 2);
        talloc_free(hostname);
        hostname = copy;
    }

    s = getaddrinfo(hostname,             // address
                    port ? port : "2083", // port as a string
                    NULL,                 // hints
                    &result);
    if (s != 0 || result == NULL) {
      tr_crit("tid_srvr_get_address: Could not resolve an address from %s", hostname);
      return;
    }

    *out_addr = result->ai_addr;
    *out_len = result->ai_addrlen;

    result->ai_addr = NULL; // to avoid deleting it
    freeaddrinfo(result);
    talloc_free(hostname);
    talloc_free(port);
}
Beispiel #7
0
/**
 * Create a response with minimal fields filled in
 *
 * @param mem_ctx talloc context for the return value
 * @param req request to respond to
 * @return new response structure allocated in the mem_ctx context
 */
static TID_RESP *tids_create_response(TALLOC_CTX *mem_ctx, TID_REQ *req)
{
  TID_RESP *resp=NULL;
  int success=0;

  if (NULL == (resp = tid_resp_new(mem_ctx))) {
    tr_crit("tids_create_response: Error allocating response structure.");
    return NULL;
  }

  resp->result = TID_SUCCESS; /* presume success */
  if ((NULL == (resp->rp_realm = tr_dup_name(req->rp_realm))) ||
      (NULL == (resp->realm = tr_dup_name(req->realm))) ||
      (NULL == (resp->comm = tr_dup_name(req->comm)))) {
    tr_crit("tids_create_response: Error allocating fields in response.");
    goto cleanup;
  }
  if (req->orig_coi) {
    if (NULL == (resp->orig_coi = tr_dup_name(req->orig_coi))) {
      tr_crit("tids_create_response: Error allocating fields in response.");
      goto cleanup;
    }
  }
  if (req->request_id) {
    if (NULL == (resp->request_id = tr_dup_name(req->request_id))) {
      tr_crit("tids_create_response: Error allocating fields in response.");
      goto cleanup;
    }
  }

  success=1;

cleanup:
  if ((!success) && (resp!=NULL)) {
    talloc_free(resp);
    resp=NULL;
  }
  return resp;
}
Beispiel #8
0
DH *tr_create_matching_dh (unsigned char *priv_key,
			   size_t keylen,
			   DH *in_dh) {
  DH *dh = NULL;
  int dh_err = 0;

  if (!in_dh)
    return NULL;

  if (NULL == (dh = DH_new())) {
    tr_crit("tr_create_matching_dh: unable to allocate new DH structure.");
    return NULL;
  }

  if ((NULL == (dh->g = BN_dup(in_dh->g))) ||
      (NULL == (dh->p = BN_dup(in_dh->p)))) {
    DH_free(dh);
    tr_debug("tr_create_matching_dh: Invalid dh parameter values, can't be duped.");
    return NULL;
  }

  /* TBD -- share code with previous function */
  if ((priv_key) && (keylen > 0))
    dh->priv_key = BN_bin2bn(priv_key, keylen, NULL);

  DH_generate_key(dh);		/* generates the public key */
  DH_check(dh, &dh_err);
  if (0 != dh_err) {
    tr_warning("Warning: dh_check failed with %d", dh_err);
    if (dh_err & DH_CHECK_P_NOT_PRIME)
      tr_warning(": p value is not prime");
    else if (dh_err & DH_CHECK_P_NOT_SAFE_PRIME)
      tr_warning(": p value is not a safe prime");
    else if (dh_err & DH_UNABLE_TO_CHECK_GENERATOR)
      tr_warning(": unable to check the generator value");
    else if (dh_err & DH_NOT_SUITABLE_GENERATOR)
      tr_warning(": the g value is not a generator");
    else
      tr_warning("unhandled error %i", dh_err);
  }

  return(dh);
}
Beispiel #9
0
int main(int argc, char *argv[])
{
  TALLOC_CTX *main_ctx=NULL;

  TR_INSTANCE *tr = NULL;
  struct cmdline_args opts;
  struct event_base *ev_base;
  struct tr_socket_event tids_ev;
  struct event *cfgwatch_ev;

  configure_signals();

  /* we're going to be multithreaded, so disable null context tracking */
  talloc_set_abort_fn(tr_abort);
  talloc_disable_null_tracking();
#if TALLOC_DEBUG_ENABLE
  talloc_set_log_fn(tr_talloc_log);
#endif /* TALLOC_DEBUG_ENABLE */
  main_ctx=talloc_new(NULL);

  /* Use standalone logging */
  tr_log_open();

  /***** parse command-line arguments *****/
  /* set defaults */
  opts.config_dir=".";

  /* parse the command line*/
  argp_parse(&argp, argc, argv, 0, 0, &opts);

  /* process options */
  remove_trailing_slash(opts.config_dir);

  /***** create a Trust Router instance *****/
  if (NULL == (tr = tr_create(main_ctx))) {
    tr_crit("Unable to create Trust Router instance, exiting.");
    return 1;
  }

  /***** initialize the trust path query server instance *****/
  if (NULL == (tr->tids = tids_create (tr))) {
    tr_crit("Error initializing Trust Path Query Server instance.");
    return 1;
  }

  /***** initialize the trust router protocol server instance *****/
  if (NULL == (tr->trps = trps_new(tr))) {
    tr_crit("Error initializing Trust Router Protocol Server instance.");
    return 1;
  }

  /***** process configuration *****/
  tr->cfgwatch=tr_cfgwatch_create(tr);
  if (tr->cfgwatch == NULL) {
    tr_crit("Unable to create configuration watcher object, exiting.");
    return 1;
  }
  tr->cfgwatch->config_dir=opts.config_dir;
  tr->cfgwatch->cfg_mgr=tr->cfg_mgr;
  tr->cfgwatch->update_cb=tr_config_changed; /* handle configuration changes */
  tr->cfgwatch->update_cookie=(void *)tr;
  if (0 != tr_read_and_apply_config(tr->cfgwatch)) {
    tr_crit("Error reading configuration, exiting.");
    return 1;
  }

  /***** Set up the event loop *****/
  ev_base=tr_event_loop_init(); /* Set up the event loop */
  if (ev_base==NULL) {
    tr_crit("Error initializing event loop.");
    return 1;
  }

  /* already set config_dir, fstat_list and n_files earlier */
  if (0 != tr_cfgwatch_event_init(ev_base, tr->cfgwatch, &cfgwatch_ev)) {
    tr_crit("Error initializing configuration file watcher.");
    return 1;
  }

  /*tr_status_event_init();*/ /* install status reporting events */

  /* install TID server events */
  if (0 != tr_tids_event_init(ev_base,
                              tr->tids,
                              tr->cfg_mgr,
                              tr->trps,
                             &tids_ev)) {
    tr_crit("Error initializing Trust Path Query Server instance.");
    return 1;
  }

  /* install TRP handler events */
  if (TRP_SUCCESS != tr_trps_event_init(ev_base, tr)) {
    tr_crit("Error initializing Trust Path Query Server instance.");
    return 1;
  }

  tr_event_loop_run(ev_base); /* does not return until we are done */

  tr_destroy(tr); /* thanks to talloc, should destroy everything */

  talloc_free(main_ctx);
  return 0;
}
Beispiel #10
0
/* called when talloc tries to abort */
static void tr_abort(const char *reason)
{
  tr_crit("tr_abort: Critical error, talloc aborted. Reason: %s", reason);
  abort();
}
Beispiel #11
0
/**
 * Clean up any finished TID request processes
 *
 * This is called by the main process after forking each TID request. If you want to be
 * sure finished processes are cleaned up promptly even during a lull in TID requests,
 * this can be called from the main thread of the main process. It is not thread-safe,
 * so should not be used from sub-threads. It should not be called by child processes -
 * this would probably be harmless but ineffective.
 *
 * @param tids
 */
void tids_sweep_procs(TIDS_INSTANCE *tids)
{
  guint ii;
  struct tid_process tp = {0};
  char result[TIDS_MAX_MESSAGE_LEN] = {0};
  ssize_t result_len;
  int status;
  int wait_rc;

  /* loop backwards over the array so we can remove elements as we go */
  for (ii=tids->pids->len; ii > 0; ii--) {
    /* ii-1 is the current index - get our own copy, we may destroy the list's copy */
    tp = g_array_index(tids->pids, struct tid_process, ii-1);

    wait_rc = waitpid(tp.pid, &status, WNOHANG);
    if (wait_rc == 0)
      continue; /* process still running */

    if (wait_rc < 0) {
      /* invalid options will probably keep being invalid, report that condition */
      if(errno == EINVAL)
        tr_crit("tids_sweep_procs: waitpid called with invalid options");

      /* If we got ECHILD, that means the PID was invalid; we'll assume the process was
       * terminated and we missed it. For all other errors, move on
       * to the next PID to check. */
      if (errno != ECHILD)
        continue;

      tr_warning("tid_sweep_procs: TID process %d disappeared", tp.pid);
    }

    /* remove the item (we still have a copy of the data) */
    g_array_remove_index_fast(tids->pids, ii-1); /* disturbs only indices >= ii-1 which we've already handled */

    /* Report exit status unless we got ECHILD above or somehow waitpid returned the wrong pid */
    if (wait_rc == tp.pid) {
      if (WIFEXITED(status)) {
        tr_debug("tids_sweep_procs: TID process %d exited with status %d.", tp.pid, WTERMSIG(status));
      } else if (WIFSIGNALED(status)) {
        tr_debug("tids_sweep_procs: TID process %d terminated by signal %d.", tp.pid, WTERMSIG(status));
      }
    } else if (wait_rc > 0) {
      tr_err("tids_sweep_procs: waitpid returned pid %d, expected %d", wait_rc, tp.pid);
    }

    /* read the pipe - if the TID request worked, it will have written status before terminating */
    result_len = read(tp.read_fd, result, TIDS_MAX_MESSAGE_LEN);
    close(tp.read_fd);

    if ((result_len > 0) && (strcmp(result, TIDS_SUCCESS_MESSAGE) == 0)) {
      tids->req_count++;
      tr_info("tids_sweep_procs: TID process %d exited after successful request.", tp.pid);
    } else if ((result_len > 0) && (strcmp(result, TIDS_ERROR_MESSAGE) == 0)) {
      tids->req_error_count++;
      tr_info("tids_sweep_procs: TID process %d exited after unsuccessful request.", tp.pid);
    } else {
      tids->error_count++;
      tr_info("tids_sweep_procs: TID process %d exited with an error.", tp.pid);
    }
  }
}
Beispiel #12
0
static TID_RESP *tr_msg_decode_tidresp(json_t *jresp)
{
  TID_RESP *tresp = NULL;
  json_t *jresult = NULL;
  json_t *jrp_realm = NULL;
  json_t *jrealm = NULL;
  json_t *jcomm = NULL;
  json_t *jorig_coi = NULL;
  json_t *jservers = NULL;
  json_t *jerr_msg = NULL;

  if (!(tresp=tid_resp_new(NULL))) {
    tr_crit("tr_msg_decode_tidresp(): Error allocating TID_RESP structure.");
    return NULL;
  }
 

  /* store required fields from response */
  if ((NULL == (jresult = json_object_get(jresp, "result"))) ||
      (!json_is_string(jresult)) ||
      (NULL == (jrp_realm = json_object_get(jresp, "rp_realm"))) ||
      (!json_is_string(jrp_realm)) ||
      (NULL == (jrealm = json_object_get(jresp, "target_realm"))) ||
      (!json_is_string(jrealm)) ||
      (NULL == (jcomm = json_object_get(jresp, "comm"))) ||
      (!json_is_string(jcomm))) {
    tr_debug("tr_msg_decode_tidresp(): Error parsing response.");
    talloc_free(tresp);
    return NULL;
  }

  if (0 == (strcmp(json_string_value(jresult), "success"))) {
    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); 
    } 
    else {
      talloc_free(tresp);
      return NULL;
    }
    tresp->result = TID_SUCCESS;
  }
  else {
    tresp->result = TID_ERROR;
    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->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));

  /* 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));
  }
     
  return tresp;
}
Beispiel #13
0
static TID_REQ *tr_msg_decode_tidreq(json_t *jreq)
{
  TID_REQ *treq = NULL;
  json_t *jrp_realm = NULL;
  json_t *jrealm = NULL;
  json_t *jcomm = NULL;
  json_t *jorig_coi = NULL;
  json_t *jdh = NULL;
  json_t *jpath = NULL;
  json_t *jexpire_interval = NULL;

  if (!(treq =tid_req_new())) {
    tr_crit("tr_msg_decode_tidreq(): Error allocating TID_REQ structure.");
    return NULL;
  }
 
  /* store required fields from request */
  if ((NULL == (jrp_realm = json_object_get(jreq, "rp_realm"))) ||
      (NULL == (jrealm = json_object_get(jreq, "target_realm"))) ||
      (NULL == (jcomm = json_object_get(jreq, "community")))) {
    tr_notice("tr_msg_decode(): Error parsing required fields.");
    tid_req_free(treq);
    return NULL;
  }

  jpath = json_object_get(jreq, "path");
  jexpire_interval = json_object_get(jreq, "expiration_interval");

  treq->rp_realm = tr_new_name((char *)json_string_value(jrp_realm));
  treq->realm = tr_new_name((char *)json_string_value(jrealm));
  treq->comm = tr_new_name((char *)json_string_value(jcomm));

  /* Get DH Info from the request */
  if (NULL == (jdh = json_object_get(jreq, "dh_info"))) {
    tr_debug("tr_msg_decode(): Error parsing dh_info.");
    tid_req_free(treq);
    return NULL;
  }
  treq->tidc_dh = tr_msg_decode_dh(jdh);

  /* store optional "orig_coi" field */
  if (NULL != (jorig_coi = json_object_get(jreq, "orig_coi"))) {
    treq->orig_coi = tr_new_name((char *)json_string_value(jorig_coi));
  }

  treq->cons = (TR_CONSTRAINT_SET *) json_object_get(jreq, "constraints");
  if (treq->cons) {
    if (!tr_constraint_set_validate(treq->cons)) {
      tr_debug("Constraint set validation failed");
    tid_req_free(treq);
    return NULL;
    }
    json_incref((json_t *) treq->cons);
    tid_req_cleanup_json(treq, (json_t *) treq->cons);
  }
  if (jpath) {
    json_incref(jpath);
    treq->path = jpath;
    tid_req_cleanup_json(treq, jpath);
  }
  if (jexpire_interval)
    treq->expiration_interval = json_integer_value(jexpire_interval);
  
  return treq;
}
Beispiel #14
0
static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc) {
  TR_COMM *comm = NULL;
  json_t *jid = NULL;
  json_t *jtype = NULL;
  json_t *japcs = NULL;
  json_t *jidps = NULL;
  json_t *jrps = NULL;

  if ((!trc) || (!jcomm) || (!rc)) {
    tr_debug("tr_cfg_parse_one_comm: Bad parameters.");
    if (rc)
      *rc = TR_CFG_BAD_PARAMS;
    return NULL;
  }

  if (NULL == (comm = talloc_zero(trc, TR_COMM))) {
    tr_crit("tr_cfg_parse_one_comm: Out of memory.");
    *rc = TR_CFG_NOMEM;
    return NULL;
  }


  if ((NULL == (jid = json_object_get(jcomm, "community_id"))) ||
      (!json_is_string(jid)) ||
      (NULL == (jtype = json_object_get(jcomm, "type"))) ||
      (!json_is_string(jtype)) ||
      (NULL == (japcs = json_object_get(jcomm, "apcs"))) ||
      (!json_is_array(japcs)) ||
      (NULL == (jidps = json_object_get(jcomm, "idp_realms"))) ||
      (!json_is_array(jidps)) ||
      (NULL == (jrps = json_object_get(jcomm, "rp_realms"))) ||
      (!json_is_array(jrps))) {
    tr_debug("tr_cfg_parse_one_comm: Error parsing Communities configuration.");
    *rc = TR_CFG_NOPARSE;
    return NULL;
  }

  if (NULL == (comm->id = tr_new_name((char *)json_string_value(jid)))) {
    tr_debug("tr_cfg_parse_one_comm: No memory for community id.");
    *rc = TR_CFG_NOMEM;
    return NULL;
  }

  if (0 == strcmp(json_string_value(jtype), "apc")) {
    comm->type = TR_COMM_APC;
  } else if (0 == strcmp(json_string_value(jtype), "coi")) {
    comm->type = TR_COMM_COI;
    if (NULL == (comm->apcs = tr_cfg_parse_apcs(trc, japcs, rc))) {
      tr_debug("tr_cfg_parse_one_comm: Can't parse APCs for COI %s.", comm->id->buf);
      tr_free_name(comm->id);
      return NULL;
    }
  } 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);
    *rc = TR_CFG_NOPARSE;
    return NULL;
  }

  comm->idp_realms = tr_cfg_parse_comm_idps(trc, jidps, 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;
  }

  comm->rp_realms = tr_cfg_parse_comm_rps(trc, jrps, 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;
  }

  if (TR_COMM_APC == comm->type) {
    json_t *jexpire  = json_object_get(jcomm, "expiration_interval");
    comm->expiration_interval = 43200; /*30 days*/
    if (jexpire) {
	if (!json_is_integer(jexpire)) {
	  fprintf(stderr, "tr_parse_comm: expirae_interval is not an integer\n");
	  return NULL;
	}
	comm->expiration_interval = json_integer_value(jexpire);
	if (comm->expiration_interval <= 10)
	  comm->expiration_interval = 11; /* Freeradius waits 10 minutes between successful TR queries*/
	if (comm->expiration_interval > 129600) /* 90 days*/
	comm->expiration_interval = 129600;
    }
  }
  
  return comm;
}