From 05bdc6739c6b29bcce41716000dc02d1271abd13 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Wed, 9 Nov 2016 09:50:31 +0100 Subject: xmlsecurity PDF sign: handle when Page object's Annots is an indirect array Normally it's a direct array, but it's OK to have it as a reference, and then the referenced object is an array. Change-Id: I191150632c2d8317ee6fd8c8169a90996298faa4 Reviewed-on: https://gerrit.libreoffice.org/30718 Tested-by: Jenkins Reviewed-by: Miklos Vajna --- xmlsecurity/source/pdfio/pdfdocument.cxx | 115 ++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 33 deletions(-) (limited to 'xmlsecurity') diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx index 2af2da8f164f..8c01cd94f44b 100644 --- a/xmlsecurity/source/pdfio/pdfdocument.cxx +++ b/xmlsecurity/source/pdfio/pdfdocument.cxx @@ -437,46 +437,95 @@ bool PDFDocument::Sign(const uno::Reference& xCertificat m_aEditBuffer.WriteCharPtr(" 0 R\n>>\n"); m_aEditBuffer.WriteCharPtr(">>\nendobj\n\n"); - // Write the updated first page object, references nAnnotId. - sal_uInt32 nFirstPageId = pFirstPage->GetObjectValue(); - if (nFirstPageId >= m_aXRef.size()) + PDFElement* pAnnots = pFirstPage->Lookup("Annots"); + auto pAnnotsReference = dynamic_cast(pAnnots); + if (pAnnotsReference) { - SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: invalid first page obj id"); - return false; - } - m_aXRef[nFirstPageId].m_nOffset = m_aEditBuffer.Tell(); - m_aXRef[nFirstPageId].m_bDirty = true; - m_aEditBuffer.WriteUInt32AsString(nFirstPageId); - m_aEditBuffer.WriteCharPtr(" 0 obj\n"); - m_aEditBuffer.WriteCharPtr("<<"); - auto pAnnots = dynamic_cast(pFirstPage->Lookup("Annots")); - if (!pAnnots) - { - // No Annots key, just write the key with a single reference. - m_aEditBuffer.WriteBytes(static_cast(m_aEditBuffer.GetData()) + pFirstPage->GetDictionaryOffset(), pFirstPage->GetDictionaryLength()); - m_aEditBuffer.WriteCharPtr("/Annots["); + // Write the updated Annots key of the Page object. + PDFObjectElement* pAnnotsObject = pAnnotsReference->LookupObject(); + if (!pAnnotsObject) + { + SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: invalid Annots reference"); + return false; + } + + sal_uInt32 nAnnotsId = pAnnotsObject->GetObjectValue(); + m_aXRef[nAnnotsId].m_eType = XRefEntryType::NOT_COMPRESSED; + m_aXRef[nAnnotsId].m_nOffset = m_aEditBuffer.Tell(); + m_aXRef[nAnnotsId].m_nGenerationNumber = 0; + m_aXRef[nAnnotsId].m_bDirty = true; + m_aEditBuffer.WriteUInt32AsString(nAnnotsId); + m_aEditBuffer.WriteCharPtr(" 0 obj\n["); + + // Write existing references. + PDFArrayElement* pArray = pAnnotsObject->GetArray(); + if (!pArray) + { + SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: Page Annots is a reference to a non-array"); + return false; + } + + for (size_t i = 0; i < pArray->GetElements().size(); ++i) + { + auto pReference = dynamic_cast(pArray->GetElements()[i]); + if (!pReference) + continue; + + if (i) + m_aEditBuffer.WriteCharPtr(" "); + m_aEditBuffer.WriteUInt32AsString(pReference->GetObjectValue()); + m_aEditBuffer.WriteCharPtr(" 0 R"); + } + // Write our reference. + m_aEditBuffer.WriteCharPtr(" "); m_aEditBuffer.WriteUInt32AsString(nAnnotId); - m_aEditBuffer.WriteCharPtr(" 0 R]"); + m_aEditBuffer.WriteCharPtr(" 0 R"); + + m_aEditBuffer.WriteCharPtr("]\nendobj\n\n"); } else { - // Annots key is already there, insert our reference at the end. - PDFDictionaryElement* pDictionary = pFirstPage->GetDictionary(); + // Write the updated first page object, references nAnnotId. + sal_uInt32 nFirstPageId = pFirstPage->GetObjectValue(); + if (nFirstPageId >= m_aXRef.size()) + { + SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: invalid first page obj id"); + return false; + } + m_aXRef[nFirstPageId].m_nOffset = m_aEditBuffer.Tell(); + m_aXRef[nFirstPageId].m_bDirty = true; + m_aEditBuffer.WriteUInt32AsString(nFirstPageId); + m_aEditBuffer.WriteCharPtr(" 0 obj\n"); + m_aEditBuffer.WriteCharPtr("<<"); + auto pAnnotsArray = dynamic_cast(pAnnots); + if (!pAnnotsArray) + { + // No Annots key, just write the key with a single reference. + m_aEditBuffer.WriteBytes(static_cast(m_aEditBuffer.GetData()) + pFirstPage->GetDictionaryOffset(), pFirstPage->GetDictionaryLength()); + m_aEditBuffer.WriteCharPtr("/Annots["); + m_aEditBuffer.WriteUInt32AsString(nAnnotId); + m_aEditBuffer.WriteCharPtr(" 0 R]"); + } + 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 = 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(m_aEditBuffer.GetData()) + pFirstPage->GetDictionaryOffset(), nAnnotsBeforeEndLength); - m_aEditBuffer.WriteCharPtr(" "); - m_aEditBuffer.WriteUInt32AsString(nAnnotId); - m_aEditBuffer.WriteCharPtr(" 0 R"); - // Length of Annots end -> end of the dictionary. - sal_uInt64 nAnnotsAfterEndLength = pFirstPage->GetDictionaryOffset() + pFirstPage->GetDictionaryLength() - nAnnotsEndOffset; - m_aEditBuffer.WriteBytes(static_cast(m_aEditBuffer.GetData()) + nAnnotsEndOffset, nAnnotsAfterEndLength); + // Offset right before the end of the Annots array. + 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(m_aEditBuffer.GetData()) + pFirstPage->GetDictionaryOffset(), nAnnotsBeforeEndLength); + m_aEditBuffer.WriteCharPtr(" "); + m_aEditBuffer.WriteUInt32AsString(nAnnotId); + m_aEditBuffer.WriteCharPtr(" 0 R"); + // Length of Annots end -> end of the dictionary. + sal_uInt64 nAnnotsAfterEndLength = pFirstPage->GetDictionaryOffset() + pFirstPage->GetDictionaryLength() - nAnnotsEndOffset; + m_aEditBuffer.WriteBytes(static_cast(m_aEditBuffer.GetData()) + nAnnotsEndOffset, nAnnotsAfterEndLength); + } + m_aEditBuffer.WriteCharPtr(">>"); + m_aEditBuffer.WriteCharPtr("\nendobj\n\n"); } - m_aEditBuffer.WriteCharPtr(">>"); - m_aEditBuffer.WriteCharPtr("\nendobj\n\n"); // Write the updated Catalog object, references nAnnotId. PDFReferenceElement* pRoot = nullptr; -- cgit