diff options
author | Miklos Vajna <vmiklos@collabora.co.uk> | 2016-10-21 16:00:58 +0200 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.co.uk> | 2016-10-21 16:19:49 +0000 |
commit | 5819448023377b81ac785dc31ccf8333562ee51f (patch) | |
tree | 84b9b5af386e13cb9c092a45f0e5f550aa4c84c2 | |
parent | 98d9bb5eb266ff4f897adbe4d0c0a49ff9b4c7bb (diff) |
xmlsecurity: implement removal of a signature in pdfverify
It's not exactly clear how one should guess what was file end before
signing, for now assume the followings:
- the file ended with a %%EOF, an optional \r, and a \n
- the number of incremental updates is the same as the number of
signatures
When the later is not the case, don't attempt to remove the signature.
Change-Id: I203a7b0605fc061ec6aacfde3a8eedc4736379f2
Reviewed-on: https://gerrit.libreoffice.org/30140
Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
Tested-by: Jenkins <ci@libreoffice.org>
-rw-r--r-- | xmlsecurity/inc/pdfio/pdfdocument.hxx | 6 | ||||
-rw-r--r-- | xmlsecurity/source/pdfio/pdfdocument.cxx | 42 | ||||
-rw-r--r-- | xmlsecurity/source/pdfio/pdfverify.cxx | 35 |
3 files changed, 80 insertions, 3 deletions
diff --git a/xmlsecurity/inc/pdfio/pdfdocument.hxx b/xmlsecurity/inc/pdfio/pdfdocument.hxx index 816904f9f61f..b02bf9322088 100644 --- a/xmlsecurity/inc/pdfio/pdfdocument.hxx +++ b/xmlsecurity/inc/pdfio/pdfdocument.hxx @@ -46,6 +46,8 @@ class XMLSECURITY_DLLPUBLIC PDFDocument std::vector<size_t> m_aXRef; /// List of xref offsets we know. std::vector<size_t> m_aStartXRefs; + /// List of EOF offsets we know. + std::vector<size_t> m_aEOFs; PDFTrailerElement* m_pTrailer; /// All editing takes place in this buffer, if it happens. SvMemoryStream m_aEditBuffer; @@ -65,6 +67,8 @@ public: size_t GetObjectOffset(size_t nIndex) const; const std::vector< std::unique_ptr<PDFElement> >& GetElements(); std::vector<PDFObjectElement*> GetPages(); + /// Remember the end location of an EOF token. + void PushBackEOF(size_t nOffset); bool Read(SvStream& rStream); /// Sign the read document with xCertificate in the edit buffer. @@ -74,6 +78,8 @@ public: std::vector<PDFObjectElement*> GetSignatureWidgets(); /// Return value is about if we can determine a result, rInformation is about the actual result. static bool ValidateSignature(SvStream& rStream, PDFObjectElement* pSignature, SignatureInformation& rInformation); + /// Remove the nth signature from read document in the edit buffer. + bool RemoveSignature(size_t nPosition); }; } // namespace pdfio diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx index 479bc1c627e2..6543734a6fb5 100644 --- a/xmlsecurity/source/pdfio/pdfdocument.cxx +++ b/xmlsecurity/source/pdfio/pdfdocument.cxx @@ -48,9 +48,11 @@ class PDFObjectElement; /// A one-liner comment. class PDFCommentElement : public PDFElement { + PDFDocument& m_rDoc; OString m_aComment; public: + PDFCommentElement(PDFDocument& rDoc); bool Read(SvStream& rStream) override; }; @@ -233,6 +235,30 @@ PDFDocument::PDFDocument() { } +bool PDFDocument::RemoveSignature(size_t nPosition) +{ + std::vector<PDFObjectElement*> aSignatures = GetSignatureWidgets(); + if (nPosition >= aSignatures.size()) + { + SAL_WARN("xmlsecurity.pdfio", "PDFDocument::RemoveSignature: invalid nPosition"); + return false; + } + + if (aSignatures.size() != m_aEOFs.size() - 1) + { + SAL_WARN("xmlsecurity.pdfio", "PDFDocument::RemoveSignature: no 1:1 mapping between signatures and incremental updates"); + return false; + } + + // The EOF offset is the end of the original file, without the signature at + // nPosition. + m_aEditBuffer.Seek(m_aEOFs[nPosition]); + // Drop all bytes after the current position. + m_aEditBuffer.SetStreamSize(m_aEditBuffer.Tell() + 1); + + return m_aEditBuffer.good(); +} + bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& xCertificate, const OUString& rDescription) { m_aEditBuffer.WriteCharPtr("\n"); @@ -530,7 +556,7 @@ bool PDFDocument::Read(SvStream& rStream) { case '%': { - m_aElements.push_back(std::unique_ptr<PDFElement>(new PDFCommentElement())); + m_aElements.push_back(std::unique_ptr<PDFElement>(new PDFCommentElement(*this))); rStream.SeekRel(-1); if (!m_aElements.back()->Read(rStream)) return false; @@ -927,6 +953,11 @@ std::vector<PDFObjectElement*> PDFDocument::GetPages() return aRet; } +void PDFDocument::PushBackEOF(size_t nOffset) +{ + m_aEOFs.push_back(nOffset); +} + std::vector<PDFObjectElement*> PDFDocument::GetSignatureWidgets() { std::vector<PDFObjectElement*> aRet; @@ -1288,6 +1319,11 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat #endif } +PDFCommentElement::PDFCommentElement(PDFDocument& rDoc) + : m_rDoc(rDoc) +{ +} + bool PDFCommentElement::Read(SvStream& rStream) { // Read from (including) the % char till (excluding) the end of the line. @@ -1299,6 +1335,10 @@ bool PDFCommentElement::Read(SvStream& rStream) if (ch == 0x0a) { m_aComment = aBuf.makeStringAndClear(); + + if (m_aComment.startsWith("%%EOF")) + m_rDoc.PushBackEOF(rStream.Tell()); + SAL_INFO("xmlsecurity.pdfio", "PDFCommentElement::Read: m_aComment is '" << m_aComment << "'"); return true; } diff --git a/xmlsecurity/source/pdfio/pdfverify.cxx b/xmlsecurity/source/pdfio/pdfverify.cxx index 5787aff41c2e..f980db8a4a78 100644 --- a/xmlsecurity/source/pdfio/pdfverify.cxx +++ b/xmlsecurity/source/pdfio/pdfverify.cxx @@ -62,6 +62,10 @@ SAL_IMPLEMENT_MAIN_WITH_ARGS(nArgc, pArgv) if (nArgc > 2) osl::FileBase::getFileURLFromSystemPath(OUString::fromUtf8(pArgv[2]), aOutURL); + bool bRemoveSignature = false; + if (nArgc > 3 && OString(pArgv[3]) == "-r") + bRemoveSignature = true; + SvFileStream aStream(aInURL, StreamMode::READ); xmlsecurity::pdfio::PDFDocument aDocument; if (!aDocument.Read(aStream)) @@ -70,9 +74,36 @@ SAL_IMPLEMENT_MAIN_WITH_ARGS(nArgc, pArgv) return 1; } + if (bRemoveSignature) + { + std::cerr << "removing the last signature" << std::endl; + std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets(); + if (aSignatures.empty()) + { + std::cerr << "found no signatures" << std::endl; + return 1; + } + + size_t nPosition = aSignatures.size() - 1; + if (!aDocument.RemoveSignature(nPosition)) + { + SAL_WARN("xmlsecurity.pdfio", "failed to remove signature #" << nPosition); + return 1; + } + + SvFileStream aOutStream(aOutURL, StreamMode::WRITE | StreamMode::TRUNC); + if (!aDocument.Write(aOutStream)) + { + SAL_WARN("xmlsecurity.pdfio", "failed to write the document"); + return 1; + } + + return 0; + } + if (aOutURL.isEmpty()) { - // Verify. + std::cerr << "verifying signatures" << std::endl; std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets(); if (aSignatures.empty()) std::cerr << "found no signatures" << std::endl; @@ -96,7 +127,7 @@ SAL_IMPLEMENT_MAIN_WITH_ARGS(nArgc, pArgv) return 0; } - // Sign. + std::cerr << "adding a new signature" << std::endl; uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment(); uno::Sequence<uno::Reference<security::XCertificate>> aCertificates = xSecurityEnvironment->getPersonalCertificates(); if (!aCertificates.hasElements()) |