threads / rfc / 26190

RFC patchgit-send-email: added support for S/MIME

Subject: [RFC][PATCH] git-send-email: added support for S/MIME

## tl;dr

4 messages between Jan 4, 2011 and Jan 5, 2011. Diffs are folded; open one to read it.

replies: 3people: 3as markdown or json

Roberto Sassu· Jan 4, 2011, 16:02 UTC · lore

The script git-send-email.perl has been modified in order to add support for messages with S/MIME format. First, the message body is written in a temporary file and signed by OpenSSL with the X.509 certificate provided by the user. Then the returned content is added to the previously parsed header and the message is sent as the same for unsigned messages.

Usage:
    git send-email -sign -signing-cert </path/of/PEM> <other options>
Signed-off-by: Roberto Sassu <roberto.sassu@polito.it>
---
 git-send-email.perl |   97 +++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 87 insertions(+), 10 deletions(-)
Show changes to git-send-email.perl +87 −10
diff --git a/git-send-email.perl b/git-send-email.perl
index 76565de..c040fe6 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -57,6 +57,8 @@ git send-email [options] <file | directory | rev-list options >
     --annotate                     * Review each patch that will be sent in an editor.
     --compose                      * Open an editor for introduction.
     --8bit-encoding         <str>  * Encoding to assume 8bit mails if undeclared
+    --sign                         * Sign all emails with an X.509 certificate.
+    --signing-cert          <str>  * Path of the X.509 certificate.
 
   Sending:
     --envelope-sender       <str>  * Email envelope sender.
@@ -141,7 +143,7 @@ my $auth;
 
 # Variables we fill in automatically, or via prompting:
 my (@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh,
-	$initial_reply_to,$initial_subject,@files,
+	@xb,$initial_reply_to,$initial_subject,@files,
 	$author,$sender,$smtp_authpass,$annotate,$compose,$time);
 
 my $envelope_sender;
@@ -161,9 +163,10 @@ if ($@) {
 }
 
 # Behavior modification variables
-my ($quiet, $dry_run) = (0, 0);
+my ($quiet, $dry_run, $sign) = (0, 0, 0);
 my $format_patch;
 my $compose_filename;
+my $signing_cert;
 my $force = 0;
 
 # Handle interactive edition of files.
@@ -232,6 +235,7 @@ my %config_settings = (
     "confirm"   => \$confirm,
     "from" => \$sender,
     "assume8bitencoding" => \$auto_8bit_encoding,
+    "signing_cert" => \$signing_cert
 );
 
 # Help users prepare for 1.7.0
@@ -311,6 +315,8 @@ my $rc = GetOptions("sender|from=s" => \$sender,
 		    "format-patch!" => \$format_patch,
 		    "8bit-encoding=s" => \$auto_8bit_encoding,
 		    "force" => \$force,
+		    "sign" => \$sign,
+		    "signing-cert:s" => \$signing_cert,
 	 );
 
 unless ($rc) {
@@ -356,6 +362,11 @@ sub read_config {
 	}
 }
 
+# verify if the signing certificate has been specified
+if ($sign && !$signing_cert) {
+	die "Signing certificate not specified";
+}
+
 # read configuration from [sendemail "$identity"], fall back on [sendemail]
 $identity = Git::config(@repo, "sendemail.identity") unless (defined $identity);
 read_config("sendemail.$identity") if (defined $identity);
@@ -1161,6 +1172,7 @@ foreach my $t (@files) {
 	@to = ();
 	@cc = ();
 	@xh = ();
+	@xb = ();
 	my $input_format = undef;
 	my @header = ();
 	$message = "";
@@ -1223,7 +1235,20 @@ foreach my $t (@files) {
 				if (/charset="?([^ "]+)/) {
 					$body_encoding = $1;
 				}
-				push @xh, $_;
+				if ($sign) {
+					push @xb, $_;
+				} else {
+					push @xh, $_;
+				}
+			}
+			elsif (/^MIME-Version:/i && $sign) {
+				# Do nothing: this will be added by OpenSSL
+			}
+			elsif (/Content-Transfer-Encoding:/i && $sign) {
+				# move the Content-Transfer-Encoding in the
+				# first part of the message if the latter is
+				# about to be signed
+				push @xb, $_;
 			}
 			elsif (/^Message-Id: (.*)/i) {
 				$message_id = $1;
@@ -1275,9 +1300,14 @@ foreach my $t (@files) {
 
 	if ($broken_encoding{$t} && !$has_content_type) {
 		$has_content_type = 1;
-		push @xh, "MIME-Version: 1.0",
-			"Content-Type: text/plain; charset=$auto_8bit_encoding",
-			"Content-Transfer-Encoding: 8bit";
+		if ($sign) {
+			push @xb, "Content-Type: text/plain; charset=$auto_8bit_encoding",
+				  "Content-Transfer-Encoding: 8bit";
+		} else {
+			push @xh, "MIME-Version: 1.0",
+				"Content-Type: text/plain; charset=$auto_8bit_encoding",
+				"Content-Transfer-Encoding: 8bit";
+		}
 		$body_encoding = $auto_8bit_encoding;
 	}
 
@@ -1298,12 +1328,59 @@ foreach my $t (@files) {
 			}
 			else {
 				$has_content_type = 1;
-				push @xh,
-				  'MIME-Version: 1.0',
-				  "Content-Type: text/plain; charset=$author_encoding",
-				  'Content-Transfer-Encoding: 8bit';
+				if ($sign) {
+					push @xb,
+					  "Content-Type: text/plain; charset=$author_encoding",
+					  'Content-Transfer-Encoding: 8bit';
+				} else {
+					push @xh,
+					  'MIME-Version: 1.0',
+					  "Content-Type: text/plain; charset=$author_encoding",
+					  'Content-Transfer-Encoding: 8bit';
+				}
+			}
+		}
+	}
+
+	if ($sign) {
+ 		my $linecount = 0;
+		my $message_body_tmp_file;
+
+		# put the original Content-Type, charset and Content-Transfer-Encoding
+		# information, if specified, in the first part of the message
+		if (@xb) {
+			$message = join("\n", @xb) . "\n\n" . $message;
+		} else {
+			$message = "\n" . $message;
+		}
+
+		# write the message body in a temporary file
+		$message_body_tmp_file = ($repo ?
+			tempfile(".gitsendemail.body.XXXXXX", DIR => $repo->repo_path()) :
+			tempfile(".gitsendemail.body.XXXXXX", DIR => "."))[1];
+
+		open(MESSAGE_BODY_FILE,">",$message_body_tmp_file) or
+			die "Failed to open for writing $message_body_tmp_file: $!";
+		print MESSAGE_BODY_FILE $message;
+		close MESSAGE_BODY_FILE;
+
+		# sign the message body and put the result in the $message variable
+		$message = "";
+		open(OPENSSL_SIGNED_MESSAGE, "openssl smime -sign -in $message_body_tmp_file -signer $signing_cert |")
+			or die "Could not execute OpenSSL";
+
+		while(<OPENSSL_SIGNED_MESSAGE>) {
+			chomp;
+			if($linecount < 2) {
+				# push first two lines into the header
+				push @xh, $_;
+			} else {
+				# put the remaining content in the $message variable
+				$message .= $_;
 			}
 		}
+		close OPENSSL_SIGNED_MESSAGE;
+ 		unlink($message_body_tmp_file);
 	}
 
 	$needs_confirm = (
-- 
1.7.3.4
Junio C Hamano· Jan 4, 2011, 19:22 UTC · re: Roberto Sassu · lore

Re: [RFC][PATCH] git-send-email: added support for S/MIME

Roberto Sassu <roberto.sassu@polito.it> writes:
> ...
> Signed-off-by: Roberto Sassu <roberto.sassu@polito.it>

The patch with so many "if ($sign) do this else do that" is too ugly beyond words. I wonder if the surrounding code can be better restructured.

Thomas Rast· Jan 4, 2011, 19:36 UTC · re: Roberto Sassu · lore

Re: [RFC][PATCH] git-send-email: added support for S/MIME

Roberto Sassu wrote:
> The script git-send-email.perl has been modified in order to add support
> for messages with S/MIME format.
Does git-am need symmetric support to decode the message?
-- 
Thomas Rast
trast@{inf,student}.ethz.ch
Roberto Sassu· Jan 5, 2011, 09:22 UTC · re: Thomas Rast · lore

Re: [RFC][PATCH] git-send-email: added support for S/MIME

Hi Thomas
On Tuesday, January 04, 2011 08:36:36 pm Thomas Rast wrote:
Show 5 quoted lines
> Roberto Sassu wrote:
> > The script git-send-email.perl has been modified in order to add support
> > for messages with S/MIME format.
> 
> Does git-am need symmetric support to decode the message?

I think git-am does not require any modification on the code, because it already supports emails with Content-Type 'multipart'. One example comes from git-format-patch which can generate patches with this format by adding the option '--attach'.

Regards
Roberto Sassu

← back to recent threads