X-Git-Url: http://cgit.sxemacs.org/?p=gnus;a=blobdiff_plain;f=lisp%2Fmml-smime.el;h=0bcc9c53c48f037b196b072a5ef49025148bea2b;hp=d14ccb728cf7d79d067b2dc736085b89b8ddb884;hb=b9d4597a71a404851e3180b476ffe6186131adac;hpb=bdcc91d5d7080f9d505a2e253e3e163e47ed04b8 diff --git a/lisp/mml-smime.el b/lisp/mml-smime.el index d14ccb728..0bcc9c53c 100644 --- a/lisp/mml-smime.el +++ b/lisp/mml-smime.el @@ -1,27 +1,24 @@ ;;; mml-smime.el --- S/MIME support for MML -;; Copyright (C) 2000, 2001, 2002, 2003, 2004, -;; 2005, 2006, 2007 Free Software Foundation, Inc. +;; Copyright (C) 2000-2015 Free Software Foundation, Inc. ;; Author: Simon Josefsson ;; Keywords: Gnus, MIME, S/MIME, MML ;; This file is part of GNU Emacs. -;; GNU Emacs is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published -;; by the Free Software Foundation; either version 2, or (at your -;; option) any later version. +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. -;; GNU Emacs is distributed in the hope that it will be useful, but -;; WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -;; General Public License for more details. +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -35,7 +32,12 @@ (autoload 'message-narrow-to-headers "message") (autoload 'message-fetch-field "message") -(defvar mml-smime-use 'openssl) +(defcustom mml-smime-use (if (featurep 'epg) 'epg 'openssl) + "Whether to use OpenSSL or EPG to decrypt S/MIME messages. +Defaults to EPG if it's loaded." + :group 'mime-security + :type '(choice (const :tag "EPG" epg) + (const :tag "OpenSSL" openssl))) (defvar mml-smime-function-alist '((openssl mml-smime-openssl-sign @@ -51,11 +53,6 @@ mml-smime-epg-verify mml-smime-epg-verify-test))) -(defcustom mml-smime-verbose mml-secure-verbose - "If non-nil, ask the user about the current operation more verbosely." - :group 'mime-security - :type 'boolean) - (defcustom mml-smime-cache-passphrase mml-secure-cache-passphrase "If t, cache passphrase." :group 'mime-security @@ -73,6 +70,18 @@ Whether the passphrase is cached at all is controlled by :group 'mime-security :type '(repeat (string :tag "Key ID"))) +(defcustom mml-smime-sign-with-sender nil + "If t, use message sender so find a key to sign with." + :group 'mime-security + :version "24.4" + :type 'boolean) + +(defcustom mml-smime-encrypt-to-self nil + "If t, add your own key ID to recipient list when encryption." + :group 'mime-security + :version "24.4" + :type 'boolean) + (defun mml-smime-sign (cont) (let ((func (nth 1 (assq mml-smime-use mml-smime-function-alist)))) (if func @@ -142,6 +151,8 @@ Whether the passphrase is cached at all is controlled by nil)) (goto-char (point-max))) +(defvar gnus-extract-address-components) + (defun mml-smime-openssl-sign-query () ;; query information (what certificate) from user when MML tag is ;; added, for use later by the signing process @@ -162,10 +173,10 @@ Whether the passphrase is cached at all is controlled by ""))))) (and from (smime-get-key-by-email from))) (smime-get-key-by-email - (completing-read "Sign this part with what signature? " - smime-keys nil nil - (and (listp (car-safe smime-keys)) - (caar smime-keys)))))))) + (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)))))))) (defun mml-smime-get-file-cert () (ignore-errors @@ -191,7 +202,7 @@ Whether the passphrase is cached at all is controlled by ""))))) (if (setq cert (smime-cert-by-dns who)) (setq result (list 'certfile (buffer-name cert))) - (setq bad (format "`%s' not found. " who)))) + (setq bad (gnus-format-message "`%s' not found. " who)))) (quit)) result)) @@ -210,17 +221,20 @@ Whether the passphrase is cached at all is controlled by ""))))) (if (setq cert (smime-cert-by-ldap who)) (setq result (list 'certfile (buffer-name cert))) - (setq bad (format "`%s' not found. " who)))) + (setq bad (gnus-format-message "`%s' not found. " who)))) (quit)) result)) +(autoload 'gnus-completing-read "gnus-util") + (defun mml-smime-openssl-encrypt-query () ;; todo: try dns/ldap automatically first, before prompting user (let (certs done) (while (not done) - (ecase (read (gnus-completing-read-with-default - "ldap" "Fetch certificate from" - '(("dns") ("ldap") ("file")) nil t)) + (ecase (read (gnus-completing-read + "Fetch certificate from" + '("dns" "ldap" "file") t nil nil + "ldap")) (dns (setq certs (append certs (mml-smime-get-dns-cert)))) (ldap (setq certs (append certs @@ -298,35 +312,30 @@ Whether the passphrase is cached at all is controlled by (defun mml-smime-openssl-verify-test (handle ctl) smime-openssl-program) -(eval-and-compile - (autoload 'epg-make-context "epg")) - -(eval-when-compile - (defvar epg-user-id-alist) - (defvar epg-digest-algorithm-alist) - (defvar inhibit-redisplay) - (autoload 'epg-context-set-armor "epg") - (autoload 'epg-context-set-textmode "epg") - (autoload 'epg-context-set-signers "epg") - (autoload 'epg-context-result-for "epg") - (autoload 'epg-new-signature-digest-algorithm "epg") - (autoload 'epg-verify-result-to-string "epg") - (autoload 'epg-list-keys "epg") - (autoload 'epg-decrypt-string "epg") - (autoload 'epg-verify-string "epg") - (autoload 'epg-sign-string "epg") - (autoload 'epg-encrypt-string "epg") - (autoload 'epg-passphrase-callback-function "epg") - (autoload 'epg-context-set-passphrase-callback "epg") - (autoload 'epg-configuration "epg-config") - (autoload 'epg-expand-group "epg-config") - (autoload 'epa-select-keys "epa")) - -(eval-when-compile - (defvar password-cache-expiry) - (autoload 'password-read "password") - (autoload 'password-cache-add "password") - (autoload 'password-cache-remove "password")) +(defvar epg-user-id-alist) +(defvar epg-digest-algorithm-alist) +(defvar inhibit-redisplay) +(defvar password-cache-expiry) + +(autoload 'epg-make-context "epg") +(autoload 'epg-passphrase-callback-function "epg") +(declare-function epg-context-set-signers "epg" (context signers)) +(declare-function epg-context-result-for "epg" (context name)) +(declare-function epg-new-signature-digest-algorithm "epg" (cl-x) t) +(declare-function epg-verify-result-to-string "epg" (verify-result)) +(declare-function epg-list-keys "epg" (context &optional name mode)) +(declare-function epg-verify-string "epg" + (context signature &optional signed-text)) +(declare-function epg-sign-string "epg" (context plain &optional mode)) +(declare-function epg-encrypt-string "epg" + (context plain recipients &optional sign always-trust)) +(declare-function epg-context-set-passphrase-callback "epg" + (context passphrase-callback)) +(declare-function epg-sub-key-fingerprint "epg" (cl-x) t) +(declare-function epg-configuration "epg-config" ()) +(declare-function epg-expand-group "epg-config" (config group)) +(declare-function epa-select-keys "epa" + (context prompt &optional names secret)) (defvar mml-smime-epg-secret-key-id-list nil) @@ -351,6 +360,10 @@ Whether the passphrase is cached at all is controlled by (cons key-id mml-smime-epg-secret-key-id-list)) (copy-sequence passphrase))))) +(declare-function epg-key-sub-key-list "epg" (key) t) +(declare-function epg-sub-key-capability "epg" (sub-key) t) +(declare-function epg-sub-key-validity "epg" (sub-key) t) + (defun mml-smime-epg-find-usable-key (keys usage) (catch 'found (while keys @@ -363,33 +376,64 @@ Whether the passphrase is cached at all is controlled by (setq pointer (cdr pointer)))) (setq keys (cdr keys))))) +;; XXX: since gpg --list-secret-keys does not return validity of each +;; key, `mml-smime-epg-find-usable-key' defined above is not enough for +;; secret keys. The function `mml-smime-epg-find-usable-secret-key' +;; below looks at appropriate public keys to check usability. +(defun mml-smime-epg-find-usable-secret-key (context name usage) + (let ((secret-keys (epg-list-keys context name t)) + secret-key) + (while (and (not secret-key) secret-keys) + (if (mml-smime-epg-find-usable-key + (epg-list-keys context (epg-sub-key-fingerprint + (car (epg-key-sub-key-list + (car secret-keys))))) + usage) + (setq secret-key (car secret-keys) + secret-keys nil) + (setq secret-keys (cdr secret-keys)))) + secret-key)) + +(autoload 'mml-compute-boundary "mml") + +;; We require mm-decode, which requires mm-bodies, which autoloads +;; message-options-get (!). +(declare-function message-options-set "message" (symbol value)) + (defun mml-smime-epg-sign (cont) (let* ((inhibit-redisplay t) (context (epg-make-context 'CMS)) (boundary (mml-compute-boundary cont)) + (sender (message-options-get 'message-sender)) + (signer-names (or mml-smime-signers + (if (and mml-smime-sign-with-sender sender) + (list (concat "<" sender ">"))))) signer-key (signers (or (message-options-get 'mml-smime-epg-signers) (message-options-set - 'mml-smime-epg-signers - (if mml-smime-verbose - (epa-select-keys context "\ + 'mml-smime-epg-signers + (if (eq mm-sign-option 'guided) + (epa-select-keys context "\ Select keys for signing. If no one is selected, default secret key is used. " - mml-smime-signers t) - (if mml-smime-signers - (mapcar - (lambda (signer) - (setq signer-key (mml-smime-epg-find-usable-key - (epg-list-keys context signer t) - 'sign)) - (unless (or signer-key - (y-or-n-p - (format "No secret key for %s; skip it? " + signer-names + t) + (if (or sender mml-smime-signers) + (delq nil + (mapcar + (lambda (signer) + (setq signer-key + (mml-smime-epg-find-usable-secret-key + context signer 'sign)) + (unless (or signer-key + (y-or-n-p + (format + "No secret key for %s; skip it? " signer))) - (error "No secret key for %s" signer)) - signer-key) - mml-smime-signers)))))) + (error "No secret key for %s" signer)) + signer-key) + signer-names))))))) signature micalg) (epg-context-set-signers context signers) (if mml-smime-cache-passphrase @@ -397,7 +441,10 @@ If no one is selected, default secret key is used. " context #'mml-smime-epg-passphrase-callback)) (condition-case error - (setq signature (epg-sign-string context (buffer-string) t) + (setq signature (epg-sign-string context + (mm-replace-in-string (buffer-string) + "\n" "\r\n") + t) mml-smime-epg-secret-key-id-list nil) (error (while mml-smime-epg-secret-key-id-list @@ -431,13 +478,17 @@ Content-Disposition: attachment; filename=smime.p7s (goto-char (point-max)))) (defun mml-smime-epg-encrypt (cont) - (let ((inhibit-redisplay t) - (context (epg-make-context 'CMS)) - (config (epg-configuration)) - (recipients (message-options-get 'mml-smime-epg-recipients)) - cipher signers - (boundary (mml-compute-boundary cont)) - recipient-key) + (let* ((inhibit-redisplay t) + (context (epg-make-context 'CMS)) + (config (epg-configuration)) + (recipients (message-options-get 'mml-smime-epg-recipients)) + cipher signers + (sender (message-options-get 'message-sender)) + (signer-names (or mml-smime-signers + (if (and mml-smime-sign-with-sender sender) + (list (concat "<" sender ">"))))) + (boundary (mml-compute-boundary cont)) + recipient-key) (unless recipients (setq recipients (apply #'nconc @@ -450,7 +501,11 @@ Content-Disposition: attachment; filename=smime.p7s (message-options-set 'message-recipients (read-string "Recipients: "))) "[ \f\t\n\r\v,]+")))) - (if mml-smime-verbose + (when mml-smime-encrypt-to-self + (unless signer-names + (error "Neither message sender nor mml-smime-signers are set")) + (setq recipients (nconc recipients signer-names))) + (if (eq mm-encrypt-option 'guided) (setq recipients (epa-select-keys context "\ Select recipients for encryption. @@ -508,14 +563,19 @@ Content-Disposition: attachment; filename=smime.p7m ctl 'protocol) "application/pkcs7-signature") t))) - (null (setq signature (mm-find-part-by-type - (cdr handle) - "application/pkcs7-signature" - nil t)))) + (null (setq signature (or (mm-find-part-by-type + (cdr handle) + "application/pkcs7-signature" + nil t) + (mm-find-part-by-type + (cdr handle) + "application/x-pkcs7-signature" + nil t))))) (mm-set-handle-multipart-parameter mm-security-handle 'gnus-info "Corrupted") (throw 'error handle)) - (setq context (epg-make-context 'CMS)) + (setq part (mm-replace-in-string part "\n" "\r\n") + context (epg-make-context 'CMS)) (condition-case error (setq plain (epg-verify-string context (mm-get-part signature) part)) (error @@ -537,5 +597,4 @@ Content-Disposition: attachment; filename=smime.p7m (provide 'mml-smime) -;;; arch-tag: f1bf94d4-f2cd-4c6f-b059-ad69492817e2 ;;; mml-smime.el ends here