chanmgr: bubble ClockSkew up through the Error object.

Fortunately, we don't need a separate type here: authenticated
clock skew can only come attached to a `tor_proto::Error`.

We also remove skew from `tor_proto::Error::HandshakeCertsExpired`,
since it would now be redundant.
This commit is contained in:
Nick Mathewson 2022-04-07 10:01:19 -04:00
parent 7656ab0931
commit 9160b55c57
6 changed files with 64 additions and 12 deletions

View File

@ -184,10 +184,22 @@ impl<R: Runtime> ChanBuilder<R> {
let chan = builder
.launch(tls)
.connect(|| self.runtime.wallclock())
.await?;
.await
.map_err(Error::from_proto_no_skew)?;
let clock_skew = Some(chan.clock_skew()); // Not yet authenticated; can't use it till `check` is done.
let now = self.runtime.wallclock();
let chan = chan.check(target, &peer_cert, Some(now))?;
let (chan, reactor) = chan.finish().await?;
let chan = chan
.check(target, &peer_cert, Some(now))
.map_err(|source| match &source {
tor_proto::Error::HandshakeCertsExpired { .. } => {
Error::Proto { source, clock_skew }
}
_ => Error::from_proto_no_skew(source),
})?;
let (chan, reactor) = chan
.finish()
.await
.map_err(|source| Error::Proto { source, clock_skew })?;
{
self.event_sender

View File

@ -7,6 +7,7 @@ use futures::task::SpawnError;
use thiserror::Error;
use tor_error::{internal, ErrorKind};
use tor_proto::ClockSkew;
/// An error returned by a channel manager.
#[derive(Debug, Error, Clone)]
@ -25,8 +26,15 @@ pub enum Error {
ChanTimeout,
/// A protocol error while making a channel
#[error("Protocol error while opening a channel: {0}")]
Proto(#[from] tor_proto::Error),
#[error("Protocol error while opening a channel.")]
Proto {
/// The underlying error
#[source]
source: tor_proto::Error,
/// An authenticated ClockSkew (if available) that we received from the
/// peer.
clock_skew: Option<ClockSkew>,
},
/// Network IO error or TLS error
#[error("Network IO error, or TLS error, in {action}, talking to {peer}")]
@ -82,9 +90,14 @@ impl tor_error::HasKind for Error {
use Error as E;
use ErrorKind as EK;
match self {
E::ChanTimeout | E::Io { .. } | E::Proto(ProtoErr::ChanIoErr(_)) => EK::TorAccessFailed,
E::ChanTimeout
| E::Io { .. }
| E::Proto {
source: ProtoErr::ChanIoErr(_),
..
} => EK::TorAccessFailed,
E::Spawn { cause, .. } => cause.kind(),
E::Proto(e) => e.kind(),
E::Proto { source, .. } => source.kind(),
E::PendingFailed => EK::TorAccessFailed,
E::UnusableTarget(_) | E::Internal(_) => EK::Internal,
Error::ChannelBuild { .. } => EK::TorAccessFailed,
@ -104,7 +117,7 @@ impl tor_error::HasRetryTime for Error {
//
// TODO: Someday we might want to distinguish among different kinds of IO
// errors.
E::PendingFailed | E::Proto(_) | E::Io { .. } => RT::AfterWaiting,
E::PendingFailed | E::Proto { .. } | E::Io { .. } => RT::AfterWaiting,
// This error reflects multiple attempts, but every failure is an IO
// error, so we can also retry this after a delay.
@ -131,4 +144,27 @@ impl Error {
cause: Arc::new(err),
}
}
/// Construct a new `Error` from a `tor_proto::Error`, with no additional
/// clock skew information.
///
/// This is not an `Into` implementation because we don't want to call it
/// accidentally when we actually do have clock skew information.
pub(crate) fn from_proto_no_skew(source: tor_proto::Error) -> Self {
Error::Proto {
source,
clock_skew: None,
}
}
/// Return the clock skew information from this error (or from an internal
/// error).
///
/// Only returns the clock skew information if it is authenticated.
pub fn clock_skew(&self) -> Option<ClockSkew> {
match self {
Error::Proto { clock_skew, .. } => *clock_skew,
_ => None,
}
}
}

View File

@ -125,7 +125,8 @@ impl<R: Runtime> ChanMgr<R> {
let chan = self.mgr.get_or_launch(*ed_identity, targetinfo).await?;
// Double-check the match to make sure that the RSA identity is
// what we wanted too.
chan.check_match(target)?;
chan.check_match(target)
.map_err(Error::from_proto_no_skew)?;
Ok(chan)
}

View File

@ -328,7 +328,7 @@ impl<T: AsyncRead + AsyncWrite + Send + Unpin + 'static> UnverifiedChannel<T> {
(TimeValidityError::Expired(expired_by), ClockSkew::Fast(skew))
if expired_by < skew =>
{
Error::HandshakeCertsExpired { expired_by, skew }
Error::HandshakeCertsExpired { expired_by }
}
// As it so happens, we don't need to check for this case, since the certs in use
// here only have an expiration time in them.

View File

@ -50,8 +50,6 @@ pub enum Error {
HandshakeCertsExpired {
/// For how long has the circuit been expired?
expired_by: Duration,
/// How fast does the relay claim that our clock is?
skew: Duration,
},
/// Protocol violation at the channel level, other than at the handshake
/// stage.

View File

@ -22,6 +22,10 @@ We can delete older sections here after we bump the releases.
MODIFIED: Added `reset()` method to RetrySchedule.
### tor-chanmgr
BREAKING: Added members to `Error::Proto`
### tor-circmgr
MODIFIED: Added a new variant in tor_circmgr::Error.
@ -29,6 +33,7 @@ MODIFIED: Added a new variant in tor_circmgr::Error.
### tor-proto
MODIFIED: New accessors in tor_proto::Channel.
BREAKING: Removed clock skew from Error::HandshakeCertsExpired.
### tor-rtmock