summaryrefslogtreecommitdiff
path: root/mobile/android/components/NSSDialogService.js
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/components/NSSDialogService.js')
-rw-r--r--mobile/android/components/NSSDialogService.js276
1 files changed, 276 insertions, 0 deletions
diff --git a/mobile/android/components/NSSDialogService.js b/mobile/android/components/NSSDialogService.js
new file mode 100644
index 0000000000..671cc8c351
--- /dev/null
+++ b/mobile/android/components/NSSDialogService.js
@@ -0,0 +1,276 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cc = Components.classes;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Prompt",
+ "resource://gre/modules/Prompt.jsm");
+
+// -----------------------------------------------------------------------
+// NSS Dialog Service
+// -----------------------------------------------------------------------
+
+function NSSDialogs() { }
+
+NSSDialogs.prototype = {
+ classID: Components.ID("{cbc08081-49b6-4561-9c18-a7707a50bda1}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsICertificateDialogs, Ci.nsIClientAuthDialogs]),
+
+ /**
+ * Escapes the given input via HTML entity encoding. Used to prevent HTML
+ * injection when the input is to be placed inside an HTML body, but not in
+ * any other context.
+ *
+ * @param {String} input The input to interpret as a plain string.
+ * @returns {String} The escaped input.
+ */
+ escapeHTML: function(input) {
+ return input.replace(/&/g, "&")
+ .replace(/</g, "&lt;")
+ .replace(/>/g, "&gt;")
+ .replace(/"/g, "&quot;")
+ .replace(/'/g, "&#x27;")
+ .replace(/\//g, "&#x2F;");
+ },
+
+ getString: function(aName) {
+ if (!this.bundle) {
+ this.bundle = Services.strings.createBundle("chrome://browser/locale/pippki.properties");
+ }
+ return this.bundle.GetStringFromName(aName);
+ },
+
+ formatString: function(aName, argList) {
+ if (!this.bundle) {
+ this.bundle =
+ Services.strings.createBundle("chrome://browser/locale/pippki.properties");
+ }
+ let escapedArgList = Array.from(argList, x => this.escapeHTML(x));
+ return this.bundle.formatStringFromName(aName, escapedArgList,
+ escapedArgList.length);
+ },
+
+ getPrompt: function(aTitle, aText, aButtons) {
+ return new Prompt({
+ title: aTitle,
+ text: aText,
+ buttons: aButtons,
+ });
+ },
+
+ showPrompt: function(aPrompt) {
+ let response = null;
+ aPrompt.show(function(data) {
+ response = data;
+ });
+
+ // Spin this thread while we wait for a result
+ let thread = Services.tm.currentThread;
+ while (response === null)
+ thread.processNextEvent(true);
+
+ return response;
+ },
+
+ confirmDownloadCACert: function(aCtx, aCert, aTrust) {
+ while (true) {
+ let prompt = this.getPrompt(this.getString("downloadCert.title"),
+ this.getString("downloadCert.message1"),
+ [ this.getString("nssdialogs.ok.label"),
+ this.getString("downloadCert.viewCert.label"),
+ this.getString("nssdialogs.cancel.label")
+ ]);
+
+ prompt.addCheckbox({ id: "trustSSL", label: this.getString("downloadCert.trustSSL"), checked: false })
+ .addCheckbox({ id: "trustEmail", label: this.getString("downloadCert.trustEmail"), checked: false })
+ .addCheckbox({ id: "trustSign", label: this.getString("downloadCert.trustObjSign"), checked: false });
+ let response = this.showPrompt(prompt);
+
+ // they hit the "view cert" button, so show the cert and try again
+ if (response.button == 1) {
+ this.viewCert(aCtx, aCert);
+ continue;
+ } else if (response.button != 0) {
+ return false;
+ }
+
+ aTrust.value = Ci.nsIX509CertDB.UNTRUSTED;
+ if (response.trustSSL) aTrust.value |= Ci.nsIX509CertDB.TRUSTED_SSL;
+ if (response.trustEmail) aTrust.value |= Ci.nsIX509CertDB.TRUSTED_EMAIL;
+ if (response.trustSign) aTrust.value |= Ci.nsIX509CertDB.TRUSTED_OBJSIGN;
+ return true;
+ }
+ },
+
+ setPKCS12FilePassword: function(aCtx, aPassword) {
+ // this dialog is never shown in Fennec; in Desktop it is shown while backing up a personal
+ // certificate to a file via Preferences->Advanced->Encryption->View Certificates->Your Certificates
+ throw "Unimplemented";
+ },
+
+ getPKCS12FilePassword: function(aCtx, aPassword) {
+ let prompt = this.getPrompt(this.getString("pkcs12.getpassword.title"),
+ this.getString("pkcs12.getpassword.message"),
+ [ this.getString("nssdialogs.ok.label"),
+ this.getString("nssdialogs.cancel.label")
+ ]).addPassword({id: "pw"});
+ let response = this.showPrompt(prompt);
+ if (response.button != 0) {
+ return false;
+ }
+
+ aPassword.value = response.pw;
+ return true;
+ },
+
+ certInfoSection: function(aHeading, aDataPairs, aTrailingNewline = true) {
+ let certInfoStrings = [
+ "<big>" + this.getString(aHeading) + "</big>",
+ ];
+
+ for (let i = 0; i < aDataPairs.length; i += 2) {
+ let key = aDataPairs[i];
+ let value = aDataPairs[i + 1];
+ certInfoStrings.push(this.formatString(key, [value]));
+ }
+
+ if (aTrailingNewline) {
+ certInfoStrings.push("<br/>");
+ }
+
+ return certInfoStrings.join("<br/>");
+ },
+
+ viewCert: function(aCtx, aCert) {
+ let p = this.getPrompt(this.getString("certmgr.title"), "", [
+ this.getString("nssdialogs.ok.label"),
+ ]);
+ p.addLabel({ label: this.certInfoSection("certmgr.subjectinfo.label",
+ ["certdetail.cn", aCert.commonName,
+ "certdetail.o", aCert.organization,
+ "certdetail.ou", aCert.organizationalUnit,
+ "certdetail.serialnumber", aCert.serialNumber])})
+ .addLabel({ label: this.certInfoSection("certmgr.issuerinfo.label",
+ ["certdetail.cn", aCert.issuerCommonName,
+ "certdetail.o", aCert.issuerOrganization,
+ "certdetail.ou", aCert.issuerOrganizationUnit])})
+ .addLabel({ label: this.certInfoSection("certmgr.periodofvalidity.label",
+ ["certdetail.notBefore", aCert.validity.notBeforeLocalDay,
+ "certdetail.notAfter", aCert.validity.notAfterLocalDay])})
+ .addLabel({ label: this.certInfoSection("certmgr.fingerprints.label",
+ ["certdetail.sha256fingerprint", aCert.sha256Fingerprint,
+ "certdetail.sha1fingerprint", aCert.sha1Fingerprint],
+ false) });
+ this.showPrompt(p);
+ },
+
+ /**
+ * Returns a list of details of the given cert relevant for TLS client
+ * authentication.
+ *
+ * @param {nsIX509Cert} cert Cert to get the details of.
+ * @returns {String} <br/> delimited list of details.
+ */
+ getCertDetails: function(cert) {
+ let detailLines = [
+ this.formatString("clientAuthAsk.issuedTo", [cert.subjectName]),
+ this.formatString("clientAuthAsk.serial", [cert.serialNumber]),
+ this.formatString("clientAuthAsk.validityPeriod",
+ [cert.validity.notBeforeLocalTime,
+ cert.validity.notAfterLocalTime]),
+ ];
+ let keyUsages = cert.keyUsages;
+ if (keyUsages) {
+ detailLines.push(this.formatString("clientAuthAsk.keyUsages",
+ [keyUsages]));
+ }
+ let emailAddresses = cert.getEmailAddresses({});
+ if (emailAddresses.length > 0) {
+ let joinedAddresses = emailAddresses.join(", ");
+ detailLines.push(this.formatString("clientAuthAsk.emailAddresses",
+ [joinedAddresses]));
+ }
+ detailLines.push(this.formatString("clientAuthAsk.issuedBy",
+ [cert.issuerName]));
+ detailLines.push(this.formatString("clientAuthAsk.storedOn",
+ [cert.tokenName]));
+
+ return detailLines.join("<br/>");
+ },
+
+ viewCertDetails: function(details) {
+ let p = this.getPrompt(this.getString("clientAuthAsk.message3"),
+ '',
+ [ this.getString("nssdialogs.ok.label") ]);
+ p.addLabel({ label: details });
+ this.showPrompt(p);
+ },
+
+ chooseCertificate: function(ctx, hostname, port, organization, issuerOrg,
+ certList, selectedIndex) {
+ let rememberSetting =
+ Services.prefs.getBoolPref("security.remember_cert_checkbox_default_setting");
+
+ let serverRequestedDetails = [
+ this.formatString("clientAuthAsk.hostnameAndPort",
+ [hostname, port.toString()]),
+ this.formatString("clientAuthAsk.organization", [organization]),
+ this.formatString("clientAuthAsk.issuer", [issuerOrg]),
+ ].join("<br/>");
+
+ let certNickList = [];
+ let certDetailsList = [];
+ for (let i = 0; i < certList.length; i++) {
+ let cert = certList.queryElementAt(i, Ci.nsIX509Cert);
+ certNickList.push(this.formatString("clientAuthAsk.nickAndSerial",
+ [cert.nickname, cert.serialNumber]));
+ certDetailsList.push(this.getCertDetails(cert));
+ }
+
+ selectedIndex.value = 0;
+ while (true) {
+ let buttons = [
+ this.getString("nssdialogs.ok.label"),
+ this.getString("clientAuthAsk.viewCert.label"),
+ this.getString("nssdialogs.cancel.label"),
+ ];
+ let prompt = this.getPrompt(this.getString("clientAuthAsk.title"),
+ this.getString("clientAuthAsk.message1"),
+ buttons)
+ .addLabel({ id: "requestedDetails", label: serverRequestedDetails } )
+ .addMenulist({
+ id: "nicknames",
+ label: this.getString("clientAuthAsk.message2"),
+ values: certNickList,
+ selected: selectedIndex.value,
+ }).addCheckbox({
+ id: "rememberBox",
+ label: this.getString("clientAuthAsk.remember.label"),
+ checked: rememberSetting
+ });
+ let response = this.showPrompt(prompt);
+ selectedIndex.value = response.nicknames;
+ if (response.button == 1 /* buttons[1] */) {
+ this.viewCertDetails(certDetailsList[selectedIndex.value]);
+ continue;
+ } else if (response.button == 0 /* buttons[0] */) {
+ if (response.rememberBox == true) {
+ let caud = ctx.QueryInterface(Ci.nsIClientAuthUserDecision);
+ if (caud) {
+ caud.rememberClientAuthCertificate = true;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NSSDialogs]);