From fac4b143587188d283578bc51e13ca44bcfc6382 Mon Sep 17 00:00:00 2001 From: tv Date: Sun, 8 Nov 2026 14:15:26 +0000 Subject: token: a banned node's tokens aren't taken Review finding 11: token::verify checked only that the issuer is a member, and a ban leaves the member entry. A compromised node's tokens stayed valid after it was banned, until it was forgotten too. A token issued by a banned node is now refused (BannedIssuer), as soon as the ban reaches the node judging it. --- src/access/bans.rs | 4 ++-- src/access/token.rs | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'src/access') diff --git a/src/access/bans.rs b/src/access/bans.rs index c4fad1f..a314f49 100644 --- a/src/access/bans.rs +++ b/src/access/bans.rs @@ -2,8 +2,8 @@ //! user who may write there, as any protocol data: a node refuses a banned node's //! handshake, so it neither syncs with it nor counts it a member (`swarm::peer`), and //! with the swarm's policy every node refuses the changes a banned node commits -//! (`access::rules`, judged as the ban stood when they were made). Deleting the entry -//! lifts the ban. +//! (`access::rules`, judged as the ban stood when they were made), and the tokens it +//! issued (`access::token`). Deleting the entry lifts the ban. use crate::document::{ReadDoc, meta, store}; diff --git a/src/access/token.rs b/src/access/token.rs index 3af8672..d33a3fe 100644 --- a/src/access/token.rs +++ b/src/access/token.rs @@ -75,6 +75,8 @@ pub(crate) enum Refused { OtherSwarm, #[error("issued by a node that isn't a member here (not yet, or no longer)")] UnknownIssuer, + #[error("issued by a node the swarm banned")] + BannedIssuer, #[error("its signature doesn't hold")] BadSignature, #[error("expired")] @@ -90,7 +92,7 @@ pub(crate) enum Refused { } /// The claims of `token` if this node takes it: well formed, for `swarm`, signed by a -/// node that is a member in `doc`, not expired at `now` (Unix ms), nor issued later +/// node that is a member in `doc` and not banned, not expired at `now` (Unix ms), nor issued later /// than a clock off by `SKEW_MS` would; and in a swarm with a registry, for a /// registered user that is enabled, issued after its tokens were last revoked /// (`registry::User::tokens_not_before`). @@ -126,6 +128,9 @@ pub(crate) fn verify( if !member { return Err(Refused::UnknownIssuer); } + if super::bans::banned(doc, &claims.issuer) { + return Err(Refused::BannedIssuer); + } let mut signed = LABEL.to_vec(); signed.extend_from_slice(&payload); if !identity::verify(&key, &signed, &signature) { @@ -236,6 +241,17 @@ mod tests { verify(&doc, &forged, "s", 1_500), Err(Refused::BadSignature) ); + // By a member banned since. + let token = issue(&key, &claims(&key)); + let ban = crate::access::bans::path(&key.id()); + let mut banned = doc.fork(); + change(&mut banned, |tx| { + store::write_path(tx, &ban, &json!({"reason": "lost"})).unwrap() + }); + assert_eq!( + verify(&banned, &token, "s", 1_500), + Err(Refused::BannedIssuer) + ); // Changed claims, the signature kept. let token = issue(&key, &claims(&key)); let (_, signature) = token.rsplit_once('.').unwrap(); -- cgit v1.3.1