Skip to content

Commit

Permalink
(feat) added support for S/MIME opaque signing (fixes #4582)
Browse files Browse the repository at this point in the history
  • Loading branch information
extrafu committed Aug 19, 2019
1 parent b52abfc commit 676d2e6
Show file tree
Hide file tree
Showing 15 changed files with 568 additions and 32 deletions.
1 change: 1 addition & 0 deletions NEWS
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion 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.
Expand Down
2 changes: 2 additions & 0 deletions SoObjects/Mailer/NSData+SMIME.h
Expand Up @@ -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;
Expand Down
78 changes: 77 additions & 1 deletion 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
Expand Down Expand Up @@ -296,10 +296,86 @@ - (NGMimeMessage *) messageFromEncryptedDataAndCertificate: (NSData *) theCertif
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;
Expand Down
14 changes: 14 additions & 0 deletions SoObjects/Mailer/SOGoDraftObject.m
Expand Up @@ -872,6 +872,18 @@ - (void) _fetchAttachmentsFromEncryptedMail: (SOGoMailObject *) sourceMail
}


//
//
//
- (void) _fetchAttachmentsFromOpaqueSignedMail: (SOGoMailObject *) sourceMail
{
NGMimeMessage *m;

m = [[sourceMail content] messageFromOpaqueSignedData];
[self _fileAttachmentsFromPart: [m body]];
}


//
//
//
Expand Down Expand Up @@ -1007,6 +1019,8 @@ - (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail
[self setText: [sourceMail contentForInlineForward]];
if ([sourceMail isEncrypted])
[self _fetchAttachmentsFromEncryptedMail: sourceMail];
else if ([sourceMail isOpaqueSigned])
[self _fetchAttachmentsFromOpaqueSignedMail: sourceMail];
else
[self _fetchAttachmentsFromMail: sourceMail];
}
Expand Down
2 changes: 0 additions & 2 deletions SoObjects/Mailer/SOGoMailBaseObject.m
Expand Up @@ -197,9 +197,7 @@ - (NGImap4Connection *) _createIMAP4Connection
- (NGImap4Connection *) imap4Connection
{
NSString *cacheKey, *login;

SOGoCache *sogoCache;


if (!imap4)
{
Expand Down
40 changes: 40 additions & 0 deletions SoObjects/Mailer/SOGoMailBodyPart.m
Expand Up @@ -210,6 +210,28 @@ - (id) lookupImap4BodyPartKey: (NSString *) key
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];
Expand Down Expand Up @@ -353,6 +375,24 @@ - (NSData *) fetchBLOB
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;
Expand Down
20 changes: 20 additions & 0 deletions SoObjects/Mailer/SOGoMailObject+Draft.m
Expand Up @@ -238,6 +238,24 @@ - (NSString *) _contentForEditingFromEncryptedMail
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;
}

//
//
//
Expand All @@ -250,6 +268,8 @@ - (NSString *) contentForEditing

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.
Expand Down
3 changes: 2 additions & 1 deletion SoObjects/Mailer/SOGoMailObject.h
Expand Up @@ -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 */
Expand Down
50 changes: 46 additions & 4 deletions SoObjects/Mailer/SOGoMailObject.m
Expand Up @@ -1201,6 +1201,19 @@ - (id) lookupImap4BodyPartKey: (NSString *) _key
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"];

Expand Down Expand Up @@ -1738,18 +1751,47 @@ - (BOOL) isSigned
[protocol isEqualToString: @"application/pkcs7-signature"]));
}

- (BOOL) isOpaqueSigned
{
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: @"signed-data"])
return YES;
}
}

return NO;
}

- (BOOL) isEncrypted
{
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: @"enveloped-data"])
return YES;
}
}

return NO;
Expand Down
12 changes: 12 additions & 0 deletions UI/MailPartViewers/UIxMailPartEncryptedViewer.h
Expand Up @@ -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 */

0 comments on commit 676d2e6

Please sign in to comment.