From 8bf0382b220b31605fb5a542f36a842bdd7a6ed0 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 16 Mar 2012 09:40:44 -0400 Subject: Skeleton ExtORPort implementation. Needs testing, documentation. Does not implement TransportControlPort yet. --- src/or/connection.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) (limited to 'src/or/connection.c') diff --git a/src/or/connection.c b/src/or/connection.c index 6a3cc7bec4..130b1ecefb 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -98,6 +98,7 @@ static smartlist_t *outgoing_addrs = NULL; #define CASE_ANY_LISTENER_TYPE \ case CONN_TYPE_OR_LISTENER: \ + case CONN_TYPE_EXT_OR_LISTENER: \ case CONN_TYPE_AP_LISTENER: \ case CONN_TYPE_DIR_LISTENER: \ case CONN_TYPE_CONTROL_LISTENER: \ @@ -129,6 +130,8 @@ conn_type_to_string(int type) case CONN_TYPE_CPUWORKER: return "CPU worker"; case CONN_TYPE_CONTROL_LISTENER: return "Control listener"; case CONN_TYPE_CONTROL: return "Control"; + case CONN_TYPE_EXT_OR: return "Extended OR"; + case CONN_TYPE_EXT_OR_LISTENER: return "Extended OR listener"; default: log_warn(LD_BUG, "unknown connection type %d", type); tor_snprintf(buf, sizeof(buf), "unknown [%d]", type); @@ -165,6 +168,12 @@ conn_state_to_string(int type, int state) case OR_CONN_STATE_OPEN: return "open"; } break; + case CONN_TYPE_EXT_OR: + switch (state) { + case EXT_OR_CONN_STATE_OPEN: return "open"; + case EXT_OR_CONN_STATE_FLUSHING: return "flushing final OKAY"; + } + break; case CONN_TYPE_EXIT: switch (state) { case EXIT_CONN_STATE_RESOLVING: return "waiting for dest info"; @@ -229,6 +238,7 @@ connection_type_uses_bufferevent(connection_t *conn) case CONN_TYPE_DIR: case CONN_TYPE_CONTROL: case CONN_TYPE_OR: + case CONN_TYPE_EXT_OR: case CONN_TYPE_CPUWORKER: return 1; default: @@ -259,14 +269,18 @@ dir_connection_new(int socket_family) * Set active_circuit_pqueue_last_recalibrated to current cell_ewma tick. */ or_connection_t * -or_connection_new(int socket_family) +or_connection_new(int type, int socket_family) { or_connection_t *or_conn = tor_malloc_zero(sizeof(or_connection_t)); time_t now = time(NULL); - connection_init(now, TO_CONN(or_conn), CONN_TYPE_OR, socket_family); + tor_assert(type == CONN_TYPE_OR || type == CONN_TYPE_EXT_OR); + connection_init(now, TO_CONN(or_conn), type, socket_family); or_conn->timestamp_last_added_nonpadding = time(NULL); + if (type == CONN_TYPE_EXT_OR) + connection_or_set_ext_or_identifier(or_conn); + return or_conn; } @@ -335,7 +349,8 @@ connection_new(int type, int socket_family) { switch (type) { case CONN_TYPE_OR: - return TO_CONN(or_connection_new(socket_family)); + case CONN_TYPE_EXT_OR: + return TO_CONN(or_connection_new(type, socket_family)); case CONN_TYPE_EXIT: return TO_CONN(edge_connection_new(type, socket_family)); @@ -377,6 +392,7 @@ connection_init(time_t now, connection_t *conn, int type, int socket_family) switch (type) { case CONN_TYPE_OR: + case CONN_TYPE_EXT_OR: conn->magic = OR_CONNECTION_MAGIC; break; case CONN_TYPE_EXIT: @@ -445,6 +461,7 @@ connection_free_(connection_t *conn) switch (conn->type) { case CONN_TYPE_OR: + case CONN_TYPE_EXT_OR: tor_assert(conn->magic == OR_CONNECTION_MAGIC); mem = TO_OR_CONN(conn); memlen = sizeof(or_connection_t); @@ -575,6 +592,9 @@ connection_free_(connection_t *conn) log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest"); connection_or_remove_from_identity_map(TO_OR_CONN(conn)); } + if (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR) + connection_or_remove_from_ext_or_id_map(TO_OR_CONN(conn)); + #ifdef USE_BUFFEREVENTS if (conn->type == CONN_TYPE_OR && TO_OR_CONN(conn)->bucket_cfg) { ev_token_bucket_cfg_free(TO_OR_CONN(conn)->bucket_cfg); @@ -638,6 +658,7 @@ connection_about_to_close_connection(connection_t *conn) connection_dir_about_to_close(TO_DIR_CONN(conn)); break; case CONN_TYPE_OR: + case CONN_TYPE_EXT_OR: connection_or_about_to_close(TO_OR_CONN(conn)); break; case CONN_TYPE_AP: @@ -1374,6 +1395,9 @@ connection_init_accepted_conn(connection_t *conn, connection_start_reading(conn); switch (conn->type) { + case CONN_TYPE_EXT_OR: + conn->state = EXT_OR_CONN_STATE_OPEN; + break; case CONN_TYPE_OR: control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0); rv = connection_tls_start_handshake(TO_OR_CONN(conn), 1); @@ -2886,6 +2910,8 @@ connection_handle_read_impl(connection_t *conn) switch (conn->type) { case CONN_TYPE_OR_LISTENER: return connection_handle_listener_read(conn, CONN_TYPE_OR); + case CONN_TYPE_EXT_OR_LISTENER: + return connection_handle_listener_read(conn, CONN_TYPE_EXT_OR); case CONN_TYPE_AP_LISTENER: case CONN_TYPE_AP_TRANS_LISTENER: case CONN_TYPE_AP_NATD_LISTENER: @@ -3918,6 +3944,7 @@ int connection_is_listener(connection_t *conn) { if (conn->type == CONN_TYPE_OR_LISTENER || + conn->type == CONN_TYPE_EXT_OR_LISTENER || conn->type == CONN_TYPE_AP_LISTENER || conn->type == CONN_TYPE_AP_TRANS_LISTENER || conn->type == CONN_TYPE_AP_DNS_LISTENER || @@ -3940,6 +3967,7 @@ connection_state_is_open(connection_t *conn) return 0; if ((conn->type == CONN_TYPE_OR && conn->state == OR_CONN_STATE_OPEN) || + (conn->type == CONN_TYPE_EXT_OR) || (conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN) || (conn->type == CONN_TYPE_EXIT && conn->state == EXIT_CONN_STATE_OPEN) || (conn->type == CONN_TYPE_CONTROL && @@ -4109,6 +4137,8 @@ connection_process_inbuf(connection_t *conn, int package_partial) switch (conn->type) { case CONN_TYPE_OR: return connection_or_process_inbuf(TO_OR_CONN(conn)); + case CONN_TYPE_EXT_OR: + return connection_ext_or_process_inbuf(TO_OR_CONN(conn)); case CONN_TYPE_EXIT: case CONN_TYPE_AP: return connection_edge_process_inbuf(TO_EDGE_CONN(conn), @@ -4169,6 +4199,8 @@ connection_finished_flushing(connection_t *conn) switch (conn->type) { case CONN_TYPE_OR: return connection_or_finished_flushing(TO_OR_CONN(conn)); + case CONN_TYPE_EXT_OR: + return connection_ext_or_finished_flushing(TO_OR_CONN(conn)); case CONN_TYPE_AP: case CONN_TYPE_EXIT: return connection_edge_finished_flushing(TO_EDGE_CONN(conn)); @@ -4224,6 +4256,7 @@ connection_reached_eof(connection_t *conn) { switch (conn->type) { case CONN_TYPE_OR: + case CONN_TYPE_EXT_OR: return connection_or_reached_eof(TO_OR_CONN(conn)); case CONN_TYPE_AP: case CONN_TYPE_EXIT: -- cgit v1.2.3-54-g00ecf From d303228ecae1d4c5d9a242b12a4546366544a170 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Wed, 5 Dec 2012 18:19:44 +0200 Subject: Create the Extended ORPort authentication cookie file. --- src/or/config.c | 10 ++++-- src/or/connection.c | 9 ++++- src/or/connection_or.c | 92 +++++++++++++++++++++++++++++++++++++++++++------- src/or/connection_or.h | 3 ++ src/or/control.c | 2 +- src/or/control.h | 2 +- src/or/transports.c | 20 ++++++++++- 7 files changed, 120 insertions(+), 18 deletions(-) (limited to 'src/or/connection.c') diff --git a/src/or/config.c b/src/or/config.c index afdee3d3d4..6dad0194e7 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1474,8 +1474,14 @@ options_act(const or_options_t *old_options) return -1; } - if (init_cookie_authentication(options->CookieAuthentication) < 0) { - log_warn(LD_CONFIG,"Error creating cookie authentication file."); + if (init_control_auth_cookie_authentication(options->CookieAuthentication) < 0) { + log_warn(LD_CONFIG,"Error creating control cookie authentication file."); + return -1; + } + + /* If we have an ExtORPort, initialize its auth cookie. */ + if (init_ext_or_auth_cookie_authentication(!!options->ExtORPort_lines) < 0) { + log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file."); return -1; } diff --git a/src/or/connection.c b/src/or/connection.c index 130b1ecefb..ad8e39c9a1 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -592,8 +592,10 @@ connection_free_(connection_t *conn) log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest"); connection_or_remove_from_identity_map(TO_OR_CONN(conn)); } - if (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR) + if (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR) { connection_or_remove_from_ext_or_id_map(TO_OR_CONN(conn)); + tor_free(TO_OR_CONN(conn)->ext_or_conn_id); + } #ifdef USE_BUFFEREVENTS if (conn->type == CONN_TYPE_OR && TO_OR_CONN(conn)->bucket_cfg) { @@ -4343,6 +4345,7 @@ assert_connection_ok(connection_t *conn, time_t now) switch (conn->type) { case CONN_TYPE_OR: + case CONN_TYPE_EXT_OR: tor_assert(conn->magic == OR_CONNECTION_MAGIC); break; case CONN_TYPE_AP: @@ -4447,6 +4450,9 @@ assert_connection_ok(connection_t *conn, time_t now) case CONN_TYPE_OR: tor_assert(conn->state >= OR_CONN_STATE_MIN_); tor_assert(conn->state <= OR_CONN_STATE_MAX_); + case CONN_TYPE_EXT_OR: + tor_assert(conn->state >= EXT_OR_CONN_STATE_MIN_); + tor_assert(conn->state <= EXT_OR_CONN_STATE_MAX_); break; case CONN_TYPE_EXIT: tor_assert(conn->state >= EXIT_CONN_STATE_MIN_); @@ -4580,6 +4586,7 @@ connection_free_all(void) /* Unlink everything from the identity map. */ connection_or_clear_identity_map(); + connection_or_clear_ext_or_id_map(); /* Clear out our list of broken connections */ clear_broken_connection_map(0); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index ec5733fe18..e6fbb792d8 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -185,8 +185,10 @@ void connection_or_remove_from_ext_or_id_map(or_connection_t *conn) { or_connection_t *tmp; - if (!orconn_identity_map) - orconn_identity_map = digestmap_new(); + if (!orconn_ext_or_id_map) + return; + if (!conn->ext_or_conn_id) + return; tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id); if (!tor_digest_is_zero(conn->ext_or_conn_id)) @@ -200,6 +202,7 @@ void connection_or_clear_ext_or_id_map(void) { digestmap_free(orconn_ext_or_id_map, NULL); + orconn_ext_or_id_map = NULL; } /** Creates an Extended ORPort identifier for conn and deposits @@ -214,13 +217,16 @@ connection_or_set_ext_or_identifier(or_connection_t *conn) orconn_ext_or_id_map = digestmap_new(); /* Remove any previous identifiers: */ - if (!tor_digest_is_zero(conn->ext_or_conn_id)) + if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id)) connection_or_remove_from_ext_or_id_map(conn); do { crypto_rand(random_id, sizeof(random_id)); } while (digestmap_get(orconn_ext_or_id_map, random_id)); + if (!conn->ext_or_conn_id) + conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN); + memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN); tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn); @@ -2484,7 +2490,75 @@ connection_ext_or_transition(or_connection_t *conn) connection_tls_start_handshake(conn, 1); } -#define EXT_OR_CMD_WANT_CONTROL 0x0003 +/** DOCDOCDOC */ +#define EXT_OR_PORT_AUTH_COOKIE_LEN 32 +#define EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN 32 +#define EXT_OR_PORT_AUTH_COOKIE_FILE_LEN EXT_OR_PORT_AUTH_COOKIE_LEN+EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN +#define EXT_OR_PORT_AUTH_COOKIE_HEADER "! Extended ORPort Auth Cookie !\x0a" + +/** If true, we've set ext_or_auth_cookie to a secret code and stored + * it to disk. */ +static int ext_or_auth_cookie_is_set = 0; +/** If ext_or_auth_cookie_is_set, a secret cookie that we've stored to disk + * and which we're using to authenticate controllers. (If the controller can + * read it off disk, it has permission to connect.) */ +static char ext_or_auth_cookie[EXT_OR_PORT_AUTH_COOKIE_LEN] = {0}; + +/** Helper: Return a newly allocated string containing a path to the + * file where we store our authentication cookie. */ +char * +get_ext_or_auth_cookie_file(void) +{ + const or_options_t *options = get_options(); + if (options->ExtORPortCookieAuthFile && strlen(options->ExtORPortCookieAuthFile)) { + return tor_strdup(options->ExtORPortCookieAuthFile); + } else { + return get_datadir_fname("extended_orport_auth_cookie"); + } +} + +/** Choose a random authentication cookie and write it to disk. + * Anybody who can read the cookie from disk will be considered + * authorized to use the control connection. Return -1 if we can't + * write the file, or 0 on success. */ +int +init_ext_or_auth_cookie_authentication(int is_enabled) +{ + char *fname; + char cookie_file_string[EXT_OR_PORT_AUTH_COOKIE_FILE_LEN]; + + if (!is_enabled) { + ext_or_auth_cookie_is_set = 0; + return 0; + } + + /* We don't want to generate a new cookie every time we call + * options_act(). One should be enough. */ + if (ext_or_auth_cookie_is_set) + return 0; /* all set */ + + fname = get_ext_or_auth_cookie_file(); + crypto_rand(ext_or_auth_cookie, EXT_OR_PORT_AUTH_COOKIE_LEN); + ext_or_auth_cookie_is_set = 1; + + memcpy(cookie_file_string, EXT_OR_PORT_AUTH_COOKIE_HEADER, EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN); + memcpy(cookie_file_string+EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN, + ext_or_auth_cookie, EXT_OR_PORT_AUTH_COOKIE_LEN); + + if (write_bytes_to_file(fname, cookie_file_string, + EXT_OR_PORT_AUTH_COOKIE_FILE_LEN, 1)) { + log_warn(LD_FS,"Error writing authentication cookie to %s.", + escaped(fname)); + tor_free(fname); + return -1; + } + + log_warn(LD_GENERAL, "Generated Extended ORPort cookie file in '%s'.", + fname); + + tor_free(fname); + return 0; +} /** Extended ORPort commands (Transport-to-Bridge) */ #define EXT_OR_CMD_TB_DONE 0x0000 @@ -2553,14 +2627,6 @@ connection_ext_or_process_inbuf(or_connection_t *or_conn) /* record the address */ tor_addr_copy(&conn->addr, &addr); conn->port = port; - } else if (command->cmd == EXT_OR_CMD_WANT_CONTROL) { - char response[128]; - char *cp; - memcpy(response, or_conn->ext_or_conn_id, EXT_OR_CONN_ID_LEN); - cp = response+EXT_OR_CONN_ID_LEN; - /* XXXX write the TransportControlPort; advance cp. */ - connection_write_ext_or_command(conn, EXT_OR_CMD_BT_OKAY, response, - cp-response); } else { log_notice(LD_NET, "Got an Extended ORPort command we don't understand (%u).", command->cmd); @@ -2569,6 +2635,8 @@ connection_ext_or_process_inbuf(or_connection_t *or_conn) ext_or_cmd_free(command); } + return 0; + err: ext_or_cmd_free(command); return -1; diff --git a/src/or/connection_or.h b/src/or/connection_or.h index a80871d5c9..1e9a652b45 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -104,5 +104,8 @@ void connection_or_clear_ext_or_id_map(void); int connection_ext_or_finished_flushing(or_connection_t *conn); int connection_ext_or_process_inbuf(or_connection_t *or_conn); +int init_ext_or_auth_cookie_authentication(int is_enabled); +char *get_ext_or_auth_cookie_file(void); + #endif diff --git a/src/or/control.c b/src/or/control.c index 3f8d47c554..e83a8e08cd 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4451,7 +4451,7 @@ get_cookie_file(void) * authorized to use the control connection. Return -1 if we can't * write the file, or 0 on success. */ int -init_cookie_authentication(int enabled) +init_control_auth_cookie_authentication(int enabled) { char *fname; if (!enabled) { diff --git a/src/or/control.h b/src/or/control.h index d0f682067e..663824ce31 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -77,7 +77,7 @@ int control_event_buildtimeout_set(const circuit_build_times_t *cbt, buildtimeout_set_event_t type); int control_event_signal(uintptr_t signal); -int init_cookie_authentication(int enabled); +int init_control_auth_cookie_authentication(int enabled); smartlist_t *decode_hashed_passwords(config_line_t *passwords); void disable_control_logging(void); void enable_control_logging(void); diff --git a/src/or/transports.c b/src/or/transports.c index 15faa98d40..877dc0c335 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -96,6 +96,7 @@ #include "router.h" #include "statefile.h" #include "entrynodes.h" +#include "connection_or.h" static process_environment_t * create_managed_proxy_environment(const managed_proxy_t *mp); @@ -1194,6 +1195,8 @@ get_bindaddr_for_server_proxy(const managed_proxy_t *mp) static process_environment_t * create_managed_proxy_environment(const managed_proxy_t *mp) { + const or_options_t *options = get_options(); + /* Environment variables to be added to or set in mp's environment. */ smartlist_t *envs = smartlist_new(); /* XXXX The next time someone touches this code, shorten the name of @@ -1257,7 +1260,22 @@ create_managed_proxy_environment(const managed_proxy_t *mp) * (If we remove this line entirely, some joker will stick this * variable in Tor's environment and crash PTs that try to parse * it even when not run in server mode.) */ - smartlist_add(envs, tor_strdup("TOR_PT_EXTENDED_SERVER_PORT=")); + + if (options->ExtORPort) { + char *ext_or_addrport_tmp = + get_first_listener_addrport_string(CONN_TYPE_EXT_OR_LISTENER); + char *cookie_file_loc = get_ext_or_auth_cookie_file(); + + smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=%s", + ext_or_addrport_tmp); + smartlist_add_asprintf(envs, "TOR_PT_AUTH_COOKIE_FILE=%s", cookie_file_loc); + + tor_free(ext_or_addrport_tmp); + tor_free(cookie_file_loc); + + } else { + smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT="); + } } SMARTLIST_FOREACH_BEGIN(envs, const char *, env_var) { -- cgit v1.2.3-54-g00ecf From 93b9f85d410aba1202b72e169ce386c783136b26 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Wed, 5 Dec 2012 18:38:42 +0200 Subject: Prepare codebase for the implementation of Extended ORPort auth. --- src/or/connection.c | 10 ++++++++-- src/or/or.h | 28 ++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 8 deletions(-) (limited to 'src/or/connection.c') diff --git a/src/or/connection.c b/src/or/connection.c index ad8e39c9a1..9aea501cf6 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -170,6 +170,12 @@ conn_state_to_string(int type, int state) break; case CONN_TYPE_EXT_OR: switch (state) { + case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE: + return "waiting for authentication type"; + case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE: + return "waiting for client nonce"; + case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH: + return "waiting for client hash"; case EXT_OR_CONN_STATE_OPEN: return "open"; case EXT_OR_CONN_STATE_FLUSHING: return "flushing final OKAY"; } @@ -1398,8 +1404,7 @@ connection_init_accepted_conn(connection_t *conn, switch (conn->type) { case CONN_TYPE_EXT_OR: - conn->state = EXT_OR_CONN_STATE_OPEN; - break; + return connection_ext_or_start_auth(TO_OR_CONN(conn)); case CONN_TYPE_OR: control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0); rv = connection_tls_start_handshake(TO_OR_CONN(conn), 1); @@ -4450,6 +4455,7 @@ assert_connection_ok(connection_t *conn, time_t now) case CONN_TYPE_OR: tor_assert(conn->state >= OR_CONN_STATE_MIN_); tor_assert(conn->state <= OR_CONN_STATE_MAX_); + break; case CONN_TYPE_EXT_OR: tor_assert(conn->state >= EXT_OR_CONN_STATE_MIN_); tor_assert(conn->state <= EXT_OR_CONN_STATE_MAX_); diff --git a/src/or/or.h b/src/or/or.h index e81d7a902d..63d137cf15 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -315,14 +315,24 @@ typedef enum { #define OR_CONN_STATE_OPEN 8 #define OR_CONN_STATE_MAX_ 8 -/** States of Extended ORPort. */ +/** States of the Extended ORPort protocol. Be careful before changing + * the numbers: they matter. */ #define EXT_OR_CONN_STATE_MIN_ 1 -/** Extended ORPort just launched, and is accepting connections. */ -#define EXT_OR_CONN_STATE_OPEN 1 +/** Extended ORPort authentication is waiting for the authentication + * type selected by the client. */ +#define EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE 1 +/** Extended ORPort authentication is waiting for the client nonce. */ +#define EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE 2 +/** Extended ORPort authentication is waiting for the client hash. */ +#define EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH 3 +#define EXT_OR_CONN_STATE_AUTH_MAX 3 +/** Authentication finished and the Extended ORPort is now accepting + * traffic. */ +#define EXT_OR_CONN_STATE_OPEN 4 /** Extended ORPort is flushing its last messages and preparing to * start accepting OR connections. */ -#define EXT_OR_CONN_STATE_FLUSHING 2 -#define EXT_OR_CONN_STATE_MAX_ 2 +#define EXT_OR_CONN_STATE_FLUSHING 5 +#define EXT_OR_CONN_STATE_MAX_ 5 #define EXIT_CONN_STATE_MIN_ 1 /** State for an exit connection: waiting for response from DNS farm. */ @@ -1440,6 +1450,9 @@ typedef struct or_connection_t { char identity_digest[DIGEST_LEN]; /** Extended ORPort connection identifier. */ char *ext_or_conn_id; + /** Client hash of the Extended ORPort authentication scheme */ + char *ext_or_auth_correct_client_hash; + char *nickname; /**< Nickname of OR on other side (if any). */ tor_tls_t *tls; /**< TLS connection state. */ @@ -3771,7 +3784,10 @@ typedef struct { int CookieAuthentication; /**< Boolean: do we enable cookie-based auth for * the control system? */ - char *CookieAuthFile; /**< Location of a cookie authentication file. */ + char *CookieAuthFile; /**< Filesystem location of a ControlPort + * authentication cookie. */ + char *ExtORPortCookieAuthFile; /**< Filesystem location of Extended + * ORPort authentication cookie. */ int CookieAuthFileGroupReadable; /**< Boolean: Is the CookieAuthFile g+r? */ int LeaveStreamsUnattached; /**< Boolean: Does Tor attach new streams to * circuits itself (0), or does it expect a controller -- cgit v1.2.3-54-g00ecf From e2e0d09dab0490724555d5c67c2a1d27fd5199c4 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Wed, 5 Dec 2012 19:15:28 +0200 Subject: Various Extended ORPort code improvements. * Add documentation. * Free ext_or_auth_correct_client_hash. * Use VPORT(ExtORPort) instead of V(ExtORPOrt). See dfe03d36c8749eb07e9bb2ea47e88ff05e9e3127 for details. --- src/or/connection.c | 1 + src/or/connection_or.c | 67 +++++++++++++++++++++++++++++++++++++++----------- src/or/transports.c | 2 +- 3 files changed, 54 insertions(+), 16 deletions(-) (limited to 'src/or/connection.c') diff --git a/src/or/connection.c b/src/or/connection.c index 9aea501cf6..a2719e9f42 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -601,6 +601,7 @@ connection_free_(connection_t *conn) if (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR) { connection_or_remove_from_ext_or_id_map(TO_OR_CONN(conn)); tor_free(TO_OR_CONN(conn)->ext_or_conn_id); + tor_free(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash); } #ifdef USE_BUFFEREVENTS diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 41396705e3..089bb064b4 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -179,8 +179,8 @@ connection_or_set_identity_digest(or_connection_t *conn, const char *digest) } /** Remove the Extended ORPort identifier of conn from the - global identifier list. Also, clear the identifier from the - connection itself. */ + * global identifier list. Also, clear the identifier from the + * connection itself. */ void connection_or_remove_from_ext_or_id_map(or_connection_t *conn) { @@ -2443,7 +2443,10 @@ connection_or_send_authenticate_cell(or_connection_t *conn, int authtype) return 0; } -/** Get an Extended ORPort message from conn, and place it in out. */ +/** Get an Extended ORPort message from conn, and place it in + * out. Return -1 on fail, 0 if we need more data, and 1 if we + * successfully extracted an Extended ORPort command from the + * buffer. */ static int connection_fetch_ext_or_cmd_from_buf(connection_t *conn, ext_or_cmd_t **out) { @@ -2490,13 +2493,19 @@ connection_ext_or_transition(or_connection_t *conn) connection_tls_start_handshake(conn, 1); } -/** DOCDOCDOC */ +/** Length of authentication cookie. */ #define EXT_OR_PORT_AUTH_COOKIE_LEN 32 +/** Length of the header of the cookie file. */ #define EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN 32 +/** Total length of the cookie file. */ #define EXT_OR_PORT_AUTH_COOKIE_FILE_LEN EXT_OR_PORT_AUTH_COOKIE_LEN+EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN +/** Static cookie file header. */ #define EXT_OR_PORT_AUTH_COOKIE_HEADER "! Extended ORPort Auth Cookie !\x0a" +/** Length of safe-cookie protocol hashes. */ #define EXT_OR_PORT_AUTH_HASH_LEN DIGEST256_LEN +/** Length of safe-cookie protocol nonces. */ #define EXT_OR_PORT_AUTH_NONCE_LEN 32 +/** Safe-cookie protocol constants. */ #define EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST "ExtORPort authentication server-to-client hash" #define EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST "ExtORPort authentication client-to-server hash" @@ -2541,14 +2550,15 @@ init_ext_or_auth_cookie_authentication(int is_enabled) if (ext_or_auth_cookie_is_set) return 0; /* all set */ - fname = get_ext_or_auth_cookie_file(); - crypto_rand(ext_or_auth_cookie, EXT_OR_PORT_AUTH_COOKIE_LEN); + if (crypto_rand(ext_or_auth_cookie, EXT_OR_PORT_AUTH_COOKIE_LEN) < 0) + return -1; ext_or_auth_cookie_is_set = 1; memcpy(cookie_file_string, EXT_OR_PORT_AUTH_COOKIE_HEADER, EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN); memcpy(cookie_file_string+EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN, ext_or_auth_cookie, EXT_OR_PORT_AUTH_COOKIE_LEN); + fname = get_ext_or_auth_cookie_file(); if (write_bytes_to_file(fname, cookie_file_string, EXT_OR_PORT_AUTH_COOKIE_FILE_LEN, 1)) { log_warn(LD_FS,"Error writing authentication cookie to %s.", @@ -2564,11 +2574,15 @@ init_ext_or_auth_cookie_authentication(int is_enabled) return 0; } -/** DOCDOCDOC - Return -1 on error. 0 on unsufficient data. 1 on correct. -*/ +/** Read data from conn and see if the client sent us the + * authentication type that she prefers to use in this session. + * + * Return -1 if we received corrupted data or if we don't support the + * authentication type. Return 0 if we need more data in + * conn. Return 1 if the authentication type negotiation was + * successful. */ static int -connection_ext_or_auth_neg_auth_type(connection_t *conn) /* XXX unit tests */ +connection_ext_or_auth_neg_auth_type(connection_t *conn) { char authtype[1] = {0}; @@ -2582,10 +2596,16 @@ connection_ext_or_auth_neg_auth_type(connection_t *conn) /* XXX unit tests */ if (authtype[0] != 1) /* '1' is the only auth type supported atm */ return -1; - conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE; /* XXX maybe do state transition in process_inbuf ? */ + conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE; return 1; } +/** Read the client's nonce out of conn, setup the safe-cookie + * crypto, and then send our own hash and nonce to the client + * + * Return -1 if there was an error; return 0 if we need more data in + * conn, and return 1 if we successfully retrieved the + * client's nonce and sent our own. */ static int connection_ext_or_auth_handle_client_nonce(connection_t *conn) { @@ -2692,6 +2712,8 @@ connection_ext_or_auth_handle_client_nonce(connection_t *conn) #define connection_ext_or_auth_send_result_fail(c) \ connection_ext_or_auth_send_result(c, 0) +/** Send authentication results to conn. Successful results if + * success is set; failure results otherwise. */ static void connection_ext_or_auth_send_result(connection_t *conn, int success) { @@ -2701,6 +2723,13 @@ connection_ext_or_auth_send_result(connection_t *conn, int success) connection_write_to_buf("\x00", 1, conn); } +/** Receive the client's hash from conn, validate that it's + * correct, and then send the authentication results to the client. + * + * Return -1 if there was an error during validation; return 0 if we + * need more data in conn, and return 1 if we successfully + * validated the client's hash and sent a happy authentication + * result. */ static int connection_ext_or_auth_handle_client_hash(connection_t *conn) { @@ -2727,9 +2756,8 @@ connection_ext_or_auth_handle_client_hash(connection_t *conn) return 1; } -/** DOCDOCDOC - Return -1 on error. 0 on unsufficient data. 1 on correct. -*/ +/** Handle data from or_conn received on Extended ORPort. + * Return -1 on error. 0 on unsufficient data. 1 on correct. */ static int connection_ext_or_auth_process_inbuf(or_connection_t *or_conn) { @@ -2761,6 +2789,14 @@ connection_ext_or_auth_process_inbuf(or_connection_t *or_conn) #define EXT_OR_CMD_BT_DENY 0x1001 #define EXT_OR_CMD_BT_CONTROL 0x1002 + +/** Process a USERADDR command from the Extended + * ORPort. payload is a payload of size len. + * + * If the USERADDR command was well formed, change the address of + * conn to the address on the USERADDR command. + * + * Return 0 on success and -1 on error. */ static int connection_ext_or_handle_useraddr(connection_t *conn, char *payload, uint16_t len) { @@ -2883,7 +2919,8 @@ connection_ext_or_finished_flushing(or_connection_t *conn) return 0; } -/* DOCDOCDOC */ +/** Initiate Extended ORPort authentication, by sending the list of + * supported authentication types to the client. */ int connection_ext_or_start_auth(or_connection_t *or_conn) { diff --git a/src/or/transports.c b/src/or/transports.c index 877dc0c335..474a9db9b0 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -1261,7 +1261,7 @@ create_managed_proxy_environment(const managed_proxy_t *mp) * variable in Tor's environment and crash PTs that try to parse * it even when not run in server mode.) */ - if (options->ExtORPort) { + if (options->ExtORPort_lines) { char *ext_or_addrport_tmp = get_first_listener_addrport_string(CONN_TYPE_EXT_OR_LISTENER); char *cookie_file_loc = get_ext_or_auth_cookie_file(); -- cgit v1.2.3-54-g00ecf From d8f74cc439ad025cc52aea8495705096d6538029 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Wed, 5 Dec 2012 19:18:18 +0200 Subject: Move Extended ORPort code to its own module. Move the code from the connection_or module to ext_orport. This commit only moves code: it shouldn't modify anything. --- src/or/buffers.c | 1 + src/or/config.c | 1 + src/or/connection.c | 1 + src/or/connection_or.c | 524 +---------------------------------------------- src/or/connection_or.h | 14 -- src/or/ext_orport.c | 537 +++++++++++++++++++++++++++++++++++++++++++++++++ src/or/ext_orport.h | 14 ++ src/or/include.am | 2 + src/or/transports.c | 1 + 9 files changed, 558 insertions(+), 537 deletions(-) create mode 100644 src/or/ext_orport.c create mode 100644 src/or/ext_orport.h (limited to 'src/or/connection.c') diff --git a/src/or/buffers.c b/src/or/buffers.c index ead6baa882..50016d3a86 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -19,6 +19,7 @@ #include "connection_or.h" #include "control.h" #include "reasons.h" +#include "ext_orport.h" #include "../common/util.h" #include "../common/torlog.h" #ifdef HAVE_UNISTD_H diff --git a/src/or/config.c b/src/or/config.c index 19da45a63b..542191dbeb 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -45,6 +45,7 @@ #include "routerset.h" #include "statefile.h" #include "transports.h" +#include "ext_orport.h" #ifdef _WIN32 #include #endif diff --git a/src/or/connection.c b/src/or/connection.c index a2719e9f42..fcdc9ab996 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -33,6 +33,7 @@ #include "dns.h" #include "dnsserv.h" #include "entrynodes.h" +#include "ext_orport.h" #include "geoip.h" #include "main.h" #include "policies.h" diff --git a/src/or/connection_or.c b/src/or/connection_or.c index d6d74a703c..42127ad3e8 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -37,7 +37,7 @@ #include "rephist.h" #include "router.h" #include "routerlist.h" - +#include "ext_orport.h" #ifdef USE_BUFFEREVENTS #include #endif @@ -482,24 +482,6 @@ var_cell_free(var_cell_t *cell) tor_free(cell); } -/** Allocate and return a structure capable of holding an Extended - * ORPort message of body length len. */ -ext_or_cmd_t * -ext_or_cmd_new(uint16_t len) -{ - size_t size = STRUCT_OFFSET(ext_or_cmd_t, body) + len; - ext_or_cmd_t *cmd = tor_malloc(size); - cmd->len = len; - return cmd; -} - -/** Deallocate the Extended ORPort message in cmd. */ -void -ext_or_cmd_free(ext_or_cmd_t *cmd) -{ - tor_free(cmd); -} - /** We've received an EOF from conn. Mark it for close and return. */ int connection_or_reached_eof(or_connection_t *conn) @@ -2442,507 +2424,3 @@ connection_or_send_authenticate_cell(or_connection_t *conn, int authtype) return 0; } - -/** Get an Extended ORPort message from conn, and place it in - * out. Return -1 on fail, 0 if we need more data, and 1 if we - * successfully extracted an Extended ORPort command from the - * buffer. */ -static int -connection_fetch_ext_or_cmd_from_buf(connection_t *conn, ext_or_cmd_t **out) -{ - IF_HAS_BUFFEREVENT(conn, { - struct evbuffer *input = bufferevent_get_input(conn->bufev); - return fetch_ext_or_command_from_evbuffer(input, out); - }) ELSE_IF_NO_BUFFEREVENT { - return fetch_ext_or_command_from_buf(conn->inbuf, out); - } -} - -/** Write an Extended ORPort message to conn. Use - * command as the command type, bodylen as the body - * length, and body, if it's present, as the body of the - * message. */ -static int -connection_write_ext_or_command(connection_t *conn, - uint16_t command, - const char *body, - size_t bodylen) -{ - char header[4]; - if (bodylen > UINT16_MAX) - return -1; - set_uint16(header, htons(command)); - set_uint16(header+2, htons(bodylen)); - connection_write_to_buf(header, 4, conn); - if (bodylen) { - tor_assert(body); - connection_write_to_buf(body, bodylen, conn); - } - return 0; -} - -/** Transition from an Extended ORPort which accepts Extended ORPort - * messages, to an Extended ORport which accepts OR traffic. */ -static void -connection_ext_or_transition(or_connection_t *conn) -{ - tor_assert(conn->base_.type == CONN_TYPE_EXT_OR); - - conn->base_.type = CONN_TYPE_OR; - control_event_or_conn_status(conn, OR_CONN_EVENT_NEW, 0); - connection_tls_start_handshake(conn, 1); -} - -/** Length of authentication cookie. */ -#define EXT_OR_PORT_AUTH_COOKIE_LEN 32 -/** Length of the header of the cookie file. */ -#define EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN 32 -/** Total length of the cookie file. */ -#define EXT_OR_PORT_AUTH_COOKIE_FILE_LEN \ - EXT_OR_PORT_AUTH_COOKIE_LEN+EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN -/** Static cookie file header. */ -#define EXT_OR_PORT_AUTH_COOKIE_HEADER "! Extended ORPort Auth Cookie !\x0a" -/** Length of safe-cookie protocol hashes. */ -#define EXT_OR_PORT_AUTH_HASH_LEN DIGEST256_LEN -/** Length of safe-cookie protocol nonces. */ -#define EXT_OR_PORT_AUTH_NONCE_LEN 32 -/** Safe-cookie protocol constants. */ -#define EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST \ - "ExtORPort authentication server-to-client hash" -#define EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST \ - "ExtORPort authentication client-to-server hash" - -/** If true, we've set ext_or_auth_cookie to a secret code and stored - * it to disk. */ -static int ext_or_auth_cookie_is_set = 0; -/** If ext_or_auth_cookie_is_set, a secret cookie that we've stored to disk - * and which we're using to authenticate controllers. (If the controller can - * read it off disk, it has permission to connect.) */ -static char ext_or_auth_cookie[EXT_OR_PORT_AUTH_COOKIE_LEN] = {0}; - -/** Helper: Return a newly allocated string containing a path to the - * file where we store our authentication cookie. */ -char * -get_ext_or_auth_cookie_file(void) -{ - const or_options_t *options = get_options(); - if (options->ExtORPortCookieAuthFile && - strlen(options->ExtORPortCookieAuthFile)) { - return tor_strdup(options->ExtORPortCookieAuthFile); - } else { - return get_datadir_fname("extended_orport_auth_cookie"); - } -} - -/** Choose a random authentication cookie and write it to disk. - * Anybody who can read the cookie from disk will be considered - * authorized to use the control connection. Return -1 if we can't - * write the file, or 0 on success. */ -int -init_ext_or_auth_cookie_authentication(int is_enabled) -{ - char *fname; - char cookie_file_string[EXT_OR_PORT_AUTH_COOKIE_FILE_LEN]; - - if (!is_enabled) { - ext_or_auth_cookie_is_set = 0; - return 0; - } - - /* We don't want to generate a new cookie every time we call - * options_act(). One should be enough. */ - if (ext_or_auth_cookie_is_set) - return 0; /* all set */ - - if (crypto_rand(ext_or_auth_cookie, EXT_OR_PORT_AUTH_COOKIE_LEN) < 0) - return -1; - ext_or_auth_cookie_is_set = 1; - - memcpy(cookie_file_string, EXT_OR_PORT_AUTH_COOKIE_HEADER, - EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN); - memcpy(cookie_file_string+EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN, - ext_or_auth_cookie, EXT_OR_PORT_AUTH_COOKIE_LEN); - - fname = get_ext_or_auth_cookie_file(); - if (write_bytes_to_file(fname, cookie_file_string, - EXT_OR_PORT_AUTH_COOKIE_FILE_LEN, 1)) { - log_warn(LD_FS,"Error writing authentication cookie to %s.", - escaped(fname)); - tor_free(fname); - return -1; - } - - log_warn(LD_GENERAL, "Generated Extended ORPort cookie file in '%s'.", - fname); - - tor_free(fname); - return 0; -} - -/** Read data from conn and see if the client sent us the - * authentication type that she prefers to use in this session. - * - * Return -1 if we received corrupted data or if we don't support the - * authentication type. Return 0 if we need more data in - * conn. Return 1 if the authentication type negotiation was - * successful. */ -static int -connection_ext_or_auth_neg_auth_type(connection_t *conn) -{ - char authtype[1] = {0}; - - if (connection_get_inbuf_len(conn) < 1) - return 0; - - if (connection_fetch_from_buf(authtype, 1, conn) < 0) - return -1; - - log_warn(LD_GENERAL, "Client wants us to use %d auth type", authtype[0]); - if (authtype[0] != 1) /* '1' is the only auth type supported atm */ - return -1; - - conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE; - return 1; -} - -/** Read the client's nonce out of conn, setup the safe-cookie - * crypto, and then send our own hash and nonce to the client - * - * Return -1 if there was an error; return 0 if we need more data in - * conn, and return 1 if we successfully retrieved the - * client's nonce and sent our own. */ -static int -connection_ext_or_auth_handle_client_nonce(connection_t *conn) -{ - char server_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0}; - char client_nonce[EXT_OR_PORT_AUTH_NONCE_LEN] = {0}; - char server_nonce[EXT_OR_PORT_AUTH_NONCE_LEN] = {0}; - char reply[EXT_OR_PORT_AUTH_COOKIE_LEN+EXT_OR_PORT_AUTH_NONCE_LEN] = {0}; - - if (!ext_or_auth_cookie_is_set) { /* this should not happen */ - log_warn(LD_BUG, "Extended ORPort authentication cookie was not set. " - "That's weird since we should have done that on startup. " - "This might be a Tor bug, please file a bug report. "); - return -1; - } - - if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_NONCE_LEN) - return 0; - - if (connection_fetch_from_buf(client_nonce, - EXT_OR_PORT_AUTH_NONCE_LEN, conn) < 0) /* XXX check-spaces */ - return -1; - - /* Get our nonce */ - if (crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN) < 0) - return -1; - - { /* set up macs */ - size_t hmac_s_msg_len = strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) + - 2*EXT_OR_PORT_AUTH_NONCE_LEN; - size_t hmac_c_msg_len = strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) + - 2*EXT_OR_PORT_AUTH_NONCE_LEN; - - char *hmac_s_msg = tor_malloc_zero(hmac_s_msg_len); - char *hmac_c_msg = tor_malloc_zero(hmac_c_msg_len); - char *correct_client_hash = tor_malloc_zero(EXT_OR_PORT_AUTH_HASH_LEN); - - memcpy(hmac_s_msg, - EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST, - strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST)); - memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST), - client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); - memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) + - EXT_OR_PORT_AUTH_NONCE_LEN, - server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); - - memcpy(hmac_c_msg, - EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST, - strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST)); - memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST), - client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); - memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) + - EXT_OR_PORT_AUTH_NONCE_LEN, - server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); - - crypto_hmac_sha256(server_hash, - ext_or_auth_cookie, - EXT_OR_PORT_AUTH_COOKIE_LEN, - hmac_s_msg, - hmac_s_msg_len); - - crypto_hmac_sha256(correct_client_hash, - ext_or_auth_cookie, - EXT_OR_PORT_AUTH_COOKIE_LEN, - hmac_c_msg, - hmac_c_msg_len); - - /* Store the client hash we generated. We will need to compare it - with the hash sent by the client. */ - TO_OR_CONN(conn)->ext_or_auth_correct_client_hash = correct_client_hash; - - tor_free(hmac_s_msg); - tor_free(hmac_c_msg); - } - - { /* debug logging */ /* XXX disable this codepath if not logging on debug?*/ - char server_hash_encoded[(2*EXT_OR_PORT_AUTH_HASH_LEN) + 1]; - char server_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1]; - char client_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1]; - - base16_encode(server_hash_encoded, sizeof(server_hash_encoded), - server_hash, sizeof(server_hash)); - base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded), - server_nonce, sizeof(server_nonce)); - base16_encode(client_nonce_encoded, sizeof(client_nonce_encoded), - client_nonce, sizeof(client_nonce)); - - log_warn(LD_GENERAL, - "server_hash: '%s'\nserver_nonce: '%s'\nclient_nonce: '%s'", - server_hash_encoded, server_nonce_encoded, client_nonce_encoded); - } - - { /* write reply: (server_hash, server_nonce) */ - memcpy(reply, server_hash, EXT_OR_PORT_AUTH_HASH_LEN); - memcpy(reply + EXT_OR_PORT_AUTH_HASH_LEN, server_nonce, - EXT_OR_PORT_AUTH_NONCE_LEN); - connection_write_to_buf(reply, sizeof(reply), conn); - } - - log_warn(LD_GENERAL, "Got client nonce, and sent our own nonce and hash."); - - conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH; - return 1; -} - -#define connection_ext_or_auth_send_result_success(c) \ - connection_ext_or_auth_send_result(c, 1) -#define connection_ext_or_auth_send_result_fail(c) \ - connection_ext_or_auth_send_result(c, 0) - -/** Send authentication results to conn. Successful results if - * success is set; failure results otherwise. */ -static void -connection_ext_or_auth_send_result(connection_t *conn, int success) -{ - if (success) - connection_write_to_buf("\x01", 1, conn); - else - connection_write_to_buf("\x00", 1, conn); -} - -/** Receive the client's hash from conn, validate that it's - * correct, and then send the authentication results to the client. - * - * Return -1 if there was an error during validation; return 0 if we - * need more data in conn, and return 1 if we successfully - * validated the client's hash and sent a happy authentication - * result. */ -static int -connection_ext_or_auth_handle_client_hash(connection_t *conn) -{ - char provided_client_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0}; - - if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_HASH_LEN) - return 0; - - if (connection_fetch_from_buf(provided_client_hash, - EXT_OR_PORT_AUTH_HASH_LEN, conn) < 0) - return -1; - - if (tor_memneq(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash, - provided_client_hash, EXT_OR_PORT_AUTH_HASH_LEN)) { - log_warn(LD_GENERAL, "Incorrect client hash. Authentication failed."); - connection_ext_or_auth_send_result_fail(conn); - return -1; - } - - log_warn(LD_GENERAL, "Got client's hash and it was legit."); - - /* send positive auth result */ - connection_ext_or_auth_send_result_success(conn); - conn->state = EXT_OR_CONN_STATE_OPEN; - return 1; -} - -/** Handle data from or_conn received on Extended ORPort. - * Return -1 on error. 0 on unsufficient data. 1 on correct. */ -static int -connection_ext_or_auth_process_inbuf(or_connection_t *or_conn) -{ - connection_t *conn = TO_CONN(or_conn); - - switch (conn->state) { /* Functionify */ - case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE: - return connection_ext_or_auth_neg_auth_type(conn); - - case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE: - return connection_ext_or_auth_handle_client_nonce(conn); - - case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH: - return connection_ext_or_auth_handle_client_hash(conn); - - default: - log_warn(LD_BUG, "Encountered unexpected connection state %d while trying " - "to process Extended ORPort authentication data.", conn->state); - return -1; - } -} - -/** Extended ORPort commands (Transport-to-Bridge) */ -#define EXT_OR_CMD_TB_DONE 0x0000 -#define EXT_OR_CMD_TB_USERADDR 0x0001 - -/** Extended ORPort commands (Bridge-to-Transport) */ -#define EXT_OR_CMD_BT_OKAY 0x1000 -#define EXT_OR_CMD_BT_DENY 0x1001 -#define EXT_OR_CMD_BT_CONTROL 0x1002 - -/** Process a USERADDR command from the Extended - * ORPort. payload is a payload of size len. - * - * If the USERADDR command was well formed, change the address of - * conn to the address on the USERADDR command. - * - * Return 0 on success and -1 on error. */ -static int -connection_ext_or_handle_useraddr(connection_t *conn, - char *payload, uint16_t len) -{ - /* Copy address string. */ - tor_addr_t addr; - uint16_t port; - char *addr_str; - char *address_part=NULL; - int res; - addr_str = tor_malloc(len + 1); - memcpy(addr_str, payload, len); - addr_str[len] = 0; - - res = tor_addr_port_split(LOG_INFO, addr_str, &address_part, &port); - tor_free(addr_str); - if (res<0) - return -1; - - res = tor_addr_parse(&addr, address_part); - tor_free(address_part); - if (res<0) - return -1; - - { /* do some logging */ - char *old_address = tor_dup_addr(&conn->addr); - char *new_address = tor_dup_addr(&addr); - - log_warn(LD_NET, "Received USERADDR." /* XXX Fix log severities/messages */ - "We rewrite our address from '%s:%u' to '%s:%u'.", - safe_str(old_address), conn->port, safe_str(new_address), port); - - tor_free(old_address); - tor_free(new_address); - } - - /* record the address */ - tor_addr_copy(&conn->addr, &addr); - conn->port = port; - - return 0; -} - -/** Process Extended ORPort messages from or_conn. */ -int -connection_ext_or_process_inbuf(or_connection_t *or_conn) -{ - connection_t *conn = TO_CONN(or_conn); - ext_or_cmd_t *command; - int r; - - /* If we are still in the authentication stage, process traffic as - authentication data: */ - while (conn->state <= EXT_OR_CONN_STATE_AUTH_MAX) { - log_warn(LD_GENERAL, "Got Extended ORPort authentication data (%u).", - (unsigned int) connection_get_inbuf_len(conn)); - r = connection_ext_or_auth_process_inbuf(or_conn); - if (r < 0) { - connection_mark_for_close(conn); - return -1; - } else if (r == 0) { - return 0; - } - /* if r > 0, loop and process more data (if any). */ - } - - while (1) { - log_warn(LD_GENERAL, "Got Extended ORPort data."); - command = NULL; - r = connection_fetch_ext_or_cmd_from_buf(conn, &command); - if (r < 0) - goto err; - else if (r == 0) - return 0; /* need to wait for more data */ - - /* Got a command! */ - tor_assert(command); - - if (command->cmd == EXT_OR_CMD_TB_DONE) { - if (connection_get_inbuf_len(conn)) { - /* The inbuf isn't empty; the client is misbehaving. */ - goto err; - } - - log_debug(LD_NET, "Received DONE."); - - connection_write_ext_or_command(conn, EXT_OR_CMD_BT_OKAY, NULL, 0); - - /* can't transition immediately; need to flush first. */ - conn->state = EXT_OR_CONN_STATE_FLUSHING; - connection_stop_reading(conn); - } else if (command->cmd == EXT_OR_CMD_TB_USERADDR) { - if (connection_ext_or_handle_useraddr(conn, - command->body, command->len) < 0) - goto err; - } else { - log_notice(LD_NET,"Got Extended ORPort command we don't regognize (%u).", - command->cmd); - } - - ext_or_cmd_free(command); - } - - return 0; - - err: - ext_or_cmd_free(command); - connection_mark_for_close(conn); - return -1; -} - -/** conn finished flushing Extended ORPort messages to the - * network, and is now ready to accept OR traffic. This function - * does the transition. */ -int -connection_ext_or_finished_flushing(or_connection_t *conn) -{ - if (conn->base_.state == EXT_OR_CONN_STATE_FLUSHING) { - connection_start_reading(TO_CONN(conn)); - connection_ext_or_transition(conn); - } - return 0; -} - -/** Initiate Extended ORPort authentication, by sending the list of - * supported authentication types to the client. */ -int -connection_ext_or_start_auth(or_connection_t *or_conn) -{ - connection_t *conn = TO_CONN(or_conn); - char authtypes[2] = "\x01\x00"; /* We only support authtype '1' for now. */ - - log_warn(LD_GENERAL, - "ExtORPort authentication: Sending supported authentication types"); - - connection_write_to_buf(authtypes, sizeof(authtypes), conn); - conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE; - - return 0; -} - diff --git a/src/or/connection_or.h b/src/or/connection_or.h index d420dc7848..85e68f1a33 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -45,8 +45,6 @@ void connection_or_close_for_error(or_connection_t *orconn, int flush); void connection_or_report_broken_states(int severity, int domain); -int connection_ext_or_start_auth(or_connection_t *or_conn); - int connection_tls_start_handshake(or_connection_t *conn, int receiving); int connection_tls_continue_handshake(or_connection_t *conn); @@ -97,17 +95,5 @@ void var_cell_free(var_cell_t *cell); /** DOCDOC */ #define MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS 4 -ext_or_cmd_t *ext_or_cmd_new(uint16_t len); -void ext_or_cmd_free(ext_or_cmd_t *cmd); -void connection_or_set_ext_or_identifier(or_connection_t *conn); -void connection_or_remove_from_ext_or_id_map(or_connection_t *conn); -void connection_or_clear_ext_or_id_map(void); - -int connection_ext_or_finished_flushing(or_connection_t *conn); -int connection_ext_or_process_inbuf(or_connection_t *or_conn); - -int init_ext_or_auth_cookie_authentication(int is_enabled); -char *get_ext_or_auth_cookie_file(void); - #endif diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c new file mode 100644 index 0000000000..18ee5ab651 --- /dev/null +++ b/src/or/ext_orport.c @@ -0,0 +1,537 @@ +/* Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file ext_orport.c + * \brief Code implementing the Extended ORPort. +*/ + +#include "or.h" +#include "connection.h" +#include "connection_or.h" +#include "ext_orport.h" +#include "control.h" +#include "config.h" +#include "main.h" + +/** Allocate and return a structure capable of holding an Extended + * ORPort message of body length len. */ +ext_or_cmd_t * +ext_or_cmd_new(uint16_t len) +{ + size_t size = STRUCT_OFFSET(ext_or_cmd_t, body) + len; + ext_or_cmd_t *cmd = tor_malloc(size); + cmd->len = len; + return cmd; +} + +/** Deallocate the Extended ORPort message in cmd. */ +void +ext_or_cmd_free(ext_or_cmd_t *cmd) +{ + tor_free(cmd); +} + +/** Get an Extended ORPort message from conn, and place it in + * out. Return -1 on fail, 0 if we need more data, and 1 if we + * successfully extracted an Extended ORPort command from the + * buffer. */ +static int +connection_fetch_ext_or_cmd_from_buf(connection_t *conn, ext_or_cmd_t **out) +{ + IF_HAS_BUFFEREVENT(conn, { + struct evbuffer *input = bufferevent_get_input(conn->bufev); + return fetch_ext_or_command_from_evbuffer(input, out); + }) ELSE_IF_NO_BUFFEREVENT { + return fetch_ext_or_command_from_buf(conn->inbuf, out); + } +} + +/** Write an Extended ORPort message to conn. Use + * command as the command type, bodylen as the body + * length, and body, if it's present, as the body of the + * message. */ +static int +connection_write_ext_or_command(connection_t *conn, + uint16_t command, + const char *body, + size_t bodylen) +{ + char header[4]; + if (bodylen > UINT16_MAX) + return -1; + set_uint16(header, htons(command)); + set_uint16(header+2, htons(bodylen)); + connection_write_to_buf(header, 4, conn); + if (bodylen) { + tor_assert(body); + connection_write_to_buf(body, bodylen, conn); + } + return 0; +} + +/** Transition from an Extended ORPort which accepts Extended ORPort + * messages, to an Extended ORport which accepts OR traffic. */ +static void +connection_ext_or_transition(or_connection_t *conn) +{ + tor_assert(conn->base_.type == CONN_TYPE_EXT_OR); + + conn->base_.type = CONN_TYPE_OR; + control_event_or_conn_status(conn, OR_CONN_EVENT_NEW, 0); + connection_tls_start_handshake(conn, 1); +} + +/** Length of authentication cookie. */ +#define EXT_OR_PORT_AUTH_COOKIE_LEN 32 +/** Length of the header of the cookie file. */ +#define EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN 32 +/** Total length of the cookie file. */ +#define EXT_OR_PORT_AUTH_COOKIE_FILE_LEN \ + EXT_OR_PORT_AUTH_COOKIE_LEN+EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN +/** Static cookie file header. */ +#define EXT_OR_PORT_AUTH_COOKIE_HEADER "! Extended ORPort Auth Cookie !\x0a" +/** Length of safe-cookie protocol hashes. */ +#define EXT_OR_PORT_AUTH_HASH_LEN DIGEST256_LEN +/** Length of safe-cookie protocol nonces. */ +#define EXT_OR_PORT_AUTH_NONCE_LEN 32 +/** Safe-cookie protocol constants. */ +#define EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST \ + "ExtORPort authentication server-to-client hash" +#define EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST \ + "ExtORPort authentication client-to-server hash" + +/** If true, we've set ext_or_auth_cookie to a secret code and stored + * it to disk. */ +static int ext_or_auth_cookie_is_set = 0; +/** If ext_or_auth_cookie_is_set, a secret cookie that we've stored to disk + * and which we're using to authenticate controllers. (If the controller can + * read it off disk, it has permission to connect.) */ +static char ext_or_auth_cookie[EXT_OR_PORT_AUTH_COOKIE_LEN] = {0}; + +/** Helper: Return a newly allocated string containing a path to the + * file where we store our authentication cookie. */ +char * +get_ext_or_auth_cookie_file(void) +{ + const or_options_t *options = get_options(); + if (options->ExtORPortCookieAuthFile && + strlen(options->ExtORPortCookieAuthFile)) { + return tor_strdup(options->ExtORPortCookieAuthFile); + } else { + return get_datadir_fname("extended_orport_auth_cookie"); + } +} + +/** Choose a random authentication cookie and write it to disk. + * Anybody who can read the cookie from disk will be considered + * authorized to use the control connection. Return -1 if we can't + * write the file, or 0 on success. */ +int +init_ext_or_auth_cookie_authentication(int is_enabled) +{ + char *fname; + char cookie_file_string[EXT_OR_PORT_AUTH_COOKIE_FILE_LEN]; + + if (!is_enabled) { + ext_or_auth_cookie_is_set = 0; + return 0; + } + + /* We don't want to generate a new cookie every time we call + * options_act(). One should be enough. */ + if (ext_or_auth_cookie_is_set) + return 0; /* all set */ + + if (crypto_rand(ext_or_auth_cookie, EXT_OR_PORT_AUTH_COOKIE_LEN) < 0) + return -1; + ext_or_auth_cookie_is_set = 1; + + memcpy(cookie_file_string, EXT_OR_PORT_AUTH_COOKIE_HEADER, + EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN); + memcpy(cookie_file_string+EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN, + ext_or_auth_cookie, EXT_OR_PORT_AUTH_COOKIE_LEN); + + fname = get_ext_or_auth_cookie_file(); + if (write_bytes_to_file(fname, cookie_file_string, + EXT_OR_PORT_AUTH_COOKIE_FILE_LEN, 1)) { + log_warn(LD_FS,"Error writing authentication cookie to %s.", + escaped(fname)); + tor_free(fname); + return -1; + } + + log_warn(LD_GENERAL, "Generated Extended ORPort cookie file in '%s'.", + fname); + + tor_free(fname); + return 0; +} + +/** Read data from conn and see if the client sent us the + * authentication type that she prefers to use in this session. + * + * Return -1 if we received corrupted data or if we don't support the + * authentication type. Return 0 if we need more data in + * conn. Return 1 if the authentication type negotiation was + * successful. */ +static int +connection_ext_or_auth_neg_auth_type(connection_t *conn) +{ + char authtype[1] = {0}; + + if (connection_get_inbuf_len(conn) < 1) + return 0; + + if (connection_fetch_from_buf(authtype, 1, conn) < 0) + return -1; + + log_warn(LD_GENERAL, "Client wants us to use %d auth type", authtype[0]); + if (authtype[0] != 1) /* '1' is the only auth type supported atm */ + return -1; + + conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE; + return 1; +} + +/** Read the client's nonce out of conn, setup the safe-cookie + * crypto, and then send our own hash and nonce to the client + * + * Return -1 if there was an error; return 0 if we need more data in + * conn, and return 1 if we successfully retrieved the + * client's nonce and sent our own. */ +static int +connection_ext_or_auth_handle_client_nonce(connection_t *conn) +{ + char server_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0}; + char client_nonce[EXT_OR_PORT_AUTH_NONCE_LEN] = {0}; + char server_nonce[EXT_OR_PORT_AUTH_NONCE_LEN] = {0}; + char reply[EXT_OR_PORT_AUTH_COOKIE_LEN+EXT_OR_PORT_AUTH_NONCE_LEN] = {0}; + + if (!ext_or_auth_cookie_is_set) { /* this should not happen */ + log_warn(LD_BUG, "Extended ORPort authentication cookie was not set. " + "That's weird since we should have done that on startup. " + "This might be a Tor bug, please file a bug report. "); + return -1; + } + + if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_NONCE_LEN) + return 0; + + if (connection_fetch_from_buf(client_nonce, + EXT_OR_PORT_AUTH_NONCE_LEN, conn) < 0) /* XXX check-spaces */ + return -1; + + /* Get our nonce */ + if (crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN) < 0) + return -1; + + { /* set up macs */ + size_t hmac_s_msg_len = strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) + + 2*EXT_OR_PORT_AUTH_NONCE_LEN; + size_t hmac_c_msg_len = strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) + + 2*EXT_OR_PORT_AUTH_NONCE_LEN; + + char *hmac_s_msg = tor_malloc_zero(hmac_s_msg_len); + char *hmac_c_msg = tor_malloc_zero(hmac_c_msg_len); + char *correct_client_hash = tor_malloc_zero(EXT_OR_PORT_AUTH_HASH_LEN); + + memcpy(hmac_s_msg, + EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST, + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST)); + memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST), + client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); + memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) + + EXT_OR_PORT_AUTH_NONCE_LEN, + server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); + + memcpy(hmac_c_msg, + EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST, + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST)); + memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST), + client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); + memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) + + EXT_OR_PORT_AUTH_NONCE_LEN, + server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); + + crypto_hmac_sha256(server_hash, + ext_or_auth_cookie, + EXT_OR_PORT_AUTH_COOKIE_LEN, + hmac_s_msg, + hmac_s_msg_len); + + crypto_hmac_sha256(correct_client_hash, + ext_or_auth_cookie, + EXT_OR_PORT_AUTH_COOKIE_LEN, + hmac_c_msg, + hmac_c_msg_len); + + /* Store the client hash we generated. We will need to compare it + with the hash sent by the client. */ + TO_OR_CONN(conn)->ext_or_auth_correct_client_hash = correct_client_hash; + + tor_free(hmac_s_msg); + tor_free(hmac_c_msg); + } + + { /* debug logging */ /* XXX disable this codepath if not logging on debug?*/ + char server_hash_encoded[(2*EXT_OR_PORT_AUTH_HASH_LEN) + 1]; + char server_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1]; + char client_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1]; + + base16_encode(server_hash_encoded, sizeof(server_hash_encoded), + server_hash, sizeof(server_hash)); + base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded), + server_nonce, sizeof(server_nonce)); + base16_encode(client_nonce_encoded, sizeof(client_nonce_encoded), + client_nonce, sizeof(client_nonce)); + + log_warn(LD_GENERAL, + "server_hash: '%s'\nserver_nonce: '%s'\nclient_nonce: '%s'", + server_hash_encoded, server_nonce_encoded, client_nonce_encoded); + } + + { /* write reply: (server_hash, server_nonce) */ + memcpy(reply, server_hash, EXT_OR_PORT_AUTH_HASH_LEN); + memcpy(reply + EXT_OR_PORT_AUTH_HASH_LEN, server_nonce, + EXT_OR_PORT_AUTH_NONCE_LEN); + connection_write_to_buf(reply, sizeof(reply), conn); + } + + log_warn(LD_GENERAL, "Got client nonce, and sent our own nonce and hash."); + + conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH; + return 1; +} + +#define connection_ext_or_auth_send_result_success(c) \ + connection_ext_or_auth_send_result(c, 1) +#define connection_ext_or_auth_send_result_fail(c) \ + connection_ext_or_auth_send_result(c, 0) + +/** Send authentication results to conn. Successful results if + * success is set; failure results otherwise. */ +static void +connection_ext_or_auth_send_result(connection_t *conn, int success) +{ + if (success) + connection_write_to_buf("\x01", 1, conn); + else + connection_write_to_buf("\x00", 1, conn); +} + +/** Receive the client's hash from conn, validate that it's + * correct, and then send the authentication results to the client. + * + * Return -1 if there was an error during validation; return 0 if we + * need more data in conn, and return 1 if we successfully + * validated the client's hash and sent a happy authentication + * result. */ +static int +connection_ext_or_auth_handle_client_hash(connection_t *conn) +{ + char provided_client_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0}; + + if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_HASH_LEN) + return 0; + + if (connection_fetch_from_buf(provided_client_hash, + EXT_OR_PORT_AUTH_HASH_LEN, conn) < 0) + return -1; + + if (tor_memneq(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash, + provided_client_hash, EXT_OR_PORT_AUTH_HASH_LEN)) { + log_warn(LD_GENERAL, "Incorrect client hash. Authentication failed."); + connection_ext_or_auth_send_result_fail(conn); + return -1; + } + + log_warn(LD_GENERAL, "Got client's hash and it was legit."); + + /* send positive auth result */ + connection_ext_or_auth_send_result_success(conn); + conn->state = EXT_OR_CONN_STATE_OPEN; + return 1; +} + +/** Handle data from or_conn received on Extended ORPort. + * Return -1 on error. 0 on unsufficient data. 1 on correct. */ +static int +connection_ext_or_auth_process_inbuf(or_connection_t *or_conn) +{ + connection_t *conn = TO_CONN(or_conn); + + switch (conn->state) { /* Functionify */ + case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE: + return connection_ext_or_auth_neg_auth_type(conn); + + case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE: + return connection_ext_or_auth_handle_client_nonce(conn); + + case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH: + return connection_ext_or_auth_handle_client_hash(conn); + + default: + log_warn(LD_BUG, "Encountered unexpected connection state %d while trying " + "to process Extended ORPort authentication data.", conn->state); + return -1; + } +} + +/** Extended ORPort commands (Transport-to-Bridge) */ +#define EXT_OR_CMD_TB_DONE 0x0000 +#define EXT_OR_CMD_TB_USERADDR 0x0001 + +/** Extended ORPort commands (Bridge-to-Transport) */ +#define EXT_OR_CMD_BT_OKAY 0x1000 +#define EXT_OR_CMD_BT_DENY 0x1001 +#define EXT_OR_CMD_BT_CONTROL 0x1002 + +/** Process a USERADDR command from the Extended + * ORPort. payload is a payload of size len. + * + * If the USERADDR command was well formed, change the address of + * conn to the address on the USERADDR command. + * + * Return 0 on success and -1 on error. */ +static int +connection_ext_or_handle_useraddr(connection_t *conn, + const char *payload, uint16_t len) +{ + /* Copy address string. */ + tor_addr_t addr; + uint16_t port; + char *addr_str; + char *address_part=NULL; + int res; + addr_str = tor_malloc(len + 1); + memcpy(addr_str, payload, len); + addr_str[len] = 0; + + res = tor_addr_port_split(LOG_INFO, addr_str, &address_part, &port); + tor_free(addr_str); + if (res<0) + return -1; + + res = tor_addr_parse(&addr, address_part); + tor_free(address_part); + if (res<0) + return -1; + + { /* do some logging */ + char *old_address = tor_dup_addr(&conn->addr); + char *new_address = tor_dup_addr(&addr); + + log_warn(LD_NET, "Received USERADDR." /* XXX Fix log severities/messages */ + "We rewrite our address from '%s:%u' to '%s:%u'.", + safe_str(old_address), conn->port, safe_str(new_address), port); + + tor_free(old_address); + tor_free(new_address); + } + + /* record the address */ + tor_addr_copy(&conn->addr, &addr); + conn->port = port; + + return 0; +} + +/** Process Extended ORPort messages from or_conn. */ +int +connection_ext_or_process_inbuf(or_connection_t *or_conn) +{ + connection_t *conn = TO_CONN(or_conn); + ext_or_cmd_t *command; + int r; + + /* If we are still in the authentication stage, process traffic as + authentication data: */ + while (conn->state <= EXT_OR_CONN_STATE_AUTH_MAX) { + log_warn(LD_GENERAL, "Got Extended ORPort authentication data (%u).", + (unsigned int) connection_get_inbuf_len(conn)); + r = connection_ext_or_auth_process_inbuf(or_conn); + if (r < 0) { + connection_mark_for_close(conn); + return -1; + } else if (r == 0) { + return 0; + } + /* if r > 0, loop and process more data (if any). */ + } + + while (1) { + log_warn(LD_GENERAL, "Got Extended ORPort data."); + command = NULL; + r = connection_fetch_ext_or_cmd_from_buf(conn, &command); + if (r < 0) + goto err; + else if (r == 0) + return 0; /* need to wait for more data */ + + /* Got a command! */ + tor_assert(command); + + if (command->cmd == EXT_OR_CMD_TB_DONE) { + if (connection_get_inbuf_len(conn)) { + /* The inbuf isn't empty; the client is misbehaving. */ + goto err; + } + + log_debug(LD_NET, "Received DONE."); + + connection_write_ext_or_command(conn, EXT_OR_CMD_BT_OKAY, NULL, 0); + + /* can't transition immediately; need to flush first. */ + conn->state = EXT_OR_CONN_STATE_FLUSHING; + connection_stop_reading(conn); + } else if (command->cmd == EXT_OR_CMD_TB_USERADDR) { + if (connection_ext_or_handle_useraddr(conn, + command->body, command->len) < 0) + goto err; + } else { + log_notice(LD_NET,"Got Extended ORPort command we don't regognize (%u).", + command->cmd); + } + + ext_or_cmd_free(command); + } + + return 0; + + err: + ext_or_cmd_free(command); + connection_mark_for_close(conn); + return -1; +} + +/** conn finished flushing Extended ORPort messages to the + * network, and is now ready to accept OR traffic. This function + * does the transition. */ +int +connection_ext_or_finished_flushing(or_connection_t *conn) +{ + if (conn->base_.state == EXT_OR_CONN_STATE_FLUSHING) { + connection_start_reading(TO_CONN(conn)); + connection_ext_or_transition(conn); + } + return 0; +} + +/** Initiate Extended ORPort authentication, by sending the list of + * supported authentication types to the client. */ +int +connection_ext_or_start_auth(or_connection_t *or_conn) +{ + connection_t *conn = TO_CONN(or_conn); + char authtypes[2] = "\x01\x00"; /* We only support authtype '1' for now. */ + + log_warn(LD_GENERAL, + "ExtORPort authentication: Sending supported authentication types"); + + connection_write_to_buf(authtypes, sizeof(authtypes), conn); + conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE; + + return 0; +} + diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h new file mode 100644 index 0000000000..fbd7ed6537 --- /dev/null +++ b/src/or/ext_orport.h @@ -0,0 +1,14 @@ +int connection_ext_or_start_auth(or_connection_t *or_conn); + +ext_or_cmd_t *ext_or_cmd_new(uint16_t len); +void ext_or_cmd_free(ext_or_cmd_t *cmd); +void connection_or_set_ext_or_identifier(or_connection_t *conn); +void connection_or_remove_from_ext_or_id_map(or_connection_t *conn); +void connection_or_clear_ext_or_id_map(void); + +int connection_ext_or_finished_flushing(or_connection_t *conn); +int connection_ext_or_process_inbuf(or_connection_t *or_conn); + +int init_ext_or_auth_cookie_authentication(int is_enabled); +char *get_ext_or_auth_cookie_file(void); + diff --git a/src/or/include.am b/src/or/include.am index f5002e6986..8922e03978 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -56,6 +56,7 @@ LIBTOR_A_SOURCES = \ src/or/fp_pair.c \ src/or/geoip.c \ src/or/entrynodes.c \ + src/or/ext_orport.c \ src/or/hibernate.c \ src/or/main.c \ src/or/microdesc.c \ @@ -153,6 +154,7 @@ ORHEADERS = \ src/or/dns.h \ src/or/dnsserv.h \ src/or/eventdns_tor.h \ + src/or/ext_orport.h \ src/or/fp_pair.h \ src/or/geoip.h \ src/or/entrynodes.h \ diff --git a/src/or/transports.c b/src/or/transports.c index 8beb9a5c73..196e18bba5 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -97,6 +97,7 @@ #include "statefile.h" #include "entrynodes.h" #include "connection_or.h" +#include "ext_orport.h" static process_environment_t * create_managed_proxy_environment(const managed_proxy_t *mp); -- cgit v1.2.3-54-g00ecf From c46f1b810dab79514d650f44ccf21ca4d24a20d5 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Wed, 5 Dec 2012 19:19:24 +0200 Subject: More Extended ORPort code improvements. * Change name of init_ext_or_auth_cookie_authentication(). * Add a small comment. --- src/or/config.c | 2 +- src/or/connection.c | 1 + src/or/ext_orport.c | 2 +- src/or/ext_orport.h | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/or/connection.c') diff --git a/src/or/config.c b/src/or/config.c index 542191dbeb..9c1505cf88 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1481,7 +1481,7 @@ options_act(const or_options_t *old_options) } /* If we have an ExtORPort, initialize its auth cookie. */ - if (init_ext_or_auth_cookie_authentication(!!options->ExtORPort_lines) < 0) { + if (init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) { log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file."); return -1; } diff --git a/src/or/connection.c b/src/or/connection.c index fcdc9ab996..6f66f797bd 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1406,6 +1406,7 @@ connection_init_accepted_conn(connection_t *conn, switch (conn->type) { case CONN_TYPE_EXT_OR: + /* Initiate Extended ORPort authentication. */ return connection_ext_or_start_auth(TO_OR_CONN(conn)); case CONN_TYPE_OR: control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0); diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c index 18ee5ab651..f44a3f5d51 100644 --- a/src/or/ext_orport.c +++ b/src/or/ext_orport.c @@ -128,7 +128,7 @@ get_ext_or_auth_cookie_file(void) * authorized to use the control connection. Return -1 if we can't * write the file, or 0 on success. */ int -init_ext_or_auth_cookie_authentication(int is_enabled) +init_ext_or_cookie_authentication(int is_enabled) { char *fname; char cookie_file_string[EXT_OR_PORT_AUTH_COOKIE_FILE_LEN]; diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h index fbd7ed6537..a7038b9188 100644 --- a/src/or/ext_orport.h +++ b/src/or/ext_orport.h @@ -9,6 +9,6 @@ void connection_or_clear_ext_or_id_map(void); int connection_ext_or_finished_flushing(or_connection_t *conn); int connection_ext_or_process_inbuf(or_connection_t *or_conn); -int init_ext_or_auth_cookie_authentication(int is_enabled); +int init_ext_or_cookie_authentication(int is_enabled); char *get_ext_or_auth_cookie_file(void); -- cgit v1.2.3-54-g00ecf From 210210f219a1773530dd117d7a48d6edc3a5e714 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Mon, 11 Feb 2013 20:45:17 +0100 Subject: Make the Extended ORPort understand the TRANSPORT command. --- src/or/connection.c | 1 + src/or/ext_orport.c | 43 ++++++++++++++++++++++++++++++++++++++++--- src/or/or.h | 3 +++ 3 files changed, 44 insertions(+), 3 deletions(-) (limited to 'src/or/connection.c') diff --git a/src/or/connection.c b/src/or/connection.c index 6f66f797bd..57a9c5838b 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -603,6 +603,7 @@ connection_free_(connection_t *conn) connection_or_remove_from_ext_or_id_map(TO_OR_CONN(conn)); tor_free(TO_OR_CONN(conn)->ext_or_conn_id); tor_free(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash); + tor_free(TO_OR_CONN(conn)->ext_or_transport); } #ifdef USE_BUFFEREVENTS diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c index db95843c7c..ff752f4865 100644 --- a/src/or/ext_orport.c +++ b/src/or/ext_orport.c @@ -12,6 +12,7 @@ #include "ext_orport.h" #include "control.h" #include "config.h" +#include "util.h" #include "main.h" /** Allocate and return a structure capable of holding an Extended @@ -381,6 +382,7 @@ connection_ext_or_auth_process_inbuf(or_connection_t *or_conn) /** Extended ORPort commands (Transport-to-Bridge) */ #define EXT_OR_CMD_TB_DONE 0x0000 #define EXT_OR_CMD_TB_USERADDR 0x0001 +#define EXT_OR_CMD_TB_TRANSPORT 0x0002 /** Extended ORPort commands (Bridge-to-Transport) */ #define EXT_OR_CMD_BT_OKAY 0x1000 @@ -395,8 +397,8 @@ connection_ext_or_auth_process_inbuf(or_connection_t *or_conn) * * Return 0 on success and -1 on error. */ static int -connection_ext_or_handle_useraddr(connection_t *conn, - const char *payload, uint16_t len) +connection_ext_or_handle_cmd_useraddr(connection_t *conn, + const char *payload, uint16_t len) { /* Copy address string. */ tor_addr_t addr; @@ -437,6 +439,32 @@ connection_ext_or_handle_useraddr(connection_t *conn, return 0; } +/** Process a TRANSPORT command from the Extended + * ORPort. payload is a payload of size len. + * + * If the TRANSPORT command was well formed, register the name of the + * transport on conn. + * + * Return 0 on success and -1 on error. */ +static int +connection_ext_or_handle_cmd_transport(or_connection_t *conn, + const char *payload, uint16_t len) +{ + char *transport_str = tor_malloc(len + 1); /* NUL-terminate the string */ + memcpy(transport_str, payload, len); + transport_str[len] = 0; + + /* Transport names MUST be C-identifiers. */ + if (!string_is_C_identifier(transport_str)) { + tor_free(transport_str); + return -1; + } + + conn->ext_or_transport = transport_str; + return 0; +} + + /** Process Extended ORPort messages from or_conn. */ int connection_ext_or_process_inbuf(or_connection_t *or_conn) @@ -480,15 +508,24 @@ connection_ext_or_process_inbuf(or_connection_t *or_conn) log_debug(LD_NET, "Received DONE."); + /* If the transport proxy did not use the TRANSPORT command to + * specify the transport name, mark this as unknown transport. */ + if (!or_conn->ext_or_transport) + or_conn->ext_or_transport = tor_strdup(""); + connection_write_ext_or_command(conn, EXT_OR_CMD_BT_OKAY, NULL, 0); /* can't transition immediately; need to flush first. */ conn->state = EXT_OR_CONN_STATE_FLUSHING; connection_stop_reading(conn); } else if (command->cmd == EXT_OR_CMD_TB_USERADDR) { - if (connection_ext_or_handle_useraddr(conn, + if (connection_ext_or_handle_cmd_useraddr(conn, command->body, command->len) < 0) goto err; + } else if (command->cmd == EXT_OR_CMD_TB_TRANSPORT) { + if (connection_ext_or_handle_cmd_transport(or_conn, + command->body, command->len) < 0) + goto err; } else { log_notice(LD_NET,"Got Extended ORPort command we don't regognize (%u).", command->cmd); diff --git a/src/or/or.h b/src/or/or.h index 63d137cf15..7916c476ad 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1452,6 +1452,9 @@ typedef struct or_connection_t { char *ext_or_conn_id; /** Client hash of the Extended ORPort authentication scheme */ char *ext_or_auth_correct_client_hash; + /** Name of the pluggable transport that is obfuscating this + connection. If no pluggable transports are used, it's NULL. */ + char *ext_or_transport; char *nickname; /**< Nickname of OR on other side (if any). */ -- cgit v1.2.3-54-g00ecf From c342ea98791ccbeb67b1255816ca2e92167cefb0 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 18 Jul 2013 15:51:29 -0400 Subject: Unit tests for ext_or_id_map. --- src/or/connection.c | 3 ++- src/or/connection.h | 4 +++ src/or/connection_or.c | 10 ++++++++ src/or/ext_orport.h | 1 + src/test/include.am | 1 + src/test/test.c | 2 ++ src/test/test_extorport.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++ src/test/test_options.c | 3 ++- 8 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 src/test/test_extorport.c (limited to 'src/or/connection.c') diff --git a/src/or/connection.c b/src/or/connection.c index 57a9c5838b..f1d7961a17 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -10,6 +10,7 @@ * on connections. **/ +#define CONNECTION_PRIVATE #include "or.h" #include "buffers.h" /* @@ -458,7 +459,7 @@ connection_link_connections(connection_t *conn_a, connection_t *conn_b) * necessary, close its socket if necessary, and mark the directory as dirty * if conn is an OR or OP connection. */ -static void +STATIC void connection_free_(connection_t *conn) { void *mem; diff --git a/src/or/connection.h b/src/or/connection.h index 5ca8ca3430..19f11c7439 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -214,5 +214,9 @@ void connection_enable_rate_limiting(connection_t *conn); #define connection_type_uses_bufferevent(c) (0) #endif +#ifdef CONNECTION_PRIVATE +STATIC void connection_free_(connection_t *conn); +#endif + #endif diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 3711cfeb33..a55ca3aa01 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -197,6 +197,16 @@ connection_or_remove_from_ext_or_id_map(or_connection_t *conn) memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN); } +/** Return the connection whose ext_or_id is id. Return NULL if no such + * connection is found. */ +or_connection_t * +connection_or_get_by_ext_or_id(const char *id) +{ + if (!orconn_ext_or_id_map) + return NULL; + return digestmap_get(orconn_ext_or_id_map, id); +} + /** Deallocate the global Extended ORPort identifier list */ void connection_or_clear_ext_or_id_map(void) diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h index 89c3032dfc..92ace7779c 100644 --- a/src/or/ext_orport.h +++ b/src/or/ext_orport.h @@ -14,6 +14,7 @@ void ext_or_cmd_free(ext_or_cmd_t *cmd); void connection_or_set_ext_or_identifier(or_connection_t *conn); void connection_or_remove_from_ext_or_id_map(or_connection_t *conn); void connection_or_clear_ext_or_id_map(void); +or_connection_t *connection_or_get_by_ext_or_id(const char *id); int connection_ext_or_finished_flushing(or_connection_t *conn); int connection_ext_or_process_inbuf(or_connection_t *or_conn); diff --git a/src/test/include.am b/src/test/include.am index 8718ce7c91..74311ac199 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -26,6 +26,7 @@ src_test_test_SOURCES = \ src/test/test_cell_queue.c \ src/test/test_data.c \ src/test/test_dir.c \ + src/test/test_extorport.c \ src/test/test_introduce.c \ src/test/test_microdesc.c \ src/test/test_options.c \ diff --git a/src/test/test.c b/src/test/test.c index a436688c57..851ddf026a 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1569,6 +1569,7 @@ extern struct testcase_t circuitlist_tests[]; extern struct testcase_t cell_queue_tests[]; extern struct testcase_t options_tests[]; extern struct testcase_t socks_tests[]; +extern struct testcase_t extorport_tests[]; static struct testgroup_t testgroups[] = { { "", test_array }, @@ -1588,6 +1589,7 @@ static struct testgroup_t testgroups[] = { { "introduce/", introduce_tests }, { "circuitlist/", circuitlist_tests }, { "options/", options_tests }, + { "extorport/", extorport_tests }, END_OF_GROUPS }; diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c new file mode 100644 index 0000000000..cfe810ef0b --- /dev/null +++ b/src/test/test_extorport.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONNECTION_PRIVATE +#include "or.h" +#include "connection.h" +#include "ext_orport.h" +#include "test.h" + +/* Test connection_or_remove_from_ext_or_id_map and + * connection_or_set_ext_or_identifier */ +static void +test_ext_or_id_map(void *arg) +{ + or_connection_t *c1 = NULL, *c2 = NULL, *c3 = NULL; + char *idp = NULL, *idp2 = NULL; + (void)arg; + + /* pre-initialization */ + tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx")); + + c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); + c2 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); + c3 = or_connection_new(CONN_TYPE_OR, AF_INET); + + tt_ptr_op(c1->ext_or_conn_id, !=, NULL); + tt_ptr_op(c2->ext_or_conn_id, !=, NULL); + tt_ptr_op(c3->ext_or_conn_id, ==, NULL); + + tt_ptr_op(c1, ==, connection_or_get_by_ext_or_id(c1->ext_or_conn_id)); + tt_ptr_op(c2, ==, connection_or_get_by_ext_or_id(c2->ext_or_conn_id)); + tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx")); + + idp = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN); + + /* Give c2 a new ID. */ + connection_or_set_ext_or_identifier(c2); + test_mem_op(idp, !=, c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN); + idp2 = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN); + tt_assert(!tor_digest_is_zero(idp2)); + + tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp)); + tt_ptr_op(c2, ==, connection_or_get_by_ext_or_id(idp2)); + + /* Now remove it. */ + connection_or_remove_from_ext_or_id_map(c2); + tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp)); + tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp2)); + + done: + if (c1) + connection_free_(TO_CONN(c1)); + if (c2) + connection_free_(TO_CONN(c2)); + if (c3) + connection_free_(TO_CONN(c3)); + tor_free(idp); + tor_free(idp2); + connection_or_clear_ext_or_id_map(); +} + +struct testcase_t extorport_tests[] = { + { "id_map", test_ext_or_id_map, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; diff --git a/src/test/test_options.c b/src/test/test_options.c index 6beff2567e..737f658e2c 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -148,6 +148,8 @@ test_options_validate(void *arg) (void)arg; setup_log_callback(); + WANT_ERR("ExtORPort 500000", "Invalid ExtORPort"); + WANT_ERR_LOG("ServerTransportOptions trebuchet", "ServerTransportOptions did not parse", LOG_WARN, "Too few arguments"); @@ -157,7 +159,6 @@ test_options_validate(void *arg) "ServerTransportOptions did not parse", LOG_WARN, "\"slingsnappy\" is not a k=v"); -// done: clear_log_messages(); return; } -- cgit v1.2.3-54-g00ecf From 03e3881043de68f371883fdb82a1d2bebf4179ed Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 18 Jul 2013 16:23:48 -0400 Subject: Tests for connection_write_ext_or_command. --- src/or/connection.c | 6 ++-- src/or/connection.h | 4 +-- src/or/ext_orport.c | 3 +- src/or/ext_orport.h | 7 ++++ src/test/test_extorport.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 6 deletions(-) (limited to 'src/or/connection.c') diff --git a/src/or/connection.c b/src/or/connection.c index f1d7961a17..6c95245b57 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -3714,9 +3714,9 @@ connection_flush(connection_t *conn) * it all, so we don't end up with many megabytes of controller info queued at * once. */ -void -connection_write_to_buf_impl_(const char *string, size_t len, - connection_t *conn, int zlib) +MOCK_IMPL(void, +connection_write_to_buf_impl_,(const char *string, size_t len, + connection_t *conn, int zlib)) { /* XXXX This function really needs to return -1 on failure. */ int r; diff --git a/src/or/connection.h b/src/or/connection.h index 19f11c7439..0454ac2f36 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -130,8 +130,8 @@ int connection_outbuf_too_full(connection_t *conn); int connection_handle_write(connection_t *conn, int force); int connection_flush(connection_t *conn); -void connection_write_to_buf_impl_(const char *string, size_t len, - connection_t *conn, int zlib); +MOCK_DECL(void, connection_write_to_buf_impl_, + (const char *string, size_t len, connection_t *conn, int zlib)); /* DOCDOC connection_write_to_buf */ static void connection_write_to_buf(const char *string, size_t len, connection_t *conn); diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c index e0980dedac..b1bb11bd46 100644 --- a/src/or/ext_orport.c +++ b/src/or/ext_orport.c @@ -6,6 +6,7 @@ * \brief Code implementing the Extended ORPort. */ +#define EXT_ORPORT_PRIVATE #include "or.h" #include "connection.h" #include "connection_or.h" @@ -52,7 +53,7 @@ connection_fetch_ext_or_cmd_from_buf(connection_t *conn, ext_or_cmd_t **out) * command as the command type, bodylen as the body * length, and body, if it's present, as the body of the * message. */ -static int +STATIC int connection_write_ext_or_command(connection_t *conn, uint16_t command, const char *body, diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h index 92ace7779c..35b92ad63f 100644 --- a/src/or/ext_orport.h +++ b/src/or/ext_orport.h @@ -22,5 +22,12 @@ int connection_ext_or_process_inbuf(or_connection_t *or_conn); int init_ext_or_cookie_authentication(int is_enabled); char *get_ext_or_auth_cookie_file_name(void); +#ifdef EXT_ORPORT_PRIVATE +STATIC int connection_write_ext_or_command(connection_t *conn, + uint16_t command, + const char *body, + size_t bodylen); +#endif + #endif diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c index cfe810ef0b..525ac4f191 100644 --- a/src/test/test_extorport.c +++ b/src/test/test_extorport.c @@ -2,7 +2,9 @@ /* See LICENSE for licensing information */ #define CONNECTION_PRIVATE +#define EXT_ORPORT_PRIVATE #include "or.h" +#include "buffers.h" #include "connection.h" #include "ext_orport.h" #include "test.h" @@ -59,7 +61,92 @@ test_ext_or_id_map(void *arg) connection_or_clear_ext_or_id_map(); } +/* Simple connection_write_to_buf_impl_ replacement that unconditionally + * writes to outbuf. */ +static void +connection_write_to_buf_impl_replacement(const char *string, size_t len, + connection_t *conn, int zlib) +{ + (void) zlib; + + tor_assert(string); + tor_assert(conn); + write_to_buf(string, len, conn->outbuf); +} + +static char * +buf_get_contents(buf_t *buf, size_t *sz_out) +{ + char *out; + *sz_out = buf_datalen(buf); + if (*sz_out >= ULONG_MAX) + return NULL; /* C'mon, really? */ + out = tor_malloc(*sz_out + 1); + if (fetch_from_buf(out, (unsigned long)*sz_out, buf) != 0) { + tor_free(out); + return NULL; + } + out[*sz_out] = '\0'; /* Hopefully gratuitous. */ + return out; +} + +static void +test_ext_or_write_command(void *arg) +{ + or_connection_t *c1; + char *cp = NULL; + char *buf = NULL; + size_t sz; + + (void) arg; + MOCK(connection_write_to_buf_impl_, + connection_write_to_buf_impl_replacement); + + c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); + tt_assert(c1); + + /* Length too long */ + tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 100, "X", 100000), + <, 0); + + /* Empty command */ + tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0x99, NULL, 0), + ==, 0); + cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz); + tt_int_op(sz, ==, 4); + test_mem_op(cp, ==, "\x00\x99\x00\x00", 4); + tor_free(cp); + + /* Medium command. */ + tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0x99, + "Wai\0Hello", 9), ==, 0); + cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz); + tt_int_op(sz, ==, 13); + test_mem_op(cp, ==, "\x00\x99\x00\x09Wai\x00Hello", 13); + tor_free(cp); + + /* Long command */ + buf = tor_malloc(65535); + memset(buf, 'x', 65535); + tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0xf00d, + buf, 65535), ==, 0); + cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz); + tt_int_op(sz, ==, 65539); + test_mem_op(cp, ==, "\xf0\x0d\xff\xff", 4); + test_mem_op(cp+4, ==, buf, 65535); + tor_free(cp); + + done: + if (c1) + connection_free_(TO_CONN(c1)); + tor_free(cp); + tor_free(buf); + UNMOCK(connection_write_to_buf_impl_); +} + struct testcase_t extorport_tests[] = { { "id_map", test_ext_or_id_map, TT_FORK, NULL, NULL }, + { "write_command", test_ext_or_write_command, TT_FORK, NULL, NULL }, END_OF_TESTCASES }; + -- cgit v1.2.3-54-g00ecf