chanmgr: expose whether we are failing because of expired certs.

This commit is contained in:
Nick Mathewson 2022-04-11 16:29:26 -04:00
parent 72f00daf12
commit 5f946b8d45
2 changed files with 87 additions and 20 deletions

View File

@ -192,6 +192,10 @@ impl<R: Runtime> ChanBuilder<R> {
.check(target, &peer_cert, Some(now))
.map_err(|source| match &source {
tor_proto::Error::HandshakeCertsExpired { .. } => {
self.event_sender
.lock()
.expect("Lock poisoned")
.record_handshake_done_with_skewed_clock();
Error::Proto { source, clock_skew }
}
_ => Error::from_proto_no_skew(source),

View File

@ -20,13 +20,22 @@ pub struct ConnStatus {
/// None if we haven't succeeded yet, but it's too early to say if
/// that's a problem.
online: Option<bool>,
/// Have we ever been able to make TLS handshakes and negotiate
/// certificates, _not including timeliness checking_?
///
/// True if we've been able to make TLS handshakes and talk to Tor relays we
/// like recently. False if we've definitely been failing. None if we
/// haven't succeeded yet, but it's too early to say if that's a problem.
auth_works: Option<bool>,
/// Have we been able to successfully negotiate full Tor handshakes?
///
/// True if we've been able to make TLS sessions recently.
/// True if we've been able to make Tor handshakes recently.
/// False if we've definitely been failing.
/// None if we haven't succeeded yet, but it's too early to say if
/// that's a problem.
tls_works: Option<bool>,
handshake_works: Option<bool>,
}
/// A problem detected while connecting to the Tor network.
@ -40,6 +49,11 @@ pub enum ConnBlockage {
/// got hit by an attempted man-in-the-middle attack.
#[display(fmt = "our internet connection seems to be filtered")]
NoHandshake,
/// We've made TCP connections, and our TLS connections mostly succeeded,
/// but we encountered failures that are well explained by clock skew,
/// or expired certificates.
#[display(fmt = "relays all seem to be using expired certificates")]
CertsExpired,
}
impl ConnStatus {
@ -48,12 +62,12 @@ impl ConnStatus {
/// Note:(This would just be a PartialEq implementation, but I'm not sure I
/// want to expose that PartialEq for this struct.)
fn eq(&self, other: &ConnStatus) -> bool {
self.online == other.online && self.tls_works == other.tls_works
self.online == other.online && self.handshake_works == other.handshake_works
}
/// Return true if this status indicates that we can successfully open Tor channels.
pub fn usable(&self) -> bool {
self.online == Some(true) && self.tls_works == Some(true)
self.online == Some(true) && self.handshake_works == Some(true)
}
/// Return a float representing "how bootstrapped" we are with respect to
@ -66,7 +80,8 @@ impl ConnStatus {
match self {
Self {
online: Some(true),
tls_works: Some(true),
auth_works: Some(true),
handshake_works: Some(true),
} => 1.0,
Self {
online: Some(true), ..
@ -84,9 +99,13 @@ impl ConnStatus {
..
} => Some(ConnBlockage::NoTcp),
Self {
tls_works: Some(false),
auth_works: Some(false),
..
} => Some(ConnBlockage::NoHandshake),
Self {
handshake_works: Some(false),
..
} => Some(ConnBlockage::CertsExpired),
_ => None,
}
}
@ -101,15 +120,25 @@ impl fmt::Display for ConnStatus {
..
} => write!(f, "unable to connect to the internet"),
ConnStatus {
tls_works: None, ..
handshake_works: None,
..
} => write!(f, "handshaking with Tor relays"),
ConnStatus {
tls_works: Some(false),
auth_works: Some(true),
handshake_works: Some(false),
..
} => write!(
f,
"unable to handshake with Tor relays, possibly due to clock skew"
),
ConnStatus {
handshake_works: Some(false),
..
} => write!(f, "unable to handshake with Tor relays"),
ConnStatus {
online: Some(true),
tls_works: Some(true),
handshake_works: Some(true),
..
} => write!(f, "connecting successfully"),
}
}
@ -180,6 +209,10 @@ struct ChanMgrStatus {
// where TLS fails.
last_tls_success: Option<Instant>,
/// When (if ever) have we ever finished the inner Tor handshake with a relay,
/// up to the point where we check for certificate timeliness?
last_chan_auth_success: Option<Instant>,
/// When (if ever) have we successfully finished the inner Tor handshake
/// with a relay?
///
@ -198,6 +231,7 @@ impl ChanMgrStatus {
n_attempts: 0,
last_tcp_success: None,
last_tls_success: None,
last_chan_auth_success: None,
last_chan_success: None,
}
}
@ -221,13 +255,23 @@ impl ChanMgrStatus {
(false, false) => Some(false),
};
let tls_works = match (self.last_chan_success.is_some(), early) {
let auth_works = match (self.last_chan_auth_success.is_some(), early) {
(true, _) => Some(true),
(_, true) => None,
(false, false) => Some(false),
};
ConnStatus { online, tls_works }
let handshake_works = match (self.last_chan_success.is_some(), early) {
(true, _) => Some(true),
(_, true) => None,
(false, false) => Some(false),
};
ConnStatus {
online,
auth_works,
handshake_works,
}
}
/// Note that an attempt to connect has been started.
@ -247,11 +291,18 @@ impl ChanMgrStatus {
self.last_tls_success = Some(now);
}
/// Note that we've completed a Tor handshake with a relay, _but failed to
/// verify the certificates in a way that could indicate clock skew_.
fn record_handshake_done_with_skewed_clock(&mut self, now: Instant) {
self.last_chan_auth_success = Some(now);
}
/// Note that we've completed a Tor handshake with a relay.
///
/// (This includes performing the TLS handshake, and verifying that the
/// relay was indeed the one that we wanted to reach.)
fn record_handshake_done(&mut self, now: Instant) {
self.last_chan_auth_success = Some(now);
self.last_chan_success = Some(now);
}
}
@ -312,6 +363,14 @@ impl ChanMgrEventSender {
self.push_at(now);
}
/// Record that a handshake has succeeded _except for the certificate
/// timeliness check, which may indicate a skewed clock.
pub(crate) fn record_handshake_done_with_skewed_clock(&mut self) {
let now = Instant::now();
self.mgr_status.record_handshake_done_with_skewed_clock(now);
self.push_at(now);
}
/// Note that we've completed a Tor handshake with a relay.
///
/// (This includes performing the TLS handshake, and verifying that the
@ -355,7 +414,8 @@ mod test {
let s2 = ConnStatus {
online: Some(false),
tls_works: None,
auth_works: None,
handshake_works: None,
};
assert_eq!(s2.to_string(), "unable to connect to the internet");
assert_float_eq!(s2.frac(), 0.0, abs <= TOL);
@ -370,7 +430,8 @@ mod test {
let s3 = ConnStatus {
online: Some(true),
tls_works: None,
auth_works: None,
handshake_works: None,
};
assert_eq!(s3.to_string(), "handshaking with Tor relays");
assert_float_eq!(s3.frac(), 0.5, abs <= TOL);
@ -380,7 +441,8 @@ mod test {
let s4 = ConnStatus {
online: Some(true),
tls_works: Some(false),
auth_works: Some(false),
handshake_works: Some(false),
};
assert_eq!(s4.to_string(), "unable to handshake with Tor relays");
assert_float_eq!(s4.frac(), 0.5, abs <= TOL);
@ -397,7 +459,8 @@ mod test {
let s5 = ConnStatus {
online: Some(true),
tls_works: Some(true),
auth_works: Some(true),
handshake_works: Some(true),
};
assert_eq!(s5.to_string(), "connecting successfully");
assert_float_eq!(s5.frac(), 1.0, abs <= TOL);
@ -418,7 +481,7 @@ mod test {
// when we start, we're unable to reach any conclusions.
let s0 = ms.conn_status_at(start);
assert!(s0.online.is_none());
assert!(s0.tls_works.is_none());
assert!(s0.handshake_works.is_none());
// Time won't let us make conclusions either, unless there have been
// attempts.
@ -436,22 +499,22 @@ mod test {
// (... but after a while.)
let s = ms.conn_status_at(start + hour);
assert_eq!(s.online, Some(false));
assert_eq!(s.tls_works, Some(false));
assert_eq!(s.handshake_works, Some(false));
// If TCP has succeeded, we should notice that.
ms.record_tcp_success(start + sec);
let s = ms.conn_status_at(start + sec * 2);
assert_eq!(s.online, Some(true));
assert!(s.tls_works.is_none());
assert!(s.handshake_works.is_none());
let s = ms.conn_status_at(start + hour);
assert_eq!(s.online, Some(true));
assert_eq!(s.tls_works, Some(false));
assert_eq!(s.handshake_works, Some(false));
// If the handshake succeeded, we can notice that too.
ms.record_handshake_done(start + sec * 2);
let s = ms.conn_status_at(start + sec * 3);
assert_eq!(s.online, Some(true));
assert_eq!(s.tls_works, Some(true));
assert_eq!(s.handshake_works, Some(true));
}
#[test]