Add automatic browser configuration for kerberos SSO using javascript.

This uses the UniversalPreferencesWrite function to set the browser
preferences to allow negotiation and ticket forwarding in the IPA domain.
A self-signed certificate is generated to sign the javascript.
This commit is contained in:
Rob Crittenden
2007-12-12 09:36:32 -05:00
parent 1c3849eb57
commit 6390db3502
6 changed files with 134 additions and 9 deletions

View File

@@ -19,6 +19,7 @@ app_DATA = \
krb.con.template \ krb.con.template \
krbrealm.con.template \ krbrealm.con.template \
ntp.conf.server.template \ ntp.conf.server.template \
preferences.html.template \
radius.radiusd.conf.template \ radius.radiusd.conf.template \
referint-conf.ldif \ referint-conf.ldif \
dna-posix.ldif \ dna-posix.ldif \

View File

@@ -0,0 +1,33 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Automatically set browser preferences</title>
</head>
<body>
<form action="undefined" method="get">
<input type=button onclick="setPreferences()" name="prefs" value="Configure Firefox">
</form>
<script type="text/javascript">
function setPreferences() {
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalPreferencesWrite");
try {
navigator.preference("network.negotiate-auth.using-native-gsslib", true)
navigator.preference("network.negotiate-auth.delegation-uris", ".$DOMAIN")
navigator.preference("network.negotiate-auth.trusted-uris", ".$DOMAIN")
navigator.preference("network.negotiate-auth.allow-proxies", true)
} catch (e) {
alert("Unable to store preferences: " + e)
}
netscape.security.PrivilegeManager.disablePrivilege("UniversalPreferencesWrite");
alert("Successfully configured Firefox for single sign on.")
} catch (e) {
alert("Unable to apply recommended settings.\n\nClick on the Certificate Authority link and select trust for all, then reload this page and try again.\n\nThe error returned was: " + e);
return;
}
}
</script>
</body>
</html>

View File

@@ -77,6 +77,11 @@ class CertDB(object):
new_args = new_args + args new_args = new_args + args
ipautil.run(new_args, stdin) ipautil.run(new_args, stdin)
def run_signtool(self, args, stdin=None):
new_args = ["/usr/bin/signtool", "-d", self.secdir]
new_args = new_args + args
ipautil.run(new_args, stdin)
def create_noise_file(self): def create_noise_file(self):
ipautil.backup_file(self.noise_fname) ipautil.backup_file(self.noise_fname)
f = open(self.noise_fname, "w") f = open(self.noise_fname, "w")
@@ -108,7 +113,7 @@ class CertDB(object):
self.run_certutil(["-S", "-n", self.cacert_name, self.run_certutil(["-S", "-n", self.cacert_name,
"-s", "cn=CAcert", "-s", "cn=CAcert",
"-x", "-x",
"-t", "CT,,", "-t", "CT,,C",
"-m", self.next_serial(), "-m", self.next_serial(),
"-v", self.valid_months, "-v", self.valid_months,
"-z", self.noise_fname, "-z", self.noise_fname,
@@ -130,7 +135,7 @@ class CertDB(object):
def load_cacert(self, cacert_fname): def load_cacert(self, cacert_fname):
self.run_certutil(["-A", "-n", self.cacert_name, self.run_certutil(["-A", "-n", self.cacert_name,
"-t", "CT,CT,", "-t", "CT,,C",
"-a", "-a",
"-i", cacert_fname]) "-i", cacert_fname])
@@ -139,7 +144,17 @@ class CertDB(object):
if not cdb: if not cdb:
cdb = self cdb = self
self.request_cert(name) self.request_cert(name)
cdb.issue_cert(self.certreq_fname, self.certder_fname) cdb.issue_server_cert(self.certreq_fname, self.certder_fname)
self.add_cert(self.certder_fname, nickname)
os.unlink(self.certreq_fname)
os.unlink(self.certder_fname)
def create_signing_cert(self, nickname, name, other_certdb=None):
cdb = other_certdb
if not cdb:
cdb = self
self.request_cert(name)
cdb.issue_signing_cert(self.certreq_fname, self.certder_fname)
self.add_cert(self.certder_fname, nickname) self.add_cert(self.certder_fname, nickname)
os.unlink(self.certreq_fname) os.unlink(self.certreq_fname)
os.unlink(self.certder_fname) os.unlink(self.certder_fname)
@@ -151,7 +166,7 @@ class CertDB(object):
"-z", self.noise_fname, "-z", self.noise_fname,
"-f", self.passwd_fname]) "-f", self.passwd_fname])
def issue_cert(self, certreq_fname, cert_fname): def issue_server_cert(self, certreq_fname, cert_fname):
p = subprocess.Popen(["/usr/bin/certutil", p = subprocess.Popen(["/usr/bin/certutil",
"-d", self.secdir, "-d", self.secdir,
"-C", "-c", self.cacert_name, "-C", "-c", self.cacert_name,
@@ -179,8 +194,37 @@ class CertDB(object):
# n - not critical # n - not critical
p.stdin.write("2\n9\nn\n1\n9\nn\n") p.stdin.write("2\n9\nn\n1\n9\nn\n")
p.wait() p.wait()
def issue_signing_cert(self, certreq_fname, cert_fname):
p = subprocess.Popen(["/usr/bin/certutil",
"-d", self.secdir,
"-C", "-c", self.cacert_name,
"-i", certreq_fname,
"-o", cert_fname,
"-m", self.next_serial(),
"-v", self.valid_months,
"-f", self.passwd_fname,
"-1", "-5"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
# Bah - this sucks, but I guess it isn't possible to fully
# control this with command line arguments.
#
# What this is requesting is:
# -1 (Create key usage extension)
# 0 - Digital Signature
# 5 - Cert signing key
# 9 - done
# n - not critical
#
# -5 (Create netscape cert type extension)
# 3 - Object Signing
# 9 - done
# n - not critical
p.stdin.write("0\n5\n9\nn\n3\n9\nn\n")
p.wait()
def add_cert(self, cert_fname, nickname): def add_cert(self, cert_fname, nickname):
self.run_certutil(["-A", "-n", nickname, self.run_certutil(["-A", "-n", nickname,
"-t", "u,u,u", "-t", "u,u,u",

View File

@@ -25,6 +25,7 @@ import pwd
import fileinput import fileinput
import sys import sys
import time import time
import shutil
import service import service
import certs import certs
@@ -49,9 +50,10 @@ class HTTPInstance(service.Service):
service.Service.__init__(self, "httpd") service.Service.__init__(self, "httpd")
def create_instance(self, realm, fqdn): def create_instance(self, realm, fqdn):
self.sub_dict = { "REALM" : realm, "FQDN": fqdn }
self.fqdn = fqdn self.fqdn = fqdn
self.realm = realm self.realm = realm
self.domain = fqdn[fqdn.find(".")+1:]
self.sub_dict = { "REALM" : realm, "FQDN": fqdn, "DOMAIN" : self.domain }
self.start_creation(7, "Configuring the web interface") self.start_creation(7, "Configuring the web interface")
@@ -60,6 +62,7 @@ class HTTPInstance(service.Service):
self.__configure_http() self.__configure_http()
self.__create_http_keytab() self.__create_http_keytab()
self.__setup_ssl() self.__setup_ssl()
self.__setup_autoconfig()
self.step("restarting httpd") self.step("restarting httpd")
self.restart() self.restart()
@@ -141,4 +144,31 @@ class HTTPInstance(service.Service):
ds_ca.cur_serial = 2000 ds_ca.cur_serial = 2000
ca.create_from_cacert(ds_ca.cacert_fname) ca.create_from_cacert(ds_ca.cacert_fname)
ca.create_server_cert("Server-Cert", "cn=%s,ou=Apache Web Server" % self.fqdn, ds_ca) ca.create_server_cert("Server-Cert", "cn=%s,ou=Apache Web Server" % self.fqdn, ds_ca)
ca.create_signing_cert("Signing-Cert", "cn=%s,ou=Signing Certificate,o=Identity Policy Audit" % self.fqdn, ds_ca)
def __setup_autoconfig(self):
prefs_txt = template_file(SHARE_DIR + "preferences.html.template", self.sub_dict)
prefs_fd = open("/usr/share/ipa/html/preferences.html", "w")
prefs_fd.write(prefs_txt)
prefs_fd.close()
# The signing cert is generated in __setup_ssl
ds_ca = certs.CertDB(dsinstance.config_dirname(self.realm))
ca = certs.CertDB(NSS_DIR)
# Publish the CA certificate
shutil.copy(ds_ca.cacert_fname, "/usr/share/ipa/html/ca.crt")
os.chmod("/usr/share/ipa/html/ca.crt", 0444)
try:
shutil.rmtree("/tmp/ipa")
except:
pass
os.mkdir("/tmp/ipa")
shutil.copy("/usr/share/ipa/html/preferences.html", "/tmp/ipa")
ca.run_signtool(["-k", "Signing-Cert",
"-Z", "/usr/share/ipa/html/configure.jar",
"-e", ".html",
"/tmp/ipa"])
shutil.rmtree("/tmp/ipa")

View File

@@ -12,9 +12,12 @@ RewriteRule ^/(.*) http://$FQDN/$$1 [L,R=301]
# Redirect to the secure port if not displaying an error or retrieving # Redirect to the secure port if not displaying an error or retrieving
# configuration. # configuration.
RewriteCond %{SERVER_PORT} !^443$$ RewriteCond %{SERVER_PORT} !^443$$
RewriteCond %{REQUEST_URI} !^/(errors|config)/ RewriteCond %{REQUEST_URI} !^/(errors|config|favicon.ico)
RewriteRule ^/(.*) https://$FQDN/$$1 [L,R=301,NC] RewriteRule ^/(.*) https://$FQDN/$$1 [L,R=301,NC]
# This is required so the auto-configuration works with Firefox 2+
AddType application/java-archive jar
<Proxy *> <Proxy *>
AuthType Kerberos AuthType Kerberos
AuthName "Kerberos Login" AuthName "Kerberos Login"

View File

@@ -9,6 +9,20 @@ have <a href="/errors/ssbrowser.html">configured your
browser correctly</a>. If you are still unable to access browser correctly</a>. If you are still unable to access
the IPA Web interface, please contact the helpdesk on for additional assistance. the IPA Web interface, please contact the helpdesk on for additional assistance.
</p> </p>
<p>
Import the <a href="/errors/ca.crt">IPA Certificate Authority</a>.
</p>
<p>
<script type="text/javascript">
if (navigator.userAgent.indexOf("Firefox") != -1 ||
navigator.userAgent.indexOf("SeaMonkey") != -1)
{
document.write("<p>You can automatically configure your browser to work with Kerberos by importing the Certificate Authority below and clicking on the Configure Browser button.</p>");
document.write("<p>You <strong>must</strong> reload this page after importing the Certificate Authority for the automatic settings to work</p>");
document.write("<object data=\"jar:/errots/configure.jar!/preferences.html\" type=\"text/html\"><\/object");
}
</script>
</p>
</ul> </ul>
</body> </body>
</html> </html>