Commit e8199e76 authored by Michael Albinus's avatar Michael Albinus

Add Tramp sudoedit method

* doc/misc/tramp.texi (Quick Start Guide): New section "Using sudoedit".
(External methods) <sudoedit>: Describe.

* lisp/net/tramp-adb.el (tramp-adb-file-name-handler-alist):
* lisp/net/tramp-gvfs.el (tramp-gvfs-file-name-handler-alist):
* lisp/net/tramp-rclone.el (tramp-rclone-file-name-handler-alist):
* lisp/net/tramp-sh.el (tramp-sh-file-name-handler-alist)
* lisp/net/tramp-smb.el (tramp-smb-file-name-handler-alist):
Add handler.

* lisp/net/tramp-sh.el (tramp-sh-handle-set-file-uid-gid): Rename from
`tramp-sh-handle-set-file-uid-gid'.  Handle only remote file names.

* lisp/net/tramp-sudoedit.el: New file.

* lisp/net/tramp.el (tramp-file-name-for-operation): Handle also
`tramp-set-file-uid-gid'.
(tramp-set-file-uid-gid): New defun.
(tramp-get-local-uid, tramp-get-local-gid): Cache result.

* test/lisp/net/tramp-tests.el (tramp--test-sudoedit-p): New defun.
(tramp-test20-file-modes, tramp-test22-file-times)
(tramp--test-sudoedit-p): Use it.
parent 4f230e8d
Pipeline #295 failed with stage
in 2 seconds
......@@ -468,6 +468,19 @@ The method @option{sg} stands for ``switch group''; the changed group
must be used here as user name. The default host name is the same.
@anchor{Quick Start Guide: @option{sudoedit} method}
@section Using @command{sudoedit}
@cindex method @option{sudoedit}
@cindex @option{sudoedit} method
The @option{sudoedit} method is similar to the @option{sudo} method.
However, it is a different implementation: it does not keep an open
session running in the background. This is for security reasons; on
the backside this method is less performant than the @option{sudo}
method, it is restricted to the @samp{localhost} only, and it does not
support external processes.
@anchor{Quick Start Guide: @option{smb} method}
@section Using @command{smbclient}
@cindex method @option{smb}
......@@ -919,6 +932,30 @@ NAS hosts. These dumb devices have severely restricted local shells,
such as the @command{busybox} and do not host any other encode or
decode programs.
@item @option{sudoedit}
@cindex method @option{sudoedit}
@cindex @option{sudoedit} method
The @option{sudoedit} method allows to edit a file as a different user
on the local host. You could regard this as @value{tramp}'s
implementation of the @command{sudoedit}. Contrary to the
@option{sudo} method, all magic file name functions are implemented by
single @command{sudo @dots{}} commands. The purpose is to make
editing such a file as secure as possible; there must be no session
running in the Emacs background which could be attacked from inside
Emacs.
Consequently, external processes are not implemented.
The host name of such remote file names must represent the local host.
Since the default value is already proper, it is recommended not to
use any host name in the remote file name, like
@file{@trampfn{sudoedit,,/path/to/file}} or
@file{@trampfn{sudoedit,user@@,/path/to/file}}.
Like the @option{sudo} method, a @option{sudoedit} password expires
after a predefined timeout.
@item @option{ftp}
@cindex method @option{ftp}
@cindex @option{ftp} method
......
......@@ -864,6 +864,12 @@ or NextCloud hosted files and directories.
*** New connection method "rclone", which allows to access system
storages via the 'rclone' program. This feature is experimental.
+++
*** New connection method "sudoedit", which allows to edit local files
with different user credentials. Contrary to the "sudo" method, no
session is run permanently in the background. This is for security
reasons.
+++
*** Connection methods "obex" and "synce" are removed, because they
are obsoleted in GVFS.
......
......@@ -161,6 +161,7 @@ It is used for TCP/IP devices."
(start-file-process . tramp-adb-handle-start-file-process)
(substitute-in-file-name . tramp-handle-substitute-in-file-name)
(temporary-file-directory . tramp-handle-temporary-file-directory)
(tramp-set-file-uid-gid . ignore)
(unhandled-file-name-directory . ignore)
(vc-registered . ignore)
(verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
......
......@@ -273,6 +273,7 @@ It must be supported by libarchive(3).")
(start-file-process . tramp-archive-handle-not-implemented)
;; `substitute-in-file-name' performed by default handler.
(temporary-file-directory . tramp-archive-handle-temporary-file-directory)
;; `tramp-set-file-uid-gid' performed by default handler.
(unhandled-file-name-directory . ignore)
(vc-registered . ignore)
(verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
......
......@@ -50,10 +50,11 @@
;; definitions already sent to the remote shell, "last-cmd-time" is
;; the time stamp a command has been sent to the remote process.
;;
;; - The key is `nil'. This are temporary properties related to the
;; - The key is nil. This are temporary properties related to the
;; local machine. Examples: "parse-passwd" and "parse-group" keep
;; the results of parsing "/etc/passwd" and "/etc/group", "locale"
;; is the used shell locale.
;; the results of parsing "/etc/passwd" and "/etc/group",
;; "{uid,gid}-{integer,string}" are the local uid and gid, and
;; "locale" is the used shell locale.
;; Some properties are handled special:
;;
......
......@@ -589,6 +589,7 @@ It has been changed in GVFS 1.14.")
(start-file-process . ignore)
(substitute-in-file-name . tramp-handle-substitute-in-file-name)
(temporary-file-directory . tramp-handle-temporary-file-directory)
(tramp-set-file-uid-gid . ignore)
(unhandled-file-name-directory . ignore)
(vc-registered . ignore)
(verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
......@@ -1843,7 +1844,7 @@ connection if a previous connection has died for some reason."
(tramp-get-connection-process vec) "connected" t))))
;; In `tramp-check-cached-permissions', the connection properties
;; {uig,gid}-{integer,string} are used. We set them to proper values.
;; "{uid,gid}-{integer,string}" are used. We set them to proper values.
(unless tramp-gvfs-get-remote-uid-gid-in-progress
(let ((tramp-gvfs-get-remote-uid-gid-in-progress t))
(tramp-gvfs-get-remote-uid vec 'integer)
......
......@@ -134,6 +134,7 @@
(start-file-process . ignore)
(substitute-in-file-name . tramp-handle-substitute-in-file-name)
(temporary-file-directory . tramp-handle-temporary-file-directory)
(tramp-set-file-uid-gid . ignore)
(unhandled-file-name-directory . ignore)
(vc-registered . ignore)
(verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
......@@ -575,7 +576,7 @@ connection if a previous connection has died for some reason."
(tramp-cleanup-connection vec 'keep-debug 'keep-password)))))
;; In `tramp-check-cached-permissions', the connection properties
;; {uig,gid}-{integer,string} are used. We set them to proper values.
;; "{uid,gid}-{integer,string}" are used. We set them to proper values.
(with-tramp-connection-property
vec "uid-integer" (tramp-get-local-uid 'integer))
(with-tramp-connection-property
......
......@@ -1044,6 +1044,7 @@ of command line.")
(start-file-process . tramp-sh-handle-start-file-process)
(substitute-in-file-name . tramp-handle-substitute-in-file-name)
(temporary-file-directory . tramp-handle-temporary-file-directory)
(tramp-set-file-uid-gid . tramp-sh-handle-set-file-uid-gid)
(unhandled-file-name-directory . ignore)
(vc-registered . tramp-sh-handle-vc-registered)
(verify-visited-file-modtime . tramp-sh-handle-verify-visited-file-modtime)
......@@ -1516,39 +1517,26 @@ of."
"")
(tramp-shell-quote-argument localname)))))))
(defun tramp-set-file-uid-gid (filename &optional uid gid)
"Set the ownership for FILENAME.
If UID and GID are provided, these values are used; otherwise uid
and gid of the corresponding user is taken. Both parameters must
be non-negative integers."
(defun tramp-sh-handle-set-file-uid-gid (filename &optional uid gid)
"Like `tramp-set-file-uid-gid' for Tramp files."
;; Modern Unices allow chown only for root. So we might need
;; another implementation, see `dired-do-chown'. OTOH, it is mostly
;; working with su(do)? when it is needed, so it shall succeed in
;; the majority of cases.
;; Don't modify `last-coding-system-used' by accident.
(let ((last-coding-system-used last-coding-system-used))
(if (tramp-tramp-file-p filename)
(with-parsed-tramp-file-name filename nil
(if (and (zerop (user-uid)) (tramp-local-host-p v))
;; If we are root on the local host, we can do it directly.
(tramp-set-file-uid-gid localname uid gid)
(let ((uid (or (and (natnump uid) uid)
(tramp-get-remote-uid v 'integer)))
(gid (or (and (natnump gid) gid)
(tramp-get-remote-gid v 'integer))))
(tramp-send-command
v (format
"chown %d:%d %s" uid gid
(tramp-shell-quote-argument localname))))))
;; We handle also the local part, because there doesn't exist
;; `set-file-uid-gid'. On W32 "chown" does not work.
(unless (memq system-type '(ms-dos windows-nt))
(let ((uid (or (and (natnump uid) uid) (tramp-get-local-uid 'integer)))
(gid (or (and (natnump gid) gid) (tramp-get-local-gid 'integer))))
(tramp-call-process
nil "chown" nil nil nil
(format "%d:%d" uid gid) (shell-quote-argument filename)))))))
(with-parsed-tramp-file-name filename nil
(if (and (zerop (user-uid)) (tramp-local-host-p v))
;; If we are root on the local host, we can do it directly.
(tramp-set-file-uid-gid localname uid gid)
(let ((uid (or (and (natnump uid) uid)
(tramp-get-remote-uid v 'integer)))
(gid (or (and (natnump gid) gid)
(tramp-get-remote-gid v 'integer))))
(tramp-send-command
v (format
"chown %d:%d %s" uid gid
(tramp-shell-quote-argument localname))))))))
(defun tramp-remote-selinux-p (vec)
"Check, whether SELINUX is enabled on the remote host."
......@@ -2114,6 +2102,7 @@ file names."
;; Handle `preserve-extended-attributes'. We ignore possible
;; errors, because ACL strings could be incompatible.
;; `set-file-extended-attributes' exists since Emacs 24.4.
(when attributes
(ignore-errors
(apply 'set-file-extended-attributes (list newname attributes))))
......
......@@ -282,6 +282,7 @@ See `tramp-actions-before-shell' for more info.")
(start-file-process . tramp-smb-handle-start-file-process)
(substitute-in-file-name . tramp-smb-handle-substitute-in-file-name)
(temporary-file-directory . tramp-handle-temporary-file-directory)
(tramp-set-file-uid-gid . ignore)
(unhandled-file-name-directory . ignore)
(vc-registered . ignore)
(verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
......
This diff is collapsed.
......@@ -2234,7 +2234,9 @@ ARGS are the arguments OPERATION has been called with."
;; Emacs 26+ only.
file-name-case-insensitive-p
;; Emacs 27+ only.
file-system-info))
file-system-info
;; Tramp internal magic file name function.
tramp-set-file-uid-gid))
(if (file-name-absolute-p (nth 0 args))
(nth 0 args)
default-directory))
......@@ -4329,24 +4331,49 @@ This is used internally by `tramp-file-mode-from-int'."
(and suid (upcase suid-text)) ; suid, !execute
(and x "x") "-")))) ; !suid
;; This is a Tramp internal function. A general `set-file-uid-gid'
;; outside Tramp is not needed, I believe.
(defun tramp-set-file-uid-gid (filename &optional uid gid)
"Set the ownership for FILENAME.
If UID and GID are provided, these values are used; otherwise uid
and gid of the corresponding remote or local user is taken,
depending whether FILENAME is remote or local. Both parameters
must be non-negative integers.
If FILENAME is remote, a file name handler is called."
(let ((handler (find-file-name-handler filename 'tramp-set-file-uid-gid)))
(if handler
(funcall handler 'tramp-set-file-uid-gid filename uid gid)
;; On W32 "chown" does not work.
(unless (memq system-type '(ms-dos windows-nt))
(let ((uid (or (and (natnump uid) uid) (tramp-get-local-uid 'integer)))
(gid (or (and (natnump gid) gid) (tramp-get-local-gid 'integer))))
(tramp-call-process
nil "chown" nil nil nil
(format "%d:%d" uid gid) (shell-quote-argument filename)))))))
;;;###tramp-autoload
(defun tramp-get-local-uid (id-format)
"The uid of the local user, in ID-FORMAT.
ID-FORMAT valid values are `string' and `integer'."
(if (equal id-format 'integer) (user-uid) (user-login-name)))
;; We use key nil for local connection properties.
(with-tramp-connection-property nil (format "uid-%s" id-format)
(if (equal id-format 'integer) (user-uid) (user-login-name))))
;;;###tramp-autoload
(defun tramp-get-local-gid (id-format)
"The gid of the local user, in ID-FORMAT.
ID-FORMAT valid values are `string' and `integer'."
(cond
;; `group-gid' has been introduced with Emacs 24.4.
((and (fboundp 'group-gid) (equal id-format 'integer))
(tramp-compat-funcall 'group-gid))
;; `group-name' has been introduced with Emacs 27.1.
((and (fboundp 'group-name) (equal id-format 'string))
(tramp-compat-funcall 'group-name (tramp-compat-funcall 'group-gid)))
((tramp-compat-file-attribute-group-id (file-attributes "~/" id-format)))))
;; We use key nil for local connection properties.
(with-tramp-connection-property nil (format "gid-%s" id-format)
(cond
;; `group-gid' has been introduced with Emacs 24.4.
((and (fboundp 'group-gid) (equal id-format 'integer))
(tramp-compat-funcall 'group-gid))
;; `group-name' has been introduced with Emacs 27.1.
((and (fboundp 'group-name) (equal id-format 'string))
(tramp-compat-funcall 'group-name (tramp-compat-funcall 'group-gid)))
((tramp-compat-file-attribute-group-id
(file-attributes "~/" id-format))))))
(defun tramp-get-local-locale (&optional vec)
"Determine locale, supporting UTF8 if possible.
......
......@@ -3009,7 +3009,7 @@ This tests also `file-readable-p', `file-regular-p' and
"Check `file-modes'.
This tests also `file-executable-p', `file-writable-p' and `set-file-modes'."
(skip-unless (tramp--test-enabled))
(skip-unless (tramp--test-sh-p))
(skip-unless (or (tramp--test-sh-p) (tramp--test-sudoedit-p)))
(dolist (quoted (if (tramp--test-expensive-test) '(nil t) '(nil)))
(let ((tmp-name (tramp--test-make-temp-name nil quoted)))
......@@ -3309,7 +3309,8 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'."
(ert-deftest tramp-test22-file-times ()
"Check `set-file-times' and `file-newer-than-file-p'."
(skip-unless (tramp--test-enabled))
(skip-unless (or (tramp--test-adb-p) (tramp--test-sh-p)))
(skip-unless
(or (tramp--test-adb-p) (tramp--test-sh-p) (tramp--test-sudoedit-p)))
(dolist (quoted (if (tramp--test-expensive-test) '(nil t) '(nil)))
(let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
......@@ -4567,6 +4568,10 @@ This does not support special file names."
(tramp-find-foreign-file-name-handler tramp-test-temporary-file-directory)
'tramp-sh-file-name-handler))
(defun tramp--test-sudoedit-p ()
"Check, whether the sudoedit method is used."
(tramp-sudoedit-file-name-p tramp-test-temporary-file-directory))
(defun tramp--test-windows-nt ()
"Check, whether the locale host runs MS Windows."
(eq system-type 'windows-nt))
......@@ -4761,6 +4766,7 @@ This requires restrictions of file name syntax."
(list
(if (or (tramp--test-gvfs-p)
(tramp--test-rclone-p)
(tramp--test-sudoedit-p)
(tramp--test-windows-nt-or-smb-p))
"foo bar baz"
(if (or (tramp--test-adb-p)
......
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