diff options
author | George Kadianakis <desnacked@riseup.net> | 2019-05-30 15:58:20 +0300 |
---|---|---|
committer | George Kadianakis <desnacked@riseup.net> | 2019-11-18 19:16:01 +0200 |
commit | 00fdaaee1e3cdfe40230a866c497d3648c43940c (patch) | |
tree | 684128731393d2cd563098e9290d286313d0b0ed /src/feature | |
parent | d28b6792cb99f25d42607eb46985ac4553013dd8 (diff) | |
download | tor-00fdaaee1e3cdfe40230a866c497d3648c43940c.tar.gz tor-00fdaaee1e3cdfe40230a866c497d3648c43940c.zip |
control-port: Implement ONION_CLIENT_AUTH_ADD.
Diffstat (limited to 'src/feature')
-rw-r--r-- | src/feature/control/control_cmd.c | 3 | ||||
-rw-r--r-- | src/feature/control/control_hs.c | 161 | ||||
-rw-r--r-- | src/feature/control/control_hs.h | 24 | ||||
-rw-r--r-- | src/feature/hs/hs_client.c | 42 | ||||
-rw-r--r-- | src/feature/hs/hs_client.h | 31 |
5 files changed, 253 insertions, 8 deletions
diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c index e1a8292c9f..3c16722e15 100644 --- a/src/feature/control/control_cmd.c +++ b/src/feature/control/control_cmd.c @@ -26,6 +26,7 @@ #include "feature/control/control.h" #include "feature/control/control_auth.h" #include "feature/control/control_cmd.h" +#include "feature/control/control_hs.h" #include "feature/control/control_events.h" #include "feature/control/control_getinfo.h" #include "feature/control/control_proto.h" @@ -1970,6 +1971,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, decoded_key->v2 = pk; *hs_version = HS_VERSION_TWO; } else if (!strcasecmp(key_type_ed25519_v3, key_type)) { + /* parsing of private ed25519 key */ /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */ ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk)); if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob, @@ -2317,6 +2319,7 @@ static const control_cmd_def_t CONTROL_COMMANDS[] = MULTLINE(hspost, 0), ONE_LINE(add_onion, CMD_FL_WIPE), ONE_LINE(del_onion, CMD_FL_WIPE), + ONE_LINE(onion_client_auth_add, CMD_FL_WIPE), }; /** diff --git a/src/feature/control/control_hs.c b/src/feature/control/control_hs.c new file mode 100644 index 0000000000..5db8a0f007 --- /dev/null +++ b/src/feature/control/control_hs.c @@ -0,0 +1,161 @@ +/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file control_hs.c + * + * \brief Implement commands for Tor's control-socket interface that are + * related to onion services. + **/ + +#include "core/or/or.h" +#include "feature/control/control_cmd.h" +#include "feature/control/control_hs.h" +#include "feature/control/control_proto.h" +#include "feature/hs/hs_client.h" +#include "lib/encoding/confline.h" + +#include "feature/control/control_cmd_args_st.h" + +/** Parse the 'KeyType ":" PrivateKey' from <b>client_privkey_str</b> and store + * it into <b>privkey</b>. Use <b>conn</b> to output any errors if needed. + * + * Return 0 if all went well, -1 otherwise. */ +static int +parse_private_key_from_control_port(const char *client_privkey_str, + curve25519_secret_key_t *privkey, + control_connection_t *conn) +{ + int retval = -1; + smartlist_t *key_args = smartlist_new(); + + tor_assert(privkey); + + smartlist_split_string(key_args, client_privkey_str, ":", + SPLIT_IGNORE_BLANK, 0); + if (smartlist_len(key_args) != 2) { + control_printf_endreply(conn, 512, "Invalid key type/blob"); + goto err; + } + + const char *key_type = smartlist_get(key_args, 0); + const char *key_blob = smartlist_get(key_args, 1); + + if (strcasecmp(key_type, "x25519")) { + control_printf_endreply(conn, 552, + "Unrecognized key type \"%s\"", key_type); + goto err; + } + + if (base64_decode((char*)privkey->secret_key, sizeof(privkey->secret_key), + key_blob, + strlen(key_blob)) != sizeof(privkey->secret_key)) { + control_printf_endreply(conn, 512, "Failed to decode ED25519-V3 key"); + goto err; + } + + retval = 0; + + err: + SMARTLIST_FOREACH(key_args, char *, c, tor_free(c)); + smartlist_free(key_args); + return retval; +} + +/** Syntax details for ONION_CLIENT_AUTH_ADD */ +const control_cmd_syntax_t onion_client_auth_add_syntax = { + .max_args = 2, + .accept_keywords = true, +}; + +/** Called when we get an ONION_CLIENT_AUTH_ADD command; parse the body, and + * register the new client-side client auth credentials: + * "ONION_CLIENT_AUTH_ADD" SP HSAddress + * SP KeyType ":" PrivateKeyBlob + * [SP "ClientName=" Nickname] + * [SP "Type=" TYPE] CRLF + */ +int +handle_control_onion_client_auth_add(control_connection_t *conn, + const control_cmd_args_t *args) +{ + int retval = -1; + smartlist_t *flags = smartlist_new(); + hs_client_service_authorization_t *creds = NULL; + + tor_assert(args); + + int argc = smartlist_len(args->args); + /* We need at least 'HSAddress' and 'PrivateKeyBlob' */ + if (argc < 2) { + control_printf_endreply(conn, 512, + "Incomplete ONION_CLIENT_AUTH_ADD command"); + goto err; + } + + creds = tor_malloc_zero(sizeof(hs_client_service_authorization_t)); + + const char *hsaddress = smartlist_get(args->args, 0); + if (!hs_address_is_valid(hsaddress)) { + control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress); + goto err; + } + strlcpy(creds->onion_address, hsaddress, sizeof(creds->onion_address)); + + /* Parse the client private key */ + const char *client_privkey = smartlist_get(args->args, 1); + if (parse_private_key_from_control_port(client_privkey, + &creds->enc_seckey, conn) < 0) { + goto err; + } + + /* Now let's parse the remaining arguments (variable size) */ + for (const config_line_t *line = args->kwargs; line; line = line->next) { + if (!strcasecmp(line->key, "ClientName")) { + /* XXX apply length restriction? */ + creds->nickname = tor_strdup(line->value); + + } else if (!strcasecmpstart(line->key, "Flags")) { + smartlist_split_string(flags, line->value, ",", SPLIT_IGNORE_BLANK, 0); + if (smartlist_len(flags) < 1) { + control_write_endreply(conn, 512, "Invalid 'Flags' argument"); + goto err; + } + SMARTLIST_FOREACH_BEGIN(flags, const char *, flag) { + if (!strcasecmp(flag, "Permanent")) { + creds->flags |= CLIENT_AUTH_FLAG_IS_PERMANENT; + } else { + control_printf_endreply(conn, 512, "Invalid 'Flags' argument: %s", + escaped(flag)); + goto err; + } + } SMARTLIST_FOREACH_END(flag); + } + } + + hs_client_register_auth_status_t register_status; + /* Register the credential (register func takes ownership of cred.) */ + register_status = hs_client_register_auth_credentials(creds); + if (BUG(register_status == REGISTER_FAIL_BAD_ADDRESS)) { + /* It's a bug because the service addr has already been validated above */ + control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"", hsaddress); + } else if (register_status == REGISTER_FAIL_ALREADY_EXISTS) { + control_printf_endreply(conn, 551, "Client already exists"); + } else if (register_status == REGISTER_SUCCESS) { + control_printf_endreply(conn, 250, "OK"); + } else { + tor_assert_nonfatal_unreached(); + } + + retval = 0; + goto done; + + err: + client_service_authorization_free(creds); + + done: + SMARTLIST_FOREACH(flags, char *, s, tor_free(s)); + smartlist_free(flags); + return retval; +} diff --git a/src/feature/control/control_hs.h b/src/feature/control/control_hs.h new file mode 100644 index 0000000000..1fcd7de36c --- /dev/null +++ b/src/feature/control/control_hs.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file control_hs.c + * + * \brief Header file for control_hs.c. + **/ + +#ifndef TOR_CONTROL_HS_H +#define TOR_CONTROL_HS_H + +struct control_cmd_syntax_t; + +extern const char *onion_client_auth_add_keywords[]; +extern const struct control_cmd_syntax_t onion_client_auth_add_syntax; + +int +handle_control_onion_client_auth_add(control_connection_t *conn, + const control_cmd_args_t *args); + +#endif + diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c index 0d7c48dd84..7b981908db 100644 --- a/src/feature/hs/hs_client.c +++ b/src/feature/hs/hs_client.c @@ -1445,6 +1445,32 @@ client_dir_fetch_unexpected(dir_connection_t *dir_conn, const char *reason, NULL); } +/** Register the credential <b>creds</b> as part of the client auth subsystem. + * + * Takes ownership of <b>creds</b>. + **/ +hs_client_register_auth_status_t +hs_client_register_auth_credentials(hs_client_service_authorization_t *creds) +{ + ed25519_public_key_t service_identity_pk; + + tor_assert(creds); + + if (hs_parse_address(creds->onion_address, &service_identity_pk, + NULL, NULL) < 0) { + client_service_authorization_free(creds); + return REGISTER_FAIL_BAD_ADDRESS; + } + + if (digest256map_get(client_auths, service_identity_pk.pubkey)) { + client_service_authorization_free(creds); + return REGISTER_FAIL_ALREADY_EXISTS; + } + + digest256map_set(client_auths, service_identity_pk.pubkey, creds); + return REGISTER_SUCCESS; +} + /* ========== */ /* Public API */ /* ========== */ @@ -1669,16 +1695,18 @@ hs_client_receive_rendezvous_acked(origin_circuit_t *circ, return -1; } -#define client_service_authorization_free(auth) \ - FREE_AND_NULL(hs_client_service_authorization_t, \ - client_service_authorization_free_, (auth)) - -static void +void client_service_authorization_free_(hs_client_service_authorization_t *auth) { - if (auth) { - memwipe(auth, 0, sizeof(*auth)); + if (!auth) { + return; + } + + if (auth->nickname) { + tor_free(auth->nickname); } + + memwipe(auth, 0, sizeof(*auth)); tor_free(auth); } diff --git a/src/feature/hs/hs_client.h b/src/feature/hs/hs_client.h index 699ce60510..ea726e2370 100644 --- a/src/feature/hs/hs_client.h +++ b/src/feature/hs/hs_client.h @@ -31,7 +31,20 @@ typedef enum { HS_CLIENT_FETCH_PENDING = 5, } hs_client_fetch_status_t; -/** Client-side configuration of authorization for a service. */ +/* Status code of client auth credential registration */ +typedef enum { + /* We successfuly registered these credentials */ + REGISTER_SUCCESS, + /* We failed to register these credentials, because they already exist. */ + REGISTER_FAIL_ALREADY_EXISTS, + /* We failed to register these credentials, because of a bad HS address. */ + REGISTER_FAIL_BAD_ADDRESS, +} hs_client_register_auth_status_t; + +/** Flag to set when a client auth is permanent (saved on disk). */ +#define CLIENT_AUTH_FLAG_IS_PERMANENT (1<<0) + +/** Client-side configuration of client authorization */ typedef struct hs_client_service_authorization_t { /** An curve25519 secret key used to compute decryption keys that * allow the client to decrypt the hidden service descriptor. */ @@ -39,8 +52,24 @@ typedef struct hs_client_service_authorization_t { /** An onion address that is used to connect to the onion service. */ char onion_address[HS_SERVICE_ADDR_LEN_BASE32+1]; + + /* An optional nickname for this client */ + char *nickname; + + /* Optional flags for this client. */ + int flags; } hs_client_service_authorization_t; +hs_client_register_auth_status_t +hs_client_register_auth_credentials(hs_client_service_authorization_t *creds); + +#define client_service_authorization_free(auth) \ + FREE_AND_NULL(hs_client_service_authorization_t, \ + client_service_authorization_free_, (auth)) + +void +client_service_authorization_free_(hs_client_service_authorization_t *auth); + void hs_client_note_connection_attempt_succeeded( const edge_connection_t *conn); |