From 6ecf01a3bdb6213fdb5570bc41c57fdb1116ffe5 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sun, 12 Apr 2015 13:04:32 -0400 Subject: [PATCH] console: Catch and handle password auth errors (bz 1151801) --- virtManager/console.py | 49 ++++++++++++++++++++++----------------- virtManager/details.py | 2 +- virtManager/sshtunnels.py | 1 + virtManager/viewers.py | 29 ++++++++++++++--------- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/virtManager/console.py b/virtManager/console.py index 1996dd95..0813f422 100644 --- a/virtManager/console.py +++ b/virtManager/console.py @@ -487,9 +487,6 @@ class vmmConsolePages(vmmGObjectUI): ########################## def _show_vm_status_unavailable(self): - self.widget("console-pages").set_current_page( - self.CONSOLE_PAGE_UNAVAILABLE) - if self.vm.is_crashed(): self._activate_unavailable_page(_("Guest has crashed")) else: @@ -511,9 +508,9 @@ class vmmConsolePages(vmmGObjectUI): for serial in self._serial_tabs: serial.close() - def _update_widget_states(self, vm, status_ignore): - runable = vm.is_runable() - paused = vm.is_paused() + def _update_vm_widget_states(self): + runable = self.vm.is_runable() + paused = self.vm.is_paused() pages = self.widget("console-pages") page = pages.get_current_page() @@ -714,10 +711,23 @@ class vmmConsolePages(vmmGObjectUI): else: self._enable_modifiers() - def _viewer_auth_error(self, viewer, errmsg): - viewer.close() + def _viewer_auth_rejected(self, ignore, errmsg): + self._close_viewer() self._activate_unavailable_page(errmsg) + def _viewer_auth_error(self, ignore, errmsg, viewer_will_disconnect): + errmsg = _("Viewer authentication error: %s") % errmsg + self.err.val_err(errmsg) + + if viewer_will_disconnect: + # GtkVNC will disconnect after an auth error, so lets do it for + # them and re-init the viewer (which will be triggered by + # update_vm_widget_states if needed) + self._close_viewer() + self._activate_unavailable_page(errmsg) + + self._update_vm_widget_states() + def _viewer_need_auth(self, ignore, withPassword, withUsername): self._activate_auth_page(withPassword, withUsername) @@ -744,15 +754,14 @@ class vmmConsolePages(vmmGObjectUI): if self.vm.is_runable(): # Exit was probably for legitimate reasons self._show_vm_status_unavailable() - return + else: + error = _("Error: viewer connection to hypervisor host got " + "refused or disconnected!") + if errout: + logging.debug("Error output from closed console: %s", errout) + error += "\n\nError: %s" % errout + self._activate_unavailable_page(error) - error = _("Error: viewer connection to hypervisor host got refused " - "or disconnected!") - if errout: - logging.debug("Error output from closed console: %s", errout) - error += "\n\nError: %s" % errout - - self._activate_unavailable_page(error) self._refresh_resizeguest_from_settings() def _viewer_connected(self, ignore): @@ -775,6 +784,7 @@ class vmmConsolePages(vmmGObjectUI): self._viewer.connect("connected", self._viewer_connected) self._viewer.connect("disconnected", self._viewer_disconnected) self._viewer.connect("auth-error", self._viewer_auth_error) + self._viewer.connect("auth-rejected", self._viewer_auth_rejected) self._viewer.connect("need-auth", self._viewer_need_auth) self._viewer.connect("agent-connected", self._viewer_agent_connected) self._viewer.connect("usb-redirect-error", @@ -947,8 +957,8 @@ class vmmConsolePages(vmmGObjectUI): def details_activate_default_console_page(self): return self._activate_default_console_page() - def details_update_widget_states(self, *args, **kwargs): - return self._update_widget_states(*args, **kwargs) + def details_update_widget_states(self): + return self._update_vm_widget_states() def details_build_keycombo_menu(self, *args, **kwargs): return self._build_keycombo_menu(*args, **kwargs) @@ -972,7 +982,4 @@ class vmmConsolePages(vmmGObjectUI): self._change_fullscreen(do_fullscreen) def details_auth_login(self, ignore): - self.widget("console-pages").set_current_page( - self.CONSOLE_PAGE_UNAVAILABLE) self._set_credentials() - self._activate_viewer_page() diff --git a/virtManager/details.py b/virtManager/details.py index e51511e7..29f388fe 100644 --- a/virtManager/details.py +++ b/virtManager/details.py @@ -1336,7 +1336,7 @@ class vmmDetails(vmmGObjectUI): for c in send_key.get_submenu().get_children(): c.set_sensitive(not (run or paused)) - self.console.details_update_widget_states(vm, status) + self.console.details_update_widget_states() if not run: self.activate_default_console_page() diff --git a/virtManager/sshtunnels.py b/virtManager/sshtunnels.py index acee0592..53274ea2 100644 --- a/virtManager/sshtunnels.py +++ b/virtManager/sshtunnels.py @@ -274,6 +274,7 @@ class SSHTunnels(object): for l in self._tunnels: l.close() self._tunnels = [] + self.unlock() def get_err_output(self): errout = "" diff --git a/virtManager/viewers.py b/virtManager/viewers.py index 401f8d8e..f2b6d691 100644 --- a/virtManager/viewers.py +++ b/virtManager/viewers.py @@ -53,7 +53,8 @@ class Viewer(vmmGObject): "pointer-ungrab": (GObject.SignalFlags.RUN_FIRST, None, []), "connected": (GObject.SignalFlags.RUN_FIRST, None, []), "disconnected": (GObject.SignalFlags.RUN_FIRST, None, []), - "auth-error": (GObject.SignalFlags.RUN_FIRST, None, [str]), + "auth-error": (GObject.SignalFlags.RUN_FIRST, None, [str, bool]), + "auth-rejected": (GObject.SignalFlags.RUN_FIRST, None, [str]), "need-auth": (GObject.SignalFlags.RUN_FIRST, None, [bool, bool]), "agent-connected": (GObject.SignalFlags.RUN_FIRST, None, []), "usb-redirect-error": (GObject.SignalFlags.RUN_FIRST, None, [str]), @@ -80,9 +81,7 @@ class Viewer(vmmGObject): self._display.destroy() self._display = None - if self._tunnels: - self._tunnels.close_all() - self._tunnels = None + self._tunnels.close_all() ######################## @@ -288,6 +287,7 @@ class VNCViewer(Viewer): self._make_signal_proxy("pointer-ungrab")) self._display.connect("vnc-auth-credential", self._auth_credential) + self._display.connect("vnc-auth-failure", self._auth_failure_cb) self._display.connect("vnc-initialized", self._connected_cb) self._display.connect("vnc-disconnected", self._disconnected_cb) self._display.connect("vnc-desktop-resize", self._desktop_resize) @@ -307,6 +307,10 @@ class VNCViewer(Viewer): # Queue a resize self.emit("size-allocate", None) + def _auth_failure_cb(self, ignore, msg): + logging.debug("VNC auth failure. msg=%s", msg) + self.emit("auth-error", msg, True) + def _auth_credential(self, src_ignore, credList): values = [] for idx in range(int(credList.n_values)): @@ -322,7 +326,8 @@ class VNCViewer(Viewer): "server.\n The credential type %s is not supported") % str(cred)) - self.emit("auth-error", errmsg) + # XXX test this + self.emit("auth-rejected", errmsg) return withUsername = False @@ -349,7 +354,6 @@ class VNCViewer(Viewer): if self._sockfd: self._sockfd.close() self._sockfd = None - self._tunnels.close_all() def _is_open(self): return self._display.is_open() @@ -519,10 +523,14 @@ class SpiceViewer(Viewer): if event == SpiceClientGLib.ChannelEvent.CLOSED: self.emit("disconnected") elif event == SpiceClientGLib.ChannelEvent.ERROR_AUTH: - logging.debug("Spice channel received ERROR_AUTH, assuming " - "it needs credentials.") - self.emit("need-auth", True, False) - self._close_main_channel() + if not self._spice_session.get_property("password"): + logging.debug("Spice channel received ERROR_AUTH, but no " + "password set, assuming it wants credentials.") + self.emit("need-auth", True, False) + else: + logging.debug("Spice channel received ERROR_AUTH, but a " + "password is already set. Assuming authentication failed.") + self.emit("auth-error", channel.get_error().message, False) elif event in [SpiceClientGLib.ChannelEvent.ERROR_CONNECT, SpiceClientGLib.ChannelEvent.ERROR_IO, SpiceClientGLib.ChannelEvent.ERROR_LINK, @@ -603,7 +611,6 @@ class SpiceViewer(Viewer): self._close_main_channel() self._usbdev_manager = None - self._tunnels.close_all() def _is_open(self): return self._spice_session is not None