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; }
/** * 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; }
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; }
/** * 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; }
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; }
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); }
/** * 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; }
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); }
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; }
/* 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(); }
/** * 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); } } }
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; }
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; }
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; }