random_uint32() is a xorshift32 PRNG (see lib/random.c): it holds many properties including uniformity and reversibility, so recovering the internal state from any single observed output lets an attacker compute every other output of that (per-thread) stream, forward and backward. Using it for anything security-relevant is documented in random.c itself as inappropriate.
Most NAT tuple selection does not call random_uint32() directly Instead, nat_get_unique_tuple() picks both the NAT address (get_addr_in_range()) and, in the common case, the NAT port (set_sport_range()/set_dport_range()) via nat_range_hash(), a deterministic hash of the flow tuple mixed with a single 32-bit value: ct->hash_basis. That basis was previously drawn once from random_uint32() at conntrack_init() time. Because it is a single, long-lived value on which every NAT address and default port choice depends, an attacker able to recover the xorshift state feeding that one random_uint32() call, e.g. by observing any other output drawn from the same per-thread PRNG stream elsewhere in the process, could predict every NAT tuple ovs-vswitchd will assign. In effect, it was derived from a shared, reversible state. Two narrower paths draw directly from random_uint32() per new connection rather than through the hash: the 'nat(...,random)' port path, and the retry offset used when the hash-selected port collides with an existing connection under range congestion. These are directly observable per-connection outputs. Fix this by drawing ct->hash_basis, and the two per-connection port paths above, from a cryptographic source: the recently added CSPRNG support in random.c which is a ChaCha20 based CSPRNG. Signed-off-by: Aaron Conole <[email protected]> --- lib/conntrack.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/conntrack.c b/lib/conntrack.c index f84cdd216a..9b322c93ea 100644 --- a/lib/conntrack.c +++ b/lib/conntrack.c @@ -55,6 +55,17 @@ COVERAGE_DEFINE(conntrack_l4csum_err); COVERAGE_DEFINE(conntrack_lookup_natted_miss); COVERAGE_DEFINE(conntrack_zone_full); +/* NAT tuple selection (both the address/port hash basis and the + * fully-random port paths) must not be predictable from an observed + * random_uint32() output, so it must draw from a cryptographic + * source here instead of the general-purpose xorshift32 PRNG in + * random.c. */ +static uint32_t +nat_random_uint32(void) +{ + return cs_random_uint32(); +} + struct conn_lookup_ctx { struct conn_key key; struct conn *conn; @@ -252,7 +263,7 @@ conntrack_init(void) /* This value can be used during init (e.g. timeout_policy_init()), * set it first to ensure it is available. */ - ct->hash_basis = random_uint32(); + ct->hash_basis = nat_random_uint32(); ovs_rwlock_init(&ct->resources_lock); ovs_rwlock_wrlock(&ct->resources_lock); @@ -2557,7 +2568,7 @@ another_round: if (attempts < range && attempts >= 16) { attempts /= 2; - curr = min + (random_uint32() % range); + curr = min + (nat_random_uint32() % range); goto another_round; } @@ -2613,7 +2624,7 @@ nat_get_unique_tuple(struct conntrack *ct, struct conn *conn, hash = nat_range_hash(fwd_key, basis, nat_info); if (nat_info->nat_flags & NAT_RANGE_RANDOM) { - port_off = random_uint32(); + port_off = nat_random_uint32(); } else if (basis) { port_off = hash; } else { -- 2.55.0 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
