mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
Merge branch 'tor-github/pr/1423'
This commit is contained in:
@@ -1411,6 +1411,44 @@ The following options are useful only for clients (that is, if
|
||||
one. You can disable this behavior, so that Tor will select "No
|
||||
authentication" when IsolateSOCKSAuth is disabled, or when this
|
||||
option is set.
|
||||
**ExtendedErrors**;;
|
||||
Return extended error code in the SOCKS reply. So far, the possible
|
||||
errors are:
|
||||
|
||||
X'F0' Onion Service Descriptor Can Not be Found
|
||||
|
||||
The requested onion service descriptor can't be found on the
|
||||
hashring and thus not reachable by the client.
|
||||
|
||||
X'F1' Onion Service Descriptor Is Invalid
|
||||
|
||||
The requested onion service descriptor can't be parsed or
|
||||
signature validation failed.
|
||||
|
||||
X'F2' Onion Service Introduction Failed
|
||||
|
||||
Client failed to introduce to the service meaning the descriptor
|
||||
was found but the service is not connected anymore to the
|
||||
introduction point. The service has likely changed its descriptor
|
||||
or is not running.
|
||||
|
||||
X'F3' Onion Service Rendezvous Failed
|
||||
|
||||
Client failed to rendezvous with the service which means that the
|
||||
client is unable to finalize the connection.
|
||||
|
||||
X'F4' Onion Service Missing Client Authorization
|
||||
|
||||
Client was able to download the requested onion service descriptor
|
||||
but is unable to decrypt its content because it is missing client
|
||||
authorization information.
|
||||
|
||||
X'F5' Onion Service Wrong Client Authorization
|
||||
|
||||
Client was able to download the requested onion service descriptor
|
||||
but is unable to decrypt its content using the client
|
||||
authorization information it has. This means the client access
|
||||
were revoked.
|
||||
|
||||
// Anchor only for formatting, not visible in the man page.
|
||||
[[SocksPortFlagsMisc]]::
|
||||
|
||||
@@ -5829,7 +5829,7 @@ port_parse_config(smartlist_t *out,
|
||||
cache_ipv6 = 0, use_cached_ipv6 = 0,
|
||||
prefer_ipv6_automap = 1, world_writable = 0, group_writable = 0,
|
||||
relax_dirmode_check = 0,
|
||||
has_used_unix_socket_only_option = 0;
|
||||
has_used_unix_socket_only_option = 0, extended_errors = 0;
|
||||
|
||||
int is_unix_tagged_addr = 0;
|
||||
const char *rest_of_line = NULL;
|
||||
@@ -6068,6 +6068,9 @@ port_parse_config(smartlist_t *out,
|
||||
} else if (!strcasecmp(elt, "KeepAliveIsolateSOCKSAuth")) {
|
||||
socks_iso_keep_alive = ! no;
|
||||
continue;
|
||||
} else if (!strcasecmp(elt, "ExtendedErrors")) {
|
||||
extended_errors = ! no;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcasecmpend(elt, "s"))
|
||||
@@ -6180,6 +6183,7 @@ port_parse_config(smartlist_t *out,
|
||||
if (! (isolation & ISO_SOCKSAUTH))
|
||||
cfg->entry_cfg.socks_prefer_no_auth = 1;
|
||||
cfg->entry_cfg.socks_iso_keep_alive = socks_iso_keep_alive;
|
||||
cfg->entry_cfg.extended_socks5_codes = extended_errors;
|
||||
|
||||
smartlist_add(out, cfg);
|
||||
}
|
||||
|
||||
@@ -1905,6 +1905,8 @@ connection_init_accepted_conn(connection_t *conn,
|
||||
conn->state = AP_CONN_STATE_SOCKS_WAIT;
|
||||
TO_ENTRY_CONN(conn)->socks_request->socks_prefer_no_auth =
|
||||
listener->entry_cfg.socks_prefer_no_auth;
|
||||
TO_ENTRY_CONN(conn)->socks_request->socks_use_extended_errors =
|
||||
listener->entry_cfg.extended_socks5_codes;
|
||||
break;
|
||||
case CONN_TYPE_AP_TRANS_LISTENER:
|
||||
TO_ENTRY_CONN(conn)->is_transparent_ap = 1;
|
||||
|
||||
+109
-70
@@ -1553,6 +1553,102 @@ consider_plaintext_ports(entry_connection_t *conn, uint16_t port)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Parse the given hostname in address. Returns true if the parsing was
|
||||
* successful and type_out contains the type of the hostname. Else, false is
|
||||
* returned which means it was not recognized and type_out is set to
|
||||
* BAD_HOSTNAME.
|
||||
*
|
||||
* The possible recognized forms are (where true is returned):
|
||||
*
|
||||
* If address is of the form "y.onion" with a well-formed handle y:
|
||||
* Put a NUL after y, lower-case it, and return ONION_V2_HOSTNAME or
|
||||
* ONION_V3_HOSTNAME depending on the HS version.
|
||||
*
|
||||
* If address is of the form "x.y.onion" with a well-formed handle x:
|
||||
* Drop "x.", put a NUL after y, lower-case it, and return
|
||||
* ONION_V2_HOSTNAME or ONION_V3_HOSTNAME depending on the HS version.
|
||||
*
|
||||
* If address is of the form "y.onion" with a badly-formed handle y:
|
||||
* Return BAD_HOSTNAME and log a message.
|
||||
*
|
||||
* If address is of the form "y.exit":
|
||||
* Put a NUL after y and return EXIT_HOSTNAME.
|
||||
*
|
||||
* Otherwise:
|
||||
* Return NORMAL_HOSTNAME and change nothing.
|
||||
*/
|
||||
STATIC bool
|
||||
parse_extended_hostname(char *address, hostname_type_t *type_out)
|
||||
{
|
||||
char *s;
|
||||
char *q;
|
||||
char query[HS_SERVICE_ADDR_LEN_BASE32+1];
|
||||
|
||||
s = strrchr(address,'.');
|
||||
if (!s) {
|
||||
*type_out = NORMAL_HOSTNAME; /* no dot, thus normal */
|
||||
goto success;
|
||||
}
|
||||
if (!strcmp(s+1,"exit")) {
|
||||
*s = 0; /* NUL-terminate it */
|
||||
*type_out = EXIT_HOSTNAME; /* .exit */
|
||||
goto success;
|
||||
}
|
||||
if (strcmp(s+1,"onion")) {
|
||||
*type_out = NORMAL_HOSTNAME; /* neither .exit nor .onion, thus normal */
|
||||
goto success;
|
||||
}
|
||||
|
||||
/* so it is .onion */
|
||||
*s = 0; /* NUL-terminate it */
|
||||
/* locate a 'sub-domain' component, in order to remove it */
|
||||
q = strrchr(address, '.');
|
||||
if (q == address) {
|
||||
*type_out = BAD_HOSTNAME;
|
||||
goto failed; /* reject sub-domain, as DNS does */
|
||||
}
|
||||
q = (NULL == q) ? address : q + 1;
|
||||
if (strlcpy(query, q, HS_SERVICE_ADDR_LEN_BASE32+1) >=
|
||||
HS_SERVICE_ADDR_LEN_BASE32+1) {
|
||||
*type_out = BAD_HOSTNAME;
|
||||
goto failed;
|
||||
}
|
||||
if (q != address) {
|
||||
memmove(address, q, strlen(q) + 1 /* also get \0 */);
|
||||
}
|
||||
/* v2 onion address check. */
|
||||
if (strlen(query) == REND_SERVICE_ID_LEN_BASE32) {
|
||||
*type_out = ONION_V2_HOSTNAME;
|
||||
if (rend_valid_v2_service_id(query)) {
|
||||
goto success;
|
||||
}
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* v3 onion address check. */
|
||||
if (strlen(query) == HS_SERVICE_ADDR_LEN_BASE32) {
|
||||
*type_out = ONION_V3_HOSTNAME;
|
||||
if (hs_address_is_valid(query)) {
|
||||
goto success;
|
||||
}
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Reaching this point, nothing was recognized. */
|
||||
*type_out = BAD_HOSTNAME;
|
||||
goto failed;
|
||||
|
||||
success:
|
||||
return true;
|
||||
failed:
|
||||
/* otherwise, return to previous state and return 0 */
|
||||
*s = '.';
|
||||
log_warn(LD_APP, "Invalid %shostname %s; rejecting",
|
||||
(*type_out == (ONION_V2_HOSTNAME || ONION_V3_HOSTNAME) ? "onion " : ""),
|
||||
safe_str_client(address));
|
||||
return false;
|
||||
}
|
||||
|
||||
/** How many times do we try connecting with an exit configured via
|
||||
* TrackHostExits before concluding that it won't work any more and trying a
|
||||
* different one? */
|
||||
@@ -2020,16 +2116,15 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
|
||||
const int automap = rr.automap;
|
||||
const addressmap_entry_source_t exit_source = rr.exit_source;
|
||||
|
||||
/* Now, we parse the address to see if it's an .onion or .exit or
|
||||
* other special address.
|
||||
*/
|
||||
const hostname_type_t addresstype = parse_extended_hostname(socks->address);
|
||||
|
||||
/* Now see whether the hostname is bogus. This could happen because of an
|
||||
* onion hostname whose format we don't recognize. */
|
||||
if (addresstype == BAD_HOSTNAME) {
|
||||
hostname_type_t addresstype;
|
||||
if (!parse_extended_hostname(socks->address, &addresstype)) {
|
||||
control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s",
|
||||
escaped(socks->address));
|
||||
if (addresstype == ONION_V3_HOSTNAME) {
|
||||
conn->socks_request->socks_extended_error_code = SOCKS5_HS_IS_INVALID;
|
||||
}
|
||||
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
|
||||
return -1;
|
||||
}
|
||||
@@ -3522,11 +3617,17 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
|
||||
size_t replylen, int endreason)
|
||||
{
|
||||
char buf[256];
|
||||
socks5_reply_status_t status =
|
||||
stream_end_reason_to_socks5_response(endreason);
|
||||
socks5_reply_status_t status;
|
||||
|
||||
tor_assert(conn->socks_request); /* make sure it's an AP stream */
|
||||
|
||||
if (conn->socks_request->socks_use_extended_errors &&
|
||||
conn->socks_request->socks_extended_error_code != 0) {
|
||||
status = conn->socks_request->socks_extended_error_code;
|
||||
} else {
|
||||
status = stream_end_reason_to_socks5_response(endreason);
|
||||
}
|
||||
|
||||
if (!SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) {
|
||||
control_event_stream_status(conn, status==SOCKS5_SUCCEEDED ?
|
||||
STREAM_EVENT_SUCCEEDED : STREAM_EVENT_FAILED,
|
||||
@@ -4306,68 +4407,6 @@ connection_ap_can_use_exit(const entry_connection_t *conn,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** If address is of the form "y.onion" with a well-formed handle y:
|
||||
* Put a NUL after y, lower-case it, and return ONION_V2_HOSTNAME or
|
||||
* ONION_V3_HOSTNAME depending on the HS version.
|
||||
*
|
||||
* If address is of the form "x.y.onion" with a well-formed handle x:
|
||||
* Drop "x.", put a NUL after y, lower-case it, and return
|
||||
* ONION_V2_HOSTNAME or ONION_V3_HOSTNAME depending on the HS version.
|
||||
*
|
||||
* If address is of the form "y.onion" with a badly-formed handle y:
|
||||
* Return BAD_HOSTNAME and log a message.
|
||||
*
|
||||
* If address is of the form "y.exit":
|
||||
* Put a NUL after y and return EXIT_HOSTNAME.
|
||||
*
|
||||
* Otherwise:
|
||||
* Return NORMAL_HOSTNAME and change nothing.
|
||||
*/
|
||||
hostname_type_t
|
||||
parse_extended_hostname(char *address)
|
||||
{
|
||||
char *s;
|
||||
char *q;
|
||||
char query[HS_SERVICE_ADDR_LEN_BASE32+1];
|
||||
|
||||
s = strrchr(address,'.');
|
||||
if (!s)
|
||||
return NORMAL_HOSTNAME; /* no dot, thus normal */
|
||||
if (!strcmp(s+1,"exit")) {
|
||||
*s = 0; /* NUL-terminate it */
|
||||
return EXIT_HOSTNAME; /* .exit */
|
||||
}
|
||||
if (strcmp(s+1,"onion"))
|
||||
return NORMAL_HOSTNAME; /* neither .exit nor .onion, thus normal */
|
||||
|
||||
/* so it is .onion */
|
||||
*s = 0; /* NUL-terminate it */
|
||||
/* locate a 'sub-domain' component, in order to remove it */
|
||||
q = strrchr(address, '.');
|
||||
if (q == address) {
|
||||
goto failed; /* reject sub-domain, as DNS does */
|
||||
}
|
||||
q = (NULL == q) ? address : q + 1;
|
||||
if (strlcpy(query, q, HS_SERVICE_ADDR_LEN_BASE32+1) >=
|
||||
HS_SERVICE_ADDR_LEN_BASE32+1)
|
||||
goto failed;
|
||||
if (q != address) {
|
||||
memmove(address, q, strlen(q) + 1 /* also get \0 */);
|
||||
}
|
||||
if (rend_valid_v2_service_id(query)) {
|
||||
return ONION_V2_HOSTNAME; /* success */
|
||||
}
|
||||
if (hs_address_is_valid(query)) {
|
||||
return ONION_V3_HOSTNAME;
|
||||
}
|
||||
failed:
|
||||
/* otherwise, return to previous state and return 0 */
|
||||
*s = '.';
|
||||
log_warn(LD_APP, "Invalid onion hostname %s; rejecting",
|
||||
safe_str_client(address));
|
||||
return BAD_HOSTNAME;
|
||||
}
|
||||
|
||||
/** Return true iff the (possibly NULL) <b>alen</b>-byte chunk of memory at
|
||||
* <b>a</b> is equal to the (possibly NULL) <b>blen</b>-byte chunk of memory
|
||||
* at <b>b</b>. */
|
||||
|
||||
@@ -71,6 +71,15 @@ entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *);
|
||||
#define connection_mark_unattached_ap(conn, endreason) \
|
||||
connection_mark_unattached_ap_((conn), (endreason), __LINE__, SHORT_FILE__)
|
||||
|
||||
/** Possible return values for parse_extended_hostname. */
|
||||
typedef enum hostname_type_t {
|
||||
BAD_HOSTNAME,
|
||||
EXIT_HOSTNAME,
|
||||
NORMAL_HOSTNAME,
|
||||
ONION_V2_HOSTNAME,
|
||||
ONION_V3_HOSTNAME,
|
||||
} hostname_type_t;
|
||||
|
||||
MOCK_DECL(void,connection_mark_unattached_ap_,
|
||||
(entry_connection_t *conn, int endreason,
|
||||
int line, const char *file));
|
||||
@@ -155,13 +164,6 @@ int connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
|
||||
origin_circuit_t *circ,
|
||||
crypt_path_t *cpath);
|
||||
|
||||
/** Possible return values for parse_extended_hostname. */
|
||||
typedef enum hostname_type_t {
|
||||
NORMAL_HOSTNAME, ONION_V2_HOSTNAME, ONION_V3_HOSTNAME,
|
||||
EXIT_HOSTNAME, BAD_HOSTNAME
|
||||
} hostname_type_t;
|
||||
hostname_type_t parse_extended_hostname(char *address);
|
||||
|
||||
#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H)
|
||||
int get_pf_socket(void);
|
||||
#endif
|
||||
@@ -219,6 +221,8 @@ void half_edge_free_(struct half_edge_t *he);
|
||||
|
||||
#ifdef CONNECTION_EDGE_PRIVATE
|
||||
|
||||
STATIC bool parse_extended_hostname(char *address, hostname_type_t *type_out);
|
||||
|
||||
/** A parsed BEGIN or BEGIN_DIR cell */
|
||||
typedef struct begin_cell_t {
|
||||
/** The address the client has asked us to connect to, or NULL if this is
|
||||
|
||||
@@ -53,6 +53,9 @@ struct entry_port_cfg_t {
|
||||
* do we prefer IPv6? */
|
||||
unsigned int prefer_ipv6_virtaddr : 1;
|
||||
|
||||
/** For socks listeners: can we send back the extended SOCKS5 error code? */
|
||||
unsigned int extended_socks5_codes : 1;
|
||||
|
||||
};
|
||||
|
||||
#endif /* !defined(ENTRY_PORT_CFG_ST_H) */
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#ifndef SOCKS_REQUEST_ST_H
|
||||
#define SOCKS_REQUEST_ST_H
|
||||
|
||||
#include "lib/net/socks5_status.h"
|
||||
|
||||
#define MAX_SOCKS_REPLY_LEN 1024
|
||||
|
||||
#define SOCKS_NO_AUTH 0x00
|
||||
@@ -63,6 +65,11 @@ struct socks_request_t {
|
||||
* "username/password" authentication if both are offered. Used as input to
|
||||
* parse_socks. */
|
||||
unsigned int socks_prefer_no_auth : 1;
|
||||
/** If set, we can send back the extended error code in the reply. */
|
||||
unsigned int socks_use_extended_errors : 1;
|
||||
/** If non zero, this contains the extended error code that should be used
|
||||
* if the port was configured to use them. */
|
||||
socks5_reply_status_t socks_extended_error_code;
|
||||
|
||||
/** Number of bytes in username; 0 if username is NULL */
|
||||
size_t usernamelen;
|
||||
|
||||
@@ -2733,62 +2733,7 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
|
||||
log_info(LD_REND,"Received v3 hsdesc (body size %d, status %d (%s))",
|
||||
(int)body_len, status_code, escaped(reason));
|
||||
|
||||
switch (status_code) {
|
||||
case 200:
|
||||
/* We got something: Try storing it in the cache. */
|
||||
if (hs_cache_store_as_client(body, &conn->hs_ident->identity_pk) < 0) {
|
||||
log_info(LD_REND, "Failed to store hidden service descriptor");
|
||||
/* Fire control port FAILED event. */
|
||||
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
|
||||
"BAD_DESC");
|
||||
hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
|
||||
NULL);
|
||||
} else {
|
||||
log_info(LD_REND, "Stored hidden service descriptor successfully.");
|
||||
TO_CONN(conn)->purpose = DIR_PURPOSE_HAS_FETCHED_HSDESC;
|
||||
hs_client_desc_has_arrived(conn->hs_ident);
|
||||
/* Fire control port RECEIVED event. */
|
||||
hs_control_desc_event_received(conn->hs_ident, conn->identity_digest);
|
||||
hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
|
||||
body);
|
||||
}
|
||||
break;
|
||||
case 404:
|
||||
/* Not there. We'll retry when connection_about_to_close_connection()
|
||||
* tries to clean this conn up. */
|
||||
log_info(LD_REND, "Fetching hidden service v3 descriptor not found: "
|
||||
"Retrying at another directory.");
|
||||
/* Fire control port FAILED event. */
|
||||
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
|
||||
"NOT_FOUND");
|
||||
hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
|
||||
NULL);
|
||||
break;
|
||||
case 400:
|
||||
log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
|
||||
"http status 400 (%s). Dirserver didn't like our "
|
||||
"query? Retrying at another directory.",
|
||||
escaped(reason));
|
||||
/* Fire control port FAILED event. */
|
||||
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
|
||||
"QUERY_REJECTED");
|
||||
hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
|
||||
NULL);
|
||||
break;
|
||||
default:
|
||||
log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
|
||||
"http status %d (%s) response unexpected from HSDir server "
|
||||
"'%s:%d'. Retrying at another directory.",
|
||||
status_code, escaped(reason), TO_CONN(conn)->address,
|
||||
TO_CONN(conn)->port);
|
||||
/* Fire control port FAILED event. */
|
||||
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
|
||||
"UNEXPECTED");
|
||||
hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
|
||||
NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
hs_client_dir_fetch_done(conn, reason, body, status_code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+88
-13
@@ -395,8 +395,10 @@ lookup_v3_desc_as_client(const uint8_t *key)
|
||||
* hs_cache_client_descriptor_t object. In case of error, return NULL. */
|
||||
static hs_cache_client_descriptor_t *
|
||||
cache_client_desc_new(const char *desc_str,
|
||||
const ed25519_public_key_t *service_identity_pk)
|
||||
const ed25519_public_key_t *service_identity_pk,
|
||||
hs_desc_decode_status_t *decode_status_out)
|
||||
{
|
||||
hs_desc_decode_status_t ret;
|
||||
hs_descriptor_t *desc = NULL;
|
||||
hs_cache_client_descriptor_t *client_desc = NULL;
|
||||
|
||||
@@ -404,10 +406,24 @@ cache_client_desc_new(const char *desc_str,
|
||||
tor_assert(service_identity_pk);
|
||||
|
||||
/* Decode the descriptor we just fetched. */
|
||||
if (hs_client_decode_descriptor(desc_str, service_identity_pk, &desc) < 0) {
|
||||
ret = hs_client_decode_descriptor(desc_str, service_identity_pk, &desc);
|
||||
if (ret != HS_DESC_DECODE_OK &&
|
||||
ret != HS_DESC_DECODE_NEED_CLIENT_AUTH &&
|
||||
ret != HS_DESC_DECODE_BAD_CLIENT_AUTH) {
|
||||
/* In the case of a missing or bad client authorization, we'll keep the
|
||||
* descriptor in the cache because those credentials can arrive later. */
|
||||
goto end;
|
||||
}
|
||||
tor_assert(desc);
|
||||
/* Make sure we do have a descriptor if decoding was successful. */
|
||||
if (ret == HS_DESC_DECODE_OK) {
|
||||
tor_assert(desc);
|
||||
} else {
|
||||
if (BUG(desc != NULL)) {
|
||||
/* We are not suppose to have a descriptor if the decoding code is not
|
||||
* indicating success. Just in case, bail early to recover. */
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* All is good: make a cache object for this descriptor */
|
||||
client_desc = tor_malloc_zero(sizeof(hs_cache_client_descriptor_t));
|
||||
@@ -420,6 +436,9 @@ cache_client_desc_new(const char *desc_str,
|
||||
client_desc->encoded_desc = tor_strdup(desc_str);
|
||||
|
||||
end:
|
||||
if (decode_status_out) {
|
||||
*decode_status_out = ret;
|
||||
}
|
||||
return client_desc;
|
||||
}
|
||||
|
||||
@@ -635,9 +654,19 @@ cache_store_as_client(hs_cache_client_descriptor_t *client_desc)
|
||||
tor_assert(client_desc);
|
||||
|
||||
/* Check if we already have a descriptor from this HS in cache. If we do,
|
||||
* check if this descriptor is newer than the cached one */
|
||||
* check if this descriptor is newer than the cached one only if we have a
|
||||
* decoded descriptor. We do keep non-decoded descriptor that requires
|
||||
* client authorization. */
|
||||
cache_entry = lookup_v3_desc_as_client(client_desc->key.pubkey);
|
||||
if (cache_entry != NULL) {
|
||||
/* Signalling an undecrypted descriptor. We'll always replace the one we
|
||||
* have with the new one just fetched. */
|
||||
if (cache_entry->desc == NULL) {
|
||||
remove_v3_desc_as_client(cache_entry);
|
||||
cache_client_desc_free(cache_entry);
|
||||
goto store;
|
||||
}
|
||||
|
||||
/* If we have an entry in our cache that has a revision counter greater
|
||||
* than the one we just fetched, discard the one we fetched. */
|
||||
if (cache_entry->desc->plaintext_data.revision_counter >
|
||||
@@ -657,6 +686,7 @@ cache_store_as_client(hs_cache_client_descriptor_t *client_desc)
|
||||
cache_client_desc_free(cache_entry);
|
||||
}
|
||||
|
||||
store:
|
||||
/* Store descriptor in cache */
|
||||
store_v3_desc_as_client(client_desc);
|
||||
|
||||
@@ -752,7 +782,9 @@ hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key)
|
||||
}
|
||||
|
||||
/** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
|
||||
* its HS descriptor if it's stored in our cache, or NULL if not. */
|
||||
* its HS descriptor if it's stored in our cache, or NULL if not or if the
|
||||
* descriptor was never decrypted. The later can happen if we are waiting for
|
||||
* client authorization to be added. */
|
||||
const hs_descriptor_t *
|
||||
hs_cache_lookup_as_client(const ed25519_public_key_t *key)
|
||||
{
|
||||
@@ -761,27 +793,41 @@ hs_cache_lookup_as_client(const ed25519_public_key_t *key)
|
||||
tor_assert(key);
|
||||
|
||||
cached_desc = lookup_v3_desc_as_client(key->pubkey);
|
||||
if (cached_desc) {
|
||||
tor_assert(cached_desc->desc);
|
||||
if (cached_desc && cached_desc->desc) {
|
||||
return cached_desc->desc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Public API: Given an encoded descriptor, store it in the client HS
|
||||
* cache. Return -1 on error, 0 on success .*/
|
||||
int
|
||||
/** Public API: Given an encoded descriptor, store it in the client HS cache.
|
||||
* Return a decode status which changes how we handle the SOCKS connection
|
||||
* depending on its value:
|
||||
*
|
||||
* HS_DESC_DECODE_OK: Returned on success. Descriptor was properly decoded
|
||||
* and is now stored.
|
||||
*
|
||||
* HS_DESC_DECODE_NEED_CLIENT_AUTH: Client authorization is needed but the
|
||||
* descriptor was still stored.
|
||||
*
|
||||
* HS_DESC_DECODE_BAD_CLIENT_AUTH: Client authorization for this descriptor
|
||||
* was not usable but the descriptor was
|
||||
* still stored.
|
||||
*
|
||||
* Any other codes means indicate where the error occured and the descriptor
|
||||
* was not stored. */
|
||||
hs_desc_decode_status_t
|
||||
hs_cache_store_as_client(const char *desc_str,
|
||||
const ed25519_public_key_t *identity_pk)
|
||||
{
|
||||
hs_desc_decode_status_t ret;
|
||||
hs_cache_client_descriptor_t *client_desc = NULL;
|
||||
|
||||
tor_assert(desc_str);
|
||||
tor_assert(identity_pk);
|
||||
|
||||
/* Create client cache descriptor object */
|
||||
client_desc = cache_client_desc_new(desc_str, identity_pk);
|
||||
client_desc = cache_client_desc_new(desc_str, identity_pk, &ret);
|
||||
if (!client_desc) {
|
||||
log_warn(LD_GENERAL, "HSDesc parsing failed!");
|
||||
log_debug(LD_GENERAL, "Failed to parse HSDesc: %s.", escaped(desc_str));
|
||||
@@ -790,14 +836,15 @@ hs_cache_store_as_client(const char *desc_str,
|
||||
|
||||
/* Push it to the cache */
|
||||
if (cache_store_as_client(client_desc) < 0) {
|
||||
ret = HS_DESC_DECODE_GENERIC_ERROR;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
err:
|
||||
cache_client_desc_free(client_desc);
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Clean all client caches using the current time now. */
|
||||
@@ -895,6 +942,34 @@ hs_cache_client_intro_state_purge(void)
|
||||
"cache purged.");
|
||||
}
|
||||
|
||||
/* This is called when new client authorization was added to the global state.
|
||||
* It attemps to decode the descriptor of the given service identity key.
|
||||
*
|
||||
* Return true if decoding was successful else false. */
|
||||
bool
|
||||
hs_cache_client_new_auth_parse(const ed25519_public_key_t *service_pk)
|
||||
{
|
||||
bool ret = false;
|
||||
hs_cache_client_descriptor_t *cached_desc = NULL;
|
||||
|
||||
tor_assert(service_pk);
|
||||
|
||||
cached_desc = lookup_v3_desc_as_client(service_pk->pubkey);
|
||||
if (cached_desc == NULL || cached_desc->desc != NULL) {
|
||||
/* No entry for that service or the descriptor is already decoded. */
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Attempt a decode. If we are successful, inform the caller. */
|
||||
if (hs_client_decode_descriptor(cached_desc->encoded_desc, service_pk,
|
||||
&cached_desc->desc) == HS_DESC_DECODE_OK) {
|
||||
ret = true;
|
||||
}
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**************** Generics *********************************/
|
||||
|
||||
/** Do a round of OOM cleanup on all directory caches. Return the amount of
|
||||
|
||||
@@ -83,8 +83,8 @@ const hs_descriptor_t *
|
||||
hs_cache_lookup_as_client(const struct ed25519_public_key_t *key);
|
||||
const char *
|
||||
hs_cache_lookup_encoded_as_client(const struct ed25519_public_key_t *key);
|
||||
int hs_cache_store_as_client(const char *desc_str,
|
||||
const struct ed25519_public_key_t *identity_pk);
|
||||
hs_desc_decode_status_t hs_cache_store_as_client(const char *desc_str,
|
||||
const struct ed25519_public_key_t *identity_pk);
|
||||
void hs_cache_clean_as_client(time_t now);
|
||||
void hs_cache_purge_as_client(void);
|
||||
|
||||
@@ -99,6 +99,8 @@ const hs_cache_intro_state_t *hs_cache_client_intro_state_find(
|
||||
void hs_cache_client_intro_state_clean(time_t now);
|
||||
void hs_cache_client_intro_state_purge(void);
|
||||
|
||||
bool hs_cache_client_new_auth_parse(const ed25519_public_key_t *service_pk);
|
||||
|
||||
#ifdef HS_CACHE_PRIVATE
|
||||
#include "lib/crypt_ops/crypto_ed25519.h"
|
||||
|
||||
@@ -112,8 +114,10 @@ typedef struct hs_cache_client_descriptor_t {
|
||||
* using the next blinded key of the service. */
|
||||
time_t expiration_ts;
|
||||
|
||||
/** The cached descriptor, this object is the owner. It can't be NULL. A
|
||||
* cache object without a valid descriptor is not possible. */
|
||||
/** The cached decoded descriptor, this object is the owner. This can be
|
||||
* NULL if the descriptor couldn't be decoded due to missing or bad client
|
||||
* authorization. It can be decoded later from the encoded_desc object if
|
||||
* the proper client authorization is given tor. */
|
||||
hs_descriptor_t *desc;
|
||||
|
||||
/** Encoded descriptor in string form. Can't be NULL. */
|
||||
|
||||
+282
-80
@@ -42,6 +42,7 @@
|
||||
#include "core/or/entry_connection_st.h"
|
||||
#include "core/or/extend_info_st.h"
|
||||
#include "core/or/origin_circuit_st.h"
|
||||
#include "core/or/socks_request_st.h"
|
||||
|
||||
/** Client-side authorizations for hidden services; map of service identity
|
||||
* public key to hs_client_service_authorization_t *. */
|
||||
@@ -100,7 +101,46 @@ fetch_status_should_close_socks(hs_client_fetch_status_t status)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Cancel all descriptor fetches currently in progress. */
|
||||
/* Return a newly allocated list of all the entry connections that matches the
|
||||
* given service identity pk. If service_identity_pk is NULL, all entry
|
||||
* connections with an hs_ident are returned.
|
||||
*
|
||||
* Caller must free the returned list but does NOT have ownership of the
|
||||
* object inside thus they have to remain untouched. */
|
||||
static smartlist_t *
|
||||
find_entry_conns(const ed25519_public_key_t *service_identity_pk)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
smartlist_t *conns = NULL, *entry_conns = NULL;
|
||||
|
||||
entry_conns = smartlist_new();
|
||||
|
||||
conns = connection_list_by_type_state(CONN_TYPE_AP,
|
||||
AP_CONN_STATE_RENDDESC_WAIT);
|
||||
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
|
||||
entry_connection_t *entry_conn = TO_ENTRY_CONN(base_conn);
|
||||
const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn);
|
||||
|
||||
/* Only consider the entry connections that matches the service for which
|
||||
* we just fetched its descriptor. */
|
||||
if (!edge_conn->hs_ident ||
|
||||
(service_identity_pk &&
|
||||
!ed25519_pubkey_eq(service_identity_pk,
|
||||
&edge_conn->hs_ident->identity_pk))) {
|
||||
continue;
|
||||
}
|
||||
assert_connection_ok(base_conn, now);
|
||||
|
||||
/* Validated! Add the entry connection to the list. */
|
||||
smartlist_add(entry_conns, entry_conn);
|
||||
} SMARTLIST_FOREACH_END(base_conn);
|
||||
|
||||
/* We don't have ownership of the objects in this list. */
|
||||
smartlist_free(conns);
|
||||
return entry_conns;
|
||||
}
|
||||
|
||||
/* Cancel all descriptor fetches currently in progress. */
|
||||
static void
|
||||
cancel_descriptor_fetches(void)
|
||||
{
|
||||
@@ -230,26 +270,13 @@ close_all_socks_conns_waiting_for_desc(const ed25519_public_key_t *identity_pk,
|
||||
int reason)
|
||||
{
|
||||
unsigned int count = 0;
|
||||
time_t now = approx_time();
|
||||
smartlist_t *conns =
|
||||
connection_list_by_type_state(CONN_TYPE_AP, AP_CONN_STATE_RENDDESC_WAIT);
|
||||
smartlist_t *entry_conns = find_entry_conns(identity_pk);
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
|
||||
entry_connection_t *entry_conn = TO_ENTRY_CONN(base_conn);
|
||||
const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn);
|
||||
|
||||
/* Only consider the entry connections that matches the service for which
|
||||
* we tried to get the descriptor */
|
||||
if (!edge_conn->hs_ident ||
|
||||
!ed25519_pubkey_eq(identity_pk,
|
||||
&edge_conn->hs_ident->identity_pk)) {
|
||||
continue;
|
||||
}
|
||||
assert_connection_ok(base_conn, now);
|
||||
SMARTLIST_FOREACH_BEGIN(entry_conns, entry_connection_t *, entry_conn) {
|
||||
/* Unattach the entry connection which will close for the reason. */
|
||||
connection_mark_unattached_ap(entry_conn, reason);
|
||||
count++;
|
||||
} SMARTLIST_FOREACH_END(base_conn);
|
||||
} SMARTLIST_FOREACH_END(entry_conn);
|
||||
|
||||
if (count > 0) {
|
||||
char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
|
||||
@@ -262,7 +289,7 @@ close_all_socks_conns_waiting_for_desc(const ed25519_public_key_t *identity_pk,
|
||||
}
|
||||
|
||||
/* No ownership of the object(s) in this list. */
|
||||
smartlist_free(conns);
|
||||
smartlist_free(entry_conns);
|
||||
}
|
||||
|
||||
/** Find all pending SOCKS connection waiting for a descriptor and retry them
|
||||
@@ -270,18 +297,18 @@ close_all_socks_conns_waiting_for_desc(const ed25519_public_key_t *identity_pk,
|
||||
STATIC void
|
||||
retry_all_socks_conn_waiting_for_desc(void)
|
||||
{
|
||||
smartlist_t *conns =
|
||||
connection_list_by_type_state(CONN_TYPE_AP, AP_CONN_STATE_RENDDESC_WAIT);
|
||||
smartlist_t *entry_conns = find_entry_conns(NULL);
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
|
||||
SMARTLIST_FOREACH_BEGIN(entry_conns, entry_connection_t *, entry_conn) {
|
||||
hs_client_fetch_status_t status;
|
||||
const edge_connection_t *edge_conn =
|
||||
ENTRY_TO_EDGE_CONN(TO_ENTRY_CONN(base_conn));
|
||||
edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn);
|
||||
connection_t *base_conn = &edge_conn->base_;
|
||||
|
||||
/* Ignore non HS or non v3 connection. */
|
||||
if (edge_conn->hs_ident == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* In this loop, we will possibly try to fetch a descriptor for the
|
||||
* pending connections because we just got more directory information.
|
||||
* However, the refetch process can cleanup all SOCKS request to the same
|
||||
@@ -315,10 +342,10 @@ retry_all_socks_conn_waiting_for_desc(void)
|
||||
* closed or we are still missing directory information. Leave the
|
||||
* connection in renddesc wait state so when we get more info, we'll be
|
||||
* able to try it again. */
|
||||
} SMARTLIST_FOREACH_END(base_conn);
|
||||
} SMARTLIST_FOREACH_END(entry_conn);
|
||||
|
||||
/* We don't have ownership of those objects. */
|
||||
smartlist_free(conns);
|
||||
smartlist_free(entry_conns);
|
||||
}
|
||||
|
||||
/** A v3 HS circuit successfully connected to the hidden service. Update the
|
||||
@@ -1235,6 +1262,189 @@ find_client_auth(const ed25519_public_key_t *service_identity_pk)
|
||||
return digest256map_get(client_auths, service_identity_pk->pubkey);
|
||||
}
|
||||
|
||||
/** This is called when a descriptor has arrived following a fetch request and
|
||||
* has been stored in the client cache. The given entry connections, matching
|
||||
* the service identity key, will get attached to the service circuit. */
|
||||
static void
|
||||
client_desc_has_arrived(const smartlist_t *entry_conns)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
|
||||
tor_assert(entry_conns);
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(entry_conns, entry_connection_t *, entry_conn) {
|
||||
const hs_descriptor_t *desc;
|
||||
edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn);
|
||||
const ed25519_public_key_t *identity_pk =
|
||||
&edge_conn->hs_ident->identity_pk;
|
||||
|
||||
/* We were just called because we stored the descriptor for this service
|
||||
* so not finding a descriptor means we have a bigger problem. */
|
||||
desc = hs_cache_lookup_as_client(identity_pk);
|
||||
if (BUG(desc == NULL)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!hs_client_any_intro_points_usable(identity_pk, desc)) {
|
||||
log_info(LD_REND, "Hidden service descriptor is unusable. "
|
||||
"Closing streams.");
|
||||
connection_mark_unattached_ap(entry_conn,
|
||||
END_STREAM_REASON_RESOLVEFAILED);
|
||||
/* We are unable to use the descriptor so remove the directory request
|
||||
* from the cache so the next connection can try again. */
|
||||
note_connection_attempt_succeeded(edge_conn->hs_ident);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_info(LD_REND, "Descriptor has arrived. Launching circuits.");
|
||||
|
||||
/* Mark connection as waiting for a circuit since we do have a usable
|
||||
* descriptor now. */
|
||||
mark_conn_as_waiting_for_circuit(&edge_conn->base_, now);
|
||||
} SMARTLIST_FOREACH_END(entry_conn);
|
||||
|
||||
end:
|
||||
return;
|
||||
}
|
||||
|
||||
/** This is called when a descriptor fetch was successful but the descriptor
|
||||
* couldn't be decrypted due to missing or bad client authorization. */
|
||||
static void
|
||||
client_desc_missing_bad_client_auth(const smartlist_t *entry_conns,
|
||||
hs_desc_decode_status_t status)
|
||||
{
|
||||
tor_assert(entry_conns);
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(entry_conns, entry_connection_t *, entry_conn) {
|
||||
socks5_reply_status_t code;
|
||||
if (status == HS_DESC_DECODE_BAD_CLIENT_AUTH) {
|
||||
code = SOCKS5_HS_BAD_CLIENT_AUTH;
|
||||
} else if (status == HS_DESC_DECODE_NEED_CLIENT_AUTH) {
|
||||
code = SOCKS5_HS_MISSING_CLIENT_AUTH;
|
||||
} else {
|
||||
/* We should not be called with another type of status. Recover by
|
||||
* sending a generic error. */
|
||||
tor_assert_nonfatal_unreached();
|
||||
code = HS_DESC_DECODE_GENERIC_ERROR;
|
||||
}
|
||||
entry_conn->socks_request->socks_extended_error_code = code;
|
||||
connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_MISC);
|
||||
} SMARTLIST_FOREACH_END(entry_conn);
|
||||
}
|
||||
|
||||
/** Called when we get a 200 directory fetch status code. */
|
||||
static void
|
||||
client_dir_fetch_200(dir_connection_t *dir_conn,
|
||||
const smartlist_t *entry_conns, const char *body)
|
||||
{
|
||||
hs_desc_decode_status_t decode_status;
|
||||
|
||||
tor_assert(dir_conn);
|
||||
tor_assert(entry_conns);
|
||||
tor_assert(body);
|
||||
|
||||
/* We got something: Try storing it in the cache. */
|
||||
decode_status = hs_cache_store_as_client(body,
|
||||
&dir_conn->hs_ident->identity_pk);
|
||||
switch (decode_status) {
|
||||
case HS_DESC_DECODE_OK:
|
||||
case HS_DESC_DECODE_NEED_CLIENT_AUTH:
|
||||
case HS_DESC_DECODE_BAD_CLIENT_AUTH:
|
||||
log_info(LD_REND, "Stored hidden service descriptor successfully.");
|
||||
TO_CONN(dir_conn)->purpose = DIR_PURPOSE_HAS_FETCHED_HSDESC;
|
||||
if (decode_status == HS_DESC_DECODE_OK) {
|
||||
client_desc_has_arrived(entry_conns);
|
||||
} else {
|
||||
/* This handles both client auth decode status. */
|
||||
client_desc_missing_bad_client_auth(entry_conns, decode_status);
|
||||
log_info(LD_REND, "Stored hidden service descriptor requires "
|
||||
"%s client authorization.",
|
||||
decode_status == HS_DESC_DECODE_NEED_CLIENT_AUTH ? "missing"
|
||||
: "new");
|
||||
}
|
||||
/* Fire control port RECEIVED event. */
|
||||
hs_control_desc_event_received(dir_conn->hs_ident,
|
||||
dir_conn->identity_digest);
|
||||
hs_control_desc_event_content(dir_conn->hs_ident,
|
||||
dir_conn->identity_digest, body);
|
||||
break;
|
||||
case HS_DESC_DECODE_ENCRYPTED_ERROR:
|
||||
case HS_DESC_DECODE_SUPERENC_ERROR:
|
||||
case HS_DESC_DECODE_PLAINTEXT_ERROR:
|
||||
case HS_DESC_DECODE_GENERIC_ERROR:
|
||||
default:
|
||||
log_info(LD_REND, "Failed to store hidden service descriptor. "
|
||||
"Descriptor decoding status: %d", decode_status);
|
||||
/* Fire control port FAILED event. */
|
||||
hs_control_desc_event_failed(dir_conn->hs_ident,
|
||||
dir_conn->identity_digest, "BAD_DESC");
|
||||
hs_control_desc_event_content(dir_conn->hs_ident,
|
||||
dir_conn->identity_digest, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when we get a 404 directory fetch status code. */
|
||||
static void
|
||||
client_dir_fetch_404(dir_connection_t *dir_conn,
|
||||
const smartlist_t *entry_conns)
|
||||
{
|
||||
tor_assert(entry_conns);
|
||||
|
||||
/* Not there. We'll retry when connection_about_to_close_connection() tries
|
||||
* to clean this conn up. */
|
||||
log_info(LD_REND, "Fetching hidden service v3 descriptor not found: "
|
||||
"Retrying at another directory.");
|
||||
/* Fire control port FAILED event. */
|
||||
hs_control_desc_event_failed(dir_conn->hs_ident, dir_conn->identity_digest,
|
||||
"NOT_FOUND");
|
||||
hs_control_desc_event_content(dir_conn->hs_ident, dir_conn->identity_digest,
|
||||
NULL);
|
||||
|
||||
/* Flag every entry connections that the descriptor was not found. */
|
||||
SMARTLIST_FOREACH_BEGIN(entry_conns, entry_connection_t *, entry_conn) {
|
||||
entry_conn->socks_request->socks_extended_error_code =
|
||||
SOCKS5_HS_NOT_FOUND;
|
||||
} SMARTLIST_FOREACH_END(entry_conn);
|
||||
}
|
||||
|
||||
/** Called when we get a 400 directory fetch status code. */
|
||||
static void
|
||||
client_dir_fetch_400(dir_connection_t *dir_conn, const char *reason)
|
||||
{
|
||||
tor_assert(dir_conn);
|
||||
|
||||
log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
|
||||
"http status 400 (%s). Dirserver didn't like our "
|
||||
"query? Retrying at another directory.",
|
||||
escaped(reason));
|
||||
|
||||
/* Fire control port FAILED event. */
|
||||
hs_control_desc_event_failed(dir_conn->hs_ident, dir_conn->identity_digest,
|
||||
"QUERY_REJECTED");
|
||||
hs_control_desc_event_content(dir_conn->hs_ident, dir_conn->identity_digest,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/** Called when we get an unexpected directory fetch status code. */
|
||||
static void
|
||||
client_dir_fetch_unexpected(dir_connection_t *dir_conn, const char *reason,
|
||||
const int status_code)
|
||||
{
|
||||
tor_assert(dir_conn);
|
||||
|
||||
log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
|
||||
"http status %d (%s) response unexpected from HSDir "
|
||||
"server '%s:%d'. Retrying at another directory.",
|
||||
status_code, escaped(reason), TO_CONN(dir_conn)->address,
|
||||
TO_CONN(dir_conn)->port);
|
||||
/* Fire control port FAILED event. */
|
||||
hs_control_desc_event_failed(dir_conn->hs_ident, dir_conn->identity_digest,
|
||||
"UNEXPECTED");
|
||||
hs_control_desc_event_content(dir_conn->hs_ident, dir_conn->identity_digest,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* ========== */
|
||||
/* Public API */
|
||||
/* ========== */
|
||||
@@ -1264,13 +1474,15 @@ hs_client_note_connection_attempt_succeeded(const edge_connection_t *conn)
|
||||
* service_identity_pk, decode the descriptor and set the desc pointer with a
|
||||
* newly allocated descriptor object.
|
||||
*
|
||||
* Return 0 on success else a negative value and desc is set to NULL. */
|
||||
int
|
||||
* On success, HS_DESC_DECODE_OK is returned and desc is set to the decoded
|
||||
* descriptor. On error, desc is set to NULL and a decoding error status is
|
||||
* returned depending on what was the issue. */
|
||||
hs_desc_decode_status_t
|
||||
hs_client_decode_descriptor(const char *desc_str,
|
||||
const ed25519_public_key_t *service_identity_pk,
|
||||
hs_descriptor_t **desc)
|
||||
{
|
||||
int ret;
|
||||
hs_desc_decode_status_t ret;
|
||||
uint8_t subcredential[DIGEST256_LEN];
|
||||
ed25519_public_key_t blinded_pubkey;
|
||||
hs_client_service_authorization_t *client_auth = NULL;
|
||||
@@ -1298,7 +1510,7 @@ hs_client_decode_descriptor(const char *desc_str,
|
||||
ret = hs_desc_decode_descriptor(desc_str, subcredential,
|
||||
client_auht_sk, desc);
|
||||
memwipe(subcredential, 0, sizeof(subcredential));
|
||||
if (ret < 0) {
|
||||
if (ret != HS_DESC_DECODE_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -1311,12 +1523,13 @@ hs_client_decode_descriptor(const char *desc_str,
|
||||
log_warn(LD_GENERAL, "Descriptor signing key certificate signature "
|
||||
"doesn't validate with computed blinded key: %s",
|
||||
tor_cert_describe_signature_status(cert));
|
||||
ret = HS_DESC_DECODE_GENERIC_ERROR;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return HS_DESC_DECODE_OK;
|
||||
err:
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Return true iff there are at least one usable intro point in the service
|
||||
@@ -1685,62 +1898,45 @@ hs_config_client_authorization(const or_options_t *options,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** This is called when a descriptor has arrived following a fetch request and
|
||||
* has been stored in the client cache. Every entry connection that matches
|
||||
* the service identity key in the ident will get attached to the hidden
|
||||
* service circuit. */
|
||||
/** Called when a descriptor directory fetch is done.
|
||||
*
|
||||
* Act accordingly on all entry connections depending on the HTTP status code
|
||||
* we got. In case of an error, the SOCKS error is set (if ExtendedErrors is
|
||||
* set).
|
||||
*
|
||||
* The reason is a human readable string returned by the directory server
|
||||
* which can describe the status of the request. The body is the response
|
||||
* content, on 200 code it is the descriptor itself. Finally, the status_code
|
||||
* is the HTTP code returned by the directory server. */
|
||||
void
|
||||
hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident)
|
||||
hs_client_dir_fetch_done(dir_connection_t *dir_conn, const char *reason,
|
||||
const char *body, const int status_code)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
smartlist_t *conns = NULL;
|
||||
smartlist_t *entry_conns;
|
||||
|
||||
tor_assert(ident);
|
||||
tor_assert(dir_conn);
|
||||
tor_assert(body);
|
||||
|
||||
conns = connection_list_by_type_state(CONN_TYPE_AP,
|
||||
AP_CONN_STATE_RENDDESC_WAIT);
|
||||
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
|
||||
const hs_descriptor_t *desc;
|
||||
entry_connection_t *entry_conn = TO_ENTRY_CONN(base_conn);
|
||||
const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn);
|
||||
/* Get all related entry connections. */
|
||||
entry_conns = find_entry_conns(&dir_conn->hs_ident->identity_pk);
|
||||
|
||||
/* Only consider the entry connections that matches the service for which
|
||||
* we just fetched its descriptor. */
|
||||
if (!edge_conn->hs_ident ||
|
||||
!ed25519_pubkey_eq(&ident->identity_pk,
|
||||
&edge_conn->hs_ident->identity_pk)) {
|
||||
continue;
|
||||
}
|
||||
assert_connection_ok(base_conn, now);
|
||||
switch (status_code) {
|
||||
case 200:
|
||||
client_dir_fetch_200(dir_conn, entry_conns, body);
|
||||
break;
|
||||
case 404:
|
||||
client_dir_fetch_404(dir_conn, entry_conns);
|
||||
break;
|
||||
case 400:
|
||||
client_dir_fetch_400(dir_conn, reason);
|
||||
break;
|
||||
default:
|
||||
client_dir_fetch_unexpected(dir_conn, reason, status_code);
|
||||
break;
|
||||
}
|
||||
|
||||
/* We were just called because we stored the descriptor for this service
|
||||
* so not finding a descriptor means we have a bigger problem. */
|
||||
desc = hs_cache_lookup_as_client(&ident->identity_pk);
|
||||
if (BUG(desc == NULL)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!hs_client_any_intro_points_usable(&ident->identity_pk, desc)) {
|
||||
log_info(LD_REND, "Hidden service descriptor is unusable. "
|
||||
"Closing streams.");
|
||||
connection_mark_unattached_ap(entry_conn,
|
||||
END_STREAM_REASON_RESOLVEFAILED);
|
||||
/* We are unable to use the descriptor so remove the directory request
|
||||
* from the cache so the next connection can try again. */
|
||||
note_connection_attempt_succeeded(edge_conn->hs_ident);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_info(LD_REND, "Descriptor has arrived. Launching circuits.");
|
||||
|
||||
/* Mark connection as waiting for a circuit since we do have a usable
|
||||
* descriptor now. */
|
||||
mark_conn_as_waiting_for_circuit(base_conn, now);
|
||||
} SMARTLIST_FOREACH_END(base_conn);
|
||||
|
||||
end:
|
||||
/* We don't have ownership of the objects in this list. */
|
||||
smartlist_free(conns);
|
||||
smartlist_free(entry_conns);
|
||||
}
|
||||
|
||||
/** Return a newly allocated extend_info_t for a randomly chosen introduction
|
||||
@@ -1948,4 +2144,10 @@ get_hs_client_auths_map(void)
|
||||
return client_auths;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
set_hs_client_auths_map(digest256map_t *map)
|
||||
{
|
||||
client_auths = map;
|
||||
}
|
||||
|
||||
#endif /* defined(TOR_UNIT_TESTS) */
|
||||
|
||||
@@ -48,7 +48,7 @@ void hs_client_launch_v3_desc_fetch(
|
||||
const ed25519_public_key_t *onion_identity_pk,
|
||||
const smartlist_t *hsdirs);
|
||||
|
||||
int hs_client_decode_descriptor(
|
||||
hs_desc_decode_status_t hs_client_decode_descriptor(
|
||||
const char *desc_str,
|
||||
const ed25519_public_key_t *service_identity_pk,
|
||||
hs_descriptor_t **desc);
|
||||
@@ -72,7 +72,8 @@ int hs_client_receive_rendezvous2(origin_circuit_t *circ,
|
||||
const uint8_t *payload,
|
||||
size_t payload_len);
|
||||
|
||||
void hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident);
|
||||
void hs_client_dir_fetch_done(dir_connection_t *dir_conn, const char *reason,
|
||||
const char *body, const int status_code);
|
||||
|
||||
extend_info_t *hs_client_get_random_intro_from_edge(
|
||||
const edge_connection_t *edge_conn);
|
||||
@@ -114,6 +115,7 @@ STATIC void retry_all_socks_conn_waiting_for_desc(void);
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
|
||||
STATIC digest256map_t *get_hs_client_auths_map(void);
|
||||
STATIC void set_hs_client_auths_map(digest256map_t *map);
|
||||
|
||||
#endif /* defined(TOR_UNIT_TESTS) */
|
||||
|
||||
|
||||
@@ -2038,7 +2038,7 @@ desc_sig_is_valid(const char *b64_sig,
|
||||
* unknowns but requires that all v3 token be present and valid.
|
||||
*
|
||||
* Return 0 on success else a negative value. */
|
||||
static int
|
||||
static hs_desc_decode_status_t
|
||||
desc_decode_plaintext_v3(smartlist_t *tokens,
|
||||
hs_desc_plaintext_data_t *desc,
|
||||
const char *encoded_desc, size_t encoded_len)
|
||||
@@ -2128,21 +2128,19 @@ desc_decode_plaintext_v3(smartlist_t *tokens,
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
return HS_DESC_DECODE_OK;
|
||||
err:
|
||||
return -1;
|
||||
return HS_DESC_DECODE_PLAINTEXT_ERROR;
|
||||
}
|
||||
|
||||
/** Decode the version 3 superencrypted section of the given descriptor desc.
|
||||
* The desc_superencrypted_out will be populated with the decoded data.
|
||||
* Return 0 on success else -1. */
|
||||
static int
|
||||
* The desc_superencrypted_out will be populated with the decoded data. */
|
||||
static hs_desc_decode_status_t
|
||||
desc_decode_superencrypted_v3(const hs_descriptor_t *desc,
|
||||
hs_desc_superencrypted_data_t *
|
||||
desc_superencrypted_out)
|
||||
{
|
||||
int ret = -1;
|
||||
int ret = HS_DESC_DECODE_SUPERENC_ERROR;
|
||||
char *message = NULL;
|
||||
size_t message_len;
|
||||
memarea_t *area = NULL;
|
||||
@@ -2228,11 +2226,11 @@ desc_decode_superencrypted_v3(const hs_descriptor_t *desc,
|
||||
tok->object_size);
|
||||
superencrypted->encrypted_blob_size = tok->object_size;
|
||||
|
||||
ret = 0;
|
||||
ret = HS_DESC_DECODE_OK;
|
||||
goto done;
|
||||
|
||||
err:
|
||||
tor_assert(ret < 0);
|
||||
tor_assert(ret < HS_DESC_DECODE_OK);
|
||||
hs_desc_superencrypted_data_free_contents(desc_superencrypted_out);
|
||||
|
||||
done:
|
||||
@@ -2250,14 +2248,13 @@ desc_decode_superencrypted_v3(const hs_descriptor_t *desc,
|
||||
}
|
||||
|
||||
/** Decode the version 3 encrypted section of the given descriptor desc. The
|
||||
* desc_encrypted_out will be populated with the decoded data. Return 0 on
|
||||
* success else -1. */
|
||||
static int
|
||||
* desc_encrypted_out will be populated with the decoded data. */
|
||||
static hs_desc_decode_status_t
|
||||
desc_decode_encrypted_v3(const hs_descriptor_t *desc,
|
||||
const curve25519_secret_key_t *client_auth_sk,
|
||||
hs_desc_encrypted_data_t *desc_encrypted_out)
|
||||
{
|
||||
int ret = -1;
|
||||
int ret = HS_DESC_DECODE_ENCRYPTED_ERROR;
|
||||
char *message = NULL;
|
||||
size_t message_len;
|
||||
memarea_t *area = NULL;
|
||||
@@ -2280,12 +2277,14 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc,
|
||||
* authorization is failing. */
|
||||
log_warn(LD_REND, "Client authorization for requested onion address "
|
||||
"is invalid. Can't decrypt the descriptor.");
|
||||
ret = HS_DESC_DECODE_BAD_CLIENT_AUTH;
|
||||
} else {
|
||||
/* Inform at notice level that the onion address requested can't be
|
||||
* reached without client authorization most likely. */
|
||||
log_notice(LD_REND, "Fail to decrypt descriptor for requested onion "
|
||||
"address. It is likely requiring client "
|
||||
"authorization.");
|
||||
ret = HS_DESC_DECODE_NEED_CLIENT_AUTH;
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
@@ -2343,11 +2342,11 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc,
|
||||
/* NOTE: Unknown fields are allowed because this function could be used to
|
||||
* decode other descriptor version. */
|
||||
|
||||
ret = 0;
|
||||
ret = HS_DESC_DECODE_OK;
|
||||
goto done;
|
||||
|
||||
err:
|
||||
tor_assert(ret < 0);
|
||||
tor_assert(ret < HS_DESC_DECODE_OK);
|
||||
hs_desc_encrypted_data_free_contents(desc_encrypted_out);
|
||||
|
||||
done:
|
||||
@@ -2366,7 +2365,7 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc,
|
||||
|
||||
/** Table of encrypted decode function version specific. The function are
|
||||
* indexed by the version number so v3 callback is at index 3 in the array. */
|
||||
static int
|
||||
static hs_desc_decode_status_t
|
||||
(*decode_encrypted_handlers[])(
|
||||
const hs_descriptor_t *desc,
|
||||
const curve25519_secret_key_t *client_auth_sk,
|
||||
@@ -2379,12 +2378,12 @@ static int
|
||||
/** Decode the encrypted data section of the given descriptor and store the
|
||||
* data in the given encrypted data object. Return 0 on success else a
|
||||
* negative value on error. */
|
||||
int
|
||||
hs_desc_decode_status_t
|
||||
hs_desc_decode_encrypted(const hs_descriptor_t *desc,
|
||||
const curve25519_secret_key_t *client_auth_sk,
|
||||
hs_desc_encrypted_data_t *desc_encrypted)
|
||||
{
|
||||
int ret;
|
||||
int ret = HS_DESC_DECODE_ENCRYPTED_ERROR;
|
||||
uint32_t version;
|
||||
|
||||
tor_assert(desc);
|
||||
@@ -2398,7 +2397,6 @@ hs_desc_decode_encrypted(const hs_descriptor_t *desc,
|
||||
/* Let's make sure we have a supported version as well. By correctly parsing
|
||||
* the plaintext, this should not fail. */
|
||||
if (BUG(!hs_desc_is_supported_version(version))) {
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
/* Extra precaution. Having no handler for the supported version should
|
||||
@@ -2419,7 +2417,7 @@ hs_desc_decode_encrypted(const hs_descriptor_t *desc,
|
||||
|
||||
/** Table of superencrypted decode function version specific. The function are
|
||||
* indexed by the version number so v3 callback is at index 3 in the array. */
|
||||
static int
|
||||
static hs_desc_decode_status_t
|
||||
(*decode_superencrypted_handlers[])(
|
||||
const hs_descriptor_t *desc,
|
||||
hs_desc_superencrypted_data_t *desc_superencrypted) =
|
||||
@@ -2429,14 +2427,13 @@ static int
|
||||
};
|
||||
|
||||
/** Decode the superencrypted data section of the given descriptor and store
|
||||
* the data in the given superencrypted data object. Return 0 on success else
|
||||
* a negative value on error. */
|
||||
int
|
||||
* the data in the given superencrypted data object. */
|
||||
hs_desc_decode_status_t
|
||||
hs_desc_decode_superencrypted(const hs_descriptor_t *desc,
|
||||
hs_desc_superencrypted_data_t *
|
||||
desc_superencrypted)
|
||||
{
|
||||
int ret;
|
||||
int ret = HS_DESC_DECODE_SUPERENC_ERROR;
|
||||
uint32_t version;
|
||||
|
||||
tor_assert(desc);
|
||||
@@ -2450,7 +2447,6 @@ hs_desc_decode_superencrypted(const hs_descriptor_t *desc,
|
||||
/* Let's make sure we have a supported version as well. By correctly parsing
|
||||
* the plaintext, this should not fail. */
|
||||
if (BUG(!hs_desc_is_supported_version(version))) {
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
/* Extra precaution. Having no handler for the supported version should
|
||||
@@ -2470,7 +2466,7 @@ hs_desc_decode_superencrypted(const hs_descriptor_t *desc,
|
||||
|
||||
/** Table of plaintext decode function version specific. The function are
|
||||
* indexed by the version number so v3 callback is at index 3 in the array. */
|
||||
static int
|
||||
static hs_desc_decode_status_t
|
||||
(*decode_plaintext_handlers[])(
|
||||
smartlist_t *tokens,
|
||||
hs_desc_plaintext_data_t *desc,
|
||||
@@ -2482,12 +2478,12 @@ static int
|
||||
};
|
||||
|
||||
/** Fully decode the given descriptor plaintext and store the data in the
|
||||
* plaintext data object. Returns 0 on success else a negative value. */
|
||||
int
|
||||
* plaintext data object. */
|
||||
hs_desc_decode_status_t
|
||||
hs_desc_decode_plaintext(const char *encoded,
|
||||
hs_desc_plaintext_data_t *plaintext)
|
||||
{
|
||||
int ok = 0, ret = -1;
|
||||
int ok = 0, ret = HS_DESC_DECODE_PLAINTEXT_ERROR;
|
||||
memarea_t *area = NULL;
|
||||
smartlist_t *tokens = NULL;
|
||||
size_t encoded_len;
|
||||
@@ -2537,11 +2533,11 @@ hs_desc_decode_plaintext(const char *encoded,
|
||||
/* Run the version specific plaintext decoder. */
|
||||
ret = decode_plaintext_handlers[plaintext->version](tokens, plaintext,
|
||||
encoded, encoded_len);
|
||||
if (ret < 0) {
|
||||
if (ret != HS_DESC_DECODE_OK) {
|
||||
goto err;
|
||||
}
|
||||
/* Success. Descriptor has been populated with the data. */
|
||||
ret = 0;
|
||||
ret = HS_DESC_DECODE_OK;
|
||||
|
||||
err:
|
||||
if (tokens) {
|
||||
@@ -2560,13 +2556,13 @@ hs_desc_decode_plaintext(const char *encoded,
|
||||
*
|
||||
* Return 0 on success. A negative value is returned on error and desc_out is
|
||||
* set to NULL. */
|
||||
int
|
||||
hs_desc_decode_status_t
|
||||
hs_desc_decode_descriptor(const char *encoded,
|
||||
const uint8_t *subcredential,
|
||||
const curve25519_secret_key_t *client_auth_sk,
|
||||
hs_descriptor_t **desc_out)
|
||||
{
|
||||
int ret = -1;
|
||||
hs_desc_decode_status_t ret = HS_DESC_DECODE_GENERIC_ERROR;
|
||||
hs_descriptor_t *desc;
|
||||
|
||||
tor_assert(encoded);
|
||||
@@ -2583,17 +2579,17 @@ hs_desc_decode_descriptor(const char *encoded,
|
||||
memcpy(desc->subcredential, subcredential, sizeof(desc->subcredential));
|
||||
|
||||
ret = hs_desc_decode_plaintext(encoded, &desc->plaintext_data);
|
||||
if (ret < 0) {
|
||||
if (ret != HS_DESC_DECODE_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = hs_desc_decode_superencrypted(desc, &desc->superencrypted_data);
|
||||
if (ret < 0) {
|
||||
if (ret != HS_DESC_DECODE_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = hs_desc_decode_encrypted(desc, client_auth_sk, &desc->encrypted_data);
|
||||
if (ret < 0) {
|
||||
if (ret != HS_DESC_DECODE_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -2672,7 +2668,8 @@ hs_desc_encode_descriptor,(const hs_descriptor_t *desc,
|
||||
if (!descriptor_cookie) {
|
||||
ret = hs_desc_decode_descriptor(*encoded_out, desc->subcredential,
|
||||
NULL, NULL);
|
||||
if (BUG(ret < 0)) {
|
||||
if (BUG(ret != HS_DESC_DECODE_OK)) {
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
@@ -2815,7 +2812,9 @@ hs_desc_encrypted_obj_size(const hs_desc_encrypted_data_t *data)
|
||||
size_t
|
||||
hs_desc_obj_size(const hs_descriptor_t *data)
|
||||
{
|
||||
tor_assert(data);
|
||||
if (data == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return (hs_desc_plaintext_obj_size(&data->plaintext_data) +
|
||||
hs_desc_encrypted_obj_size(&data->encrypted_data) +
|
||||
sizeof(data->subcredential));
|
||||
|
||||
@@ -69,6 +69,31 @@ typedef enum {
|
||||
HS_DESC_AUTH_ED25519 = 1
|
||||
} hs_desc_auth_type_t;
|
||||
|
||||
/** Error code when decoding a descriptor. */
|
||||
typedef enum {
|
||||
/* The configured client authorization for the requested .onion address
|
||||
* failed to decode the descriptor. */
|
||||
HS_DESC_DECODE_BAD_CLIENT_AUTH = -6,
|
||||
|
||||
/* The requested .onion address requires a client authorization. */
|
||||
HS_DESC_DECODE_NEED_CLIENT_AUTH = -5,
|
||||
|
||||
/* Error during decryption of the encrypted layer. */
|
||||
HS_DESC_DECODE_ENCRYPTED_ERROR = -4,
|
||||
|
||||
/* Error during decryption of the super encrypted layer. */
|
||||
HS_DESC_DECODE_SUPERENC_ERROR = -3,
|
||||
|
||||
/* Error while decoding the plaintext section. */
|
||||
HS_DESC_DECODE_PLAINTEXT_ERROR = -2,
|
||||
|
||||
/* Generic error. */
|
||||
HS_DESC_DECODE_GENERIC_ERROR = -1,
|
||||
|
||||
/* Decoding a descriptor was successful. */
|
||||
HS_DESC_DECODE_OK = 0,
|
||||
} hs_desc_decode_status_t;
|
||||
|
||||
/** Introduction point information located in a descriptor. */
|
||||
typedef struct hs_desc_intro_point_t {
|
||||
/** Link specifier(s) which details how to extend to the relay. This list
|
||||
|
||||
@@ -27,6 +27,15 @@ typedef enum {
|
||||
SOCKS5_TTL_EXPIRED = 0x06,
|
||||
SOCKS5_COMMAND_NOT_SUPPORTED = 0x07,
|
||||
SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED = 0x08,
|
||||
|
||||
/* Extended error code (see prop304). Only used if the SocksPort flag
|
||||
* "ExtendedErrors" is set. */
|
||||
SOCKS5_HS_NOT_FOUND = 0xF0,
|
||||
SOCKS5_HS_IS_INVALID = 0xF1,
|
||||
SOCKS5_HS_INTRO_FAILED = 0xF2,
|
||||
SOCKS5_HS_REND_FAILED = 0xF3,
|
||||
SOCKS5_HS_MISSING_CLIENT_AUTH = 0xF4,
|
||||
SOCKS5_HS_BAD_CLIENT_AUTH = 0xF5,
|
||||
} socks5_reply_status_t;
|
||||
|
||||
#endif /* !defined(TOR_SOCKS5_STATUS_H) */
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
/* Copyright (c) 2017-2019, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#define HS_CLIENT_PRIVATE
|
||||
|
||||
#include "core/or/or.h"
|
||||
#include "lib/crypt_ops/crypto_ed25519.h"
|
||||
#include "test/test.h"
|
||||
#include "feature/nodelist/torcert.h"
|
||||
|
||||
#include "feature/hs/hs_client.h"
|
||||
#include "feature/hs/hs_common.h"
|
||||
#include "feature/hs/hs_service.h"
|
||||
#include "test/hs_test_helpers.h"
|
||||
|
||||
hs_desc_intro_point_t *
|
||||
@@ -207,6 +211,35 @@ hs_helper_build_hs_desc_no_ip(const ed25519_keypair_t *signing_kp)
|
||||
return hs_helper_build_hs_desc_impl(1, signing_kp);
|
||||
}
|
||||
|
||||
hs_descriptor_t *
|
||||
hs_helper_build_hs_desc_with_client_auth(
|
||||
const uint8_t *descriptor_cookie,
|
||||
const curve25519_public_key_t *client_pk,
|
||||
const ed25519_keypair_t *signing_kp)
|
||||
{
|
||||
curve25519_keypair_t auth_ephemeral_kp;
|
||||
hs_descriptor_t *desc = hs_helper_build_hs_desc_impl(0, signing_kp);
|
||||
hs_desc_authorized_client_t *desc_client;
|
||||
|
||||
/* The number of client authorized auth has tobe a multiple of
|
||||
* HS_DESC_AUTH_CLIENT_MULTIPLE so remove one that we'll replace. */
|
||||
desc_client = smartlist_get(desc->superencrypted_data.clients, 0);
|
||||
smartlist_remove(desc->superencrypted_data.clients, desc_client);
|
||||
hs_desc_authorized_client_free(desc_client);
|
||||
|
||||
desc_client = tor_malloc_zero(sizeof(hs_desc_authorized_client_t));
|
||||
|
||||
curve25519_keypair_generate(&auth_ephemeral_kp, 0);
|
||||
memcpy(&desc->superencrypted_data.auth_ephemeral_pubkey,
|
||||
&auth_ephemeral_kp.pubkey, sizeof(curve25519_public_key_t));
|
||||
|
||||
hs_desc_build_authorized_client(desc->subcredential, client_pk,
|
||||
&auth_ephemeral_kp.seckey,
|
||||
descriptor_cookie, desc_client);
|
||||
smartlist_add(desc->superencrypted_data.clients, desc_client);
|
||||
return desc;
|
||||
}
|
||||
|
||||
void
|
||||
hs_helper_desc_equal(const hs_descriptor_t *desc1,
|
||||
const hs_descriptor_t *desc2)
|
||||
@@ -353,3 +386,19 @@ hs_helper_desc_equal(const hs_descriptor_t *desc1,
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
hs_helper_add_client_auth(const ed25519_public_key_t *service_pk,
|
||||
const curve25519_secret_key_t *client_sk)
|
||||
{
|
||||
digest256map_t *client_auths = get_hs_client_auths_map();
|
||||
if (client_auths == NULL) {
|
||||
client_auths = digest256map_new();
|
||||
set_hs_client_auths_map(client_auths);
|
||||
}
|
||||
|
||||
hs_client_service_authorization_t *auth =
|
||||
tor_malloc_zero(sizeof(hs_client_service_authorization_t));
|
||||
memcpy(&auth->enc_seckey, client_sk, sizeof(curve25519_secret_key_t));
|
||||
hs_build_address(service_pk, HS_VERSION_THREE, auth->onion_address);
|
||||
digest256map_set(client_auths, service_pk->pubkey, auth);
|
||||
}
|
||||
|
||||
@@ -15,11 +15,18 @@ hs_descriptor_t *hs_helper_build_hs_desc_no_ip(
|
||||
const ed25519_keypair_t *signing_kp);
|
||||
hs_descriptor_t *hs_helper_build_hs_desc_with_ip(
|
||||
const ed25519_keypair_t *signing_kp);
|
||||
hs_descriptor_t *hs_helper_build_hs_desc_with_client_auth(
|
||||
const uint8_t *descriptor_cookie,
|
||||
const curve25519_public_key_t *client_pk,
|
||||
const ed25519_keypair_t *signing_kp);
|
||||
void hs_helper_desc_equal(const hs_descriptor_t *desc1,
|
||||
const hs_descriptor_t *desc2);
|
||||
void
|
||||
hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
|
||||
uint8_t *subcred_out);
|
||||
|
||||
void hs_helper_add_client_auth(const ed25519_public_key_t *service_pk,
|
||||
const curve25519_secret_key_t *client_sk);
|
||||
|
||||
#endif /* !defined(TOR_HS_TEST_HELPERS_H) */
|
||||
|
||||
|
||||
@@ -20,9 +20,10 @@
|
||||
#include "feature/nodelist/networkstatus.h"
|
||||
#include "core/mainloop/connection.h"
|
||||
#include "core/proto/proto_http.h"
|
||||
#include "lib/crypt_ops/crypto_format.h"
|
||||
#include "core/or/circuitlist.h"
|
||||
#include "core/or/channel.h"
|
||||
#include "lib/crypt_ops/crypto_format.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
|
||||
#include "core/or/edge_connection_st.h"
|
||||
#include "core/or/or_circuit_st.h"
|
||||
@@ -411,7 +412,7 @@ test_hsdir_revision_counter_check(void *arg)
|
||||
|
||||
retval = hs_desc_decode_descriptor(received_desc_str,
|
||||
subcredential, NULL, &received_desc);
|
||||
tt_int_op(retval, OP_EQ, 0);
|
||||
tt_int_op(retval, OP_EQ, HS_DESC_DECODE_OK);
|
||||
tt_assert(received_desc);
|
||||
|
||||
/* Check that the revision counter is correct */
|
||||
@@ -444,7 +445,7 @@ test_hsdir_revision_counter_check(void *arg)
|
||||
|
||||
retval = hs_desc_decode_descriptor(received_desc_str,
|
||||
subcredential, NULL, &received_desc);
|
||||
tt_int_op(retval, OP_EQ, 0);
|
||||
tt_int_op(retval, OP_EQ, HS_DESC_DECODE_OK);
|
||||
tt_assert(received_desc);
|
||||
|
||||
/* Check that the revision counter is the latest */
|
||||
@@ -567,6 +568,83 @@ test_client_cache(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
/** Test that we can store HS descriptors in the client HS cache. */
|
||||
static void
|
||||
test_client_cache_decrypt(void *arg)
|
||||
{
|
||||
int ret;
|
||||
char *desc_encoded = NULL;
|
||||
uint8_t descriptor_cookie[HS_DESC_DESCRIPTOR_COOKIE_LEN];
|
||||
curve25519_keypair_t client_kp;
|
||||
ed25519_keypair_t service_kp;
|
||||
hs_descriptor_t *desc = NULL;
|
||||
const hs_descriptor_t *search_desc;
|
||||
const char *search_desc_encoded;
|
||||
|
||||
(void) arg;
|
||||
|
||||
/* Initialize HSDir cache subsystem */
|
||||
hs_init();
|
||||
|
||||
MOCK(networkstatus_get_live_consensus,
|
||||
mock_networkstatus_get_live_consensus);
|
||||
|
||||
/* Set consensus time */
|
||||
parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
|
||||
&mock_ns.valid_after);
|
||||
parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
|
||||
&mock_ns.fresh_until);
|
||||
parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC",
|
||||
&mock_ns.valid_until);
|
||||
|
||||
/* Generate a valid descriptor with normal values. */
|
||||
{
|
||||
ret = ed25519_keypair_generate(&service_kp, 0);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
ret = curve25519_keypair_generate(&client_kp, 0);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
crypto_rand((char *) descriptor_cookie, sizeof(descriptor_cookie));
|
||||
|
||||
desc = hs_helper_build_hs_desc_with_client_auth(descriptor_cookie,
|
||||
&client_kp.pubkey,
|
||||
&service_kp);
|
||||
tt_assert(desc);
|
||||
ret = hs_desc_encode_descriptor(desc, &service_kp, descriptor_cookie,
|
||||
&desc_encoded);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
}
|
||||
|
||||
/* Put it in the cache. Should not be decrypted since the client
|
||||
* authorization creds were not added to the global map. */
|
||||
ret = hs_cache_store_as_client(desc_encoded, &service_kp.pubkey);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_NEED_CLIENT_AUTH);
|
||||
|
||||
/* We should not be able to decrypt anything. */
|
||||
ret = hs_cache_client_new_auth_parse(&service_kp.pubkey);
|
||||
tt_int_op(ret, OP_EQ, false);
|
||||
|
||||
/* Add client auth to global map. */
|
||||
hs_helper_add_client_auth(&service_kp.pubkey, &client_kp.seckey);
|
||||
|
||||
/* We should not be able to decrypt anything. */
|
||||
ret = hs_cache_client_new_auth_parse(&service_kp.pubkey);
|
||||
tt_int_op(ret, OP_EQ, true);
|
||||
|
||||
/* Lookup the cache to make sure it is usable and there. */
|
||||
search_desc = hs_cache_lookup_as_client(&service_kp.pubkey);
|
||||
tt_assert(search_desc);
|
||||
search_desc_encoded = hs_cache_lookup_encoded_as_client(&service_kp.pubkey);
|
||||
tt_mem_op(search_desc_encoded, OP_EQ, desc_encoded, strlen(desc_encoded));
|
||||
|
||||
done:
|
||||
hs_descriptor_free(desc);
|
||||
tor_free(desc_encoded);
|
||||
|
||||
hs_free_all();
|
||||
|
||||
UNMOCK(networkstatus_get_live_consensus);
|
||||
}
|
||||
|
||||
struct testcase_t hs_cache[] = {
|
||||
/* Encoding tests. */
|
||||
{ "directory", test_directory, TT_FORK,
|
||||
@@ -579,6 +657,8 @@ struct testcase_t hs_cache[] = {
|
||||
NULL, NULL },
|
||||
{ "client_cache", test_client_cache, TT_FORK,
|
||||
NULL, NULL },
|
||||
{ "client_cache_decrypt", test_client_cache_decrypt, TT_FORK,
|
||||
NULL, NULL },
|
||||
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
||||
+146
-8
@@ -25,6 +25,7 @@
|
||||
#include "app/config/config.h"
|
||||
#include "lib/crypt_ops/crypto_cipher.h"
|
||||
#include "lib/crypt_ops/crypto_dh.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "core/or/channeltls.h"
|
||||
#include "feature/dircommon/directory.h"
|
||||
#include "core/mainloop/mainloop.h"
|
||||
@@ -92,6 +93,24 @@ helper_config_client(const char *conf, int validate_only)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
helper_add_random_client_auth(const ed25519_public_key_t *service_pk)
|
||||
{
|
||||
char *conf = NULL;
|
||||
#define conf_fmt "ClientOnionAuthDir %s\n"
|
||||
tor_asprintf(&conf, conf_fmt, get_fname("auth_keys"));
|
||||
#undef conf_fmt
|
||||
helper_config_client(conf, 0);
|
||||
tor_free(conf);
|
||||
|
||||
digest256map_t *client_auths = get_hs_client_auths_map();
|
||||
hs_client_service_authorization_t *auth =
|
||||
tor_malloc_zero(sizeof(hs_client_service_authorization_t));
|
||||
curve25519_secret_key_generate(&auth->enc_seckey, 0);
|
||||
hs_build_address(service_pk, HS_VERSION_THREE, auth->onion_address);
|
||||
digest256map_set(client_auths, service_pk->pubkey, auth);
|
||||
}
|
||||
|
||||
/* Test helper function: Setup a circuit and a stream with the same hidden
|
||||
* service destination, and put them in <b>circ_out</b> and
|
||||
* <b>conn_out</b>. Make the stream wait for circuits to be established to the
|
||||
@@ -393,7 +412,8 @@ test_client_pick_intro(void *arg)
|
||||
tt_assert(encoded);
|
||||
|
||||
/* store it */
|
||||
hs_cache_store_as_client(encoded, &service_kp.pubkey);
|
||||
ret = hs_cache_store_as_client(encoded, &service_kp.pubkey);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
||||
|
||||
/* fetch it to make sure it works */
|
||||
const hs_descriptor_t *fetched_desc =
|
||||
@@ -549,6 +569,17 @@ mock_connection_mark_unattached_ap_(entry_connection_t *conn, int endreason,
|
||||
conn->edge_.base_.marked_for_close = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
mock_connection_mark_unattached_ap_no_close(entry_connection_t *conn,
|
||||
int endreason, int line,
|
||||
const char *file)
|
||||
{
|
||||
(void) conn;
|
||||
(void) endreason;
|
||||
(void) line;
|
||||
(void) file;
|
||||
}
|
||||
|
||||
static void
|
||||
test_descriptor_fetch(void *arg)
|
||||
{
|
||||
@@ -824,6 +855,7 @@ test_desc_has_arrived_cleanup(void *arg)
|
||||
ed25519_keypair_t signing_kp;
|
||||
entry_connection_t *socks1 = NULL, *socks2 = NULL;
|
||||
hs_ident_dir_conn_t hs_dir_ident;
|
||||
dir_connection_t *dir_conn = NULL;
|
||||
|
||||
(void) arg;
|
||||
|
||||
@@ -852,7 +884,7 @@ test_desc_has_arrived_cleanup(void *arg)
|
||||
|
||||
/* Store in the client cache. */
|
||||
ret = hs_cache_store_as_client(desc_str, &signing_kp.pubkey);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
||||
cached_desc = hs_cache_lookup_as_client(&signing_kp.pubkey);
|
||||
tt_assert(cached_desc);
|
||||
hs_helper_desc_equal(desc, cached_desc);
|
||||
@@ -880,9 +912,11 @@ test_desc_has_arrived_cleanup(void *arg)
|
||||
* SOCKS connection to be ended with a resolved failed. */
|
||||
hs_ident_dir_conn_init(&signing_kp.pubkey,
|
||||
&desc->plaintext_data.blinded_pubkey, &hs_dir_ident);
|
||||
hs_client_desc_has_arrived(&hs_dir_ident);
|
||||
dir_conn = dir_connection_new(AF_INET);
|
||||
dir_conn->hs_ident = hs_ident_dir_conn_dup(&hs_dir_ident);
|
||||
hs_client_dir_fetch_done(dir_conn, "A reason", desc_str, 200);
|
||||
connection_free_minimal(TO_CONN(dir_conn));
|
||||
tt_int_op(socks1->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED);
|
||||
/* XXX: MUST work with OP_EQ. */
|
||||
tt_int_op(socks2->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED);
|
||||
|
||||
/* Now let say tor cleans up the intro state cache which resets all intro
|
||||
@@ -891,7 +925,6 @@ test_desc_has_arrived_cleanup(void *arg)
|
||||
|
||||
/* Retrying all SOCKS which should basically do nothing since we don't have
|
||||
* any pending SOCKS connection in AP_CONN_STATE_RENDDESC_WAIT state. */
|
||||
/* XXX: BUG() is triggered here, shouldn't if socks2 wasn't alive. */
|
||||
retry_all_socks_conn_waiting_for_desc();
|
||||
|
||||
done:
|
||||
@@ -953,7 +986,7 @@ test_close_intro_circuits_new_desc(void *arg)
|
||||
|
||||
/* Store it */
|
||||
ret = hs_cache_store_as_client(encoded, &service_kp.pubkey);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
||||
tor_free(encoded);
|
||||
tt_assert(hs_cache_lookup_as_client(&service_kp.pubkey));
|
||||
}
|
||||
@@ -988,8 +1021,8 @@ test_close_intro_circuits_new_desc(void *arg)
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_assert(encoded);
|
||||
|
||||
hs_cache_store_as_client(encoded, &service_kp.pubkey);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
ret = hs_cache_store_as_client(encoded, &service_kp.pubkey);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
||||
tor_free(encoded);
|
||||
tt_assert(hs_cache_lookup_as_client(&service_kp.pubkey));
|
||||
}
|
||||
@@ -1091,6 +1124,108 @@ test_close_intro_circuits_cache_clean(void *arg)
|
||||
UNMOCK(networkstatus_get_live_consensus);
|
||||
}
|
||||
|
||||
static void
|
||||
test_socks_hs_errors(void *arg)
|
||||
{
|
||||
int ret;
|
||||
char *desc_encoded = NULL;
|
||||
ed25519_keypair_t service_kp;
|
||||
ed25519_keypair_t signing_kp;
|
||||
entry_connection_t *socks_conn = NULL;
|
||||
dir_connection_t *dir_conn = NULL;
|
||||
hs_descriptor_t *desc = NULL;
|
||||
uint8_t descriptor_cookie[HS_DESC_DESCRIPTOR_COOKIE_LEN];
|
||||
|
||||
(void) arg;
|
||||
|
||||
MOCK(networkstatus_get_live_consensus,
|
||||
mock_networkstatus_get_live_consensus);
|
||||
MOCK(connection_mark_unattached_ap_,
|
||||
mock_connection_mark_unattached_ap_no_close);
|
||||
MOCK(read_file_to_str, mock_read_file_to_str);
|
||||
MOCK(tor_listdir, mock_tor_listdir);
|
||||
MOCK(check_private_dir, mock_check_private_dir);
|
||||
|
||||
/* Set consensus time */
|
||||
parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
|
||||
&mock_ns.valid_after);
|
||||
parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
|
||||
&mock_ns.fresh_until);
|
||||
parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC",
|
||||
&mock_ns.valid_until);
|
||||
|
||||
hs_init();
|
||||
|
||||
ret = ed25519_keypair_generate(&service_kp, 0);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
ret = ed25519_keypair_generate(&signing_kp, 0);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
|
||||
socks_conn = helper_build_socks_connection(&service_kp.pubkey,
|
||||
AP_CONN_STATE_RENDDESC_WAIT);
|
||||
tt_assert(socks_conn);
|
||||
|
||||
/* Create directory connection. */
|
||||
dir_conn = dir_connection_new(AF_INET);
|
||||
dir_conn->hs_ident = tor_malloc_zero(sizeof(hs_ident_dir_conn_t));
|
||||
TO_CONN(dir_conn)->purpose = DIR_PURPOSE_FETCH_HSDESC;
|
||||
ed25519_pubkey_copy(&dir_conn->hs_ident->identity_pk, &service_kp.pubkey);
|
||||
|
||||
/* Encode descriptor so we can decode it. */
|
||||
desc = hs_helper_build_hs_desc_with_ip(&service_kp);
|
||||
tt_assert(desc);
|
||||
|
||||
crypto_rand((char *) descriptor_cookie, sizeof(descriptor_cookie));
|
||||
ret = hs_desc_encode_descriptor(desc, &service_kp, descriptor_cookie,
|
||||
&desc_encoded);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_assert(desc_encoded);
|
||||
|
||||
/* Try decoding. Point this to an existing descriptor. The following should
|
||||
* fail thus the desc_out should be set to NULL. */
|
||||
hs_descriptor_t *desc_out = desc;
|
||||
ret = hs_client_decode_descriptor(desc_encoded, &service_kp.pubkey,
|
||||
&desc_out);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_NEED_CLIENT_AUTH);
|
||||
tt_assert(desc_out == NULL);
|
||||
|
||||
/* The caching will fail to decrypt because the descriptor_cookie used above
|
||||
* is not known to the HS subsystem. This will lead to a missing client
|
||||
* auth. */
|
||||
hs_client_dir_fetch_done(dir_conn, "Reason", desc_encoded, 200);
|
||||
|
||||
tt_int_op(socks_conn->socks_request->socks_extended_error_code, OP_EQ,
|
||||
SOCKS5_HS_MISSING_CLIENT_AUTH);
|
||||
|
||||
/* Add in the global client auth list bad creds for this service. */
|
||||
helper_add_random_client_auth(&service_kp.pubkey);
|
||||
|
||||
ret = hs_client_decode_descriptor(desc_encoded, &service_kp.pubkey,
|
||||
&desc_out);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_BAD_CLIENT_AUTH);
|
||||
tt_assert(desc_out == NULL);
|
||||
|
||||
/* Simmulate a fetch done again. This should replace the cached descriptor
|
||||
* and signal a bad client authorization. */
|
||||
hs_client_dir_fetch_done(dir_conn, "Reason", desc_encoded, 200);
|
||||
tt_int_op(socks_conn->socks_request->socks_extended_error_code, OP_EQ,
|
||||
SOCKS5_HS_BAD_CLIENT_AUTH);
|
||||
|
||||
done:
|
||||
connection_free_minimal(ENTRY_TO_CONN(socks_conn));
|
||||
connection_free_minimal(TO_CONN(dir_conn));
|
||||
hs_descriptor_free(desc);
|
||||
tor_free(desc_encoded);
|
||||
|
||||
hs_free_all();
|
||||
|
||||
UNMOCK(networkstatus_get_live_consensus);
|
||||
UNMOCK(connection_mark_unattached_ap_);
|
||||
UNMOCK(read_file_to_str);
|
||||
UNMOCK(tor_listdir);
|
||||
UNMOCK(check_private_dir);
|
||||
}
|
||||
|
||||
struct testcase_t hs_client_tests[] = {
|
||||
{ "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy,
|
||||
TT_FORK, NULL, NULL },
|
||||
@@ -1113,5 +1248,8 @@ struct testcase_t hs_client_tests[] = {
|
||||
{ "close_intro_circuits_cache_clean", test_close_intro_circuits_cache_clean,
|
||||
TT_FORK, NULL, NULL },
|
||||
|
||||
/* SOCKS5 Extended Error Code. */
|
||||
{ "socks_hs_errors", test_socks_hs_errors, TT_FORK, NULL, NULL },
|
||||
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
||||
+34
-11
@@ -6,6 +6,7 @@
|
||||
* \brief Test hidden service common functionalities.
|
||||
*/
|
||||
|
||||
#define CONNECTION_EDGE_PRIVATE
|
||||
#define HS_COMMON_PRIVATE
|
||||
#define HS_CLIENT_PRIVATE
|
||||
#define HS_SERVICE_PRIVATE
|
||||
@@ -778,6 +779,7 @@ static void
|
||||
test_parse_extended_hostname(void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
hostname_type_t type;
|
||||
|
||||
char address1[] = "fooaddress.onion";
|
||||
char address2[] = "aaaaaaaaaaaaaaaa.onion";
|
||||
@@ -788,21 +790,42 @@ test_parse_extended_hostname(void *arg)
|
||||
char address7[] = ".abcdefghijklmnop.onion";
|
||||
char address8[] =
|
||||
"www.25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid.onion";
|
||||
char address9[] =
|
||||
"www.15njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid.onion";
|
||||
|
||||
tt_assert(BAD_HOSTNAME == parse_extended_hostname(address1));
|
||||
tt_assert(ONION_V2_HOSTNAME == parse_extended_hostname(address2));
|
||||
tt_str_op(address2,OP_EQ, "aaaaaaaaaaaaaaaa");
|
||||
tt_assert(EXIT_HOSTNAME == parse_extended_hostname(address3));
|
||||
tt_assert(NORMAL_HOSTNAME == parse_extended_hostname(address4));
|
||||
tt_assert(ONION_V2_HOSTNAME == parse_extended_hostname(address5));
|
||||
tt_str_op(address5,OP_EQ, "abcdefghijklmnop");
|
||||
tt_assert(ONION_V2_HOSTNAME == parse_extended_hostname(address6));
|
||||
tt_str_op(address6,OP_EQ, "abcdefghijklmnop");
|
||||
tt_assert(BAD_HOSTNAME == parse_extended_hostname(address7));
|
||||
tt_assert(ONION_V3_HOSTNAME == parse_extended_hostname(address8));
|
||||
tt_assert(!parse_extended_hostname(address1, &type));
|
||||
tt_int_op(type, OP_EQ, BAD_HOSTNAME);
|
||||
|
||||
tt_assert(parse_extended_hostname(address2, &type));
|
||||
tt_int_op(type, OP_EQ, ONION_V2_HOSTNAME);
|
||||
tt_str_op(address2, OP_EQ, "aaaaaaaaaaaaaaaa");
|
||||
|
||||
tt_assert(parse_extended_hostname(address3, &type));
|
||||
tt_int_op(type, OP_EQ, EXIT_HOSTNAME);
|
||||
|
||||
tt_assert(parse_extended_hostname(address4, &type));
|
||||
tt_int_op(type, OP_EQ, NORMAL_HOSTNAME);
|
||||
|
||||
tt_assert(parse_extended_hostname(address5, &type));
|
||||
tt_int_op(type, OP_EQ, ONION_V2_HOSTNAME);
|
||||
tt_str_op(address5, OP_EQ, "abcdefghijklmnop");
|
||||
|
||||
tt_assert(parse_extended_hostname(address6, &type));
|
||||
tt_int_op(type, OP_EQ, ONION_V2_HOSTNAME);
|
||||
tt_str_op(address6, OP_EQ, "abcdefghijklmnop");
|
||||
|
||||
tt_assert(!parse_extended_hostname(address7, &type));
|
||||
tt_int_op(type, OP_EQ, BAD_HOSTNAME);
|
||||
|
||||
tt_assert(parse_extended_hostname(address8, &type));
|
||||
tt_int_op(type, OP_EQ, ONION_V3_HOSTNAME);
|
||||
tt_str_op(address8, OP_EQ,
|
||||
"25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid");
|
||||
|
||||
/* Invalid v3 address. */
|
||||
tt_assert(!parse_extended_hostname(address9, &type));
|
||||
tt_int_op(type, OP_EQ, ONION_V3_HOSTNAME);
|
||||
|
||||
done: ;
|
||||
}
|
||||
|
||||
|
||||
@@ -235,14 +235,14 @@ test_decode_descriptor(void *arg)
|
||||
/* Give some bad stuff to the decoding function. */
|
||||
ret = hs_desc_decode_descriptor("hladfjlkjadf", subcredential,
|
||||
NULL, &decoded);
|
||||
tt_int_op(ret, OP_EQ, -1);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR);
|
||||
|
||||
ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &encoded);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
||||
tt_assert(encoded);
|
||||
|
||||
ret = hs_desc_decode_descriptor(encoded, subcredential, NULL, &decoded);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
||||
tt_assert(decoded);
|
||||
|
||||
hs_helper_desc_equal(desc, decoded);
|
||||
@@ -263,7 +263,7 @@ test_decode_descriptor(void *arg)
|
||||
tt_assert(encoded);
|
||||
hs_descriptor_free(decoded);
|
||||
ret = hs_desc_decode_descriptor(encoded, subcredential, NULL, &decoded);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
||||
tt_assert(decoded);
|
||||
}
|
||||
|
||||
@@ -317,21 +317,21 @@ test_decode_descriptor(void *arg)
|
||||
hs_descriptor_free(decoded);
|
||||
ret = hs_desc_decode_descriptor(encoded, subcredential,
|
||||
NULL, &decoded);
|
||||
tt_int_op(ret, OP_LT, 0);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_NEED_CLIENT_AUTH);
|
||||
tt_assert(!decoded);
|
||||
|
||||
/* If we have an invalid client secret key, the decoding must fail. */
|
||||
hs_descriptor_free(decoded);
|
||||
ret = hs_desc_decode_descriptor(encoded, subcredential,
|
||||
&invalid_client_kp.seckey, &decoded);
|
||||
tt_int_op(ret, OP_LT, 0);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_BAD_CLIENT_AUTH);
|
||||
tt_assert(!decoded);
|
||||
|
||||
/* If we have the client secret key, the decoding must succeed and the
|
||||
* decoded descriptor must be correct. */
|
||||
ret = hs_desc_decode_descriptor(encoded, subcredential,
|
||||
&client_kp.seckey, &decoded);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
||||
tt_assert(decoded);
|
||||
|
||||
hs_helper_desc_equal(desc, decoded);
|
||||
@@ -567,7 +567,7 @@ test_decode_bad_signature(void *arg)
|
||||
|
||||
setup_full_capture_of_logs(LOG_WARN);
|
||||
ret = hs_desc_decode_plaintext(HS_DESC_BAD_SIG, &desc_plaintext);
|
||||
tt_int_op(ret, OP_EQ, -1);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR);
|
||||
expect_log_msg_containing("Malformed signature line. Rejecting.");
|
||||
teardown_capture_of_logs();
|
||||
|
||||
@@ -607,14 +607,14 @@ test_decode_plaintext(void *arg)
|
||||
tor_asprintf(&plaintext, template, bad_value, "180", "42", "MESSAGE");
|
||||
ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext);
|
||||
tor_free(plaintext);
|
||||
tt_int_op(ret, OP_EQ, -1);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR);
|
||||
}
|
||||
|
||||
/* Missing fields. */
|
||||
{
|
||||
const char *plaintext = "hs-descriptor 3\n";
|
||||
ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext);
|
||||
tt_int_op(ret, OP_EQ, -1);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR);
|
||||
}
|
||||
|
||||
/* Max length. */
|
||||
@@ -627,7 +627,7 @@ test_decode_plaintext(void *arg)
|
||||
plaintext[big - 1] = '\0';
|
||||
ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext);
|
||||
tor_free(plaintext);
|
||||
tt_int_op(ret, OP_EQ, -1);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR);
|
||||
}
|
||||
|
||||
/* Bad lifetime value. */
|
||||
@@ -636,7 +636,7 @@ test_decode_plaintext(void *arg)
|
||||
tor_asprintf(&plaintext, template, "3", bad_value, "42", "MESSAGE");
|
||||
ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext);
|
||||
tor_free(plaintext);
|
||||
tt_int_op(ret, OP_EQ, -1);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR);
|
||||
}
|
||||
|
||||
/* Huge lifetime value. */
|
||||
@@ -645,7 +645,7 @@ test_decode_plaintext(void *arg)
|
||||
tor_asprintf(&plaintext, template, "3", "7181615", "42", "MESSAGE");
|
||||
ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext);
|
||||
tor_free(plaintext);
|
||||
tt_int_op(ret, OP_EQ, -1);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR);
|
||||
}
|
||||
|
||||
/* Invalid encrypted section. */
|
||||
@@ -654,7 +654,7 @@ test_decode_plaintext(void *arg)
|
||||
tor_asprintf(&plaintext, template, "3", "180", "42", bad_value);
|
||||
ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext);
|
||||
tor_free(plaintext);
|
||||
tt_int_op(ret, OP_EQ, -1);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR);
|
||||
}
|
||||
|
||||
/* Invalid revision counter. */
|
||||
@@ -663,7 +663,7 @@ test_decode_plaintext(void *arg)
|
||||
tor_asprintf(&plaintext, template, "3", "180", bad_value, "MESSAGE");
|
||||
ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext);
|
||||
tor_free(plaintext);
|
||||
tt_int_op(ret, OP_EQ, -1);
|
||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR);
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
Reference in New Issue
Block a user