mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-11 05:33:47 +01:00
Expose authority certificate download statuses on the control port
This commit is contained in:
parent
8cf9fe5ba6
commit
18c6e13993
206
src/or/control.c
206
src/or/control.c
@ -2053,6 +2053,29 @@ getinfo_helper_dir(control_connection_t *control_conn,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Turn a smartlist of digests into a human-readable list of hex strings */
|
||||||
|
|
||||||
|
static char *
|
||||||
|
digest_list_to_string(smartlist_t *sl)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
char *result, *s;
|
||||||
|
|
||||||
|
/* Allow for newlines, and a \0 at the end */
|
||||||
|
len = smartlist_len(sl) * (HEX_DIGEST_LEN + 1) + 1;
|
||||||
|
result = tor_malloc_zero(len);
|
||||||
|
|
||||||
|
s = result;
|
||||||
|
SMARTLIST_FOREACH_BEGIN(sl, char *, digest) {
|
||||||
|
base16_encode(s, HEX_DIGEST_LEN + 1, digest, DIGEST_LEN);
|
||||||
|
s[HEX_DIGEST_LEN] = '\n';
|
||||||
|
s += HEX_DIGEST_LEN + 1;
|
||||||
|
} SMARTLIST_FOREACH_END(digest);
|
||||||
|
*s = '\0';
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/** Turn a download_status_t into a human-readable description in a newly
|
/** Turn a download_status_t into a human-readable description in a newly
|
||||||
* allocated string. */
|
* allocated string. */
|
||||||
|
|
||||||
@ -2155,6 +2178,135 @@ download_status_to_string(const download_status_t *dl)
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Handle the consensus download cases for getinfo_helper_downloads() */
|
||||||
|
static void
|
||||||
|
getinfo_helper_downloads_networkstatus(const char *flavor,
|
||||||
|
download_status_t **dl_to_emit,
|
||||||
|
const char **errmsg)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We get the one for the current bootstrapped status by default, or
|
||||||
|
* take an extra /bootstrap or /running suffix
|
||||||
|
*/
|
||||||
|
if (strcmp(flavor, "ns") == 0) {
|
||||||
|
*dl_to_emit = networkstatus_get_dl_status_by_flavor(FLAV_NS);
|
||||||
|
} else if (strcmp(flavor, "ns/bootstrap") == 0) {
|
||||||
|
*dl_to_emit = networkstatus_get_dl_status_by_flavor_bootstrap(FLAV_NS);
|
||||||
|
} else if (strcmp(flavor, "ns/running") == 0 ) {
|
||||||
|
*dl_to_emit = networkstatus_get_dl_status_by_flavor_running(FLAV_NS);
|
||||||
|
} else if (strcmp(flavor, "microdesc") == 0) {
|
||||||
|
*dl_to_emit = networkstatus_get_dl_status_by_flavor(FLAV_MICRODESC);
|
||||||
|
} else if (strcmp(flavor, "microdesc/bootstrap") == 0) {
|
||||||
|
*dl_to_emit =
|
||||||
|
networkstatus_get_dl_status_by_flavor_bootstrap(FLAV_MICRODESC);
|
||||||
|
} else if (strcmp(flavor, "microdesc/running") == 0) {
|
||||||
|
*dl_to_emit =
|
||||||
|
networkstatus_get_dl_status_by_flavor_running(FLAV_MICRODESC);
|
||||||
|
} else {
|
||||||
|
*errmsg = "Unknown flavor";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handle the cert download cases for getinfo_helper_downloads() */
|
||||||
|
static void
|
||||||
|
getinfo_helper_downloads_cert(const char *fp_sk_req,
|
||||||
|
download_status_t **dl_to_emit,
|
||||||
|
smartlist_t **digest_list,
|
||||||
|
const char **errmsg)
|
||||||
|
{
|
||||||
|
const char *sk_req;
|
||||||
|
char id_digest[DIGEST_LEN];
|
||||||
|
char sk_digest[DIGEST_LEN];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have to handle four cases; fp_sk_req is the request with
|
||||||
|
* a prefix of "downloads/cert/" snipped off.
|
||||||
|
*
|
||||||
|
* Case 1: fp_sk_req = "fps"
|
||||||
|
* - We should emit a digest_list with a list of all the identity
|
||||||
|
* fingerprints that can be queried for certificate download status;
|
||||||
|
* get it by calling list_authority_ids_with_downloads().
|
||||||
|
*
|
||||||
|
* Case 2: fp_sk_req = "fp/<fp>" for some fingerprint fp
|
||||||
|
* - We want the default certificate for this identity fingerprint's
|
||||||
|
* download status; this is the download we get from URLs starting
|
||||||
|
* in /fp/ on the directory server. We can get it with
|
||||||
|
* id_only_download_status_for_authority_id().
|
||||||
|
*
|
||||||
|
* Case 3: fp_sk_req = "fp/<fp>/sks" for some fingerprint fp
|
||||||
|
* - We want a list of all signing key digests for this identity
|
||||||
|
* fingerprint which can be queried for certificate download status.
|
||||||
|
* Get it with list_sk_digests_for_authority_id().
|
||||||
|
*
|
||||||
|
* Case 4: fp_sk_req = "fp/<fp>/<sk>" for some fingerprint fp and
|
||||||
|
* signing key digest sk
|
||||||
|
* - We want the download status for the certificate for this specific
|
||||||
|
* signing key and fingerprint. These correspond to the ones we get
|
||||||
|
* from URLs starting in /fp-sk/ on the directory server. Get it with
|
||||||
|
* list_sk_digests_for_authority_id().
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (strcmp(fp_sk_req, "fps") == 0) {
|
||||||
|
*digest_list = list_authority_ids_with_downloads();
|
||||||
|
if (!(*digest_list)) {
|
||||||
|
*errmsg = "Failed to get list of authority identity digests (!)";
|
||||||
|
}
|
||||||
|
} else if (!strcmpstart(fp_sk_req, "fp/")) {
|
||||||
|
fp_sk_req += strlen("fp/");
|
||||||
|
/* Okay, look for another / to tell the fp from fp-sk cases */
|
||||||
|
sk_req = strchr(fp_sk_req, '/');
|
||||||
|
if (sk_req) {
|
||||||
|
/* okay, split it here and try to parse <fp> */
|
||||||
|
if (base16_decode(id_digest, DIGEST_LEN,
|
||||||
|
fp_sk_req, sk_req - fp_sk_req) == DIGEST_LEN) {
|
||||||
|
/* Skip past the '/' */
|
||||||
|
++sk_req;
|
||||||
|
if (strcmp(sk_req, "sks") == 0) {
|
||||||
|
/* We're asking for the list of signing key fingerprints */
|
||||||
|
*digest_list = list_sk_digests_for_authority_id(id_digest);
|
||||||
|
if (!(*digest_list)) {
|
||||||
|
*errmsg = "Failed to get list of signing key digests for this "
|
||||||
|
"authority identity digest";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* We've got a signing key digest */
|
||||||
|
if (base16_decode(sk_digest, DIGEST_LEN,
|
||||||
|
sk_req, strlen(sk_req)) == DIGEST_LEN) {
|
||||||
|
*dl_to_emit =
|
||||||
|
download_status_for_authority_id_and_sk(id_digest, sk_digest);
|
||||||
|
if (!(*dl_to_emit)) {
|
||||||
|
*errmsg = "Failed to get download status for this identity/"
|
||||||
|
"signing key digest pair";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*errmsg = "That didn't look like a signing key digest";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*errmsg = "That didn't look like an identity digest";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* We're either in downloads/certs/fp/<fp>, or we can't parse <fp> */
|
||||||
|
if (strlen(fp_sk_req) == HEX_DIGEST_LEN) {
|
||||||
|
if (base16_decode(id_digest, DIGEST_LEN,
|
||||||
|
fp_sk_req, strlen(fp_sk_req)) == DIGEST_LEN) {
|
||||||
|
*dl_to_emit = id_only_download_status_for_authority_id(id_digest);
|
||||||
|
if (!(*dl_to_emit)) {
|
||||||
|
*errmsg = "Failed to get download status for this authority "
|
||||||
|
"identity digest";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*errmsg = "That didn't look like a digest";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*errmsg = "That didn't look like a digest";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*errmsg = "Unknown certificate download status query";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Implementation helper for GETINFO: knows the answers for questions about
|
/** Implementation helper for GETINFO: knows the answers for questions about
|
||||||
* download status information. */
|
* download status information. */
|
||||||
static int
|
static int
|
||||||
@ -2162,8 +2314,8 @@ getinfo_helper_downloads(control_connection_t *control_conn,
|
|||||||
const char *question, char **answer,
|
const char *question, char **answer,
|
||||||
const char **errmsg)
|
const char **errmsg)
|
||||||
{
|
{
|
||||||
const char *flavor;
|
|
||||||
download_status_t *dl_to_emit = NULL;
|
download_status_t *dl_to_emit = NULL;
|
||||||
|
smartlist_t *digest_list = NULL;
|
||||||
|
|
||||||
/* Assert args are sane */
|
/* Assert args are sane */
|
||||||
tor_assert(control_conn != NULL);
|
tor_assert(control_conn != NULL);
|
||||||
@ -2176,33 +2328,26 @@ getinfo_helper_downloads(control_connection_t *control_conn,
|
|||||||
|
|
||||||
/* Are we after networkstatus downloads? */
|
/* Are we after networkstatus downloads? */
|
||||||
if (!strcmpstart(question, "downloads/networkstatus/")) {
|
if (!strcmpstart(question, "downloads/networkstatus/")) {
|
||||||
flavor = question + strlen("downloads/networkstatus/");
|
getinfo_helper_downloads_networkstatus(
|
||||||
/*
|
question + strlen("downloads/networkstatus/"),
|
||||||
* We get the one for the current bootstrapped status by default, or
|
&dl_to_emit, errmsg);
|
||||||
* take an extra /bootstrap or /running suffix
|
} else if (!strcmpstart(question, "downloads/cert/")) {
|
||||||
*/
|
getinfo_helper_downloads_cert(
|
||||||
if (strcmp(flavor, "ns") == 0) {
|
question + strlen("downloads/cert/"),
|
||||||
dl_to_emit = networkstatus_get_dl_status_by_flavor(FLAV_NS);
|
&dl_to_emit, &digest_list, errmsg);
|
||||||
} else if (strcmp(flavor, "ns/bootstrap") == 0) {
|
} else {
|
||||||
dl_to_emit = networkstatus_get_dl_status_by_flavor_bootstrap(FLAV_NS);
|
*errmsg = "Unknown download status query";
|
||||||
} else if (strcmp(flavor, "ns/running") == 0 ) {
|
|
||||||
dl_to_emit = networkstatus_get_dl_status_by_flavor_running(FLAV_NS);
|
|
||||||
} else if (strcmp(flavor, "microdesc") == 0) {
|
|
||||||
dl_to_emit = networkstatus_get_dl_status_by_flavor(FLAV_MICRODESC);
|
|
||||||
} else if (strcmp(flavor, "microdesc/bootstrap") == 0) {
|
|
||||||
dl_to_emit =
|
|
||||||
networkstatus_get_dl_status_by_flavor_bootstrap(FLAV_MICRODESC);
|
|
||||||
} else if (strcmp(flavor, "microdesc/running") == 0) {
|
|
||||||
dl_to_emit =
|
|
||||||
networkstatus_get_dl_status_by_flavor_running(FLAV_MICRODESC);
|
|
||||||
} else {
|
|
||||||
*errmsg = "Unknown flavor";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dl_to_emit) {
|
if (dl_to_emit) {
|
||||||
*answer = download_status_to_string(dl_to_emit);
|
*answer = download_status_to_string(dl_to_emit);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else if (digest_list) {
|
||||||
|
*answer = digest_list_to_string(digest_list);
|
||||||
|
SMARTLIST_FOREACH(digest_list, void *, s, tor_free(s));
|
||||||
|
smartlist_free(digest_list);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
if (!(*errmsg)) {
|
if (!(*errmsg)) {
|
||||||
@ -2666,6 +2811,21 @@ static const getinfo_item_t getinfo_items[] = {
|
|||||||
"Download status for bootstrap-time microdesc download"),
|
"Download status for bootstrap-time microdesc download"),
|
||||||
DOC("downloads/networkstatus/microdesc/running",
|
DOC("downloads/networkstatus/microdesc/running",
|
||||||
"Download status for run-time microdesc download"),
|
"Download status for run-time microdesc download"),
|
||||||
|
PREFIX("downloads/cert/", downloads,
|
||||||
|
"Download statuses for certificates, by id fingerprint and "
|
||||||
|
"signing key"),
|
||||||
|
DOC("downloads/cert/fps",
|
||||||
|
"List of authority fingerprints for which any download statuses "
|
||||||
|
"exist"),
|
||||||
|
DOC("downloads/cert/fp/<fp>",
|
||||||
|
"Download status for <fp> with the default signing key; corresponds "
|
||||||
|
"to /fp/ URLs on directory server."),
|
||||||
|
DOC("downloads/cert/fp/<fp>/sks",
|
||||||
|
"List of signing keys for which specific download statuses are "
|
||||||
|
"available for this id fingerprint"),
|
||||||
|
DOC("downloads/cert/fp/<fp>/<sk>",
|
||||||
|
"Download status for <fp> with signing key <sk>; corresponds "
|
||||||
|
"to /fp-sk/ URLs on directory server."),
|
||||||
ITEM("info/names", misc,
|
ITEM("info/names", misc,
|
||||||
"List of GETINFO options, types, and documentation."),
|
"List of GETINFO options, types, and documentation."),
|
||||||
ITEM("events/names", misc,
|
ITEM("events/names", misc,
|
||||||
|
@ -253,6 +253,112 @@ get_cert_list(const char *id_digest)
|
|||||||
return cl;
|
return cl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return a list of authority ID digests with potentially enumerable lists
|
||||||
|
* of download_status_t objects; used by controller GETINFO queries.
|
||||||
|
*/
|
||||||
|
|
||||||
|
smartlist_t *
|
||||||
|
list_authority_ids_with_downloads(void)
|
||||||
|
{
|
||||||
|
smartlist_t *ids = smartlist_new();
|
||||||
|
digestmap_iter_t *i;
|
||||||
|
const char *digest;
|
||||||
|
char *tmp;
|
||||||
|
void *cl;
|
||||||
|
|
||||||
|
if (trusted_dir_certs) {
|
||||||
|
for (i = digestmap_iter_init(trusted_dir_certs);
|
||||||
|
!(digestmap_iter_done(i));
|
||||||
|
i = digestmap_iter_next(trusted_dir_certs, i)) {
|
||||||
|
/*
|
||||||
|
* We always have at least dl_status_by_id to query, so no need to
|
||||||
|
* probe deeper than the existence of a cert_list_t.
|
||||||
|
*/
|
||||||
|
digestmap_iter_get(i, &digest, &cl);
|
||||||
|
tmp = tor_malloc(DIGEST_LEN);
|
||||||
|
memcpy(tmp, digest, DIGEST_LEN);
|
||||||
|
smartlist_add(ids, tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* else definitely no downlaods going since nothing even has a cert list */
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Given an authority ID digest, return a pointer to the default download
|
||||||
|
* status, or NULL if there is no such entry in trusted_dir_certs */
|
||||||
|
|
||||||
|
download_status_t *
|
||||||
|
id_only_download_status_for_authority_id(const char *digest)
|
||||||
|
{
|
||||||
|
download_status_t *dl = NULL;
|
||||||
|
cert_list_t *cl;
|
||||||
|
|
||||||
|
if (trusted_dir_certs) {
|
||||||
|
cl = digestmap_get(trusted_dir_certs, digest);
|
||||||
|
if (cl) {
|
||||||
|
dl = &(cl->dl_status_by_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Given an authority ID digest, return a smartlist of signing key digests
|
||||||
|
* for which download_status_t is potentially queryable, or NULL if no such
|
||||||
|
* authority ID digest is known. */
|
||||||
|
|
||||||
|
smartlist_t *
|
||||||
|
list_sk_digests_for_authority_id(const char *digest)
|
||||||
|
{
|
||||||
|
smartlist_t *sks = NULL;
|
||||||
|
cert_list_t *cl;
|
||||||
|
dsmap_iter_t *i;
|
||||||
|
const char *sk_digest;
|
||||||
|
char *tmp;
|
||||||
|
download_status_t *dl;
|
||||||
|
|
||||||
|
if (trusted_dir_certs) {
|
||||||
|
cl = digestmap_get(trusted_dir_certs, digest);
|
||||||
|
if (cl) {
|
||||||
|
sks = smartlist_new();
|
||||||
|
if (cl->dl_status_map) {
|
||||||
|
for (i = dsmap_iter_init(cl->dl_status_map);
|
||||||
|
!(dsmap_iter_done(i));
|
||||||
|
i = dsmap_iter_next(cl->dl_status_map, i)) {
|
||||||
|
/* Pull the digest out and add it to the list */
|
||||||
|
dsmap_iter_get(i, &sk_digest, &dl);
|
||||||
|
tmp = tor_malloc(DIGEST_LEN);
|
||||||
|
memcpy(tmp, sk_digest, DIGEST_LEN);
|
||||||
|
smartlist_add(sks, tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Given an authority ID digest and a signing key digest, return the
|
||||||
|
* download_status_t or NULL if none exists. */
|
||||||
|
|
||||||
|
download_status_t *
|
||||||
|
download_status_for_authority_id_and_sk(const char *id_digest,
|
||||||
|
const char *sk_digest)
|
||||||
|
{
|
||||||
|
download_status_t *dl = NULL;
|
||||||
|
cert_list_t *cl = NULL;
|
||||||
|
|
||||||
|
if (trusted_dir_certs) {
|
||||||
|
cl = digestmap_get(trusted_dir_certs, id_digest);
|
||||||
|
if (cl && cl->dl_status_map) {
|
||||||
|
dl = dsmap_get(cl->dl_status_map, sk_digest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dl;
|
||||||
|
}
|
||||||
|
|
||||||
/** Release all space held by a cert_list_t */
|
/** Release all space held by a cert_list_t */
|
||||||
static void
|
static void
|
||||||
cert_list_free(cert_list_t *cl)
|
cert_list_free(cert_list_t *cl)
|
||||||
|
@ -104,6 +104,14 @@ void routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int make_old,
|
|||||||
void routerlist_free_all(void);
|
void routerlist_free_all(void);
|
||||||
void routerlist_reset_warnings(void);
|
void routerlist_reset_warnings(void);
|
||||||
|
|
||||||
|
smartlist_t * list_authority_ids_with_downloads(void);
|
||||||
|
download_status_t * id_only_download_status_for_authority_id(
|
||||||
|
const char *digest);
|
||||||
|
smartlist_t * list_sk_digests_for_authority_id(const char *digest);
|
||||||
|
download_status_t * download_status_for_authority_id_and_sk(
|
||||||
|
const char *id_digest,
|
||||||
|
const char *sk_digest);
|
||||||
|
|
||||||
static int WRA_WAS_ADDED(was_router_added_t s);
|
static int WRA_WAS_ADDED(was_router_added_t s);
|
||||||
static int WRA_WAS_OUTDATED(was_router_added_t s);
|
static int WRA_WAS_OUTDATED(was_router_added_t s);
|
||||||
static int WRA_WAS_REJECTED(was_router_added_t s);
|
static int WRA_WAS_REJECTED(was_router_added_t s);
|
||||||
|
Loading…
Reference in New Issue
Block a user