Commit 6536112b authored by Michael Albinus's avatar Michael Albinus
Browse files

Handle sensitive auto-save or backup remote files (Bug#45245)

* doc/misc/tramp.texi (Auto-save and Backup):
Describe tramp-allow-unsafe-temporary-files.
(Ad-hoc multi-hops): Use proper format.

* etc/NEWS: Mention confirmation for writing sensitive auto-save
or backup remote files to the local temporary directory..

* lisp/net/tramp-cache.el (tramp-dump-connection-properties):
Strengthen test.

* lisp/net/tramp.el (tramp-allow-unsafe-temporary-files): New defcustom.
(tramp-handle-find-backup-file-name)
(tramp-handle-make-auto-save-file-name): Don't expose sensible
auto-save or backup files on local temporary directory.  (Bug#45245)

* test/lisp/net/tramp-tests.el (tramp--test-always): New defalias.
(tramp-test10-write-region, tramp-test21-file-links)
(tramp--test--deftest-direct-async-process): Use it.
(tramp-test37-make-auto-save-file-name)
(tramp-test38-find-backup-file-name): Extend tests.
parent d0c7d8bc
Pipeline #11083 failed with stages
in 184 minutes and 28 seconds
......@@ -2842,6 +2842,13 @@ auto-saved files to the same directory as the original file.
Alternatively, set the user option @code{tramp-auto-save-directory}
to direct all auto saves to that location.
@vindex tramp-allow-unsafe-temporary-files
Per default, @value{tramp} asks for confirmation if a
@samp{root}-owned backup or auto-save remote file has to be written to
your local temporary directory. If you want to suppress this
confirmation question, set user option
@code{tramp-allow-unsafe-temporary-files} to @code{t}.
@node Keeping files encrypted
@section Protect remote files by encryption
......@@ -3309,8 +3316,8 @@ For ad-hoc definitions to be saved automatically in
Ad-hoc proxies can take patterns @code{%h} or @code{%u} like in
@code{tramp-default-proxies-alist}. The following file name expands
to user @code{root} on host @code{remotehost}, starting with an
@option{ssh} session on host @code{remotehost}:
to user @samp{root} on host @samp{remotehost}, starting with an
@option{ssh} session on host @samp{remotehost}:
@samp{@value{prefix}ssh@value{postfixhop}%h|su@value{postfixhop}remotehost@value{postfix}}.
On the other hand, if a trailing hop does not specify a host name,
......@@ -5294,7 +5301,7 @@ attributes cache in its process sentinel with this code:
@end lisp
Since @value{tramp} traverses subdirectories starting with the
root-directory, it is most likely sufficient to make the
root directory, it is most likely sufficient to make the
@code{default-directory} of the process buffer as the root directory.
......
......@@ -605,7 +605,7 @@ These options include 'windmove-default-keybindings',
** Windows
+++
*** New option 'delete-window-choose-selected'.
*** New user option 'delete-window-choose-selected'.
This allows to choose a frame's selected window after deleting the
previously selected one.
......@@ -1403,6 +1403,11 @@ When non-nil, this user option instructs Tramp to mirror the debug
buffer to a file under the "/tmp/" directory. This is useful, if (in
rare cases) Tramp blocks Emacs, and we need further debug information.
+++
*** Writing sensitive auto-save or backup files to the local temporary
directory must be confirmed. In order to suppress this confirmation,
set user option 'tramp-allow-unsafe-temporary-files' to t.
** Tempo
---
......
......@@ -70,7 +70,8 @@
;; process key retrieved by `tramp-get-process' (the main connection
;; process). Other processes could reuse these properties, avoiding
;; recomputation when a new asynchronous process is created by
;; `make-process'. Examples are "remote-path" or "device" (tramp-adb.el).
;; `make-process'. Examples are "remote-path",
;; "unsafe-temporary-file" or "device" (tramp-adb.el).
;;; Code:
......@@ -470,11 +471,11 @@ used to cache connection properties of the local machine."
;; don't save either, because all other properties might
;; depend on the login name, and we want to give the
;; possibility to use another login name later on. Key
;; "started" exists for the "ftp" method only, which must be
;; "started" exists for the "ftp" method only, which must not
;; be kept persistent.
(maphash
(lambda (key value)
(if (and (tramp-file-name-p key) value
(if (and (tramp-file-name-p key) (hash-table-p value)
(not (string-equal
(tramp-file-name-method key) tramp-archive-method))
(not (tramp-file-name-localname key))
......
......@@ -5296,8 +5296,7 @@ Nonexistent directories are removed from spec."
;; cache the result for the session only. Otherwise, the
;; result is cached persistently.
(if (memq 'tramp-own-remote-path tramp-remote-path)
(tramp-get-process vec)
vec)
(tramp-get-process vec) vec)
"remote-path"
(let* ((remote-path (copy-tree tramp-remote-path))
(elt1 (memq 'tramp-default-remote-path remote-path))
......
......@@ -3627,6 +3627,11 @@ User is always nil."
(and (file-directory-p (file-name-directory filename))
(file-writable-p (file-name-directory filename)))))))
(defcustom tramp-allow-unsafe-temporary-files nil
"Whether root-owned auto-save or backup files can be written to \"/tmp\"."
:version "28.1"
:type 'boolean)
(defun tramp-handle-find-backup-file-name (filename)
"Like `find-backup-file-name' for Tramp files."
(with-parsed-tramp-file-name filename nil
......@@ -3642,8 +3647,25 @@ User is always nil."
(tramp-make-tramp-file-name v (cdr x))
(cdr x))))
tramp-backup-directory-alist)
backup-directory-alist)))
(tramp-run-real-handler #'find-backup-file-name (list filename)))))
backup-directory-alist))
(uid (tramp-compat-file-attribute-user-id
(file-attributes filename 'integer)))
result)
(prog1 ;; Run plain `find-backup-file-name'.
(setq result
(tramp-run-real-handler
#'find-backup-file-name (list filename)))
;; Protect against security hole.
(when (and (natnump uid) (zerop uid)
(file-in-directory-p (car result) temporary-file-directory)
(not tramp-allow-unsafe-temporary-files)
(not (with-tramp-connection-property
(tramp-get-process v) "unsafe-temporary-file"
(yes-or-no-p
(concat
"Backup file on local temporary directory, "
"do you want to continue? ")))))
(tramp-error v 'file-error "Unsafe backup file name"))))))
(defun tramp-handle-insert-directory
(filename switches &optional wildcard full-directory-p)
......@@ -5225,6 +5247,7 @@ Return the local name of the temporary file."
"Like `make-auto-save-file-name' for Tramp files.
Returns a file name in `tramp-auto-save-directory' for autosaving
this file, if that variable is non-nil."
(with-parsed-tramp-file-name buffer-file-name nil
(when (stringp tramp-auto-save-directory)
(setq tramp-auto-save-directory
(expand-file-name tramp-auto-save-directory)))
......@@ -5241,6 +5264,8 @@ this file, if that variable is non-nil."
(auto-save-file-name-transforms
(if (null tramp-auto-save-directory)
auto-save-file-name-transforms))
(uid (tramp-compat-file-attribute-user-id
(file-attributes buffer-file-name 'integer)))
(buffer-file-name
(if (null tramp-auto-save-directory)
buffer-file-name
......@@ -5253,9 +5278,21 @@ this file, if that variable is non-nil."
("[" . "_l")
("]" . "_r"))
(tramp-compat-file-name-unquote (buffer-file-name)))
tramp-auto-save-directory))))
;; Run plain `make-auto-save-file-name'.
(tramp-run-real-handler #'make-auto-save-file-name nil)))
tramp-auto-save-directory)))
result)
(prog1 ;; Run plain `make-auto-save-file-name'.
(setq result (tramp-run-real-handler #'make-auto-save-file-name nil))
;; Protect against security hole.
(when (and (natnump uid) (zerop uid)
(file-in-directory-p result temporary-file-directory)
(not tramp-allow-unsafe-temporary-files)
(not (with-tramp-connection-property
(tramp-get-process v) "unsafe-temporary-file"
(yes-or-no-p
(concat
"Autosave file on local temporary directory, "
"do you want to continue? ")))))
(tramp-error v 'file-error "Unsafe autosave file name"))))))
(defun tramp-subst-strs-in-string (alist string)
"Replace all occurrences of the string FROM with TO in STRING.
......
......@@ -229,6 +229,16 @@ is greater than 10.
"%s %f sec"
,message (float-time (time-subtract (current-time) start))))))
;; `always' is introduced with Emacs 28.1.
(defalias 'tramp--test-always
(if (fboundp 'always)
#'always
(lambda (&rest _arguments)
"Do nothing and return t.
This function accepts any number of ARGUMENTS, but ignores them.
Also see `ignore'."
t)))
(ert-deftest tramp-test00-availability ()
"Test availability of Tramp functions."
:expected-result (if (tramp--test-enabled) :passed :failed)
......@@ -2454,9 +2464,9 @@ This checks also `file-name-as-directory', `file-name-directory',
tramp--test-messages))))))))
;; Do not overwrite if excluded.
(cl-letf (((symbol-function #'y-or-n-p) (lambda (_prompt) t))
(cl-letf (((symbol-function #'y-or-n-p) #'tramp--test-always)
;; Ange-FTP.
((symbol-function 'yes-or-no-p) (lambda (_prompt) t)))
((symbol-function 'yes-or-no-p) #'tramp--test-always))
(write-region "foo" nil tmp-name nil nil nil 'mustbenew))
;; `mustbenew' is passed to Tramp since Emacs 26.1.
(when (tramp--test-emacs26-p)
......@@ -3671,7 +3681,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'."
(should-error
(make-symbolic-link tmp-name1 tmp-name2 0)
:type 'file-already-exists)))
(cl-letf (((symbol-function #'yes-or-no-p) (lambda (_prompt) t)))
(cl-letf (((symbol-function #'yes-or-no-p) #'tramp--test-always))
(make-symbolic-link tmp-name1 tmp-name2 0)
(should
(string-equal
......@@ -3747,7 +3757,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'."
(should-error
(add-name-to-file tmp-name1 tmp-name2 0)
:type 'file-already-exists))
(cl-letf (((symbol-function #'yes-or-no-p) (lambda (_prompt) t)))
(cl-letf (((symbol-function #'yes-or-no-p) #'tramp--test-always))
(add-name-to-file tmp-name1 tmp-name2 0)
(should (file-regular-p tmp-name2)))
(add-name-to-file tmp-name1 tmp-name2 'ok-if-already-exists)
......@@ -4545,7 +4555,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'."
If UNSTABLE is non-nil, the test is tagged as `:unstable'."
(declare (indent 1))
;; `make-process' supports file name handlers since Emacs 27.
(when (let ((file-name-handler-alist '(("" . (lambda (&rest _) t)))))
(when (let ((file-name-handler-alist '(("" . #'tramp--test-always))))
(ignore-errors (make-process :file-handler t)))
`(ert-deftest ,(intern (concat (symbol-name test) "-direct-async")) ()
,docstring
......@@ -4561,7 +4571,7 @@ If UNSTABLE is non-nil, the test is tagged as `:unstable'."
;; `file-truename' does it by side-effect. Suppress
;; `tramp--test-enabled', in order to keep the connection.
;; Suppress "Process ... finished" messages.
(cl-letf (((symbol-function #'tramp--test-enabled) (lambda nil t))
(cl-letf (((symbol-function #'tramp--test-enabled) #'tramp--test-always)
((symbol-function #'internal-default-process-sentinel)
#'ignore))
(file-truename tramp-test-temporary-file-directory)
......@@ -5554,11 +5564,38 @@ Use direct async.")
("]" . "_r"))
(tramp-compat-file-name-unquote tmp-name1)))
tmp-name2)))
(should (file-directory-p tmp-name2))))))
(should (file-directory-p tmp-name2)))))
;; Create temporary file. This shall check for sensible
;; files, owned by root.
(let ((tramp-auto-save-directory temporary-file-directory)
tramp-allow-unsafe-temporary-files)
(write-region "foo" nil tmp-name1)
(when (zerop (or (tramp-compat-file-attribute-user-id
(file-attributes tmp-name1))
tramp-unknown-id-integer))
(with-temp-buffer
(setq buffer-file-name tmp-name1)
(tramp-cleanup-connection
tramp-test-vec 'keep-debug 'keep-password)
(let ((tramp-allow-unsafe-temporary-files t))
(should (stringp (make-auto-save-file-name))))
(tramp-cleanup-connection
tramp-test-vec 'keep-debug 'keep-password)
(cl-letf (((symbol-function #'yes-or-no-p) #'ignore))
(should-error
(make-auto-save-file-name)
:type 'file-error))
(tramp-cleanup-connection
tramp-test-vec 'keep-debug 'keep-password)
(cl-letf (((symbol-function #'yes-or-no-p)
#'tramp--test-always))
(should (stringp (make-auto-save-file-name))))))))
;; Cleanup.
(ignore-errors (delete-file tmp-name1))
(ignore-errors (delete-directory tmp-name2 'recursive))))))
(ignore-errors (delete-directory tmp-name2 'recursive))
(tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)))))
(ert-deftest tramp-test38-find-backup-file-name ()
"Check `find-backup-file-name'."
......@@ -5672,7 +5709,37 @@ Use direct async.")
(should (file-directory-p tmp-name2))))
;; Cleanup.
(ignore-errors (delete-directory tmp-name2 'recursive))))))
(ignore-errors (delete-directory tmp-name2 'recursive)))
(unwind-protect
;; Create temporary file. This shall check for sensible
;; files, owned by root.
(let ((backup-directory-alist `(("." . ,temporary-file-directory)))
tramp-allow-unsafe-temporary-files
tramp-backup-directory-alist)
(write-region "foo" nil tmp-name1)
(when (zerop (or (tramp-compat-file-attribute-user-id
(file-attributes tmp-name1))
tramp-unknown-id-integer))
(tramp-cleanup-connection
tramp-test-vec 'keep-debug 'keep-password)
(let ((tramp-allow-unsafe-temporary-files t))
(should (stringp (car (find-backup-file-name tmp-name1)))))
(tramp-cleanup-connection
tramp-test-vec 'keep-debug 'keep-password)
(cl-letf (((symbol-function #'yes-or-no-p) #'ignore))
(should-error
(find-backup-file-name tmp-name1)
:type 'file-error))
(tramp-cleanup-connection
tramp-test-vec 'keep-debug 'keep-password)
(cl-letf (((symbol-function #'yes-or-no-p)
#'tramp--test-always))
(should (stringp (car (find-backup-file-name tmp-name1)))))))
;; Cleanup.
(ignore-errors (delete-file tmp-name1))
(tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)))))
;; The functions were introduced in Emacs 26.1.
(ert-deftest tramp-test39-make-nearby-temp-file ()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment