1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "fxjs/cfx_globaldata.h"
8
9 #include <utility>
10
11 #include "core/fdrm/fx_crypt.h"
12 #include "third_party/base/ptr_util.h"
13 #include "third_party/base/stl_util.h"
14
15 namespace {
16
17 constexpr size_t kMinGlobalDataBytes = 12;
18 constexpr size_t kMaxGlobalDataBytes = 4 * 1024 - 8;
19 constexpr uint16_t kMagic = ('X' << 8) | 'F';
20 constexpr uint16_t kMaxVersion = 2;
21
22 const uint8_t kRC4KEY[] = {
23 0x19, 0xa8, 0xe8, 0x01, 0xf6, 0xa8, 0xb6, 0x4d, 0x82, 0x04, 0x45, 0x6d,
24 0xb4, 0xcf, 0xd7, 0x77, 0x67, 0xf9, 0x75, 0x9f, 0xf0, 0xe0, 0x1e, 0x51,
25 0xee, 0x46, 0xfd, 0x0b, 0xc9, 0x93, 0x25, 0x55, 0x4a, 0xee, 0xe0, 0x16,
26 0xd0, 0xdf, 0x8c, 0xfa, 0x2a, 0xa9, 0x49, 0xfd, 0x97, 0x1c, 0x0e, 0x22,
27 0x13, 0x28, 0x7c, 0xaf, 0xc4, 0xfc, 0x9c, 0x12, 0x65, 0x8c, 0x4e, 0x5b,
28 0x04, 0x75, 0x89, 0xc9, 0xb1, 0xed, 0x50, 0xca, 0x96, 0x6f, 0x1a, 0x7a,
29 0xfe, 0x58, 0x5d, 0xec, 0x19, 0x4a, 0xf6, 0x35, 0x6a, 0x97, 0x14, 0x00,
30 0x0e, 0xd0, 0x6b, 0xbb, 0xd5, 0x75, 0x55, 0x8b, 0x6e, 0x6b, 0x19, 0xa0,
31 0xf8, 0x77, 0xd5, 0xa3};
32
33 CFX_GlobalData* g_pInstance = nullptr;
34
35 // Returns true if non-empty, setting sPropName
TrimPropName(ByteString * sPropName)36 bool TrimPropName(ByteString* sPropName) {
37 sPropName->Trim();
38 return sPropName->GetLength() != 0;
39 }
40
MakeNameTypeString(const ByteString & name,CFX_Value::DataType eType,CFX_BinaryBuf * result)41 void MakeNameTypeString(const ByteString& name,
42 CFX_Value::DataType eType,
43 CFX_BinaryBuf* result) {
44 uint32_t dwNameLen = (uint32_t)name.GetLength();
45 result->AppendBlock(&dwNameLen, sizeof(uint32_t));
46 result->AppendString(name);
47
48 uint16_t wType = static_cast<uint16_t>(eType);
49 result->AppendBlock(&wType, sizeof(uint16_t));
50 }
51
MakeByteString(const ByteString & name,const CFX_KeyValue & pData,CFX_BinaryBuf * result)52 bool MakeByteString(const ByteString& name,
53 const CFX_KeyValue& pData,
54 CFX_BinaryBuf* result) {
55 switch (pData.nType) {
56 case CFX_Value::DataType::NUMBER: {
57 MakeNameTypeString(name, pData.nType, result);
58 double dData = pData.dData;
59 result->AppendBlock(&dData, sizeof(double));
60 return true;
61 }
62 case CFX_Value::DataType::BOOLEAN: {
63 MakeNameTypeString(name, pData.nType, result);
64 uint16_t wData = static_cast<uint16_t>(pData.bData);
65 result->AppendBlock(&wData, sizeof(uint16_t));
66 return true;
67 }
68 case CFX_Value::DataType::STRING: {
69 MakeNameTypeString(name, pData.nType, result);
70 uint32_t dwDataLen = (uint32_t)pData.sData.GetLength();
71 result->AppendBlock(&dwDataLen, sizeof(uint32_t));
72 result->AppendString(pData.sData);
73 return true;
74 }
75 case CFX_Value::DataType::NULLOBJ: {
76 MakeNameTypeString(name, pData.nType, result);
77 return true;
78 }
79 // Arrays don't get persisted per JS spec page 484.
80 case CFX_Value::DataType::OBJECT:
81 default:
82 break;
83 }
84 return false;
85 }
86
87 } // namespace
88
89 // static
GetRetainedInstance(Delegate * pDelegate)90 CFX_GlobalData* CFX_GlobalData::GetRetainedInstance(Delegate* pDelegate) {
91 if (!g_pInstance) {
92 g_pInstance = new CFX_GlobalData(pDelegate);
93 }
94 ++g_pInstance->m_RefCount;
95 return g_pInstance;
96 }
97
Release()98 bool CFX_GlobalData::Release() {
99 if (--m_RefCount)
100 return false;
101
102 delete g_pInstance;
103 g_pInstance = nullptr;
104 return true;
105 }
106
CFX_GlobalData(Delegate * pDelegate)107 CFX_GlobalData::CFX_GlobalData(Delegate* pDelegate) : m_pDelegate(pDelegate) {
108 LoadGlobalPersistentVariables();
109 }
110
~CFX_GlobalData()111 CFX_GlobalData::~CFX_GlobalData() {
112 SaveGlobalPersisitentVariables();
113 }
114
FindGlobalVariable(const ByteString & propname)115 CFX_GlobalData::iterator CFX_GlobalData::FindGlobalVariable(
116 const ByteString& propname) {
117 for (auto it = m_arrayGlobalData.begin(); it != m_arrayGlobalData.end();
118 ++it) {
119 if ((*it)->data.sKey == propname)
120 return it;
121 }
122 return m_arrayGlobalData.end();
123 }
124
GetGlobalVariable(const ByteString & propname)125 CFX_GlobalData::Element* CFX_GlobalData::GetGlobalVariable(
126 const ByteString& propname) {
127 auto iter = FindGlobalVariable(propname);
128 return iter != m_arrayGlobalData.end() ? iter->get() : nullptr;
129 }
130
SetGlobalVariableNumber(ByteString sPropName,double dData)131 void CFX_GlobalData::SetGlobalVariableNumber(ByteString sPropName,
132 double dData) {
133 if (!TrimPropName(&sPropName))
134 return;
135
136 CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
137 if (pData) {
138 pData->data.nType = CFX_Value::DataType::NUMBER;
139 pData->data.dData = dData;
140 return;
141 }
142 auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
143 pNewData->data.sKey = std::move(sPropName);
144 pNewData->data.nType = CFX_Value::DataType::NUMBER;
145 pNewData->data.dData = dData;
146 m_arrayGlobalData.push_back(std::move(pNewData));
147 }
148
SetGlobalVariableBoolean(ByteString sPropName,bool bData)149 void CFX_GlobalData::SetGlobalVariableBoolean(ByteString sPropName,
150 bool bData) {
151 if (!TrimPropName(&sPropName))
152 return;
153
154 CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
155 if (pData) {
156 pData->data.nType = CFX_Value::DataType::BOOLEAN;
157 pData->data.bData = bData;
158 return;
159 }
160 auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
161 pNewData->data.sKey = std::move(sPropName);
162 pNewData->data.nType = CFX_Value::DataType::BOOLEAN;
163 pNewData->data.bData = bData;
164 m_arrayGlobalData.push_back(std::move(pNewData));
165 }
166
SetGlobalVariableString(ByteString sPropName,const ByteString & sData)167 void CFX_GlobalData::SetGlobalVariableString(ByteString sPropName,
168 const ByteString& sData) {
169 if (!TrimPropName(&sPropName))
170 return;
171
172 CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
173 if (pData) {
174 pData->data.nType = CFX_Value::DataType::STRING;
175 pData->data.sData = sData;
176 return;
177 }
178 auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
179 pNewData->data.sKey = std::move(sPropName);
180 pNewData->data.nType = CFX_Value::DataType::STRING;
181 pNewData->data.sData = sData;
182 m_arrayGlobalData.push_back(std::move(pNewData));
183 }
184
SetGlobalVariableObject(ByteString sPropName,std::vector<std::unique_ptr<CFX_KeyValue>> array)185 void CFX_GlobalData::SetGlobalVariableObject(
186 ByteString sPropName,
187 std::vector<std::unique_ptr<CFX_KeyValue>> array) {
188 if (!TrimPropName(&sPropName))
189 return;
190
191 CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
192 if (pData) {
193 pData->data.nType = CFX_Value::DataType::OBJECT;
194 pData->data.objData = std::move(array);
195 return;
196 }
197 auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
198 pNewData->data.sKey = std::move(sPropName);
199 pNewData->data.nType = CFX_Value::DataType::OBJECT;
200 pNewData->data.objData = std::move(array);
201 m_arrayGlobalData.push_back(std::move(pNewData));
202 }
203
SetGlobalVariableNull(ByteString sPropName)204 void CFX_GlobalData::SetGlobalVariableNull(ByteString sPropName) {
205 if (!TrimPropName(&sPropName))
206 return;
207
208 CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
209 if (pData) {
210 pData->data.nType = CFX_Value::DataType::NULLOBJ;
211 return;
212 }
213 auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
214 pNewData->data.sKey = std::move(sPropName);
215 pNewData->data.nType = CFX_Value::DataType::NULLOBJ;
216 m_arrayGlobalData.push_back(std::move(pNewData));
217 }
218
SetGlobalVariablePersistent(ByteString sPropName,bool bPersistent)219 bool CFX_GlobalData::SetGlobalVariablePersistent(ByteString sPropName,
220 bool bPersistent) {
221 if (!TrimPropName(&sPropName))
222 return false;
223
224 CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
225 if (!pData)
226 return false;
227
228 pData->bPersistent = bPersistent;
229 return true;
230 }
231
DeleteGlobalVariable(ByteString sPropName)232 bool CFX_GlobalData::DeleteGlobalVariable(ByteString sPropName) {
233 if (!TrimPropName(&sPropName))
234 return false;
235
236 auto iter = FindGlobalVariable(sPropName);
237 if (iter == m_arrayGlobalData.end())
238 return false;
239
240 m_arrayGlobalData.erase(iter);
241 return true;
242 }
243
GetSize() const244 int32_t CFX_GlobalData::GetSize() const {
245 return pdfium::CollectionSize<int32_t>(m_arrayGlobalData);
246 }
247
GetAt(int index)248 CFX_GlobalData::Element* CFX_GlobalData::GetAt(int index) {
249 if (index < 0 || index >= GetSize())
250 return nullptr;
251 return m_arrayGlobalData[index].get();
252 }
253
LoadGlobalPersistentVariables()254 bool CFX_GlobalData::LoadGlobalPersistentVariables() {
255 if (!m_pDelegate)
256 return false;
257
258 bool ret;
259 {
260 // Span can't outlive call to BufferDone().
261 Optional<pdfium::span<uint8_t>> buffer = m_pDelegate->LoadBuffer();
262 if (!buffer.has_value() || buffer.value().empty())
263 return false;
264
265 ret = LoadGlobalPersistentVariablesFromBuffer(buffer.value());
266 }
267 m_pDelegate->BufferDone();
268 return ret;
269 }
270
LoadGlobalPersistentVariablesFromBuffer(pdfium::span<uint8_t> buffer)271 bool CFX_GlobalData::LoadGlobalPersistentVariablesFromBuffer(
272 pdfium::span<uint8_t> buffer) {
273 if (buffer.size() < kMinGlobalDataBytes)
274 return false;
275
276 CRYPT_ArcFourCryptBlock(buffer, kRC4KEY);
277
278 uint8_t* p = buffer.data();
279 uint16_t wType = *((uint16_t*)p);
280 p += sizeof(uint16_t);
281 if (wType != kMagic)
282 return false;
283
284 uint16_t wVersion = *((uint16_t*)p);
285 p += sizeof(uint16_t);
286 if (wVersion > kMaxVersion)
287 return false;
288
289 uint32_t dwCount = *((uint32_t*)p);
290 p += sizeof(uint32_t);
291
292 uint32_t dwSize = *((uint32_t*)p);
293 p += sizeof(uint32_t);
294
295 if (dwSize != buffer.size() - sizeof(uint16_t) * 2 - sizeof(uint32_t) * 2)
296 return false;
297
298 for (int32_t i = 0, sz = dwCount; i < sz; i++) {
299 if (p > buffer.end())
300 break;
301
302 uint32_t dwNameLen = *((uint32_t*)p);
303 p += sizeof(uint32_t);
304 if (p + dwNameLen > buffer.end())
305 break;
306
307 ByteString sEntry = ByteString(p, dwNameLen);
308 p += sizeof(char) * dwNameLen;
309
310 CFX_Value::DataType wDataType =
311 static_cast<CFX_Value::DataType>(*((uint16_t*)p));
312 p += sizeof(uint16_t);
313
314 switch (wDataType) {
315 case CFX_Value::DataType::NUMBER: {
316 double dData = 0;
317 switch (wVersion) {
318 case 1: {
319 uint32_t dwData = *((uint32_t*)p);
320 p += sizeof(uint32_t);
321 dData = dwData;
322 } break;
323 case 2: {
324 dData = *((double*)p);
325 p += sizeof(double);
326 } break;
327 }
328 SetGlobalVariableNumber(sEntry, dData);
329 SetGlobalVariablePersistent(sEntry, true);
330 } break;
331 case CFX_Value::DataType::BOOLEAN: {
332 uint16_t wData = *((uint16_t*)p);
333 p += sizeof(uint16_t);
334 SetGlobalVariableBoolean(sEntry, (bool)(wData == 1));
335 SetGlobalVariablePersistent(sEntry, true);
336 } break;
337 case CFX_Value::DataType::STRING: {
338 uint32_t dwLength = *((uint32_t*)p);
339 p += sizeof(uint32_t);
340 if (p + dwLength > buffer.end())
341 break;
342
343 SetGlobalVariableString(sEntry, ByteString(p, dwLength));
344 SetGlobalVariablePersistent(sEntry, true);
345 p += sizeof(char) * dwLength;
346 } break;
347 case CFX_Value::DataType::NULLOBJ: {
348 SetGlobalVariableNull(sEntry);
349 SetGlobalVariablePersistent(sEntry, true);
350 } break;
351 case CFX_Value::DataType::OBJECT:
352 default:
353 // Arrays aren't allowed in these buffers, nor are unrecoginzed tags.
354 return false;
355 }
356 }
357 return true;
358 }
359
SaveGlobalPersisitentVariables()360 bool CFX_GlobalData::SaveGlobalPersisitentVariables() {
361 if (!m_pDelegate)
362 return false;
363
364 uint32_t nCount = 0;
365 CFX_BinaryBuf sData;
366 for (const auto& pElement : m_arrayGlobalData) {
367 if (!pElement->bPersistent)
368 continue;
369
370 CFX_BinaryBuf sElement;
371 if (!MakeByteString(pElement->data.sKey, pElement->data, &sElement))
372 continue;
373
374 if (sData.GetSize() + sElement.GetSize() > kMaxGlobalDataBytes)
375 break;
376
377 sData.AppendSpan(sElement.GetSpan());
378 nCount++;
379 }
380
381 CFX_BinaryBuf sFile;
382 uint16_t wType = kMagic;
383 uint16_t wVersion = 2;
384 sFile.AppendBlock(&wType, sizeof(uint16_t));
385 sFile.AppendBlock(&wVersion, sizeof(uint16_t));
386 sFile.AppendBlock(&nCount, sizeof(uint32_t));
387
388 uint32_t dwSize = sData.GetSize();
389 sFile.AppendBlock(&dwSize, sizeof(uint32_t));
390 sFile.AppendSpan(sData.GetSpan());
391
392 CRYPT_ArcFourCryptBlock(sFile.GetSpan(), kRC4KEY);
393
394 return m_pDelegate->StoreBuffer({sFile.GetBuffer(), sFile.GetSize()});
395 }
396
397 CFX_GlobalData::Element::Element() = default;
398
399 CFX_GlobalData::Element::~Element() = default;
400