Add arch taglines
[gnus] / lisp / smime.el
index 2d47307..259ec3c 100644 (file)
@@ -1,10 +1,10 @@
 ;;; smime.el --- S/MIME support library
-;; Copyright (c) 2000, 2001 Free Software Foundation, Inc.
+;; Copyright (c) 2000, 2001, 2003 Free Software Foundation, Inc.
 
 ;; Author: Simon Josefsson <simon@josefsson.org>
 ;; Keywords: SMIME X.509 PEM OpenSSL
 
-;; This file is not a part of GNU Emacs, but the same permissions apply.
+;; 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
@@ -41,6 +41,9 @@
 ;; PKCSx or similar, it's intended to perform common operations
 ;; done on messages encoded in these formats.  The terminology chosen
 ;; reflect this.
+;;
+;; The home of this file is in Gnus CVS, but also available from
+;; http://josefsson.org/smime.html.
 
 ;;; Quick introduction:
 
 ;; environment variables to pass the password to OpenSSL, which is
 ;; slightly insecure. Hence a new todo: use a better -passin method.
 ;;
+;; Cache password for e.g. 1h
+;;
 ;; Suggestions and comments are appreciated, mail me at simon@josefsson.org.
 
-;; <rant>
+;; begin rant
 ;;
 ;; I would include pointers to introductory text on concepts used in
 ;; this library here, but the material I've read are so horrible I
 ;; Also, I'm not going to mention anything about the wonders of
 ;; cryptopolitics.  Oops, I just did.
 ;;
-;; </rant>
+;; end rant
 
 ;;; Revision history:
 
-;; version 0 not released
+;; 2000-06-05  initial version, committed to Gnus CVS contrib/
+;; 2000-10-28  retrieve certificates via DNS CERT RRs
+;; 2001-10-14  posted to gnu.emacs.sources
 
 ;;; Code:
 
 (require 'dig)
-(require 'comint)
 (eval-when-compile (require 'cl))
 
 (defgroup smime nil
@@ -133,7 +139,7 @@ certificates to be sent with every message to each address."
 Directory should contain files (in PEM format) named to the X.509
 hash of the certificate.  This can be done using OpenSSL such as:
 
-$ ln -s ca.pem `openssl x509 -noout -hash -in ca.pem`
+$ ln -s ca.pem `openssl x509 -noout -hash -in ca.pem`.0
 
 where `ca.pem' is the file containing a PEM encoded X.509 CA
 certificate."
@@ -178,6 +184,27 @@ and the files themself should be in PEM format."
                 (const :tag "RC2 128 bits" "-rc2-128"))
   :group 'smime)
 
+(defcustom smime-crl-check nil
+  "*Check revocation status of signers certificate using CRLs.
+Enabling this will have OpenSSL check the signers certificate
+against a certificate revocation list (CRL).
+
+For this to work the CRL must be up-to-date and since they are
+normally updated quite often (ie. several times a day) you
+probably need some tool to keep them up-to-date. Unfortunately
+Gnus cannot do this for you.
+
+The CRL should either be appended (in PEM format) to your
+`smime-CA-file' or be located in a file (also in PEM format) in
+your `smime-certificate-directory' named to the X.509 hash of the
+certificate with .r0 as file name extension.
+
+At least OpenSSL version 0.9.7 is required for this to work."
+  :type '(choice (const :tag "No check" nil)
+                (const :tag "Check certificate" "-crl_check")
+                (const :tag "Check certificate chain" "-crl_check_all"))
+  :group 'smime)
+
 (defcustom smime-dns-server nil
   "*DNS server to query certificates from.
 If nil, use system defaults."
@@ -185,22 +212,27 @@ If nil, use system defaults."
                 string)
   :group 'smime)
 
-(defcustom smime-extra-arguments nil
-  "*List of additional arguments passed to OpenSSL.
-For instance, if you don't have a /dev/random you might be forced
-to set this to e.g. `(\"-rand\" \"/etc/entropy\")'."
-  :type '(repeat string)
-  :group 'smime)
-
 (defvar smime-details-buffer "*OpenSSL output*")
 
+;; Use mm-util?
+(eval-and-compile
+  (defalias 'smime-make-temp-file
+    (if (fboundp 'make-temp-file)
+       'make-temp-file
+      (lambda (prefix &optional dir-flag) ;; Simple implementation
+       (expand-file-name
+        (make-temp-name prefix)
+        (if (fboundp 'temp-directory)
+            (temp-directory)
+          temporary-file-directory))))))
+
 ;; Password dialog function
 
 (defun smime-ask-passphrase ()
   "Asks the passphrase to unlock the secret key."
   (let ((passphrase
-        (comint-read-noecho
-         "Passphrase for secret key (RET for no passphrase): " t)))
+        (read-passwd
+         "Passphrase for secret key (RET for no passphrase): ")))
     (if (string= passphrase "")
        nil
       passphrase)))
@@ -208,8 +240,7 @@ to set this to e.g. `(\"-rand\" \"/etc/entropy\")'."
 ;; OpenSSL wrappers.
 
 (defun smime-call-openssl-region (b e buf &rest args)
-  (case (apply 'call-process-region b e smime-openssl-program nil
-              buf nil (append smime-extra-arguments args))
+  (case (apply 'call-process-region b e smime-openssl-program nil buf nil args)
     (0 t)
     (1 (message "OpenSSL: An error occurred parsing the command options.") nil)
     (2 (message "OpenSSL: One of the input files could not be read.") nil)
@@ -224,33 +255,42 @@ to set this to e.g. `(\"-rand\" \"/etc/entropy\")'."
 
 ;; Sign+encrypt region
 
-(defun smime-sign-region (b e keyfiles)
-  "Sign region with certified key in KEYFILES.
+(defun smime-sign-region (b e keyfile)
+  "Sign region with certified key in KEYFILE.
 If signing fails, the buffer is not modified.  Region is assumed to
-have proper MIME tags.  KEYFILES 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."
-  (let ((keyfile (car keyfiles))
-       (certfiles (and (cdr keyfiles) (cadr keyfiles)))
+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
+included, KEYFILE may be the file containing the PEM encoded private
+key and certificate itself."
+  (smime-new-details-buffer)
+  (let ((keyfile (or (car-safe keyfile) keyfile))
+       (certfiles (and (cdr-safe keyfile) (cadr keyfile)))
        (buffer (generate-new-buffer (generate-new-buffer-name " *smime*")))
-       (passphrase (smime-ask-passphrase)))
+       (passphrase (smime-ask-passphrase))
+       (tmpfile (smime-make-temp-file "smime")))
     (if passphrase
        (setenv "GNUS_SMIME_PASSPHRASE" passphrase))
     (prog1
-       (when (apply 'smime-call-openssl-region b e buffer "smime" "-sign"
-                    "-signer" (expand-file-name keyfile)
-                    (append
-                     (smime-make-certfiles certfiles)
-                     (if passphrase
-                         (list "-passin" "env:GNUS_SMIME_PASSPHRASE"))))
+       (when (prog1
+                 (apply 'smime-call-openssl-region b e (list buffer tmpfile)
+                        "smime" "-sign" "-signer" (expand-file-name keyfile)
+                        (append
+                         (smime-make-certfiles certfiles)
+                         (if passphrase
+                             (list "-passin" "env:GNUS_SMIME_PASSPHRASE"))))
+               (if passphrase
+                   (setenv "GNUS_SMIME_PASSPHRASE" "" t))
+               (with-current-buffer smime-details-buffer
+                 (insert-file-contents tmpfile)
+                 (delete-file tmpfile)))
          (delete-region b e)
          (insert-buffer-substring buffer)
+         (goto-char b)
          (when (looking-at "^MIME-Version: 1.0$")
            (delete-region (point) (progn (forward-line 1) (point))))
          t)
-      (if passphrase
-         (setenv "GNUS_SMIME_PASSPHRASE" "" t))
-      (with-current-buffer (get-buffer-create smime-details-buffer)
+      (with-current-buffer smime-details-buffer
        (goto-char (point-max))
        (insert-buffer-substring buffer))
       (kill-buffer buffer))))
@@ -260,16 +300,24 @@ to include in its caar."
 If encryption fails, the buffer is not modified.  Region is assumed to
 have proper MIME tags.  CERTFILES is a list of filenames, each file
 is expected to contain of a PEM encoded certificate."
-  (let ((buffer (generate-new-buffer (generate-new-buffer-name " *smime*"))))
+  (smime-new-details-buffer)
+  (let ((buffer (generate-new-buffer (generate-new-buffer-name " *smime*")))
+       (tmpfile (smime-make-temp-file "smime")))
     (prog1
-       (when (apply 'smime-call-openssl-region b e buffer "smime" "-encrypt"
-                    smime-encrypt-cipher (mapcar 'expand-file-name certfiles))
+       (when (prog1
+                 (apply 'smime-call-openssl-region b e (list buffer tmpfile)
+                        "smime" "-encrypt" smime-encrypt-cipher
+                        (mapcar 'expand-file-name certfiles))
+               (with-current-buffer smime-details-buffer
+                 (insert-file-contents tmpfile)
+                 (delete-file tmpfile)))
          (delete-region b e)
          (insert-buffer-substring buffer)
+         (goto-char b)
          (when (looking-at "^MIME-Version: 1.0$")
            (delete-region (point) (progn (forward-line 1) (point))))
          t)
-      (with-current-buffer (get-buffer-create smime-details-buffer)
+      (with-current-buffer smime-details-buffer
        (goto-char (point-max))
        (insert-buffer-substring buffer))
       (kill-buffer buffer))))
@@ -284,11 +332,13 @@ KEYFILE should contain a PEM encoded key and certificate."
     (smime-sign-region
      (point-min) (point-max)
      (if keyfile
-        (list keyfile (smime-get-certfiles keyfile smime-keys))
-       (smime-get-key-by-email
-       (completing-read "Sign using which signature? " smime-keys nil nil
-                        (and (listp (car-safe smime-keys))
-                             (cdr smime-keys))))))))
+        keyfile
+       (smime-get-key-with-certs-by-email
+       (completing-read
+        (concat "Sign using which key? "
+                (if smime-keys (concat "(default " (caar smime-keys) ") ")
+                  ""))
+        smime-keys nil nil (car-safe (car-safe smime-keys))))))))
 
 (defun smime-encrypt-buffer (&optional certfiles buffer)
   "S/MIME encrypt BUFFER for recipients specified in CERTFILES.
@@ -319,6 +369,8 @@ Any details (stdout and stderr) are left in the buffer specified by
                               (expand-file-name smime-CA-directory))))))
     (unless CAs
       (error "No CA configured"))
+    (if smime-crl-check
+       (add-to-list 'CAs smime-crl-check))
     (if (apply 'smime-call-openssl-region b e (list smime-details-buffer t)
               "smime" "-verify" "-out" "/dev/null" CAs)
        t
@@ -337,6 +389,9 @@ Any details (stdout and stderr) are left in the buffer specified by
     (insert-buffer-substring smime-details-buffer)
     nil))
 
+(eval-when-compile
+  (defvar from))
+
 (defun smime-decrypt-region (b e keyfile)
   "Decrypt S/MIME message in region between B and E with key in KEYFILE.
 On success, replaces region with decrypted data and return non-nil.
@@ -345,7 +400,7 @@ in the buffer specified by `smime-details-buffer'."
   (smime-new-details-buffer)
   (let ((buffer (generate-new-buffer (generate-new-buffer-name " *smime*")))
        CAs (passphrase (smime-ask-passphrase))
-       (tmpfile (make-temp-file "smime")))
+       (tmpfile (smime-make-temp-file "smime")))
     (if passphrase
        (setenv "GNUS_SMIME_PASSPHRASE" passphrase))
     (if (prog1
@@ -361,6 +416,9 @@ in the buffer specified by `smime-details-buffer'."
            (delete-file tmpfile)))
        (progn
          (delete-region b e)
+         (when (boundp 'from)
+           ;; `from' is dynamically bound in mm-dissect.
+           (insert "From: " from "\n"))
          (insert-buffer-substring buffer)
          (kill-buffer buffer)
          t)
@@ -405,9 +463,11 @@ in the buffer specified by `smime-details-buffer'."
      (expand-file-name
       (or keyfile
          (smime-get-key-by-email
-          (completing-read "Decrypt with which key? " smime-keys nil nil
-                           (and (listp (car-safe smime-keys))
-                                (caar smime-keys)))))))))
+          (completing-read
+           (concat "Decipher using which key? "
+                   (if smime-keys (concat "(default " (caar smime-keys) ") ")
+                     ""))
+           smime-keys nil nil (car-safe (car-safe smime-keys)))))))))
 
 ;; Various operations
 
@@ -436,7 +496,7 @@ in the buffer specified by `smime-details-buffer'."
   "Get email addresses contained in certificate between points B and E.
 A string or a list of strings is returned."
   (smime-new-details-buffer)
-  (when (smime-call-openssl-region 
+  (when (smime-call-openssl-region
         b e smime-details-buffer "x509" "-email" "-noout")
     (delete-region b e)
     (insert-buffer-substring smime-details-buffer)
@@ -452,18 +512,13 @@ A string or a list of strings is returned."
            (caddr curkey)
          (smime-get-certfiles keyfile otherkeys)))))
 
-(defalias 'smime-point-at-eol
-  (if (fboundp 'point-at-eol)
-      'point-at-eol
-    'line-end-position))
-
 (defun smime-buffer-as-string-region (b e)
   "Return each line in region between B and E as a list of strings."
   (save-excursion
     (goto-char b)
     (let (res)
       (while (< (point) e)
-       (let ((str (buffer-substring (point) (smime-point-at-eol))))
+       (let ((str (buffer-substring (point) (point-at-eol))))
          (unless (string= "" str)
            (push str res)))
        (forward-line))
@@ -573,6 +628,10 @@ The following commands are available:
 (defun smime-get-key-by-email (email)
   (cadr (assoc email smime-keys)))
 
+(defun smime-get-key-with-certs-by-email (email)
+  (cdr (assoc email smime-keys)))
+
 (provide 'smime)
 
+;;; arch-tag: e3f9b938-5085-4510-8a11-6625269c9a9e
 ;;; smime.el ends here