Implementing XPInstall Signing. sr=dveditz, r=mstoltz, b=178687

This commit is contained in:
dougt%netscape.com 2002-12-17 00:59:42 +00:00
parent a1d3c960e5
commit 59a520b858
24 changed files with 671 additions and 26 deletions

View File

@ -1169,6 +1169,13 @@
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>CertReader.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
</FILELIST>
<LINKORDER>
<FILEREF>
@ -1356,6 +1363,11 @@
<PATH>nsXPIProxy.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>CertReader.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
</LINKORDER>
</TARGET>
<TARGET>
@ -2474,6 +2486,13 @@
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>CertReader.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
<FILEKIND>Text</FILEKIND>
<FILEFLAGS>Debug</FILEFLAGS>
</FILE>
</FILELIST>
<LINKORDER>
<FILEREF>
@ -2661,6 +2680,11 @@
<PATH>nsXPIProxy.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>CertReader.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
</LINKORDER>
</TARGET>
</TARGETLIST>
@ -2840,6 +2864,12 @@
<PATH>nsXPIProxy.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<TARGETNAME>xpinstallDebug.shlb</TARGETNAME>
<PATHTYPE>Name</PATHTYPE>
<PATH>CertReader.cpp</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
</GROUP>
<GROUP><NAME>NS Libraries</NAME>
<GROUP><NAME>Optimized</NAME>

View File

@ -731,6 +731,13 @@
<FILEKIND>Text</FILEKIND>
<FILEFLAGS></FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsPICertNotification.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
<FILEKIND>Text</FILEKIND>
<FILEFLAGS></FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIXPIDialogService.idl</PATH>
@ -762,6 +769,11 @@
<PATH>nsPIXPIStubHook.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsPICertNotification.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIXPIDialogService.idl</PATH>
@ -1452,6 +1464,13 @@
<FILEKIND>Text</FILEKIND>
<FILEFLAGS></FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsPICertNotification.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
<FILEKIND>Text</FILEKIND>
<FILEFLAGS></FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIXPIDialogService.idl</PATH>
@ -1483,6 +1502,11 @@
<PATH>nsPIXPIStubHook.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsPICertNotification.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIXPIDialogService.idl</PATH>
@ -1521,6 +1545,12 @@
<PATH>nsPIXPIStubHook.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<TARGETNAME>headers</TARGETNAME>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsPICertNotification.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<TARGETNAME>headers</TARGETNAME>
<PATHTYPE>Name</PATHTYPE>

View File

@ -37,6 +37,7 @@ XPIDLSRCS = \
nsIXPINotifier.idl \
nsPIXPIProxy.idl \
nsPIXPIStubHook.idl \
nsPICertNotification.idl \
$(NULL)
EXPORTS = \

View File

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

View File

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

View File

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

View File

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

View File

@ -54,6 +54,7 @@ Contributor(s):
<grid id="confirmGrid" flex="1">
<columns>
<column id="xpiColumn"/>
<column id="certColumn"/>
<column id="urlColumn" flex="1"/>
</columns>
<rows id="xpirows">

View File

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

View File

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

View File

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

View File

@ -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<nsIPrincipal> mPrincipal;
nsCOMPtr<nsISignatureVerifier> mVerifier;
nsCOMPtr<nsISupports> mContext;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsPICertNotification> mObserver;
};

View File

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

View File

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

View File

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

View File

@ -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" },

View File

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

View File

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

View File

@ -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<nsICertificatePrincipal> cp(do_QueryInterface(aPrincipal));
if (!cp)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIJAR> jar(do_QueryInterface(hZip));
if (!jar)
return NS_ERROR_FAILURE;
// See if the archive is signed at all first
nsCOMPtr<nsIPrincipal> 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<nsISimpleEnumerator> 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<nsIZipEntry> 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<nsILocalFile> 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<nsIFile> 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;
}

View File

@ -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<nsICertificatePrincipal> cp(do_QueryInterface(aPrincipal));
if (cp) {
nsXPIDLCString cName;
cp->GetCommonName(getter_Copies(cName));
mCertName = NS_ConvertUTF8toUCS2(cName);
}
}
//
// nsXPITriggerInfo
//

View File

@ -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<nsILocalFile> mFile;
nsCOMPtr<nsIOutputStream> mOutStream;
nsCOMPtr<nsIPrincipal> 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);

View File

@ -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<nsIURI> 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<nsIDOMWindowInternal> parentWindow(do_QueryInterface(aGlobalObject));
// --- use embedding dialogs if any registered
nsCOMPtr<nsIXPIDialogService> 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<nsIURI> 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;
}

View File

@ -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<nsIXPIProgressDialog> mDlg;
nsCOMPtr<nsIStringBundle> mStringBundle;
nsCOMPtr<nsISoftwareUpdate> mInstallSvc;
nsCOMPtr<nsIDOMWindowInternal> mParentWindow;
};
#endif

View File

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