From bf55fc27b634152c758479b2fd1ba1353c69031d Mon Sep 17 00:00:00 2001 From: Raul Metsma <raul@metsma.ee> Date: Wed, 22 Mar 2023 13:13:49 +0200 Subject: [PATCH] Validate CAdES signature in SiVa service IB-6671 Signed-off-by: Raul Metsma <raul@metsma.ee> --- .github/workflows/build.yml | 4 +-- src/ASiContainer.cpp | 20 ++++++--------- src/ASiContainer.h | 4 +-- src/SiVaContainer.cpp | 50 +++++++++++++++++++++++++++++-------- src/SiVaContainer.h | 2 +- 5 files changed, 52 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6f116d504..1e45146e1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -82,7 +82,7 @@ jobs: steps: - name: Install Deps run: | - dnf install -y \ + dnf install -y --setopt=install_weak_deps=False \ git gcc-c++ cmake rpm-build xml-security-c-devel zlib-devel vim-common doxygen boost-test swig python3-devel java-1.8.0-openjdk-devel \ https://www.codesynthesis.com/download/xsd/4.0/linux-gnu/x86_64/xsd-4.0.0-1.x86_64.rpm - name: Checkout @@ -105,7 +105,7 @@ jobs: container: ${{ matrix.container }} strategy: matrix: - container: ['ubuntu:20.04', 'ubuntu:22.04', 'ubuntu:23.04'] + container: ['ubuntu:20.04', 'ubuntu:22.04', 'ubuntu:23.04', 'ubuntu:23.10'] env: DEBIAN_FRONTEND: noninteractive DEBFULLNAME: github-actions 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<unsigned char,3> 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<Signature*> 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<Private>()) { + string ext = File::fileExtension(path); DEBUG("SiVaContainer::SiVaContainer(%s, %s, %d)", path.c_str(), ext.c_str(), useHashCode); unique_ptr<istream> ifs = make_unique<ifstream>(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<string> 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<stringstream>(); + z.extract(file, *data); + d->dataFiles.push_back(new DataFilePrivate(std::move(data), file, "application/octet-stream")); + } + } + } + else + THROW("Unknown file"); - array<XMLByte, 48*100> buf{}; + array<XMLByte, 4800> buf{}; string b64; is->clear(); is->seekg(0); @@ -314,15 +346,13 @@ vector<DataFile *> SiVaContainer::dataFiles() const unique_ptr<Container> 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<Container>(new SiVaContainer(path, ext, true)); + return unique_ptr<Container>(new SiVaContainer(path, true)); } catch(const Exception &e) { if(e.msg().find("Bad digest for DataFile") == 0) - return unique_ptr<Container>(new SiVaContainer(path, ext, false)); + return unique_ptr<Container>(new SiVaContainer(path, false)); + if(e.msg() == "Unknown file") + return {}; throw; } } @@ -337,7 +367,7 @@ unique_ptr<istream> SiVaContainer::parseDDoc(bool useHashCode) DOMNodeList *nodeList = dom->getElementsByTagName(cpXMLCh(u"DataFile")); for(XMLSize_t i = 0; i < nodeList->getLength(); ++i) { - DOMElement *item = static_cast<DOMElement*>(nodeList->item(i)); + auto *item = static_cast<DOMElement*>(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<Container> 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<std::istream> parseDDoc(bool useHashCode);