Back to index

lightning-sunbird  0.9+nobinonly
nsInstallTrigger.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla Communicator client code, released
00015  * March 31, 1998.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 
00039 #include "nsSoftwareUpdate.h"
00040 #include "nsXPInstallManager.h"
00041 #include "nsInstallTrigger.h"
00042 #include "nsInstallVersion.h"
00043 #include "nsIDOMInstallTriggerGlobal.h"
00044 
00045 #include "nscore.h"
00046 #include "netCore.h"
00047 #include "nsIFactory.h"
00048 #include "nsISupports.h"
00049 #include "nsIScriptGlobalObject.h"
00050 #include "nsIScriptGlobalObjectOwner.h"
00051 
00052 #include "nsIPrefBranch.h"
00053 #include "nsIPrefService.h"
00054 #include "nsIPermissionManager.h"
00055 #include "nsIDocShell.h"
00056 #include "nsNetUtil.h"
00057 #include "nsIDOMDocument.h"
00058 #include "nsIDocument.h"
00059 #include "nsIPrincipal.h"
00060 #include "nsIObserverService.h"
00061 #include "nsIPropertyBag2.h"
00062 
00063 #include "nsIComponentManager.h"
00064 #include "nsIServiceManager.h"
00065 
00066 #include "VerReg.h"
00067 
00068 #include "nsIContentHandler.h"
00069 #include "nsIChannel.h"
00070 #include "nsIURI.h"
00071 
00072 
00073 
00074 nsInstallTrigger::nsInstallTrigger()
00075 {
00076     mScriptObject   = nsnull;
00077 
00078     // make sure all the SoftwareUpdate initialization has happened
00079     nsCOMPtr<nsISoftwareUpdate> svc (do_GetService(NS_IXPINSTALLCOMPONENT_CONTRACTID));
00080 }
00081 
00082 nsInstallTrigger::~nsInstallTrigger()
00083 {
00084 }
00085 
00086 
00087 NS_IMPL_THREADSAFE_ISUPPORTS3 (nsInstallTrigger,
00088                               nsIScriptObjectOwner,
00089                               nsIDOMInstallTriggerGlobal,
00090                               nsIContentHandler)
00091 
00092 
00093 NS_IMETHODIMP
00094 nsInstallTrigger::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject)
00095 {
00096     NS_PRECONDITION(nsnull != aScriptObject, "null arg");
00097     nsresult res = NS_OK;
00098 
00099     if (nsnull == mScriptObject)
00100     {
00101         res = NS_NewScriptInstallTriggerGlobal(aContext,
00102                                                (nsIDOMInstallTriggerGlobal*)this,
00103                                                aContext->GetGlobalObject(),
00104                                                &mScriptObject);
00105     }
00106 
00107     *aScriptObject = mScriptObject;
00108     return res;
00109 }
00110 
00111 NS_IMETHODIMP
00112 nsInstallTrigger::SetScriptObject(void *aScriptObject)
00113 {
00114   mScriptObject = aScriptObject;
00115   return NS_OK;
00116 }
00117 
00118 
00119 
00120 
00121 NS_IMETHODIMP
00122 nsInstallTrigger::HandleContent(const char * aContentType,
00123                                 nsIInterfaceRequestor* aWindowContext,
00124                                 nsIRequest* aRequest)
00125 {
00126     nsresult rv = NS_OK;
00127     if (!aRequest)
00128         return NS_ERROR_NULL_POINTER;
00129 
00130     if (nsCRT::strcasecmp(aContentType, "application/x-xpinstall") != 0)
00131     {
00132         // We only support content-type application/x-xpinstall
00133         return NS_ERROR_WONT_HANDLE_CONTENT;
00134     }
00135 
00136     // Save the URI so nsXPInstallManager can re-load it later
00137     nsCOMPtr<nsIURI> uri;
00138     nsCAutoString    urispec;
00139     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
00140     if (channel)
00141     {
00142         rv = channel->GetURI(getter_AddRefs(uri));
00143         if (NS_SUCCEEDED(rv) && uri)
00144             rv = uri->GetSpec(urispec);
00145     }
00146     if (NS_FAILED(rv))
00147         return rv;
00148     if (urispec.IsEmpty())
00149         return NS_ERROR_ILLEGAL_VALUE;
00150 
00151 
00152     // Save the referrer if any, for permission checks
00153     NS_NAMED_LITERAL_STRING(referrerProperty, "docshell.internalReferrer");
00154     PRBool useReferrer = PR_FALSE;
00155     nsCOMPtr<nsIURI> referringURI;
00156     nsCOMPtr<nsIPropertyBag2> channelprops(do_QueryInterface(channel));
00157 
00158     if (channelprops)
00159     {
00160         // Get the referrer from the channel properties if we can (not all
00161         // channels support our internal-referrer property).
00162         //
00163         // It's possible docshell explicitly set a null referrer in the case
00164         // of typed, pasted, or bookmarked URLs and the like. In such a case
00165         // we get a success return value with null pointer.
00166         //
00167         // A null referrer is automatically whitelisted as an explicit user
00168         // action (though they'll still get the confirmation dialog). For a
00169         // missing referrer we go to our fall-back plan of using the XPI
00170         // location for whitelisting purposes.
00171         rv = channelprops->GetPropertyAsInterface(referrerProperty,
00172                                                   NS_GET_IID(nsIURI),
00173                                                   getter_AddRefs(referringURI));
00174         if (NS_SUCCEEDED(rv))
00175             useReferrer = PR_TRUE;
00176     }
00177 
00178     // Cancel the current request. nsXPInstallManager restarts the download
00179     // under its control (shared codepath with InstallTrigger)
00180     aRequest->Cancel(NS_BINDING_ABORTED);
00181 
00182 
00183     // Get the global object of the target window for StartSoftwareUpdate
00184     nsIScriptGlobalObject* globalObject;
00185     nsCOMPtr<nsIScriptGlobalObjectOwner> globalObjectOwner = 
00186                                          do_QueryInterface(aWindowContext);
00187     if ( globalObjectOwner )
00188     {
00189         globalObject = globalObjectOwner->GetScriptGlobalObject();
00190     }
00191     if ( !globalObject )
00192         return NS_ERROR_INVALID_ARG;
00193 
00194 
00195     // We have what we need to start an XPInstall, now figure out if we are
00196     // going to honor this request based on PermissionManager settings
00197     PRBool enabled = PR_FALSE;
00198 
00199     if ( useReferrer )
00200     {
00201         // easiest and most common case: base decision on the page that
00202         // contained the link
00203         //
00204         // NOTE: the XPI itself may be from elsewhere; the user can decide if
00205         // they trust the actual source when they get the install confirmation
00206         // dialog. The decision we're making here is whether the triggering
00207         // site is one which is allowed to annoy the user with modal dialogs.
00208 
00209         enabled = AllowInstall( referringURI );
00210     }
00211     else
00212     {
00213         // Now we're stumbing in the dark. In the most likely case the user
00214         // simply clicked on an FTP link (no referrer) and it's perfectly
00215         // sane to use the current window.
00216         //
00217         // On the other hand the user might be opening a non-http XPI link
00218         // in an unrelated existing window (typed in location bar, bookmark,
00219         // dragged link ...) in which case the current window is irrelevant.
00220         // If we knew it was one of these explicit user actions we'd like to
00221         // allow it, but we have no way of knowing that here.
00222         //
00223         // But there's no way to distinguish the innocent cases from a clever
00224         // malicious site. If we used the target window then evil.com could
00225         // embed a presumed allowed site (e.g. mozilla.org) in a frame, then
00226         // change the location to the XPI and trigger the install. Or evil.com
00227         // could do the same thing in a new window (more work to get around
00228         // popup blocking, but possible).
00229         //
00230         // Our choices appear to be block this type of load entirely or to
00231         // trust only the install URI. The former is unacceptably restrictive,
00232         // the latter allows malicious sites to pester people with modal
00233         // dialogs. As long as the trusted sites don't host bad content that's
00234         // no worse than an endless stream of alert()s -- already possible.
00235         // If the trusted sites don't even have an ftp server then even this
00236         // level of annoyance is not possible.
00237         //
00238         // If a trusted site hosts an install with an exploitable flaw it
00239         // might be possible that a malicious site would attempt to trick
00240         // people into installing it, hoping to turn around and exploit it.
00241         // This is not entirely far-fetched (it's been done with ActiveX
00242         // controls) and will require community policing of the default
00243         // trusted sites.
00244 
00245         enabled = AllowInstall( uri );
00246     }
00247 
00248 
00249     if ( enabled )
00250     {
00251         rv = StartSoftwareUpdate( globalObject,
00252                                   NS_ConvertUTF8toUTF16(urispec),
00253                                   0,
00254                                   &enabled);
00255     }
00256     else
00257     {
00258         nsCOMPtr<nsIObserverService> os(do_GetService("@mozilla.org/observer-service;1"));
00259         if (os) {
00260             os->NotifyObservers(globalObject->GetDocShell(), 
00261                                 "xpinstall-install-blocked", 
00262                                 NS_LITERAL_STRING("install-chrome").get());
00263         }
00264         rv = NS_ERROR_ABORT;
00265     }
00266     
00267     return rv;
00268 }
00269 
00270 
00271 // updateWhitelist
00272 //
00273 // Helper function called by nsInstallTrigger::AllowInstall().
00274 // Interprets the pref as a comma-delimited list of hosts and adds each one
00275 // to the permission manager using the given action. Clear pref when done.
00276 static void updatePermissions( const char* aPref,
00277                                PRUint32 aPermission,
00278                                nsIPermissionManager* aPermissionManager,
00279                                nsIPrefBranch*        aPrefBranch)
00280 {
00281     NS_PRECONDITION(aPref && aPermissionManager && aPrefBranch, "Null arguments!");
00282 
00283     nsXPIDLCString hostlist;
00284     nsresult rv = aPrefBranch->GetCharPref( aPref, getter_Copies(hostlist));
00285     if (NS_SUCCEEDED(rv) && !hostlist.IsEmpty())
00286     {
00287         nsCAutoString host;
00288         PRInt32 start=0, match=0;
00289         nsresult rv;
00290         nsCOMPtr<nsIURI> uri;
00291 
00292         do {
00293             match = hostlist.FindChar(',', start);
00294 
00295             host = Substring(hostlist, start, match-start);
00296             host.CompressWhitespace();
00297             host.Insert("http://", 0);
00298 
00299             rv = NS_NewURI(getter_AddRefs(uri), host);
00300             if (NS_SUCCEEDED(rv))
00301             {
00302                 aPermissionManager->Add( uri, XPI_PERMISSION, aPermission );
00303             }
00304             start = match+1;
00305         } while ( match > 0 );
00306 
00307         // save empty list, we don't need to do this again
00308         aPrefBranch->SetCharPref( aPref, "");
00309     }
00310 }
00311 
00312 
00313 // Check whether an Install is allowed. The launching URI can be null,
00314 // in which case only the global pref-setting matters.
00315 PRBool
00316 nsInstallTrigger::AllowInstall(nsIURI* aLaunchURI)
00317 {
00318     // Check the global setting.
00319     PRBool xpiEnabled = PR_FALSE;
00320     nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
00321     if ( !prefBranch)
00322     {
00323         return PR_TRUE; // no pref service in native install, it's OK
00324     }
00325 
00326     prefBranch->GetBoolPref( XPINSTALL_ENABLE_PREF, &xpiEnabled);
00327     if ( !xpiEnabled )
00328     {
00329         // globally turned off
00330         return PR_FALSE;
00331     }
00332 
00333 
00334     // Check permissions for the launching host if we have one
00335     nsCOMPtr<nsIPermissionManager> permissionMgr =
00336                             do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
00337 
00338     if ( permissionMgr && aLaunchURI )
00339     {
00340         PRBool isChrome = PR_FALSE;
00341         PRBool isFile = PR_FALSE;
00342         aLaunchURI->SchemeIs( "chrome", &isChrome );
00343         aLaunchURI->SchemeIs( "file", &isFile );
00344 
00345         // file: and chrome: don't need whitelisted hosts
00346         if ( !isChrome && !isFile )
00347         {
00348             // check prefs for permission updates before testing URI
00349             updatePermissions( XPINSTALL_WHITELIST_ADD,
00350                                nsIPermissionManager::ALLOW_ACTION,
00351                                permissionMgr, prefBranch );
00352             updatePermissions( XPINSTALL_WHITELIST_ADD_103,
00353                                nsIPermissionManager::ALLOW_ACTION,
00354                                permissionMgr, prefBranch );
00355             updatePermissions( XPINSTALL_BLACKLIST_ADD,
00356                                nsIPermissionManager::DENY_ACTION,
00357                                permissionMgr, prefBranch );
00358 
00359             PRBool requireWhitelist = PR_TRUE;
00360             prefBranch->GetBoolPref( XPINSTALL_WHITELIST_REQUIRED, &requireWhitelist );
00361 
00362             PRUint32 permission = nsIPermissionManager::UNKNOWN_ACTION;
00363             permissionMgr->TestPermission( aLaunchURI, XPI_PERMISSION, &permission );
00364 
00365             if ( permission == nsIPermissionManager::DENY_ACTION )
00366             {
00367                 xpiEnabled = PR_FALSE;
00368             }
00369             else if ( requireWhitelist &&
00370                       permission != nsIPermissionManager::ALLOW_ACTION )
00371             {
00372                 xpiEnabled = PR_FALSE;
00373             }
00374         }
00375     }
00376 
00377     return xpiEnabled;
00378 }
00379 
00380 
00381 NS_IMETHODIMP
00382 nsInstallTrigger::UpdateEnabled(nsIScriptGlobalObject* aGlobalObject, PRBool aUseWhitelist, PRBool* aReturn)
00383 {
00384     // disallow unless we successfully find otherwise
00385     *aReturn = PR_FALSE;
00386 
00387     if (!aUseWhitelist)
00388     {
00389         // simple global pref check
00390         nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
00391         if (prefBranch)
00392             prefBranch->GetBoolPref( XPINSTALL_ENABLE_PREF, aReturn);
00393     }
00394     else
00395     {
00396         NS_ENSURE_ARG_POINTER(aGlobalObject);
00397 
00398         // find the current site
00399         nsCOMPtr<nsIDOMDocument> domdoc;
00400         nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(aGlobalObject));
00401         if ( window )
00402         {
00403             window->GetDocument(getter_AddRefs(domdoc));
00404             nsCOMPtr<nsIDocument> doc(do_QueryInterface(domdoc));
00405             if ( doc )
00406             {
00407                 *aReturn = AllowInstall( doc->GetDocumentURI() );
00408             }
00409         }
00410     }
00411 
00412     return NS_OK;
00413 }
00414 
00415 
00416 NS_IMETHODIMP
00417 nsInstallTrigger::Install(nsIScriptGlobalObject* aGlobalObject, nsXPITriggerInfo* aTrigger, PRBool* aReturn)
00418 {
00419     NS_ASSERTION(aReturn, "Invalid pointer arg");
00420     *aReturn = PR_FALSE;
00421 
00422     nsresult rv;
00423     nsXPInstallManager *mgr = new nsXPInstallManager();
00424     if (mgr)
00425     {
00426         // The Install manager will delete itself when done
00427         rv = mgr->InitManager( aGlobalObject, aTrigger, 0 );
00428         if (NS_SUCCEEDED(rv))
00429             *aReturn = PR_TRUE;
00430     }
00431     else
00432     {
00433         delete aTrigger;
00434         rv = NS_ERROR_OUT_OF_MEMORY;
00435     }
00436 
00437 
00438     return rv;
00439 }
00440 
00441 
00442 NS_IMETHODIMP
00443 nsInstallTrigger::InstallChrome(nsIScriptGlobalObject* aGlobalObject, PRUint32 aType, nsXPITriggerItem *aItem, PRBool* aReturn)
00444 {
00445     NS_ENSURE_ARG_POINTER(aReturn);
00446     NS_ENSURE_ARG_POINTER(aItem);
00447     *aReturn = PR_FALSE;
00448 
00449 
00450     // The Install manager will delete itself when done, once we've called
00451     // InitManager. Before then **WE** must delete it
00452     nsresult rv = NS_ERROR_OUT_OF_MEMORY;
00453     nsXPInstallManager *mgr = new nsXPInstallManager();
00454     if (mgr)
00455     {
00456         nsXPITriggerInfo* trigger = new nsXPITriggerInfo();
00457         if ( trigger )
00458         {
00459             trigger->Add( aItem );
00460 
00461             // The Install manager will delete itself when done
00462             rv = mgr->InitManager( aGlobalObject, trigger, aType );
00463             *aReturn = PR_TRUE;
00464         }
00465         else
00466         {
00467             rv = NS_ERROR_OUT_OF_MEMORY;
00468             delete mgr;
00469         }
00470     }
00471 
00472     return NS_OK;
00473 }
00474 
00475 NS_IMETHODIMP
00476 nsInstallTrigger::StartSoftwareUpdate(nsIScriptGlobalObject* aGlobalObject, const nsString& aURL, PRInt32 aFlags, PRBool* aReturn)
00477 {
00478     nsresult rv = NS_ERROR_OUT_OF_MEMORY;
00479     *aReturn = PR_FALSE;
00480 
00481     // The Install manager will delete itself when done, once we've called
00482     // InitManager. Before then **WE** must delete it
00483     nsXPInstallManager *mgr = new nsXPInstallManager();
00484     if (mgr)
00485     {
00486         nsXPITriggerInfo* trigger = new nsXPITriggerInfo();
00487         if ( trigger )
00488         {
00489             nsXPITriggerItem* item = new nsXPITriggerItem(0,aURL.get(),nsnull);
00490             if (item)
00491             {
00492                 trigger->Add( item );
00493                 // The Install manager will delete itself when done
00494                 rv = mgr->InitManager(aGlobalObject, trigger, 0 );
00495                 *aReturn = PR_TRUE;
00496             }
00497             else
00498             {
00499                 rv = NS_ERROR_OUT_OF_MEMORY;
00500                 delete trigger;
00501                 delete mgr;
00502             }
00503         }
00504         else
00505         {
00506             rv = NS_ERROR_OUT_OF_MEMORY;
00507             delete mgr;
00508         }
00509     }
00510 
00511     return rv;
00512 }
00513 
00514 
00515 NS_IMETHODIMP
00516 nsInstallTrigger::CompareVersion(const nsString& aRegName, PRInt32 aMajor, PRInt32 aMinor, PRInt32 aRelease, PRInt32 aBuild, PRInt32* aReturn)
00517 {
00518     nsInstallVersion inVersion;
00519     inVersion.Init(aMajor, aMinor, aRelease, aBuild);
00520 
00521     return CompareVersion(aRegName, &inVersion, aReturn);
00522 }
00523 
00524 NS_IMETHODIMP
00525 nsInstallTrigger::CompareVersion(const nsString& aRegName, const nsString& aVersion, PRInt32* aReturn)
00526 {
00527     nsInstallVersion inVersion;
00528     inVersion.Init(aVersion);
00529 
00530     return CompareVersion(aRegName, &inVersion, aReturn);
00531 }
00532 
00533 NS_IMETHODIMP
00534 nsInstallTrigger::CompareVersion(const nsString& aRegName, nsIDOMInstallVersion* aVersion, PRInt32* aReturn)
00535 {
00536     *aReturn = NOT_FOUND;  // assume failure.
00537 
00538     VERSION              cVersion;
00539     NS_ConvertUCS2toUTF8 regName(aRegName);
00540     REGERR               status;
00541     nsInstallVersion     regNameVersion;
00542 
00543     status = VR_GetVersion( NS_CONST_CAST(char *, regName.get()), &cVersion );
00544     if ( status == REGERR_OK )
00545     {
00546         // we found the version
00547         if ( VR_ValidateComponent( NS_CONST_CAST(char *, regName.get()) ) != REGERR_NOFILE )
00548         {
00549             // and the installed file was not missing:  do the compare
00550             regNameVersion.Init(cVersion.major,
00551                                 cVersion.minor,
00552                                 cVersion.release,
00553                                 cVersion.build);
00554 
00555             regNameVersion.CompareTo( aVersion, aReturn );
00556         }
00557     }
00558 
00559     return NS_OK;
00560 }
00561 
00562 NS_IMETHODIMP
00563 nsInstallTrigger::GetVersion(const nsString& component, nsString& version)
00564 {
00565     VERSION              cVersion;
00566     NS_ConvertUCS2toUTF8 regName(component);
00567     REGERR               status;
00568 
00569     status = VR_GetVersion( NS_CONST_CAST(char *, regName.get()), &cVersion );
00570 
00571     version.Truncate();
00572 
00573     /* if we got the version */
00574     // XXX fix the right way after PR3 or RTM
00575     // if ( status == REGERR_OK && VR_ValidateComponent( tempCString ) == REGERR_OK)
00576     if ( status == REGERR_OK )
00577     {
00578         nsInstallVersion regNameVersion;
00579 
00580         regNameVersion.Init(cVersion.major,
00581                             cVersion.minor,
00582                             cVersion.release,
00583                             cVersion.build);
00584 
00585         regNameVersion.ToString(version);
00586     }
00587 
00588     return NS_OK;
00589 }
00590