Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebUI: Certificate login #559

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions freeipa.spec.in
Expand Up @@ -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
Expand Down
33 changes: 31 additions & 2 deletions install/conf/ipa.conf
@@ -1,11 +1,16 @@
#
# VERSION 23 - DO NOT REMOVE THIS LINE
# VERSION 24 - DO NOT REMOVE THIS LINE
#
# 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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions install/share/gssproxy.conf.template
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions install/ui/src/freeipa/auth.js
Expand Up @@ -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
Expand Down Expand Up @@ -249,4 +249,4 @@ auth.Auth = declare([Stateful, Evented], {

auth.current = new auth.Auth();
return auth;
});
});
73 changes: 71 additions & 2 deletions install/ui/src/freeipa/widgets/LoginScreen.js
Expand Up @@ -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',
Expand All @@ -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) {


Expand All @@ -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",
Expand All @@ -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.
Expand All @@ -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"),
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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();
Expand All @@ -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');
Expand Down Expand Up @@ -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);
},

Expand Down
5 changes: 5 additions & 0 deletions install/ui/src/freeipa/widgets/LoginScreenBase.js
Expand Up @@ -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();
},
Expand Down
20 changes: 20 additions & 0 deletions ipaclient/install/client.py
Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions ipaserver/install/httpinstance.py
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions ipaserver/install/server/upgrade.py
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down