Refactor mml-smime.el, mml1991.el, mml2015.el
[gnus.git] / contrib / smime-card.el
bloba0fcab427d993846f8b4979a4226a6b2480fbda5
1 ;;; smime-card.el --- Make smime.el work with card readers
3 ;; Copyright (C) 2005 Brailcom, o.p.s.
4 ;; Author: Milan Zamazal <pdm@zamazal.org>
6 ;; COPYRIGHT NOTICE
7 ;;
8 ;; This program is free software; you can redistribute it and/or modify it
9 ;; under the terms of the GNU General Public License as published by the Free
10 ;; Software Foundation; either version 2, or (at your option) any later
11 ;; version.
13 ;; This program is distributed in the hope that it will be useful, but
14 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 ;; for more details.
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this program; if not, see <http://www.gnu.org/licenses/>.
21 ;;; Commentary:
23 ;; This is a simple wrapper around smime.el allowing to use private keys stored
24 ;; on a smard card.
26 ;; To use it, just put (require 'smime-card) to you Emacs startup file and
27 ;; customize the variable `smime-card-file-keys'.
29 ;;; Code:
31 (require 'smime)
33 ;;; Configuration
35 (defcustom smime-card-file-keys '()
36 "Alist of certificate files and their corresponding private key card ids.
37 Each element of the list is of the form (FILE . KEY-ID), where FILE is a
38 certificate file stored on a regular file system and KEY-ID is the identifier
39 of the corresponding private key stored on the card.
40 If FILE begins with the prefix `card:', the certificate is retrieved from the
41 card under the id following the `card:' prefix in FILE."
42 :type '(alist :key-type (file :tag "Certificate file")
43 :value-type (string :tag "Key identifier"))
44 :group 'smime)
46 (defcustom smime-card-fetch-certificates nil
47 "If non-nil, fetch certificates from the card before verifying messages."
48 :type 'boolean
49 :group 'smime)
51 ;;; Internals
53 (defvar smime-card-key nil)
55 (defun smime-card-key (keyfile)
56 (cdr (assoc keyfile smime-card-file-keys)))
58 (defvar smime-card-engine-command
59 "engine dynamic -pre SO_PATH:/usr/lib/opensc/engine_pkcs11.so -pre ID:pkcs11 -pre LIST_ADD:1 -pre LOAD\n")
61 (defvar smime-card-process-output "")
63 (defun smime-card-process-filter (process string)
64 (setq smime-card-process-output (concat smime-card-process-output string)))
66 (defun smime-card-wait-for-prompt (process)
67 (while (not (string-match "\\(OpenSSL> \\|PIN: \\)$"
68 smime-card-process-output))
69 (unless (accept-process-output process 5)
70 (message "OpenSSL: Timeout")
71 (throw 'error nil)))
72 (prog1 (if (string= (match-string 1 smime-card-process-output) "PIN: ")
73 'pin
75 (setq smime-card-process-output "")))
77 (defun smime-card-call-openssl-region (b e buf &rest args)
78 (let* ((infile (make-temp-file "smime-card-in"))
79 (outfile (make-temp-file "smime-card-out"))
80 (cert-on-card (and (string-match "^card:\\(.*\\)$" keyfile)
81 (match-string 1 keyfile)))
82 (certfile (and cert-on-card (make-temp-file "smime-card-cert")))
83 (args (append args
84 (list "-engine" "pkcs11"
85 "-keyform" "engine"
86 "-inkey" smime-card-key
87 "-in" infile "-out" outfile)))
88 (process (start-process "openssl" " *openssl*" smime-openssl-program)))
89 (unwind-protect
90 (catch 'error
91 (when certfile
92 (unless (= (call-process "pkcs15-tool" nil nil nil
93 "-r" cert-on-card "-o" certfile)
95 (message "pkcs15: Error")
96 (throw 'error nil))
97 (let ((args* args))
98 (while (and args* (not (string= (car args*) "-signer")))
99 (setq args* (cdr args*)))
100 (setq args* (cdr args*))
101 (when args*
102 (setcar args* certfile))))
103 (setq smime-card-process-output "")
104 (set-process-filter process 'smime-card-process-filter)
105 (unless (eq (smime-card-wait-for-prompt process) t)
106 (message "OpenSSL: Error on startup")
107 (throw 'error nil))
108 (process-send-string process smime-card-engine-command)
109 (unless (eq (smime-card-wait-for-prompt process) t)
110 (message "OpenSSL: Error in pkcs11 loading")
111 (throw 'error nil))
112 (write-region b e infile nil 0)
113 (process-send-string process
114 (concat (mapconcat 'identity args " ") "\n"))
115 (let ((answer (smime-card-wait-for-prompt process)))
116 (cond
117 ((eq answer 'pin)
118 (process-send-string process (concat (read-passwd "Smartcard PIN: ") "\n"))
119 (unless (eq (smime-card-wait-for-prompt process) t)
120 (message "OpenSSL: Error after passphrase")
121 (throw 'error nil)))
122 ((eq answer t)
123 nil)
125 (message "OpenSSL: Error in processing")
126 (throw 'error nil))))
127 (process-send-eof process)
128 (with-current-buffer (car buf)
129 (when (= (cadr (insert-file-contents outfile)) 0)
130 (message "OpenSSL: Empty output")
131 (throw 'error nil)))
133 (delete-file infile)
134 (delete-file outfile)
135 (when certfile (delete-file certfile))
136 (delete-process process)
137 (kill-buffer " *openssl*"))))
139 ;;; smime.el advices
141 (defadvice smime-sign-region (around smime-card-sign-region activate)
142 (let ((smime-card-key (smime-card-key (ad-get-arg 2))))
143 ad-do-it))
145 (defadvice smime-decrypt-region (around smime-card-decrypt-region activate)
146 (let ((smime-card-key (smime-card-key (ad-get-arg 2))))
147 ad-do-it))
149 (defadvice smime-call-openssl-region (around smime-card-openssl activate)
150 (if smime-card-key
151 (setq ad-return-value
152 (apply 'smime-card-call-openssl-region (ad-get-args 0)))
153 ad-do-it))
155 (defadvice smime-verify-region (around smime-card-verify-region activate)
156 (if smime-card-fetch-certificates
157 (let ((cert-ids '()))
158 (with-temp-buffer
159 (unless (= (call-process
160 "pkcs15-tool" nil t nil "--list-certificates")
162 (error "pkcs15: Certificate listing"))
163 (goto-char (point-min))
164 (while (re-search-forward "^[\t ]+ID[ ]+: \\([0-9]+\\) *$" nil t)
165 (setq cert-ids (cons (match-string 1) cert-ids))))
166 (let ((certfile (make-temp-file "smime-card")))
167 (unwind-protect
168 (progn
169 (with-temp-file certfile
170 (when smime-CA-file
171 (insert-file-contents smime-CA-file))
172 (mapc (lambda (id)
173 (unless (= (call-process "pkcs15-tool" nil t nil
174 "-r" id)
176 (error "pkcs15: Certificat read")))
177 cert-ids))
178 (let ((smime-CA-file certfile))
179 ad-do-it))
180 (delete-file certfile))))
181 ad-do-it))
183 (defadvice mml-smime-verify (around smime-card-mml-smime-verify activate)
184 ;; If both smime-CA-directory and smime-CA-file are unset, `mml-smime-verify'
185 ;; refuses to perform certificate verification.
186 (let ((smime-CA-file (if smime-card-fetch-certificates
187 (or smime-CA-file "/dev/null")
188 smime-CA-file)))
189 ad-do-it))
191 ;;; Announce
193 (provide 'smime-card)
195 ;;; smime-card.el ends here