mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-08-25 16:33:22 +00:00
View smime certificate of signed messages
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/* UIxMailPartSignedViewer.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009-2017 Inverse inc.
|
||||
* Copyright (C) 2009-2018 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
|
||||
@@ -30,11 +30,15 @@
|
||||
{
|
||||
BOOL processed;
|
||||
BOOL validSignature;
|
||||
NSMutableString *validationMessage;
|
||||
NSMutableArray *certificates;
|
||||
NSString *validationMessage;
|
||||
}
|
||||
|
||||
- (BOOL) validSignature;
|
||||
- (NSString *) validationMessage;
|
||||
- (NSArray *) smimeCertificates;
|
||||
- (NSDictionary *) certificateForSubject: (NSString *) subject
|
||||
andIssuer: (NSString *) issuer;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* UIxMailPartSignedViewer.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009-2017 Inverse inc.
|
||||
* Copyright (C) 2009-2018 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
|
||||
@@ -53,19 +53,19 @@
|
||||
|
||||
success = NO;
|
||||
|
||||
store = X509_STORE_new ();
|
||||
OpenSSL_add_all_algorithms ();
|
||||
store = X509_STORE_new();
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
if (store)
|
||||
{
|
||||
lookup = X509_STORE_add_lookup (store, X509_LOOKUP_file());
|
||||
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());
|
||||
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);
|
||||
X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
|
||||
ERR_clear_error();
|
||||
success = YES;
|
||||
}
|
||||
@@ -86,18 +86,15 @@
|
||||
|
||||
- (void) _processMessage
|
||||
{
|
||||
NSString *issuer, *subject;
|
||||
NSData *signedData;
|
||||
|
||||
STACK_OF(X509) *certs;
|
||||
X509_STORE *x509Store;
|
||||
BIO *msgBio, *inData;
|
||||
char sslError[1024];
|
||||
const char* sslError;
|
||||
PKCS7 *p7;
|
||||
int err, i;
|
||||
|
||||
memset(sslError, 0, 1024);
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]])
|
||||
@@ -110,45 +107,50 @@
|
||||
inData = NULL;
|
||||
p7 = SMIME_read_PKCS7(msgBio, &inData);
|
||||
|
||||
subject = nil;
|
||||
issuer = nil;
|
||||
certs = NULL;
|
||||
certificates = [NSMutableArray array];
|
||||
validationMessage = nil;
|
||||
|
||||
if (p7)
|
||||
{
|
||||
i = OBJ_obj2nid(p7->type);
|
||||
|
||||
if (i == NID_pkcs7_signed)
|
||||
if (OBJ_obj2nid(p7->type) == NID_pkcs7_signed)
|
||||
{
|
||||
NSString *subject, *issuer;
|
||||
X509 *x;
|
||||
|
||||
certs=p7->d.sign->cert;
|
||||
|
||||
if (sk_X509_num(certs) > 0)
|
||||
{
|
||||
certs = p7->d.sign->cert;
|
||||
|
||||
for (i = 0; i < sk_X509_num(certs); i++)
|
||||
{
|
||||
BIO *buf;
|
||||
char p[256];
|
||||
|
||||
memset(p, 0, 256);
|
||||
x = sk_X509_value(certs,0);
|
||||
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, XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB);
|
||||
BIO_gets(buf, p, 256);
|
||||
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];
|
||||
|
||||
memset(p, 0, 256);
|
||||
X509_NAME_print_ex(buf, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB);
|
||||
BIO_gets(buf, p, 256);
|
||||
issuer = [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)
|
||||
{
|
||||
ERR_error_string_n (err, sslError, 1023);
|
||||
validSignature = NO;
|
||||
}
|
||||
else
|
||||
@@ -158,30 +160,29 @@
|
||||
NULL, PKCS7_DETACHED) == 1);
|
||||
|
||||
err = ERR_get_error();
|
||||
if (err)
|
||||
ERR_error_string_n(err, sslError, 1023);
|
||||
|
||||
if (x509Store)
|
||||
X509_STORE_free (x509Store);
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
ERR_load_crypto_strings();
|
||||
sslError = ERR_reason_error_string(err);
|
||||
validationMessage = [[self labelForKey: [NSString stringWithUTF8String: sslError]] retain];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIO_free (msgBio);
|
||||
if (inData)
|
||||
BIO_free (inData);
|
||||
|
||||
validationMessage = [NSMutableString string];
|
||||
if (validSignature)
|
||||
validationMessage = [NSString stringWithString: [self labelForKey: @"Message is signed"]];
|
||||
else if (!validationMessage)
|
||||
validationMessage = [NSString stringWithString: [self labelForKey: @"Digital signature is not valid"]];
|
||||
|
||||
if (!validSignature)
|
||||
[validationMessage appendString: [self labelForKey: @"Digital signature is not valid"]];
|
||||
else
|
||||
[validationMessage appendString: [self labelForKey: @"Message is signed"]];
|
||||
|
||||
if (issuer && subject)
|
||||
[validationMessage appendFormat: @"\n%@: %@\n%@: %@",
|
||||
[self labelForKey: @"Subject"], subject,
|
||||
[self labelForKey: @"Issuer"], issuer];
|
||||
|
||||
processed = YES;
|
||||
}
|
||||
|
||||
@@ -193,6 +194,41 @@
|
||||
return validSignature;
|
||||
}
|
||||
|
||||
- (NSArray *) componentsForDN: (NSString *) dn
|
||||
{
|
||||
NSArray *pair;
|
||||
NSEnumerator *componentsEnum;
|
||||
NSMutableArray *components;
|
||||
NSString *pairString;
|
||||
|
||||
components = [NSMutableArray array];
|
||||
componentsEnum = [[dn componentsSeparatedByString: @"\n"] objectEnumerator];
|
||||
while (( pairString = [componentsEnum nextObject] ))
|
||||
{
|
||||
pair = [pairString componentsSeparatedByString: @"="];
|
||||
if ([pair count] == 2)
|
||||
[components addObject: [NSArray arrayWithObjects:
|
||||
[self labelForKey: [pair objectAtIndex: 0]],
|
||||
[pair objectAtIndex: 1], nil]];
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
- (NSDictionary *) certificateForSubject: (NSString *) subject
|
||||
andIssuer: (NSString *) issuer
|
||||
{
|
||||
return [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[self componentsForDN: subject], @"subject",
|
||||
[self componentsForDN: issuer], @"issuer",
|
||||
nil];
|
||||
}
|
||||
|
||||
- (NSArray *) smimeCertificates
|
||||
{
|
||||
return certificates;
|
||||
}
|
||||
|
||||
- (NSString *) validationMessage
|
||||
{
|
||||
if (!processed)
|
||||
@@ -206,6 +242,11 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSArray *) smimeCertificates
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL) validSignature
|
||||
{
|
||||
return NO;
|
||||
@@ -251,12 +292,16 @@
|
||||
[renderedParts addObject: [viewer renderedPart]];
|
||||
}
|
||||
|
||||
if (!processed)
|
||||
[self _processMessage];
|
||||
|
||||
return [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[self className], @"type",
|
||||
[NSNumber numberWithBool: [self supportsSMIME]], @"supports-smime",
|
||||
[NSNumber numberWithBool: [self validSignature]], @"valid",
|
||||
[self validationMessage], @"error",
|
||||
renderedParts, @"content",
|
||||
[self smimeCertificates], @"certificates",
|
||||
[self validationMessage], @"error",
|
||||
nil];
|
||||
}
|
||||
|
||||
|
||||
@@ -246,6 +246,19 @@
|
||||
/* Number of selected messages in list */
|
||||
"selected" = "selected";
|
||||
|
||||
/* SMIME Certificate field */
|
||||
"countryName" = "Country";
|
||||
"organizationName" = "Organization";
|
||||
"organizationalUnitName" = "Organizational Unit";
|
||||
"commonName" = "Common Name";
|
||||
"emailAddress" = "Email Address";
|
||||
|
||||
/* OpenSSL certificate error - unknown issuer */
|
||||
"certificate verify error" = "This certificate was signed by an unknown issuer";
|
||||
|
||||
/* OpenSSL certificate error - email mismatch */
|
||||
"digest failure" = "This certificate doesn't match the sender email address";
|
||||
|
||||
"This Folder" = "This Folder";
|
||||
|
||||
/* Label popup menu */
|
||||
|
||||
@@ -206,16 +206,42 @@
|
||||
</div>
|
||||
|
||||
<!-- S/MIME Signature -->
|
||||
<md-whiteframe class="md-whiteframe-z2 sg-no-print" layout="row" layout-align="space-between center"
|
||||
ng-show="::viewer.message.$smime.message">
|
||||
<div layout="row" layout-align="start center">
|
||||
<div class="md-tile-left">
|
||||
<md-icon ng-hide="::viewer.message.$smime.validSignature" class="md-warn">error</md-icon>
|
||||
<md-icon ng-show="::viewer.message.$smime.validSignature" class="md-accent">check</md-icon>
|
||||
<sg-block-toggle class="sg-no-print" layout="column"
|
||||
ng-show="::viewer.message.$smime.message">
|
||||
<md-divider><!-- divider --></md-divider>
|
||||
<md-list-item class="sg-button-toggle">
|
||||
<div>
|
||||
<md-icon ng-hide="::viewer.message.$smime.validSignature"
|
||||
class="md-warn"
|
||||
rsrc:md-svg-src="img/certificate-off.svg"><!-- certificate --></md-icon>
|
||||
<md-icon ng-show="::viewer.message.$smime.validSignature"
|
||||
class="md-accent"
|
||||
rsrc:md-svg-src="img/certificate.svg"><!-- certificate --></md-icon>
|
||||
</div>
|
||||
<p class="md-padding md-flex" ng-bind-html="::viewer.message.$smime.message"><!-- message --></p>
|
||||
<md-icon class="sg-icon-toggle">expand_more</md-icon>
|
||||
</md-list-item>
|
||||
<div class="sg-block-toggle">
|
||||
<div class="md-margin" md-whiteframe="3">
|
||||
<div class="md-padding" layout="row">
|
||||
<div flex="50">
|
||||
<h6 class="md-subhead md-default-theme md-fg md-primary">Subject Name</h6>
|
||||
<div ng-repeat="field in ::viewer.message.$smime.certificate.subject">
|
||||
<div class="pseudo-input-label" ng-bind="field[0]"><!-- label --></div>
|
||||
<div class="pseudo-input-field md-body-1" ng-bind="field[1]"><!-- value --></div>
|
||||
</div>
|
||||
</div>
|
||||
<div flex="50">
|
||||
<h6 class="md-subhead md-default-theme md-fg md-primary">Issuer</h6>
|
||||
<div ng-repeat="field in ::viewer.message.$smime.certificate.issuer">
|
||||
<div class="pseudo-input-label" ng-bind="field[0]"><!-- label --></div>
|
||||
<div class="pseudo-input-field md-body-1" ng-bind="field[1]"><!-- value --></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p ng-bind-html="::viewer.message.$smime.message"><!-- message --></p>
|
||||
</div>
|
||||
</md-whiteframe>
|
||||
</sg-block-toggle>
|
||||
|
||||
<!-- Load external images -->
|
||||
<md-whiteframe class="md-whiteframe-z2 sg-no-print" layout="row" layout-align="space-between center"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M4.859 4.251l.013-.004 14.881 14.881-.004.013 1.599 1.599-1.262 1.263-2.538-2.538-1.477-.42-.642 2.517c-.09.358-.317.59-.683.697a1.637 1.637 0 0 1-.255.027.884.884 0 0 1-.683-.295L12 20.143l-1.808 1.848c-.25.268-.563.357-.938.268a.896.896 0 0 1-.683-.697l-.642-2.517-2.491.709c-.367.108-.679.023-.938-.254-.277-.259-.362-.571-.254-.938l.709-2.491-2.517-.642c-.358-.09-.59-.317-.697-.683-.089-.375 0-.688.268-.938L3.857 12l-1.848-1.808c-.268-.25-.357-.563-.268-.938.107-.366.339-.593.697-.683l2.517-.642-.42-1.477-2.948-2.948L2.85 2.242l2.009 2.009zm3.106.563l.606-2.377a.87.87 0 0 1 .683-.683c.367-.107.679-.022.938.255L12 3.871l1.808-1.862c.259-.268.571-.353.938-.255a.87.87 0 0 1 .683.683l.642 2.518 2.492-.709c.366-.108.678-.023.937.254.277.259.362.571.254.937l-.709 2.492 2.517.642c.358.09.59.317.697.683.089.375 0 .688-.268.938L20.143 12l1.848 1.808c.268.25.357.563.268.938-.107.366-.339.593-.697.683l-2.376.606L7.965 4.814z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M20.143 12l1.848 1.808c.268.25.357.563.268.938-.107.366-.339.593-.697.683l-2.517.642.709 2.491c.108.367.023.679-.254.938-.259.277-.571.362-.938.254l-2.491-.709-.642 2.517c-.09.358-.317.59-.683.697a1.637 1.637 0 0 1-.255.027.884.884 0 0 1-.683-.295L12 20.143l-1.808 1.848c-.25.268-.563.357-.938.268a.896.896 0 0 1-.683-.697l-.642-2.517-2.492.709c-.366.108-.678.023-.937-.254-.277-.259-.362-.571-.254-.938l.709-2.491-2.518-.642c-.357-.09-.589-.317-.696-.683-.089-.375 0-.688.268-.938L3.857 12l-1.848-1.808c-.268-.25-.357-.563-.268-.938.107-.366.339-.593.697-.683l2.517-.642-.709-2.492c-.108-.366-.023-.678.254-.937.259-.277.571-.362.938-.254l2.491.709.642-2.518a.87.87 0 0 1 .683-.683c.367-.107.679-.022.938.255L12 3.871l1.808-1.862c.259-.268.571-.353.938-.255a.87.87 0 0 1 .683.684l.642 2.517 2.491-.709c.367-.108.679-.023.938.254.277.259.362.571.254.938l-.709 2.491 2.517.642c.358.09.59.317.697.683.089.375 0 .688-.268.938L20.143 12z" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,75 @@
|
||||
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* sgBlockToggle - expandable block, collapsed by default
|
||||
* @memberof SOGo.Common
|
||||
* @ngInject
|
||||
* @example:
|
||||
|
||||
<sg-block-toggle>
|
||||
<md-list-item class="sg-button-toggle">
|
||||
<md-icon>warning</md-icon>
|
||||
<p flex>{{ message }}</p>
|
||||
<md-icon class="sg-icon-toggle">expand_more</md-icon>
|
||||
</md-list-item>
|
||||
<div class="sg-block-toggle">
|
||||
<!-- block's content -->
|
||||
</div>
|
||||
*/
|
||||
sgBlockToggle.$inject = ['$mdUtil', '$animateCss', '$$rAF'];
|
||||
function sgBlockToggle($mdUtil, $animateCss, $$rAF) {
|
||||
return {
|
||||
link: link
|
||||
};
|
||||
|
||||
function link($scope, $element) {
|
||||
var button = $element[0].querySelector('.sg-button-toggle'),
|
||||
icon = button.querySelector('.sg-icon-toggle'),
|
||||
icon_rotate_class = 'md-rotate-180-ccw',
|
||||
block = $element[0].querySelector('.sg-block-toggle'),
|
||||
isOpen = false;
|
||||
|
||||
button.classList.add('md-clickable');
|
||||
angular.element(button).on('click', toggle);
|
||||
|
||||
renderContent();
|
||||
|
||||
function renderContent() {
|
||||
block.setAttribute('aria-hidden', !isOpen);
|
||||
block.setAttribute('aria-expanded', isOpen);
|
||||
if (!isOpen)
|
||||
block.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
isOpen = !isOpen;
|
||||
if (isOpen)
|
||||
icon.classList.add(icon_rotate_class);
|
||||
else
|
||||
icon.classList.remove(icon_rotate_class);
|
||||
|
||||
if (isOpen)
|
||||
block.style.visibility = 'visible';
|
||||
|
||||
$$rAF(function() {
|
||||
var targetHeight = isOpen ? block.scrollHeight : 0;
|
||||
|
||||
$animateCss(angular.element(block), {
|
||||
easing: 'cubic-bezier(0.35, 0, 0.25, 1)',
|
||||
to: { height: targetHeight + 'px' },
|
||||
duration: 0.75 // seconds
|
||||
}).start().then(function() {
|
||||
renderContent();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module('SOGo.Common')
|
||||
.directive('sgBlockToggle', sgBlockToggle);
|
||||
})();
|
||||
@@ -0,0 +1,8 @@
|
||||
/// block-toggle.scss -*- Mode: scss; indent-tabs-mode: nil; basic-offset: 2 -*-
|
||||
|
||||
.sg-block-toggle {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 0;
|
||||
};
|
||||
@@ -35,13 +35,16 @@
|
||||
.pseudo-input-label,
|
||||
.button-label {
|
||||
display: inline-block;
|
||||
margin-top: $mg;
|
||||
color: $colorGray;
|
||||
font-weight: $sg-font-regular;
|
||||
line-height: $sg-line-height-1;
|
||||
transform: scale($input-label-float-scale);
|
||||
}
|
||||
|
||||
.pseudo-input-label {
|
||||
margin-top: $mg;
|
||||
}
|
||||
|
||||
.pseudo-input-label {
|
||||
@include rtl(transform-origin, left top, right top);
|
||||
}
|
||||
@@ -64,15 +67,16 @@
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
md-select.pseudo-input-field {
|
||||
md-select.pseudo-input-field,
|
||||
md-checkbox.pseudo-input-field {
|
||||
margin-bottom: $mg;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// The specs dimensions are too large to fit with angular-material
|
||||
// Here's a modifier
|
||||
.pseudo-input-container--compact {
|
||||
.pseudo-input-label {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
// .pseudo-input-container--compact {
|
||||
// .pseudo-input-label {
|
||||
// margin-top: 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
@import 'components/ripple/ripple';
|
||||
@import 'components/timepicker/timepicker';
|
||||
@import 'components/pseudo-input/pseudo-input';
|
||||
@import 'components/block-toggle/block-toggle';
|
||||
@import 'views/view';
|
||||
@import 'core/print';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user