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