From d139adc25f42693330796c694467342cb83ad4d3 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Thu, 27 Jun 2013 08:54:19 -0400 Subject: [PATCH] authPrompt: support smartcard authentication This commit detects when a user inserts a smartcard, and then initiates user verification using the gdm-smartcard PAM service. Likewise, if a user removes their smartcard, password verification (or the user list depending on auth mode and configuration) are initiated https://bugzilla.gnome.org/show_bug.cgi?id=683437 --- js/gdm/authPrompt.js | 33 +++++++++++++++++++++++++++- js/gdm/util.js | 51 +++++++++++++++++++++++++++++++++++++++++++ js/ui/screenShield.js | 8 +++++++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js index 8e673c393..85a2f4b1a 100644 --- a/js/gdm/authPrompt.js +++ b/js/gdm/authPrompt.js @@ -60,6 +60,8 @@ const AuthPrompt = new Lang.Class({ this._userVerifier.connect('reset', Lang.bind(this, this._onReset)); this._userVerifier.connect('show-login-hint', Lang.bind(this, this._onShowLoginHint)); this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._onHideLoginHint)); + this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged)); + this.smartcardDetected = this._userVerifier.smartcardDetected; this.connect('next', Lang.bind(this, function() { this.updateSensitivity(false); @@ -220,6 +222,20 @@ const AuthPrompt = new Lang.Class({ this.emit('prompted'); }, + _onSmartcardStatusChanged: function() { + this.smartcardDetected = this._userVerifier.smartcardDetected; + + // Don't reset on smartcard insertion if we're already verifying + // and the smartcard is the main service + if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) && + this.verificationStatus == AuthPromptStatus.VERIFYING && + this.smartcardDetected) + return; + + if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) + this.reset(); + }, + _onShowMessage: function(userVerifier, message, styleClass) { this.setMessage(message, styleClass); this.emit('prompted'); @@ -429,7 +445,22 @@ const AuthPrompt = new Lang.Class({ if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED) this.emit('failed'); - this.emit('reset', BeginRequestType.PROVIDE_USERNAME); + let beginRequestType; + + if (this._mode == AuthPromptMode.UNLOCK_ONLY) { + // The user is constant at the unlock screen + beginRequestType = BeginRequestType.PROVIDE_USERNAME; + } else if (this.smartcardDetected && + this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) { + // We don't need to know the username if the user preempted the login screen + // with a smartcard. + beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME; + } else { + // In all other cases, we should get the username up front. + beginRequestType = BeginRequestType.PROVIDE_USERNAME; + } + + this.emit('reset', beginRequestType); }, addCharacter: function(unichar) { diff --git a/js/gdm/util.js b/js/gdm/util.js index cc583087a..0733d57e4 100644 --- a/js/gdm/util.js +++ b/js/gdm/util.js @@ -13,16 +13,19 @@ const Fprint = imports.gdm.fingerprint; const Main = imports.ui.main; const Params = imports.misc.params; const ShellEntry = imports.ui.shellEntry; +const SmartcardManager = imports.misc.smartcardManager; const Tweener = imports.ui.tweener; const PASSWORD_SERVICE_NAME = 'gdm-password'; const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; +const SMARTCARD_SERVICE_NAME = 'gdm-smartcard'; const FADE_ANIMATION_TIME = 0.16; const CLONE_FADE_ANIMATION_TIME = 0.25; const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen'; const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication'; const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication'; +const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication'; const BANNER_MESSAGE_KEY = 'banner-message-enable'; const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text'; const ALLOWED_FAILURES_KEY = 'allowed-failures'; @@ -124,6 +127,23 @@ const ShellUserVerifier = new Lang.Class({ this._updateDefaultService(); this._fprintManager = new Fprint.FprintManager(); + this._smartcardManager = SmartcardManager.getSmartcardManager(); + + // We check for smartcards right away, since an inserted smartcard + // at startup should result in immediately initiating authentication. + // This is different than fingeprint readers, where we only check them + // after a user has been picked. + this._checkForSmartcard(); + + this._smartcardManager.connect('smartcard-inserted', + Lang.bind(this, function() { + this._checkForSmartcard(); + })); + this._smartcardManager.connect('smartcard-removed', + Lang.bind(this, function() { + this._checkForSmartcard(); + })); + this._messageQueue = []; this._messageQueueTimeoutId = 0; this.hasPendingMessages = false; @@ -252,6 +272,28 @@ const ShellUserVerifier = new Lang.Class({ })); }, + _checkForSmartcard: function() { + let smartcardDetected; + + if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY)) + smartcardDetected = false; + else if (this.reauthenticating) + smartcardDetected = this._smartcardManager.hasInsertedLoginToken(); + else + smartcardDetected = this._smartcardManager.hasInsertedTokens(); + + if (smartcardDetected != this.smartcardDetected) { + this.smartcardDetected = smartcardDetected; + + if (this.smartcardDetected) + this._preemptingService = SMARTCARD_SERVICE_NAME; + else if (this._preemptingService == SMARTCARD_SERVICE_NAME) + this._preemptingService = null; + + this.emit('smartcard-status-changed'); + } + }, + _reportInitError: function(where, error) { logError(error, where); this._hold.release(); @@ -309,6 +351,9 @@ const ShellUserVerifier = new Lang.Class({ }, _getForegroundService: function() { + if (this._preemptingService) + return this._preemptingService; + return this._defaultService; }, @@ -316,9 +361,15 @@ const ShellUserVerifier = new Lang.Class({ return serviceName == this._getForegroundService(); }, + serviceIsDefault: function(serviceName) { + return serviceName == this._defaultService; + }, + _updateDefaultService: function() { if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY)) this._defaultService = PASSWORD_SERVICE_NAME; + else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY)) + this._defaultService = SMARTCARD_SERVICE_NAME; else if (this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) this._defaultService = FINGERPRINT_SERVICE_NAME; }, diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js index 8271aa0a1..53eea0324 100644 --- a/js/ui/screenShield.js +++ b/js/ui/screenShield.js @@ -23,6 +23,7 @@ const Main = imports.ui.main; const Overview = imports.ui.overview; const MessageTray = imports.ui.messageTray; const ShellDBus = imports.ui.shellDBus; +const SmartcardManager = imports.misc.smartcardManager; const Tweener = imports.ui.tweener; const Util = imports.misc.util; @@ -516,6 +517,13 @@ const ScreenShield = new Lang.Class({ this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this); + this._smartcardManager = SmartcardManager.getSmartcardManager(); + this._smartcardManager.connect('smartcard-inserted', + Lang.bind(this, function() { + if (this._isLocked) + this._liftShield(true, 0); + })); + this._inhibitor = null; this._aboutToSuspend = false; this._loginManager = LoginManager.getLoginManager();