From 81a55052707d460a7f437b664682817c2c99dce6 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Thu, 31 Dec 2020 19:00:54 +0100 Subject: [PATCH] Allow certificates from unknown CAs from .onion domains It's barely possible for .onion servers to provide a non-self-signed cert. But that's fine because encryption is provided independently though TOR. see #958 --- libdino/src/service/connection_manager.vala | 14 +++++++++++++- libdino/src/service/registration.vala | 16 ++++++++++++---- xmpp-vala/src/core/direct_tls_xmpp_stream.vala | 11 +++++++---- xmpp-vala/src/core/io_xmpp_stream.vala | 4 ++++ xmpp-vala/src/core/starttls_xmpp_stream.vala | 7 +++++-- xmpp-vala/src/core/stream_connect.vala | 8 ++++---- xmpp-vala/src/core/tls_xmpp_stream.vala | 8 +++++++- xmpp-vala/src/core/xmpp_stream.vala | 6 +++++- 8 files changed, 57 insertions(+), 17 deletions(-) diff --git a/libdino/src/service/connection_manager.vala b/libdino/src/service/connection_manager.vala index 40cd21d4..3ea6386b 100644 --- a/libdino/src/service/connection_manager.vala +++ b/libdino/src/service/connection_manager.vala @@ -196,7 +196,9 @@ public class ConnectionManager : Object { connection_directly_retry[account] = false; change_connection_state(account, ConnectionState.CONNECTING); - stream_result = yield Xmpp.establish_stream(account.bare_jid, module_manager.get_modules(account, resource), log_options); + stream_result = yield Xmpp.establish_stream(account.bare_jid, module_manager.get_modules(account, resource), log_options, + (_, peer_cert, errors) => { return on_invalid_certificate(account.domainpart, peer_cert, errors); } + ); connections[account].stream = stream_result.stream; connection_ongoing[account] = false; @@ -368,6 +370,16 @@ public class ConnectionManager : Object { connection_errors[account] = error; connection_error(account, error); } + + public static bool on_invalid_certificate(string domain, TlsCertificate peer_cert, TlsCertificateFlags errors) { + if (domain.has_suffix(".onion") && errors == TlsCertificateFlags.UNKNOWN_CA) { + // It's barely possible for .onion servers to provide a non-self-signed cert. + // But that's fine because encryption is provided independently though TOR. + warning("Accepting TLS certificate from unknown CA from .onion address %s", domain); + return true; + } + return false; + } } } diff --git a/libdino/src/service/registration.vala b/libdino/src/service/registration.vala index b4377b98..dc9ed95c 100644 --- a/libdino/src/service/registration.vala +++ b/libdino/src/service/registration.vala @@ -29,7 +29,9 @@ public class Register : StreamInteractionModule, Object{ list.add(new Iq.Module()); list.add(new Sasl.Module(account.bare_jid.to_string(), account.password)); - XmppStreamResult stream_result = yield Xmpp.establish_stream(account.bare_jid.domain_jid, list, Application.print_xmpp); + XmppStreamResult stream_result = yield Xmpp.establish_stream(account.bare_jid.domain_jid, list, Application.print_xmpp, + (_, peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(account.domainpart, peer_cert, errors); } + ); if (stream_result.stream == null) { if (stream_result.tls_errors != null) { @@ -80,7 +82,9 @@ public class Register : StreamInteractionModule, Object{ Gee.List list = new ArrayList(); list.add(new Iq.Module()); - XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp); + XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp, + (_, peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(jid.domainpart, peer_cert, errors); } + ); if (stream_result.stream == null) { if (stream_result.io_error != null) { @@ -125,7 +129,9 @@ public class Register : StreamInteractionModule, Object{ list.add(new Iq.Module()); list.add(new Xep.InBandRegistration.Module()); - XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp); + XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp, + (_, peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(jid.domainpart, peer_cert, errors); } + ); if (stream_result.stream == null) { return null; @@ -169,7 +175,9 @@ public class Register : StreamInteractionModule, Object{ list.add(new Iq.Module()); list.add(new Xep.InBandRegistration.Module()); - XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp); + XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp, + (_, peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(jid.domainpart, peer_cert, errors); } + ); if (stream_result.stream == null) { return null; diff --git a/xmpp-vala/src/core/direct_tls_xmpp_stream.vala b/xmpp-vala/src/core/direct_tls_xmpp_stream.vala index 1d2f7339..1c262598 100644 --- a/xmpp-vala/src/core/direct_tls_xmpp_stream.vala +++ b/xmpp-vala/src/core/direct_tls_xmpp_stream.vala @@ -2,11 +2,13 @@ public class Xmpp.DirectTlsXmppStream : TlsXmppStream { string host; uint16 port; + TlsXmppStream.OnInvalidCert on_invalid_cert_outer; - public DirectTlsXmppStream(Jid remote, string host, uint16 port) { - this.remote_name = remote; + public DirectTlsXmppStream(Jid remote_name, string host, uint16 port, TlsXmppStream.OnInvalidCert on_invalid_cert) { + base(remote_name); this.host = host; this.port = port; + this.on_invalid_cert_outer = on_invalid_cert; } public override async void connect() throws IOStreamError { @@ -15,10 +17,11 @@ public class Xmpp.DirectTlsXmppStream : TlsXmppStream { debug("Connecting to %s %i (tls)", host, port); IOStream? io_stream = yield client.connect_to_host_async(host, port); TlsConnection tls_connection = TlsClientConnection.new(io_stream, new NetworkAddress(remote_name.to_string(), port)); - #if ALPN_SUPPORT +#if ALPN_SUPPORT tls_connection.set_advertised_protocols(new string[]{"xmpp-client"}); - #endif +#endif tls_connection.accept_certificate.connect(on_invalid_certificate); + tls_connection.accept_certificate.connect(on_invalid_cert_outer); reset_stream(tls_connection); yield setup(); diff --git a/xmpp-vala/src/core/io_xmpp_stream.vala b/xmpp-vala/src/core/io_xmpp_stream.vala index 56efd7cd..02653720 100644 --- a/xmpp-vala/src/core/io_xmpp_stream.vala +++ b/xmpp-vala/src/core/io_xmpp_stream.vala @@ -11,6 +11,10 @@ public abstract class Xmpp.IoXmppStream : XmppStream { internal WriteNodeFunc? write_obj = null; + protected IoXmppStream(Jid remote_name) { + base(remote_name); + } + public override async void disconnect() throws IOStreamError, XmlError, IOError { disconnected = true; if (writer == null || reader == null || stream == null) { diff --git a/xmpp-vala/src/core/starttls_xmpp_stream.vala b/xmpp-vala/src/core/starttls_xmpp_stream.vala index 3df0dffb..401d7295 100644 --- a/xmpp-vala/src/core/starttls_xmpp_stream.vala +++ b/xmpp-vala/src/core/starttls_xmpp_stream.vala @@ -4,11 +4,13 @@ public class Xmpp.StartTlsXmppStream : TlsXmppStream { string host; uint16 port; + TlsXmppStream.OnInvalidCert on_invalid_cert_outer; - public StartTlsXmppStream(Jid remote, string host, uint16 port) { - this.remote_name = remote; + public StartTlsXmppStream(Jid remote, string host, uint16 port, TlsXmppStream.OnInvalidCert on_invalid_cert) { + base(remote); this.host = host; this.port = port; + this.on_invalid_cert_outer = on_invalid_cert; } public override async void connect() throws IOStreamError { @@ -40,6 +42,7 @@ public class Xmpp.StartTlsXmppStream : TlsXmppStream { reset_stream(conn); conn.accept_certificate.connect(on_invalid_certificate); + conn.accept_certificate.connect(on_invalid_cert_outer); } catch (Error e) { stderr.printf("Failed to start TLS: %s\n", e.message); } diff --git a/xmpp-vala/src/core/stream_connect.vala b/xmpp-vala/src/core/stream_connect.vala index a7615e9f..9a3dac76 100644 --- a/xmpp-vala/src/core/stream_connect.vala +++ b/xmpp-vala/src/core/stream_connect.vala @@ -8,12 +8,12 @@ namespace Xmpp { } public class XmppStreamResult { - public XmppStream? stream { get; set; } + public TlsXmppStream? stream { get; set; } public TlsCertificateFlags? tls_errors { get; set; } public IOStreamError? io_error { get; set; } } - public async XmppStreamResult establish_stream(Jid bare_jid, Gee.List modules, string? log_options) { + public async XmppStreamResult establish_stream(Jid bare_jid, Gee.List modules, string? log_options, TlsXmppStream.OnInvalidCert on_invalid_cert) { Jid remote = bare_jid.domain_jid; //Lookup xmpp-client and xmpps-client SRV records @@ -58,9 +58,9 @@ namespace Xmpp { foreach (SrvTargetInfo target in targets) { try { if (target.service == "xmpp-client") { - stream = new StartTlsXmppStream(remote, target.host, target.port); + stream = new StartTlsXmppStream(remote, target.host, target.port, on_invalid_cert); } else { - stream = new DirectTlsXmppStream(remote, target.host, target.port); + stream = new DirectTlsXmppStream(remote, target.host, target.port, on_invalid_cert); } stream.log = new XmppLog(bare_jid.to_string(), log_options); diff --git a/xmpp-vala/src/core/tls_xmpp_stream.vala b/xmpp-vala/src/core/tls_xmpp_stream.vala index 956a9a22..f47b3c80 100644 --- a/xmpp-vala/src/core/tls_xmpp_stream.vala +++ b/xmpp-vala/src/core/tls_xmpp_stream.vala @@ -2,6 +2,12 @@ public abstract class Xmpp.TlsXmppStream : IoXmppStream { public TlsCertificateFlags? errors; + public delegate bool OnInvalidCert(GLib.TlsConnection conn, GLib.TlsCertificate peer_cert, GLib.TlsCertificateFlags errors); + + protected TlsXmppStream(Jid remote_name) { + base(remote_name); + } + protected bool on_invalid_certificate(TlsCertificate peer_cert, TlsCertificateFlags errors) { this.errors = errors; @@ -13,7 +19,7 @@ public abstract class Xmpp.TlsXmppStream : IoXmppStream { error_str += @"$(f), "; } } - warning(@"Tls Certificate Errors: $(error_str)"); + warning(@"[%p, %s] Tls Certificate Errors: %s", this, this.remote_name, error_str); return false; } } \ No newline at end of file diff --git a/xmpp-vala/src/core/xmpp_stream.vala b/xmpp-vala/src/core/xmpp_stream.vala index 99dbffe6..0f0793e9 100644 --- a/xmpp-vala/src/core/xmpp_stream.vala +++ b/xmpp-vala/src/core/xmpp_stream.vala @@ -34,6 +34,10 @@ public abstract class Xmpp.XmppStream { protected bool setup_needed = false; protected bool disconnected = false; + protected XmppStream(Jid remote_name) { + this.remote_name = remote_name; + } + public abstract async void connect() throws IOStreamError; public abstract async void disconnect() throws IOStreamError, XmlError, IOError; @@ -41,7 +45,7 @@ public abstract class Xmpp.XmppStream { public abstract async StanzaNode read() throws IOStreamError; [Version (deprecated = true, deprecated_since = "0.1", replacement = "write_async")] - public abstract void write(StanzaNode node); + public abstract void write(StanzaNode node); public abstract async void write_async(StanzaNode node) throws IOStreamError;