epa-file.el 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. ;;; epa-file.el --- the EasyPG Assistant, transparent file encryption -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2006-2017 Free Software Foundation, Inc.
  3. ;; Author: Daiki Ueno <ueno@unixuser.org>
  4. ;; Keywords: PGP, GnuPG
  5. ;; Package: epa
  6. ;; This file is part of GNU Emacs.
  7. ;; GNU Emacs is free software: you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; GNU Emacs is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Code:
  18. (require 'epa)
  19. (require 'epa-hook)
  20. (defcustom epa-file-cache-passphrase-for-symmetric-encryption nil
  21. "If non-nil, cache passphrase for symmetric encryption.
  22. For security reasons, this option is turned off by default and
  23. not recommended to use. Instead, consider using gpg-agent which
  24. does the same job in a safer way. See Info node `(epa) Caching
  25. Passphrases' for more information.
  26. Note that this option has no effect if you use GnuPG 2.0."
  27. :type 'boolean
  28. :group 'epa-file)
  29. (defcustom epa-file-select-keys nil
  30. "Control whether or not to pop up the key selection dialog.
  31. If t, always asks user to select recipients.
  32. If nil, query user only when `epa-file-encrypt-to' is not set.
  33. If neither t nor nil, doesn't ask user. In this case, symmetric
  34. encryption is used."
  35. :type '(choice (const :tag "Ask always" t)
  36. (const :tag "Ask when recipients are not set" nil)
  37. (const :tag "Don't ask" silent))
  38. :group 'epa-file)
  39. (defvar epa-file-passphrase-alist nil)
  40. (eval-and-compile
  41. (if (fboundp 'encode-coding-string)
  42. (defalias 'epa-file--encode-coding-string 'encode-coding-string)
  43. (defalias 'epa-file--encode-coding-string 'identity)))
  44. (eval-and-compile
  45. (if (fboundp 'decode-coding-string)
  46. (defalias 'epa-file--decode-coding-string 'decode-coding-string)
  47. (defalias 'epa-file--decode-coding-string 'identity)))
  48. (defun epa-file-passphrase-callback-function (context key-id file)
  49. (if (and epa-file-cache-passphrase-for-symmetric-encryption
  50. (eq key-id 'SYM))
  51. (progn
  52. (setq file (file-truename file))
  53. (let ((entry (assoc file epa-file-passphrase-alist))
  54. passphrase)
  55. (or (copy-sequence (cdr entry))
  56. (progn
  57. (unless entry
  58. (setq entry (list file)
  59. epa-file-passphrase-alist
  60. (cons entry
  61. epa-file-passphrase-alist)))
  62. (setq passphrase (epa-passphrase-callback-function context
  63. key-id
  64. file))
  65. (setcdr entry (copy-sequence passphrase))
  66. passphrase))))
  67. (epa-passphrase-callback-function context key-id file)))
  68. (defvar epa-inhibit nil
  69. "Non-nil means don't try to decrypt .gpg files when operating on them.")
  70. ;;;###autoload
  71. (defun epa-file-handler (operation &rest args)
  72. (save-match-data
  73. (let ((op (get operation 'epa-file)))
  74. (if (and op (not epa-inhibit))
  75. (apply op args)
  76. (epa-file-run-real-handler operation args)))))
  77. (defun epa-file-run-real-handler (operation args)
  78. (let ((inhibit-file-name-handlers
  79. (cons 'epa-file-handler
  80. (and (eq inhibit-file-name-operation operation)
  81. inhibit-file-name-handlers)))
  82. (inhibit-file-name-operation operation))
  83. (apply operation args)))
  84. (defun epa-file-decode-and-insert (string file visit beg end replace)
  85. (if (fboundp 'decode-coding-inserted-region)
  86. (save-restriction
  87. (narrow-to-region (point) (point))
  88. (insert (if enable-multibyte-characters
  89. (string-to-multibyte string)
  90. string))
  91. (decode-coding-inserted-region
  92. (point-min) (point-max)
  93. (substring file 0 (string-match epa-file-name-regexp file))
  94. visit beg end replace))
  95. (insert (epa-file--decode-coding-string string (or coding-system-for-read
  96. 'undecided)))))
  97. (defvar epa-file-error nil)
  98. (defun epa-file--find-file-not-found-function ()
  99. (let ((error epa-file-error))
  100. (save-window-excursion
  101. (kill-buffer))
  102. (signal 'file-missing
  103. (cons "Opening input file" (cdr error)))))
  104. (defvar last-coding-system-used)
  105. (defun epa-file-insert-file-contents (file &optional visit beg end replace)
  106. (barf-if-buffer-read-only)
  107. (if (and visit (or beg end))
  108. (error "Attempt to visit less than an entire file"))
  109. (setq file (expand-file-name file))
  110. (let* ((local-copy
  111. (condition-case nil
  112. (epa-file-run-real-handler #'file-local-copy (list file))
  113. (error)))
  114. (local-file (or local-copy file))
  115. (context (epg-make-context))
  116. (buf (current-buffer))
  117. string length entry)
  118. (if visit
  119. (setq buffer-file-name file))
  120. (epg-context-set-passphrase-callback
  121. context
  122. (cons #'epa-file-passphrase-callback-function
  123. local-file))
  124. (epg-context-set-progress-callback
  125. context
  126. (cons #'epa-progress-callback-function
  127. (format "Decrypting %s" file)))
  128. (setf (epg-context-pinentry-mode context) epa-pinentry-mode)
  129. (unwind-protect
  130. (progn
  131. (if replace
  132. (goto-char (point-min)))
  133. (condition-case error
  134. (setq string (epg-decrypt-file context local-file nil))
  135. (error
  136. (if (setq entry (assoc file epa-file-passphrase-alist))
  137. (setcdr entry nil))
  138. ;; If the decryption program can't be found,
  139. ;; signal that as a non-file error
  140. ;; so that find-file-noselect-1 won't handle it.
  141. ;; Borrowed from jka-compr.el.
  142. (if (and (memq 'file-error (get (car error) 'error-conditions))
  143. (equal (cadr error) "Searching for program"))
  144. (error "Decryption program `%s' not found"
  145. (nth 3 error)))
  146. (let ((exists (file-exists-p local-file)))
  147. (when exists
  148. ;; Hack to prevent find-file from opening empty buffer
  149. ;; when decryption failed (bug#6568). See the place
  150. ;; where `find-file-not-found-functions' are called in
  151. ;; `find-file-noselect-1'.
  152. (setq-local epa-file-error error)
  153. (add-hook 'find-file-not-found-functions
  154. 'epa-file--find-file-not-found-function
  155. nil t)
  156. (epa-display-error context))
  157. (signal (if exists 'file-error 'file-missing)
  158. (cons "Opening input file" (cdr error))))))
  159. (set-buffer buf) ;In case timer/filter changed/killed it (bug#16029)!
  160. (setq-local epa-file-encrypt-to
  161. (mapcar #'car (epg-context-result-for
  162. context 'encrypted-to)))
  163. (if (or beg end)
  164. (setq string (substring string (or beg 0) end)))
  165. (save-excursion
  166. ;; If visiting, bind off buffer-file-name so that
  167. ;; file-locking will not ask whether we should
  168. ;; really edit the buffer.
  169. (let ((buffer-file-name
  170. (if visit nil buffer-file-name)))
  171. (save-restriction
  172. (narrow-to-region (point) (point))
  173. (epa-file-decode-and-insert string file visit beg end replace)
  174. (setq length (- (point-max) (point-min))))
  175. (if replace
  176. (delete-region (point) (point-max))))
  177. (if visit
  178. (set-visited-file-modtime))))
  179. (if (and local-copy
  180. (file-exists-p local-copy))
  181. (delete-file local-copy)))
  182. (list file length)))
  183. (put 'insert-file-contents 'epa-file 'epa-file-insert-file-contents)
  184. (defun epa-file-write-region (start end file &optional append visit lockname
  185. mustbenew)
  186. (if append
  187. (error "Can't append to the file"))
  188. (setq file (expand-file-name file))
  189. (let* ((coding-system (or coding-system-for-write
  190. (if (fboundp 'select-safe-coding-system)
  191. ;; This is needed since Emacs 22 has
  192. ;; no-conversion setting for *.gpg in
  193. ;; `auto-coding-alist'.
  194. (let ((buffer-file-name
  195. (file-name-sans-extension file)))
  196. (select-safe-coding-system
  197. (point-min) (point-max)))
  198. buffer-file-coding-system)))
  199. (context (epg-make-context))
  200. (coding-system-for-write 'binary)
  201. string entry
  202. (recipients
  203. (cond
  204. ((listp epa-file-encrypt-to) epa-file-encrypt-to)
  205. ((stringp epa-file-encrypt-to) (list epa-file-encrypt-to))))
  206. buffer)
  207. (epg-context-set-passphrase-callback
  208. context
  209. (cons #'epa-file-passphrase-callback-function
  210. file))
  211. (epg-context-set-progress-callback
  212. context
  213. (cons #'epa-progress-callback-function
  214. (format "Encrypting %s" file)))
  215. (setf (epg-context-armor context) epa-armor)
  216. (setf (epg-context-pinentry-mode context) epa-pinentry-mode)
  217. (condition-case error
  218. (setq string
  219. (epg-encrypt-string
  220. context
  221. (if (stringp start)
  222. (epa-file--encode-coding-string start coding-system)
  223. (unless start
  224. (setq start (point-min)
  225. end (point-max)))
  226. (setq buffer (current-buffer))
  227. (with-temp-buffer
  228. (insert-buffer-substring buffer start end)
  229. ;; Translate the region according to
  230. ;; `buffer-file-format', as `write-region' would.
  231. ;; We can't simply do `write-region' (into a
  232. ;; temporary file) here, since it writes out
  233. ;; decrypted contents.
  234. (format-encode-buffer (with-current-buffer buffer
  235. buffer-file-format))
  236. (epa-file--encode-coding-string (buffer-string)
  237. coding-system)))
  238. (if (or (eq epa-file-select-keys t)
  239. (and (null epa-file-select-keys)
  240. (not (local-variable-p 'epa-file-encrypt-to
  241. (current-buffer)))))
  242. (epa-select-keys
  243. context
  244. "Select recipients for encryption.
  245. If no one is selected, symmetric encryption will be performed. "
  246. recipients)
  247. (if epa-file-encrypt-to
  248. (epg-list-keys context recipients)))))
  249. (error
  250. (epa-display-error context)
  251. (if (setq entry (assoc file epa-file-passphrase-alist))
  252. (setcdr entry nil))
  253. (signal 'file-error (cons "Opening output file" (cdr error)))))
  254. (epa-file-run-real-handler
  255. #'write-region
  256. (list string nil file append visit lockname mustbenew))
  257. (if (boundp 'last-coding-system-used)
  258. (setq last-coding-system-used coding-system))
  259. (if (eq visit t)
  260. (progn
  261. (setq buffer-file-name file)
  262. (set-visited-file-modtime))
  263. (if (stringp visit)
  264. (progn
  265. (set-visited-file-modtime)
  266. (setq buffer-file-name visit))))
  267. (if (or (eq visit t)
  268. (eq visit nil)
  269. (stringp visit))
  270. (message "Wrote %s" buffer-file-name))))
  271. (put 'write-region 'epa-file 'epa-file-write-region)
  272. (defun epa-file-select-keys ()
  273. "Select recipients for encryption."
  274. (interactive)
  275. (setq-local epa-file-encrypt-to
  276. (mapcar
  277. (lambda (key)
  278. (epg-sub-key-id (car (epg-key-sub-key-list key))))
  279. (epa-select-keys
  280. (epg-make-context)
  281. "Select recipients for encryption.
  282. If no one is selected, symmetric encryption will be performed. "))))
  283. ;;;###autoload
  284. (defun epa-file-enable ()
  285. (interactive)
  286. (if (memq epa-file-handler file-name-handler-alist)
  287. (message "`epa-file' already enabled")
  288. (setq file-name-handler-alist
  289. (cons epa-file-handler file-name-handler-alist))
  290. (add-hook 'find-file-hook 'epa-file-find-file-hook)
  291. (setq auto-mode-alist (cons epa-file-auto-mode-alist-entry auto-mode-alist))
  292. (message "`epa-file' enabled")))
  293. ;;;###autoload
  294. (defun epa-file-disable ()
  295. (interactive)
  296. (if (memq epa-file-handler file-name-handler-alist)
  297. (progn
  298. (setq file-name-handler-alist
  299. (delq epa-file-handler file-name-handler-alist))
  300. (remove-hook 'find-file-hook 'epa-file-find-file-hook)
  301. (setq auto-mode-alist (delq epa-file-auto-mode-alist-entry
  302. auto-mode-alist))
  303. (message "`epa-file' disabled"))
  304. (message "`epa-file' already disabled")))
  305. (provide 'epa-file)
  306. ;;; epa-file.el ends here