I recently had the need to deal with application/x-pkcs7-mime signed data for one my personal archives since I've been receiving mails for a project that use it.
MHonArc does not have direct support for the type, so the content gets saved to an external file, which is of little use. After I figured out how to utilize the openssl program to extract the content successfully from the command-line, I leveraged the simple callback API mhonarc provides to extract the signed data out automatically. Note, I do not care to validate the signatures. I just need to get at the message data. Attached are two files depending on how you may need this capability. The mha-p7m script is a custom front-end replacement for the standard 'mhonarc' command that registers the callbacks for processing application/x-pkcs7-mime data. The other attachment is a sample mhasiteinit.pl file defining the callbacks so the regular 'mhonarc' command can still be used (see reference manual on how to install mhasiteinit.pl). Using mhasiteinit.pl is beneficial if using something like mharc to manage your archives so the application/x-pkcs7-mime extraction can be made available for mharc-based archives. I encourage anyone to extend what I have done to support other application/x-pkcs7-mime content (e.g. encrypted data). I did not bother with it since I do not need it and such support requires dealing with key management, and I did not want to mess with it at this time. There may be a general use to make such support intrinsic to mhonarc and I have no problem if the openssl command is a required dependency. However, I've been busy with other things for awhile to do any real work on mhonarc so do not hold your breath waiting for me to get something done. Enjoy, --ewh
#!/usr/bin/perl use lib qw( /usr/lib/MHonArc ); use Symbol; use File::Temp; my @argv = @ARGV; require 'mhamain.pl' || die qq/ERROR: Unable to require "mhamain.pl"\n/; my $openssl = 'openssl'; # Variable that holds the raw header text of the current message my $current_header_txt = ''; #$mhonarc::Debug = 1; &mhonarc::initialize(); # Message head callback function to capture raw header text. # This is needed so we can print it out to temp file when openssl # is used to extract signed message # $mhonarc::CBMessageHeadRead = sub { my $fields = shift; $current_header_txt = shift; $current_header_txt .= "\n"; }; # Raw message body read callback: If application/x-pkcs7-mime signed # data, openssl used to extract contents and then we replace # body variable to extracted contents for mhonarc to parse and archive. # $mhonarc::CBRawMessageBodyRead = sub { my $fields = shift; my $data_ref = shift; my $ctype = $fields->{'content-type'}[0]; # If not signed-data, we return immediately if (!$ctype || $ctype !~ m{application/x-pkcs7-mime} || $ctype !~ m{signed-data}) { return 1; } # Get here, use openssl to extract main data. # Write input data to temp file for use by openssl. my $tmp = new File::Temp(); syswrite($tmp, $current_header_txt); # openssl requires mail headers syswrite($tmp, $$data_ref); # Open pipe to openssl to extract the data: The command-line # options to make this work is non-intuitive. Luckily, I found # a page on the web that listed the right options for extracting # the content w/o the need to validate the signature. my $handle = gensym; mhonarc::cmd_pipe_open($handle, $openssl, 'smime', '-verify', '-in', $tmp->filename, '-noverify'); my $new_data = join('', <$handle>); close($handle); close($tmp); # Parse the sub-header from extracted data and update # main fields to use them over original fields. my($sub_header, $txt) = readmail::MAILread_header(\$new_data); foreach my $key (keys %$sub_header) { $fields->{$key} = $sub_header->{$key}; } if ($sub_header->{'content-type'}) { # Make sure update mhonarc's meta field for content-type my $ctype = $sub_header->{'content-type'}[0]; if ($ctype =~ m%^\s*([\w\-\./]+)%) { $fields->{'x-mha-content-type'} = $1; } } # IMPORTANT: Make sure CTE is adjusted to reflect extracted data if (!$sub_header->{'content-transfer-encoding'}) { $fields->{'content-transfer-encoding'} = [ '7bit' ]; } # Replace message data to what was extracted and let mhonarc parse it. $$data_ref = $new_data; return 1; }; &mhonarc::process_input(@argv);
#!/usr/local/bin/perl ##---------------------------------------------------------------------------## ## File: ## $Id: mhasiteinit.pl,v 1.4 2005/06/02 02:12:30 ehood Exp $ ## Description: ## Site-specific initialization code for MHonArc. If used, it ## should be place in the MHonArc library directory as specified ## during initialization, or in a directory that perl checks ## when requiring libraries. ## ## >>> THE EXPRESSIONS IN THIS FILE ARE EXECUTED EVERYTIME AN ## >>> ARCHIVE IS OPENED FOR PROCESSING. ## ## Note, it is recommended to use a default resource file when ## possible. ##---------------------------------------------------------------------------## ## Set package to something other than "mhonarc" to protect ourselves ## from unintentionally screwing with MHonArc's internals package mhonarc_site_init; use Symbol; use File::Temp; my $openssl = '/usr/bin/openssl'; # Variable that holds the raw header text of the current message my $current_header_txt = ''; # Message head callback function to capture raw header text. # This is needed so we can print it out to temp file when openssl # is used to extract signed message # $mhonarc::CBMessageHeadRead = sub { my $fields = shift; $current_header_txt = shift; $current_header_txt .= "\n"; }; # Raw message body read callback: If application/x-pkcs7-mime signed # data, openssl used to extract contents and then we replace # body variable to extracted contents for mhonarc to parse and archive. # $mhonarc::CBRawMessageBodyRead = sub { my $fields = shift; my $data_ref = shift; my $ctype = $fields->{'content-type'}[0]; # If not signed-data, we return immediately if (!$ctype || $ctype !~ m{application/x-pkcs7-mime} || $ctype !~ m{signed-data}) { return 1; } # Get here, use openssl to extract main data. # Write input data to temp file for use by openssl. my $tmp = new File::Temp(); syswrite($tmp, $current_header_txt); # openssl requires mail headers syswrite($tmp, $$data_ref); # Open pipe to openssl to extract the data: The command-line # options to make this work is non-intuitive. Luckily, I found # a page on the web that listed the right options for extracting # the content w/o the need to validate the signature. my $handle = gensym; mhonarc::cmd_pipe_open($handle, $openssl, 'smime', '-verify', '-in', $tmp->filename, '-noverify'); my $new_data = join('', <$handle>); close($handle); close($tmp); # Parse the sub-header from extracted data and update # main fields to use them over original fields. my($sub_header, $txt) = readmail::MAILread_header(\$new_data); foreach my $key (keys %$sub_header) { $fields->{$key} = $sub_header->{$key}; } if ($sub_header->{'content-type'}) { # Make sure update mhonarc's meta field for content-type my $ctype = $sub_header->{'content-type'}[0]; if ($ctype =~ m%^\s*([\w\-\./]+)%) { $fields->{'x-mha-content-type'} = $1; } } # IMPORTANT: Make sure CTE is adjusted to reflect extracted data if (!$sub_header->{'content-transfer-encoding'}) { $fields->{'content-transfer-encoding'} = [ '7bit' ]; } # Replace message data to what was extracted and let mhonarc parse it. $$data_ref = $new_data; return 1; }; ##---------------------------------------------------------------------------## ## Make sure to return a true value for require(). ##---------------------------------------------------------------------------## 1;