mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-31 06:23:52 +00:00
Fix findings from the security audit
Remote: - TCP: handshake/connect deadlines follow --timeout instead of a 24h minimum, so idle sockets can no longer fill the connection pool. - ACL: IPv4-mapped IPv6 addresses are matched against the IPv4 lists. - UDP server now enforces the peer ACL and the outbound block list. - ACL loading fails closed on bad files or invalid entries. - TCP half-close is relayed instead of tearing the connection down. - AEAD-2022 UDP sessions are created only after timestamp/replay checks. ss-manager: - Owner-only conf files (deleted on remove), 0600 control socket, umask 022, safe working directory, loopback-only IP addresses, per-user sender check on Linux, plugins only over the UNIX socket. - Port canonicalisation, list() overflow, add/remove waits for the server, stale pid files can't signal unrelated processes. Also: plugins drop to --user, daemonize readiness/pid file handling, resolver cancellation, replay-filter fail-closed, key wiping, spec strictness in AEAD/2022 parsing, JSON surrogate validation, ss:// plugin name checks, embedding API state reset, Android protect_socket replies. Adds tests/test_server_acl.py and tests/test_half_close.py and extends the manager, ACL, AEAD-2022 and ssurl tests. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -351,6 +351,41 @@ void udp_window()
|
||||
SS_CHECK(pkt && !sc::udp2022_accept(sc::Role::server, server, *pkt, out, std::time(nullptr) + 31));
|
||||
}
|
||||
|
||||
// A client keeps the replay window of the server session it heard from
|
||||
// before, so a server that starts a new session cannot have its old replies
|
||||
// replayed into a fresh window.
|
||||
void udp_server_session_change()
|
||||
{
|
||||
sc::Psk psk = psk_of("2022-blake3-aes-128-gcm");
|
||||
sc::UdpSession2022 client, server1, server2;
|
||||
std::uint8_t plain[sizeof(kAddr)];
|
||||
std::memcpy(plain, kAddr, sizeof(kAddr));
|
||||
std::vector<std::uint8_t> req(sizeof(plain) + sc::udp2022_overhead(psk, sc::Role::client));
|
||||
SS_CHECK(sc::udp2022_seal(psk, sc::Role::client, client, plain, req));
|
||||
for (sc::UdpSession2022 *server : { &server1, &server2 }) {
|
||||
std::vector<std::uint8_t> out(req.size());
|
||||
auto pkt = sc::udp2022_decrypt(psk, req, out);
|
||||
SS_CHECK(pkt && sc::udp2022_accept(sc::Role::server, *server, *pkt, out, std::time(nullptr)));
|
||||
}
|
||||
auto reply = [&](sc::UdpSession2022 &server) {
|
||||
std::vector<std::uint8_t> p(sizeof(plain) + sc::udp2022_overhead(psk, sc::Role::server));
|
||||
SS_CHECK(sc::udp2022_seal(psk, sc::Role::server, server, plain, p));
|
||||
return p;
|
||||
};
|
||||
auto deliver = [&](const std::vector<std::uint8_t> &p) {
|
||||
std::vector<std::uint8_t> out(p.size());
|
||||
auto pkt = sc::udp2022_decrypt(psk, p, out);
|
||||
SS_CHECK(pkt);
|
||||
return static_cast<bool>(sc::udp2022_accept(sc::Role::client, client, *pkt, out, std::time(nullptr)));
|
||||
};
|
||||
auto a1 = reply(server1), b1 = reply(server1), a2 = reply(server2);
|
||||
SS_CHECK(deliver(a1));
|
||||
SS_CHECK(deliver(a2)); // the server moved to a new session
|
||||
SS_CHECK(!deliver(a1)); // a replay of the old session is still caught
|
||||
SS_CHECK(deliver(b1)); // a late packet of the old session is not
|
||||
SS_CHECK(!deliver(a2) && !deliver(b1));
|
||||
}
|
||||
|
||||
void salt_pool()
|
||||
{
|
||||
sc::SaltPool pool;
|
||||
@@ -358,7 +393,11 @@ void salt_pool()
|
||||
std::uint8_t a[32] = { 1 }, b[32] = { 2 }, d[32] = { 3 };
|
||||
SS_CHECK(pool.check_and_add(a, 100) && !pool.check_and_add(a, 159));
|
||||
SS_CHECK(pool.check_and_add(a, 160)); // retention over
|
||||
SS_CHECK(pool.check_and_add(b, 161) && pool.check_and_add(d, 161) && pool.size() == 2);
|
||||
SS_CHECK(pool.check_and_add(b, 161) && pool.size() == 2);
|
||||
// Full of salts still inside the window: a new one is refused rather
|
||||
// than evicting one that could then be replayed.
|
||||
SS_CHECK(!pool.check_and_add(d, 161) && pool.size() == 2 && !pool.check_and_add(a, 161));
|
||||
SS_CHECK(pool.check_and_add(d, 221)); // a and b have expired
|
||||
}
|
||||
|
||||
void steady(const sc::Psk &psk, sc::SaltPool &pool)
|
||||
@@ -395,6 +434,7 @@ void test_methods()
|
||||
}
|
||||
tcp_rejects();
|
||||
udp_window();
|
||||
udp_server_session_change();
|
||||
salt_pool();
|
||||
SS_CHECK(!sc::find_aead2022("aes-256-gcm"));
|
||||
auto spec = sc::find_aead2022("2022-blake3-aes-256-gcm");
|
||||
|
||||
Reference in New Issue
Block a user