Bug 408153 - "Use IAttachmentExecute antivirus API when it is available" [p=VYV03354@nifty.ne.jp (Masatoshi Kimura [emk]) / jmathies@mozilla.com (Jim Mathies) r=sdwilsh r=jmathies a=blocking-firefox3+]

This commit is contained in:
reed@reedloden.com 2008-02-08 14:18:54 -08:00
parent 6eb4098d75
commit 979604b65e
2 changed files with 109 additions and 30 deletions

View File

@ -20,6 +20,8 @@
*
* Contributor(s):
* Rob Arnold <robarnold@mozilla.com> (Original Author)
* Masatoshi Kimura <VYV03354@nifty.ne.jp>
* Jim Mathies <jmathies@mozilla.com>
*
* 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
@ -47,25 +49,25 @@
/**
* Code overview
*
* Antivirus vendors on Windows can implement the IOfficeAntiVirus interface
* so that other programs (not just Office) can use their functionality.
* According to the Microsoft documentation, this interface requires only
* Windows 95/NT 4 and IE 5 so it is used in preference to IAttachmentExecute
* which requires XP SP2 (Windows 2000 is still supported at this time).
* Download scanner attempts to make use of one of two different virus
* scanning interfaces available on Windows - IOfficeAntiVirus (Windows
* 95/NT 4 and IE 5) and IAttachmentExecute (XPSP2 and up). The latter
* interface supports calling IOfficeAntiVirus internally, while also
* adding support for XPSP2+ ADS forks which define security related
* prompting on downloaded content.
*
* The interface is rather simple; it provides a Scan method which takes a
* small structure describing what to scan. Unfortunately, the method is
* synchronous and could take a while, so it is not a good idea to call it from
* the main thread. Some antivirus scanners can take a long time to scan or the
* call might block while the scanner shows its UI so if the user were to
* download many files that finished around the same time, they would have to
* wait a while if the scanning were done on exactly one other thread. Since
* the overhead of creating a thread is relatively small compared to the time
* it takes to download a file and scan it, a new thread is spawned for each
* download that is to be scanned. Since most of the mozilla codebase is not
* threadsafe, all the information needed for the scanner is gathered in the
* main thread in nsDownloadScanner::Scan::Start. The only function of
* nsDownloadScanner::Scan which is invoked on another thread is DoScan.
* Both interfaces are synchronous and can take a while, so it is not a
* good idea to call either from the main thread. Some antivirus scanners can
* take a long time to scan or the call might block while the scanner shows
* its UI so if the user were to download many files that finished around the
* same time, they would have to wait a while if the scanning were done on
* exactly one other thread. Since the overhead of creating a thread is
* relatively small compared to the time it takes to download a file and scan
* it, a new thread is spawned for each download that is to be scanned. Since
* most of the mozilla codebase is not threadsafe, all the information needed
* for the scanner is gathered in the main thread in nsDownloadScanner::Scan::Start.
* The only function of nsDownloadScanner::Scan which is invoked on another
* thread is DoScan.
*
* There are 4 possible outcomes of the virus scan:
* AVSCAN_GOOD => the file is clean
@ -78,8 +80,6 @@
* Failed states transition to finished downloads.
*
* Possible Future enhancements:
* * Use all available virus scanners instead of just the first one that is
* enumerated (or use some heuristic)
* * Create an interface for scanning files in general
* * Make this a service
* * Get antivirus scanner status via WMI/registry
@ -87,8 +87,20 @@
#define PREF_BDA_DONTCLEAN "browser.download.antivirus.dontclean"
// IAttachementExecute supports user definable settings for certain
// security related prompts. This defines a general GUID for use in
// all projects. Individual projects can define an individual guid
// if they want to.
#ifndef MOZ_VIRUS_SCANNER_PROMPT_GUID
#define MOZ_VIRUS_SCANNER_PROMPT_GUID \
{ 0xb50563d1, 0x16b6, 0x43c2, { 0xa6, 0x6a, 0xfa, 0xe6, 0xd2, 0x11, 0xf2, \
0xea } }
#endif
static const GUID GUID_MozillaVirusScannerPromptGeneric =
MOZ_VIRUS_SCANNER_PROMPT_GUID;
nsDownloadScanner::nsDownloadScanner()
: mHaveAVScanner(PR_FALSE)
: mHaveAVScanner(PR_FALSE), mHaveAttachmentExecute(PR_FALSE)
{
}
@ -99,13 +111,30 @@ nsDownloadScanner::Init()
// codebase. All other COM calls/objects are made on different threads.
nsresult rv = NS_OK;
CoInitialize(NULL);
if (ListCLSID() < 0)
if (!IsAESAvailable() && ListCLSID() < 0)
rv = NS_ERROR_NOT_AVAILABLE;
CoUninitialize();
return rv;
}
PRBool
nsDownloadScanner::IsAESAvailable()
{
nsRefPtr<IAttachmentExecute> ae;
HRESULT hr;
hr = CoCreateInstance(CLSID_AttachmentServices, NULL, CLSCTX_INPROC,
IID_IAttachmentExecute, getter_AddRefs(ae));
if (FAILED(hr)) {
NS_WARNING("Could not instantiate attachment execution service\n");
return PR_FALSE;
}
mHaveAVScanner = PR_TRUE;
mHaveAttachmentExecute = PR_TRUE;
return PR_TRUE;
}
PRInt32
nsDownloadScanner::ListCLSID()
{
@ -181,7 +210,6 @@ nsDownloadScanner::Scan::Start()
rv = file->GetPath(mPath);
NS_ENSURE_SUCCESS(rv, rv);
// Grab the app name
nsCOMPtr<nsIXULAppInfo> appinfo =
do_GetService(XULAPPINFO_SERVICE_CONTRACTID, &rv);
@ -192,7 +220,6 @@ nsDownloadScanner::Scan::Start()
NS_ENSURE_SUCCESS(rv, rv);
CopyUTF8toUTF16(name, mName);
// Get the origin
nsCOMPtr<nsIURI> uri;
rv = mDownload->GetSource(getter_AddRefs(uri));
@ -204,7 +231,6 @@ nsDownloadScanner::Scan::Start()
CopyUTF8toUTF16(origin, mOrigin);
// We count https/ftp/http as an http download
PRBool isHttp(PR_FALSE), isFtp(PR_FALSE), isHttps(PR_FALSE);
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
@ -247,7 +273,41 @@ nsDownloadScanner::Scan::Run()
}
void
nsDownloadScanner::Scan::DoScan()
nsDownloadScanner::Scan::DoScanAES()
{
HRESULT hr;
nsRefPtr<IAttachmentExecute> ae;
hr = CoCreateInstance(CLSID_AttachmentServices, NULL, CLSCTX_ALL,
IID_IAttachmentExecute, getter_AddRefs(ae));
mStatus = AVSCAN_SCANNING;
if (SUCCEEDED(hr)) {
(void)ae->SetClientGuid(GUID_MozillaVirusScannerPromptGeneric);
(void)ae->SetLocalPath(mPath.BeginWriting());
(void)ae->SetSource(mOrigin.BeginWriting());
// Save() will invoke the scanner
hr = ae->Save();
if (SUCCEEDED(hr)) { // Passed the scan
mStatus = AVSCAN_GOOD;
}
else if (HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND) {
NS_WARNING("Downloaded file disappeared before it could be scanned");
mStatus = AVSCAN_FAILED;
}
else {
mStatus = AVSCAN_UGLY;
}
}
else {
mStatus = AVSCAN_FAILED;
}
}
void
nsDownloadScanner::Scan::DoScanOAV()
{
HRESULT hr;
MSOAVINFO info;
@ -262,8 +322,6 @@ nsDownloadScanner::Scan::DoScan()
info.u.pwzFullPath = mPath.BeginWriting();
info.pwzOrigURL = mOrigin.BeginWriting();
CoInitialize(NULL);
for (PRUint32 i = 0; i < mDLScanner->mScanCLSID.Length(); i++) {
nsRefPtr<IOfficeAntiVirus> vScanner;
hr = CoCreateInstance(mDLScanner->mScanCLSID[i], NULL, CLSCTX_ALL,
@ -284,7 +342,7 @@ nsDownloadScanner::Scan::DoScan()
mStatus = AVSCAN_UGLY;
continue;
}
else if (hr == ERROR_FILE_NOT_FOUND) {
else if (HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND) {
NS_WARNING("Downloaded file disappeared before it could be scanned");
mStatus = AVSCAN_FAILED;
break;
@ -299,7 +357,18 @@ nsDownloadScanner::Scan::DoScan()
}
}
}
}
void
nsDownloadScanner::Scan::DoScan()
{
CoInitialize(NULL);
if (mDLScanner->mHaveAttachmentExecute)
DoScanAES();
else
DoScanOAV();
CoUninitialize();
// We need to do a few more things on the main thread

View File

@ -10,6 +10,12 @@
#include <Windows.h>
#define AVVENDOR
#include <msoav.h>
// To cope with both msvs8 header and sdk6 header
#ifdef _WIN32_IE_IE60SP2
#undef _WIN32_IE
#define _WIN32_IE _WIN32_IE_IE60SP2
#endif
#include <shlobj.h>
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
@ -35,7 +41,9 @@ public:
private:
PRBool mHaveAVScanner;
PRBool mHaveAttachmentExecute;
nsTArray<CLSID> mScanCLSID;
PRBool IsAESAvailable();
PRInt32 ListCLSID();
static unsigned int __stdcall ScannerThreadFunction(void *p);
@ -60,6 +68,8 @@ private:
NS_IMETHOD Run();
void DoScan();
void DoScanAES();
void DoScanOAV();
friend unsigned int __stdcall nsDownloadScanner::ScannerThreadFunction(void *);
};