]> code.delx.au - gnu-emacs/blob - lisp/gnus/mml-sec.el
Identify unsafe combinations of Bcc and encryption
[gnu-emacs] / lisp / gnus / mml-sec.el
1 ;;; mml-sec.el --- A package with security functions for MML documents
2
3 ;; Copyright (C) 2000-2015 Free Software Foundation, Inc.
4
5 ;; Author: Simon Josefsson <simon@josefsson.org>
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
21
22 ;;; Commentary:
23
24 ;;; Code:
25
26 (eval-when-compile (require 'cl))
27
28 (autoload 'mml2015-sign "mml2015")
29 (autoload 'mml2015-encrypt "mml2015")
30 (autoload 'mml1991-sign "mml1991")
31 (autoload 'mml1991-encrypt "mml1991")
32 (autoload 'message-goto-body "message")
33 (autoload 'mml-insert-tag "mml")
34 (autoload 'mml-smime-sign "mml-smime")
35 (autoload 'mml-smime-encrypt "mml-smime")
36 (autoload 'mml-smime-sign-query "mml-smime")
37 (autoload 'mml-smime-encrypt-query "mml-smime")
38 (autoload 'mml-smime-verify "mml-smime")
39 (autoload 'mml-smime-verify-test "mml-smime")
40
41 (defvar mml-sign-alist
42 '(("smime" mml-smime-sign-buffer mml-smime-sign-query)
43 ("pgp" mml-pgp-sign-buffer list)
44 ("pgpauto" mml-pgpauto-sign-buffer list)
45 ("pgpmime" mml-pgpmime-sign-buffer list))
46 "Alist of MIME signer functions.")
47
48 (defcustom mml-default-sign-method "pgpmime"
49 "Default sign method.
50 The string must have an entry in `mml-sign-alist'."
51 :version "22.1"
52 :type '(choice (const "smime")
53 (const "pgp")
54 (const "pgpauto")
55 (const "pgpmime")
56 string)
57 :group 'message)
58
59 (defvar mml-encrypt-alist
60 '(("smime" mml-smime-encrypt-buffer mml-smime-encrypt-query)
61 ("pgp" mml-pgp-encrypt-buffer list)
62 ("pgpauto" mml-pgpauto-sign-buffer list)
63 ("pgpmime" mml-pgpmime-encrypt-buffer list))
64 "Alist of MIME encryption functions.")
65
66 (defcustom mml-default-encrypt-method "pgpmime"
67 "Default encryption method.
68 The string must have an entry in `mml-encrypt-alist'."
69 :version "22.1"
70 :type '(choice (const "smime")
71 (const "pgp")
72 (const "pgpauto")
73 (const "pgpmime")
74 string)
75 :group 'message)
76
77 (defcustom mml-signencrypt-style-alist
78 '(("smime" separate)
79 ("pgp" combined)
80 ("pgpauto" combined)
81 ("pgpmime" combined))
82 "Alist specifying if `signencrypt' results in two separate operations or not.
83 The first entry indicates the MML security type, valid entries include
84 the strings \"smime\", \"pgp\", and \"pgpmime\". The second entry is
85 a symbol `separate' or `combined' where `separate' means that MML signs
86 and encrypt messages in a two step process, and `combined' means that MML
87 signs and encrypt the message in one step.
88
89 Note that the output generated by using a `combined' mode is NOT
90 understood by all PGP implementations, in particular PGP version
91 2 does not support it! See Info node `(message)Security' for
92 details."
93 :version "22.1"
94 :group 'message
95 :type '(repeat (list (choice (const :tag "S/MIME" "smime")
96 (const :tag "PGP" "pgp")
97 (const :tag "PGP/MIME" "pgpmime")
98 (string :tag "User defined"))
99 (choice (const :tag "Separate" separate)
100 (const :tag "Combined" combined)))))
101
102 (defcustom mml-secure-verbose nil
103 "If non-nil, ask the user about the current operation more verbosely."
104 :group 'message
105 :type 'boolean)
106
107 (defcustom mml-secure-cache-passphrase
108 (if (boundp 'password-cache)
109 password-cache
110 t)
111 "If t, cache passphrase."
112 :group 'message
113 :type 'boolean)
114
115 (defcustom mml-secure-passphrase-cache-expiry
116 (if (boundp 'password-cache-expiry)
117 password-cache-expiry
118 16)
119 "How many seconds the passphrase is cached.
120 Whether the passphrase is cached at all is controlled by
121 `mml-secure-cache-passphrase'."
122 :group 'message
123 :type 'integer)
124
125 (defcustom mml-secure-safe-bcc-list nil
126 "List of e-mail addresses that are safe to use in Bcc headers.
127 EasyPG encrypts e-mails to Bcc addresses, and the encrypted e-mail
128 by default identifies the used encryption keys, giving away the
129 Bcc'ed identities. Clearly, this contradicts the original goal of
130 *blind* copies.
131 For an academic paper explaining the problem, see URL
132 `http://crypto.stanford.edu/portia/papers/bb-bcc.pdf'.
133 Use this variable to specify e-mail addresses whose owners do not
134 mind if they are identifiable as recipients. This may be useful if
135 you use Bcc headers to encrypt e-mails to yourself."
136 :version "25.1"
137 :group 'message
138 :type '(repeat string))
139
140 ;;; Configuration/helper functions
141
142 (defun mml-signencrypt-style (method &optional style)
143 "Function for setting/getting the signencrypt-style used. Takes two
144 arguments, the method (e.g. \"pgp\") and optionally the mode
145 \(e.g. combined). If the mode is omitted, the current value is returned.
146
147 For example, if you prefer to use combined sign & encrypt with
148 smime, putting the following in your Gnus startup file will
149 enable that behavior:
150
151 \(mml-set-signencrypt-style \"smime\" combined)
152
153 You can also customize or set `mml-signencrypt-style-alist' instead."
154 (let ((style-item (assoc method mml-signencrypt-style-alist)))
155 (if style-item
156 (if (or (eq style 'separate)
157 (eq style 'combined))
158 ;; valid style setting?
159 (setf (second style-item) style)
160 ;; otherwise, just return the current value
161 (second style-item))
162 (message "Warning, attempt to set invalid signencrypt style"))))
163
164 ;;; Security functions
165
166 (defun mml-smime-sign-buffer (cont)
167 (or (mml-smime-sign cont)
168 (error "Signing failed... inspect message logs for errors")))
169
170 (defun mml-smime-encrypt-buffer (cont &optional sign)
171 (when sign
172 (message "Combined sign and encrypt S/MIME not support yet")
173 (sit-for 1))
174 (or (mml-smime-encrypt cont)
175 (error "Encryption failed... inspect message logs for errors")))
176
177 (defun mml-pgp-sign-buffer (cont)
178 (or (mml1991-sign cont)
179 (error "Signing failed... inspect message logs for errors")))
180
181 (defun mml-pgp-encrypt-buffer (cont &optional sign)
182 (or (mml1991-encrypt cont sign)
183 (error "Encryption failed... inspect message logs for errors")))
184
185 (defun mml-pgpmime-sign-buffer (cont)
186 (or (mml2015-sign cont)
187 (error "Signing failed... inspect message logs for errors")))
188
189 (defun mml-pgpmime-encrypt-buffer (cont &optional sign)
190 (or (mml2015-encrypt cont sign)
191 (error "Encryption failed... inspect message logs for errors")))
192
193 (defun mml-pgpauto-sign-buffer (cont)
194 (message-goto-body)
195 (or (if (re-search-backward "Content-Type: *multipart/.*" nil t) ; there must be a better way...
196 (mml2015-sign cont)
197 (mml1991-sign cont))
198 (error "Encryption failed... inspect message logs for errors")))
199
200 (defun mml-pgpauto-encrypt-buffer (cont &optional sign)
201 (message-goto-body)
202 (or (if (re-search-backward "Content-Type: *multipart/.*" nil t) ; there must be a better way...
203 (mml2015-encrypt cont sign)
204 (mml1991-encrypt cont sign))
205 (error "Encryption failed... inspect message logs for errors")))
206
207 (defun mml-secure-part (method &optional sign)
208 (save-excursion
209 (let ((tags (funcall (nth 2 (assoc method (if sign mml-sign-alist
210 mml-encrypt-alist))))))
211 (cond ((re-search-backward
212 "<#\\(multipart\\|part\\|external\\|mml\\)" nil t)
213 (goto-char (match-end 0))
214 (insert (if sign " sign=" " encrypt=") method)
215 (while tags
216 (let ((key (pop tags))
217 (value (pop tags)))
218 (when value
219 ;; Quote VALUE if it contains suspicious characters.
220 (when (string-match "[\"'\\~/*;() \t\n]" value)
221 (setq value (prin1-to-string value)))
222 (insert (format " %s=%s" key value))))))
223 ((or (re-search-backward
224 (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
225 (re-search-forward
226 (concat "^" (regexp-quote mail-header-separator) "\n") nil t))
227 (goto-char (match-end 0))
228 (apply 'mml-insert-tag 'part (cons (if sign 'sign 'encrypt)
229 (cons method tags))))
230 (t (error "The message is corrupted. No mail header separator"))))))
231
232 (defvar mml-secure-method
233 (if (equal mml-default-encrypt-method mml-default-sign-method)
234 mml-default-sign-method
235 "pgpmime")
236 "Current security method. Internal variable.")
237
238 (defun mml-secure-sign (&optional method)
239 "Add MML tags to sign this MML part.
240 Use METHOD if given. Else use `mml-secure-method' or
241 `mml-default-sign-method'."
242 (interactive)
243 (mml-secure-part
244 (or method mml-secure-method mml-default-sign-method)
245 'sign))
246
247 (defun mml-secure-encrypt (&optional method)
248 "Add MML tags to encrypt this MML part.
249 Use METHOD if given. Else use `mml-secure-method' or
250 `mml-default-sign-method'."
251 (interactive)
252 (mml-secure-part
253 (or method mml-secure-method mml-default-sign-method)))
254
255 (defun mml-secure-sign-pgp ()
256 "Add MML tags to PGP sign this MML part."
257 (interactive)
258 (mml-secure-part "pgp" 'sign))
259
260 (defun mml-secure-sign-pgpauto ()
261 "Add MML tags to PGP-auto sign this MML part."
262 (interactive)
263 (mml-secure-part "pgpauto" 'sign))
264
265 (defun mml-secure-sign-pgpmime ()
266 "Add MML tags to PGP/MIME sign this MML part."
267 (interactive)
268 (mml-secure-part "pgpmime" 'sign))
269
270 (defun mml-secure-sign-smime ()
271 "Add MML tags to S/MIME sign this MML part."
272 (interactive)
273 (mml-secure-part "smime" 'sign))
274
275 (defun mml-secure-encrypt-pgp ()
276 "Add MML tags to PGP encrypt this MML part."
277 (interactive)
278 (mml-secure-part "pgp"))
279
280 (defun mml-secure-encrypt-pgpmime ()
281 "Add MML tags to PGP/MIME encrypt this MML part."
282 (interactive)
283 (mml-secure-part "pgpmime"))
284
285 (defun mml-secure-encrypt-smime ()
286 "Add MML tags to S/MIME encrypt this MML part."
287 (interactive)
288 (mml-secure-part "smime"))
289
290 (defun mml-secure-is-encrypted-p ()
291 "Check whether secure encrypt tag is present."
292 (save-excursion
293 (goto-char (point-min))
294 (re-search-forward
295 (concat "^" (regexp-quote mail-header-separator) "\n"
296 "<#secure[^>]+encrypt")
297 nil t)))
298
299 (defun mml-secure-bcc-is-safe ()
300 "Check whether usage of Bcc is safe (or absent).
301 Bcc usage is safe in two cases: first, if the current message does
302 not contain an MML secure encrypt tag;
303 second, if the Bcc addresses are a subset of `mml-secure-safe-bcc-list'.
304 In all other cases, ask the user whether Bcc usage is safe.
305 Raise error if user answers no.
306 Note that this function does not produce a meaningful return value:
307 either an error is raised or not."
308 (when (mml-secure-is-encrypted-p)
309 (let ((bcc (mail-strip-quoted-names (message-fetch-field "bcc"))))
310 (when bcc
311 ;; Split recipients at "," boundary, omit empty strings (t),
312 ;; and strip whitespace.
313 (let ((bcc-list (split-string hdr "," t "\\s-+")))
314 (unless (gnus-subsetp bcc-list mml-secure-safe-bcc-list)
315 (unless (yes-or-no-p "Message for encryption contains Bcc header.\
316 This may give away all Bcc'ed identities to all recipients.\
317 Are you sure that this is safe?\
318 (Customize `mml-secure-safe-bcc-list' to avoid this warning.) ")
319 (error "Aborted"))))))))
320
321 ;; defuns that add the proper <#secure ...> tag to the top of the message body
322 (defun mml-secure-message (method &optional modesym)
323 (let ((mode (prin1-to-string modesym))
324 (tags (append
325 (if (or (eq modesym 'sign)
326 (eq modesym 'signencrypt))
327 (funcall (nth 2 (assoc method mml-sign-alist))))
328 (if (or (eq modesym 'encrypt)
329 (eq modesym 'signencrypt))
330 (funcall (nth 2 (assoc method mml-encrypt-alist))))))
331 insert-loc)
332 (mml-unsecure-message)
333 (save-excursion
334 (goto-char (point-min))
335 (cond ((re-search-forward
336 (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
337 (goto-char (setq insert-loc (match-end 0)))
338 (unless (looking-at "<#secure")
339 (apply 'mml-insert-tag
340 'secure 'method method 'mode mode tags)))
341 (t (error
342 "The message is corrupted. No mail header separator"))))
343 (when (eql insert-loc (point))
344 (forward-line 1))))
345
346 (defun mml-unsecure-message ()
347 "Remove security related MML tags from message."
348 (interactive)
349 (save-excursion
350 (goto-char (point-max))
351 (when (re-search-backward "^<#secure.*>\n" nil t)
352 (delete-region (match-beginning 0) (match-end 0)))))
353
354
355 (defun mml-secure-message-sign (&optional method)
356 "Add MML tags to sign the entire message.
357 Use METHOD if given. Else use `mml-secure-method' or
358 `mml-default-sign-method'."
359 (interactive)
360 (mml-secure-message
361 (or method mml-secure-method mml-default-sign-method)
362 'sign))
363
364 (defun mml-secure-message-sign-encrypt (&optional method)
365 "Add MML tag to sign and encrypt the entire message.
366 Use METHOD if given. Else use `mml-secure-method' or
367 `mml-default-sign-method'."
368 (interactive)
369 (mml-secure-message
370 (or method mml-secure-method mml-default-sign-method)
371 'signencrypt))
372
373 (defun mml-secure-message-encrypt (&optional method)
374 "Add MML tag to encrypt the entire message.
375 Use METHOD if given. Else use `mml-secure-method' or
376 `mml-default-sign-method'."
377 (interactive)
378 (mml-secure-message
379 (or method mml-secure-method mml-default-sign-method)
380 'encrypt))
381
382 (defun mml-secure-message-sign-smime ()
383 "Add MML tag to encrypt/sign the entire message."
384 (interactive)
385 (mml-secure-message "smime" 'sign))
386
387 (defun mml-secure-message-sign-pgp ()
388 "Add MML tag to encrypt/sign the entire message."
389 (interactive)
390 (mml-secure-message "pgp" 'sign))
391
392 (defun mml-secure-message-sign-pgpmime ()
393 "Add MML tag to encrypt/sign the entire message."
394 (interactive)
395 (mml-secure-message "pgpmime" 'sign))
396
397 (defun mml-secure-message-sign-pgpauto ()
398 "Add MML tag to encrypt/sign the entire message."
399 (interactive)
400 (mml-secure-message "pgpauto" 'sign))
401
402 (defun mml-secure-message-encrypt-smime (&optional dontsign)
403 "Add MML tag to encrypt and sign the entire message.
404 If called with a prefix argument, only encrypt (do NOT sign)."
405 (interactive "P")
406 (mml-secure-message "smime" (if dontsign 'encrypt 'signencrypt)))
407
408 (defun mml-secure-message-encrypt-pgp (&optional dontsign)
409 "Add MML tag to encrypt and sign the entire message.
410 If called with a prefix argument, only encrypt (do NOT sign)."
411 (interactive "P")
412 (mml-secure-message "pgp" (if dontsign 'encrypt 'signencrypt)))
413
414 (defun mml-secure-message-encrypt-pgpmime (&optional dontsign)
415 "Add MML tag to encrypt and sign the entire message.
416 If called with a prefix argument, only encrypt (do NOT sign)."
417 (interactive "P")
418 (mml-secure-message "pgpmime" (if dontsign 'encrypt 'signencrypt)))
419
420 (defun mml-secure-message-encrypt-pgpauto (&optional dontsign)
421 "Add MML tag to encrypt and sign the entire message.
422 If called with a prefix argument, only encrypt (do NOT sign)."
423 (interactive "P")
424 (mml-secure-message "pgpauto" (if dontsign 'encrypt 'signencrypt)))
425
426 (provide 'mml-sec)
427
428 ;;; mml-sec.el ends here