Subject: bug#67931: [PATCH] Use S/MIME key from content for mail signing via OpenSSL

From: Illia Ostapyshyn <[email-masked]>
Date: Mon, 06 May 2024 20:46:33 +0200

Sorry, forgot to attach the patch, sending it with this email.

>From b228ee97f41911f2aba7b98ae1b5d1226e95e099 Mon Sep 17 00:00:00 2001
From: Illia Ostapyshyn <[email-masked]>
Date: Mon, 6 May 2024 20:24:22 +0200
Subject: [PATCH] Use proper smime-keys entry for S/MIME signatures using
 OpenSSL

* lisp/gnus/mml-smime.el (mml-smime-openssl-sign-query): Include the
additional certificates from smime-keys in plist for MML tag generation.
(mml-smime-openssl-sign): Forward certfile entries from the MML tag to
smime-sign-buffer.
* doc/misc/emacs-mime.texi (MML Definition): certfile parameter is now
common to both sign and encrypt.  Clarify that certfile entries can be
repeated.
; * lisp/gnus/smime.el (smime-sign-region): Fix typo in documentation.
; (smime-sign-buffer): Improve documentation to match smime-sign-region.
---
 doc/misc/emacs-mime.texi | 11 +++-------
 lisp/gnus/mml-smime.el   | 46 +++++++++++++++++++++++-----------------
 lisp/gnus/smime.el       |  7 ++++--
 3 files changed, 34 insertions(+), 30 deletions(-)

diff --git a/doc/misc/emacs-mime.texi b/doc/misc/emacs-mime.texi
index 96a6328cd47..e3e33bad8b4 100644
--- a/doc/misc/emacs-mime.texi
+++ b/doc/misc/emacs-mime.texi
@@ -780,21 +780,16 @@ MML Definition
 
 @end table
 
-Parameters for @samp{sign=smime}:
+Parameters for @samp{sign=smime} and @samp{encrypt=smime}:
 
 @table @samp
 
 @item keyfile
 File containing key and certificate for signer.
 
-@end table
-
-Parameters for @samp{encrypt=smime}:
-
-@table @samp
-
 @item certfile
-File containing certificate for recipient.
+File containing certificate for recipient.  May appear multiple times
+for multiple certificates.
 
 @end table
 
diff --git a/lisp/gnus/mml-smime.el b/lisp/gnus/mml-smime.el
index 3064c46d2a3..17b338755e3 100644
--- a/lisp/gnus/mml-smime.el
+++ b/lisp/gnus/mml-smime.el
@@ -129,11 +129,15 @@ mml-smime-verify-test
     (if func
 	(funcall func handle ctl))))
 
-(defun mml-smime-openssl-sign (_cont)
-  (when (null smime-keys)
-    (customize-variable 'smime-keys)
-    (error "No S/MIME keys configured, use customize to add your key"))
-  (smime-sign-buffer (cdar smime-keys))
+(defun mml-smime-openssl-sign (cont)
+  (smime-sign-buffer
+   ;; List with key and certificate as its car, and a list of additional
+   ;; certificates to include in its cadr for smime-sign-region
+   (list
+    (cdr (assq 'keyfile cont))
+    (mapcar #'cdr (cl-remove-if-not (apply-partially #'equal 'certfile)
+                                    cont
+                                    :key #'car-safe))))
   (goto-char (point-min))
   (while (search-forward "\r\n" nil t)
     (replace-match "\n" t t))
@@ -167,21 +171,23 @@ mml-smime-openssl-sign-query
   (when (null smime-keys)
     (customize-variable 'smime-keys)
     (error "No S/MIME keys configured, use customize to add your key"))
-  (list 'keyfile
-	(if (= (length smime-keys) 1)
-	    (cadar smime-keys)
-	  (or (let ((from (cadr (mail-extract-address-components
-				 (or (save-excursion
-				       (save-restriction
-					 (message-narrow-to-headers)
-					 (message-fetch-field "from")))
-				     "")))))
-		(and from (smime-get-key-by-email from)))
-	      (smime-get-key-by-email
-	       (gnus-completing-read "Sign this part with what signature"
-                                     (mapcar #'car smime-keys) nil nil nil
-                                     (and (listp (car-safe smime-keys))
-                                          (caar smime-keys))))))))
+  (let ((key-with-certs
+	 (if (= (length smime-keys) 1)
+	     (cdar smime-keys)
+	   (or (let ((from (cadr (mail-extract-address-components
+				  (or (save-excursion
+				        (save-restriction
+					  (message-narrow-to-headers)
+					  (message-fetch-field "from")))
+				      "")))))
+		 (and from (smime-get-key-with-certs-by-email from)))
+	       (smime-get-key-with-certs-by-email
+	        (gnus-completing-read "Sign this part with what signature"
+                                      (mapcar #'car smime-keys) nil nil nil
+                                      (and (listp (car-safe smime-keys))
+                                           (caar smime-keys))))))))
+    (append (list 'keyfile (car key-with-certs))
+            (mapcan (apply-partially #'list 'certfile) (cadr key-with-certs)))))
 
 (defun mml-smime-get-file-cert ()
   (ignore-errors
diff --git a/lisp/gnus/smime.el b/lisp/gnus/smime.el
index b61579912dd..987bc7273db 100644
--- a/lisp/gnus/smime.el
+++ b/lisp/gnus/smime.el
@@ -261,7 +261,7 @@ smime-sign-region
 If signing fails, the buffer is not modified.  Region is assumed to
 have proper MIME tags.  KEYFILE is expected to contain a PEM encoded
 private key and certificate as its car, and a list of additional
-certificates to include in its caar.  If no additional certificates is
+certificates to include in its cadr.  If no additional certificates are
 included, KEYFILE may be the file containing the PEM encoded private
 key and certificate itself."
   (smime-new-details-buffer)
@@ -327,7 +327,10 @@ smime-encrypt-region
 
 (defun smime-sign-buffer (&optional keyfile buffer)
   "S/MIME sign BUFFER with key in KEYFILE.
-KEYFILE should contain a PEM encoded key and certificate."
+KEYFILE is expected to contain a PEM encoded private key and certificate
+as its car, and a list of additional certificates to include in its
+cadr.  If no additional certificates are included, KEYFILE may be the
+file containing the PEM encoded private key and certificate itself."
   (interactive)
   (with-current-buffer (or buffer (current-buffer))
     (unless (smime-sign-region
-- 
2.39.2