Show error on invalid TLS certificate
This commit is contained in:
parent
6b58b348fa
commit
a8cc94c188
|
@ -32,12 +32,20 @@ public class ConnectionManager {
|
||||||
public enum Source {
|
public enum Source {
|
||||||
CONNECTION,
|
CONNECTION,
|
||||||
SASL,
|
SASL,
|
||||||
|
TLS,
|
||||||
STREAM_ERROR
|
STREAM_ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Reconnect {
|
||||||
|
NOW,
|
||||||
|
LATER,
|
||||||
|
NEVER
|
||||||
|
}
|
||||||
|
|
||||||
public Source source;
|
public Source source;
|
||||||
public string? identifier;
|
public string? identifier;
|
||||||
public StreamError.Flag? flag;
|
public Reconnect reconnect_recomendation { get; set; default=Reconnect.NOW; }
|
||||||
|
public bool resource_rejected = false;
|
||||||
|
|
||||||
public ConnectionError(Source source, string? identifier) {
|
public ConnectionError(Source source, string? identifier) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
|
@ -79,7 +87,8 @@ public class ConnectionManager {
|
||||||
}
|
}
|
||||||
Timeout.add_seconds(60, () => {
|
Timeout.add_seconds(60, () => {
|
||||||
foreach (Account account in connection_todo) {
|
foreach (Account account in connection_todo) {
|
||||||
if (connections[account].last_activity.compare(new DateTime.now_utc().add_minutes(-1)) < 0) {
|
if (connections[account].last_activity != null &&
|
||||||
|
connections[account].last_activity.compare(new DateTime.now_utc().add_minutes(-1)) < 0) {
|
||||||
check_reconnect(account);
|
check_reconnect(account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +173,7 @@ public class ConnectionManager {
|
||||||
change_connection_state(account, ConnectionState.CONNECTED);
|
change_connection_state(account, ConnectionState.CONNECTED);
|
||||||
});
|
});
|
||||||
stream.get_module(PlainSasl.Module.IDENTITY).received_auth_failure.connect((stream, node) => {
|
stream.get_module(PlainSasl.Module.IDENTITY).received_auth_failure.connect((stream, node) => {
|
||||||
set_connection_error(account, ConnectionError.Source.SASL, null);
|
set_connection_error(account, new ConnectionError(ConnectionError.Source.SASL, null));
|
||||||
change_connection_state(account, ConnectionState.DISCONNECTED);
|
change_connection_state(account, ConnectionState.DISCONNECTED);
|
||||||
});
|
});
|
||||||
stream.received_node.connect(() => {
|
stream.received_node.connect(() => {
|
||||||
|
@ -186,9 +195,13 @@ public class ConnectionManager {
|
||||||
connections.unset(account);
|
connections.unset(account);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (e is Core.IOStreamError.TLS) {
|
||||||
|
set_connection_error(account, new ConnectionError(ConnectionError.Source.TLS, e.message) { reconnect_recomendation=ConnectionError.Reconnect.NEVER});
|
||||||
|
return;
|
||||||
|
}
|
||||||
StreamError.Flag? flag = stream.get_flag(StreamError.Flag.IDENTITY);
|
StreamError.Flag? flag = stream.get_flag(StreamError.Flag.IDENTITY);
|
||||||
if (flag != null) {
|
if (flag != null) {
|
||||||
set_connection_error(account, ConnectionError.Source.STREAM_ERROR, flag.error_type);
|
set_connection_error(account, new ConnectionError(ConnectionError.Source.STREAM_ERROR, flag.error_type) { resource_rejected=flag.resource_rejected });
|
||||||
}
|
}
|
||||||
interpret_connection_error(account);
|
interpret_connection_error(account);
|
||||||
}
|
}
|
||||||
|
@ -199,17 +212,17 @@ public class ConnectionManager {
|
||||||
int wait_sec = 5;
|
int wait_sec = 5;
|
||||||
if (error == null) {
|
if (error == null) {
|
||||||
wait_sec = 3;
|
wait_sec = 3;
|
||||||
} else if (error.source == ConnectionError.Source.STREAM_ERROR && error.flag != null) {
|
} else if (error.source == ConnectionError.Source.STREAM_ERROR) {
|
||||||
if (error.flag.resource_rejected) {
|
if (error.resource_rejected) {
|
||||||
connect_(account, account.resourcepart + "-" + random_uuid());
|
connect_(account, account.resourcepart + "-" + random_uuid());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (error.flag.reconnection_recomendation) {
|
switch (error.reconnect_recomendation) {
|
||||||
case StreamError.Flag.Reconnect.NOW:
|
case ConnectionError.Reconnect.NOW:
|
||||||
wait_sec = 5; break;
|
wait_sec = 5; break;
|
||||||
case StreamError.Flag.Reconnect.LATER:
|
case ConnectionError.Reconnect.LATER:
|
||||||
wait_sec = 60; break;
|
wait_sec = 60; break;
|
||||||
case StreamError.Flag.Reconnect.NEVER:
|
case ConnectionError.Reconnect.NEVER:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (error.source == ConnectionError.Source.SASL) {
|
} else if (error.source == ConnectionError.Source.SASL) {
|
||||||
|
@ -291,8 +304,7 @@ public class ConnectionManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void set_connection_error(Account account, ConnectionError.Source source, string? id) {
|
private void set_connection_error(Account account, ConnectionError error) {
|
||||||
ConnectionError error = new ConnectionError(source, id);
|
|
||||||
connection_errors[account] = error;
|
connection_errors[account] = error;
|
||||||
connection_error(account, error);
|
connection_error(account, error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="jid_label">
|
<object class="GtkLabel" id="jid_label">
|
||||||
<property name="xalign">0</property>
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
<property name="selectable">True</property>
|
<property name="selectable">True</property>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|
|
@ -88,6 +88,7 @@
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -121,6 +122,7 @@
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="jid_label">
|
<object class="GtkLabel" id="jid_label">
|
||||||
<property name="xalign">0</property>
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">1</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
|
<attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
|
||||||
|
@ -136,6 +138,7 @@
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="state_label">
|
<object class="GtkLabel" id="state_label">
|
||||||
<property name="xalign">0</property>
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<style>
|
<style>
|
||||||
<class name="dim-label"/>
|
<class name="dim-label"/>
|
||||||
|
|
|
@ -171,7 +171,7 @@ public class Dialog : Gtk.Dialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool on_active_switch_state_changed(bool state) {
|
private bool change_account_state(bool state) {
|
||||||
selected_account.enabled = state;
|
selected_account.enabled = state;
|
||||||
if (state) {
|
if (state) {
|
||||||
if (selected_account.enabled) account_disabled(selected_account);
|
if (selected_account.enabled) account_disabled(selected_account);
|
||||||
|
@ -189,7 +189,7 @@ public class Dialog : Gtk.Dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populate_grid_data(Account account) {
|
private void populate_grid_data(Account account) {
|
||||||
active_switch.state_set.disconnect(on_active_switch_state_changed);
|
active_switch.state_set.disconnect(change_account_state);
|
||||||
|
|
||||||
Util.image_set_from_scaled_pixbuf(image, (new AvatarGenerator(50, 50, image.scale_factor)).draw_account(stream_interactor, account));
|
Util.image_set_from_scaled_pixbuf(image, (new AvatarGenerator(50, 50, image.scale_factor)).draw_account(stream_interactor, account));
|
||||||
active_switch.set_active(account.enabled);
|
active_switch.set_active(account.enabled);
|
||||||
|
@ -201,7 +201,7 @@ public class Dialog : Gtk.Dialog {
|
||||||
|
|
||||||
update_status_label(account);
|
update_status_label(account);
|
||||||
|
|
||||||
active_switch.state_set.connect(on_active_switch_state_changed);
|
active_switch.state_set.connect(change_account_state);
|
||||||
|
|
||||||
foreach(Plugins.AccountSettingsWidget widget in plugin_widgets) {
|
foreach(Plugins.AccountSettingsWidget widget in plugin_widgets) {
|
||||||
widget.set_account(account);
|
widget.set_account(account);
|
||||||
|
@ -216,8 +216,11 @@ public class Dialog : Gtk.Dialog {
|
||||||
state_label.get_style_context().add_class("is_error");
|
state_label.get_style_context().add_class("is_error");
|
||||||
|
|
||||||
if (error.source == ConnectionManager.ConnectionError.Source.SASL ||
|
if (error.source == ConnectionManager.ConnectionError.Source.SASL ||
|
||||||
(error.flag != null && error.flag.reconnection_recomendation == Xmpp.StreamError.Flag.Reconnect.NEVER)) {
|
error.source == ConnectionManager.ConnectionError.Source.TLS ||
|
||||||
|
error.reconnect_recomendation == ConnectionManager.ConnectionError.Reconnect.NEVER) {
|
||||||
|
active_switch.state_set.disconnect(change_account_state);
|
||||||
active_switch.active = false;
|
active_switch.active = false;
|
||||||
|
active_switch.state_set.connect(change_account_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -238,6 +241,8 @@ public class Dialog : Gtk.Dialog {
|
||||||
switch (error.source) {
|
switch (error.source) {
|
||||||
case ConnectionManager.ConnectionError.Source.SASL:
|
case ConnectionManager.ConnectionError.Source.SASL:
|
||||||
return _("Wrong password");
|
return _("Wrong password");
|
||||||
|
case ConnectionManager.ConnectionError.Source.TLS:
|
||||||
|
return _("Invalid TLS certificate");
|
||||||
}
|
}
|
||||||
if (error.identifier != null) {
|
if (error.identifier != null) {
|
||||||
return _("Error") + ": " + error.identifier;
|
return _("Error") + ": " + error.identifier;
|
||||||
|
|
|
@ -7,12 +7,12 @@ public const string XML_URI = "http://www.w3.org/XML/1998/namespace";
|
||||||
public const string JABBER_URI = "jabber:client";
|
public const string JABBER_URI = "jabber:client";
|
||||||
|
|
||||||
public errordomain XmlError {
|
public errordomain XmlError {
|
||||||
XML_ERROR,
|
|
||||||
NS_DICT_ERROR,
|
NS_DICT_ERROR,
|
||||||
UNSUPPORTED,
|
UNSUPPORTED,
|
||||||
EOF,
|
EOF,
|
||||||
BAD_XML,
|
BAD_XML,
|
||||||
IO_ERROR
|
IO,
|
||||||
|
TLS
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StanzaReader {
|
public class StanzaReader {
|
||||||
|
@ -52,8 +52,10 @@ public class StanzaReader {
|
||||||
buffer_fill = (int) yield ((!)input).read_async(buffer, GLib.Priority.DEFAULT, cancellable);
|
buffer_fill = (int) yield ((!)input).read_async(buffer, GLib.Priority.DEFAULT, cancellable);
|
||||||
if (buffer_fill == 0) throw new XmlError.EOF("End of input stream reached.");
|
if (buffer_fill == 0) throw new XmlError.EOF("End of input stream reached.");
|
||||||
buffer_pos = 0;
|
buffer_pos = 0;
|
||||||
|
} catch (TlsError e) {
|
||||||
|
throw new XmlError.TLS("TlsError: %s".printf(e.message));
|
||||||
} catch (GLib.IOError e) {
|
} catch (GLib.IOError e) {
|
||||||
throw new XmlError.IO_ERROR("IOError in GLib: %s".printf(e.message));
|
throw new XmlError.IO("GLib.IOError: %s".printf(e.message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class StanzaWriter {
|
||||||
sfw.sfun();
|
sfw.sfun();
|
||||||
}
|
}
|
||||||
} catch (GLib.Error e) {
|
} catch (GLib.Error e) {
|
||||||
throw new XmlError.IO_ERROR(@"IOError in GLib: $(e.message)");
|
throw new XmlError.IO(@"IOError in GLib: $(e.message)");
|
||||||
} finally {
|
} finally {
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ public errordomain IOStreamError {
|
||||||
READ,
|
READ,
|
||||||
WRITE,
|
WRITE,
|
||||||
CONNECT,
|
CONNECT,
|
||||||
DISCONNECT
|
DISCONNECT,
|
||||||
|
TLS
|
||||||
}
|
}
|
||||||
|
|
||||||
public class XmppStream {
|
public class XmppStream {
|
||||||
|
@ -58,10 +58,13 @@ public class XmppStream {
|
||||||
IOStream? stream = null;
|
IOStream? stream = null;
|
||||||
if (best_provider != null) {
|
if (best_provider != null) {
|
||||||
stream = yield best_provider.connect(this);
|
stream = yield best_provider.connect(this);
|
||||||
} else {
|
}
|
||||||
|
if (stream != null) {
|
||||||
stream = yield (new SocketClient()).connect_async(new NetworkService("xmpp-client", "tcp", this.remote_name));
|
stream = yield (new SocketClient()).connect_async(new NetworkService("xmpp-client", "tcp", this.remote_name));
|
||||||
}
|
}
|
||||||
if (stream == null) throw new IOStreamError.CONNECT("client.connect() returned null");
|
if (stream == null) {
|
||||||
|
throw new IOStreamError.CONNECT("client.connect() returned null");
|
||||||
|
}
|
||||||
reset_stream((!)stream);
|
reset_stream((!)stream);
|
||||||
} catch (Error e) {
|
} catch (Error e) {
|
||||||
stderr.printf("CONNECTION LOST?\n");
|
stderr.printf("CONNECTION LOST?\n");
|
||||||
|
@ -154,7 +157,10 @@ public class XmppStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void detach_modules() {
|
public void detach_modules() {
|
||||||
foreach (XmppStreamModule module in modules) module.detach(this);
|
foreach (XmppStreamModule module in modules) {
|
||||||
|
if (!(module is XmppStreamNegotiationModule) && !negotiation_complete) continue;
|
||||||
|
module.detach(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public T? get_module<T>(ModuleIdentity<T>? identity) {
|
public T? get_module<T>(ModuleIdentity<T>? identity) {
|
||||||
|
@ -238,24 +244,19 @@ public class XmppStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool negotiation_modules_done() throws IOStreamError {
|
private bool negotiation_modules_done() throws IOStreamError {
|
||||||
if (!setup_needed) {
|
if (setup_needed) return false;
|
||||||
bool mandatory_outstanding = false;
|
if (is_negotiation_active()) return false;
|
||||||
|
|
||||||
foreach (XmppStreamModule module in modules) {
|
foreach (XmppStreamModule module in modules) {
|
||||||
if (module is XmppStreamNegotiationModule) {
|
if (module is XmppStreamNegotiationModule) {
|
||||||
XmppStreamNegotiationModule negotiation_module = (XmppStreamNegotiationModule) module;
|
XmppStreamNegotiationModule negotiation_module = (XmppStreamNegotiationModule) module;
|
||||||
if (negotiation_module.mandatory_outstanding(this)) mandatory_outstanding = true;
|
if (negotiation_module.mandatory_outstanding(this)) {
|
||||||
|
throw new IOStreamError.CONNECT("mandatory-to-negotiate feature not negotiated: " + negotiation_module.get_id());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!is_negotiation_active()) {
|
|
||||||
if (mandatory_outstanding) {
|
|
||||||
throw new IOStreamError.CONNECT("mandatory-to-negotiate feature not negotiated");
|
|
||||||
} else {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void attach_non_negotation_modules() {
|
private void attach_non_negotation_modules() {
|
||||||
foreach (XmppStreamModule module in modules) {
|
foreach (XmppStreamModule module in modules) {
|
||||||
|
@ -281,7 +282,9 @@ public class XmppStream {
|
||||||
StanzaNode node = yield ((!)reader).read_root_node();
|
StanzaNode node = yield ((!)reader).read_root_node();
|
||||||
log.node("IN ROOT", node);
|
log.node("IN ROOT", node);
|
||||||
return node;
|
return node;
|
||||||
} catch (XmlError e) {
|
} catch (XmlError.TLS e) {
|
||||||
|
throw new IOStreamError.TLS(e.message);
|
||||||
|
} catch (Error e) {
|
||||||
throw new IOStreamError.READ(e.message);
|
throw new IOStreamError.READ(e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,10 +27,6 @@ namespace Xmpp.Tls {
|
||||||
var io_stream = stream.get_stream();
|
var io_stream = stream.get_stream();
|
||||||
if (io_stream == null) return;
|
if (io_stream == null) return;
|
||||||
var conn = TlsClientConnection.new(io_stream, identity);
|
var conn = TlsClientConnection.new(io_stream, identity);
|
||||||
// TODO: Add certificate error handling, that is, allow the
|
|
||||||
// program to handle certificate errors. The certificate
|
|
||||||
// *is checked* by TlsClientConnection, and connection is
|
|
||||||
// not allowed to continue in case that there is an error.
|
|
||||||
stream.reset_stream(conn);
|
stream.reset_stream(conn);
|
||||||
|
|
||||||
var flag = stream.get_flag(Flag.IDENTITY);
|
var flag = stream.get_flag(Flag.IDENTITY);
|
||||||
|
|
Loading…
Reference in a new issue