Commit 8f9b85b6 authored by Sandro Knauß's avatar Sandro Knauß 🐝
Browse files

Make unexpected data leak harder via reply.

ObjectTreeParser.htmlContent/plainTextContent may concats different encrypted parts
without the user noticing it. That would lead to a Decryption oracle. We have already
a logic to find the topleveltextNode in MimeTreeParser, this does not take HTML nodes
into account nor that TEXT nodes may have several PGP Inline blocks.

That plaintextContent for HtmlMessagePart is not return QString(), that bubbled up by
testing the stuff.

BUG: 404698
parent 676893d2
......@@ -482,6 +482,11 @@ QString HtmlMessagePart::text() const
return mBodyHTML;
}
QString MimeTreeParser::HtmlMessagePart::plaintextContent() const
{
return QString();
}
bool HtmlMessagePart::isHtml() const
{
return true;
......
......@@ -234,6 +234,7 @@ public:
~HtmlMessagePart() override;
QString text() const override;
QString plaintextContent() const override;
void fix() const override;
bool isHtml() const override;
......
Subject: Testcase 'reply-decrytion-oracle' (PGP INLINE)
To: brucewayne45@web.de
From: brucewayne45@web.de
MIME-Version: 1.0
Content-Type: text/plain
-----BEGIN PGP MESSAGE-----
hQEMAwzOQ1qnzNo7AQf7BxmM0vO8nG37hKqoqOHb35JqprJM+sqF7JFmrsuWe6V2
PAyyE2wdtq+AhvXjVnggxYLwU+DEFpBTmWr1rsanyV8hWXRbecfN/9gN/4/N9y7Z
XSx2OeE/uA5z8Kz5vrv/ywMqcVHjB5MQPTcLC2Zlg8MVltpriy6mdAkON4I3t7kl
j9uwQRY7HeKvsib63HWnYAOV/fYPXXor/lioeYIll08uuCiTh3Z9fEhXQI/az5Ft
e/xa70xGqviux+OvhoNUSZspzl7vK7e/NTBlC+LF1zVXUXT8prrd+ZFNwKvtn0Hl
W4KfNqTM9TJB8vpE5FWnH6+B365ZvxZopZ5F/9szp9JGAUCNdX5WujBreg7nTLui
UrnDNwOvjvsE/gsoO3n3jARK+Tu8PfUl8V1bHiCeGJz/mkA9uGJ/IApcT4rYsoHB
nVQjW1NJ6A==
=zrF/
-----END PGP MESSAGE-----
whoo three encrypted parts inside.
......@@ -29,6 +29,7 @@
#include <MessageCore/StringUtil>
#include <MimeTreeParser/ObjectTreeParser>
#include <MimeTreeParser/MessagePart>
#include <MimeTreeParser/SimpleObjectTreeSource>
#include <KIdentityManagement/Identity>
......@@ -296,29 +297,59 @@ void TemplateParserJob::processWithIdentity(uint uoid, const KMime::Message::Ptr
process(aorig_msg, afolder);
}
MimeTreeParser::MessagePart::Ptr toplevelTextNode(MimeTreeParser::MessagePart::Ptr messageTree)
{
foreach (const auto &mp, messageTree->subParts()) {
auto text = mp.dynamicCast<MimeTreeParser::TextMessagePart>();
const auto attach = mp.dynamicCast<MimeTreeParser::AttachmentMessagePart>();
if (text && !attach) {
// TextMessagePart can have several subparts cause of PGP inline, we search for the first part with content
foreach (const auto &sub, mp->subParts()) {
if (!sub->text().trimmed().isEmpty()) {
return sub;
}
}
return text;
} else if (const auto html = mp.dynamicCast<MimeTreeParser::HtmlMessagePart>()) {
return html;
} else if (const auto alternative = mp.dynamicCast<MimeTreeParser::AlternativeMessagePart>()) {
return alternative;
} else {
auto ret = toplevelTextNode(mp);
if (ret) {
return ret;
}
}
}
return MimeTreeParser::MessagePart::Ptr();
}
void TemplateParserJob::processWithTemplate(const QString &tmpl)
{
d->mOtp->parseObjectTree(d->mOrigMsg.data());
TemplateParserExtractHtmlInfo *job = new TemplateParserExtractHtmlInfo(this);
connect(job, &TemplateParserExtractHtmlInfo::finished, this, &TemplateParserJob::slotExtractInfoDone);
const auto mp = toplevelTextNode(d->mOtp->parsedPart());
QString plainText = d->mOtp->plainTextContent();
if (plainText.isEmpty()) { //HTML-only mails
plainText = d->mOtp->htmlContent();
}
QString plainText = mp->plaintextContent();
QString htmlElement;
job->setHtmlForExtractingTextPlain(plainText);
job->setTemplate(tmpl);
QString htmlElement = d->mOtp->htmlContent();
if (htmlElement.isEmpty()) { //plain mails only
QString htmlReplace = d->mOtp->plainTextContent().toHtmlEscaped();
if (mp->isHtml()) {
htmlElement = d->mOtp->htmlContent();
if (plainText.isEmpty()) { //HTML-only mails
plainText = htmlElement;
}
} else { //plain mails only
QString htmlReplace = plainText.toHtmlEscaped();
htmlReplace = htmlReplace.replace(QLatin1Char('\n'), QStringLiteral("<br />"));
htmlElement = QStringLiteral("<html><head></head><body>%1</body></html>\n").arg(htmlReplace);
}
TemplateParserExtractHtmlInfo *job = new TemplateParserExtractHtmlInfo(this);
connect(job, &TemplateParserExtractHtmlInfo::finished, this, &TemplateParserJob::slotExtractInfoDone);
job->setHtmlForExtractingTextPlain(plainText);
job->setTemplate(tmpl);
job->setHtmlForExtractionHeaderAndBody(htmlElement);
job->start();
}
......@@ -1483,7 +1514,8 @@ QString TemplateParserJob::plainMessageText(bool aStripSignature, AllowSelection
if (!d->mOrigMsg) {
return QString();
}
QString result = d->mOtp->plainTextContent();
const auto mp = toplevelTextNode(d->mOtp->parsedPart());
QString result = mp->plaintextContent();
if (result.isEmpty()) {
result = d->mExtractHtmlInfoResult.mPlainText;
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment