• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The PDFium Authors
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 "core/fxcrt/stl_util.h"
13 #include "third_party/base/numerics/safe_conversions.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,BinaryBuffer * result)41 void MakeNameTypeString(const ByteString& name,
42                         CFX_Value::DataType eType,
43                         BinaryBuffer* result) {
44   uint32_t dwNameLen = pdfium::base::checked_cast<uint32_t>(name.GetLength());
45   result->AppendUint32(dwNameLen);
46   result->AppendString(name);
47   result->AppendUint16(static_cast<uint16_t>(eType));
48 }
49 
MakeByteString(const ByteString & name,const CFX_KeyValue & pData,BinaryBuffer * result)50 bool MakeByteString(const ByteString& name,
51                     const CFX_KeyValue& pData,
52                     BinaryBuffer* result) {
53   switch (pData.nType) {
54     case CFX_Value::DataType::kNumber: {
55       MakeNameTypeString(name, pData.nType, result);
56       result->AppendDouble(pData.dData);
57       return true;
58     }
59     case CFX_Value::DataType::kBoolean: {
60       MakeNameTypeString(name, pData.nType, result);
61       result->AppendUint16(static_cast<uint16_t>(pData.bData));
62       return true;
63     }
64     case CFX_Value::DataType::kString: {
65       MakeNameTypeString(name, pData.nType, result);
66       uint32_t dwDataLen =
67           pdfium::base::checked_cast<uint32_t>(pData.sData.GetLength());
68       result->AppendUint32(dwDataLen);
69       result->AppendString(pData.sData);
70       return true;
71     }
72     case CFX_Value::DataType::kNull: {
73       MakeNameTypeString(name, pData.nType, result);
74       return true;
75     }
76     // Arrays don't get persisted per JS spec page 484.
77     case CFX_Value::DataType::kObject:
78     default:
79       break;
80   }
81   return false;
82 }
83 
84 }  // namespace
85 
86 // static
GetRetainedInstance(Delegate * pDelegate)87 CFX_GlobalData* CFX_GlobalData::GetRetainedInstance(Delegate* pDelegate) {
88   if (!g_pInstance) {
89     g_pInstance = new CFX_GlobalData(pDelegate);
90   }
91   ++g_pInstance->m_RefCount;
92   return g_pInstance;
93 }
94 
Release()95 bool CFX_GlobalData::Release() {
96   if (--m_RefCount)
97     return false;
98 
99   delete g_pInstance;
100   g_pInstance = nullptr;
101   return true;
102 }
103 
CFX_GlobalData(Delegate * pDelegate)104 CFX_GlobalData::CFX_GlobalData(Delegate* pDelegate) : m_pDelegate(pDelegate) {
105   LoadGlobalPersistentVariables();
106 }
107 
~CFX_GlobalData()108 CFX_GlobalData::~CFX_GlobalData() {
109   SaveGlobalPersisitentVariables();
110 }
111 
FindGlobalVariable(const ByteString & propname)112 CFX_GlobalData::iterator CFX_GlobalData::FindGlobalVariable(
113     const ByteString& propname) {
114   for (auto it = m_arrayGlobalData.begin(); it != m_arrayGlobalData.end();
115        ++it) {
116     if ((*it)->data.sKey == propname)
117       return it;
118   }
119   return m_arrayGlobalData.end();
120 }
121 
GetGlobalVariable(const ByteString & propname)122 CFX_GlobalData::Element* CFX_GlobalData::GetGlobalVariable(
123     const ByteString& propname) {
124   auto iter = FindGlobalVariable(propname);
125   return iter != m_arrayGlobalData.end() ? iter->get() : nullptr;
126 }
127 
SetGlobalVariableNumber(ByteString sPropName,double dData)128 void CFX_GlobalData::SetGlobalVariableNumber(ByteString sPropName,
129                                              double dData) {
130   if (!TrimPropName(&sPropName))
131     return;
132 
133   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
134   if (pData) {
135     pData->data.nType = CFX_Value::DataType::kNumber;
136     pData->data.dData = dData;
137     return;
138   }
139   auto pNewData = std::make_unique<CFX_GlobalData::Element>();
140   pNewData->data.sKey = std::move(sPropName);
141   pNewData->data.nType = CFX_Value::DataType::kNumber;
142   pNewData->data.dData = dData;
143   m_arrayGlobalData.push_back(std::move(pNewData));
144 }
145 
SetGlobalVariableBoolean(ByteString sPropName,bool bData)146 void CFX_GlobalData::SetGlobalVariableBoolean(ByteString sPropName,
147                                               bool bData) {
148   if (!TrimPropName(&sPropName))
149     return;
150 
151   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
152   if (pData) {
153     pData->data.nType = CFX_Value::DataType::kBoolean;
154     pData->data.bData = bData;
155     return;
156   }
157   auto pNewData = std::make_unique<CFX_GlobalData::Element>();
158   pNewData->data.sKey = std::move(sPropName);
159   pNewData->data.nType = CFX_Value::DataType::kBoolean;
160   pNewData->data.bData = bData;
161   m_arrayGlobalData.push_back(std::move(pNewData));
162 }
163 
SetGlobalVariableString(ByteString sPropName,const ByteString & sData)164 void CFX_GlobalData::SetGlobalVariableString(ByteString sPropName,
165                                              const ByteString& sData) {
166   if (!TrimPropName(&sPropName))
167     return;
168 
169   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
170   if (pData) {
171     pData->data.nType = CFX_Value::DataType::kString;
172     pData->data.sData = sData;
173     return;
174   }
175   auto pNewData = std::make_unique<CFX_GlobalData::Element>();
176   pNewData->data.sKey = std::move(sPropName);
177   pNewData->data.nType = CFX_Value::DataType::kString;
178   pNewData->data.sData = sData;
179   m_arrayGlobalData.push_back(std::move(pNewData));
180 }
181 
SetGlobalVariableObject(ByteString sPropName,std::vector<std::unique_ptr<CFX_KeyValue>> array)182 void CFX_GlobalData::SetGlobalVariableObject(
183     ByteString sPropName,
184     std::vector<std::unique_ptr<CFX_KeyValue>> array) {
185   if (!TrimPropName(&sPropName))
186     return;
187 
188   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
189   if (pData) {
190     pData->data.nType = CFX_Value::DataType::kObject;
191     pData->data.objData = std::move(array);
192     return;
193   }
194   auto pNewData = std::make_unique<CFX_GlobalData::Element>();
195   pNewData->data.sKey = std::move(sPropName);
196   pNewData->data.nType = CFX_Value::DataType::kObject;
197   pNewData->data.objData = std::move(array);
198   m_arrayGlobalData.push_back(std::move(pNewData));
199 }
200 
SetGlobalVariableNull(ByteString sPropName)201 void CFX_GlobalData::SetGlobalVariableNull(ByteString sPropName) {
202   if (!TrimPropName(&sPropName))
203     return;
204 
205   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
206   if (pData) {
207     pData->data.nType = CFX_Value::DataType::kNull;
208     return;
209   }
210   auto pNewData = std::make_unique<CFX_GlobalData::Element>();
211   pNewData->data.sKey = std::move(sPropName);
212   pNewData->data.nType = CFX_Value::DataType::kNull;
213   m_arrayGlobalData.push_back(std::move(pNewData));
214 }
215 
SetGlobalVariablePersistent(ByteString sPropName,bool bPersistent)216 bool CFX_GlobalData::SetGlobalVariablePersistent(ByteString sPropName,
217                                                  bool bPersistent) {
218   if (!TrimPropName(&sPropName))
219     return false;
220 
221   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
222   if (!pData)
223     return false;
224 
225   pData->bPersistent = bPersistent;
226   return true;
227 }
228 
DeleteGlobalVariable(ByteString sPropName)229 bool CFX_GlobalData::DeleteGlobalVariable(ByteString sPropName) {
230   if (!TrimPropName(&sPropName))
231     return false;
232 
233   auto iter = FindGlobalVariable(sPropName);
234   if (iter == m_arrayGlobalData.end())
235     return false;
236 
237   m_arrayGlobalData.erase(iter);
238   return true;
239 }
240 
GetSize() const241 int32_t CFX_GlobalData::GetSize() const {
242   return fxcrt::CollectionSize<int32_t>(m_arrayGlobalData);
243 }
244 
GetAt(int index)245 CFX_GlobalData::Element* CFX_GlobalData::GetAt(int index) {
246   if (index < 0 || index >= GetSize())
247     return nullptr;
248   return m_arrayGlobalData[index].get();
249 }
250 
LoadGlobalPersistentVariables()251 bool CFX_GlobalData::LoadGlobalPersistentVariables() {
252   if (!m_pDelegate)
253     return false;
254 
255   bool ret;
256   {
257     // Span can't outlive call to BufferDone().
258     absl::optional<pdfium::span<uint8_t>> buffer = m_pDelegate->LoadBuffer();
259     if (!buffer.has_value() || buffer.value().empty())
260       return false;
261 
262     ret = LoadGlobalPersistentVariablesFromBuffer(buffer.value());
263   }
264   m_pDelegate->BufferDone();
265   return ret;
266 }
267 
LoadGlobalPersistentVariablesFromBuffer(pdfium::span<uint8_t> buffer)268 bool CFX_GlobalData::LoadGlobalPersistentVariablesFromBuffer(
269     pdfium::span<uint8_t> buffer) {
270   if (buffer.size() < kMinGlobalDataBytes)
271     return false;
272 
273   CRYPT_ArcFourCryptBlock(buffer, kRC4KEY);
274 
275   uint8_t* p = buffer.data();
276   uint16_t wType = *((uint16_t*)p);
277   p += sizeof(uint16_t);
278   if (wType != kMagic)
279     return false;
280 
281   uint16_t wVersion = *((uint16_t*)p);
282   p += sizeof(uint16_t);
283   if (wVersion > kMaxVersion)
284     return false;
285 
286   uint32_t dwCount = *((uint32_t*)p);
287   p += sizeof(uint32_t);
288 
289   uint32_t dwSize = *((uint32_t*)p);
290   p += sizeof(uint32_t);
291 
292   if (dwSize != buffer.size() - sizeof(uint16_t) * 2 - sizeof(uint32_t) * 2)
293     return false;
294 
295   for (int32_t i = 0, sz = dwCount; i < sz; i++) {
296     if (p > buffer.end())
297       break;
298 
299     uint32_t dwNameLen = *((uint32_t*)p);
300     p += sizeof(uint32_t);
301     if (p + dwNameLen > buffer.end())
302       break;
303 
304     ByteString sEntry = ByteString(p, dwNameLen);
305     p += sizeof(char) * dwNameLen;
306 
307     CFX_Value::DataType wDataType =
308         static_cast<CFX_Value::DataType>(*((uint16_t*)p));
309     p += sizeof(uint16_t);
310 
311     switch (wDataType) {
312       case CFX_Value::DataType::kNumber: {
313         double dData = 0;
314         switch (wVersion) {
315           case 1: {
316             uint32_t dwData = *((uint32_t*)p);
317             p += sizeof(uint32_t);
318             dData = dwData;
319           } break;
320           case 2: {
321             dData = *((double*)p);
322             p += sizeof(double);
323           } break;
324         }
325         SetGlobalVariableNumber(sEntry, dData);
326         SetGlobalVariablePersistent(sEntry, true);
327       } break;
328       case CFX_Value::DataType::kBoolean: {
329         uint16_t wData = *((uint16_t*)p);
330         p += sizeof(uint16_t);
331         SetGlobalVariableBoolean(sEntry, (bool)(wData == 1));
332         SetGlobalVariablePersistent(sEntry, true);
333       } break;
334       case CFX_Value::DataType::kString: {
335         uint32_t dwLength = *((uint32_t*)p);
336         p += sizeof(uint32_t);
337         if (p + dwLength > buffer.end())
338           break;
339 
340         SetGlobalVariableString(sEntry, ByteString(p, dwLength));
341         SetGlobalVariablePersistent(sEntry, true);
342         p += sizeof(char) * dwLength;
343       } break;
344       case CFX_Value::DataType::kNull: {
345         SetGlobalVariableNull(sEntry);
346         SetGlobalVariablePersistent(sEntry, true);
347       } break;
348       case CFX_Value::DataType::kObject:
349       default:
350         // Arrays aren't allowed in these buffers, nor are unrecoginzed tags.
351         return false;
352     }
353   }
354   return true;
355 }
356 
SaveGlobalPersisitentVariables()357 bool CFX_GlobalData::SaveGlobalPersisitentVariables() {
358   if (!m_pDelegate)
359     return false;
360 
361   uint32_t nCount = 0;
362   BinaryBuffer sData;
363   for (const auto& pElement : m_arrayGlobalData) {
364     if (!pElement->bPersistent)
365       continue;
366 
367     BinaryBuffer sElement;
368     if (!MakeByteString(pElement->data.sKey, pElement->data, &sElement))
369       continue;
370 
371     if (sData.GetSize() + sElement.GetSize() > kMaxGlobalDataBytes)
372       break;
373 
374     sData.AppendSpan(sElement.GetSpan());
375     nCount++;
376   }
377 
378   BinaryBuffer sFile;
379   sFile.AppendUint16(kMagic);
380   sFile.AppendUint16(kMaxVersion);
381   sFile.AppendUint32(nCount);
382 
383   uint32_t dwSize = pdfium::base::checked_cast<uint32_t>(sData.GetSize());
384   sFile.AppendUint32(dwSize);
385   sFile.AppendSpan(sData.GetSpan());
386 
387   CRYPT_ArcFourCryptBlock(sFile.GetMutableSpan(), kRC4KEY);
388   return m_pDelegate->StoreBuffer(sFile.GetSpan());
389 }
390 
391 CFX_GlobalData::Element::Element() = default;
392 
393 CFX_GlobalData::Element::~Element() = default;
394