File indexing completed on 2024-06-23 05:19:20

0001 /*
0002    SPDX-FileCopyrightText: 2016 Sandro Knauß <sknauss@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "applicationpkcs7mime.h"
0008 
0009 #include "messagepart.h"
0010 #include "objecttreeparser.h"
0011 
0012 #include <QGpgME/Protocol>
0013 
0014 #include <KMime/Content>
0015 
0016 #include "mimetreeparser_debug.h"
0017 
0018 using namespace MimeTreeParser;
0019 
0020 const ApplicationPkcs7MimeBodyPartFormatter *ApplicationPkcs7MimeBodyPartFormatter::self;
0021 
0022 const Interface::BodyPartFormatter *ApplicationPkcs7MimeBodyPartFormatter::create()
0023 {
0024     if (!self) {
0025         self = new ApplicationPkcs7MimeBodyPartFormatter();
0026     }
0027     return self;
0028 }
0029 
0030 MessagePart::Ptr ApplicationPkcs7MimeBodyPartFormatter::process(Interface::BodyPart &part) const
0031 {
0032     KMime::Content *node = part.content();
0033 
0034     if (node->head().isEmpty()) {
0035         return {};
0036     }
0037 
0038     const auto smimeCrypto = QGpgME::smime();
0039     if (!smimeCrypto) {
0040         return {};
0041     }
0042 
0043     // we are also registered for octet-stream, in that case stop here if that's not a part for us
0044     const auto ct = node->contentType(); // Create
0045     const auto mt = ct->mimeType();
0046     const auto isCorrectMimeType = mt == QByteArrayLiteral("application/pkcs7-mime") || mt == QByteArrayLiteral("application/x-pkcs7-mime");
0047     const auto hasCorrectName = mt == QByteArrayLiteral("application/octet-stream")
0048         && (ct->name().endsWith(QLatin1StringView("p7m")) || ct->name().endsWith(QLatin1StringView("p7s")) || ct->name().endsWith(QLatin1StringView("p7c")));
0049     if (!isCorrectMimeType && !hasCorrectName) {
0050         return {};
0051     }
0052 
0053     const QString smimeType = node->contentType(false)->parameter(QStringLiteral("smime-type")).toLower();
0054 
0055     if (smimeType == QLatin1StringView("certs-only")) {
0056         part.processResult()->setNeverDisplayInline(true);
0057 
0058         CertMessagePart::Ptr mp(new CertMessagePart(part.objectTreeParser(), node, smimeCrypto, part.source()->autoImportKeys()));
0059         return mp;
0060     }
0061 
0062     bool isSigned = (smimeType == QLatin1StringView("signed-data"));
0063     bool isEncrypted = (smimeType == QLatin1StringView("enveloped-data"));
0064 
0065     // Analyze "signTestNode" node to find/verify a signature.
0066     // If zero part.objectTreeParser() verification was successfully done after
0067     // decrypting via recursion by insertAndParseNewChildNode().
0068     KMime::Content *signTestNode = isEncrypted ? nullptr : node;
0069 
0070     // We try decrypting the content
0071     // if we either *know* that it is an encrypted message part
0072     // or there is neither signed nor encrypted parameter.
0073     MessagePart::Ptr mp;
0074     if (!isSigned) {
0075         if (isEncrypted) {
0076             qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime     ==      S/MIME TYPE: enveloped (encrypted) data";
0077         } else {
0078             qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime  -  type unknown  -  enveloped (encrypted) data ?";
0079         }
0080 
0081         auto _mp = EncryptedMessagePart::Ptr(
0082             new EncryptedMessagePart(part.objectTreeParser(), node->decodedText(), smimeCrypto, part.nodeHelper()->fromAsString(node), node));
0083         mp = _mp;
0084         _mp->setIsEncrypted(true);
0085         _mp->setDecryptMessage(part.source()->decryptMessage());
0086         PartMetaData *messagePart(_mp->partMetaData());
0087         if (!part.source()->decryptMessage()) {
0088             isEncrypted = true;
0089             signTestNode = nullptr; // PENDING(marc) to be abs. sure, we'd need to have to look at the content
0090         } else {
0091             _mp->startDecryption();
0092             if (messagePart->isDecryptable) {
0093                 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime  -  encryption found  -  enveloped (encrypted) data !";
0094                 isEncrypted = true;
0095                 part.nodeHelper()->setEncryptionState(node, KMMsgFullyEncrypted);
0096                 signTestNode = nullptr;
0097             } else {
0098                 // decryption failed, which could be because the part was encrypted but
0099                 // decryption failed, or because we didn't know if it was encrypted, tried,
0100                 // and failed. If the message was not actually encrypted, we continue
0101                 // assuming it's signed
0102                 if (_mp->passphraseError() || (smimeType.isEmpty() && messagePart->isEncrypted)) {
0103                     isEncrypted = true;
0104                     signTestNode = nullptr;
0105                 }
0106 
0107                 if (isEncrypted) {
0108                     qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime  -  ERROR: COULD NOT DECRYPT enveloped data !";
0109                 } else {
0110                     qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime  -  NO encryption found";
0111                 }
0112             }
0113         }
0114 
0115         if (isEncrypted) {
0116             part.nodeHelper()->setEncryptionState(node, KMMsgFullyEncrypted);
0117         }
0118     }
0119 
0120     // We now try signature verification if necessary.
0121     if (signTestNode) {
0122         if (isSigned) {
0123             qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime     ==      S/MIME TYPE: opaque signed data";
0124         } else {
0125             qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime  -  type unknown  -  opaque signed data ?";
0126         }
0127 
0128         QStringDecoder aCodec(part.objectTreeParser()->codecNameFor(signTestNode).constData());
0129         const QByteArray signaturetext = signTestNode->decodedContent();
0130         auto _mp = SignedMessagePart::Ptr(
0131             new SignedMessagePart(part.objectTreeParser(), aCodec.decode(signaturetext), smimeCrypto, part.nodeHelper()->fromAsString(node), signTestNode));
0132         mp = _mp;
0133         _mp->startVerificationDetached(signaturetext, nullptr, QByteArray());
0134 
0135         if (_mp->isSigned()) {
0136             if (!isSigned) {
0137                 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime  -  signature found  -  opaque signed data !";
0138             }
0139 
0140             if (signTestNode != node) {
0141                 part.nodeHelper()->setSignatureState(node, KMMsgFullySigned);
0142             }
0143         } else {
0144             qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime  -  NO signature found   :-(";
0145         }
0146     }
0147 
0148     return mp;
0149 }