From 59a520b8581ac9f7f18f9f650d663017488035b4 Mon Sep 17 00:00:00 2001 From: "dougt%netscape.com" Date: Tue, 17 Dec 2002 00:59:42 +0000 Subject: [PATCH] Implementing XPInstall Signing. sr=dveditz, r=mstoltz, b=178687 --- xpinstall/macbuild/xpinstall.xml | 30 +++ xpinstall/macbuild/xpinstallIDL.xml | 30 +++ xpinstall/public/Makefile.in | 1 + xpinstall/public/nsISoftwareUpdate.h | 2 + xpinstall/public/nsIXPIDialogService.idl | 16 +- xpinstall/public/nsPICertNotification.idl | 48 ++++ xpinstall/res/content/institems.js | 18 +- xpinstall/res/content/institems.xul | 1 + xpinstall/res/content/xpistatus.js | 1 + .../res/locale/en-US/xpinstall.properties | 1 + xpinstall/src/CertReader.cpp | 249 ++++++++++++++++++ xpinstall/src/CertReader.h | 63 +++++ xpinstall/src/Makefile.in | 2 + xpinstall/src/nsInstall.cpp | 2 + xpinstall/src/nsInstall.h | 6 + xpinstall/src/nsJSInstall.cpp | 1 + xpinstall/src/nsSoftwareUpdate.cpp | 4 +- xpinstall/src/nsSoftwareUpdate.h | 3 +- xpinstall/src/nsSoftwareUpdateRun.cpp | 95 ++++++- xpinstall/src/nsXPITriggerInfo.cpp | 15 +- xpinstall/src/nsXPITriggerInfo.h | 6 +- xpinstall/src/nsXPInstallManager.cpp | 90 ++++++- xpinstall/src/nsXPInstallManager.h | 12 +- xpinstall/stub/xpistub.cpp | 1 + 24 files changed, 671 insertions(+), 26 deletions(-) create mode 100644 xpinstall/public/nsPICertNotification.idl create mode 100644 xpinstall/src/CertReader.cpp create mode 100644 xpinstall/src/CertReader.h diff --git a/xpinstall/macbuild/xpinstall.xml b/xpinstall/macbuild/xpinstall.xml index fa1609a47fa4..8d6f8437e7df 100644 --- a/xpinstall/macbuild/xpinstall.xml +++ b/xpinstall/macbuild/xpinstall.xml @@ -1169,6 +1169,13 @@ Text Debug + + Name + CertReader.cpp + MacOS + Text + Debug + @@ -1356,6 +1363,11 @@ nsXPIProxy.cpp MacOS + + Name + CertReader.cpp + MacOS + @@ -2474,6 +2486,13 @@ Text Debug + + Name + CertReader.cpp + MacOS + Text + Debug + @@ -2661,6 +2680,11 @@ nsXPIProxy.cpp MacOS + + Name + CertReader.cpp + MacOS + @@ -2840,6 +2864,12 @@ nsXPIProxy.cpp MacOS + + xpinstallDebug.shlb + Name + CertReader.cpp + MacOS + NS Libraries Optimized diff --git a/xpinstall/macbuild/xpinstallIDL.xml b/xpinstall/macbuild/xpinstallIDL.xml index 22316d51710e..84f5ab55f0e1 100644 --- a/xpinstall/macbuild/xpinstallIDL.xml +++ b/xpinstall/macbuild/xpinstallIDL.xml @@ -731,6 +731,13 @@ Text + + Name + nsPICertNotification.idl + MacOS + Text + + Name nsIXPIDialogService.idl @@ -762,6 +769,11 @@ nsPIXPIStubHook.idl MacOS + + Name + nsPICertNotification.idl + MacOS + Name nsIXPIDialogService.idl @@ -1452,6 +1464,13 @@ Text + + Name + nsPICertNotification.idl + MacOS + Text + + Name nsIXPIDialogService.idl @@ -1483,6 +1502,11 @@ nsPIXPIStubHook.idl MacOS + + Name + nsPICertNotification.idl + MacOS + Name nsIXPIDialogService.idl @@ -1521,6 +1545,12 @@ nsPIXPIStubHook.idl MacOS + + headers + Name + nsPICertNotification.idl + MacOS + headers Name diff --git a/xpinstall/public/Makefile.in b/xpinstall/public/Makefile.in index 317b8522e543..b3bba3ef661a 100644 --- a/xpinstall/public/Makefile.in +++ b/xpinstall/public/Makefile.in @@ -37,6 +37,7 @@ XPIDLSRCS = \ nsIXPINotifier.idl \ nsPIXPIProxy.idl \ nsPIXPIStubHook.idl \ + nsPICertNotification.idl \ $(NULL) EXPORTS = \ diff --git a/xpinstall/public/nsISoftwareUpdate.h b/xpinstall/public/nsISoftwareUpdate.h index e571b5b3ae96..81b32003ffac 100644 --- a/xpinstall/public/nsISoftwareUpdate.h +++ b/xpinstall/public/nsISoftwareUpdate.h @@ -58,6 +58,7 @@ {0xbc, 0xde, 0x00, 0x80, 0x5f, 0x0e, 0x13, 0x53}\ } +class nsIPrincipal; class nsISoftwareUpdate : public nsISupports { @@ -67,6 +68,7 @@ class nsISoftwareUpdate : public nsISupports NS_IMETHOD InstallJar(nsIFile* localFile, const PRUnichar* URL, const PRUnichar* arguments, + nsIPrincipal* aPrincipalDisplayed, PRUint32 flags, nsIXPIListener* aListener = 0) = 0; diff --git a/xpinstall/public/nsIXPIDialogService.idl b/xpinstall/public/nsIXPIDialogService.idl index ae7380451a7c..bcff53cad836 100644 --- a/xpinstall/public/nsIXPIDialogService.idl +++ b/xpinstall/public/nsIXPIDialogService.idl @@ -63,11 +63,19 @@ interface nsIXPIDialogService : nsISupports * * @param parent a window that can be used to parent the modal dialog * - * @param packageList For each install package there will be two strings, - * a display name and a source URL. + * @param packageList For each install package there will be three strings, + * a display name, a source URL, and a the name of the + * organization that signed this install. Note that the + * name of the signer is not verified. Verification + * happens when the the install has completely downloaded. + * Your user interface should only suggest that the + * install may be signed by this organization name. + * Note that an unsigned archive is indicated by an + * empty string. * * @param count The number of strings in the packageList. This - * will always be even (twice the number of packages) + * will always be three times the number of + * packages. * * @return true to install, false to cancel */ @@ -91,7 +99,7 @@ interface nsIXPIDialogService : nsISupports * be called or nsXPInstallManager will wait forever and never clean * itself up. * - * @param packageList two strings per package as in confirmInstall() + * @param packageList three strings per package as in confirmInstall() * @param count the number of strings in the list * @param observer nsIObserver to receive messages from the dialog */ diff --git a/xpinstall/public/nsPICertNotification.idl b/xpinstall/public/nsPICertNotification.idl new file mode 100644 index 000000000000..8061935f224c --- /dev/null +++ b/xpinstall/public/nsPICertNotification.idl @@ -0,0 +1,48 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is XPInstall Signing. + * + * The Initial Developer of the Original Code is Doug Turner. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +interface nsIURI; +interface nsIPrincipal; + +[uuid(42cd7162-ea4a-4088-9888-63ea5095869e)] +interface nsPICertNotification : nsISupports +{ + void onCertAvailable(in nsIURI aURI, + in nsISupports aContext, + in PRUint32 aStatus, + in nsIPrincipal aPrincipal); +}; diff --git a/xpinstall/res/content/institems.js b/xpinstall/res/content/institems.js index 39f36a7cb681..dac74bd0f397 100644 --- a/xpinstall/res/content/institems.js +++ b/xpinstall/res/content/institems.js @@ -21,7 +21,7 @@ // dialog param block var gParam; -function addTreeItem(num, aName, aUrl) +function addTreeItem(num, aName, aUrl, aCertName) { // first column is the package name var item = document.createElement("description"); @@ -29,7 +29,14 @@ function addTreeItem(num, aName, aUrl) item.setAttribute("tooltiptext", aUrl); item.setAttribute("class", "confirmName"); - // second column is the host serving the file + // second column is for the cert name + var certName = document.createElement("description"); + if (aCertName == "") + certName.setAttribute("value", "Unsigned"); // i18n! + else + certName.setAttribute("value", aCertName); + + // third column is the host serving the file var urltext = aUrl.replace(/^([^:]*:\/*[^\/]+).*/, "$1"); var url = document.createElement('description'); url.setAttribute("value", aUrl); @@ -40,6 +47,7 @@ function addTreeItem(num, aName, aUrl) // create row and add it to the grid var row = document.createElement("row"); row.appendChild(item); + row.appendChild(certName); row.appendChild(url); document.getElementById("xpirows").appendChild(row); @@ -49,7 +57,7 @@ function addTreeItem(num, aName, aUrl) function onLoad() { var row = 0; - var moduleName, URL, numberOfDialogTreeElements; + var moduleName, URL, certName, numberOfDialogTreeElements; doSetOKCancel(onOk, onCancel); @@ -63,7 +71,9 @@ function onLoad() { moduleName = gParam.GetString(i); URL = gParam.GetString(++i); - addTreeItem(row++, moduleName, URL); + certName = gParam.GetString(++i); + + addTreeItem(row++, moduleName, URL, certName); } var okText = document.getElementById("xpinstallBundle").getString("OK"); diff --git a/xpinstall/res/content/institems.xul b/xpinstall/res/content/institems.xul index c12f8a339c03..094f11242e98 100644 --- a/xpinstall/res/content/institems.xul +++ b/xpinstall/res/content/institems.xul @@ -54,6 +54,7 @@ Contributor(s): + diff --git a/xpinstall/res/content/xpistatus.js b/xpinstall/res/content/xpistatus.js index 60bbf880e269..9ad34f63a13e 100644 --- a/xpinstall/res/content/xpistatus.js +++ b/xpinstall/res/content/xpistatus.js @@ -122,6 +122,7 @@ function onLoad() { var moduleName = param.GetString(i++); var URL = param.GetString(i++); + var certName = param.GetString(i++); addTreeItem(row++, moduleName, URL); } diff --git a/xpinstall/res/locale/en-US/xpinstall.properties b/xpinstall/res/locale/en-US/xpinstall.properties index 0d2f112c7177..8660f96aa184 100644 --- a/xpinstall/res/locale/en-US/xpinstall.properties +++ b/xpinstall/res/locale/en-US/xpinstall.properties @@ -102,6 +102,7 @@ error-230=Already exists error-235=Out of space error-239=Chrome registration failed error-240=Unfinished install +error-260=Signing could not be verified. error-299=Out of memory # there are other error codes, either rare or obsolete, diff --git a/xpinstall/src/CertReader.cpp b/xpinstall/src/CertReader.cpp new file mode 100644 index 000000000000..8cea98465bff --- /dev/null +++ b/xpinstall/src/CertReader.cpp @@ -0,0 +1,249 @@ +/* ***** BEGIN LICENSE BLOCK ***** ++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is XPInstall Signing. + * + * The Initial Developer of the Original Code is Doug Turner. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "zlib.h" +#include "zipstruct.h" + +#include "CertReader.h" + +#include "nsCRT.h" +#include "nsIServiceManager.h" +#include "nsISignatureVerifier.h" +#include "nsIInputStream.h" + +#include "nsNetUtil.h" + +// just a guess at the max size of the cert. +#define MAX_SIGNATURE_SIZE (32*1024) + + +/* + * x t o i n t + * + * Converts a two byte ugly endianed integer + * to our platform's integer. + * + */ + +static unsigned int xtoint (unsigned char *ii) +{ + return (int) (ii [0]) | ((int) ii [1] << 8); +} + +/* + * x t o l o n g + * + * Converts a four byte ugly endianed integer + * to our platform's integer. + * + */ + +static unsigned long xtolong (unsigned char *ll) +{ + unsigned long ret; + + ret = ((((unsigned long) ll [0]) << 0) | + (((unsigned long) ll [1]) << 8) | + (((unsigned long) ll [2]) << 16) | + (((unsigned long) ll [3]) << 24) ); + + return ret; +} + +static int my_inflate(unsigned char* compr, PRUint32 comprLen, unsigned char* uncompr, PRUint32 uncomprLen) +{ + int err; + z_stream d_stream; /* decompression stream */ + memset (&d_stream, 0, sizeof (d_stream)); + + // buffer is way to small to even deal with. + if (uncomprLen < 10) + return -1; + + *uncompr = '\0'; + + if (inflateInit2 (&d_stream, -MAX_WBITS) != Z_OK) + return -1; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + err = inflate(&d_stream, Z_NO_FLUSH); + + if (err != Z_OK && err != Z_STREAM_END) { + inflateEnd(&d_stream); + return -1; + } + + err = inflateEnd(&d_stream); + if (err != Z_OK) { + return -1; + } + return 0; +} + +CertReader::CertReader(nsIURI* aURI, nsISupports* aContext, nsPICertNotification* aObs) +{ + NS_INIT_ISUPPORTS(); + mObserver = aObs; + mContext = aContext; + mURI = aURI; +} + +CertReader::~CertReader() +{ +} + +NS_IMPL_ISUPPORTS2(CertReader, nsIStreamListener, nsIRequestObserver) + +NS_IMETHODIMP +CertReader::OnStartRequest(nsIRequest *request, nsISupports* context) +{ + mVerifier = do_GetService(SIGNATURE_VERIFIER_CONTRACTID); + if (!mVerifier) + return NS_BINDING_ABORTED; + + mLeftoverBuffer.Truncate(); + return NS_OK; +} + +NS_IMETHODIMP +CertReader::OnDataAvailable(nsIRequest *request, + nsISupports* context, + nsIInputStream *aIStream, + PRUint32 aSourceOffset, + PRUint32 aLength) +{ + if (!mVerifier) + return NS_BINDING_ABORTED; + + char buf[4096]; + PRUint32 amt, size; + nsresult rv; + + while (aLength) + { + size = PR_MIN(aLength, sizeof(buf)); + + rv = aIStream->Read(buf, size, &amt); + + if (NS_FAILED(rv)) + return rv; + + aLength -= amt; + + mLeftoverBuffer.Append(buf, amt); + + if (mLeftoverBuffer.Length() < ZIPLOCAL_SIZE) + continue; + + const char* caret = mLeftoverBuffer.get(); + const char* end = caret + mLeftoverBuffer.Length(); + + ZipLocal_* ziplocal = (ZipLocal_*) caret; + + if (xtolong(ziplocal->signature) != LOCALSIG) + return NS_BINDING_ABORTED; + + // did we read the entire file entry into memory? + PRUint32 fileEntryLen = (ZIPLOCAL_SIZE + + xtoint(ziplocal->filename_len) + + xtoint(ziplocal->extrafield_len) + + xtolong(ziplocal->size)); + + + // prevent downloading a huge file on an unsigned cert + if (fileEntryLen > MAX_SIGNATURE_SIZE) + return NS_BINDING_ABORTED; + + if (mLeftoverBuffer.Length() < fileEntryLen) + { + // we are just going to buffer and continue. + continue; + } + + // the assumption here is that we have the fileEntry in mLeftoverBuffer + + const char* data = (caret + + ZIPLOCAL_SIZE + + xtoint(ziplocal->filename_len) + + xtoint(ziplocal->extrafield_len)); + + PRUint32 orgSize = xtolong ((unsigned char *) ziplocal->orglen); + PRUint32 cSize = xtolong ((unsigned char *) ziplocal->size); + + if (orgSize == 0) + return NS_ERROR_FAILURE; + + unsigned char* orgData = (unsigned char*) malloc(orgSize); + + if (!orgData) + return NS_BINDING_ABORTED; + + int err = my_inflate((unsigned char*)data, + cSize, + orgData, + orgSize); + + if (err == 0) + { + PRInt32 verifyError; + rv = mVerifier->VerifySignature((char*)orgData, orgSize, nsnull, 0, + &verifyError, getter_AddRefs(mPrincipal)); + } + if (orgData) + free(orgData); + + return NS_BINDING_ABORTED; + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +CertReader::OnStopRequest(nsIRequest *request, nsISupports* context, + nsresult aStatus) +{ + mObserver->OnCertAvailable(mURI, + mContext, + aStatus, + mPrincipal); + + return NS_OK; +} + + diff --git a/xpinstall/src/CertReader.h b/xpinstall/src/CertReader.h new file mode 100644 index 000000000000..22e62a750a30 --- /dev/null +++ b/xpinstall/src/CertReader.h @@ -0,0 +1,63 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is XPInstall Signing. + * + * The Initial Developer of the Original Code is Doug Turner. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsCOMPtr.h" +#include "nsIStreamListener.h" +#include "nsString.h" +#include "nsISignatureVerifier.h" +#include "nsICertificatePrincipal.h" +#include "nsIPrincipal.h" +#include "nsIURI.h" +#include "nsPICertNotification.h" + +class CertReader : public nsIStreamListener +{ +public: + CertReader(nsIURI* uri, nsISupports* aContext, nsPICertNotification* aObs); + virtual ~CertReader(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + +private: + nsCString mLeftoverBuffer; + nsCOMPtr mPrincipal; + nsCOMPtr mVerifier; + + nsCOMPtr mContext; + nsCOMPtr mURI; + nsCOMPtr mObserver; +}; diff --git a/xpinstall/src/Makefile.in b/xpinstall/src/Makefile.in index 46bbf0bea476..3582efa98ed0 100644 --- a/xpinstall/src/Makefile.in +++ b/xpinstall/src/Makefile.in @@ -44,6 +44,7 @@ MODULE_NAME = nsSoftwareUpdate REQUIRES = xpcom \ string \ jar \ + caps \ chrome \ necko \ intl \ @@ -66,6 +67,7 @@ REQUIRES = xpcom \ EXPORTS = nsXPITriggerInfo.h CPPSRCS = \ + CertReader.cpp \ nsInstall.cpp \ nsInstallTrigger.cpp \ nsInstallVersion.cpp \ diff --git a/xpinstall/src/nsInstall.cpp b/xpinstall/src/nsInstall.cpp index 093d3616d00f..154019addc7c 100644 --- a/xpinstall/src/nsInstall.cpp +++ b/xpinstall/src/nsInstall.cpp @@ -143,6 +143,7 @@ nsInstallInfo::nsInstallInfo(PRUint32 aInstallType, nsIFile* aFile, const PRUnichar* aURL, const PRUnichar* aArgs, + nsIPrincipal* aPrincipal, PRUint32 flags, nsIXPIListener* aListener, nsIXULChromeRegistry* aChromeRegistry) @@ -151,6 +152,7 @@ nsInstallInfo::nsInstallInfo(PRUint32 aInstallType, mFlags(flags), mURL(aURL), mArgs(aArgs), + mPrincipal(aPrincipal), mFile(aFile), mListener(aListener), mChromeRegistry(aChromeRegistry) diff --git a/xpinstall/src/nsInstall.h b/xpinstall/src/nsInstall.h index a4919e6a6415..b3fe6a6b5e07 100644 --- a/xpinstall/src/nsInstall.h +++ b/xpinstall/src/nsInstall.h @@ -58,6 +58,7 @@ #include "nsIEnumerator.h" #include "nsIZipReader.h" #include "nsIChromeRegistry.h" +#include "nsIPrincipal.h" #define XPINSTALL_BUNDLE_URL "chrome://communicator/locale/xpinstall/xpinstall.properties" @@ -78,6 +79,7 @@ class nsInstallInfo nsIFile* aFile, const PRUnichar* aURL, const PRUnichar* aArgs, + nsIPrincipal* mPrincipal, PRUint32 aFlags, nsIXPIListener* aListener, nsIXULChromeRegistry* aChromeReg); @@ -92,6 +94,8 @@ class nsInstallInfo nsIXPIListener* GetListener() { return mListener.get(); } nsIXULChromeRegistry* GetChromeRegistry() { return mChromeRegistry.get(); } + nsCOMPtr mPrincipal; + private: nsresult mError; @@ -180,6 +184,8 @@ class nsInstall KEY_DOES_NOT_EXIST = -242, VALUE_DOES_NOT_EXIST = -243, + INVALID_SIGNATURE = -260, + OUT_OF_MEMORY = -299, GESTALT_UNKNOWN_ERR = -5550, diff --git a/xpinstall/src/nsJSInstall.cpp b/xpinstall/src/nsJSInstall.cpp index c7de4f1a514d..a301766bac4e 100644 --- a/xpinstall/src/nsJSInstall.cpp +++ b/xpinstall/src/nsJSInstall.cpp @@ -1788,6 +1788,7 @@ static JSConstDoubleSpec install_constants[] = { nsInstall::SUCCESS, "SUCCESS" }, { nsInstall::REBOOT_NEEDED, "REBOOT_NEEDED" }, + { nsInstall::INVALID_SIGNATURE, "INVALID_SIGNATURE" }, // these are bitwise values supported by addFile { DO_NOT_UNINSTALL, "DO_NOT_UNINSTALL" }, diff --git a/xpinstall/src/nsSoftwareUpdate.cpp b/xpinstall/src/nsSoftwareUpdate.cpp index 19f261ca6842..aca367f6174d 100644 --- a/xpinstall/src/nsSoftwareUpdate.cpp +++ b/xpinstall/src/nsSoftwareUpdate.cpp @@ -299,6 +299,7 @@ NS_IMETHODIMP nsSoftwareUpdate::InstallJar( nsIFile* aLocalFile, const PRUnichar* aURL, const PRUnichar* aArguments, + nsIPrincipal* aPrincipal, PRUint32 flags, nsIXPIListener* aListener) { @@ -316,7 +317,7 @@ nsSoftwareUpdate::InstallJar( nsIFile* aLocalFile, chromeRegistry = tmpReg; // we want to call this with or without a chrome registry - nsInstallInfo *info = new nsInstallInfo( 0, aLocalFile, aURL, aArguments, + nsInstallInfo *info = new nsInstallInfo( 0, aLocalFile, aURL, aArguments, aPrincipal, flags, aListener, chromeRegistry ); if (!info) @@ -351,6 +352,7 @@ nsSoftwareUpdate::InstallChrome( PRUint32 aType, aFile, URL, aName, + nsnull, (PRUint32)aSelect, aListener, chromeRegistry); diff --git a/xpinstall/src/nsSoftwareUpdate.h b/xpinstall/src/nsSoftwareUpdate.h index b4988aadcae4..a10d39e42a2a 100644 --- a/xpinstall/src/nsSoftwareUpdate.h +++ b/xpinstall/src/nsSoftwareUpdate.h @@ -16,13 +16,13 @@ #include "nsCOMPtr.h" class nsInstallInfo; +class nsIPrincipal; #include "nsIScriptExternalNameSet.h" #include "nsIObserver.h" #include "nsPIXPIStubHook.h" #include "nsTopProgressNotifier.h" - class nsSoftwareUpdate: public nsISoftwareUpdate, public nsPIXPIStubHook, public nsIObserver @@ -53,6 +53,7 @@ class nsSoftwareUpdate: public nsISoftwareUpdate, NS_IMETHOD InstallJar( nsIFile* localFile, const PRUnichar* URL, const PRUnichar* arguments, + nsIPrincipal* principal = nsnull, PRUint32 flags = 0, nsIXPIListener* aListener = 0); diff --git a/xpinstall/src/nsSoftwareUpdateRun.cpp b/xpinstall/src/nsSoftwareUpdateRun.cpp index e8f652642069..64c04d2f87b8 100644 --- a/xpinstall/src/nsSoftwareUpdateRun.cpp +++ b/xpinstall/src/nsSoftwareUpdateRun.cpp @@ -55,21 +55,94 @@ #include "nsIConsoleService.h" #include "nsIScriptError.h" +#include "nsIJAR.h" +#include "nsIPrincipal.h" +#include "nsICertificatePrincipal.h" + static NS_DEFINE_CID(kSoftwareUpdateCID, NS_SoftwareUpdate_CID); static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); -extern JSObject *InitXPInstallObjects(JSContext *jscontext, JSObject *global, nsIFile* jarfile, const PRUnichar* url, const PRUnichar* args, PRUint32 flags, nsIXULChromeRegistry* registry, nsIZipReader* hZip); +extern JSObject *InitXPInstallObjects(JSContext *jscontext, JSObject *global, + nsIFile* jarfile, const PRUnichar* url, + const PRUnichar* args, PRUint32 flags, + nsIXULChromeRegistry* registry, + nsIZipReader* hZip); extern nsresult InitInstallVersionClass(JSContext *jscontext, JSObject *global, void** prototype); extern nsresult InitInstallTriggerGlobalClass(JSContext *jscontext, JSObject *global, void** prototype); // Defined in this file: PR_STATIC_CALLBACK(void) XPInstallErrorReporter(JSContext *cx, const char *message, JSErrorReport *report); -static PRInt32 GetInstallScriptFromJarfile(nsIZipReader* hZip, nsIFile* jarFile, char** scriptBuffer, PRUint32 *scriptLength); -static nsresult SetupInstallContext(nsIZipReader* hZip, nsIFile* jarFile, const PRUnichar* url, const PRUnichar* args, PRUint32 flags, nsIXULChromeRegistry* reg, JSRuntime *jsRT, JSContext **jsCX, JSObject **jsGlob); +static PRInt32 GetInstallScriptFromJarfile(nsIZipReader* hZip, nsIFile* jarFile, nsIPrincipal* aPrincipal, char** scriptBuffer, PRUint32 *scriptLength); +static nsresult SetupInstallContext(nsIZipReader* hZip, nsIFile* jarFile, const PRUnichar* url, const PRUnichar* args, + PRUint32 flags, nsIXULChromeRegistry* reg, JSRuntime *jsRT, JSContext **jsCX, JSObject **jsGlob); extern "C" void RunInstallOnThread(void *data); +nsresult VerifySigning(nsIZipReader* hZip, nsIPrincipal* aPrincipal) +{ + if (!aPrincipal) + return NS_OK; // not signed, but not an error + + nsCOMPtr cp(do_QueryInterface(aPrincipal)); + if (!cp) + return NS_ERROR_FAILURE; + + nsCOMPtr jar(do_QueryInterface(hZip)); + if (!jar) + return NS_ERROR_FAILURE; + + // See if the archive is signed at all first + nsCOMPtr principal; + nsresult rv = jar->GetCertificatePrincipal(nsnull, getter_AddRefs(principal)); + if (NS_FAILED(rv) || !principal) + return NS_ERROR_FAILURE; + + PRUint32 entryCount = 0; + + // first verify all files in the jar are also in the manifest. + nsCOMPtr entries; + rv = hZip->FindEntries("*", getter_AddRefs(entries)); + if (NS_FAILED(rv)) + return rv; + + PRBool more; + nsXPIDLCString name; + while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) + { + nsCOMPtr file; + rv = entries->GetNext(getter_AddRefs(file)); + if (NS_FAILED(rv)) return rv; + + file->GetName(getter_Copies(name)); + + if ( PL_strncasecmp("META-INF/", name.get(), 9) == 0) + continue; + + // we only count the entries not in the meta-inf directory + entryCount++; + + // Each entry must be signed + PRBool equal; + rv = jar->GetCertificatePrincipal(name, getter_AddRefs(principal)); + if (NS_FAILED(rv) || !principal) return NS_ERROR_FAILURE; + + rv = principal->Equals(aPrincipal, &equal); + if (NS_FAILED(rv) || !equal) return NS_ERROR_FAILURE; + } + + // next verify all files in the manifest are in the archive. + PRUint32 manifestEntryCount; + rv = jar->GetManifestEntriesCount(&manifestEntryCount); + if (NS_FAILED(rv)) + return rv; + + if (entryCount != manifestEntryCount) + return NS_ERROR_FAILURE; // some files were deleted from archive + + return NS_OK; +} + /////////////////////////////////////////////////////////////////////////////////////////////// // Function name : XPInstallErrorReporter // Description : Prints error message to stdout @@ -127,7 +200,6 @@ XPInstallErrorReporter(JSContext *cx, const char *message, JSErrorReport *report do_GetService(kSoftwareUpdateCID, &rv); if (NS_FAILED(rv)) - { NS_WARNING("shouldn't have RunInstall() if we can't get SoftwareUpdate"); return; @@ -159,12 +231,14 @@ XPInstallErrorReporter(JSContext *cx, const char *message, JSErrorReport *report // Description : Extracts and reads in a install.js file from a passed jar file. // Return type : static PRInt32 // Argument : const char* jarFile - **NSPR** filepath +// Argument : nsIPrincipal* aPrincipal - a principal, if any, displayed to the user +// regarding the cert used to sign this install // Argument : char** scriptBuffer - must be deleted via delete [] // Argument : PRUint32 *scriptLength /////////////////////////////////////////////////////////////////////////////////////////////// static PRInt32 -GetInstallScriptFromJarfile(nsIZipReader* hZip, nsIFile* jarFile, char** scriptBuffer, PRUint32 *scriptLength) +GetInstallScriptFromJarfile(nsIZipReader* hZip, nsIFile* jarFile, nsIPrincipal* aPrincipal, char** scriptBuffer, PRUint32 *scriptLength) { PRInt32 result = NS_OK; @@ -192,6 +266,13 @@ GetInstallScriptFromJarfile(nsIZipReader* hZip, nsIFile* jarFile, char** scriptB return nsInstall::CANT_READ_ARCHIVE; } + rv = VerifySigning(hZip, aPrincipal); + if (NS_FAILED(rv)) + { + NS_ASSERTION(0, "Signing check of archive failed!"); + return nsInstall::INVALID_SIGNATURE; + } + // Extract the install.js file to the temporary directory nsSpecialSystemDirectory installJSFileSpec(nsSpecialSystemDirectory::OS_TemporaryDirectory); installJSFileSpec += "install.js"; @@ -200,6 +281,7 @@ GetInstallScriptFromJarfile(nsIZipReader* hZip, nsIFile* jarFile, char** scriptB // Extract the install.js file. nsCOMPtr iFile; rv = NS_NewNativeLocalFile(nsDependentCString(installJSFileSpec), PR_TRUE, getter_AddRefs(iFile)); + if (NS_SUCCEEDED(rv)) rv = hZip->Extract("install.js", iFile); if ( NS_SUCCEEDED(rv) ) @@ -393,10 +475,12 @@ extern "C" void RunInstallOnThread(void *data) listener->OnInstallStart( installInfo->GetURL() ); nsCOMPtr jarpath = installInfo->GetFile(); + if (NS_SUCCEEDED(rv)) { finalStatus = GetInstallScriptFromJarfile( hZip, jarpath, + installInfo->mPrincipal, &scriptBuffer, &scriptLength); @@ -587,4 +671,3 @@ extern "C" void RunChromeInstallOnThread(void *data) delete info; } - diff --git a/xpinstall/src/nsXPITriggerInfo.cpp b/xpinstall/src/nsXPITriggerInfo.cpp index afd3e36c7cf5..d607731cea8a 100644 --- a/xpinstall/src/nsXPITriggerInfo.cpp +++ b/xpinstall/src/nsXPITriggerInfo.cpp @@ -27,6 +27,7 @@ #include "nsDebug.h" #include "nsIServiceManager.h" #include "nsIEventQueueService.h" +#include "nsICertificatePrincipal.h" static NS_DEFINE_IID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); @@ -38,7 +39,7 @@ MOZ_DECL_CTOR_COUNTER(nsXPITriggerItem) nsXPITriggerItem::nsXPITriggerItem( const PRUnichar* aName, const PRUnichar* aURL, - PRInt32 aFlags ) + PRInt32 aFlags) : mName(aName), mURL(aURL), mFlags(aFlags) { MOZ_COUNT_CTOR(nsXPITriggerItem); @@ -89,6 +90,18 @@ PRBool nsXPITriggerItem::IsRelativeURL() } +void +nsXPITriggerItem::SetPrincipal(nsIPrincipal* aPrincipal) +{ + mPrincipal = aPrincipal; + + nsCOMPtr cp(do_QueryInterface(aPrincipal)); + if (cp) { + nsXPIDLCString cName; + cp->GetCommonName(getter_Copies(cName)); + mCertName = NS_ConvertUTF8toUCS2(cName); + } +} // // nsXPITriggerInfo // diff --git a/xpinstall/src/nsXPITriggerInfo.h b/xpinstall/src/nsXPITriggerInfo.h index 7e9d6fe938a0..d9157c62697e 100644 --- a/xpinstall/src/nsXPITriggerInfo.h +++ b/xpinstall/src/nsXPITriggerInfo.h @@ -38,7 +38,7 @@ #include "plevent.h" #include "nsIXPConnect.h" - +#include "nsIPrincipal.h" typedef struct XPITriggerEvent { PLEvent e; @@ -61,14 +61,18 @@ class nsXPITriggerItem nsString mName; nsString mURL; nsString mArguments; + nsString mCertName; PRInt32 mFlags; nsCOMPtr mFile; nsCOMPtr mOutStream; + nsCOMPtr mPrincipal; + PRBool IsFileURL() { return Substring(mURL, 0, 6).Equals(NS_LITERAL_STRING("file:/")); } PRBool IsRelativeURL(); + void SetPrincipal(nsIPrincipal* aPrincipal); private: //-- prevent inadvertent copies and assignments nsXPITriggerItem& operator=(const nsXPITriggerItem& rhs); diff --git a/xpinstall/src/nsXPInstallManager.cpp b/xpinstall/src/nsXPInstallManager.cpp index b1562a550dd1..a48ba02a4303 100644 --- a/xpinstall/src/nsXPInstallManager.cpp +++ b/xpinstall/src/nsXPInstallManager.cpp @@ -65,6 +65,8 @@ #include "nsXPCOM.h" #include "nsISupportsPrimitives.h" +#include "CertReader.h" + static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_IID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID); static NS_DEFINE_IID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); @@ -134,7 +136,6 @@ nsXPInstallManager::InitManager(nsIScriptGlobalObject* aGlobalObject, nsXPITrigg { nsresult rv = NS_OK; - PRBool OKtoInstall = PR_FALSE; // initialize to secure state mTriggers = aTriggers; mChromeType = aChromeType; mNeedsShutdown = PR_TRUE; @@ -146,14 +147,36 @@ nsXPInstallManager::InitManager(nsIScriptGlobalObject* aGlobalObject, nsXPITrigg return rv; } + mParentWindow = do_QueryInterface(aGlobalObject); + mOutstandingCertLoads = mTriggers->Size(); + + nsXPITriggerItem *item = mTriggers->Get(--mOutstandingCertLoads); + + nsCOMPtr uri; + NS_NewURI(getter_AddRefs(uri), NS_ConvertUCS2toUTF8(item->mURL.get()).get()); + nsIStreamListener* listener = new CertReader(uri, nsnull, this); + NS_ADDREF(listener); + rv = NS_OpenURI(listener, nsnull, uri); + NS_RELEASE(listener); + if (NS_FAILED(rv)) { + NS_RELEASE_THIS(); + } + return rv; +} + + +nsresult +nsXPInstallManager::InitManagerInternal() +{ + nsresult rv; + PRBool OKtoInstall = PR_FALSE; // initialize to secure state + //----------------------------------------------------- // *** Do not return early after this point *** // // We have to clean up the triggers in case of error //----------------------------------------------------- - nsCOMPtr parentWindow(do_QueryInterface(aGlobalObject)); - // --- use embedding dialogs if any registered nsCOMPtr dlgSvc(do_CreateInstance(NS_XPIDIALOGSERVICE_CONTRACTID)); if ( !dlgSvc ) @@ -164,7 +187,7 @@ nsXPInstallManager::InitManager(nsIScriptGlobalObject* aGlobalObject, nsXPITrigg // --- prepare dialog params PRUint32 numTriggers = mTriggers->Size(); - PRUint32 numStrings = 2 * numTriggers; + PRUint32 numStrings = 3 * numTriggers; const PRUnichar** packageList = (const PRUnichar**)malloc( sizeof(PRUnichar*) * numStrings ); @@ -176,6 +199,7 @@ nsXPInstallManager::InitManager(nsIScriptGlobalObject* aGlobalObject, nsXPITrigg nsXPITriggerItem *item = mTriggers->Get(i); packageList[j++] = item->mName.get(); packageList[j++] = item->mURL.get(); + packageList[j++] = item->mCertName.get(); } //----------------------------------------------------- @@ -186,11 +210,11 @@ nsXPInstallManager::InitManager(nsIScriptGlobalObject* aGlobalObject, nsXPITrigg { // skins get a simpler/friendlier dialog // XXX currently not embeddable - OKtoInstall = ConfirmChromeInstall( parentWindow, packageList ); + OKtoInstall = ConfirmChromeInstall( mParentWindow, packageList ); } else { - rv = dlgSvc->ConfirmInstall( parentWindow, + rv = dlgSvc->ConfirmInstall( mParentWindow, packageList, numStrings, &OKtoInstall ); @@ -585,6 +609,7 @@ NS_IMETHODIMP nsXPInstallManager::DownloadNext() rv = mInstallSvc->InstallJar( mItem->mFile, mItem->mURL.get(), mItem->mArguments.get(), + mItem->mPrincipal, mItem->mFlags, this ); } @@ -1014,3 +1039,56 @@ nsXPInstallManager::OnLogComment(const PRUnichar* comment) return NS_OK; } + +NS_IMETHODIMP +nsXPInstallManager::OnCertAvailable(nsIURI *aURI, + nsISupports* context, + nsresult aStatus, + nsIPrincipal *aPrincipal) +{ + if (NS_FAILED(aStatus)) { + // if there was a failure for whatever reason, we will treat + // the install as unsigned. An error here could me that the + // location of the install is unreachable or that the install + // is currupt. In either case, we want to ensure that the + // nsIPrincipal is nsnull (although it already should be + // -- we are just being paranoid here. + NS_ASSERTION(aPrincipal == nsnull, "There has been an error, but we have a principal!"); + aPrincipal = nsnull; + } + + // get the current one and assign the cert name + nsXPITriggerItem *item = mTriggers->Get(mOutstandingCertLoads); + item->SetPrincipal(aPrincipal); + + if (mOutstandingCertLoads == 0) { + InitManagerInternal(); + return NS_OK; + } + + // get the next one to load. If there is any failure, we just go on to the + // next trigger. When all triggers items are handled, we call into InitManagerInternal + + item = mTriggers->Get(--mOutstandingCertLoads); + + nsCOMPtr uri; + NS_NewURI(getter_AddRefs(uri), NS_ConvertUCS2toUTF8(item->mURL.get()).get()); + + if (!uri || mChromeType != NOT_CHROME) + return OnCertAvailable(uri, context, NS_ERROR_FAILURE, nsnull); + + nsIStreamListener* listener = new CertReader(uri, nsnull, this); + if (!listener) + return OnCertAvailable(uri, context, NS_ERROR_FAILURE, nsnull); + + NS_ADDREF(listener); + nsresult rv = NS_OpenURI(listener, nsnull, uri); + NS_ASSERTION(NS_SUCCEEDED(rv), "OpenURI failed"); + NS_RELEASE(listener); + + if (NS_FAILED(rv)) + return OnCertAvailable(uri, context, NS_ERROR_FAILURE, nsnull); + + return NS_OK; +} + diff --git a/xpinstall/src/nsXPInstallManager.h b/xpinstall/src/nsXPInstallManager.h index cd8144d68ffb..80c76c2dc89b 100644 --- a/xpinstall/src/nsXPInstallManager.h +++ b/xpinstall/src/nsXPInstallManager.h @@ -52,6 +52,8 @@ #include "nsIDialogParamBlock.h" +#include "nsPICertNotification.h" + #define NS_XPIDIALOGSERVICE_CONTRACTID "@mozilla.org/embedui/xpinstall-dialog-service;1" #define XPI_PROGRESS_TOPIC "xpinstall-progress" @@ -60,7 +62,8 @@ class nsXPInstallManager : public nsIXPIListener, public nsIObserver, public nsIStreamListener, public nsIProgressEventSink, - public nsIInterfaceRequestor + public nsIInterfaceRequestor, + public nsPICertNotification { public: nsXPInstallManager(); @@ -74,10 +77,12 @@ class nsXPInstallManager : public nsIXPIListener, NS_DECL_NSIPROGRESSEVENTSINK NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSPICERTNOTIFICATION NS_IMETHOD InitManager(nsIScriptGlobalObject* aGlobalObject, nsXPITriggerInfo* aTrigger, PRUint32 aChromeType ); private: + nsresult InitManagerInternal(); NS_IMETHOD DownloadNext(); void Shutdown(); NS_IMETHOD GetDestinationFile(nsString& url, nsILocalFile* *file); @@ -93,14 +98,17 @@ class nsXPInstallManager : public nsIXPIListener, PRInt32 mNumJars; PRUint32 mChromeType; PRInt32 mContentLength; + PRInt32 mOutstandingCertLoads; PRBool mDialogOpen; PRBool mCancelled; PRBool mSelectChrome; PRBool mNeedsShutdown; - + nsCOMPtr mDlg; nsCOMPtr mStringBundle; nsCOMPtr mInstallSvc; + + nsCOMPtr mParentWindow; }; #endif diff --git a/xpinstall/stub/xpistub.cpp b/xpinstall/stub/xpistub.cpp index 5d4377f0bf99..1b724a2a34e4 100644 --- a/xpinstall/stub/xpistub.cpp +++ b/xpinstall/stub/xpistub.cpp @@ -270,6 +270,7 @@ PR_PUBLIC_API(PRInt32) XPI_Install( rv = gXPI->InstallJar( iFile, URLstr.get(), args.get(), + nsnull, (aFlags | XPI_NO_NEW_THREAD), gListener );