[Libreoffice-commits] core.git: xmlsecurity/source

Miklos Vajna vmiklos at collabora.co.uk
Tue Oct 25 17:53:50 UTC 2016


 xmlsecurity/source/pdfio/pdfdocument.cxx |  153 +++++++++++++++++++++++--------
 1 file changed, 115 insertions(+), 38 deletions(-)

New commits:
commit a3b3e336e5bd83754d9caae85dd5b9246bb4f7c1
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Tue Oct 25 18:08:27 2016 +0200

    xmlsecurity PDF sign: append reference to the Catalog's AcroForm key
    
    Similar to the Page object's Annots key, but here we want to append our
    reference to the nested AcroForm/Fields key, so that needs more
    infrastructure.
    
    This is also needed (but not enough) to be able to sign a PDF document
    multiple times.
    
    Change-Id: I4d5e2aa8f49d2181a15cbf7c4e27577fc98b547d
    Reviewed-on: https://gerrit.libreoffice.org/30267
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    Tested-by: Jenkins <ci at libreoffice.org>

diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 5778f20..dfd3938 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -74,6 +74,7 @@ public:
 };
 
 class PDFReferenceElement;
+class PDFDictionaryElement;
 
 /// Indirect object: something with a unique ID.
 class PDFObjectElement : public PDFElement
@@ -86,10 +87,7 @@ class PDFObjectElement : public PDFElement
     sal_uInt64 m_nDictionaryOffset;
     /// Length of the dictionary buffer till (before) the '<<' token.
     sal_uInt64 m_nDictionaryLength;
-    /// Position after the '/' token.
-    std::map<OString, sal_uInt64> m_aDictionaryKeyOffset;
-    /// Length of the dictionary key and value, till (before) the next token.
-    std::map<OString, sal_uInt64> m_aDictionaryKeyValueLength;
+    PDFDictionaryElement* m_pDictionaryElement;
 
 public:
     PDFObjectElement(PDFDocument& rDoc, double fObjectValue, double fGenerationValue);
@@ -102,24 +100,33 @@ public:
     sal_uInt64 GetDictionaryOffset();
     void SetDictionaryLength(sal_uInt64 nDictionaryLength);
     sal_uInt64 GetDictionaryLength();
-    void SetDictionaryKeyOffset(const OString& rKey, sal_uInt64 nOffset);
-    sal_uInt64 GetDictionaryKeyOffset(const OString& rKey) const;
-    void SetDictionaryKeyValueLength(const OString& rKey, sal_uInt64 nLength);
-    sal_uInt64 GetDictionaryKeyValueLength(const OString& rKey) const;
+    PDFDictionaryElement* GetDictionary() const;
+    void SetDictionary(PDFDictionaryElement* pDictionaryElement);
 };
 
 /// Dictionary object: a set key-value pairs.
 class PDFDictionaryElement : public PDFElement
 {
+    /// Key-value pairs when the dictionary is a nested value.
+    std::map<OString, PDFElement*> m_aItems;
     /// Offset after the '<<' token.
     sal_uInt64 m_nLocation;
+    /// Position after the '/' token.
+    std::map<OString, sal_uInt64> m_aDictionaryKeyOffset;
+    /// Length of the dictionary key and value, till (before) the next token.
+    std::map<OString, sal_uInt64> m_aDictionaryKeyValueLength;
 
 public:
     PDFDictionaryElement();
     bool Read(SvStream& rStream) override;
 
-    static void Parse(const std::vector< std::unique_ptr<PDFElement> >& rElements, PDFElement* pThis, std::map<OString, PDFElement*>& rDictionary);
+    static size_t Parse(const std::vector< std::unique_ptr<PDFElement> >& rElements, PDFElement* pThis, std::map<OString, PDFElement*>& rDictionary);
     static PDFElement* Lookup(const std::map<OString, PDFElement*>& rDictionary, const OString& rKey);
+    void SetKeyOffset(const OString& rKey, sal_uInt64 nOffset);
+    sal_uInt64 GetKeyOffset(const OString& rKey) const;
+    void SetKeyValueLength(const OString& rKey, sal_uInt64 nLength);
+    sal_uInt64 GetKeyValueLength(const OString& rKey) const;
+    const std::map<OString, PDFElement*>& GetItems() const;
 };
 
 /// End of a dictionary: '>>'.
@@ -396,9 +403,10 @@ bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& xCertificat
     else
     {
         // Annots key is already there, insert our reference at the end.
+        PDFDictionaryElement* pDictionary = pFirstPage->GetDictionary();
 
         // Offset right before the end of the Annots array.
-        sal_uInt64 nAnnotsEndOffset = pFirstPage->GetDictionaryKeyOffset("Annots") + pFirstPage->GetDictionaryKeyValueLength("Annots") - 1;
+        sal_uInt64 nAnnotsEndOffset = pDictionary->GetKeyOffset("Annots") + pDictionary->GetKeyValueLength("Annots") - 1;
         // Length of beginning of the dictionary -> Annots end.
         sal_uInt64 nAnnotsBeforeEndLength = nAnnotsEndOffset - pFirstPage->GetDictionaryOffset();
         m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pFirstPage->GetDictionaryOffset(), nAnnotsBeforeEndLength);
@@ -435,10 +443,44 @@ bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& xCertificat
     m_aEditBuffer.WriteUInt32AsString(nCatalogId);
     m_aEditBuffer.WriteCharPtr(" 0 obj\n");
     m_aEditBuffer.WriteCharPtr("<<");
-    m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pCatalog->GetDictionaryOffset(), pCatalog->GetDictionaryLength());
-    m_aEditBuffer.WriteCharPtr("/AcroForm<</Fields[\n");
-    m_aEditBuffer.WriteUInt32AsString(nAnnotId);
-    m_aEditBuffer.WriteCharPtr(" 0 R\n]/SigFlags 3>>\n");
+    auto pAcroForm = dynamic_cast<PDFDictionaryElement*>(pCatalog->Lookup("AcroForm"));
+    if (!pAcroForm)
+    {
+        // No AcroForm key, assume no signatures.
+        m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pCatalog->GetDictionaryOffset(), pCatalog->GetDictionaryLength());
+        m_aEditBuffer.WriteCharPtr("/AcroForm<</Fields[\n");
+        m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+        m_aEditBuffer.WriteCharPtr(" 0 R\n]/SigFlags 3>>\n");
+    }
+    else
+    {
+        // AcroForm key is already there, insert our reference at the Fields end.
+        auto it = pAcroForm->GetItems().find("Fields");
+        if (it == pAcroForm->GetItems().end())
+        {
+            SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: AcroForm without required Fields key");
+            return false;
+        }
+
+        auto pFields = dynamic_cast<PDFArrayElement*>(it->second);
+        if (!pFields)
+        {
+            SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: AcroForm Fields is not an array");
+            return false;
+        }
+
+        // Offset right before the end of the Fields array.
+        sal_uInt64 nFieldsEndOffset = pAcroForm->GetKeyOffset("Fields") + pAcroForm->GetKeyValueLength("Fields") - 1;
+        // Length of beginning of the Catalog dictionary -> Fields end.
+        sal_uInt64 nFieldsBeforeEndLength = nFieldsEndOffset - pCatalog->GetDictionaryOffset();
+        m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pCatalog->GetDictionaryOffset(), nFieldsBeforeEndLength);
+        m_aEditBuffer.WriteCharPtr(" ");
+        m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+        m_aEditBuffer.WriteCharPtr(" 0 R");
+        // Length of Fields end -> end of the Catalog dictionary.
+        sal_uInt64 nFieldsAfterEndLength = pCatalog->GetDictionaryOffset() + pCatalog->GetDictionaryLength() - nFieldsEndOffset;
+        m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + nFieldsEndOffset, nFieldsAfterEndLength);
+    }
     m_aEditBuffer.WriteCharPtr(">>\nendobj\n\n");
 
     // Write the xref table.
@@ -1542,7 +1584,8 @@ PDFObjectElement::PDFObjectElement(PDFDocument& rDoc, double fObjectValue, doubl
       m_fObjectValue(fObjectValue),
       m_fGenerationValue(fGenerationValue),
       m_nDictionaryOffset(0),
-      m_nDictionaryLength(0)
+      m_nDictionaryLength(0),
+      m_pDictionaryElement(nullptr)
 {
 }
 
@@ -1557,12 +1600,17 @@ PDFDictionaryElement::PDFDictionaryElement()
 {
 }
 
-void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement> >& rElements, PDFElement* pThis, std::map<OString, PDFElement*>& rDictionary)
+size_t PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement> >& rElements, PDFElement* pThis, std::map<OString, PDFElement*>& rDictionary)
 {
+    // The index of last parsed element, in case of nested dictionaries.
+    size_t nRet = 0;
+
     if (!rDictionary.empty())
-        return;
+        return nRet;
 
     auto pThisObject = dynamic_cast<PDFObjectElement*>(pThis);
+    // This is set to non-nullptr here for nested dictionaries only.
+    auto pThisDictionary = dynamic_cast<PDFDictionaryElement*>(pThis);
 
     // Find out where the dictionary for this object starts.
     size_t nIndex = 0;
@@ -1592,7 +1640,18 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
                 // First dictionary start, track start offset.
                 nDictionaryOffset = pDictionary->m_nLocation;
                 if (pThisObject)
+                {
+                    pThisObject->SetDictionary(pDictionary);
+                    pThisDictionary = pDictionary;
                     pThisObject->SetDictionaryOffset(nDictionaryOffset);
+                }
+            }
+            else
+            {
+                // Nested dictionary.
+                nIndex = PDFDictionaryElement::Parse(rElements, pDictionary, pDictionary->m_aItems);
+                rDictionary[aName] = pDictionary;
+                aName.clear();
             }
         }
 
@@ -1603,6 +1662,7 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
                 // Last dictionary end, track length and stop parsing.
                 if (pThisObject)
                     pThisObject->SetDictionaryLength(pEndDictionary->GetLocation() - nDictionaryOffset);
+                nRet = nIndex;
                 break;
             }
         }
@@ -1614,10 +1674,10 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
             {
                 PDFNumberElement* pNumber = aNumbers.back();
                 rDictionary[aName] = pNumber;
-                if (pThisObject)
+                if (pThisDictionary)
                 {
-                    pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
-                    pThisObject->SetDictionaryKeyValueLength(aName, pNumber->GetLocation() + pNumber->GetLength() - nNameOffset);
+                    pThisDictionary->SetKeyOffset(aName, nNameOffset);
+                    pThisDictionary->SetKeyValueLength(aName, pNumber->GetLocation() + pNumber->GetLength() - nNameOffset);
                 }
                 aName.clear();
                 aNumbers.clear();
@@ -1633,10 +1693,10 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
             {
                 // Name-name key-value.
                 rDictionary[aName] = pName;
-                if (pThisObject)
+                if (pThisDictionary)
                 {
-                    pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
-                    pThisObject->SetDictionaryKeyValueLength(aName, pName->GetLocation() + pName->GetLength() - nNameOffset);
+                    pThisDictionary->SetKeyOffset(aName, nNameOffset);
+                    pThisDictionary->SetKeyValueLength(aName, pName->GetLocation() + pName->GetLength() - nNameOffset);
                 }
                 aName.clear();
             }
@@ -1660,11 +1720,11 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
                 aNumbers.clear();
             }
             rDictionary[aName] = pArray;
-            if (pThisObject)
+            if (pThisDictionary)
             {
-                pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
+                pThisDictionary->SetKeyOffset(aName, nNameOffset);
                 // Include the ending ']' in the length of the key - (array)value pair length.
-                pThisObject->SetDictionaryKeyValueLength(aName, pEndArr->GetOffset() - nNameOffset + 1);
+                pThisDictionary->SetKeyValueLength(aName, pEndArr->GetOffset() - nNameOffset + 1);
             }
             aName.clear();
             pArray = nullptr;
@@ -1677,8 +1737,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
             if (!pArray)
             {
                 rDictionary[aName] = pReference;
-                if (pThisObject)
-                    pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
+                if (pThisDictionary)
+                    pThisDictionary->SetKeyOffset(aName, nNameOffset);
                 aName.clear();
             }
             else
@@ -1693,8 +1753,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
         if (pLiteralString)
         {
             rDictionary[aName] = pLiteralString;
-            if (pThisObject)
-                pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
+            if (pThisDictionary)
+                pThisDictionary->SetKeyOffset(aName, nNameOffset);
             aName.clear();
             continue;
         }
@@ -1705,8 +1765,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
             if (!pArray)
             {
                 rDictionary[aName] = pHexString;
-                if (pThisObject)
-                    pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
+                if (pThisDictionary)
+                    pThisDictionary->SetKeyOffset(aName, nNameOffset);
                 aName.clear();
             }
             else
@@ -1729,11 +1789,13 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
     if (!aNumbers.empty())
     {
         rDictionary[aName] = aNumbers.back();
-        if (pThisObject)
-            pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
+        if (pThisDictionary)
+            pThisDictionary->SetKeyOffset(aName, nNameOffset);
         aName.clear();
         aNumbers.clear();
     }
+
+    return nRet;
 }
 
 PDFElement* PDFDictionaryElement::Lookup(const std::map<OString, PDFElement*>& rDictionary, const OString& rKey)
@@ -1788,17 +1850,17 @@ sal_uInt64 PDFObjectElement::GetDictionaryOffset()
     return m_nDictionaryOffset;
 }
 
-void PDFObjectElement::SetDictionaryKeyOffset(const OString& rKey, sal_uInt64 nOffset)
+void PDFDictionaryElement::SetKeyOffset(const OString& rKey, sal_uInt64 nOffset)
 {
     m_aDictionaryKeyOffset[rKey] = nOffset;
 }
 
-void PDFObjectElement::SetDictionaryKeyValueLength(const OString& rKey, sal_uInt64 nLength)
+void PDFDictionaryElement::SetKeyValueLength(const OString& rKey, sal_uInt64 nLength)
 {
     m_aDictionaryKeyValueLength[rKey] = nLength;
 }
 
-sal_uInt64 PDFObjectElement::GetDictionaryKeyOffset(const OString& rKey) const
+sal_uInt64 PDFDictionaryElement::GetKeyOffset(const OString& rKey) const
 {
     auto it = m_aDictionaryKeyOffset.find(rKey);
     if (it == m_aDictionaryKeyOffset.end())
@@ -1807,7 +1869,7 @@ sal_uInt64 PDFObjectElement::GetDictionaryKeyOffset(const OString& rKey) const
     return it->second;
 }
 
-sal_uInt64 PDFObjectElement::GetDictionaryKeyValueLength(const OString& rKey) const
+sal_uInt64 PDFDictionaryElement::GetKeyValueLength(const OString& rKey) const
 {
     auto it = m_aDictionaryKeyValueLength.find(rKey);
     if (it == m_aDictionaryKeyValueLength.end())
@@ -1816,6 +1878,11 @@ sal_uInt64 PDFObjectElement::GetDictionaryKeyValueLength(const OString& rKey) co
     return it->second;
 }
 
+const std::map<OString, PDFElement*>& PDFDictionaryElement::GetItems() const
+{
+    return m_aItems;
+}
+
 void PDFObjectElement::SetDictionaryLength(sal_uInt64 nDictionaryLength)
 {
     m_nDictionaryLength = nDictionaryLength;
@@ -1829,6 +1896,16 @@ sal_uInt64 PDFObjectElement::GetDictionaryLength()
     return m_nDictionaryLength;
 }
 
+PDFDictionaryElement* PDFObjectElement::GetDictionary() const
+{
+    return m_pDictionaryElement;
+}
+
+void PDFObjectElement::SetDictionary(PDFDictionaryElement* pDictionaryElement)
+{
+    m_pDictionaryElement = pDictionaryElement;
+}
+
 PDFReferenceElement::PDFReferenceElement(PDFDocument& rDoc, int fObjectValue, int fGenerationValue)
     : m_rDoc(rDoc),
       m_fObjectValue(fObjectValue),


More information about the Libreoffice-commits mailing list