summaryrefslogtreecommitdiff
path: root/xmlsecurity/source
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2020-07-24 11:29:27 +0200
committerMiklos Vajna <vmiklos@collabora.com>2020-07-27 23:28:22 +0200
commite1c1f1332942fc4f15122cf5e894a64da8e1be0e (patch)
tree9de6bc5a3d88522a3c81386323e4428f92efe00b /xmlsecurity/source
parent31b595c0785c6991c55f8aa647efb2eab27fb0b8 (diff)
xmlsecurity: detect unsigned incremental update between signatures
(cherry picked from commit 7468d5df5ec79783eae84b62bdc5ecf12f0ca255) Conflicts: vcl/source/filter/ipdf/pdfdocument.cxx xmlsecurity/source/pdfio/pdfdocument.cxx Change-Id: I269ed858852ee7d1275adf340c8cc1565fc30693 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/99480 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com> Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Diffstat (limited to 'xmlsecurity/source')
-rw-r--r--xmlsecurity/source/helper/pdfsignaturehelper.cxx3
-rw-r--r--xmlsecurity/source/pdfio/pdfdocument.cxx140
2 files changed, 117 insertions, 26 deletions
diff --git a/xmlsecurity/source/helper/pdfsignaturehelper.cxx b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
index 1f11b7856f4a..0398acac7ea0 100644
--- a/xmlsecurity/source/helper/pdfsignaturehelper.cxx
+++ b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
@@ -57,8 +57,7 @@ bool PDFSignatureHelper::ReadAndVerifySignature(
{
SignatureInformation aInfo(i);
- bool bLast = i == aSignatures.size() - 1;
- if (!xmlsecurity::pdfio::ValidateSignature(*pStream, aSignatures[i], aInfo, bLast))
+ if (!xmlsecurity::pdfio::ValidateSignature(*pStream, aSignatures[i], aInfo, aDocument))
SAL_WARN("xmlsecurity.helper", "failed to determine digest match");
m_aSignatureInfos.push_back(aInfo);
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index febcd2e3a1ea..7cf2c137c1c4 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -23,12 +23,124 @@
using namespace com::sun::star;
+namespace
+{
+/// Turns an array of floats into offset + length pairs.
+bool GetByteRangesFromPDF(vcl::filter::PDFArrayElement& rArray,
+ std::vector<std::pair<size_t, size_t>>& rByteRanges)
+{
+ size_t nByteRangeOffset = 0;
+ const std::vector<vcl::filter::PDFElement*>& rByteRangeElements = rArray.GetElements();
+ for (size_t i = 0; i < rByteRangeElements.size(); ++i)
+ {
+ auto pNumber = dynamic_cast<vcl::filter::PDFNumberElement*>(rByteRangeElements[i]);
+ if (!pNumber)
+ {
+ SAL_WARN("xmlsecurity.pdfio",
+ "ValidateSignature: signature offset and length has to be a number");
+ return false;
+ }
+
+ if (i % 2 == 0)
+ {
+ nByteRangeOffset = pNumber->GetValue();
+ continue;
+ }
+ size_t nByteRangeLength = pNumber->GetValue();
+ rByteRanges.emplace_back(nByteRangeOffset, nByteRangeLength);
+ }
+
+ return true;
+}
+
+/// Determines the last position that is covered by a signature.
+bool GetEOFOfSignature(vcl::filter::PDFObjectElement* pSignature, size_t& rEOF)
+{
+ vcl::filter::PDFObjectElement* pValue = pSignature->LookupObject("V");
+ if (!pValue)
+ {
+ return false;
+ }
+
+ auto pByteRange = dynamic_cast<vcl::filter::PDFArrayElement*>(pValue->Lookup("ByteRange"));
+ if (!pByteRange || pByteRange->GetElements().size() < 2)
+ {
+ return false;
+ }
+
+ std::vector<std::pair<size_t, size_t>> aByteRanges;
+ if (!GetByteRangesFromPDF(*pByteRange, aByteRanges))
+ {
+ return false;
+ }
+
+ rEOF = aByteRanges[1].first + aByteRanges[1].second;
+ return true;
+}
+
+/// Checks if there are unsigned incremental updates between the signatures or after the last one.
+bool IsCompleteSignature(SvStream& rStream, vcl::filter::PDFDocument& rDocument,
+ vcl::filter::PDFObjectElement* pSignature)
+{
+ std::set<size_t> aSignedEOFs;
+ for (const auto& i : rDocument.GetSignatureWidgets())
+ {
+ size_t nEOF = 0;
+ if (!GetEOFOfSignature(i, nEOF))
+ {
+ return false;
+ }
+
+ aSignedEOFs.insert(nEOF);
+ }
+
+ size_t nSignatureEOF = 0;
+ if (!GetEOFOfSignature(pSignature, nSignatureEOF))
+ {
+ return false;
+ }
+
+ const std::vector<size_t>& rAllEOFs = rDocument.GetEOFs();
+ bool bFoundOwn = false;
+ for (const auto& rEOF : rAllEOFs)
+ {
+ if (rEOF == nSignatureEOF)
+ {
+ bFoundOwn = true;
+ continue;
+ }
+
+ if (!bFoundOwn)
+ {
+ continue;
+ }
+
+ if (aSignedEOFs.find(rEOF) == aSignedEOFs.end())
+ {
+ // Unsigned incremental update found.
+ return false;
+ }
+ }
+
+ // Make sure we find the incremental update of the signature itself.
+ if (!bFoundOwn)
+ {
+ return false;
+ }
+
+ // No additional content after the last incremental update.
+ rStream.Seek(STREAM_SEEK_TO_END);
+ size_t nFileEnd = rStream.Tell();
+ return std::find(rAllEOFs.begin(), rAllEOFs.end(), nFileEnd) != rAllEOFs.end();
+}
+}
+
namespace xmlsecurity
{
namespace pdfio
{
bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignature,
- SignatureInformation& rInformation, bool bLast)
+ SignatureInformation& rInformation, vcl::filter::PDFDocument& rDocument)
{
vcl::filter::PDFObjectElement* pValue = pSignature->LookupObject("V");
if (!pValue)
@@ -110,25 +222,9 @@ bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignat
// Build a list of offset-length pairs, representing the signed bytes.
std::vector<std::pair<size_t, size_t>> aByteRanges;
- size_t nByteRangeOffset = 0;
- const std::vector<vcl::filter::PDFElement*>& rByteRangeElements = pByteRange->GetElements();
- for (size_t i = 0; i < rByteRangeElements.size(); ++i)
+ if (!GetByteRangesFromPDF(*pByteRange, aByteRanges))
{
- auto pNumber = dynamic_cast<vcl::filter::PDFNumberElement*>(rByteRangeElements[i]);
- if (!pNumber)
- {
- SAL_WARN("xmlsecurity.pdfio",
- "ValidateSignature: signature offset and length has to be a number");
- return false;
- }
-
- if (i % 2 == 0)
- {
- nByteRangeOffset = pNumber->GetValue();
- continue;
- }
- size_t nByteRangeLength = pNumber->GetValue();
- aByteRanges.emplace_back(nByteRangeOffset, nByteRangeLength);
+ return false;
}
// Detect if the byte ranges don't cover everything, but the signature itself.
@@ -150,11 +246,7 @@ bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignat
"ValidateSignature: second range start is not the end of the signature");
return false;
}
- rStream.Seek(STREAM_SEEK_TO_END);
- size_t nFileEnd = rStream.Tell();
- if (bLast && (aByteRanges[1].first + aByteRanges[1].second) != nFileEnd)
- // Second range end is not the end of the file.
- rInformation.bPartialDocumentSignature = true;
+ rInformation.bPartialDocumentSignature = !IsCompleteSignature(rStream, rDocument, pSignature);
// At this point there is no obviously missing info to validate the
// signature.