From 72c5eb893e380537c06aa66296ff548859e52bcd Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Thu, 9 Nov 2023 10:18:59 +0200 Subject: [PATCH] Validate CAdES signature in SiVa service (#552) IB-6671 Signed-off-by: Raul Metsma --- src/ASiContainer.cpp | 20 ++++++----------- src/ASiContainer.h | 4 ++-- src/SiVaContainer.cpp | 50 ++++++++++++++++++++++++++++++++++--------- src/SiVaContainer.h | 2 +- 4 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/ASiContainer.cpp b/src/ASiContainer.cpp index 5f158ce8d..2a6440482 100644 --- a/src/ASiContainer.cpp +++ b/src/ASiContainer.cpp @@ -300,21 +300,15 @@ string ASiContainer::readMimetype(const ZipSerialize &z) DEBUG("ASiContainer::readMimetype()"); stringstream is; z.extract("mimetype", is); - - array bom{}; - is.read((char*)bom.data(), bom.size()); - // Contains UTF-16 BOM - if((bom[0] == 0xFF && bom[1] == 0xEF) || - (bom[0] == 0xEF && bom[1] == 0xFF)) - THROW("Mimetype file must be UTF-8 format."); - // does not contain UTF-8 BOM reset pos - if(bom[0] != 0xEF || bom[1] != 0xBB || bom[2] != 0xBF) - is.seekg(0, ios::beg); - string text; is >> text; - if(is.fail()) + if(!is) THROW("Failed to read mimetype."); - + // Contains UTF-16 BOM + if(text.find("\xFF\xEF") == 0 || text.find("\xEF\xFF") == 0) + THROW("Mimetype file must be UTF-8 format."); + // contains UTF-8 BOM, remove + if(text.find("\xEF\xBB\xBF") == 0) + text.erase(text.cbegin(), text.cbegin() + 3); return text; } diff --git a/src/ASiContainer.h b/src/ASiContainer.h index 56c6de0bf..73ddfb3c2 100644 --- a/src/ASiContainer.h +++ b/src/ASiContainer.h @@ -58,6 +58,8 @@ namespace digidoc void removeSignature(unsigned int id) override; std::vector signatures() const override; + static std::string readMimetype(const ZipSerialize &z); + protected: ASiContainer(const std::string &mimetype); @@ -72,8 +74,6 @@ namespace digidoc ZipSerialize::Properties zproperty(const std::string &file) const; void zproperty(const std::string &file, ZipSerialize::Properties &&prop); - static std::string readMimetype(const ZipSerialize &z); - private: DISABLE_COPY(ASiContainer); diff --git a/src/SiVaContainer.cpp b/src/SiVaContainer.cpp index 49420c3ff..180612146 100644 --- a/src/SiVaContainer.cpp +++ b/src/SiVaContainer.cpp @@ -24,6 +24,7 @@ #include "SiVaContainer.h" +#include "ASiContainer.h" #include "Conf.h" #include "DataFile_p.h" #include "Signature.h" @@ -31,6 +32,7 @@ #include "crypto/Digest.h" #include "util/File.h" #include "util/log.h" +#include "util/ZipSerialize.h" #include "xml/xml.hxx" #include "xml/SecureDOMParser.h" @@ -142,13 +144,15 @@ void SignatureSiVa::validate(const string &policy) const } -SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHashCode) +SiVaContainer::SiVaContainer(const string &path, bool useHashCode) : d(make_unique()) { + string ext = File::fileExtension(path); DEBUG("SiVaContainer::SiVaContainer(%s, %s, %d)", path.c_str(), ext.c_str(), useHashCode); unique_ptr ifs = make_unique(File::encodeName(d->path = path), ifstream::binary); auto fileName = File::fileName(path); istream *is = ifs.get(); + static const array asic {"asice", "sce", "asics", "scs"}; if(ext == "ddoc") { d->mediaType = "application/x-ddoc"; @@ -156,13 +160,41 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHash ifs = parseDDoc(useHashCode); is = ifs.get(); } - else + else if(ext == "pdf") { d->mediaType = "application/pdf"; d->dataFiles.push_back(new DataFilePrivate(std::move(ifs), fileName, "application/pdf")); } + else if(find(asic.cbegin(), asic.cend(), ext) != asic.cend()) + { + ZipSerialize z(path, false); + vector list = z.list(); + if(list.empty() || list.front() != "mimetype") + THROW("Missing mimetype"); + if(d->mediaType = ASiContainer::readMimetype(z); + d->mediaType != ASiContainer::MIMETYPE_ASIC_E && d->mediaType != ASiContainer::MIMETYPE_ASIC_S) + THROW("Unknown file"); + if(none_of(list.cbegin(), list.cend(), [](const string &file) { return file.find("p7s") != string::npos; })) + THROW("Unknown file"); + + static const string metaInf = "META-INF/"; + for(const string &file: list) + { + if(file == "mimetype" || file.substr(0, metaInf.size()) == metaInf) + continue; + const auto directory = File::directory(file); + if(directory.empty() || directory == "/" || directory == "./") + { + auto data = make_unique(); + z.extract(file, *data); + d->dataFiles.push_back(new DataFilePrivate(std::move(data), file, "application/octet-stream")); + } + } + } + else + THROW("Unknown file"); - array buf{}; + array buf{}; string b64; is->clear(); is->seekg(0); @@ -314,15 +346,13 @@ vector SiVaContainer::dataFiles() const unique_ptr SiVaContainer::openInternal(const string &path) { - static const array supported {"pdf", "ddoc"}; - string ext = File::fileExtension(path); - if(find(supported.cbegin(), supported.cend(), ext) == supported.cend()) - return {}; try { - return unique_ptr(new SiVaContainer(path, ext, true)); + return unique_ptr(new SiVaContainer(path, true)); } catch(const Exception &e) { if(e.msg().find("Bad digest for DataFile") == 0) - return unique_ptr(new SiVaContainer(path, ext, false)); + return unique_ptr(new SiVaContainer(path, false)); + if(e.msg() == "Unknown file") + return {}; throw; } } @@ -337,7 +367,7 @@ unique_ptr SiVaContainer::parseDDoc(bool useHashCode) DOMNodeList *nodeList = dom->getElementsByTagName(cpXMLCh(u"DataFile")); for(XMLSize_t i = 0; i < nodeList->getLength(); ++i) { - DOMElement *item = static_cast(nodeList->item(i)); + auto *item = static_cast(nodeList->item(i)); if(!item) continue; diff --git a/src/SiVaContainer.h b/src/SiVaContainer.h index 060796615..ce93edff7 100644 --- a/src/SiVaContainer.h +++ b/src/SiVaContainer.h @@ -102,7 +102,7 @@ class SiVaContainer final: public Container static std::unique_ptr openInternal(const std::string &path); private: - SiVaContainer(const std::string &path, const std::string &ext, bool useHashCode); + SiVaContainer(const std::string &path, bool useHashCode); DISABLE_COPY(SiVaContainer); std::unique_ptr parseDDoc(bool useHashCode);