aboutsummaryrefslogtreecommitdiff
path: root/src/feature
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature')
-rw-r--r--src/feature/control/control_cmd.c79
-rw-r--r--src/feature/control/control_cmd.h9
-rw-r--r--src/feature/control/control_events.c3
-rw-r--r--src/feature/hs/hs_service.c77
-rw-r--r--src/feature/hs/hs_service.h18
-rw-r--r--src/feature/rend/rendcommon.c2
-rw-r--r--src/feature/rend/rendservice.c1
7 files changed, 148 insertions, 41 deletions
diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c
index 5b75c24692..32c87c6daa 100644
--- a/src/feature/control/control_cmd.c
+++ b/src/feature/control/control_cmd.c
@@ -33,6 +33,7 @@
#include "feature/control/control_getinfo.h"
#include "feature/control/control_proto.h"
#include "feature/hs/hs_control.h"
+#include "feature/hs/hs_service.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerinfo.h"
#include "feature/nodelist/routerlist.h"
@@ -1648,12 +1649,13 @@ handle_control_hspost(control_connection_t *conn,
* On success (RSAE_OKAY), the address_out points to a newly allocated string
* containing the onion address without the .onion part. On error, address_out
* is untouched. */
-static hs_service_add_ephemeral_status_t
+STATIC hs_service_add_ephemeral_status_t
add_onion_helper_add_service(int hs_version,
add_onion_secret_key_t *pk,
smartlist_t *port_cfgs, int max_streams,
int max_streams_close_circuit, int auth_type,
- smartlist_t *auth_clients, char **address_out)
+ smartlist_t *auth_clients,
+ smartlist_t *auth_clients_v3, char **address_out)
{
hs_service_add_ephemeral_status_t ret;
@@ -1669,7 +1671,8 @@ add_onion_helper_add_service(int hs_version,
break;
case HS_VERSION_THREE:
ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams,
- max_streams_close_circuit, address_out);
+ max_streams_close_circuit,
+ auth_clients_v3, address_out);
break;
default:
tor_assert_unreached();
@@ -1693,7 +1696,7 @@ get_detached_onion_services(void)
}
static const char *add_onion_keywords[] = {
- "Port", "Flags", "MaxStreams", "ClientAuth", NULL
+ "Port", "Flags", "MaxStreams", "ClientAuth", "ClientAuthV3", NULL
};
static const control_cmd_syntax_t add_onion_syntax = {
.min_args = 1, .max_args = 1,
@@ -1714,6 +1717,8 @@ handle_control_add_onion(control_connection_t *conn,
smartlist_t *port_cfgs = smartlist_new();
smartlist_t *auth_clients = NULL;
smartlist_t *auth_created_clients = NULL;
+ smartlist_t *auth_clients_v3 = NULL;
+ smartlist_t *auth_clients_v3_str = NULL;
int discard_pk = 0;
int detach = 0;
int max_streams = 0;
@@ -1758,6 +1763,7 @@ handle_control_add_onion(control_connection_t *conn,
static const char *detach_flag = "Detach";
static const char *max_s_close_flag = "MaxStreamsCloseCircuit";
static const char *basicauth_flag = "BasicAuth";
+ static const char *v3auth_flag = "V3Auth";
static const char *non_anonymous_flag = "NonAnonymous";
smartlist_t *flags = smartlist_new();
@@ -1778,6 +1784,8 @@ handle_control_add_onion(control_connection_t *conn,
max_streams_close_circuit = 1;
} else if (!strcasecmp(flag, basicauth_flag)) {
auth_type = REND_BASIC_AUTH;
+ } else if (!strcasecmp(flag, v3auth_flag)) {
+ auth_type = REND_V3_AUTH;
} else if (!strcasecmp(flag, non_anonymous_flag)) {
non_anonymous = 1;
} else {
@@ -1821,6 +1829,21 @@ handle_control_add_onion(control_connection_t *conn,
if (created) {
smartlist_add(auth_created_clients, client);
}
+ } else if (!strcasecmp(arg->key, "ClientAuthV3")) {
+ hs_service_authorized_client_t *client_v3 =
+ parse_authorized_client_key(arg->value, LOG_INFO);
+ if (!client_v3) {
+ control_write_endreply(conn, 512, "Cannot decode v3 client auth key");
+ goto out;
+ }
+
+ if (auth_clients_v3 == NULL) {
+ auth_clients_v3 = smartlist_new();
+ auth_clients_v3_str = smartlist_new();
+ }
+
+ smartlist_add(auth_clients_v3, client_v3);
+ smartlist_add(auth_clients_v3_str, tor_strdup(arg->value));
} else {
tor_assert_nonfatal_unreached();
goto out;
@@ -1829,10 +1852,12 @@ handle_control_add_onion(control_connection_t *conn,
if (smartlist_len(port_cfgs) == 0) {
control_write_endreply(conn, 512, "Missing 'Port' argument");
goto out;
- } else if (auth_type == REND_NO_AUTH && auth_clients != NULL) {
+ } else if (auth_type == REND_NO_AUTH &&
+ (auth_clients != NULL && auth_clients_v3 != NULL)) {
control_write_endreply(conn, 512, "No auth type specified");
goto out;
- } else if (auth_type != REND_NO_AUTH && auth_clients == NULL) {
+ } else if (auth_type != REND_NO_AUTH &&
+ (auth_clients == NULL && auth_clients_v3 == NULL)) {
control_write_endreply(conn, 512, "No auth clients specified");
goto out;
} else if ((auth_type == REND_BASIC_AUTH &&
@@ -1841,6 +1866,15 @@ handle_control_add_onion(control_connection_t *conn,
smartlist_len(auth_clients) > 16)) {
control_write_endreply(conn, 512, "Too many auth clients");
goto out;
+ } else if ((auth_type == REND_BASIC_AUTH ||
+ auth_type == REND_STEALTH_AUTH) && auth_clients_v3) {
+ control_write_endreply(conn, 512,
+ "ClientAuthV3 does not support basic or stealth auth");
+ goto out;
+ } else if (auth_type == REND_V3_AUTH && auth_clients) {
+ control_write_endreply(conn, 512, "ClientAuth does not support v3 auth");
+ goto out;
+
} else if (non_anonymous != rend_service_non_anonymous_mode_enabled(
get_options())) {
/* If we failed, and the non-anonymous flag is set, Tor must be in
@@ -1869,12 +1903,16 @@ handle_control_add_onion(control_connection_t *conn,
goto out;
}
- /* Hidden service version 3 don't have client authentication support so if
- * ClientAuth was given, send back an error. */
+ /* We can't mix ClientAuth and Version 3 Onion Services, or ClientAuthV3 and
+ * Version 2. If that's the case, send back an error. */
if (hs_version == HS_VERSION_THREE && auth_clients) {
control_write_endreply(conn, 513, "ClientAuth not supported");
goto out;
}
+ if (hs_version == HS_VERSION_TWO && auth_clients_v3) {
+ control_write_endreply(conn, 513, "ClientAuthV3 not supported");
+ goto out;
+ }
/* Create the HS, using private key pk, client authentication auth_type,
* the list of auth_clients, and port config port_cfg.
@@ -1882,12 +1920,13 @@ handle_control_add_onion(control_connection_t *conn,
* regardless of success/failure.
*/
char *service_id = NULL;
- int ret = add_onion_helper_add_service(hs_version, &pk, port_cfgs,
- max_streams,
- max_streams_close_circuit, auth_type,
- auth_clients, &service_id);
+ int ret =
+ add_onion_helper_add_service(hs_version, &pk, port_cfgs, max_streams,
+ max_streams_close_circuit, auth_type,
+ auth_clients, auth_clients_v3, &service_id);
port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
auth_clients = NULL; /* so is auth_clients */
+ auth_clients_v3 = NULL; /* so is auth_clients_v3 */
switch (ret) {
case RSAE_OKAY:
{
@@ -1919,6 +1958,11 @@ handle_control_add_onion(control_connection_t *conn,
tor_free(encoded);
});
}
+ if (auth_clients_v3_str) {
+ SMARTLIST_FOREACH(auth_clients_v3_str, char *, client_str, {
+ control_printf_midreply(conn, 250, "ClientAuthV3=%s", client_str);
+ });
+ }
send_control_done(conn);
break;
@@ -1956,6 +2000,17 @@ handle_control_add_onion(control_connection_t *conn,
rend_authorized_client_free(ac));
smartlist_free(auth_clients);
}
+ if (auth_clients_v3) {
+ SMARTLIST_FOREACH(auth_clients_v3, hs_service_authorized_client_t *, ac,
+ service_authorized_client_free(ac));
+ smartlist_free(auth_clients_v3);
+ }
+ if (auth_clients_v3_str) {
+ SMARTLIST_FOREACH(auth_clients_v3_str, char *, client_str,
+ tor_free(client_str));
+ smartlist_free(auth_clients_v3_str);
+ }
+
if (auth_created_clients) {
// Do not free entries; they are the same as auth_clients
smartlist_free(auth_created_clients);
diff --git a/src/feature/control/control_cmd.h b/src/feature/control/control_cmd.h
index 0ff0f0755f..b3c1d5cb2f 100644
--- a/src/feature/control/control_cmd.h
+++ b/src/feature/control/control_cmd.h
@@ -75,6 +75,7 @@ typedef struct control_cmd_syntax_t {
} control_cmd_syntax_t;
#ifdef CONTROL_CMD_PRIVATE
+#include "feature/hs/hs_service.h"
#include "lib/crypt_ops/crypto_ed25519.h"
/* ADD_ONION secret key to create an ephemeral service. The command supports
@@ -94,6 +95,14 @@ STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk,
int *hs_version,
control_connection_t *conn);
+STATIC hs_service_add_ephemeral_status_t add_onion_helper_add_service(
+ int hs_version,
+ add_onion_secret_key_t *pk,
+ smartlist_t *port_cfgs, int max_streams,
+ int max_streams_close_circuit, int auth_type,
+ smartlist_t *auth_clients,
+ smartlist_t *auth_clients_v3, char **address_out);
+
STATIC rend_authorized_client_t *add_onion_helper_clientauth(const char *arg,
int *created, control_connection_t *conn);
diff --git a/src/feature/control/control_events.c b/src/feature/control/control_events.c
index 0dd52659ec..c0ccb1eb26 100644
--- a/src/feature/control/control_events.c
+++ b/src/feature/control/control_events.c
@@ -1927,6 +1927,9 @@ rend_auth_type_to_string(rend_auth_type_t auth_type)
case REND_STEALTH_AUTH:
str = "STEALTH_AUTH";
break;
+ case REND_V3_AUTH:
+ str = "REND_V3_AUTH";
+ break;
default:
str = "UNKNOWN";
}
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index 07e3550986..f0c493d91d 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -1116,6 +1116,43 @@ client_filename_is_valid(const char *filename)
return ret;
}
+/** Parse an base32-encoded authorized client from a string.
+ *
+ * Return the key on success, return NULL, otherwise. */
+hs_service_authorized_client_t *
+parse_authorized_client_key(const char *key_str, int severity)
+{
+ hs_service_authorized_client_t *client = NULL;
+
+ /* We expect a specific length of the base64 encoded key so make sure we
+ * have that so we don't successfully decode a value with a different length
+ * and end up in trouble when copying the decoded key into a fixed length
+ * buffer. */
+ if (strlen(key_str) != BASE32_NOPAD_LEN(CURVE25519_PUBKEY_LEN)) {
+ log_fn(severity, LD_REND, "Client authorization encoded base32 public key "
+ "length is invalid: %s", key_str);
+ goto err;
+ }
+
+ client = tor_malloc_zero(sizeof(hs_service_authorized_client_t));
+ if (base32_decode((char *) client->client_pk.public_key,
+ sizeof(client->client_pk.public_key),
+ key_str, strlen(key_str)) !=
+ sizeof(client->client_pk.public_key)) {
+ log_fn(severity, LD_REND, "Client authorization public key cannot be "
+ "decoded: %s", key_str);
+ goto err;
+ }
+
+ return client;
+
+ err:
+ if (client != NULL) {
+ service_authorized_client_free(client);
+ }
+ return NULL;
+}
+
/** Parse an authorized client from a string. The format of a client string
* looks like (see rend-spec-v3.txt):
*
@@ -1162,23 +1199,7 @@ parse_authorized_client(const char *client_key_str)
goto err;
}
- /* We expect a specific length of the base32 encoded key so make sure we
- * have that so we don't successfully decode a value with a different length
- * and end up in trouble when copying the decoded key into a fixed length
- * buffer. */
- if (strlen(pubkey_b32) != BASE32_NOPAD_LEN(CURVE25519_PUBKEY_LEN)) {
- log_warn(LD_REND, "Client authorization encoded base32 public key "
- "length is invalid: %s", pubkey_b32);
- goto err;
- }
-
- client = tor_malloc_zero(sizeof(hs_service_authorized_client_t));
- if (base32_decode((char *) client->client_pk.public_key,
- sizeof(client->client_pk.public_key),
- pubkey_b32, strlen(pubkey_b32)) !=
- sizeof(client->client_pk.public_key)) {
- log_warn(LD_REND, "Client authorization public key cannot be decoded: %s",
- pubkey_b32);
+ if ((client = parse_authorized_client_key(pubkey_b32, LOG_WARN)) == NULL) {
goto err;
}
@@ -1302,7 +1323,7 @@ load_client_keys(hs_service_t *service)
}
/** Release all storage held in <b>client</b>. */
-STATIC void
+void
service_authorized_client_free_(hs_service_authorized_client_t *client)
{
if (!client) {
@@ -3682,15 +3703,17 @@ hs_service_upload_desc_to_dir(const char *encoded_desc,
/** Add the ephemeral service using the secret key sk and ports. Both max
* streams parameter will be set in the newly created service.
*
- * Ownership of sk and ports is passed to this routine. Regardless of
- * success/failure, callers should not touch these values after calling this
- * routine, and may assume that correct cleanup has been done on failure.
+ * Ownership of sk, ports, and auth_clients_v3 is passed to this routine.
+ * Regardless of success/failure, callers should not touch these values
+ * after calling this routine, and may assume that correct cleanup has
+ * been done on failure.
*
* Return an appropriate hs_service_add_ephemeral_status_t. */
hs_service_add_ephemeral_status_t
hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports,
int max_streams_per_rdv_circuit,
- int max_streams_close_circuit, char **address_out)
+ int max_streams_close_circuit,
+ smartlist_t *auth_clients_v3, char **address_out)
{
hs_service_add_ephemeral_status_t ret;
hs_service_t *service = NULL;
@@ -3734,6 +3757,16 @@ hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports,
goto err;
}
+ if (auth_clients_v3) {
+ service->config.clients = smartlist_new();
+ SMARTLIST_FOREACH(auth_clients_v3, hs_service_authorized_client_t *, c, {
+ if (c != NULL) {
+ smartlist_add(service->config.clients, c);
+ }
+ });
+ smartlist_free(auth_clients_v3);
+ }
+
/* Build the onion address for logging purposes but also the control port
* uses it for the HS_DESC event. */
hs_build_address(&service->keys.identity_pk,
diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h
index ec0e83f2c2..6a39dee037 100644
--- a/src/feature/hs/hs_service.h
+++ b/src/feature/hs/hs_service.h
@@ -372,7 +372,8 @@ char *hs_service_lookup_current_desc(const ed25519_public_key_t *pk);
hs_service_add_ephemeral_status_t
hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports,
int max_streams_per_rdv_circuit,
- int max_streams_close_circuit, char **address_out);
+ int max_streams_close_circuit,
+ smartlist_t *auth_clients_v3, char **address_out);
int hs_service_del_ephemeral(const char *address);
/* Used outside of the HS subsystem by the control port command HSPOST. */
@@ -388,6 +389,15 @@ hs_service_exports_circuit_id(const ed25519_public_key_t *pk);
void hs_service_dump_stats(int severity);
void hs_service_circuit_cleanup_on_close(const circuit_t *circ);
+hs_service_authorized_client_t *
+parse_authorized_client_key(const char *key_str, int severity);
+
+void
+service_authorized_client_free_(hs_service_authorized_client_t *client);
+#define service_authorized_client_free(c) \
+ FREE_AND_NULL(hs_service_authorized_client_t, \
+ service_authorized_client_free_, (c))
+
#ifdef HS_SERVICE_PRIVATE
#ifdef TOR_UNIT_TESTS
@@ -452,12 +462,6 @@ STATIC void service_descriptor_free_(hs_service_descriptor_t *desc);
FREE_AND_NULL(hs_service_descriptor_t, \
service_descriptor_free_, (d))
-STATIC void
-service_authorized_client_free_(hs_service_authorized_client_t *client);
-#define service_authorized_client_free(c) \
- FREE_AND_NULL(hs_service_authorized_client_t, \
- service_authorized_client_free_, (c))
-
STATIC int
write_address_to_file(const hs_service_t *service, const char *fname_);
diff --git a/src/feature/rend/rendcommon.c b/src/feature/rend/rendcommon.c
index 775d487805..f00dfee68a 100644
--- a/src/feature/rend/rendcommon.c
+++ b/src/feature/rend/rendcommon.c
@@ -548,6 +548,8 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
ipos = ipos_encrypted;
ipos_len = ipos_encrypted_len;
break;
+ case REND_V3_AUTH:
+ break; /* v3 service, break. */
default:
log_warn(LD_REND|LD_BUG, "Unrecognized authorization type %d",
(int)auth_type);
diff --git a/src/feature/rend/rendservice.c b/src/feature/rend/rendservice.c
index a2be900e2a..add25579b3 100644
--- a/src/feature/rend/rendservice.c
+++ b/src/feature/rend/rendservice.c
@@ -3818,6 +3818,7 @@ upload_service_descriptor(rend_service_t *service)
smartlist_clear(client_cookies);
switch (service->auth_type) {
case REND_NO_AUTH:
+ case REND_V3_AUTH:
/* Do nothing here. */
break;
case REND_BASIC_AUTH: