diff options
author | George Kadianakis <desnacked@riseup.net> | 2019-11-18 19:06:53 +0200 |
---|---|---|
committer | George Kadianakis <desnacked@riseup.net> | 2019-11-18 19:06:53 +0200 |
commit | d28b6792cb99f25d42607eb46985ac4553013dd8 (patch) | |
tree | 62209d549df1c21f4502c99fa3a96fa028ba5421 /src/core/or | |
parent | c34fb3413dee5be00be7299a63c294ddb86b0599 (diff) | |
parent | d60ed5a6a2586bdf4feb266e44b7db4703d3c4a6 (diff) | |
download | tor-d28b6792cb99f25d42607eb46985ac4553013dd8.tar.gz tor-d28b6792cb99f25d42607eb46985ac4553013dd8.zip |
Merge branch 'tor-github/pr/1423'
Diffstat (limited to 'src/core/or')
-rw-r--r-- | src/core/or/connection_edge.c | 179 | ||||
-rw-r--r-- | src/core/or/connection_edge.h | 18 | ||||
-rw-r--r-- | src/core/or/entry_port_cfg_st.h | 3 | ||||
-rw-r--r-- | src/core/or/socks_request_st.h | 7 |
4 files changed, 130 insertions, 77 deletions
diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 5f1664d286..4b4bcff2f4 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -1553,6 +1553,102 @@ consider_plaintext_ports(entry_connection_t *conn, uint16_t port) return 0; } +/** Parse the given hostname in address. Returns true if the parsing was + * successful and type_out contains the type of the hostname. Else, false is + * returned which means it was not recognized and type_out is set to + * BAD_HOSTNAME. + * + * The possible recognized forms are (where true is returned): + * + * If address is of the form "y.onion" with a well-formed handle y: + * Put a NUL after y, lower-case it, and return ONION_V2_HOSTNAME or + * ONION_V3_HOSTNAME depending on the HS version. + * + * If address is of the form "x.y.onion" with a well-formed handle x: + * Drop "x.", put a NUL after y, lower-case it, and return + * ONION_V2_HOSTNAME or ONION_V3_HOSTNAME depending on the HS version. + * + * If address is of the form "y.onion" with a badly-formed handle y: + * Return BAD_HOSTNAME and log a message. + * + * If address is of the form "y.exit": + * Put a NUL after y and return EXIT_HOSTNAME. + * + * Otherwise: + * Return NORMAL_HOSTNAME and change nothing. + */ +STATIC bool +parse_extended_hostname(char *address, hostname_type_t *type_out) +{ + char *s; + char *q; + char query[HS_SERVICE_ADDR_LEN_BASE32+1]; + + s = strrchr(address,'.'); + if (!s) { + *type_out = NORMAL_HOSTNAME; /* no dot, thus normal */ + goto success; + } + if (!strcmp(s+1,"exit")) { + *s = 0; /* NUL-terminate it */ + *type_out = EXIT_HOSTNAME; /* .exit */ + goto success; + } + if (strcmp(s+1,"onion")) { + *type_out = NORMAL_HOSTNAME; /* neither .exit nor .onion, thus normal */ + goto success; + } + + /* so it is .onion */ + *s = 0; /* NUL-terminate it */ + /* locate a 'sub-domain' component, in order to remove it */ + q = strrchr(address, '.'); + if (q == address) { + *type_out = BAD_HOSTNAME; + goto failed; /* reject sub-domain, as DNS does */ + } + q = (NULL == q) ? address : q + 1; + if (strlcpy(query, q, HS_SERVICE_ADDR_LEN_BASE32+1) >= + HS_SERVICE_ADDR_LEN_BASE32+1) { + *type_out = BAD_HOSTNAME; + goto failed; + } + if (q != address) { + memmove(address, q, strlen(q) + 1 /* also get \0 */); + } + /* v2 onion address check. */ + if (strlen(query) == REND_SERVICE_ID_LEN_BASE32) { + *type_out = ONION_V2_HOSTNAME; + if (rend_valid_v2_service_id(query)) { + goto success; + } + goto failed; + } + + /* v3 onion address check. */ + if (strlen(query) == HS_SERVICE_ADDR_LEN_BASE32) { + *type_out = ONION_V3_HOSTNAME; + if (hs_address_is_valid(query)) { + goto success; + } + goto failed; + } + + /* Reaching this point, nothing was recognized. */ + *type_out = BAD_HOSTNAME; + goto failed; + + success: + return true; + failed: + /* otherwise, return to previous state and return 0 */ + *s = '.'; + log_warn(LD_APP, "Invalid %shostname %s; rejecting", + (*type_out == (ONION_V2_HOSTNAME || ONION_V3_HOSTNAME) ? "onion " : ""), + safe_str_client(address)); + return false; +} + /** How many times do we try connecting with an exit configured via * TrackHostExits before concluding that it won't work any more and trying a * different one? */ @@ -2020,16 +2116,15 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, const int automap = rr.automap; const addressmap_entry_source_t exit_source = rr.exit_source; - /* Now, we parse the address to see if it's an .onion or .exit or - * other special address. - */ - const hostname_type_t addresstype = parse_extended_hostname(socks->address); - /* Now see whether the hostname is bogus. This could happen because of an * onion hostname whose format we don't recognize. */ - if (addresstype == BAD_HOSTNAME) { + hostname_type_t addresstype; + if (!parse_extended_hostname(socks->address, &addresstype)) { control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s", escaped(socks->address)); + if (addresstype == ONION_V3_HOSTNAME) { + conn->socks_request->socks_extended_error_code = SOCKS5_HS_IS_INVALID; + } connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); return -1; } @@ -3522,11 +3617,17 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply, size_t replylen, int endreason) { char buf[256]; - socks5_reply_status_t status = - stream_end_reason_to_socks5_response(endreason); + socks5_reply_status_t status; tor_assert(conn->socks_request); /* make sure it's an AP stream */ + if (conn->socks_request->socks_use_extended_errors && + conn->socks_request->socks_extended_error_code != 0) { + status = conn->socks_request->socks_extended_error_code; + } else { + status = stream_end_reason_to_socks5_response(endreason); + } + if (!SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) { control_event_stream_status(conn, status==SOCKS5_SUCCEEDED ? STREAM_EVENT_SUCCEEDED : STREAM_EVENT_FAILED, @@ -4306,68 +4407,6 @@ connection_ap_can_use_exit(const entry_connection_t *conn, return 1; } -/** If address is of the form "y.onion" with a well-formed handle y: - * Put a NUL after y, lower-case it, and return ONION_V2_HOSTNAME or - * ONION_V3_HOSTNAME depending on the HS version. - * - * If address is of the form "x.y.onion" with a well-formed handle x: - * Drop "x.", put a NUL after y, lower-case it, and return - * ONION_V2_HOSTNAME or ONION_V3_HOSTNAME depending on the HS version. - * - * If address is of the form "y.onion" with a badly-formed handle y: - * Return BAD_HOSTNAME and log a message. - * - * If address is of the form "y.exit": - * Put a NUL after y and return EXIT_HOSTNAME. - * - * Otherwise: - * Return NORMAL_HOSTNAME and change nothing. - */ -hostname_type_t -parse_extended_hostname(char *address) -{ - char *s; - char *q; - char query[HS_SERVICE_ADDR_LEN_BASE32+1]; - - s = strrchr(address,'.'); - if (!s) - return NORMAL_HOSTNAME; /* no dot, thus normal */ - if (!strcmp(s+1,"exit")) { - *s = 0; /* NUL-terminate it */ - return EXIT_HOSTNAME; /* .exit */ - } - if (strcmp(s+1,"onion")) - return NORMAL_HOSTNAME; /* neither .exit nor .onion, thus normal */ - - /* so it is .onion */ - *s = 0; /* NUL-terminate it */ - /* locate a 'sub-domain' component, in order to remove it */ - q = strrchr(address, '.'); - if (q == address) { - goto failed; /* reject sub-domain, as DNS does */ - } - q = (NULL == q) ? address : q + 1; - if (strlcpy(query, q, HS_SERVICE_ADDR_LEN_BASE32+1) >= - HS_SERVICE_ADDR_LEN_BASE32+1) - goto failed; - if (q != address) { - memmove(address, q, strlen(q) + 1 /* also get \0 */); - } - if (rend_valid_v2_service_id(query)) { - return ONION_V2_HOSTNAME; /* success */ - } - if (hs_address_is_valid(query)) { - return ONION_V3_HOSTNAME; - } - failed: - /* otherwise, return to previous state and return 0 */ - *s = '.'; - log_warn(LD_APP, "Invalid onion hostname %s; rejecting", - safe_str_client(address)); - return BAD_HOSTNAME; -} - /** Return true iff the (possibly NULL) <b>alen</b>-byte chunk of memory at * <b>a</b> is equal to the (possibly NULL) <b>blen</b>-byte chunk of memory * at <b>b</b>. */ diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h index e82b6bd765..cda087b163 100644 --- a/src/core/or/connection_edge.h +++ b/src/core/or/connection_edge.h @@ -71,6 +71,15 @@ entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *); #define connection_mark_unattached_ap(conn, endreason) \ connection_mark_unattached_ap_((conn), (endreason), __LINE__, SHORT_FILE__) +/** Possible return values for parse_extended_hostname. */ +typedef enum hostname_type_t { + BAD_HOSTNAME, + EXIT_HOSTNAME, + NORMAL_HOSTNAME, + ONION_V2_HOSTNAME, + ONION_V3_HOSTNAME, +} hostname_type_t; + MOCK_DECL(void,connection_mark_unattached_ap_, (entry_connection_t *conn, int endreason, int line, const char *file)); @@ -155,13 +164,6 @@ int connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, origin_circuit_t *circ, crypt_path_t *cpath); -/** Possible return values for parse_extended_hostname. */ -typedef enum hostname_type_t { - NORMAL_HOSTNAME, ONION_V2_HOSTNAME, ONION_V3_HOSTNAME, - EXIT_HOSTNAME, BAD_HOSTNAME -} hostname_type_t; -hostname_type_t parse_extended_hostname(char *address); - #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) int get_pf_socket(void); #endif @@ -219,6 +221,8 @@ void half_edge_free_(struct half_edge_t *he); #ifdef CONNECTION_EDGE_PRIVATE +STATIC bool parse_extended_hostname(char *address, hostname_type_t *type_out); + /** A parsed BEGIN or BEGIN_DIR cell */ typedef struct begin_cell_t { /** The address the client has asked us to connect to, or NULL if this is diff --git a/src/core/or/entry_port_cfg_st.h b/src/core/or/entry_port_cfg_st.h index f52f47d1c9..174d420c12 100644 --- a/src/core/or/entry_port_cfg_st.h +++ b/src/core/or/entry_port_cfg_st.h @@ -53,6 +53,9 @@ struct entry_port_cfg_t { * do we prefer IPv6? */ unsigned int prefer_ipv6_virtaddr : 1; + /** For socks listeners: can we send back the extended SOCKS5 error code? */ + unsigned int extended_socks5_codes : 1; + }; #endif /* !defined(ENTRY_PORT_CFG_ST_H) */ diff --git a/src/core/or/socks_request_st.h b/src/core/or/socks_request_st.h index 2931543ee2..0396f5abbd 100644 --- a/src/core/or/socks_request_st.h +++ b/src/core/or/socks_request_st.h @@ -12,6 +12,8 @@ #ifndef SOCKS_REQUEST_ST_H #define SOCKS_REQUEST_ST_H +#include "lib/net/socks5_status.h" + #define MAX_SOCKS_REPLY_LEN 1024 #define SOCKS_NO_AUTH 0x00 @@ -63,6 +65,11 @@ struct socks_request_t { * "username/password" authentication if both are offered. Used as input to * parse_socks. */ unsigned int socks_prefer_no_auth : 1; + /** If set, we can send back the extended error code in the reply. */ + unsigned int socks_use_extended_errors : 1; + /** If non zero, this contains the extended error code that should be used + * if the port was configured to use them. */ + socks5_reply_status_t socks_extended_error_code; /** Number of bytes in username; 0 if username is NULL */ size_t usernamelen; |