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
This commit is contained in:
Petr Vobornik 2012-10-01 17:25:08 +02:00 committed by Rob Crittenden
parent 459c83fb75
commit b4e19509c0
6 changed files with 306 additions and 0 deletions

88
install/ffextension/bootstrap.js vendored Normal file
View File

@ -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() {}

View File

@ -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/

View File

@ -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);
};
};

View File

@ -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>

View File

@ -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>

View File

@ -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?