commit 7ea0e041aec14f6d8c544be1d4d223b7648c5c61
parent 11542a3d04bb746e4d2ba83c3127f2e6b9292afc
Author: Nick Mathewson <nickm@torproject.org>
Date: Tue, 4 Nov 2025 10:10:54 -0500
HTTP CONNECT: Warn on Proxy-Authorization in legacy format.
To avoid confusing with other apps, we want applications to
deliberately choose a format that indicates that they know they're using Tor
for isolation. That's "Basic" auth, with username "tor" and
password chosen for isolation.
Diffstat:
3 files changed, 62 insertions(+), 1 deletion(-)
diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c
@@ -3049,6 +3049,37 @@ host_header_is_localhost(const char *host_value)
return result;
}
+/** Return true if the Proxy-Authorization header present in 'auth'
+ * isn't using the "modern" format introduced by proposal 365,
+ * with "basic" auth and username "tor". */
+STATIC bool
+using_old_proxy_auth(const char *auth)
+{
+ auth = eat_whitespace(auth);
+ if (strcasecmpstart(auth, "Basic ")) {
+ // Not Basic.
+ return true;
+ }
+ auth += strlen("Basic ");
+ auth = eat_whitespace(auth);
+
+ ssize_t clen = base64_decode_maxsize(strlen(auth)) + 1;
+ char *credential = tor_malloc_zero(clen);
+ ssize_t n = base64_decode(credential, clen, auth, strlen(auth));
+ if (n < 0 || BUG(n >= clen)) {
+ // not base64, or somehow too long.
+ tor_free(credential);
+ return true;
+ }
+ // nul-terminate.
+ credential[n] = 0;
+
+ bool username_is_modern = ! strcmpstart(credential, "tor:");
+ tor_free(credential);
+
+ return ! username_is_modern;
+}
+
/** Called on an HTTP CONNECT entry connection when some bytes have arrived,
* but we have not yet received a full HTTP CONNECT request. Try to parse an
* HTTP CONNECT request from the connection's inbuf. On success, set up the
@@ -3144,6 +3175,10 @@ connection_ap_process_http_connect(entry_connection_t *conn)
{
char *authorization = http_get_header(headers, "Proxy-Authorization: ");
if (authorization) {
+ if (using_old_proxy_auth(authorization)) {
+ log_warn(LD_GENERAL, "Proxy-Authorization header in legacy format. "
+ "With modern Tor, use Basic auth with username=tor.");
+ }
socks->username = authorization; // steal reference
socks->usernamelen = strlen(authorization);
}
diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h
@@ -310,6 +310,7 @@ STATIC void connection_half_edge_add(const edge_connection_t *conn,
STATIC struct half_edge_t *connection_half_edge_find_stream_id(
const smartlist_t *half_conns,
streamid_t stream_id);
+STATIC bool using_old_proxy_auth(const char *auth);
#endif /* defined(CONNECTION_EDGE_PRIVATE) */
#endif /* !defined(TOR_CONNECTION_EDGE_H) */
diff --git a/src/test/test_proto_http.c b/src/test/test_proto_http.c
@@ -6,11 +6,14 @@
* \brief Tests for our HTTP protocol parser code
*/
+#define CONNECTION_EDGE_PRIVATE
+
#include "core/or/or.h"
#include "test/test.h"
#include "lib/buf/buffers.h"
#include "core/proto/proto_http.h"
#include "test/log_test_helpers.h"
+#include "core/or/connection_edge.h"
#define S(str) str, sizeof(str)-1
@@ -203,11 +206,33 @@ test_proto_http_invalid(void *arg)
teardown_capture_of_logs();
}
+static void
+test_proto_http_proxy_auth(void *arg)
+{
+ (void)arg;
+
+ tt_assert(using_old_proxy_auth(""));
+ tt_assert(using_old_proxy_auth("Foo Bar"));
+ tt_assert(using_old_proxy_auth("Basicish Bar"));
+ tt_assert(using_old_proxy_auth("Basic"));
+ tt_assert(using_old_proxy_auth("Basic x"));
+ // encodes foo:bar
+ tt_assert(using_old_proxy_auth("Basic Zm9vOmJhcg=="));
+ // encodes torx:bar
+ tt_assert(using_old_proxy_auth("Basic dG9yeDpiYXI="));
+
+ // encodes tor:random
+ tt_assert(! using_old_proxy_auth("Basic dG9yOnJhbmRvbQ=="));
+
+ done:
+ ;
+}
+
struct testcase_t proto_http_tests[] = {
{ "peek", test_proto_http_peek, 0, NULL, NULL },
{ "valid", test_proto_http_valid, 0, NULL, NULL },
{ "invalid", test_proto_http_invalid, 0, NULL, NULL },
+ { "proxyauth", test_proto_http_proxy_auth, 0, NULL, NULL },
END_OF_TESTCASES
};
-