This source file includes following definitions.
- CompareTime
- CheckQuirks
- IsBlacklistedBySha1sumAndQuirks
- IsUndesirablePlugin
- IsBlacklistedPlugin
- ELFMatchesCurrentArchitecture
- UnwrapNSPluginWrapper
- ReadWebPluginInfo
- ParseMIMEDescription
- ExtractVersionString
- GetPluginDirectories
- GetPluginsInDir
- ShouldLoadPluginUsingPluginList
#include "content/common/plugin_list.h"
#include <algorithm>
#include <dlfcn.h>
#if defined(OS_OPENBSD)
#include <sys/exec_elf.h>
#else
#include <elf.h>
#endif
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "base/cpu.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/native_library.h"
#include "base/path_service.h"
#include "base/posix/eintr_wrapper.h"
#include "base/sha1.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "third_party/npapi/bindings/nphostapi.h"
namespace content {
namespace {
typedef std::pair<base::FilePath, base::Time> FileAndTime;
typedef std::vector<FileAndTime> FileTimeList;
enum PluginQuirk {
PLUGIN_QUIRK_NONE = 0,
PLUGIN_QUIRK_MISSING_SSE2_CHECK = 1 << 0,
};
enum nsPluginVariable {
nsPluginVariable_NameString = 1,
nsPluginVariable_DescriptionString = 2
};
bool CompareTime(const FileAndTime& a, const FileAndTime& b) {
if (a.second == b.second) {
return a.first < b.first;
}
return a.second > b.second;
}
bool CheckQuirks(PluginQuirk quirks) {
if (quirks == PLUGIN_QUIRK_NONE)
return true;
if ((quirks & PLUGIN_QUIRK_MISSING_SSE2_CHECK) != 0) {
base::CPU cpu;
if (!cpu.has_sse2())
return true;
}
return false;
}
bool IsBlacklistedBySha1sumAndQuirks(const base::FilePath& path) {
const struct BadEntry {
int64 size;
std::string sha1;
PluginQuirk quirks;
} bad_entries[] = {
{ 7040080, "fa5803061125ca47846713b34a26a42f1f1e98bb", PLUGIN_QUIRK_NONE },
{ 7040036, "0c4b3768a6d4bfba003088e4b9090d381de1af2b", PLUGIN_QUIRK_NONE },
{ 17406436, "1e07eac912faf9426c52a288c76c3b6238f90b6b",
PLUGIN_QUIRK_MISSING_SSE2_CHECK },
{ 17410532, "e9401097e97c8443a7d9156be62184ffe1addd5c",
PLUGIN_QUIRK_MISSING_SSE2_CHECK },
};
int64 size;
if (!base::GetFileSize(path, &size))
return false;
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(bad_entries); i++) {
if (bad_entries[i].size != size)
continue;
std::string file_content;
if (!base::ReadFileToString(path, &file_content))
continue;
std::string sha1 = base::SHA1HashString(file_content);
std::string sha1_readable;
for (size_t j = 0; j < sha1.size(); j++)
base::StringAppendF(&sha1_readable, "%02x", sha1[j] & 0xFF);
if (bad_entries[i].sha1 == sha1_readable)
return CheckQuirks(bad_entries[i].quirks);
}
return false;
}
bool IsUndesirablePlugin(const WebPluginInfo& info) {
std::string filename = info.path.BaseName().value();
const char* kUndesiredPlugins[] = {
"npcxoffice",
"npwrapper",
};
for (size_t i = 0; i < arraysize(kUndesiredPlugins); i++) {
if (filename.find(kUndesiredPlugins[i]) != std::string::npos) {
return true;
}
}
return false;
}
bool IsBlacklistedPlugin(const base::FilePath& path) {
const char* kBlackListedPlugins[] = {
"nppdf.so",
};
std::string filename = path.BaseName().value();
for (size_t i = 0; i < arraysize(kBlackListedPlugins); i++) {
if (filename.find(kBlackListedPlugins[i]) != std::string::npos) {
return true;
}
}
return IsBlacklistedBySha1sumAndQuirks(path);
}
bool ELFMatchesCurrentArchitecture(const base::FilePath& filename) {
struct stat stat_buf;
int fd = open(filename.value().c_str(), O_RDONLY|O_NONBLOCK);
if (fd < 0)
return false;
bool ret = (fstat(fd, &stat_buf) >= 0 && S_ISREG(stat_buf.st_mode));
if (IGNORE_EINTR(close(fd)) < 0)
return false;
if (!ret)
return false;
const size_t kELFBufferSize = 5;
char buffer[kELFBufferSize];
if (!base::ReadFile(filename, buffer, kELFBufferSize))
return false;
if (buffer[0] != ELFMAG0 ||
buffer[1] != ELFMAG1 ||
buffer[2] != ELFMAG2 ||
buffer[3] != ELFMAG3) {
return false;
}
int elf_class = buffer[EI_CLASS];
#if defined(ARCH_CPU_32_BITS)
if (elf_class == ELFCLASS32)
return true;
#elif defined(ARCH_CPU_64_BITS)
if (elf_class == ELFCLASS64)
return true;
#endif
return false;
}
struct __attribute__((packed)) NSPluginWrapperInfo {
char ident[32];
char path[PATH_MAX];
};
void UnwrapNSPluginWrapper(void **dl, base::FilePath* unwrapped_path) {
NSPluginWrapperInfo* info =
reinterpret_cast<NSPluginWrapperInfo*>(dlsym(*dl, "NPW_Plugin"));
if (!info)
return;
char* path_end = static_cast<char*>(memchr(info->path, '\0',
sizeof(info->path)));
if (!path_end)
path_end = info->path + sizeof(info->path);
base::FilePath path = base::FilePath(
std::string(info->path, path_end - info->path));
if (!ELFMatchesCurrentArchitecture(path)) {
LOG(WARNING) << path.value() << " is nspluginwrapper wrapping a "
<< "plugin for a different architecture; it will "
<< "work better if you instead use a native plugin.";
return;
}
base::NativeLibraryLoadError error;
void* newdl = base::LoadNativeLibrary(path, &error);
if (!newdl) {
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "Could not use unwrapped nspluginwrapper plugin "
<< unwrapped_path->value() << " (" << error.ToString() << "), "
<< "using the wrapped one.";
return;
}
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "Using unwrapped version " << unwrapped_path->value()
<< " of nspluginwrapper-wrapped plugin.";
base::UnloadNativeLibrary(*dl);
*dl = newdl;
*unwrapped_path = path;
}
}
bool PluginList::ReadWebPluginInfo(const base::FilePath& filename,
WebPluginInfo* info) {
if (!ELFMatchesCurrentArchitecture(filename)) {
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "Skipping plugin " << filename.value()
<< " because it doesn't match the current architecture.";
return false;
}
base::NativeLibraryLoadError error;
void* dl = base::LoadNativeLibrary(filename, &error);
if (!dl) {
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "While reading plugin info, unable to load library "
<< filename.value() << " (" << error.ToString() << "), skipping.";
return false;
}
info->path = filename;
UnwrapNSPluginWrapper(&dl, &info->path);
typedef const char* (*NP_GetMimeDescriptionType)();
NP_GetMimeDescriptionType NP_GetMIMEDescription =
reinterpret_cast<NP_GetMimeDescriptionType>(
dlsym(dl, "NP_GetMIMEDescription"));
const char* mime_description = NULL;
if (!NP_GetMIMEDescription) {
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "Plugin " << filename.value() << " doesn't have a "
<< "NP_GetMIMEDescription symbol";
return false;
}
mime_description = NP_GetMIMEDescription();
if (!mime_description) {
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "MIME description for " << filename.value() << " is empty";
return false;
}
ParseMIMEDescription(mime_description, &info->mime_types);
typedef NPError (*NP_GetValueType)(void* unused,
nsPluginVariable variable,
void* value_out);
NP_GetValueType NP_GetValue =
reinterpret_cast<NP_GetValueType>(dlsym(dl, "NP_GetValue"));
if (NP_GetValue) {
const char* name = NULL;
NP_GetValue(NULL, nsPluginVariable_NameString, &name);
if (name) {
info->name = base::UTF8ToUTF16(name);
ExtractVersionString(name, info);
}
const char* description = NULL;
NP_GetValue(NULL, nsPluginVariable_DescriptionString, &description);
if (description) {
info->desc = base::UTF8ToUTF16(description);
if (info->version.empty())
ExtractVersionString(description, info);
}
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "Got info for plugin " << filename.value()
<< " Name = \"" << base::UTF16ToUTF8(info->name)
<< "\", Description = \"" << base::UTF16ToUTF8(info->desc)
<< "\", Version = \"" << base::UTF16ToUTF8(info->version)
<< "\".";
} else {
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "Plugin " << filename.value()
<< " has no GetValue() and probably won't work.";
}
return true;
}
void PluginList::ParseMIMEDescription(
const std::string& description,
std::vector<WebPluginMimeType>* mime_types) {
std::string::size_type ofs = 0;
for (;;) {
WebPluginMimeType mime_type;
std::string::size_type end = description.find(':', ofs);
if (end == std::string::npos)
break;
mime_type.mime_type = description.substr(ofs, end - ofs);
ofs = end + 1;
end = description.find(':', ofs);
if (end == std::string::npos)
break;
const std::string extensions = description.substr(ofs, end - ofs);
base::SplitString(extensions, ',', &mime_type.file_extensions);
ofs = end + 1;
end = description.find(';', ofs);
if (end != std::string::npos) {
mime_type.description =
base::UTF8ToUTF16(description.substr(ofs, end - ofs));
} else {
mime_type.description = base::UTF8ToUTF16(description.substr(ofs));
}
mime_types->push_back(mime_type);
if (end == std::string::npos)
break;
ofs = end + 1;
}
}
void PluginList::ExtractVersionString(const std::string& desc,
WebPluginInfo* info) {
static const struct {
const char* kPrefix;
const char* kPostfix;
} kPrePostFixes[] = {
{ "Shockwave Flash ", 0 },
{ "Java(TM) Plug-in ", 0 },
{ "(using IcedTea-Web ", " " },
{ 0, 0 }
};
std::string version;
for (size_t i = 0; kPrePostFixes[i].kPrefix; ++i) {
size_t pos;
if ((pos = desc.find(kPrePostFixes[i].kPrefix)) != std::string::npos) {
version = desc.substr(pos + strlen(kPrePostFixes[i].kPrefix));
pos = std::string::npos;
if (kPrePostFixes[i].kPostfix)
pos = version.find(kPrePostFixes[i].kPostfix);
if (pos != std::string::npos)
version = version.substr(0, pos);
break;
}
}
if (!version.empty()) {
info->version = base::UTF8ToUTF16(version);
}
}
void PluginList::GetPluginDirectories(std::vector<base::FilePath>* plugin_dirs) {
if (PluginList::plugins_discovery_disabled_)
return;
base::FilePath dir;
PathService::Get(base::DIR_EXE, &dir);
plugin_dirs->push_back(dir.Append("plugins"));
#if !defined(OS_CHROMEOS)
const char* moz_plugin_path = getenv("MOZ_PLUGIN_PATH");
if (moz_plugin_path) {
std::vector<std::string> paths;
base::SplitString(moz_plugin_path, ':', &paths);
for (size_t i = 0; i < paths.size(); ++i)
plugin_dirs->push_back(base::FilePath(paths[i]));
}
base::FilePath home = base::GetHomeDir();
if (!home.empty())
plugin_dirs->push_back(home.Append(".mozilla/plugins"));
plugin_dirs->push_back(base::FilePath("/usr/lib/browser-plugins"));
plugin_dirs->push_back(base::FilePath("/usr/lib/mozilla/plugins"));
plugin_dirs->push_back(base::FilePath("/usr/lib/firefox/plugins"));
plugin_dirs->push_back(base::FilePath("/usr/lib/xulrunner-addons/plugins"));
#if defined(ARCH_CPU_64_BITS)
plugin_dirs->push_back(base::FilePath("/usr/lib64/browser-plugins"));
plugin_dirs->push_back(base::FilePath("/usr/lib64/mozilla/plugins"));
plugin_dirs->push_back(base::FilePath("/usr/lib64/firefox/plugins"));
plugin_dirs->push_back(base::FilePath("/usr/lib64/xulrunner-addons/plugins"));
#endif
#endif
}
void PluginList::GetPluginsInDir(
const base::FilePath& dir_path, std::vector<base::FilePath>* plugins) {
FileTimeList files;
base::FileEnumerator enumerator(dir_path,
false,
base::FileEnumerator::FILES);
for (base::FilePath path = enumerator.Next(); !path.value().empty();
path = enumerator.Next()) {
if (path.MatchesExtension(FILE_PATH_LITERAL(".xpt")))
continue;
base::FilePath orig_path = path;
path = base::MakeAbsoluteFilePath(path);
if (path.empty())
path = orig_path;
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "Resolved " << orig_path.value() << " -> " << path.value();
if (std::find(plugins->begin(), plugins->end(), path) != plugins->end()) {
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "Skipping duplicate instance of " << path.value();
continue;
}
if (IsBlacklistedPlugin(path)) {
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "Skipping blacklisted plugin " << path.value();
continue;
}
static const char kFlashPlayerFilename[] = "libflashplayer.so";
static const char kNetscapeInPath[] = "/netscape/";
if (path.BaseName().value() == kFlashPlayerFilename &&
path.value().find(kNetscapeInPath) != std::string::npos) {
if (orig_path.value().find(kNetscapeInPath) == std::string::npos) {
path = orig_path;
} else {
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "Flash misbehaves when used from a directory containing "
<< kNetscapeInPath << ", so skipping " << orig_path.value();
continue;
}
}
base::File::Info info;
if (!base::GetFileInfo(path, &info))
continue;
files.push_back(std::make_pair(path, info.last_modified));
}
std::sort(files.begin(), files.end(), CompareTime);
for (FileTimeList::const_iterator i = files.begin(); i != files.end(); ++i) {
plugins->push_back(i->first);
}
}
bool PluginList::ShouldLoadPluginUsingPluginList(
const WebPluginInfo& info, std::vector<WebPluginInfo>* plugins) {
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "Considering " << info.path.value() << " (" << info.name << ")";
if (IsUndesirablePlugin(info)) {
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< info.path.value() << " is undesirable.";
for (size_t j = 0; j < plugins->size(); ++j) {
if ((*plugins)[j].name == info.name &&
!IsUndesirablePlugin((*plugins)[j])) {
LOG_IF(ERROR, PluginList::DebugPluginLoading())
<< "Skipping " << info.path.value() << ", preferring "
<< (*plugins)[j].path.value();
return false;
}
}
}
VLOG_IF(1, PluginList::DebugPluginLoading()) << "Using " << info.path.value();
return true;
}
}