RT5.0.5 Decrypt SMIME email received as CC

RT fails to decrypt mails where the queue’s address is not in the “To” header.

Looking at sub _Decrypt in /lib/RT/Crypt/SMIME.pm I find

    my @addresses =
        grep !$seen{lc $_}++, map $_->address, map Email::Address->parse($_),
        grep length && defined, @{$args{'Recipients'}};

    my $opts = RT->Config->Get('SMIME');

    my @providers = map { ( '-provider', $_ ) } @{ $opts->{'Providers'} };

    my ($buf, $encrypted_to, %res);

    foreach my $address ( @addresses ) {
        my $file = $self->CheckKeyring( Key => $address, For => 'Encryption' );
        unless ( $file ) {
            my $keyring = $opts->{'Keyring'};
            $RT::Logger->debug("No key found for $address in $keyring directory");
            next;
        }

I am not a coder and would be very pleased if there was any perl guy who would help me letting RT parse the CC field of received mails like it does with the Recipients list.

Thanks very much!

Before diving into the code, are you sure that the incoming message is encrypted using a key that you have associated with that Queue’s CorrespondAddress or CommentAddress? If not (for example if it is encrypted using keys only known to the “To” recipient and unknown to you/RT) then there’s not much you can do.

Thanks for looking into!

Yes, we are very sure, as we fetch all mail from an IMAP folder. There we have full access to the content.

This mail was addressed to two other people and us in CC.

[367780] [Fri Apr 12 10:28:02 2024] [debug]: No key found for email1@example.com in /opt/rt5/var/data/smime directory (/opt/rt5/sbin/../lib/RT/Crypt/SMIME.pm:672)
[367780] [Fri Apr 12 10:28:02 2024] [debug]: No key found for email2@example.com in /opt/rt5/var/data/smime directory (/opt/rt5/sbin/../lib/RT/Crypt/SMIME.pm:672)
[367780] [Fri Apr 12 10:28:02 2024] [error]: openssl exited with error code 4 and error: Error decrypting PKCS#7 structure
4017EE2F1E7F0000:error:10800073:PKCS7 routines:PKCS7_dataDecode:no recipient matches certificate:../crypto/pkcs7/pk7_doit.c:574:
4017EE2F1E7F0000:error:10800077:PKCS7 routines:PKCS7_decrypt:decrypt error:../crypto/pkcs7/pk7_smime.c:516: (/opt/rt5/sbin/../lib/RT/Crypt/SMIME.pm:703)
[367780] [Fri Apr 12 10:28:02 2024] [warning]: Failure during SMIME decrypt: Decryption failed (/opt/rt5/sbin/../lib/RT/Interface/Email/Crypt.pm:104

If I interpret the debug log correctly, RT doesn’t even try to decrypt with the queues key (of course located /opt/rt5/var/data/smime). That way one would also have (info-) mail decrypted what was sent to large groups using entirely BCC (To: undisclosed recipients).

Assuming email@example.com and email2@example.com are the two other (non-RT) people, the first two lines make sense - its RT telling you it doesn’t have keys for them. The next three lines are from _Decrypt() in RT::Crypt::SMIME but further down the routine than you included above. What that is telling me is that after trying the first two (non-RT) email addresses it is trying another recipient, most probably one of your correspond/comment addresses, but openssl isn’t happy.

Now I’m guessing this is because openssl is complaining about a certificate, rather than a key. In _Decrypt() we have a check just before this error is thrown that says,

        if ( index($res{'stderr'}, 'no recipient matches key') >= 0 ) {
            $RT::Logger->debug("Although we have a key for $address, it is not the one that encrypted this message");
            next;
        }

Now, I’m wondering if you can make an overlay for RT::Crypt::SMIME that traps the certificate as well as key errors? Maybe something like this in /opt/rt5/local/lib/RT/Crypt/SMIME_Local.pm:

use strict;
no warnings qw(redefine);
package RT::Crypt::SMIME;

sub _Decrypt {
    my $self = shift;
    my %args = (Content => undef, @_ );

    my %seen;
    my @addresses =
        grep !$seen{lc $_}++, map $_->address, map Email::Address->parse($_),
        grep length && defined, @{$args{'Recipients'}};

    my $opts = RT->Config->Get('SMIME');

    my @providers = map { ( '-provider', $_ ) } @{ $opts->{'Providers'} };

    my ($buf, $encrypted_to, %res);

    foreach my $address ( @addresses ) {
        my $file = $self->CheckKeyring( Key => $address, For => 'Encryption' );
        unless ( $file ) {
            my $keyring = $opts->{'Keyring'};
            $RT::Logger->debug("No key found for $address in $keyring directory");
            next;
        }

        local $ENV{SMIME_PASS} = $self->GetPassphrase( Address => $address, For => 'Encryption' );
        local $SIG{CHLD} = 'DEFAULT';
        my $cmd = [
            $self->OpenSSLPath,
            'smime',
            @providers,
            '-decrypt',
            -recip => $file,
            (defined $ENV{'SMIME_PASS'} && length $ENV{'SMIME_PASS'})
                ? (qw(-passin env:SMIME_PASS))
                : (),
        ];
        safe_run_child { run3( $cmd, $args{'Content'}, \$buf, \$res{'stderr'} ) };
        unless ( $? ) {
            $encrypted_to = $address;
            $RT::Logger->debug("Message encrypted for $encrypted_to");
            last;
        }

        if ( index($res{'stderr'}, 'no recipient matches key') >= 0 ) {
            $RT::Logger->debug("Although we have a key for $address, it is not the one that encrypted this message");
            next;
        }
        if ( index($res{'stderr'}, 'no recipient matches certificate') >= 0 ) {
            $RT::Logger->debug("Although we have a certificate for $address, it is not the one that encrypted this message");
            next;
        }


        $res{'exit_code'} = $?;
        $res{'message'} = "openssl exited with error code ". ($? >> 8)
            ." and error: $res{stderr}";
        $RT::Logger->error( $res{'message'} );
        $res{'status'} = $self->FormatStatus({
            Operation => 'Decrypt', Status => 'ERROR',
            Message => 'Decryption failed',
            EncryptedTo => $address,
        });
        return (undef, %res);
    }
    unless ( $encrypted_to ) {
        $RT::Logger->error("Couldn't find SMIME key for addresses: ". join ', ', @addresses);
        $res{'exit_code'} = 1;
        $res{'status'} = $self->FormatStatus({
            Operation => 'KeyCheck',
            Status    => 'MISSING',
            Message   => "Secret key is not available",
            KeyType   => 'secret',
        });
        return (undef, %res);
    }

    $res{'status'} = $self->FormatStatus({
        Operation => 'Decrypt', Status => 'DONE',
        Message => 'Decryption process succeeded',
        EncryptedTo => $encrypted_to,
    });

    return (\$buf, %res);
}

1;

Nice idea and some kind of learning perl hacking… :slight_smile:

As there was no change in result, I added a line of debug code right after the opening of _Decrypt

grep _Decrypt /opt/rt5/local/lib/RT/Crypt/SMIME_Local.pm 
/opt/rt5/local/lib/RT/Crypt/SMIME_Local.pm:sub _Decrypt {
/opt/rt5/local/lib/RT/Crypt/SMIME_Local.pm:    $RT::Logger->debug("_Decrypt was called using overlay /opt/rt5/local/lib/RT/Crypt/SMIME_Local.pm");

It seems the overlay is not loaded. Mason cache was deleted and web server restarted.

sudo rm -rf /opt/rt5/var/mason_data/obj && sudo service lighttpd restart 

Are there other methods for proofing the use of the overlay?
Btw.: I found no help in RT 5.0.2 Overlays

Ah, looking at the 5.0.5 RT::Crypt::SMIME file, it doesn’t appear to have the usual,

RT::Base->_ImportOverlays();

at the end (unlike RT::Crypt::GnuPG for example). So instead, try copying the file from /opt/rt5/lib/RT/Crypt/SMIME.pm to /opt/rt5/local/lib/RT/Crypt/SMIME.pm and then replace the _Decrypt() routine in the local copy with the one above.

Thanks a lot GreenJimll, that one helped!

I now have questions for the AD people and it seems we have a candidate for the revocation list.

I have a similar situation to you with the encryption problem.
Can you write step by step what needs to be done for encrypted messages to be read by RT?

Great - I’ve popped in a bug report about the lack of overlay call and support for certificates in RT::Crypt::SMIME for @Jim_Brandt and the Best Practical coders to look at.