1 /*
2  * Copyright 2015, Mozilla Foundation and contributors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <assert.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <vector>
21 #include <string>
22 
23 #include "ClearKeyCDM.h"
24 #include "ClearKeySessionManager.h"
25 // This include is required in order for content_decryption_module to work
26 // on Unix systems.
27 #include "stddef.h"
28 #include "content_decryption_module.h"
29 #include "content_decryption_module_ext.h"
30 #include "nss.h"
31 
32 #ifndef XP_WIN
33 #  include <sys/types.h>
34 #  include <sys/stat.h>
35 #  include <unistd.h>
36 #endif
37 
38 #ifdef ENABLE_WMF
39 #  include "WMFUtils.h"
40 #endif  // ENABLE_WMF
41 
42 extern "C" {
43 
44 CDM_API
INITIALIZE_CDM_MODULE()45 void INITIALIZE_CDM_MODULE() {}
46 
47 static bool sCanReadHostVerificationFiles = false;
48 
49 CDM_API
CreateCdmInstance(int cdm_interface_version,const char * key_system,uint32_t key_system_size,GetCdmHostFunc get_cdm_host_func,void * user_data)50 void* CreateCdmInstance(int cdm_interface_version, const char* key_system,
51                         uint32_t key_system_size,
52                         GetCdmHostFunc get_cdm_host_func, void* user_data) {
53   CK_LOGE("ClearKey CreateCDMInstance");
54 
55   if (cdm_interface_version != cdm::ContentDecryptionModule_10::kVersion) {
56     CK_LOGE(
57         "ClearKey CreateCDMInstance failed due to requesting unsupported "
58         "version %d.",
59         cdm_interface_version);
60     return nullptr;
61   }
62 #ifdef ENABLE_WMF
63   if (!wmf::EnsureLibs()) {
64     CK_LOGE("Required libraries were not found");
65     return nullptr;
66   }
67 #endif
68 
69   if (NSS_NoDB_Init(nullptr) == SECFailure) {
70     CK_LOGE("Unable to initialize NSS");
71     return nullptr;
72   }
73 
74 #ifdef MOZILLA_OFFICIAL
75   // Test that we're able to read the host files.
76   if (!sCanReadHostVerificationFiles) {
77     return nullptr;
78   }
79 #endif
80 
81   cdm::Host_10* host = static_cast<cdm::Host_10*>(
82       get_cdm_host_func(cdm_interface_version, user_data));
83   ClearKeyCDM* clearKey = new ClearKeyCDM(host);
84 
85   CK_LOGE("Created ClearKeyCDM instance!");
86 
87   return clearKey;
88 }
89 
90 const size_t TEST_READ_SIZE = 16 * 1024;
91 
CanReadSome(cdm::PlatformFile aFile)92 bool CanReadSome(cdm::PlatformFile aFile) {
93   std::vector<uint8_t> data;
94   data.resize(TEST_READ_SIZE);
95 #ifdef XP_WIN
96   DWORD bytesRead = 0;
97   return ReadFile(aFile, &data.front(), TEST_READ_SIZE, &bytesRead, nullptr) &&
98          bytesRead > 0;
99 #else
100   return read(aFile, &data.front(), TEST_READ_SIZE) > 0;
101 #endif
102 }
103 
ClosePlatformFile(cdm::PlatformFile aFile)104 void ClosePlatformFile(cdm::PlatformFile aFile) {
105 #ifdef XP_WIN
106   CloseHandle(aFile);
107 #else
108   close(aFile);
109 #endif
110 }
111 
NumExpectedHostFiles(const cdm::HostFile * aHostFiles,uint32_t aNumFiles)112 static uint32_t NumExpectedHostFiles(const cdm::HostFile* aHostFiles,
113                                      uint32_t aNumFiles) {
114 #if !defined(XP_WIN)
115   // We expect 4 binaries: clearkey, libxul, plugin-container, and Firefox.
116   return 4;
117 #else
118   // Windows running x64 or x86 natively should also have 4 as above.
119   // For Windows on ARM64, we run an x86 plugin-contianer process under
120   // emulation, and so we expect one additional binary; the x86
121   // xul.dll used by plugin-container.exe.
122   bool i686underAArch64 = false;
123   // Assume that we're running under x86 emulation on an aarch64 host if
124   // one of the paths ends with the x86 plugin-container path we'd expect.
125   const std::wstring plugincontainer = L"i686\\plugin-container.exe";
126   for (uint32_t i = 0; i < aNumFiles; i++) {
127     const cdm::HostFile& hostFile = aHostFiles[i];
128     if (hostFile.file != cdm::kInvalidPlatformFile) {
129       std::wstring path = hostFile.file_path;
130       auto offset = path.find(plugincontainer);
131       if (offset != std::string::npos &&
132           offset == path.size() - plugincontainer.size()) {
133         i686underAArch64 = true;
134         break;
135       }
136     }
137   }
138   return i686underAArch64 ? 5 : 4;
139 #endif
140 }
141 
142 CDM_API
VerifyCdmHost_0(const cdm::HostFile * aHostFiles,uint32_t aNumFiles)143 bool VerifyCdmHost_0(const cdm::HostFile* aHostFiles, uint32_t aNumFiles) {
144   // Check that we've received the expected number of host files.
145   bool rv = (aNumFiles == NumExpectedHostFiles(aHostFiles, aNumFiles));
146   // Verify that each binary is readable inside the sandbox,
147   // and close the handle.
148   for (uint32_t i = 0; i < aNumFiles; i++) {
149     const cdm::HostFile& hostFile = aHostFiles[i];
150     if (hostFile.file != cdm::kInvalidPlatformFile) {
151       if (!CanReadSome(hostFile.file)) {
152         rv = false;
153       }
154       ClosePlatformFile(hostFile.file);
155     }
156     if (hostFile.sig_file != cdm::kInvalidPlatformFile) {
157       ClosePlatformFile(hostFile.sig_file);
158     }
159   }
160   sCanReadHostVerificationFiles = rv;
161   return rv;
162 }
163 
164 }  // extern "C".
165