From 6eb5e971545830d4ee8e4ebe6299d11c97ccd96b Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Fri, 29 Oct 2021 16:34:36 -0400 Subject: [PATCH] fix(mail): check if smime certificate matches sender address Fixes #5407 --- .../UIxMailPartEncryptedViewer.m | 57 ++++++++++++++++-- UI/MailPartViewers/UIxMailPartSignedViewer.m | 60 +++++++++++++++++-- UI/MailerUI/English.lproj/Localizable.strings | 10 +++- 3 files changed, 113 insertions(+), 14 deletions(-) diff --git a/UI/MailPartViewers/UIxMailPartEncryptedViewer.m b/UI/MailPartViewers/UIxMailPartEncryptedViewer.m index 01e8c7fda..a35400bc1 100644 --- a/UI/MailPartViewers/UIxMailPartEncryptedViewer.m +++ b/UI/MailPartViewers/UIxMailPartEncryptedViewer.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2019 Inverse inc. + Copyright (C) 2017-2021 Inverse inc. This file is part of SOGo. @@ -32,6 +32,7 @@ #import #import +#import #import #import #import @@ -44,6 +45,7 @@ #import #import +#import #import #import "UIxMailRenderingContext.h" @@ -175,15 +177,18 @@ ERR_load_crypto_strings(); SSL_load_error_strings(); sslError = ERR_reason_error_string(err); - validationMessage = [[self labelForKey: [NSString stringWithUTF8String: sslError ? sslError : @"Digital signature is not valid"]] retain]; + validationMessage = [[self labelForKey: sslError ? [NSString stringWithUTF8String: sslError] : @"Digital signature is not valid"] 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 : @"Digital signature is not valid"]] retain]; + validationMessage = [[self labelForKey: sslError ? [NSString stringWithUTF8String: sslError] : @"Digital signature is not valid"] retain]; #else - validationMessage = [[self labelForKey: @"Digital signature is not valid"] retain]; + const char* sslError; + ERR_load_ERR_strings(); + sslError = ERR_reason_error_string(err); + validationMessage = [[self labelForKey: sslError ? [NSString stringWithUTF8String: sslError] : @"Digital signature is not valid"] retain]; #endif /* HAVE_GNUTLS */ BUF_MEM *bptr; //DEL @@ -204,9 +209,49 @@ BIO_free (obio); if (validSignature) - validationMessage = [NSString stringWithString: [self labelForKey: @"Message is signed"]]; + { + BOOL hasMatchingAddress; + NSArray *pair; + NSDictionary *certificate, *values; + NSEnumerator *certificatesList, *subjectList; + NSString *senderAddress, *label, *value; + + validationMessage = [self labelForKey: @"Message is signed"]; + hasMatchingAddress = NO; + value = nil; + senderAddress = [[[[[self clientObject] fromEnvelopeAddresses] lastObject] baseEMail] lowercaseString]; + certificatesList = [certificates objectEnumerator]; + while ((certificate = [certificatesList nextObject]) && !hasMatchingAddress) + { + subjectList = [[certificate objectForKey: @"subject"] objectEnumerator]; + while ((pair = [subjectList nextObject]) && !hasMatchingAddress) + { + label = [[pair objectAtIndex: 0] lowercaseString]; + value = [[pair objectAtIndex: 1] lowercaseString]; + if ([label isEqualToString: @"commonname"] && [value isEqualToString: senderAddress]) + { + hasMatchingAddress = 1; + } + } + } + + if (!hasMatchingAddress) + { + if (value) + { + values = [NSDictionary dictionaryWithObjectsAndKeys: value, @"certificateCn", nil]; + validationMessage = [values keysWithFormat: [self labelForKey: @"Message is signed but the certificate (%{certificateCn}) doesn't match the sender email address"]]; + } + else + { + validationMessage = [self labelForKey: @"Message is signed but the certificate doesn't match the sender email address"]; + } + } + } else if (!validationMessage) - validationMessage = [NSString stringWithString: [self labelForKey: @"Digital signature is not valid"]]; + { + validationMessage = [NSString stringWithString: [self labelForKey: @"Digital signature is not valid"]]; + } processed = YES; opaqueSigned = YES; diff --git a/UI/MailPartViewers/UIxMailPartSignedViewer.m b/UI/MailPartViewers/UIxMailPartSignedViewer.m index 8269c2609..5923f0d8c 100644 --- a/UI/MailPartViewers/UIxMailPartSignedViewer.m +++ b/UI/MailPartViewers/UIxMailPartSignedViewer.m @@ -1,6 +1,6 @@ /* UIxMailPartSignedViewer.m - this file is part of SOGo * - * Copyright (C) 2009-2018 Inverse inc. + * Copyright (C) 2009-2021 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 @@ -29,8 +29,12 @@ #import #import +#import + +#import #import +#import #import #import @@ -173,15 +177,18 @@ ERR_load_crypto_strings(); SSL_load_error_strings(); sslError = ERR_reason_error_string(err); - validationMessage = [[self labelForKey: [NSString stringWithUTF8String: sslError ? sslError : @"Digital signature is not valid"]] retain]; + validationMessage = [[self labelForKey: sslError ? [NSString stringWithUTF8String: sslError] : @"Digital signature is not valid"] 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 : @"Digital signature is not valid"]] retain]; + validationMessage = [[self labelForKey: sslError ? [NSString stringWithUTF8String: sslError] : @"Digital signature is not valid"] retain]; #else - validationMessage = [[self labelForKey: @"Digital signature is not valid"] retain]; + const char* sslError; + ERR_load_ERR_strings(); + sslError = ERR_reason_error_string(err); + validationMessage = [[self labelForKey: sslError ? [NSString stringWithUTF8String: sslError] : @"Digital signature is not valid"] retain]; #endif /* HAVE_GNUTLS */ } } @@ -190,11 +197,52 @@ BIO_free (msgBio); if (inData) BIO_free (inData); + if (validSignature) - validationMessage = [NSString stringWithString: [self labelForKey: @"Message is signed"]]; + { + BOOL hasMatchingAddress; + NSArray *pair; + NSDictionary *certificate, *values; + NSEnumerator *certificatesList, *subjectList; + NSString *senderAddress, *label, *value; + + validationMessage = [self labelForKey: @"Message is signed"]; + hasMatchingAddress = NO; + value = nil; + senderAddress = [[[[[self clientObject] fromEnvelopeAddresses] lastObject] baseEMail] lowercaseString]; + certificatesList = [certificates objectEnumerator]; + while ((certificate = [certificatesList nextObject]) && !hasMatchingAddress) + { + subjectList = [[certificate objectForKey: @"subject"] objectEnumerator]; + while ((pair = [subjectList nextObject]) && !hasMatchingAddress) + { + label = [[pair objectAtIndex: 0] lowercaseString]; + value = [[pair objectAtIndex: 1] lowercaseString]; + if ([label isEqualToString: @"commonname"] && [value isEqualToString: senderAddress]) + { + hasMatchingAddress = 1; + } + } + } + + if (!hasMatchingAddress) + { + if (value) + { + values = [NSDictionary dictionaryWithObjectsAndKeys: value, @"certificateCn", nil]; + validationMessage = [values keysWithFormat: [self labelForKey: @"Message is signed but the certificate (%{certificateCn}) doesn't match the sender email address"]]; + } + else + { + validationMessage = [self labelForKey: @"Message is signed but the certificate doesn't match the sender email address"]; + } + } + } else if (!validationMessage) - validationMessage = [NSString stringWithString: [self labelForKey: @"Digital signature is not valid"]]; + { + validationMessage = [NSString stringWithString: [self labelForKey: @"Digital signature is not valid"]]; + } processed = YES; } diff --git a/UI/MailerUI/English.lproj/Localizable.strings b/UI/MailerUI/English.lproj/Localizable.strings index 804bb390e..7c682b216 100644 --- a/UI/MailerUI/English.lproj/Localizable.strings +++ b/UI/MailerUI/English.lproj/Localizable.strings @@ -290,12 +290,18 @@ /* Trying to access a non-existent certificate */ "No certificate associated to account." = "No certificate associated to account."; -/* Invalid message signature */ +/* Invalid S/MIME signature */ "Digital signature is not valid" = "Digital signature is not valid"; -/* Valid message signature */ +/* Valid S/MIME signature */ "Message is signed" = "Message is signed"; +/* Valid S/MIME signature but common name doesn't match */ +"Message is signed but the certificate (%{certificateCn}) doesn't match the sender email address" = "Message is signed but the certificate (%{certificateCn}) doesn't match the sender email address"; + +/* Valid S/MIME signature but common name not found */ +"Message is signed but the certificate doesn't match the sender email address" = "Message is signed but the certificate doesn't match the sender email address"; + /* Unknown error while validating message signature */ "Digital signature is not valid" = "Digital signature is not valid";