Skip to content

use PrintableString to Display CounterpartyForceClosed peer_msg #2114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ use crate::util::events::{Event, EventHandler, EventsProvider, MessageSendEvent,
use crate::util::events;
use crate::util::wakers::{Future, Notifier};
use crate::util::scid_utils::fake_scid;
use crate::util::string::UntrustedString;
use crate::util::ser::{BigSize, FixedLengthReader, Readable, ReadableArgs, MaybeReadable, Writeable, Writer, VecWriter};
use crate::util::logger::{Level, Logger};
use crate::util::errors::APIError;
Expand Down Expand Up @@ -1990,7 +1991,7 @@ where
let peer_state = &mut *peer_state_lock;
if let hash_map::Entry::Occupied(chan) = peer_state.channel_by_id.entry(channel_id.clone()) {
if let Some(peer_msg) = peer_msg {
self.issue_channel_close_events(chan.get(),ClosureReason::CounterpartyForceClosed { peer_msg: peer_msg.to_string() });
self.issue_channel_close_events(chan.get(),ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString(peer_msg.to_string()) });
} else {
self.issue_channel_close_events(chan.get(),ClosureReason::HolderForceClosed);
}
Expand Down
7 changes: 4 additions & 3 deletions lightning/src/ln/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use crate::util::test_utils;
use crate::util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination};
use crate::util::errors::APIError;
use crate::util::ser::{Writeable, ReadableArgs};
use crate::util::string::UntrustedString;
use crate::util::config::UserConfig;

use bitcoin::hash_types::BlockHash;
Expand Down Expand Up @@ -8344,7 +8345,7 @@ fn test_pre_lockin_no_chan_closed_update() {
let channel_id = crate::chain::transaction::OutPoint { txid: funding_created_msg.funding_txid, index: funding_created_msg.funding_output_index }.to_channel_id();
nodes[0].node.handle_error(&nodes[1].node.get_our_node_id(), &msgs::ErrorMessage { channel_id, data: "Hi".to_owned() });
assert!(nodes[0].chain_monitor.added_monitors.lock().unwrap().is_empty());
check_closed_event!(nodes[0], 2, ClosureReason::CounterpartyForceClosed { peer_msg: "Hi".to_string() }, true);
check_closed_event!(nodes[0], 2, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString("Hi".to_string()) }, true);
}

#[test]
Expand Down Expand Up @@ -8802,7 +8803,7 @@ fn test_error_chans_closed() {
nodes[0].node.handle_error(&nodes[1].node.get_our_node_id(), &msgs::ErrorMessage { channel_id: chan_2.2, data: "ERR".to_owned() });
check_added_monitors!(nodes[0], 1);
check_closed_broadcast!(nodes[0], false);
check_closed_event!(nodes[0], 1, ClosureReason::CounterpartyForceClosed { peer_msg: "ERR".to_string() });
check_closed_event!(nodes[0], 1, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString("ERR".to_string()) });
assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0).len(), 1);
assert_eq!(nodes[0].node.list_usable_channels().len(), 2);
assert!(nodes[0].node.list_usable_channels()[0].channel_id == chan_1.2 || nodes[0].node.list_usable_channels()[1].channel_id == chan_1.2);
Expand All @@ -8812,7 +8813,7 @@ fn test_error_chans_closed() {
let _chan_4 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001);
nodes[0].node.handle_error(&nodes[1].node.get_our_node_id(), &msgs::ErrorMessage { channel_id: [0; 32], data: "ERR".to_owned() });
check_added_monitors!(nodes[0], 2);
check_closed_event!(nodes[0], 2, ClosureReason::CounterpartyForceClosed { peer_msg: "ERR".to_string() });
check_closed_event!(nodes[0], 2, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString("ERR".to_string()) });
let events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 2);
match events[0] {
Expand Down
5 changes: 3 additions & 2 deletions lightning/src/ln/payment_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use crate::util::events::{ClosureReason, Event, HTLCDestination, MessageSendEven
use crate::util::test_utils;
use crate::util::errors::APIError;
use crate::util::ser::Writeable;
use crate::util::string::UntrustedString;

use bitcoin::{Block, BlockHeader, TxMerkleNode};
use bitcoin::hashes::Hash;
Expand Down Expand Up @@ -353,7 +354,7 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) {
MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
assert_eq!(node_id, nodes[1].node.get_our_node_id());
nodes[1].node.handle_error(&nodes[0].node.get_our_node_id(), msg);
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id()) });
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id())) });
check_added_monitors!(nodes[1], 1);
assert_eq!(nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0).len(), 1);
},
Expand Down Expand Up @@ -518,7 +519,7 @@ fn do_test_completed_payment_not_retryable_on_reload(use_dust: bool) {
MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
assert_eq!(node_id, nodes[1].node.get_our_node_id());
nodes[1].node.handle_error(&nodes[0].node.get_our_node_id(), msg);
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id()) });
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id())) });
check_added_monitors!(nodes[1], 1);
bs_commitment_tx = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
},
Expand Down
3 changes: 2 additions & 1 deletion lightning/src/ln/reload_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::util::errors::APIError;
use crate::util::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
use crate::util::ser::{Writeable, ReadableArgs};
use crate::util::config::UserConfig;
use crate::util::string::UntrustedString;

use bitcoin::hash_types::BlockHash;

Expand Down Expand Up @@ -566,7 +567,7 @@ fn do_test_data_loss_protect(reconnect_panicing: bool) {
nodes[1].node.handle_error(&nodes[0].node.get_our_node_id(), &err_msgs_0[0]);
assert!(nodes[1].node.list_usable_channels().is_empty());
check_added_monitors!(nodes[1], 1);
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id()) });
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id())) });
check_closed_broadcast!(nodes[1], false);
}

Expand Down
3 changes: 2 additions & 1 deletion lightning/src/ln/reorg_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::ln::msgs::{ChannelMessageHandler, Init};
use crate::util::events::{Event, MessageSendEventsProvider, ClosureReason, HTLCDestination};
use crate::util::test_utils;
use crate::util::ser::Writeable;
use crate::util::string::UntrustedString;

use bitcoin::blockdata::block::{Block, BlockHeader};
use bitcoin::blockdata::script::Builder;
Expand Down Expand Up @@ -368,7 +369,7 @@ fn do_test_unconf_chan(reload_node: bool, reorg_after_reload: bool, use_funding_
nodes[0].node.test_process_background_events(); // Required to free the pending background monitor update
check_added_monitors!(nodes[0], 1);
let expected_err = "Funding transaction was un-confirmed. Locked at 6 confs, now have 0 confs.";
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: "Channel closed because of an exception: ".to_owned() + expected_err });
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString(format!("Channel closed because of an exception: {}", expected_err)) });
check_closed_event!(nodes[0], 1, ClosureReason::ProcessingError { err: expected_err.to_owned() });
assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1);
nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().clear();
Expand Down
3 changes: 2 additions & 1 deletion lightning/src/ln/shutdown_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::util::test_utils::OnGetShutdownScriptpubkey;
use crate::util::events::{Event, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
use crate::util::errors::APIError;
use crate::util::config::UserConfig;
use crate::util::string::UntrustedString;

use bitcoin::blockdata::script::Builder;
use bitcoin::blockdata::opcodes;
Expand Down Expand Up @@ -380,7 +381,7 @@ fn do_test_shutdown_rebroadcast(recv_count: u8) {
// closing_signed so we do it ourselves
check_closed_broadcast!(nodes[1], false);
check_added_monitors!(nodes[1], 1);
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id()) });
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id())) });
}

assert!(nodes[0].node.list_channels().is_empty());
Expand Down
12 changes: 7 additions & 5 deletions lightning/src/util/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::routing::gossip::NetworkUpdate;
use crate::util::errors::APIError;
use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReadable, Readable, RequiredWrapper, UpgradableRequired, WithoutLength};
use crate::util::string::UntrustedString;
use crate::routing::router::{RouteHop, RouteParameters};

use bitcoin::{PackedLockTime, Transaction};
Expand Down Expand Up @@ -125,10 +126,12 @@ pub enum ClosureReason {
CounterpartyForceClosed {
/// The error which the peer sent us.
///
/// The string should be sanitized before it is used (e.g emitted to logs
/// or printed to stdout). Otherwise, a well crafted error message may exploit
/// Be careful about printing the peer_msg, a well-crafted message could exploit
/// a security vulnerability in the terminal emulator or the logging subsystem.
peer_msg: String,
/// To be safe, use `Display` on `UntrustedString`
///
/// [`UntrustedString`]: crate::util::string::UntrustedString
peer_msg: UntrustedString,
},
/// Closure generated from [`ChannelManager::force_close_channel`], called by the user.
///
Expand Down Expand Up @@ -173,8 +176,7 @@ impl core::fmt::Display for ClosureReason {
f.write_str("Channel closed because ")?;
match self {
ClosureReason::CounterpartyForceClosed { peer_msg } => {
f.write_str("counterparty force-closed with message ")?;
f.write_str(&peer_msg)
f.write_fmt(format_args!("counterparty force-closed with message: {}", peer_msg))
},
ClosureReason::HolderForceClosed => f.write_str("user manually force-closed the channel"),
ClosureReason::CooperativeClosure => f.write_str("the channel was cooperatively closed"),
Expand Down
27 changes: 27 additions & 0 deletions lightning/src/util/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,34 @@

//! Utilities for strings.

use alloc::string::String;
use core::fmt;
use crate::io::{self, Read};
use crate::ln::msgs;
use crate::util::ser::{Writeable, Writer, Readable};

/// Struct to `Display` fields in a safe way using `PrintableString`
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UntrustedString(pub String);

impl Writeable for UntrustedString {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.0.write(w)
}
}

impl Readable for UntrustedString {
fn read<R: Read>(r: &mut R) -> Result<Self, msgs::DecodeError> {
let s: String = Readable::read(r)?;
Ok(Self(s))
}
}

impl fmt::Display for UntrustedString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
PrintableString(&self.0).fmt(f)
}
}

/// A string that displays only printable characters, replacing control characters with
/// [`core::char::REPLACEMENT_CHARACTER`].
Expand Down