X-Git-Url: https://cgit.sxemacs.org/?a=blobdiff_plain;f=lisp%2Fmml2015.el;h=1b1ba0d855b3802875ab88ecdc0b3459e46ab942;hb=c358f44b1670d12d5eff5fe5a447a19afd34a252;hp=71c4ee624a24d073e398ef4b0de6ac76cdf0b535;hpb=22f7dc0931bb416a9520d4792f81a7a820e71db1;p=gnus diff --git a/lisp/mml2015.el b/lisp/mml2015.el index 71c4ee624..1b1ba0d85 100644 --- a/lisp/mml2015.el +++ b/lisp/mml2015.el @@ -1,7 +1,7 @@ ;;; mml2015.el --- MIME Security with Pretty Good Privacy (PGP) ;; Copyright (C) 2000, 2001, 2002, 2003, 2004, -;; 2005, 2006, 2007 Free Software Foundation, Inc. +;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc. ;; Author: Shenghuo Zhu ;; Keywords: PGP MIME MML @@ -10,7 +10,7 @@ ;; 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 +;; by the Free Software Foundation; either version 3, or (at your ;; option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, but @@ -30,6 +30,10 @@ ;;; Code: +;; For Emacs < 22.2. +(eval-and-compile + (unless (fboundp 'declare-function) (defmacro declare-function (&rest r)))) + (eval-when-compile (require 'cl)) (require 'mm-decode) (require 'mm-util) @@ -38,6 +42,10 @@ (defvar mc-pgp-always-sign) +(declare-function epg-check-configuration "ext:epg-config" + (config &optional minimum-version)) +(declare-function epg-configuration "ext:epg-config" ()) + (defvar mml2015-use (or (condition-case nil (progn @@ -141,6 +149,28 @@ Whether the passphrase is cached at all is controlled by :group 'mime-security :type 'boolean) +;; Extract plaintext from cleartext signature. IMO, this kind of task +;; should be done by GnuPG rather than Elisp, but older PGP backends +;; (such as Mailcrypt, PGG, and gpg.el) discard the output from GnuPG. +(defun mml2015-extract-cleartext-signature () + (goto-char (point-min)) + (forward-line) + ;; We need to be careful not to strip beyond the armor headers. + ;; Previously, an attacker could replace the text inside our + ;; markup with trailing garbage by injecting whitespace into the + ;; message. + (while (looking-at "Hash:") ; The only header allowed in cleartext + (forward-line)) ; signatures according to RFC2440. + (when (looking-at "[\t ]*$") + (forward-line)) + (delete-region (point-min) (point)) + (if (re-search-forward "^-----BEGIN PGP SIGNATURE-----" nil t) + (delete-region (match-beginning 0) (point-max))) + (goto-char (point-min)) + (while (re-search-forward "^- " nil t) + (replace-match "" t t) + (forward-line 1))) + ;;; mailcrypt wrapper (eval-and-compile @@ -151,9 +181,8 @@ Whether the passphrase is cached at all is controlled by (autoload 'mc-cleanup-recipient-headers "mc-toplev") (autoload 'mc-sign-generic "mc-toplev")) -(eval-when-compile - (defvar mc-default-scheme) - (defvar mc-schemes)) +(defvar mc-default-scheme) +(defvar mc-schemes) (defvar mml2015-decrypt-function 'mailcrypt-decrypt) (defvar mml2015-verify-function 'mailcrypt-verify) @@ -327,7 +356,8 @@ Whether the passphrase is cached at all is controlled by (mm-set-handle-multipart-parameter mm-security-handle 'gnus-info "OK") (mm-set-handle-multipart-parameter - mm-security-handle 'gnus-info "Failed")))) + mm-security-handle 'gnus-info "Failed"))) + (mml2015-extract-cleartext-signature)) (defun mml2015-mailcrypt-sign (cont) (mc-sign-generic (message-options-get 'message-sender) @@ -374,6 +404,10 @@ Whether the passphrase is cached at all is controlled by (insert (format "--%s--\n" boundary)) (goto-char (point-max)))) +;; We require mm-decode, which requires mm-bodies, which autoloads +;; message-options-get (!). +(declare-function message-options-set "message" (symbol value)) + (defun mml2015-mailcrypt-encrypt (cont &optional sign) (let ((mc-pgp-always-sign (or mc-pgp-always-sign @@ -593,7 +627,8 @@ Whether the passphrase is cached at all is controlled by (with-current-buffer mml2015-result-buffer (mml2015-gpg-extract-signature-details))) (mm-set-handle-multipart-parameter - mm-security-handle 'gnus-info "Failed"))) + mm-security-handle 'gnus-info "Failed")) + (mml2015-extract-cleartext-signature)) (defun mml2015-gpg-sign (cont) (let ((boundary (mml-compute-boundary cont)) @@ -683,10 +718,9 @@ Whether the passphrase is cached at all is controlled by ;;; pgg wrapper -(eval-when-compile - (defvar pgg-default-user-id) - (defvar pgg-errors-buffer) - (defvar pgg-output-buffer)) +(defvar pgg-default-user-id) +(defvar pgg-errors-buffer) +(defvar pgg-output-buffer) (eval-and-compile (autoload 'pgg-decrypt-region "pgg") @@ -856,7 +890,8 @@ Whether the passphrase is cached at all is controlled by (with-current-buffer pgg-errors-buffer (mml2015-gpg-extract-signature-details))) (mm-set-handle-multipart-parameter - mm-security-handle 'gnus-info "Failed")))) + mm-security-handle 'gnus-info "Failed"))) + (mml2015-extract-cleartext-signature)) (defun mml2015-pgg-sign (cont) (let ((pgg-errors-buffer mml2015-result-buffer) @@ -920,13 +955,12 @@ Whether the passphrase is cached at all is controlled by ;;; epg wrapper -(eval-and-compile - (autoload 'epg-make-context "epg")) +(defvar epg-user-id-alist) +(defvar epg-digest-algorithm-alist) +(defvar inhibit-redisplay) -(eval-when-compile - (defvar epg-user-id-alist) - (defvar epg-digest-algorithm-alist) - (defvar inhibit-redisplay) +(eval-and-compile + (autoload 'epg-make-context "epg") (autoload 'epg-context-set-armor "epg") (autoload 'epg-context-set-textmode "epg") (autoload 'epg-context-set-signers "epg") @@ -940,15 +974,14 @@ Whether the passphrase is cached at all is controlled by (autoload 'epg-encrypt-string "epg") (autoload 'epg-passphrase-callback-function "epg") (autoload 'epg-context-set-passphrase-callback "epg") + (autoload 'epg-key-sub-key-list "epg") + (autoload 'epg-sub-key-capability "epg") + (autoload 'epg-sub-key-validity "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 password-cache-expiry) (defvar mml2015-epg-secret-key-id-list nil) @@ -973,6 +1006,18 @@ Whether the passphrase is cached at all is controlled by (cons key-id mml2015-epg-secret-key-id-list)) (copy-sequence passphrase))))) +(defun mml2015-epg-find-usable-key (keys usage) + (catch 'found + (while keys + (let ((pointer (epg-key-sub-key-list (car keys)))) + (while pointer + (if (and (memq usage (epg-sub-key-capability (car pointer))) + (not (memq (epg-sub-key-validity (car pointer)) + '(revoked expired)))) + (throw 'found (car keys))) + (setq pointer (cdr pointer)))) + (setq keys (cdr keys))))) + (defun mml2015-epg-decrypt (handle ctl) (catch 'error (let ((inhibit-redisplay t) @@ -1069,7 +1114,7 @@ Whether the passphrase is cached at all is controlled by (defun mml2015-epg-verify (handle ctl) (catch 'error (let ((inhibit-redisplay t) - context plain signature-file part signature (index 0)) + context plain signature-file part signature) (when (or (null (setq part (mm-find-raw-part-by-type ctl (or (mm-handle-multipart-ctl-parameter ctl 'protocol) @@ -1081,14 +1126,11 @@ Whether the passphrase is cached at all is controlled by (mm-set-handle-multipart-parameter mm-security-handle 'gnus-info "Corrupted") (throw 'error handle)) - (while (string-match "\r?\n" part index) - (if (eq (aref part (match-beginning 0)) ?\r) - (setq index (match-end 0)) - (setq part (replace-match "\r\n" t t part) - index (1+ (match-end 0))))) - (setq context (epg-make-context)) + (setq part (mm-replace-in-string part "\n" "\r\n" t) + signature (mm-get-part signature) + context (epg-make-context)) (condition-case error - (setq plain (epg-verify-string context (mm-get-part signature) part)) + (setq plain (epg-verify-string context signature part)) (error (mm-set-handle-multipart-parameter mm-security-handle 'gnus-info "Failed") @@ -1107,7 +1149,7 @@ Whether the passphrase is cached at all is controlled by (let ((inhibit-redisplay t) (context (epg-make-context)) (signature (mm-encode-coding-string (buffer-string) - buffer-file-coding-system)) + coding-system-for-write)) plain) (condition-case error (setq plain (epg-verify-string context signature)) @@ -1120,40 +1162,43 @@ Whether the passphrase is cached at all is controlled by (mm-set-handle-multipart-parameter mm-security-handle 'gnus-details (mml2015-format-error error))))) (if plain - (mm-set-handle-multipart-parameter - mm-security-handle 'gnus-info - (epg-verify-result-to-string - (epg-context-result-for context 'verify)))))) + (progn + (mm-set-handle-multipart-parameter + mm-security-handle 'gnus-info + (epg-verify-result-to-string + (epg-context-result-for context 'verify))) + (delete-region (point-min) (point-max)) + (insert (mm-decode-coding-string plain coding-system-for-read))) + (mml2015-extract-cleartext-signature)))) (defun mml2015-epg-sign (cont) (let* ((inhibit-redisplay t) - (context (epg-make-context)) - (boundary (mml-compute-boundary cont)) - signer-keys - (signers - (or (message-options-get 'mml2015-epg-signers) - (message-options-set - 'mml2015-epg-signers - (if mml2015-verbose - (epa-select-keys context "\ + (context (epg-make-context)) + (boundary (mml-compute-boundary cont)) + signer-key + (signers + (or (message-options-get 'mml2015-epg-signers) + (message-options-set + 'mml2015-epg-signers + (if mml2015-verbose + (epa-select-keys context "\ Select keys for signing. If no one is selected, default secret key is used. " - mml2015-signers t) - (if mml2015-signers - (apply #'nconc - (mapcar - (lambda (signer) - (setq signer-keys - (epg-list-keys context signer t)) - (unless (or signer-keys - (y-or-n-p - (format - "No secret key for %s; skip it? " - signer))) - (error "No secret key for %s" signer)) - signer-keys) - mml2015-signers))))))) - signature micalg) + mml2015-signers t) + (if mml2015-signers + (mapcar + (lambda (signer) + (setq signer-key (mml2015-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))) + (error "No secret key for %s" signer)) + signer-key) + mml2015-signers)))))) + signature micalg) (epg-context-set-armor context t) (epg-context-set-textmode context t) (epg-context-set-signers context signers) @@ -1198,7 +1243,7 @@ If no one is selected, default secret key is used. " (recipients (message-options-get 'mml2015-epg-recipients)) cipher signers (boundary (mml-compute-boundary cont)) - recipient-keys signer-keys) + recipient-key signer-key) (unless recipients (setq recipients (apply #'nconc @@ -1222,17 +1267,18 @@ Select recipients for encryption. If no one is selected, symmetric encryption will be performed. " recipients)) (setq recipients - (apply #'nconc - (mapcar - (lambda (recipient) - (setq recipient-keys (epg-list-keys context recipient)) - (unless (or recipient-keys - (y-or-n-p - (format "No public key for %s; skip it? " - recipient))) - (error "No public key for %s" recipient)) - recipient-keys) - recipients))) + (mapcar + (lambda (recipient) + (setq recipient-key (mml2015-epg-find-usable-key + (epg-list-keys context recipient) + 'encrypt)) + (unless (or recipient-key + (y-or-n-p + (format "No public key for %s; skip it? " + recipient))) + (error "No public key for %s" recipient)) + recipient-key) + recipients)) (unless recipients (error "No recipient specified"))) (message-options-set 'mml2015-epg-recipients recipients)) @@ -1247,19 +1293,19 @@ Select keys for signing. If no one is selected, default secret key is used. " mml2015-signers t) (if mml2015-signers - (apply #'nconc - (mapcar - (lambda (signer) - (setq signer-keys - (epg-list-keys context signer t)) - (unless (or signer-keys - (y-or-n-p - (format - "No secret key for %s; skip it? " - signer))) - (error "No secret key for %s" signer)) - signer-keys) - mml2015-signers))))))) + (mapcar + (lambda (signer) + (setq signer-key (mml2015-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))) + (error "No secret key for %s" signer)) + signer-key) + mml2015-signers)))))) (epg-context-set-signers context signers)) (epg-context-set-armor context t) (epg-context-set-textmode context t) @@ -1295,6 +1341,9 @@ If no one is selected, default secret key is used. " ;;; General wrapper +(autoload 'gnus-buffer-live-p "gnus-util") +(autoload 'gnus-get-buffer-create "gnus") + (defun mml2015-clean-buffer () (if (gnus-buffer-live-p mml2015-result-buffer) (with-current-buffer mml2015-result-buffer