URL: https://github.com/freeipa/freeipa/pull/559
Author: pvomacka
 Title: #559: WebUI: Certificate login
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/559/head:pr559
git checkout pr559
From 94f431f7f1a8b235edea6eba51a87b1fcd5c6625 Mon Sep 17 00:00:00 2001
From: Pavel Vomacka <pvoma...@redhat.com>
Date: Thu, 9 Mar 2017 12:14:21 +0100
Subject: [PATCH 1/2] Support certificate login after installation and upgrade

Add necessary steps which set SSSD and set SELinux boolean during
installation or upgrade. Also create new endpoint in apache for
login using certificates.

https://pagure.io/freeipa/issue/6225
---
 freeipa.spec.in                      |  1 +
 install/conf/ipa.conf                | 31 ++++++++++++++++++++++++++++++-
 install/share/gssproxy.conf.template |  1 +
 ipaclient/install/client.py          | 20 ++++++++++++++++++++
 ipaserver/install/httpinstance.py    |  1 +
 ipaserver/install/server/upgrade.py  |  5 +++++
 6 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 6eb00ee..bc3f3fb 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -255,6 +255,7 @@ Requires: mod_wsgi
 Requires: mod_auth_gssapi >= 1.5.0
 Requires: mod_nss >= 1.0.8-26
 Requires: mod_session
+Requires: mod_lookup_identity
 Requires: python-ldap >= 2.4.15
 Requires: python-gssapi >= 1.2.0
 Requires: acl
diff --git a/install/conf/ipa.conf b/install/conf/ipa.conf
index 419d4e3..7ac67f5 100644
--- a/install/conf/ipa.conf
+++ b/install/conf/ipa.conf
@@ -4,8 +4,13 @@
 # This file may be overwritten on upgrades.
 #
 
-ProxyRequests Off
+# Load lookup_identity module in case it has not been loaded yet
+# The module is used to search users according the certificate.
+<IfModule !lookup_identity_module>
+    LoadModule lookup_identity_module modules/mod_lookup_identity.so
+</IfModule>
 
+ProxyRequests Off
 
 #We use xhtml, a file format that the browser validates
 DirectoryIndex index.html
@@ -70,6 +75,7 @@ WSGIScriptReloading Off
   SessionMaxAge 1800
   GssapiSessionKey file:/etc/httpd/alias/ipasession.key
 
+  GssapiImpersonate On
   GssapiDelegCcacheDir /var/run/ipa/ccaches
   GssapiDelegCcachePerms mode:0660 gid:ipaapi
   GssapiUseS4U2Proxy on
@@ -97,6 +103,29 @@ Alias /ipa/session/cookie "/usr/share/ipa/gssapi.login"
   Allow from all
 </Location>
 
+# Login with user certificate/smartcard configuration
+# This configuration needs to be loaded after <Location "/ipa">
+<Location "/ipa/session/login_x509">
+  AuthType none
+  GssapiDelegCcacheDir /var/run/ipa/ccaches
+  GssapiDelegCcachePerms mode:0660 gid:ipaapi
+  NSSVerifyClient require
+  NSSUserName SSL_CLIENT_CERT
+  LookupUserByCertificate On
+  WSGIProcessGroup ipa
+  WSGIApplicationGroup ipa
+  GssapiImpersonate On
+
+  GssapiUseSessions On
+  Session On
+  SessionCookieName ipa_session path=/ipa;httponly;secure;
+  SessionHeader IPASESSION
+  SessionMaxAge 1800
+  GssapiSessionKey file:/etc/httpd/alias/ipasession.key
+
+  Header unset Set-Cookie
+</Location>
+
 <Location "/ipa/session/change_password">
   Satisfy Any
   Order Deny,Allow
diff --git a/install/share/gssproxy.conf.template b/install/share/gssproxy.conf.template
index fbb158a..d703144 100644
--- a/install/share/gssproxy.conf.template
+++ b/install/share/gssproxy.conf.template
@@ -4,6 +4,7 @@
   cred_store = keytab:$HTTP_KEYTAB
   cred_store = client_keytab:$HTTP_KEYTAB
   allow_protocol_transition = true
+  allow_constrained_delegation = true
   cred_usage = both
   euid = $HTTPD_USER
 
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
index 774eaaf..579d1aa 100644
--- a/ipaclient/install/client.py
+++ b/ipaclient/install/client.py
@@ -846,6 +846,9 @@ def configure_sssd_conf(
         sssdconfig.new_config()
         domain = sssdconfig.new_domain(cli_domain)
 
+    if options.on_master:
+        sssd_enable_service(sssdconfig, 'ifp')
+
     if (
         (options.conf_ssh and file_exists(paths.SSH_CONFIG)) or
         (options.conf_sshd and file_exists(paths.SSHD_CONFIG))
@@ -948,6 +951,23 @@ def configure_sssd_conf(
     return 0
 
 
+def sssd_enable_service(sssdconfig, service):
+    try:
+        sssdconfig.new_service(service)
+    except SSSDConfig.ServiceAlreadyExists:
+        pass
+    except SSSDConfig.ServiceNotRecognizedError:
+        root_logger.error(
+            "Unable to activate the %s service in SSSD config.", service)
+        root_logger.info(
+            "Please make sure you have SSSD built with %s support "
+            "installed.", service)
+        root_logger.info(
+            "Configure %s support manually in /etc/sssd/sssd.conf.", service)
+
+    sssdconfig.activate_service(service)
+
+
 def change_ssh_config(filename, changes, sections):
     if not changes:
         return True
diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
index 3e8fb0c..048f317 100644
--- a/ipaserver/install/httpinstance.py
+++ b/ipaserver/install/httpinstance.py
@@ -53,6 +53,7 @@
     httpd_can_network_connect='on',
     httpd_manage_ipa='on',
     httpd_run_ipa='on',
+    httpd_dbus_sssd='on',
 )
 
 HTTPD_USER = constants.HTTPD_USER
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
index b19c2f0..993835e 100644
--- a/ipaserver/install/server/upgrade.py
+++ b/ipaserver/install/server/upgrade.py
@@ -23,6 +23,7 @@
 import SSSDConfig
 import ipalib.util
 import ipalib.errors
+from ipaclient.install.client import sssd_enable_service
 from ipaplatform import services
 from ipaplatform.tasks import tasks
 from ipapython import ipautil, version, certdb
@@ -1771,6 +1772,10 @@ def upgrade_configuration():
 
     set_sssd_domain_option('ipa_server_mode', 'True')
 
+    sssdconfig = SSSDConfig.SSSDConfig()
+    sssdconfig.import_config()
+    sssd_enable_service(sssdconfig, 'ifp')
+
     krb = krbinstance.KrbInstance(fstore)
     krb.fqdn = fqdn
     krb.realm = api.env.realm

From 0c4c802d97f63189fa31e0f7a062d8883566cf58 Mon Sep 17 00:00:00 2001
From: Pavel Vomacka <pvoma...@redhat.com>
Date: Thu, 9 Mar 2017 12:17:00 +0100
Subject: [PATCH 2/2] WebUI: add link to login page which for login using
 certificate

Also add error message when login failed.

https://pagure.io/freeipa/issue/6225
---
 install/ui/src/freeipa/auth.js                    |  4 +-
 install/ui/src/freeipa/widgets/LoginScreen.js     | 73 ++++++++++++++++++++++-
 install/ui/src/freeipa/widgets/LoginScreenBase.js |  5 ++
 3 files changed, 78 insertions(+), 4 deletions(-)

diff --git a/install/ui/src/freeipa/auth.js b/install/ui/src/freeipa/auth.js
index 5e160a7..992b54a 100644
--- a/install/ui/src/freeipa/auth.js
+++ b/install/ui/src/freeipa/auth.js
@@ -111,7 +111,7 @@ auth.Auth = declare([Stateful, Evented], {
      * Enabled auth methods
      * @property {string[]}
      */
-    auth_methods: ['kerberos', 'password'],
+    auth_methods: ['kerberos', 'password', 'certificate'],
 
     /**
      * Authenticated user's Kerberos principal
@@ -249,4 +249,4 @@ auth.Auth = declare([Stateful, Evented], {
 
 auth.current = new auth.Auth();
 return auth;
-});
\ No newline at end of file
+});
diff --git a/install/ui/src/freeipa/widgets/LoginScreen.js b/install/ui/src/freeipa/widgets/LoginScreen.js
index 0096433..66d672e 100644
--- a/install/ui/src/freeipa/widgets/LoginScreen.js
+++ b/install/ui/src/freeipa/widgets/LoginScreen.js
@@ -19,10 +19,12 @@
 */
 
 define(['dojo/_base/declare',
+        'dojo/Deferred',
         'dojo/dom-construct',
         'dojo/dom-style',
         'dojo/query',
         'dojo/on',
+        'dojo/topic',
         '../ipa',
         '../auth',
         '../reg',
@@ -31,7 +33,7 @@ define(['dojo/_base/declare',
         '../util',
         './LoginScreenBase'
        ],
-       function(declare, construct, dom_style, query, on,
+       function(declare, Deferred, construct, dom_style, query, on, topic,
                 IPA, auth, reg, FieldBinder, text, util, LoginScreenBase) {
 
 
@@ -55,11 +57,15 @@ define(['dojo/_base/declare',
                     " have valid tickets (obtainable via kinit) and " +
                     "<a href='http://${host}/ipa/config/unauthorized.html'>configured</a>" +
                     " the browser correctly, then click Login. ",
+        cert_msg: "<i class=\"fa fa-info-circle\"></i> To login with <strong>certificate</strong>," +
+              " please make sure you have valid personal certificate. ",
 
         form_auth_failed: "Login failed due to an unknown reason. ",
 
         krb_auth_failed: "Authentication with Kerberos failed",
 
+        cert_auth_failed: "Authentication with personal certificate failed",
+
         password_expired: "Your password has expired. Please enter a new password.",
 
         password_change_complete: "Password change complete",
@@ -72,9 +78,12 @@ define(['dojo/_base/declare',
 
         user_locked: "The user account you entered is locked. ",
 
+        x509_url: '/ipa/session/login_x509',
+
         //nodes:
         login_btn_node: null,
         reset_btn_node: null,
+        cert_btn_node: null,
 
         /**
          * View this form is in.
@@ -86,6 +95,16 @@ define(['dojo/_base/declare',
 
         render_buttons: function(container) {
 
+            this.cert_btn_node = IPA.button({
+                name: 'cert_auth',
+                title:"Login using personal certificate",
+                label: "Login Using Certificate",
+                button_class: 'btn btn-link',
+                click: this.login_with_cert.bind(this)
+            })[0];
+            construct.place(this.cert_btn_node, container);
+            construct.place(document.createTextNode(" "), container);
+
             this.sync_btn_node = IPA.button({
                 name: 'sync',
                 label: text.get('@i18n:login.sync_otp_token', "Sync OTP Token"),
@@ -251,6 +270,18 @@ define(['dojo/_base/declare',
             }.bind(this));
         },
 
+        login_with_cert: function() {
+
+            this.lookup_credentials().then(function(status) {
+                if (status === 200) {
+                    this.emit('logged_in');
+                } else {
+                    var val_summary = this.get_widget('validation');
+                    val_summary.add_error('login', this.cert_auth_failed);
+                }
+            }.bind(this));
+        },
+
         login_and_reset: function() {
 
             var val_summary = this.get_widget('validation');
@@ -293,6 +324,40 @@ define(['dojo/_base/declare',
 
         },
 
+        lookup_credentials: function() {
+
+            var status;
+            var d = new Deferred();
+
+            function error_handler(xhr, text_status, error_thrown) {
+                d.resolve(xhr.status);
+                topic.publish('rpc-end');
+            }
+
+            function success_handler(data, text_status, xhr) {
+                auth.current.set_authenticated(true, 'kerberos');
+                d.resolve(xhr.status);
+                topic.publish('rpc-end');
+            }
+
+            var login = this.get_field('username').get_value()[0];
+
+            var request = {
+                url: this.x509_url,
+                cache: false,
+                type: "GET",
+                data: $.param({
+                    'username': login
+                }),
+                success: success_handler,
+                error: error_handler
+            };
+            topic.publish('rpc-start');
+            $.ajax(request);
+
+            return d.promise;
+        },
+
         refresh: function() {
             if (this.view === 'reset') {
                 this.show_reset_view();
@@ -307,7 +372,7 @@ define(['dojo/_base/declare',
                 var val_summary = this.get_widget('validation');
                 val_summary.add_info('expired', this.expired_msg);
             }
-            this.set_visible_buttons(['sync', 'login']);
+            this.set_visible_buttons(['cert_auth', 'sync', 'login']);
             if (this.password_enabled()) {
                 this.use_fields(['username', 'password']);
                 var username_f = this.get_field('username');
@@ -344,6 +409,10 @@ define(['dojo/_base/declare',
             if (this.kerberos_enabled()) {
                 aside += "<p>"+this.kerberos_msg+"<p/>";
             }
+            if (this.certificate_enabled()) {
+                aside += "<p>"+this.cert_msg+"<p/>";
+            }
+
             this.set('aside', aside);
         },
 
diff --git a/install/ui/src/freeipa/widgets/LoginScreenBase.js b/install/ui/src/freeipa/widgets/LoginScreenBase.js
index a1c986e..a8c207f 100644
--- a/install/ui/src/freeipa/widgets/LoginScreenBase.js
+++ b/install/ui/src/freeipa/widgets/LoginScreenBase.js
@@ -328,6 +328,11 @@ define(['dojo/_base/declare',
             return auth.current.auth_methods.indexOf('password') > -1;
         },
 
+        certificate_enabled: function() {
+            return auth.current.auth_methods.indexOf('certificate') > -1;
+        },
+
+
         postscript: function(args) {
             this.create_fields();
         },
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to