Aaron Conole <[email protected]> writes:

> 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: OpenSSL's PRNG when
> compiled against OpenSSL 1.1.0+ (checking RAND_status() to catch the
> case where it has not been seeded properly despite RAND_bytes()
> succeeding, the same check already used in lib/stream-ssl.c), falling
> back to the system entropy pool.
>
> If neither source can provide randomness, do not silently downgrade
> to the non-cryptographic PRNG.  nat_random_uint32() instead reports
> failure, and its callers propagate that as NAT tuple exhaustion:
> nat_get_unique_l4() and nat_get_unique_tuple() both return false, so
> the connection attempt is failed the same way as any other allocation
> exhaustion (see the nat_res_exhaustion path in conn_update_state()),
> and each occurrence increments the new conntrack_entropy_failed
> coverage counter so operators can see it happening.  There is also a
> new point for ovs-vswitchd failure - at the conntrack initialization
> if sufficient entropy cannot be pulled for ct->hash_basis.
>
> Signed-off-by: Aaron Conole <[email protected]>
> ---

Fixes: 165f5fbb5e0b ("conntrack: Limit port clash resolution attempts.")
Fixes: afdc1171a8f1 ("conntrack: Handle persistent selection for IP addresses.")
Fixes: 286de2729955 ("dpdk: Userspace Datapath: Introduce NAT Support.")

The last commit listed above has been mangled by many other intervening
commits.  Irrespective of that, this should be backported to all of the
supported branches.

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to