diff options
author | Petr Vobornik <pvoborni@redhat.com> | 2012-10-01 17:25:08 +0200 |
---|---|---|
committer | Rob Crittenden <rcritten@redhat.com> | 2012-10-04 18:07:29 -0400 |
commit | b4e19509c034942a4f6bc99c371774a0944b65eb (patch) | |
tree | 7ac790c547283c1c6b9f5665c208bf93bb02e302 | |
parent | 459c83fb75fd6077ab3e5981f6e04f13ad3379c8 (diff) | |
download | freeipa-b4e19509c034942a4f6bc99c371774a0944b65eb.tar.gz freeipa-b4e19509c034942a4f6bc99c371774a0944b65eb.tar.xz freeipa-b4e19509c034942a4f6bc99c371774a0944b65eb.zip |
Kerberos authentication extension
The extension should replace signed code (configure.jar) used for Firefox configuration. Using privileged code is not possible since Firefox 15 [1] [2]. Extension is bootstrapped which means it can be used without browser restart on Firefox 4 and later.
How it works:
Extension listens on each page's document element for event 'kerberos-auth-config' which should be raised on custom data element. Communication data is transferred through data element's attributes [3]. The only required attribute is 'method'. Currently there are two possible values: 'configure' and 'can_configure'.
'can_configure' method serves for detecting if the extension is installed. 'configure' method does the actual configuration. Possible optional options for 'configure' can be found in kerberosauth.js:kerberosauth.config_options. Currently they are: 'referer', 'native_gss_lib', 'trusted_uris', 'allow_proxies'. Result of a method is stored in data element's 'answer' attribute. When 'configure' method is used, the extension asks the user if he wants to configure the browser, it should prevent silent configuration by malicious pages.
Possible enhancement:
* add UI for manual edit
* more configurations ie. for gss_lib, sspi (good with UI or with enhanced config page)
* introspection of client (read ipa client install config and such)
Ticket: https://fedorahosted.org/freeipa/ticket/3094
[1] https://bugzilla.mozilla.org/show_bug.cgi?id=546848
[2] https://bugzilla.mozilla.org/show_bug.cgi?id=757046
[3] https://developer.mozilla.org/en-US/docs/Code_snippets/Interaction_between_privileged_and_non-privileged_pages
-rw-r--r-- | install/ffextension/bootstrap.js | 88 | ||||
-rw-r--r-- | install/ffextension/chrome.manifest | 4 | ||||
-rw-r--r-- | install/ffextension/chrome/content/kerberosauth.js | 175 | ||||
-rw-r--r-- | install/ffextension/chrome/content/kerberosauth_overlay.xul | 9 | ||||
-rw-r--r-- | install/ffextension/install.rdf | 26 | ||||
-rw-r--r-- | install/ffextension/locale/en-US/kerberosauth.properties | 4 |
6 files changed, 306 insertions, 0 deletions
diff --git a/install/ffextension/bootstrap.js b/install/ffextension/bootstrap.js new file mode 100644 index 000000000..7e2ae576c --- /dev/null +++ b/install/ffextension/bootstrap.js @@ -0,0 +1,88 @@ +// Heavily inspired by Dave Townsend's post: +// Playing with windows in restartless (bootstrapped) extensions +// http://www.oxymoronical.com/blog/2011/01/Playing-with-windows-in-restartless-bootstrapped-extensions + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +var WindowListener = { + + setupBrowserUI: function(domWindow) { + var doc = domWindow.document; + domWindow.kerberosauth_listener = kerberosauth_listener(domWindow); + doc.addEventListener('kerberos-auth-config', domWindow.kerberosauth_listener, false, true); + }, + + tearDownBrowserUI: function(domWindow) { + + var doc = domWindow.document; + doc.removeEventListener('kerberos-auth-config', domWindow.kerberosauth_listener); + delete domWindow.kerberosauth_listener; + }, + + // nsIWindowMediatorListener functions + onOpenWindow: function(xulWindow) { + // A new window has opened + var domWindow = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowInternal); + + // Wait for it to finish loading + domWindow.addEventListener("load", function listener() { + domWindow.removeEventListener("load", listener, false); + + // If this is a browser window then setup its UI + if (domWindow.document.documentElement.getAttribute("windowtype") === "navigator:browser") { + WindowListener.setupBrowserUI(domWindow); + } + }, false); + }, + + onCloseWindow: function(xulWindow) { + }, + + onWindowTitleChange: function(xulWindow, newTitle) { + } +}; + +function startup(data, reason) { + var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator); + + Cu['import']("chrome://kerberosauth/content/kerberosauth.js"); + + // Get the list of browser windows already open + var windows = wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) { + var domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow); + + WindowListener.setupBrowserUI(domWindow); + } + + // Wait for any new browser windows to open + wm.addListener(WindowListener); +} + +function shutdown(data, reason) { + // When the application is shutting down we normally don't have to clean + // up any UI changes made + if (reason == APP_SHUTDOWN) + return; + + var wm = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator); + + // Get the list of browser windows already open + var windows = wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) { + var domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow); + WindowListener.tearDownBrowserUI(domWindow); + } + + // Stop listening for any new browser windows to open + wm.removeListener(WindowListener); + + Cu.unload("chrome://kerberosauth/content/kerberosauth.js"); +} + +function install() {} +function uninstall() {}
\ No newline at end of file diff --git a/install/ffextension/chrome.manifest b/install/ffextension/chrome.manifest new file mode 100644 index 000000000..775d3a338 --- /dev/null +++ b/install/ffextension/chrome.manifest @@ -0,0 +1,4 @@ +content kerberosauth chrome/content/ +resource kerberosauth chrome/content/ +overlay chrome://browser/content/browser.xul resource://kerberosauth/kerberosauth_overlay.xul +locale kerberosauth en-US locale/en-US/
\ No newline at end of file diff --git a/install/ffextension/chrome/content/kerberosauth.js b/install/ffextension/chrome/content/kerberosauth.js new file mode 100644 index 000000000..a79c925bf --- /dev/null +++ b/install/ffextension/chrome/content/kerberosauth.js @@ -0,0 +1,175 @@ +/* Authors: + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2012 Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +var EXPORTED_SYMBOLS = ["kerberosauth", "kerberosauth_listener"]; + +var Cc = Components.classes; +var Ci = Components.interfaces; + +var kerberosauth = { + + // Dictionary of configuration options this extension can configure. + // An alias (key) is set for each options. Using a set of aliases limits + // configuration pages from supplying potential malicious options. + config_options: { + referer: ['network.http.sendRefererHeader', 'int'], + native_gss_lib: ['network.negotiate-auth.using-native-gsslib', 'bool'], + trusted_uris: ['network.negotiate-auth.trusted-uris', 'str'], + allow_proxies: ['network.negotiate-auth.allow-proxies', 'bool'] + }, + + // Some preconfigurations to make things easier. Can be good if UI is added + // (mostly for future usage). + predefined_configurations: { + ipa: { + referer: '2', + native_gss_lib: 'true', + trusted_uris: '', + allow_proxies: 'true' + } + }, + + page_listener: function(event, dom_window) { + + var self = this; + + var conf = { + event: event, + window: dom_window || window, + element: event.target + }; + + if (!conf.element.hasAttribute('method')) return; + + var method = conf.element.getAttribute('method'); + + if (method === 'configure') self.configure(conf); + if (method === 'can_configure') self.send_response(conf.element, { answer: 'true' }); + }, + + send_response: function(element, options) { + + options = options || {}; + + var doc = element.ownerDocument; + + for (var opt in options) { + element.setAttribute(opt, options[opt]); + } + + var answer_event = doc.createEvent("HTMLEvents"); + answer_event.initEvent("kerberos-auth-answer", true, false); + element.dispatchEvent(answer_event); + }, + + notify_installed: function(window) { + var doc = window.document; + var event = doc.createEvent("HTMLEvents"); + event.initEvent("kerberos-auth-installed", true, false); + doc.dispatchEvent(event); + }, + + configure: function(conf) { + var self = this; + + var options = {}; // options to be configured + var opt; + + // use predefined configuration if supplied + if (conf.element.hasAttribute('predefined')) { + var predefined = conf.element.getAttribute('predefined'); + + var pconfig = self.predefined_configurations[predefined]; + if (pconfig) { + for (opt in pconfig) { + options[opt] = pconfig[opt]; + } + } + } + + // overwrite predefined with supplied and only supported options + for (var i=0; i < conf.element.attributes.length; i++) { + var attr = conf.element.attributes[i].name; + if (attr in self.config_options) { + options[attr] = conf.element.getAttribute(attr); + } + } + + if (self.prompt(conf, options)) { + self.configure_core(conf, options); + self.send_response(conf.element, { answer: 'configured' }); + } else { + self.send_response(conf.element, { answer: 'aborted' }); + } + }, + + configure_core: function(conf, options) { + + var self = this; + + var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); + + for (var opt in options) { + + var name = self.config_options[opt][0]; + var type = self.config_options[opt][1]; + var value = options[opt]; + + if (type === 'str') { + prefs.setCharPref(name, value); + } else if (type ==='int') { + prefs.setIntPref(name, Number(value)); + } else if (type === 'bool') { + prefs.setBoolPref(name, value === 'true'); + } + } + }, + + prompt: function(conf, options) { + var strs = Cc["@mozilla.org/intl/stringbundle;1"]. + getService(Ci.nsIStringBundleService). + createBundle("chrome://kerberosauth/locale/kerberosauth.properties"); + + var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"]. + getService(Ci.nsIPromptService); + + var title = strs.GetStringFromName('prompt_title'); + var text = strs.GetStringFromName('prompt_topic'); + + if (options.trusted_uris) { + text += strs.GetStringFromName('prompt_domain').replace('${domain}', options.trusted_uris); + } + text += strs.GetStringFromName('prompt_question'); + + var flags = prompts.STD_YES_NO_BUTTONS; + + var confirmed = prompts.confirmEx(conf.window, title, text, flags, "","","", + null,{value: false}) === 0; + return confirmed; + } +}; + +var kerberosauth_listener = function(window) { + + return function(event) { + + kerberosauth.page_listener(event, window); + }; +};
\ No newline at end of file diff --git a/install/ffextension/chrome/content/kerberosauth_overlay.xul b/install/ffextension/chrome/content/kerberosauth_overlay.xul new file mode 100644 index 000000000..acad07926 --- /dev/null +++ b/install/ffextension/chrome/content/kerberosauth_overlay.xul @@ -0,0 +1,9 @@ +<?xml version="1.0"?> + +<overlay id="kerberosauthOverlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script type="application/x-javascript"> + Components.utils['import']("resource://kerberosauth/kerberosauth.js"); + window.addEventListener('kerberos-auth-config', kerberosauth_listener(window), false, true); + </script> +</overlay>
\ No newline at end of file diff --git a/install/ffextension/install.rdf b/install/ffextension/install.rdf new file mode 100644 index 000000000..d931f19d8 --- /dev/null +++ b/install/ffextension/install.rdf @@ -0,0 +1,26 @@ +<?xml version="1.0"?> +<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:em="http://www.mozilla.org/2004/em-rdf#"> + + <Description about="urn:mozilla:install-manifest"> + + <em:id>kerberosauth@redhat.com</em:id> + <em:name>Kerberos Configuration</em:name> + <em:version>0.1</em:version> + <em:description>Configures browser to use negotiate authentication</em:description> + <em:type>2</em:type> + <em:creator>Red Hat, Inc.</em:creator> + <em:developer>Petr Vobornik</em:developer> + <em:homepageURL>http://www.redhat.com/</em:homepageURL> + <em:bootstrap>true</em:bootstrap> + + <!-- Firefox --> + <em:targetApplication> + <Description> + <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> + <em:minVersion>10.0</em:minVersion> + <em:maxVersion>15.0.*</em:maxVersion> + </Description> + </em:targetApplication> + </Description> +</RDF>
\ No newline at end of file diff --git a/install/ffextension/locale/en-US/kerberosauth.properties b/install/ffextension/locale/en-US/kerberosauth.properties new file mode 100644 index 000000000..b822535ee --- /dev/null +++ b/install/ffextension/locale/en-US/kerberosauth.properties @@ -0,0 +1,4 @@ +prompt_title=Kerberos configuration confirmation +prompt_topic=The page you are visiting is trying to configure Firefox for Kerberos authentication. +prompt_domain=\n\nDomain: ${domain} +prompt_question=\n\nDo you want to configure the browser?
\ No newline at end of file |