From 676d2e679009055028e229028e34e9dac83a3433 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Mon, 19 Aug 2019 10:37:15 -0400 Subject: [PATCH] (feat) added support for S/MIME opaque signing (fixes #4582) --- NEWS | 1 + SOPE/GDLContentStore/GCSFolder.m | 2 +- SoObjects/Mailer/NSData+SMIME.h | 2 + SoObjects/Mailer/NSData+SMIME.m | 78 +++- SoObjects/Mailer/SOGoDraftObject.m | 14 + SoObjects/Mailer/SOGoMailBaseObject.m | 2 - SoObjects/Mailer/SOGoMailBodyPart.m | 40 +++ SoObjects/Mailer/SOGoMailObject+Draft.m | 20 ++ SoObjects/Mailer/SOGoMailObject.h | 3 +- SoObjects/Mailer/SOGoMailObject.m | 52 ++- .../UIxMailPartEncryptedViewer.h | 12 + .../UIxMailPartEncryptedViewer.m | 333 +++++++++++++++++- UI/MailPartViewers/UIxMailRenderingContext.m | 18 +- UI/MailerUI/UIxMailView.m | 2 +- .../js/Mailer/Message.service.js | 23 +- 15 files changed, 569 insertions(+), 33 deletions(-) diff --git a/NEWS b/NEWS index 061158b10..eed67f94d 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ New features - [core] Debian 10 (Buster) support for x86_64 (#4775) - [core] now possible to specify which domains you can forward your mails to + - [core] added support for S/MIME opaque signing (#4582) Enhancements - [web] avoid saving an empty calendar name diff --git a/SOPE/GDLContentStore/GCSFolder.m b/SOPE/GDLContentStore/GCSFolder.m index 7cfac8890..ed35c383e 100644 --- a/SOPE/GDLContentStore/GCSFolder.m +++ b/SOPE/GDLContentStore/GCSFolder.m @@ -1,7 +1,7 @@ /* Copyright (C) 2004-2007 SKYRIX Software AG Copyright (C) 2007 Helge Hess - Copyright (c) 2008-2014 Inverse inc. + Copyright (c) 2008-2019 Inverse inc. This file is part of SOGo. diff --git a/SoObjects/Mailer/NSData+SMIME.h b/SoObjects/Mailer/NSData+SMIME.h index 8fce1c312..11404aaac 100644 --- a/SoObjects/Mailer/NSData+SMIME.h +++ b/SoObjects/Mailer/NSData+SMIME.h @@ -31,6 +31,8 @@ - (NSData *) encryptUsingCertificate: (NSData *) theData; - (NSData *) decryptUsingCertificate: (NSData *) theData; - (NGMimeMessage *) messageFromEncryptedDataAndCertificate: (NSData *) theCertificate; +- (NSData *) embeddedContent; +- (NGMimeMessage *) messageFromOpaqueSignedData; - (NSData *) convertPKCS12ToPEMUsingPassword: (NSString *) thePassword; - (NSData *) signersFromPKCS7; - (NSDictionary *) certificateDescription; diff --git a/SoObjects/Mailer/NSData+SMIME.m b/SoObjects/Mailer/NSData+SMIME.m index b4de19f36..64485f749 100644 --- a/SoObjects/Mailer/NSData+SMIME.m +++ b/SoObjects/Mailer/NSData+SMIME.m @@ -1,6 +1,6 @@ /* NSData+SMIME.m - this file is part of SOGo * - * Copyright (C) 2017-2018 Inverse inc. + * Copyright (C) 2017-2019 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -296,10 +296,86 @@ NGMimeMessageParser *parser; NGMimeMessage *message; NSData *decryptedData; + NGMimeType *contentType; + NSString *type, *subtype, *smimetype; decryptedData = [self decryptUsingCertificate: theCertificate]; parser = [[NGMimeMessageParser alloc] init]; message = [parser parsePartFromData: decryptedData]; + + // Extract contents if the encrypted messages contains opaque signed data + contentType = [message contentType]; + type = [[contentType type] lowercaseString]; + subtype = [[contentType subType] lowercaseString]; + if ([type isEqualToString: @"application"]) + { + if ([subtype isEqualToString: @"x-pkcs7-mime"] || + [subtype isEqualToString: @"pkcs7-mime"]) + { + smimetype = [[contentType valueOfParameter: @"smime-type"] lowercaseString]; + if ([smimetype isEqualToString: @"signed-data"]) + { + message = [decryptedData messageFromOpaqueSignedData]; + } + } + } + + RELEASE(parser); + + return message; +} + +- (NSData *) embeddedContent +{ + NSData *output = NULL; + + BIO *sbio, *obio; + BUF_MEM *bptr; + PKCS7 *p7 = NULL; + + sbio = BIO_new_mem_buf((void *)[self bytes], [self length]); + + p7 = SMIME_read_PKCS7(sbio, NULL); + + if (!p7) + { + NSLog(@"FATAL: could not read the signature"); + goto cleanup; + } + + // We output the S/MIME encrypted message + obio = BIO_new(BIO_s_mem()); + + if (!PKCS7_verify(p7, NULL, NULL, NULL, obio, PKCS7_NOVERIFY|PKCS7_NOSIGS)) + { + NSLog(@"FATAL: could not extract content"); + goto cleanup; + } + + BIO_get_mem_ptr(obio, &bptr); + + output = [NSData dataWithBytes: bptr->data length: bptr->length]; + + cleanup: + PKCS7_free(p7); + BIO_free(sbio); + BIO_free(obio); + + return output; +} + +// +// +// +- (NGMimeMessage *) messageFromOpaqueSignedData +{ + NGMimeMessageParser *parser; + NGMimeMessage *message; + NSData *extractedData; + + extractedData = [self embeddedContent]; + parser = [[NGMimeMessageParser alloc] init]; + message = [parser parsePartFromData: extractedData]; RELEASE(parser); return message; diff --git a/SoObjects/Mailer/SOGoDraftObject.m b/SoObjects/Mailer/SOGoDraftObject.m index a0a07ab9b..4238f3aae 100644 --- a/SoObjects/Mailer/SOGoDraftObject.m +++ b/SoObjects/Mailer/SOGoDraftObject.m @@ -872,6 +872,18 @@ static NSString *userAgent = nil; } +// +// +// +- (void) _fetchAttachmentsFromOpaqueSignedMail: (SOGoMailObject *) sourceMail +{ + NGMimeMessage *m; + + m = [[sourceMail content] messageFromOpaqueSignedData]; + [self _fileAttachmentsFromPart: [m body]]; +} + + // // // @@ -1007,6 +1019,8 @@ static NSString *userAgent = nil; [self setText: [sourceMail contentForInlineForward]]; if ([sourceMail isEncrypted]) [self _fetchAttachmentsFromEncryptedMail: sourceMail]; + else if ([sourceMail isOpaqueSigned]) + [self _fetchAttachmentsFromOpaqueSignedMail: sourceMail]; else [self _fetchAttachmentsFromMail: sourceMail]; } diff --git a/SoObjects/Mailer/SOGoMailBaseObject.m b/SoObjects/Mailer/SOGoMailBaseObject.m index f02c77dd8..8ef975e87 100644 --- a/SoObjects/Mailer/SOGoMailBaseObject.m +++ b/SoObjects/Mailer/SOGoMailBaseObject.m @@ -197,9 +197,7 @@ - (NGImap4Connection *) imap4Connection { NSString *cacheKey, *login; - SOGoCache *sogoCache; - if (!imap4) { diff --git a/SoObjects/Mailer/SOGoMailBodyPart.m b/SoObjects/Mailer/SOGoMailBodyPart.m index 0706183e3..032bdba69 100644 --- a/SoObjects/Mailer/SOGoMailBodyPart.m +++ b/SoObjects/Mailer/SOGoMailBodyPart.m @@ -210,6 +210,28 @@ static BOOL debugOn = NO; inContext: localContext]; obj = [clazz objectWithName:key inContainer: self]; } + else if ([o isOpaqueSigned]) + { + NGMimeMessage *m; + id part; + + int i; + + m = [[o content] messageFromOpaqueSignedData]; + part = [m body]; + + for (i = 0; i < [[self bodyPartPath] count]; i++) + { + nbr = [[[self bodyPartPath] objectAtIndex: i] intValue]-1; + part = [[part parts] objectAtIndex: nbr];; + } + + part = [[part parts] objectAtIndex: ([key intValue]-1)]; + mimeType = [[part contentType] stringValue]; + clazz = [SOGoMailBodyPart bodyPartClassForMimeType: mimeType + inContext: localContext]; + obj = [clazz objectWithName:key inContainer: self]; + } else { infos = [self partInfo]; @@ -353,6 +375,24 @@ static BOOL debugOn = NO; m = [[o content] messageFromEncryptedDataAndCertificate: certificate]; part = [m body]; + for (i = 0; i < [[self bodyPartPath] count]; i++) + { + nbr = [[[self bodyPartPath] objectAtIndex: i] intValue]-1; + part = [[part parts] objectAtIndex: nbr];; + } + + return [part body]; + } + else if ([o isOpaqueSigned]) + { + NGMimeMessage *m; + id part; + + unsigned int i, nbr; + + m = [[o content] messageFromOpaqueSignedData]; + part = [m body]; + for (i = 0; i < [[self bodyPartPath] count]; i++) { nbr = [[[self bodyPartPath] objectAtIndex: i] intValue]-1; diff --git a/SoObjects/Mailer/SOGoMailObject+Draft.m b/SoObjects/Mailer/SOGoMailObject+Draft.m index 19b3d6779..e714500d0 100644 --- a/SoObjects/Mailer/SOGoMailObject+Draft.m +++ b/SoObjects/Mailer/SOGoMailObject+Draft.m @@ -238,6 +238,24 @@ return nil; } + +// +// +// +- (NSString *) _contentForEditingFromOpaqueSignedMail +{ + SOGoUserDefaults *ud; + NGMimeMessage *m; + + m = [[self content] messageFromOpaqueSignedData]; + ud = [[context activeUser] userDefaults]; + + return [self _preferredContentFromPart: [m body] + favorHTML: [[ud mailComposeMessageType] isEqualToString: @"html"]]; + + return nil; +} + // // // @@ -250,6 +268,8 @@ if ([self isEncrypted]) output = [self _contentForEditingFromEncryptedMail]; + else if ([self isOpaqueSigned]) + output = [self _contentForEditingFromOpaqueSignedMail]; // If not encrypted or if decryption failed, we fallback // to the normal content fetching code. diff --git a/SoObjects/Mailer/SOGoMailObject.h b/SoObjects/Mailer/SOGoMailObject.h index 14e775052..2d412e47c 100644 --- a/SoObjects/Mailer/SOGoMailObject.h +++ b/SoObjects/Mailer/SOGoMailObject.h @@ -124,7 +124,8 @@ NSArray *SOGoMailCoreInfoKeys; - (BOOL) replied; /* \Answered */ - (BOOL) forwarded; /* $forwarded */ - (BOOL) deleted; /* \Deleted */ -- (BOOL) isSigned; /* S/MIME signed message */ +- (BOOL) isSigned; /* S/MIME signed message (detached signature) */ +- (BOOL) isOpaqueSigned; /* S/MIME signed message (embedded content) */ - (BOOL) isEncrypted; /* S/MIME encrypted message */ /* deletion */ diff --git a/SoObjects/Mailer/SOGoMailObject.m b/SoObjects/Mailer/SOGoMailObject.m index cb1ea818e..1c3d23d99 100644 --- a/SoObjects/Mailer/SOGoMailObject.m +++ b/SoObjects/Mailer/SOGoMailObject.m @@ -1201,6 +1201,19 @@ static BOOL debugSoParts = NO; return [clazz objectWithName:_key inContainer: self]; } } + else if ([self isOpaqueSigned]) + { + NGMimeMessage *m; + id part; + + m = [[self content] messageFromOpaqueSignedData]; + + part = [[[m body] parts] objectAtIndex: ([_key intValue]-1)]; + mimeType = [[part contentType] stringValue]; + clazz = [SOGoMailBodyPart bodyPartClassForMimeType: mimeType + inContext: _ctx]; + return [clazz objectWithName:_key inContainer: self]; + } parts = [[self bodyStructure] objectForKey: @"parts"]; @@ -1738,18 +1751,47 @@ static BOOL debugSoParts = NO; [protocol isEqualToString: @"application/pkcs7-signature"])); } -- (BOOL) isEncrypted +- (BOOL) isOpaqueSigned { - NSString *type, *subtype; + NSString *type, *subtype, *smimetype; + NGMimeType *contentType; - type = [[[[self mailHeaders] objectForKey: @"content-type"] type] lowercaseString]; - subtype = [[[[self mailHeaders] objectForKey: @"content-type"] subType] lowercaseString]; + contentType = [[self mailHeaders] objectForKey: @"content-type"]; + type = [[contentType type] lowercaseString]; + subtype = [[contentType subType] lowercaseString]; if ([type isEqualToString: @"application"]) { if ([subtype isEqualToString: @"x-pkcs7-mime"] || [subtype isEqualToString: @"pkcs7-mime"]) - return YES; + { + smimetype = [[contentType valueOfParameter: @"smime-type"] lowercaseString]; + if ([smimetype isEqualToString: @"signed-data"]) + return YES; + } + } + + return NO; +} + +- (BOOL) isEncrypted +{ + NSString *type, *subtype, *smimetype; + NGMimeType *contentType; + + contentType = [[self mailHeaders] objectForKey: @"content-type"]; + type = [[contentType type] lowercaseString]; + subtype = [[contentType subType] lowercaseString]; + + if ([type isEqualToString: @"application"]) + { + if ([subtype isEqualToString: @"x-pkcs7-mime"] || + [subtype isEqualToString: @"pkcs7-mime"]) + { + smimetype = [[contentType valueOfParameter: @"smime-type"] lowercaseString]; + if ([smimetype isEqualToString: @"enveloped-data"]) + return YES; + } } return NO; diff --git a/UI/MailPartViewers/UIxMailPartEncryptedViewer.h b/UI/MailPartViewers/UIxMailPartEncryptedViewer.h index 6c950e078..6887b98c0 100644 --- a/UI/MailPartViewers/UIxMailPartEncryptedViewer.h +++ b/UI/MailPartViewers/UIxMailPartEncryptedViewer.h @@ -28,8 +28,20 @@ @interface UIxMailPartEncryptedViewer : UIxMailPartViewer { + BOOL processed; + BOOL encrypted; + BOOL opaqueSigned; + BOOL validSignature; + NSMutableArray *certificates; + NSString *validationMessage; } +- (BOOL) validSignature; +- (NSString *) validationMessage; +- (NSArray *) smimeCertificates; +- (NSDictionary *) certificateForSubject: (NSString *) subject + andIssuer: (NSString *) issuer; + @end #endif /* UIXMAILPARTENCRYPTEDVIEWER_H */ diff --git a/UI/MailPartViewers/UIxMailPartEncryptedViewer.m b/UI/MailPartViewers/UIxMailPartEncryptedViewer.m index 43a314222..f188f9ba6 100644 --- a/UI/MailPartViewers/UIxMailPartEncryptedViewer.m +++ b/UI/MailPartViewers/UIxMailPartEncryptedViewer.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2018 Inverse inc. + Copyright (C) 2017-2019 Inverse inc. This file is part of SOGo. @@ -19,6 +19,14 @@ 02111-1307, USA. */ +#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) +#include +#include +#include +#include +#include +#endif + #import #import #import @@ -36,11 +44,222 @@ #import #import +#import + #import "UIxMailRenderingContext.h" #import "UIxMailPartEncryptedViewer.h" @implementation UIxMailPartEncryptedViewer +#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) +- (X509_STORE *) _setupVerify +{ + X509_STORE *store; + X509_LOOKUP *lookup; + BOOL success; + + success = NO; + + store = X509_STORE_new(); + OpenSSL_add_all_algorithms(); + + if (store) + { + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + if (lookup) + { + X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT); + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); + if (lookup) + { + X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT); + ERR_clear_error(); + success = YES; + } + } + } + + if (!success) + { + if (store) + { + X509_STORE_free(store); + store = NULL; + } + } + + return store; +} + +- (NSData *) _processMessageWith: (NSData *) signedData +{ + NSData *output; + + STACK_OF(X509) *certs; + X509_STORE *x509Store; + BIO *msgBio, *obio; + PKCS7 *p7; + int err, i; + + ERR_clear_error(); + + msgBio = BIO_new_mem_buf ((void *) [signedData bytes], [signedData length]); + + p7 = SMIME_read_PKCS7(msgBio, NULL); + + certs = NULL; + certificates = [NSMutableArray array]; + validationMessage = nil; + + if (p7) + { + if (OBJ_obj2nid(p7->type) == NID_pkcs7_signed) + { + NSString *subject, *issuer; + X509 *x; + + certs = p7->d.sign->cert; + + for (i = 0; i < sk_X509_num(certs); i++) + { + BIO *buf; + char p[1024]; + + x = sk_X509_value(certs, i); + + memset(p, 0, 1024); + buf = BIO_new(BIO_s_mem()); + X509_NAME_print_ex(buf, X509_get_subject_name(x), 0, + ASN1_STRFLGS_ESC_CTRL | XN_FLAG_SEP_MULTILINE | XN_FLAG_FN_LN); + BIO_read(buf, p, 1024); + subject = [NSString stringWithUTF8String: p]; + BIO_free(buf); + + memset(p, 0, 1024); + buf = BIO_new(BIO_s_mem()); + X509_NAME_print_ex(buf, X509_get_issuer_name(x), 0, + ASN1_STRFLGS_ESC_CTRL | XN_FLAG_SEP_MULTILINE | XN_FLAG_FN_LN); + BIO_read(buf, p, 1024); + issuer = [NSString stringWithUTF8String: p]; + BIO_free(buf); + + [certificates addObject: [self certificateForSubject: subject + andIssuer: issuer]]; + } + } + + err = ERR_get_error(); + if (err) + { + validSignature = NO; + } + else + { + x509Store = [self _setupVerify]; + obio = BIO_new(BIO_s_mem()); + + validSignature = (PKCS7_verify(p7, NULL, x509Store, NULL, + obio, 0) == 1); + + err = ERR_get_error(); + + if (x509Store) + X509_STORE_free (x509Store); + } + + if (err) + { +#ifdef HAVE_GNUTLS + const char* sslError; + ERR_load_crypto_strings(); + SSL_load_error_strings(); + sslError = ERR_reason_error_string(err); + validationMessage = [[self labelForKey: [NSString stringWithUTF8String: sslError ? sslError : @"No error information available"]] retain]; +#elif OPENSSL_VERSION_NUMBER < 0x10100000L + const char* sslError; + ERR_load_crypto_strings(); + SSL_load_error_strings(); + sslError = ERR_reason_error_string(err); + validationMessage = [[self labelForKey: [NSString stringWithUTF8String: sslError ? sslError : @"No error information available"]] retain]; +#else + validationMessage = [[self labelForKey: @"No error information available"] retain]; +#endif /* HAVE_GNUTLS */ + + BUF_MEM *bptr; //DEL + BIO_get_mem_ptr(obio, &bptr); //DEL + // extract contents without validation + output = [ signedData embeddedContent ]; + } + else + { + BUF_MEM *bptr; + BIO_get_mem_ptr(obio, &bptr); + output = [NSData dataWithBytes: bptr->data length: bptr->length]; + } + } + + PKCS7_free(p7); + BIO_free (msgBio); + BIO_free (obio); + + if (validSignature) + validationMessage = [NSString stringWithString: [self labelForKey: @"Message is signed"]]; + else if (!validationMessage) + validationMessage = [NSString stringWithString: [self labelForKey: @"Digital signature is not valid"]]; + + processed = YES; + opaqueSigned = YES; + return output; +} + +- (BOOL) validSignature +{ + if (!processed) + NSLog(@"ERROR: validSignature called but not processed yet"); + //[self _processMessage]; + + return validSignature; +} + +- (NSDictionary *) certificateForSubject: (NSString *) subject + andIssuer: (NSString *) issuer +{ + return [NSDictionary dictionaryWithObjectsAndKeys: + [subject componentsFromMultilineDN], @"subject", + [issuer componentsFromMultilineDN], @"issuer", + nil]; +} + +- (NSArray *) smimeCertificates +{ + return certificates; +} + +- (NSString *) validationMessage +{ + if (!processed) + NSLog(@"ERROR: validationMessage called but not processed yet"); + //[self _processMessage]; + + return validationMessage; +} +#else +- (NSArray *) smimeCertificates +{ + return nil; +} + +- (BOOL) validSignature +{ + return NO; +} + +- (NSString *) validationMessage +{ + return nil; +} +#endif + - (void) _attachmentIdsFromBodyPart: (id) thePart partPath: (NSString *) thePartPath { @@ -91,26 +310,112 @@ - (id) renderedPart { + SOGoMailObject *mailObject; NSData *certificate, *decryptedData, *encryptedData; id info, viewer; - certificate = [[[self clientObject] mailAccountFolder] certificate]; - encryptedData = [[self clientObject] content]; - decryptedData = [encryptedData decryptUsingCertificate: certificate]; + mailObject = [[self clientObject] mailObject]; + if ([mailObject isEncrypted]) + { + encrypted = YES; + certificate = [[[self clientObject] mailAccountFolder] certificate]; + encryptedData = [[self clientObject] content]; + decryptedData = [encryptedData decryptUsingCertificate: certificate]; - if (decryptedData) + if (decryptedData) + { + NGMimeMessageParser *parser; + NGMimeMessage *message; + NGMimeType *contentType; + NSString *type, *subtype, *smimetype; + id part; + + parser = [[NGMimeMessageParser alloc] init]; + message = [parser parsePartFromData: decryptedData]; + + // Extract contents if the encrypted messages contains opaque signed data + contentType = [message contentType]; + type = [[contentType type] lowercaseString]; + subtype = [[contentType subType] lowercaseString]; + if ([type isEqualToString: @"application"]) + { + if ([subtype isEqualToString: @"x-pkcs7-mime"] || + [subtype isEqualToString: @"pkcs7-mime"]) + { + smimetype = [[contentType valueOfParameter: @"smime-type"] lowercaseString]; + if ([smimetype isEqualToString: @"signed-data"]) + { + NGMimeMessageParser *parser; + NSData *extractedData; + + opaqueSigned = YES; + extractedData = [self _processMessageWith: decryptedData]; + if (extractedData) + { + parser = [[NGMimeMessageParser alloc] init]; + message = [parser parsePartFromData: extractedData]; + decryptedData = extractedData; + RELEASE(parser); + } + } + } + } + + processed = YES; + part = [message retain]; + + info = [NSDictionary dictionaryWithObjectsAndKeys: [[part contentType] type], @"type", + [[part contentType] subType], @"subtype", + [[part contentType] parametersAsDictionary], @"parameterList", nil]; + viewer = [[[self context] mailRenderingContext] viewerForBodyInfo: info]; + [viewer setBodyInfo: info]; + [viewer setFlatContent: decryptedData]; + [viewer setDecodedContent: [part body]]; + + // attachmentIds is empty in an ecrypted email as the IMAP body structure + // is of course not available for file attachments + [self _attachmentIdsFromBodyPart: [part body] partPath: @""]; + [viewer setAttachmentIds: attachmentIds]; + + return [NSDictionary dictionaryWithObjectsAndKeys: + [self className], @"type", + [NSNumber numberWithBool: YES], @"encrypted", + [NSNumber numberWithBool: YES], @"decrypted", + [NSNumber numberWithBool: opaqueSigned], @"opaqueSigned", + [NSNumber numberWithBool: [self validSignature]], @"valid", + [NSArray arrayWithObject: [viewer renderedPart]], @"content", + [self smimeCertificates], @"certificates", + [self validationMessage], @"message", + nil]; + } + } + else if ([mailObject isOpaqueSigned]) { NGMimeMessageParser *parser; + NGMimeMessage *message; + NSData *extractedData; id part; - parser = [[NGMimeMessageParser alloc] init]; - part = [[parser parsePartFromData: decryptedData] retain]; + opaqueSigned = YES; + encryptedData = [[self clientObject] content]; + extractedData = [self _processMessageWith: encryptedData]; + + if (extractedData) + { + parser = [[NGMimeMessageParser alloc] init]; + message = [parser parsePartFromData: extractedData]; + RELEASE(parser); + } + + processed = YES; + part = [message retain]; info = [NSDictionary dictionaryWithObjectsAndKeys: [[part contentType] type], @"type", - [[part contentType] subType], @"subtype", nil]; + [[part contentType] subType], @"subtype", + [[part contentType] parametersAsDictionary], @"parameterList", nil]; viewer = [[[self context] mailRenderingContext] viewerForBodyInfo: info]; [viewer setBodyInfo: info]; - [viewer setFlatContent: decryptedData]; + [viewer setFlatContent: extractedData]; [viewer setDecodedContent: [part body]]; // attachmentIds is empty in an ecrypted email as the IMAP body structure @@ -120,8 +425,12 @@ return [NSDictionary dictionaryWithObjectsAndKeys: [self className], @"type", - [NSNumber numberWithBool: YES], @"valid", + [NSNumber numberWithBool: NO], @"encrypted", + [NSNumber numberWithBool: YES], @"opaqueSigned", + [NSNumber numberWithBool: [self validSignature]], @"valid", [NSArray arrayWithObject: [viewer renderedPart]], @"content", + [self smimeCertificates], @"certificates", + [self validationMessage], @"message", nil]; } @@ -129,7 +438,9 @@ // Decryption failed, let's return something else... return [NSDictionary dictionaryWithObjectsAndKeys: [self className], @"type", - [NSNumber numberWithBool: NO], @"valid", + [NSNumber numberWithBool: encrypted], @"encrypted", + [NSNumber numberWithBool: NO], @"decrypted", + [NSNumber numberWithBool: NO], @"opaqueSigned", [NSArray array], @"content", nil]; } diff --git a/UI/MailPartViewers/UIxMailRenderingContext.m b/UI/MailPartViewers/UIxMailRenderingContext.m index e2cf01155..683e1da46 100644 --- a/UI/MailPartViewers/UIxMailRenderingContext.m +++ b/UI/MailPartViewers/UIxMailRenderingContext.m @@ -256,10 +256,20 @@ static BOOL showNamedTextAttachmentsInline = NO; if ([st isEqualToString: @"x-pkcs7-mime"] || [st isEqualToString: @"pkcs7-mime"]) { - // If the mail account has a valid certificate, we try to decode - // the encrypted email. Otherwise, we fallback to a link viewer - if ([[[viewer clientObject] mailAccountFolder] certificate]) - return [self encryptedViewer]; + NSString *smt; + + smt = [[[_info objectForKey:@"parameterList"] valueForKey:@"smime-type"] lowercaseString]; + if ([smt isEqualToString:@"signed-data"]) + { + return [self encryptedViewer]; + } + else if ([smt isEqualToString:@"enveloped-data"]) + { + // If the mail account has a valid certificate, we try to decode + // the encrypted email. Otherwise, we fallback to a link viewer + if ([[[viewer clientObject] mailAccountFolder] certificate]) + return [self encryptedViewer]; + } } #if 0 /* the link viewer looks better than plain text ;-) */ diff --git a/UI/MailerUI/UIxMailView.m b/UI/MailerUI/UIxMailView.m index b44952053..3e2b16ae0 100644 --- a/UI/MailerUI/UIxMailView.m +++ b/UI/MailerUI/UIxMailView.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2005-2015 Inverse inc. + Copyright (C) 2005-2019 Inverse inc. This file is part of SOGo. diff --git a/UI/WebServerResources/js/Mailer/Message.service.js b/UI/WebServerResources/js/Mailer/Message.service.js index 880e14d6d..1b1316b02 100644 --- a/UI/WebServerResources/js/Mailer/Message.service.js +++ b/UI/WebServerResources/js/Mailer/Message.service.js @@ -340,13 +340,22 @@ }; } else if (part.type == 'UIxMailPartEncryptedViewer') { - _this.encrypted = { - valid: part.valid - }; - if (part.valid) - _this.encrypted.message = l("This message is encrypted"); - else - _this.encrypted.message = l("This message can't be decrypted. Please make sure you have uploaded your S/MIME certificate from the mail preferences module."); + if (part.encrypted) { + _this.encrypted = { + valid: part.decrypted + }; + if (part.decrypted) + _this.encrypted.message = l("This message is encrypted"); + else + _this.encrypted.message = l("This message can't be decrypted. Please make sure you have uploaded your S/MIME certificate from the mail preferences module."); + } + if (part.opaqueSigned) { + _this.signed = { + valid: part.valid, + certificate: part.certificates[part.certificates.length - 1], + message: part.message + }; + } } _.forEach(part.content, function(mixedPart) { _visit(mixedPart);