Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(57)

Unified Diff: content/common/media/cdm_host_files.cc

Issue 2582463003: media: Verify CDM Host files (Closed)
Patch Set: comments addressed Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/common/media/cdm_host_files.cc
diff --git a/content/common/media/cdm_host_files.cc b/content/common/media/cdm_host_files.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1a3f56be1e21cfea80e0666318bc3ef5e7abf831
--- /dev/null
+++ b/content/common/media/cdm_host_files.cc
@@ -0,0 +1,334 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/common/media/cdm_host_files.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/native_library.h"
+#include "base/path_service.h"
+#include "base/scoped_native_library.h"
+#include "build/build_config.h"
+#include "content/common/media/cdm_host_file.h"
+#include "content/public/common/cdm_info.h"
+#include "content/public/common/content_client.h"
+#include "media/base/media_switches.h"
+#include "media/cdm/api/content_decryption_module_ext.h"
+#include "media/cdm/cdm_paths.h"
+
+#if defined(POSIX_WITH_ZYGOTE)
+#include "content/common/pepper_plugin_list.h"
+#include "content/public/common/pepper_plugin_info.h"
+#endif
+
+#include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
+
+namespace content {
+
+namespace {
+
+bool IgnoreMissingCdmHostFile() {
+ return base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kIgnoreMissingCdmHostFileForTesting);
+}
+
+// TODO(xhwang): Move this to a common place if needed.
+const base::FilePath::CharType kSignatureFileExtension[] =
+ FILE_PATH_LITERAL(".sig");
+
+// Returns the signature file path given the |file_path|. This function should
+// only be used when the signature file and the file are located in the same
+// directory, which is the case for the CDM and CDM adapter.
+base::FilePath GetSigFilePath(const base::FilePath& file_path) {
+ return file_path.AddExtension(kSignatureFileExtension);
+}
+
+// Returns the CDM library file name given the |cdm_adapter_file_name|. Returns
+// nullptr if |cdm_adapter_file_name| does not correspond to a known CDM.
+const char* GetCdmFileName(const base::FilePath& cdm_adapter_file_name) {
+#if defined(WIDEVINE_CDM_AVAILABLE)
+ if (cdm_adapter_file_name ==
+ base::FilePath::FromUTF8Unsafe(kWidevineCdmAdapterFileName))
+ return kWidevineCdmLibraryName;
+#endif
+
+ // Clear Key CDM. For test only.
+ if (cdm_adapter_file_name ==
+ base::FilePath::FromUTF8Unsafe(media::kClearKeyCdmAdapterFileName))
+ return media::kClearKeyCdmLibraryName;
+
+ return nullptr;
+}
+
+// Returns the path to the CDM binary given the |cdm_adapter_path|. Returns an
+// empty path if |cdm_adapter_path| does not correspond to a known CDM.
+base::FilePath GetCdmPath(const base::FilePath& cdm_adapter_path) {
+ const char* cdm_file_name = GetCdmFileName(cdm_adapter_path.BaseName());
+ if (!cdm_file_name)
+ return base::FilePath();
+
+ return cdm_adapter_path.DirName().AppendASCII(
+ base::GetNativeLibraryName(cdm_file_name));
+}
+
+#if defined(POSIX_WITH_ZYGOTE)
+// From the list of registered plugins, finds all registered CDMs and fills
+// |cdm_adapter_paths| with found CDM adapters paths.
+void GetRegisteredCdms(std::vector<base::FilePath>* cdm_adapter_paths) {
+ std::vector<PepperPluginInfo> plugins;
+ ComputePepperPluginList(&plugins);
+ for (const auto& plugin : plugins) {
+ // CDM is not an internal plugin.
+ if (plugin.is_internal)
+ continue;
+
+ if (IsCdm(plugin.path))
+ cdm_adapter_paths->push_back(plugin.path);
+ }
+}
+
+// A global instance used on platforms where we have to open the files in the
+// Zygote process.
+base::LazyInstance<std::unique_ptr<CdmHostFiles>> g_cdm_host_files =
+ LAZY_INSTANCE_INITIALIZER;
+#endif
+
+} // namespace
+
+CdmHostFiles::CdmHostFiles() {
+ DVLOG(1) << __func__;
+}
+
+CdmHostFiles::~CdmHostFiles() {
+ DVLOG(1) << __func__;
+}
+
+#if defined(POSIX_WITH_ZYGOTE)
+// static
+void CdmHostFiles::CreateGlobalInstance() {
+ DVLOG(1) << __func__;
+ DCHECK(!g_cdm_host_files.Get().get());
+
+ std::unique_ptr<CdmHostFiles> cdm_host_files =
+ base::MakeUnique<CdmHostFiles>();
+ if (!cdm_host_files->OpenFilesForAllRegisteredCdms()) {
+ DVLOG(1) << __func__ << " failed.";
+ cdm_host_files.reset();
+ return;
+ }
+
+ g_cdm_host_files.Get().reset(cdm_host_files.release());
+}
+
+// static
+std::unique_ptr<CdmHostFiles> CdmHostFiles::TakeGlobalInstance() {
+ return std::move(g_cdm_host_files.Get());
+}
+#endif
+
+// static
+std::unique_ptr<CdmHostFiles> CdmHostFiles::Create(
+ const base::FilePath& cdm_adapter_path) {
+ DVLOG(1) << __func__;
+ std::unique_ptr<CdmHostFiles> cdm_host_files =
+ base::MakeUnique<CdmHostFiles>();
+ if (!cdm_host_files->OpenFiles(cdm_adapter_path)) {
+ cdm_host_files.reset();
+ return nullptr;
+ }
+
+ return cdm_host_files;
+}
+
+void CdmHostFiles::VerifyFiles(base::NativeLibrary cdm_adapter_library,
+ const base::FilePath& cdm_adapter_path) {
+ DVLOG(1) << __func__;
+ DCHECK(cdm_adapter_library);
+
+ // Get VerifyHostFiles() function pointer exported by the CDM.
+ // See media/cdm/api/content_decryption_module_ext.h.
+ using VerifyHostFilesFunc =
+ bool (*)(const cdm::HostFile* cdm_host_files, uint32_t num_files);
+ static const char kVerifyHostFilesFuncName[] = "VerifyHostFiles";
tinskip1 2017/01/19 03:42:47 This function name should probably be versioned ju
xhwang 2017/01/19 08:30:51 Agreed that if we change the API we need a differe
+
+ base::NativeLibrary cdm_library;
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+ // On POSIX, "the dlsym() function shall search for the named symbol in all
+ // objects loaded automatically as a result of loading the object referenced
+ // by handle". Since the CDM is loaded automatically as a result of loading
+ // the CDM adapter, we can just use the adapter to look for CDM symbols.
+ cdm_library = cdm_adapter_library;
+#elif defined(OS_WIN)
+ // On Windows, we have manually load the CDM.
+ base::ScopedNativeLibrary scoped_cdm_library;
+ base::NativeLibraryLoadError error;
+ scoped_cdm_library.Reset(
+ base::LoadNativeLibrary(GetCdmPath(cdm_adapter_path), &error));
+ if (!scoped_cdm_library.is_valid()) {
+ LOG(ERROR) << "Failed to load CDM (error: " << error.ToString() << ")";
+ CloseAllFiles();
+ return;
+ }
+ cdm_library = scoped_cdm_library.get();
+#endif
+
+ VerifyHostFilesFunc verify_host_files_func =
+ reinterpret_cast<VerifyHostFilesFunc>(
+ base::GetFunctionPointerFromNativeLibrary(cdm_library,
+ kVerifyHostFilesFuncName));
+ if (!verify_host_files_func) {
+ LOG(ERROR) << "Function " << kVerifyHostFilesFuncName << " not found.";
+ CloseAllFiles();
+ return;
+ }
+
+ // Fills |cdm_host_files| with common and CDM specific files for
+ // |cdm_adapter_path|.
+ std::vector<cdm::HostFile> cdm_host_files;
+ if (!TakePlatformFiles(cdm_adapter_path, &cdm_host_files)) {
+ DVLOG(1) << "Failed to take platform files.";
+ CloseAllFiles();
+ return;
+ }
+
+ // Call VerifyHostFiles() on the CDM with |cdm_host_files|. Note that the
+ // ownership of these files are transferred to the CDM, which will close the
+ // files immediately after use.
+ DVLOG(1) << __func__ << ": Calling " << kVerifyHostFilesFuncName << "().";
+ verify_host_files_func(cdm_host_files.data(), cdm_host_files.size());
tinskip1 2017/01/19 03:42:47 Check return value. Fail if false.
xhwang 2017/01/19 08:30:51 The current API doesn't return a boolean: https:/
+
+ // Close all files not passed to the CDM.
+ CloseAllFiles();
+}
+
+#if defined(POSIX_WITH_ZYGOTE)
+bool CdmHostFiles::OpenFilesForAllRegisteredCdms() {
+ std::vector<base::FilePath> cdm_adapter_paths;
+ GetRegisteredCdms(&cdm_adapter_paths);
+ if (cdm_adapter_paths.empty()) {
+ DVLOG(1) << "No CDM registered.";
+ return false;
+ }
+
+ // Ignore
+ for (auto& cdm_adapter_path : cdm_adapter_paths) {
+ bool result = OpenCdmFiles(cdm_adapter_path);
+ if (!result)
+ DVLOG(1) << "CDM files cannot be opened for " << cdm_adapter_path.value();
+ // Ignore the failure and try other registered CDM.
+ }
+
+ if (cdm_specific_files_map_.empty()) {
+ DVLOG(1) << "CDM specific files cannot be opened for any registered CDM.";
+ return false;
+ }
+
+ return OpenCommonFiles();
+}
+#endif
+
+bool CdmHostFiles::OpenFiles(const base::FilePath& cdm_adapter_path) {
+ if (!OpenCdmFiles(cdm_adapter_path))
+ return false;
+
+ return OpenCommonFiles();
+}
+
+bool CdmHostFiles::OpenCommonFiles() {
+ DCHECK(common_files_.empty());
+
+ std::vector<CdmHostFilePath> cdm_host_file_paths;
+ GetContentClient()->AddContentDecryptionModuleHostFilePaths(
+ &cdm_host_file_paths);
+
+ for (const CdmHostFilePath& value : cdm_host_file_paths) {
+ std::unique_ptr<CdmHostFile> cdm_host_file =
+ CdmHostFile::Create(value.file_path, value.sig_file_path);
+ if (cdm_host_file) {
+ common_files_.push_back(std::move(cdm_host_file));
+ continue;
+ }
+
+ if (!IgnoreMissingCdmHostFile())
+ return false;
+ }
+
+ return true;
+}
+
+bool CdmHostFiles::OpenCdmFiles(const base::FilePath& cdm_adapter_path) {
+ DCHECK(!cdm_adapter_path.empty());
+ DCHECK(!cdm_specific_files_map_.count(cdm_adapter_path));
+
+ std::unique_ptr<CdmHostFile> cdm_adapter_file =
+ CdmHostFile::Create(cdm_adapter_path, GetSigFilePath(cdm_adapter_path));
+ if (!cdm_adapter_file)
+ return false;
+
+ base::FilePath cdm_path = GetCdmPath(cdm_adapter_path);
+ std::unique_ptr<CdmHostFile> cdm_file =
+ CdmHostFile::Create(cdm_path, GetSigFilePath(cdm_path));
+ if (!cdm_file)
+ return false;
+
+ ScopedFileVector cdm_specific_files;
+ cdm_specific_files.reserve(2);
+ cdm_specific_files.push_back(std::move(cdm_adapter_file));
+ cdm_specific_files.push_back(std::move(cdm_file));
+
+ cdm_specific_files_map_[cdm_adapter_path] = std::move(cdm_specific_files);
+ return true;
+}
+
+bool CdmHostFiles::TakePlatformFiles(
+ const base::FilePath& cdm_adapter_path,
+ std::vector<cdm::HostFile>* cdm_host_files) {
+ DCHECK(cdm_host_files->empty());
+
+ if (!IgnoreMissingCdmHostFile())
+ DCHECK(!common_files_.empty());
+
+ // Check whether CDM specific files exist.
+ const auto& iter = cdm_specific_files_map_.find(cdm_adapter_path);
+ if (iter == cdm_specific_files_map_.end()) {
+ // This could happen on Linux where CDM files fail to open for Foo CDM, but
+ // now we hit Bar CDM.
+ DVLOG(1) << "No CDM specific files for " << cdm_adapter_path.value();
+ return false;
+ }
+
+ const ScopedFileVector& cdm_specific_files = iter->second;
+
+ cdm_host_files->reserve(common_files_.size() + cdm_specific_files.size());
+
+ // Populate an array of cdm::HostFile.
+ for (const auto& file : common_files_)
+ cdm_host_files->push_back(file->TakePlatformFile());
+
+ for (const auto& file : cdm_specific_files)
+ cdm_host_files->push_back(file->TakePlatformFile());
+
+ return true;
+}
+
+void CdmHostFiles::CloseAllFiles() {
+ common_files_.clear();
+ cdm_specific_files_map_.clear();
+}
+
+// Question(xhwang): Any better way to check whether a plugin is a CDM? Maybe
+// when we register the plugin we can set some flag explicitly?
+bool IsCdm(const base::FilePath& cdm_adapter_path) {
+ return !GetCdmPath(cdm_adapter_path).empty();
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698