Skip to content

Commit

Permalink
HTTP headers should be compared case-insensitive
Browse files Browse the repository at this point in the history
IB-8163

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma committed Aug 21, 2024
1 parent 36cad6d commit 7d42554
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 46 deletions.
2 changes: 1 addition & 1 deletion cmake
Submodule cmake updated 0 files
1 change: 0 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ target_link_libraries(digidocpp_priv
LibXml2::LibXml2
OpenSSL::SSL
xmlsec
$<$<C_COMPILER_ID:MSVC>:Ws2_32>
)

add_library(digidocpp
Expand Down
31 changes: 14 additions & 17 deletions src/crypto/Connect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ Connect::Connect(const string &_url, string _method, int _timeout, const vector<
}
}

#if OPENSSL_VERSION_NUMBER > 0x30000000L
#if OPENSSL_VERSION_NUMBER < 0x30000000L
if(_timeout > 0)
{
int fd = BIO_get_fd(d, nullptr);
Expand Down Expand Up @@ -251,12 +251,6 @@ string Connect::decompress(const string &encoding, const string &data)
return out;
}

Connect::Result Connect::exec(initializer_list<pair<string_view,string_view>> headers,
const vector<unsigned char> &data)
{
return exec(headers, data.data(), data.size());
}

Connect::Result Connect::exec(initializer_list<pair<string_view,string_view>> headers,
const unsigned char *data, size_t size)
{
Expand Down Expand Up @@ -295,6 +289,10 @@ Connect::Result Connect::exec(initializer_list<pair<string_view,string_view>> he

stringstream stream(r.content);
string line;
auto to_lower = [](string str) {
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
return str;
};
while(getline(stream, line))
{
line.resize(max<size_t>(line.size() - 1, 0));
Expand All @@ -307,18 +305,17 @@ Connect::Result Connect::exec(initializer_list<pair<string_view,string_view>> he
}
size_t split = line.find(": ");
if(split != string::npos)
r.headers[line.substr(0, split)] = line.substr(split + 2);
r.headers[to_lower(line.substr(0, split))] = line.substr(split + 2);
else
r.headers[line] = string();
r.headers[to_lower(line)] = string();
}

pos = r.content.find("\r\n\r\n");
if(pos != string::npos)
r.content.erase(0, pos + 4);

const auto transfer_encoding = r.headers.find("Transfer-Encoding");
if(transfer_encoding != r.headers.cend() &&
transfer_encoding->second.find("chunked") != string::npos) {
if(const auto it = r.headers.find("transfer-encoding");
it != r.headers.cend() &&
it->second.find("chunked") != string::npos) {
pos = 0;
for(size_t chunkpos = r.content.find("\r\n", pos);
chunkpos != string::npos;
Expand All @@ -331,14 +328,14 @@ Connect::Result Connect::exec(initializer_list<pair<string_view,string_view>> he
}
}

const auto it = r.headers.find("Content-Encoding");
if(it != r.headers.cend())
if(const auto it = r.headers.find("content-encoding");
it != r.headers.cend())
r.content = decompress(it->second, r.content);

if(!r.isRedirect() || recursive > 3)
return r;
string location = r.headers.find("Location") == r.headers.cend() ? r.headers["location"] : r.headers["Location"];
string url = location.find("://") != string::npos ? location : baseurl + location;
string &location = r.headers["location"];
string url = location.find("://") != string::npos ? std::move(location) : baseurl + location;
Connect c(url, method, timeout);
c.recursive = recursive + 1;
return c.exec(headers);
Expand Down
7 changes: 5 additions & 2 deletions src/crypto/Connect.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ class Connect
Connect(const std::string &url, std::string method = "POST",
int timeout = 0, const std::vector<X509Cert> &certs = {});
~Connect();
Result exec(std::initializer_list<std::pair<std::string_view,std::string_view>> headers,
const std::vector<unsigned char> &data);
inline Result exec(std::initializer_list<std::pair<std::string_view,std::string_view>> headers,
const std::vector<unsigned char> &data)
{
return exec(headers, data.data(), data.size());
}
Result exec(std::initializer_list<std::pair<std::string_view,std::string_view>> headers = {},
const unsigned char *data = nullptr, size_t size = 0);

Expand Down
42 changes: 19 additions & 23 deletions src/crypto/TSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,20 @@ constexpr bool contains(const C &list, const T &value)



TSL::TSL(const string &file)
TSL::TSL(string file)
: XMLDocument(file, {"TrustServiceStatusList", TSL_NS})
, schemeInformation((*this)/"SchemeInformation")
, path(std::move(file))
{
if(file.empty())
if(path.empty())
return;
if(get())
{
static array<const xmlChar*,2> ids { pcxmlChar("Id"), nullptr };
xmlSecAddIDs(get(), nullptr, ids.data());
}
else
WARN("Failed to parse configuration: %s", file.c_str());
WARN("Failed to parse configuration: %s", path.c_str());
}

bool TSL::activate(const string &territory)
Expand Down Expand Up @@ -174,7 +175,7 @@ string TSL::fetch(const string &url, const string &path)
if(!r || r.content.empty())
THROW("HTTP status code is not 200 or content is empty");
ofstream(File::encodeName(path), fstream::binary|fstream::trunc) << r.content;
return r.headers["ETag"];
return r.headers["etag"];
}
catch(const Exception &)
{
Expand Down Expand Up @@ -254,19 +255,19 @@ TSL TSL::parseTSL(const string &url, const vector<X509Cert> &certs,
TSL tsl(path);
tsl.validate(certs);
valid = std::move(tsl);
DEBUG("TSL %s (%llu) signature is valid", territory.c_str(), tsl.sequenceNumber());
DEBUG("TSL %s (%llu) signature is valid", territory.c_str(), valid.sequenceNumber());

if(valid.isExpired())
{
if(!CONF(TSLAutoUpdate) && CONF(TSLAllowExpired))
return valid;
THROW("TSL %s (%llu) is expired", territory.c_str(), tsl.sequenceNumber());
THROW("TSL %s (%llu) is expired", territory.c_str(), valid.sequenceNumber());
}

if(CONF(TSLOnlineDigest) && (File::modifiedTime(path) < (time(nullptr) - (60 * 60 * 24))))
if(CONF(TSLOnlineDigest) && (File::modifiedTime(valid.path) < (time(nullptr) - (60 * 60 * 24))))
{
if(valid.validateETag(url))
File::updateModifiedTime(path, time(nullptr));
File::updateModifiedTime(valid.path, time(nullptr));
}

return valid;
Expand All @@ -279,17 +280,18 @@ TSL TSL::parseTSL(const string &url, const vector<X509Cert> &certs,
try {
string tmp = path + ".tmp";
string etag = fetch(url, tmp);
TSL tsl = TSL(tmp);
TSL tsl = TSL(std::move(tmp));
tsl.validate(certs);
valid = std::move(tsl);

ofstream(File::encodeName(path), ofstream::binary|fstream::trunc)
<< ifstream(File::encodeName(tmp), fstream::binary).rdbuf();
<< ifstream(File::encodeName(valid.path), fstream::binary).rdbuf();
error_code ec;
filesystem::remove(filesystem::u8path(tmp), ec);
filesystem::remove(filesystem::u8path(valid.path), ec);

ofstream(File::encodeName(path + ".etag"), ofstream::trunc) << etag;
DEBUG("TSL %s (%llu) signature is valid", territory.c_str(), tsl.sequenceNumber());

DEBUG("TSL %s (%llu) signature is valid", territory.c_str(), valid.sequenceNumber());
} catch(const Exception &) {
ERR("TSL %s signature is invalid", territory.c_str());
if(!valid)
Expand Down Expand Up @@ -382,16 +384,11 @@ bool TSL::parseInfo(XMLNode info, Service &s)
return true;
}

string TSL::path() const
{
return get() && get()->name ? string(get()->name) : string();
}

vector<string> TSL::pivotURLs() const
{
if(!*this)
return {};
auto current = File::fileName(path());
auto current = File::fileName(path);
if(size_t pos = current.find_first_of('.');
current.find("pivot") != string::npos && pos != string::npos)
current = current.substr(0, pos);
Expand Down Expand Up @@ -570,16 +567,15 @@ bool TSL::validateETag(const string &url)
return false;
}

auto it = r.headers.find("ETag");
auto it = r.headers.find("etag");
if(it == r.headers.cend())
return validateRemoteDigest(url);

DEBUG("Remote ETag: %s", it->second.c_str());
ifstream is(File::encodeName(path() + ".etag"));
ifstream is(File::encodeName(path + ".etag"));
if(!is.is_open())
THROW("Cached ETag does not exist");
string etag(it->second.size(), 0);
is.read(etag.data(), streamsize(etag.size()));
string etag(istreambuf_iterator<char>(is), {});
DEBUG("Cached ETag: %s", etag.c_str());
if(etag != it->second)
THROW("Remote ETag does not match");
Expand Down Expand Up @@ -617,7 +613,7 @@ bool TSL::validateRemoteDigest(const string &url)

Digest sha(URI_RSA_SHA256);
array<unsigned char, 10240> buf{};
ifstream is(path(), ifstream::binary);
ifstream is(path, ifstream::binary);
while(is)
{
is.read((char*)buf.data(), streamsize(buf.size()));
Expand Down
4 changes: 2 additions & 2 deletions src/crypto/TSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class TSL: private XMLDocument
struct Service { std::vector<X509Cert> certs; std::map<std::string,Qualifiers> validity; std::string type, additional, name; };
struct Pointer { std::string territory, location; std::vector<X509Cert> certs; };

TSL(const std::string &file = {});
TSL(std::string file = {});
bool isExpired() const;
void validate() const;
void validate(const std::vector<X509Cert> &certs, int recursion = 0) const;
Expand All @@ -59,7 +59,6 @@ class TSL: private XMLDocument
static std::vector<Service> parse();

private:
std::string path() const;
std::vector<std::string> pivotURLs() const;
X509Cert signingCert() const;
std::vector<X509Cert> signingCerts() const;
Expand All @@ -78,5 +77,6 @@ class TSL: private XMLDocument
static std::string_view toString(XMLNode obj, std::string_view lang = "en") noexcept;

XMLNode schemeInformation;
std::string path;
};
}

0 comments on commit 7d42554

Please sign in to comment.