Blame SOURCES/gdm-support-pre-authenticated-logins-from-oVirt.patch

b97e22
From 70c1cc7ed45ff4f6fa3cf0601c98ca40a168ac57 Mon Sep 17 00:00:00 2001
b97e22
From: Vinzenz Feenstra <evilissimo@redhat.com>
b97e22
Date: Thu, 10 Oct 2013 10:21:47 +0200
b97e22
Subject: [PATCH] gdm: support pre-authenticated logins from oVirt
b97e22
b97e22
oVirt is software for managing medium-to-large scale deployments of
b97e22
virtual machine guests across multiple hosts. It supports a feature
b97e22
where users can authenticate with a central server and get
b97e22
transparently connected to a guest system and then automatically get logged
b97e22
into that guest to an associated user session.
b97e22
b97e22
Guests using old versions of GDM support this single-sign-on capability
b97e22
by means of a greeter plugin, using the old greeter's extension
b97e22
API.
b97e22
b97e22
This commit adds similar support to the gnome-shell based login screen.
b97e22
b97e22
How it works:
b97e22
b97e22
* The OVirtCredentialsManager singleton listens for
b97e22
b97e22
  'org.ovirt.vdsm.Credentials.UserAuthenticated'
b97e22
b97e22
  D-Bus signal on the system bus from the
b97e22
b97e22
  'org.ovirt.vdsm.Credentials'
b97e22
b97e22
  bus name. The service that provides that bus name is called
b97e22
  the oVirt guest agent. It is also responsible for interacting
b97e22
  with the the central server to get user credentials.
b97e22
b97e22
* This UserAuthenticated signal passes, as a parameter, the a token
b97e22
  which needs to be passed through to the PAM service that is specifically
b97e22
  set up to integrate with the oVirt authentication architecture.
b97e22
  The singleton object keeps the token internally so it can be queried
b97e22
  later on.
b97e22
b97e22
* The OVirtCredentialsManager emits a signal 'user-authenticated' on
b97e22
  it's object once the dbus signal is triggered
b97e22
b97e22
* When the 'user-authenticated' signal is emitted, the login screen
b97e22
  tells GDM to start user verification using the PAM service. The
b97e22
  authentication stack of the service includes a PAM module
b97e22
  provided by oVirt that securely retrieves user credentials
b97e22
  from the oVirt guest agent. The PAM module then forwards those
b97e22
  credentials on to other modules in the stack so, e.g.,
b97e22
  the user's gnome keyring can be automatically unlocked.
b97e22
b97e22
* In case of the screen shield being visible, it also will react on that
b97e22
  'user-authenticated' signal and lift the shield.
b97e22
  In that case the login screen will check on construction time if
b97e22
  the signal has already been triggered, and a token is available.
b97e22
  If a token is available it will immediately trigger the functionality
b97e22
  as described above.
b97e22
b97e22
Upstream-Bug-Url: https://bugzilla.gnome.org/show_bug.cgi?id=702162
b97e22
Signed-off-by: Vinzenz Feenstra <evilissimo@redhat.com>
b97e22
Bug-Url: https://bugzilla.redhat.com/854712
b97e22
---
b97e22
 js/Makefile.am        |  1 +
b97e22
 js/gdm/authPrompt.js  | 13 ++++++++---
b97e22
 js/gdm/oVirt.js       | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++
b97e22
 js/gdm/util.js        | 31 ++++++++++++++++++++++++++
b97e22
 js/ui/screenShield.js |  8 +++++++
b97e22
 5 files changed, 112 insertions(+), 3 deletions(-)
b97e22
 create mode 100644 js/gdm/oVirt.js
b97e22
b97e22
diff --git a/js/Makefile.am b/js/Makefile.am
b97e22
index 425b6fb..bfcc714 100644
b97e22
--- a/js/Makefile.am
b97e22
+++ b/js/Makefile.am
b97e22
@@ -21,6 +21,7 @@ nobase_dist_js_DATA = 	\
b97e22
 	gdm/batch.js		\
b97e22
 	gdm/fingerprint.js	\
b97e22
 	gdm/loginDialog.js	\
b97e22
+	gdm/oVirt.js		\
b97e22
 	gdm/powerMenu.js	\
b97e22
 	gdm/realmd.js		\
b97e22
 	gdm/util.js		\
b97e22
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
b97e22
index 2121f2e..ba66c42 100644
b97e22
--- a/js/gdm/authPrompt.js
b97e22
+++ b/js/gdm/authPrompt.js
b97e22
@@ -59,6 +59,7 @@ const AuthPrompt = new Lang.Class({
b97e22
         this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
b97e22
         this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
b97e22
         this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
b97e22
+        this._userVerifier.connect('ovirt-user-authenticated', Lang.bind(this, this._onOVirtUserAuthenticated));
b97e22
         this.smartcardDetected = this._userVerifier.smartcardDetected;
b97e22
 
b97e22
         this.connect('next', Lang.bind(this, function() {
b97e22
@@ -219,6 +220,11 @@ const AuthPrompt = new Lang.Class({
b97e22
         this.emit('prompted');
b97e22
     },
b97e22
 
b97e22
+    _onOVirtUserAuthenticated: function() {
b97e22
+        if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
b97e22
+            this.reset();
b97e22
+    },
b97e22
+
b97e22
     _onSmartcardStatusChanged: function() {
b97e22
         this.smartcardDetected = this._userVerifier.smartcardDetected;
b97e22
 
b97e22
@@ -443,10 +449,11 @@ const AuthPrompt = new Lang.Class({
b97e22
             // The user is constant at the unlock screen, so it will immediately
b97e22
             // respond to the request with the username
b97e22
             beginRequestType = BeginRequestType.PROVIDE_USERNAME;
b97e22
-        } else if (this.smartcardDetected &&
b97e22
-                   this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) {
b97e22
+        } else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
b97e22
+                   (this.smartcardDetected &&
b97e22
+                    this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME))) {
b97e22
             // We don't need to know the username if the user preempted the login screen
b97e22
-            // with a smartcard.
b97e22
+            // with a smartcard or with preauthenticated oVirt credentials
b97e22
             beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
b97e22
         } else {
b97e22
             // In all other cases, we should get the username up front.
b97e22
diff --git a/js/gdm/oVirt.js b/js/gdm/oVirt.js
b97e22
new file mode 100644
b97e22
index 0000000..1a31f43
b97e22
--- /dev/null
b97e22
+++ b/js/gdm/oVirt.js
b97e22
@@ -0,0 +1,62 @@
b97e22
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
b97e22
+
b97e22
+const Gio = imports.gi.Gio;
b97e22
+const Lang = imports.lang;
b97e22
+const Signals = imports.signals;
b97e22
+
b97e22
+const OVirtCredentialsIface = <interface name='org.ovirt.vdsm.Credentials'>
b97e22
+<signal name="UserAuthenticated">
b97e22
+    <arg type="s" name="token"/>
b97e22
+</signal>
b97e22
+</interface>;
b97e22
+
b97e22
+const OVirtCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(OVirtCredentialsIface);
b97e22
+
b97e22
+let _oVirtCredentialsManager = null;
b97e22
+
b97e22
+function OVirtCredentials() {
b97e22
+    var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
b97e22
+                                   g_interface_name: OVirtCredentialsInfo.name,
b97e22
+                                   g_interface_info: OVirtCredentialsInfo,
b97e22
+                                   g_name: 'org.ovirt.vdsm.Credentials',
b97e22
+                                   g_object_path: '/org/ovirt/vdsm/Credentials',
b97e22
+                                   g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
b97e22
+    self.init(null);
b97e22
+    return self;
b97e22
+}
b97e22
+
b97e22
+const OVirtCredentialsManager = new Lang.Class({
b97e22
+    Name: 'OVirtCredentialsManager',
b97e22
+    _init: function() {
b97e22
+        this._token = null;
b97e22
+
b97e22
+        this._credentials = new OVirtCredentials();
b97e22
+        this._credentials.connectSignal('UserAuthenticated',
b97e22
+                                        Lang.bind(this, this._onUserAuthenticated));
b97e22
+    },
b97e22
+
b97e22
+    _onUserAuthenticated: function(proxy, sender, [token]) {
b97e22
+        this._token = token;
b97e22
+        this.emit('user-authenticated', token);
b97e22
+    },
b97e22
+
b97e22
+    hasToken: function() {
b97e22
+        return this._token != null;
b97e22
+    },
b97e22
+
b97e22
+    getToken: function() {
b97e22
+        return this._token;
b97e22
+    },
b97e22
+
b97e22
+    resetToken: function() {
b97e22
+        this._token = null;
b97e22
+    }
b97e22
+});
b97e22
+Signals.addSignalMethods(OVirtCredentialsManager.prototype);
b97e22
+
b97e22
+function getOVirtCredentialsManager() {
b97e22
+    if (!_oVirtCredentialsManager)
b97e22
+        _oVirtCredentialsManager = new OVirtCredentialsManager();
b97e22
+
b97e22
+    return _oVirtCredentialsManager;
b97e22
+}
b97e22
diff --git a/js/gdm/util.js b/js/gdm/util.js
b97e22
index f421902..66852fd 100644
b97e22
--- a/js/gdm/util.js
b97e22
+++ b/js/gdm/util.js
b97e22
@@ -10,6 +10,7 @@ const St = imports.gi.St;
b97e22
 
b97e22
 const Batch = imports.gdm.batch;
b97e22
 const Fprint = imports.gdm.fingerprint;
b97e22
+const OVirt = imports.gdm.oVirt;
b97e22
 const Main = imports.ui.main;
b97e22
 const Params = imports.misc.params;
b97e22
 const ShellEntry = imports.ui.shellEntry;
b97e22
@@ -19,6 +20,7 @@ const Tweener = imports.ui.tweener;
b97e22
 const PASSWORD_SERVICE_NAME = 'gdm-password';
b97e22
 const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
b97e22
 const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
b97e22
+const OVIRT_SERVICE_NAME = 'gdm-ovirtcred';
b97e22
 const FADE_ANIMATION_TIME = 0.16;
b97e22
 const CLONE_FADE_ANIMATION_TIME = 0.25;
b97e22
 
b97e22
@@ -151,6 +153,14 @@ const ShellUserVerifier = new Lang.Class({
b97e22
         this.reauthenticating = false;
b97e22
 
b97e22
         this._failCounter = 0;
b97e22
+
b97e22
+        this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
b97e22
+
b97e22
+        if (this._oVirtCredentialsManager.hasToken())
b97e22
+            this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
b97e22
+
b97e22
+        this._oVirtCredentialsManager.connect('user-authenticated',
b97e22
+                                              Lang.bind(this, this._oVirtUserAuthenticated));
b97e22
     },
b97e22
 
b97e22
     begin: function(userName, hold) {
b97e22
@@ -277,6 +287,11 @@ const ShellUserVerifier = new Lang.Class({
b97e22
             }));
b97e22
     },
b97e22
 
b97e22
+    _oVirtUserAuthenticated: function(token) {
b97e22
+        this._preemptingService = OVIRT_SERVICE_NAME;
b97e22
+        this.emit('ovirt-user-authenticated');
b97e22
+    },
b97e22
+
b97e22
     _checkForSmartcard: function() {
b97e22
         let smartcardDetected;
b97e22
 
b97e22
@@ -455,6 +470,12 @@ const ShellUserVerifier = new Lang.Class({
b97e22
         if (!this.serviceIsForeground(serviceName))
b97e22
             return;
b97e22
 
b97e22
+        if (serviceName == OVIRT_SERVICE_NAME) {
b97e22
+            // The only question asked by this service is "Token?"
b97e22
+            this.answerQuery(serviceName, this._oVirtCredentialsManager.getToken());
b97e22
+            return;
b97e22
+        }
b97e22
+
b97e22
         this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
b97e22
     },
b97e22
 
b97e22
@@ -515,6 +536,16 @@ const ShellUserVerifier = new Lang.Class({
b97e22
     },
b97e22
 
b97e22
     _onConversationStopped: function(client, serviceName) {
b97e22
+        // If the login failed with the preauthenticated oVirt credentials
b97e22
+        // then discard the credentials and revert to default authentication
b97e22
+        // mechanism.
b97e22
+        if (this.serviceIsForeground(OVIRT_SERVICE_NAME)) {
b97e22
+            this._oVirtCredentialsManager.resetToken();
b97e22
+            this._preemptingService = null;
b97e22
+            this._verificationFailed(false);
b97e22
+            return;
b97e22
+        }
b97e22
+
b97e22
         // if the password service fails, then cancel everything.
b97e22
         // But if, e.g., fingerprint fails, still give
b97e22
         // password authentication a chance to succeed
b97e22
diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
b97e22
index 1d296a2..ecc0bfa 100644
b97e22
--- a/js/ui/screenShield.js
b97e22
+++ b/js/ui/screenShield.js
b97e22
@@ -17,6 +17,7 @@ const Background = imports.ui.background;
b97e22
 const GnomeSession = imports.misc.gnomeSession;
b97e22
 const Hash = imports.misc.hash;
b97e22
 const Layout = imports.ui.layout;
b97e22
+const OVirt = imports.gdm.oVirt;
b97e22
 const LoginManager = imports.misc.loginManager;
b97e22
 const Lightbox = imports.ui.lightbox;
b97e22
 const Main = imports.ui.main;
b97e22
@@ -514,6 +515,13 @@ const ScreenShield = new Lang.Class({
b97e22
                                                this._liftShield(true, 0);
b97e22
                                        }));
b97e22
 
b97e22
+        this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
b97e22
+        this._oVirtCredentialsManager.connect('user-authenticated',
b97e22
+                                              Lang.bind(this, function() {
b97e22
+                                                  if (this._isLocked)
b97e22
+                                                      this._liftShield(true, 0);
b97e22
+                                              }));
b97e22
+
b97e22
         this._inhibitor = null;
b97e22
         this._aboutToSuspend = false;
b97e22
         this._loginManager = LoginManager.getLoginManager();
b97e22
-- 
b97e22
1.8.3.1
b97e22