Rebased ref, commits from common ancestor:
commit 3ac43088158476d32701d00e7d517440b128c1a4
Author:     Samuel Mehrbrodt <samuel.mehrbr...@cib.de>
AuthorDate: Tue Sep 29 09:22:04 2020 +0200
Commit:     Samuel Mehrbrodt <samuel.mehrbr...@cib.de>
CommitDate: Thu Oct 1 10:45:07 2020 +0200

    Release 5.4.11
    
    Change-Id: I94f4cb91b1cf92722ff43d3561ba0cf2405a6a29

diff --git a/configure.ac b/configure.ac
index 83fe089baf59..af7fc01b8195 100644
--- a/configure.ac
+++ b/configure.ac
@@ -9,7 +9,7 @@ dnl in order to create a configure script.
 # several non-alphanumeric characters, those are split off and used only for 
the
 # ABOUTBOXPRODUCTVERSIONSUFFIX in openoffice.lst. Why that is necessary, no 
idea.
 
-AC_INIT([LibreOffice],[5.4.10.0],[],[],[http://documentfoundation.org/])
+AC_INIT([LibreOffice],[5.4.11.0],[],[],[http://documentfoundation.org/])
 
 AC_PREREQ([2.59])
 
commit fba3e52b85c2d4d4a5e2f7394c4fe6f3a70405b9
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Sep 4 17:17:48 2020 +0200
Commit:     Samuel Mehrbrodt <samuel.mehrbr...@cib.de>
CommitDate: Thu Oct 1 10:45:03 2020 +0200

    xmlsecurity: pdf incremental updates that are non-commenting are invalid
    
    I.e. it's OK to add incremental updates for annotation/commenting
    purposes and that doesn't invalite existing signatures. Everything else
    does.
    
    (cherry picked from commit 61834cd574568613f0b0a2ee099a60fa5a8d9804)
    
    Conflicts:
            include/vcl/filter/PDFiumLibrary.hxx
            vcl/source/pdf/PDFiumLibrary.cxx
    
    Conflicts:
            xmlsecurity/qa/unit/signing/signing.cxx
    
    Change-Id: I4607c242b3c6f6b01517b02407e9e7a095e2e069

diff --git a/include/tools/stream.hxx b/include/tools/stream.hxx
index 0bc3766807fa..608f7f0adde0 100644
--- a/include/tools/stream.hxx
+++ b/include/tools/stream.hxx
@@ -257,6 +257,7 @@ public:
     SvStream&       WriteOString(const OString& rStr)
                         { return WriteCharPtr(rStr.getStr()); }
     SvStream&       WriteStream( SvStream& rStream );
+    sal_uInt64      WriteStream( SvStream& rStream, sal_uInt64 nSize );
 
     SvStream&       WriteBool( bool b )
                         { return WriteUChar(static_cast<unsigned char>(b)); }
diff --git a/include/vcl/filter/PDFiumLibrary.hxx 
b/include/vcl/filter/PDFiumLibrary.hxx
index b9bceabb8acf..ffc70874c19b 100644
--- a/include/vcl/filter/PDFiumLibrary.hxx
+++ b/include/vcl/filter/PDFiumLibrary.hxx
@@ -17,11 +17,16 @@
 #include <memory>
 #include <rtl/instance.hxx>
 #include <vcl/dllapi.h>
+#include <vcl/checksum.hxx>
+
+#include <fpdf_doc.h>
 
 namespace vcl
 {
 namespace pdf
 {
+class PDFiumDocument;
+
 class VCL_DLLPUBLIC PDFium final
 {
 private:
@@ -33,6 +38,49 @@ public:
     ~PDFium();
 };
 
+class VCL_DLLPUBLIC PDFiumPage final
+{
+private:
+    FPDF_PAGE mpPage;
+
+private:
+    PDFiumPage(const PDFiumPage&) = delete;
+    PDFiumPage& operator=(const PDFiumPage&) = delete;
+
+public:
+    PDFiumPage(FPDF_PAGE pPage)
+        : mpPage(pPage)
+    {
+    }
+
+    ~PDFiumPage()
+    {
+        if (mpPage)
+            FPDF_ClosePage(mpPage);
+    }
+
+    /// Get bitmap checksum of the page, without annotations/commenting.
+    BitmapChecksum getChecksum();
+};
+
+class VCL_DLLPUBLIC PDFiumDocument final
+{
+private:
+    FPDF_DOCUMENT mpPdfDocument;
+
+private:
+    PDFiumDocument(const PDFiumDocument&) = delete;
+    PDFiumDocument& operator=(const PDFiumDocument&) = delete;
+
+public:
+    PDFiumDocument(FPDF_DOCUMENT pPdfDocument);
+    ~PDFiumDocument();
+
+    int getPageCount();
+
+    std::unique_ptr<PDFiumPage> openPage(int nIndex);
+};
+
 struct PDFiumLibrary : public rtl::StaticWithInit<std::shared_ptr<PDFium>, 
PDFiumLibrary>
 {
     std::shared_ptr<PDFium> operator()() { return std::make_shared<PDFium>(); }
diff --git a/tools/source/stream/stream.cxx b/tools/source/stream/stream.cxx
index 488348719892..b83729e35fbf 100644
--- a/tools/source/stream/stream.cxx
+++ b/tools/source/stream/stream.cxx
@@ -1176,6 +1176,27 @@ SvStream& SvStream::WriteStream( SvStream& rStream )
     return *this;
 }
 
+sal_uInt64 SvStream::WriteStream( SvStream& rStream, sal_uInt64 nSize )
+{
+    const sal_uInt32 cBufLen = 0x8000;
+    std::unique_ptr<char[]> pBuf( new char[ cBufLen ] );
+    sal_uInt32 nCurBufLen = cBufLen;
+    sal_uInt32 nCount;
+    sal_uInt64 nWriteSize = nSize;
+
+    do {
+        if ( nSize >= nCurBufLen )
+            nWriteSize -= nCurBufLen;
+        else
+            nCurBufLen = nWriteSize;
+        nCount = rStream.ReadBytes( pBuf.get(), nCurBufLen );
+        WriteBytes( pBuf.get(), nCount );
+    }
+    while( nWriteSize && nCount == nCurBufLen );
+
+    return nSize - nWriteSize;
+}
+
 OUString SvStream::ReadUniOrByteString( rtl_TextEncoding eSrcCharSet )
 {
     // read UTF-16 string directly from stream ?
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
index 5f487b15f48b..38eb88a99db0 100644
--- a/vcl/source/pdf/PDFiumLibrary.cxx
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -15,6 +15,10 @@
 #include <vcl/filter/PDFiumLibrary.hxx>
 #include <fpdf_doc.h>
 
+#include <o3tl/make_unique.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapaccess.hxx>
+
 namespace vcl
 {
 namespace pdf
@@ -31,6 +35,57 @@ PDFium::PDFium()
 
 PDFium::~PDFium() { FPDF_DestroyLibrary(); }
 
+PDFiumDocument::PDFiumDocument(FPDF_DOCUMENT pPdfDocument)
+    : mpPdfDocument(pPdfDocument)
+{
+}
+
+PDFiumDocument::~PDFiumDocument()
+{
+    if (mpPdfDocument)
+        FPDF_CloseDocument(mpPdfDocument);
+}
+
+std::unique_ptr<PDFiumPage> PDFiumDocument::openPage(int nIndex)
+{
+    std::unique_ptr<PDFiumPage> pPDFiumPage;
+    FPDF_PAGE pPage = FPDF_LoadPage(mpPdfDocument, nIndex);
+    if (pPage)
+    {
+        pPDFiumPage = o3tl::make_unique<PDFiumPage>(pPage);
+    }
+    return pPDFiumPage;
+}
+
+int PDFiumDocument::getPageCount() { return FPDF_GetPageCount(mpPdfDocument); }
+
+BitmapChecksum PDFiumPage::getChecksum()
+{
+    size_t nPageWidth = FPDF_GetPageWidth(mpPage);
+    size_t nPageHeight = FPDF_GetPageHeight(mpPage);
+    FPDF_BITMAP pPdfBitmap = FPDFBitmap_Create(nPageWidth, nPageHeight, 
/*alpha=*/1);
+    if (!pPdfBitmap)
+    {
+        return 0;
+    }
+
+    // Intentionally not using FPDF_ANNOT here, annotations/commenting is OK 
to not affect the
+    // checksum, signature verification wants this.
+    FPDF_RenderPageBitmap(pPdfBitmap, mpPage, /*start_x=*/0, /*start_y=*/0, 
nPageWidth, nPageHeight,
+                          /*rotate=*/0, /*flags=*/0);
+    Bitmap aBitmap(Size(nPageWidth, nPageHeight), 24);
+    {
+        Bitmap::ScopedWriteAccess pWriteAccess(aBitmap);
+        const auto pPdfBuffer = static_cast<const 
sal_uInt8*>(FPDFBitmap_GetBuffer(pPdfBitmap));
+        const int nStride = FPDFBitmap_GetStride(pPdfBitmap);
+        for (size_t nRow = 0; nRow < nPageHeight; ++nRow)
+        {
+            const sal_uInt8* pPdfLine = pPdfBuffer + (nStride * nRow);
+            pWriteAccess->CopyScanline(nRow, pPdfLine, 
ScanlineFormat::N32BitTcBgra, nStride);
+        }
+    }
+    return aBitmap.GetChecksum();
+}
 }
 } // end vcl::pdf
 
diff --git a/xmlsecurity/Library_xmlsecurity.mk 
b/xmlsecurity/Library_xmlsecurity.mk
index 77d3bd81dc3b..85950d1dcd4b 100644
--- a/xmlsecurity/Library_xmlsecurity.mk
+++ b/xmlsecurity/Library_xmlsecurity.mk
@@ -20,7 +20,10 @@ $(eval $(call gb_Library_add_defs,xmlsecurity,\
     -DXMLSECURITY_DLLIMPLEMENTATION \
 ))
 
-$(eval $(call gb_Library_use_externals,xmlsecurity,boost_headers))
+$(eval $(call gb_Library_use_externals,xmlsecurity,\
+       boost_headers \
+       $(if $(filter PDFIUM,$(BUILD_TYPE)),pdfium) \
+))
 
 $(eval $(call 
gb_Library_set_precompiled_header,xmlsecurity,$(SRCDIR)/xmlsecurity/inc/pch/precompiled_xmlsecurity))
 
diff --git 
a/xmlsecurity/qa/unit/signing/data/hide-and-replace-shadow-file-signed-2.pdf 
b/xmlsecurity/qa/unit/signing/data/hide-and-replace-shadow-file-signed-2.pdf
new file mode 100644
index 000000000000..f2b1a71096b2
Binary files /dev/null and 
b/xmlsecurity/qa/unit/signing/data/hide-and-replace-shadow-file-signed-2.pdf 
differ
diff --git a/xmlsecurity/qa/unit/signing/signing.cxx 
b/xmlsecurity/qa/unit/signing/signing.cxx
index 2b6e60e7c0bd..29e35738c62b 100644
--- a/xmlsecurity/qa/unit/signing/signing.cxx
+++ b/xmlsecurity/qa/unit/signing/signing.cxx
@@ -88,6 +88,8 @@ public:
     void testPDFGood();
     /// Test a typical PDF where the signature is bad.
     void testPDFBad();
+    /// Test a maliciously manipulated signed pdf
+    void testPDFHideAndReplace();
     /// Test a typical PDF which is not signed.
     void testPDFNo();
 #endif
@@ -113,6 +115,7 @@ public:
 #if HAVE_FEATURE_PDFIMPORT
     CPPUNIT_TEST(testPDFGood);
     CPPUNIT_TEST(testPDFBad);
+    CPPUNIT_TEST(testPDFHideAndReplace);
     CPPUNIT_TEST(testPDFNo);
 #endif
     CPPUNIT_TEST(test96097Calc);
@@ -458,6 +461,22 @@ void SigningTest::testPDFBad()
     CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN), 
static_cast<int>(pObjectShell->GetDocumentSignatureState()));
 }
 
+void SigningTest::testPDFHideAndReplace()
+{
+    createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY)
+              + "hide-and-replace-shadow-file-signed-2.pdf");
+    SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
+    CPPUNIT_ASSERT(pBaseModel);
+    SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
+    CPPUNIT_ASSERT(pObjectShell);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 2 (BROKEN)
+    // - Actual  : 6 (NOTVALIDATED_PARTIAL_OK)
+    // i.e. a non-commenting update after a signature was not marked as 
invalid.
+    CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
+                         
static_cast<int>(pObjectShell->GetDocumentSignatureState()));
+}
+
 void SigningTest::testPDFNo()
 {
     createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "no.pdf");
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx 
b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 5cec868a012b..edcb72d9a9ad 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -21,11 +21,15 @@
 #include <filter/msfilter/mscodec.hxx>
 #include <rtl/character.hxx>
 #include <rtl/strbuf.hxx>
+#include <config_features.h>
+
+#include <vcl/filter/PDFiumLibrary.hxx>
 #include <rtl/string.hxx>
 #include <sal/log.hxx>
 #include <sal/types.h>
 #include <sax/tools/converter.hxx>
 #include <tools/zcodec.hxx>
+#include <tools/stream.hxx>
 #include <unotools/calendarwrapper.hxx>
 #include <unotools/datetime.hxx>
 #include <vcl/pdfwriter.hxx>
@@ -49,6 +53,8 @@
 #include <comphelper/windowserrorstring.hxx>
 #endif
 
+#include <vcl/bitmap.hxx>
+
 using namespace com::sun::star;
 
 namespace
@@ -392,6 +398,66 @@ bool VerifyNonDetachedSignature(SvStream& rStream, 
std::vector<std::pair<size_t,
     return false;
 }
 #endif
+
+/// Collects the checksum of each page of one version of the PDF.
+void AnalyizeSignatureStream(SvMemoryStream& rStream, 
std::vector<BitmapChecksum>& rPageChecksums)
+{
+#if HAVE_FEATURE_PDFIUM
+    auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+    vcl::pdf::PDFiumDocument aPdfDocument(
+        FPDF_LoadMemDocument(rStream.GetData(), rStream.GetSize(), 
/*password=*/nullptr));
+
+    int nPageCount = aPdfDocument.getPageCount();
+    for (int nPage = 0; nPage < nPageCount; ++nPage)
+    {
+        std::unique_ptr<vcl::pdf::PDFiumPage> 
pPdfPage(aPdfDocument.openPage(nPage));
+        if (!pPdfPage)
+        {
+            return;
+        }
+
+        BitmapChecksum nPageChecksum = pPdfPage->getChecksum();
+        rPageChecksums.push_back(nPageChecksum);
+    }
+#else
+    (void)rStream;
+#endif
+}
+
+/**
+ * Checks if incremental updates after singing performed valid modifications 
only.
+ * Annotations/commenting is OK, other changes are not.
+ */
+bool IsValidSignature(SvStream& rStream, vcl::filter::PDFObjectElement* 
pSignature)
+{
+    size_t nSignatureEOF = 0;
+    if (!GetEOFOfSignature(pSignature, nSignatureEOF))
+    {
+        return false;
+    }
+
+    SvMemoryStream aSignatureStream;
+    sal_uInt64 nPos = rStream.Tell();
+    rStream.Seek(0);
+    aSignatureStream.WriteStream(rStream, nSignatureEOF);
+    rStream.Seek(nPos);
+    aSignatureStream.Seek(0);
+    std::vector<BitmapChecksum> aSignedPages;
+    AnalyizeSignatureStream(aSignatureStream, aSignedPages);
+
+    SvMemoryStream aFullStream;
+    nPos = rStream.Tell();
+    rStream.Seek(0);
+    aFullStream.WriteStream(rStream);
+    rStream.Seek(nPos);
+    aFullStream.Seek(0);
+    std::vector<BitmapChecksum> aAllPages;
+    AnalyizeSignatureStream(aFullStream, aAllPages);
+
+    // Fail if any page looks different after signing and at the end. 
Annotations/commenting doesn't
+    // count, though.
+    return aSignedPages == aAllPages;
+}
 }
 
 bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* 
pSignature,
@@ -499,6 +565,12 @@ bool ValidateSignature(SvStream& rStream, 
vcl::filter::PDFObjectElement* pSignat
     }
     rInformation.bPartialDocumentSignature = !IsCompleteSignature(rStream, 
rDocument, pSignature);
 
+    if (!IsValidSignature(rStream, pSignature))
+    {
+        SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: invalid incremental 
update detected");
+        return false;
+    }
+
     // At this point there is no obviously missing info to validate the
     // signature.
     std::vector<unsigned char> aSignature = 
vcl::filter::PDFDocument::DecodeHexString(pContents);
diff --git a/xmlsecurity/workben/pdfverify.cxx 
b/xmlsecurity/workben/pdfverify.cxx
index ea48350246a6..d0b5405b015b 100644
--- a/xmlsecurity/workben/pdfverify.cxx
+++ b/xmlsecurity/workben/pdfverify.cxx
@@ -20,6 +20,7 @@
 #include <vcl/pngwrite.hxx>
 #include <vcl/svapp.hxx>
 #include <vcl/graphicfilter.hxx>
+#include <comphelper/scopeguard.hxx>
 
 #include <xmlsecurity/pdfio/pdfdocument.hxx>
 
@@ -72,11 +73,11 @@ int pdfVerify(int nArgc, char** pArgv)
     uno::Reference<lang::XMultiServiceFactory> 
xMultiServiceFactory(xMultiComponentFactory, uno::UNO_QUERY);
     comphelper::setProcessServiceFactory(xMultiServiceFactory);
 
+    InitVCL();
+    comphelper::ScopeGuard g([] { DeInitVCL(); });
     if (nArgc > 3 && OString(pArgv[3]) == "-p")
     {
-        InitVCL();
         generatePreview(pArgv[1], pArgv[2]);
-        DeInitVCL();
         return 0;
     }
 
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to