aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Albinus2018-04-23 10:16:06 +0200
committerMichael Albinus2018-04-23 10:16:06 +0200
commit0ecc10a7771bf1f62d15b2e6c747bee9f7a557ff (patch)
tree7547eb4f8b8282b88d811189b26fa6dc42d06d77
parente7044d294c1b1779b3124b27ba0f09b22b64df20 (diff)
downloademacs-0ecc10a7771bf1f62d15b2e6c747bee9f7a557ff.tar.gz
emacs-0ecc10a7771bf1f62d15b2e6c747bee9f7a557ff.zip
Let Tramp save passwords
* lisp/auth-source.el (auth-source-secrets-saver): New defun. (auth-source-secrets-create): Use it. * lisp/net/secrets.el (secrets-struct-secret-content-type): (secrets-create-item): Do not hard-code :xdg:schema. * lisp/net/tramp.el (tramp-password-save-function): New defvar. (tramp-read-passwd): Set it properly. (tramp-process-actions): * lisp/net/tramp-gvfs.el (tramp-gvfs-maybe-open-connection): Save password. * lisp/net/tramp-cmds.el (tramp-bug): Don't report `tramp-password-save-function'. * test/lisp/net/secrets-tests.el (secrets-test03-items): Extend test with another :xdg:schema.
-rw-r--r--lisp/auth-source.el37
-rw-r--r--lisp/net/secrets.el21
-rw-r--r--lisp/net/tramp-cmds.el4
-rw-r--r--lisp/net/tramp-gvfs.el3
-rw-r--r--lisp/net/tramp.el50
-rw-r--r--test/lisp/net/secrets-tests.el15
6 files changed, 102 insertions, 28 deletions
diff --git a/lisp/auth-source.el b/lisp/auth-source.el
index a2ed47a0d45..df3622a412a 100644
--- a/lisp/auth-source.el
+++ b/lisp/auth-source.el
@@ -1732,10 +1732,45 @@ authentication tokens:
1732 (item (plist-get artificial :label)) 1732 (item (plist-get artificial :label))
1733 (secret (plist-get artificial :secret)) 1733 (secret (plist-get artificial :secret))
1734 (secret (if (functionp secret) (funcall secret) secret))) 1734 (secret (if (functionp secret) (funcall secret) secret)))
1735 (lambda () (apply 'secrets-create-item collection item secret args)))) 1735 (lambda ()
1736 (apply 'auth-source-secrets-saver collection item secret args))))
1736 1737
1737 (list artificial))) 1738 (list artificial)))
1738 1739
1740(defun auth-source-secrets-saver (collection item secret args)
1741 "Wrapper around `secrets-create-item', prompting along the way.
1742Respects `auth-source-save-behavior'."
1743 (let ((prompt (format "Save auth info to secrets collection %s? " collection))
1744 (done (not (eq auth-source-save-behavior 'ask)))
1745 (bufname "*auth-source Help*")
1746 doit k)
1747 (while (not done)
1748 (setq k (auth-source-read-char-choice prompt '(?y ?n ?N ??)))
1749 (cl-case k
1750 (?y (setq done t doit t))
1751 (?? (save-excursion
1752 (with-output-to-temp-buffer bufname
1753 (princ
1754 (concat "(y)es, save\n"
1755 "(n)o but use the info\n"
1756 "(N)o and don't ask to save again\n"
1757 "(?) for help as you can see.\n"))
1758 ;; Why? Doesn't with-output-to-temp-buffer already do
1759 ;; the exact same thing anyway? --Stef
1760 (set-buffer standard-output)
1761 (help-mode))))
1762 (?n (setq done t doit nil))
1763 (?N (setq done t doit nil)
1764 (customize-save-variable 'auth-source-save-behavior nil))
1765 (t nil)))
1766
1767 (when doit
1768 (progn
1769 (auth-source-do-debug
1770 "secrets-create-item: wrote 1 new item to %s" collection)
1771 (message "Saved new authentication information to %s" collection)
1772 (apply 'secrets-create-item collection item secret args)))))
1773
1739;;; Backend specific parsing: Mac OS Keychain (using /usr/bin/security) backend 1774;;; Backend specific parsing: Mac OS Keychain (using /usr/bin/security) backend
1740 1775
1741(cl-defun auth-source-macos-keychain-search (&rest spec 1776(cl-defun auth-source-macos-keychain-search (&rest spec
diff --git a/lisp/net/secrets.el b/lisp/net/secrets.el
index 8070ccf96e2..f7cc011615e 100644
--- a/lisp/net/secrets.el
+++ b/lisp/net/secrets.el
@@ -331,9 +331,7 @@ It returns t if not."
331 ;; Properties. 331 ;; Properties.
332 `(:array 332 `(:array
333 (:dict-entry ,(concat secrets-interface-item ".Label") 333 (:dict-entry ,(concat secrets-interface-item ".Label")
334 (:variant "dummy")) 334 (:variant " ")))
335 (:dict-entry ,(concat secrets-interface-item ".Type")
336 (:variant ,secrets-interface-item-type-generic)))
337 ;; Secret. 335 ;; Secret.
338 `(:struct :object-path ,path 336 `(:struct :object-path ,path
339 (:array :signature "y") 337 (:array :signature "y")
@@ -649,11 +647,24 @@ keys are keyword symbols, starting with a colon. Example:
649 (secrets-create-item \"Tramp collection\" \"item\" \"geheim\" 647 (secrets-create-item \"Tramp collection\" \"item\" \"geheim\"
650 :method \"sudo\" :user \"joe\" :host \"remote-host\") 648 :method \"sudo\" :user \"joe\" :host \"remote-host\")
651 649
650The key `:xdg:schema' determines the scope of the item to be
651generated, i.e. for which applications the item is intended for.
652This is just a string like \"org.freedesktop.NetworkManager.Mobile\"
653or \"org.gnome.OnlineAccounts\", the other required keys are
654determined by this. If no `:xdg:schema' is given,
655\"org.freedesktop.Secret.Generic\" is used by default.
656
652The object path of the created item is returned." 657The object path of the created item is returned."
653 (unless (member item (secrets-list-items collection)) 658 (unless (member item (secrets-list-items collection))
654 (let ((collection-path (secrets-unlock-collection collection)) 659 (let ((collection-path (secrets-unlock-collection collection))
655 result props) 660 result props)
656 (unless (secrets-empty-path collection-path) 661 (unless (secrets-empty-path collection-path)
662 ;; Set default type if needed.
663 (unless (member :xdg:schema attributes)
664 (setq attributes
665 (append
666 attributes
667 `(:xdg:schema ,secrets-interface-item-type-generic))))
657 ;; Create attributes list. 668 ;; Create attributes list.
658 (while (consp (cdr attributes)) 669 (while (consp (cdr attributes))
659 (unless (keywordp (car attributes)) 670 (unless (keywordp (car attributes))
@@ -675,9 +686,7 @@ The object path of the created item is returned."
675 (append 686 (append
676 `(:array 687 `(:array
677 (:dict-entry ,(concat secrets-interface-item ".Label") 688 (:dict-entry ,(concat secrets-interface-item ".Label")
678 (:variant ,item)) 689 (:variant ,item)))
679 (:dict-entry ,(concat secrets-interface-item ".Type")
680 (:variant ,secrets-interface-item-type-generic)))
681 (when props 690 (when props
682 `((:dict-entry ,(concat secrets-interface-item ".Attributes") 691 `((:dict-entry ,(concat secrets-interface-item ".Attributes")
683 (:variant ,(append '(:array) props)))))) 692 (:variant ,(append '(:array) props))))))
diff --git a/lisp/net/tramp-cmds.el b/lisp/net/tramp-cmds.el
index cbb9cd37005..b05f475f2fd 100644
--- a/lisp/net/tramp-cmds.el
+++ b/lisp/net/tramp-cmds.el
@@ -181,7 +181,9 @@ This includes password cache, file cache, connection cache, buffers."
181 "Submit a bug report to the Tramp developers." 181 "Submit a bug report to the Tramp developers."
182 (interactive) 182 (interactive)
183 (catch 'dont-send 183 (catch 'dont-send
184 (let ((reporter-prompt-for-summary-p t)) 184 (let ((reporter-prompt-for-summary-p t)
185 ;; In rare cases, it could contain the password. So we make it nil.
186 tramp-password-save-function)
185 (reporter-submit-bug-report 187 (reporter-submit-bug-report
186 tramp-bug-report-address ; to-address 188 tramp-bug-report-address ; to-address
187 (format "tramp (%s)" tramp-version) ; package name and version 189 (format "tramp (%s)" tramp-version) ; package name and version
diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el
index b3d5339321b..199ac4fad24 100644
--- a/lisp/net/tramp-gvfs.el
+++ b/lisp/net/tramp-gvfs.el
@@ -2041,6 +2041,9 @@ connection if a previous connection has died for some reason."
2041 (tramp-get-file-property vec "/" "fuse-mountpoint" "") "/") 2041 (tramp-get-file-property vec "/" "fuse-mountpoint" "") "/")
2042 (tramp-error vec 'file-error "FUSE mount denied")) 2042 (tramp-error vec 'file-error "FUSE mount denied"))
2043 2043
2044 ;; Save the password.
2045 (ignore-errors (funcall tramp-password-save-function))
2046
2044 ;; Set connection-local variables. 2047 ;; Set connection-local variables.
2045 (tramp-set-connection-local-variables vec) 2048 (tramp-set-connection-local-variables vec)
2046 2049
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index 5c785b16d89..c394f28a561 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -1192,6 +1192,11 @@ means to use always cached values for the directory contents."
1192(defvar tramp-current-connection nil 1192(defvar tramp-current-connection nil
1193 "Last connection timestamp.") 1193 "Last connection timestamp.")
1194 1194
1195(defvar tramp-password-save-function nil
1196 "Password save function.
1197Will be called once the password has been verified by successful
1198authentication.")
1199
1195(defconst tramp-completion-file-name-handler-alist 1200(defconst tramp-completion-file-name-handler-alist
1196 '((file-name-all-completions 1201 '((file-name-all-completions
1197 . tramp-completion-handle-file-name-all-completions) 1202 . tramp-completion-handle-file-name-all-completions)
@@ -3852,7 +3857,9 @@ connection buffer."
3852 (with-current-buffer (tramp-get-connection-buffer vec) 3857 (with-current-buffer (tramp-get-connection-buffer vec)
3853 (widen) 3858 (widen)
3854 (tramp-message vec 6 "\n%s" (buffer-string))) 3859 (tramp-message vec 6 "\n%s" (buffer-string)))
3855 (unless (eq exit 'ok) 3860 (if (eq exit 'ok)
3861 (ignore-errors (funcall tramp-password-save-function))
3862 ;; Not successful.
3856 (tramp-clear-passwd vec) 3863 (tramp-clear-passwd vec)
3857 (delete-process proc) 3864 (delete-process proc)
3858 (tramp-error-with-buffer 3865 (tramp-error-with-buffer
@@ -4458,12 +4465,14 @@ Invokes `password-read' if available, `read-passwd' else."
4458 (with-current-buffer (process-buffer proc) 4465 (with-current-buffer (process-buffer proc)
4459 (tramp-check-for-regexp proc tramp-password-prompt-regexp) 4466 (tramp-check-for-regexp proc tramp-password-prompt-regexp)
4460 (format "%s for %s " (capitalize (match-string 1)) key)))) 4467 (format "%s for %s " (capitalize (match-string 1)) key))))
4468 (auth-source-creation-prompts `((secret . ,pw-prompt)))
4461 ;; We suspend the timers while reading the password. 4469 ;; We suspend the timers while reading the password.
4462 (stimers (with-timeout-suspend)) 4470 (stimers (with-timeout-suspend))
4463 auth-info auth-passwd) 4471 auth-info auth-passwd)
4464 4472
4465 (unwind-protect 4473 (unwind-protect
4466 (with-parsed-tramp-file-name key nil 4474 (with-parsed-tramp-file-name key nil
4475 (setq tramp-password-save-function nil)
4467 (setq user 4476 (setq user
4468 (or user (tramp-get-connection-property key "login-as" nil))) 4477 (or user (tramp-get-connection-property key "login-as" nil)))
4469 (prog1 4478 (prog1
@@ -4474,31 +4483,38 @@ Invokes `password-read' if available, `read-passwd' else."
4474 v "first-password-request" nil) 4483 v "first-password-request" nil)
4475 ;; Try with Tramp's current method. 4484 ;; Try with Tramp's current method.
4476 (setq auth-info 4485 (setq auth-info
4477 (auth-source-search 4486 (car
4478 :max 1 4487 (auth-source-search
4479 (and user :user) 4488 :max 1
4480 (if domain 4489 (and user :user)
4481 (concat user tramp-prefix-domain-format domain) 4490 (if domain
4482 user) 4491 (concat
4483 :host 4492 user tramp-prefix-domain-format domain)
4484 (if port 4493 user)
4485 (concat host tramp-prefix-port-format port) 4494 :host
4486 host) 4495 (if port
4487 :port method 4496 (concat
4488 :require (cons :secret (and user '(:user)))) 4497 host tramp-prefix-port-format port)
4489 auth-passwd (plist-get 4498 host)
4490 (nth 0 auth-info) :secret) 4499 :port method
4500 :require (cons :secret (and user '(:user)))
4501 :create t))
4502 tramp-password-save-function
4503 (plist-get auth-info :save-function)
4504 auth-passwd (plist-get auth-info :secret)
4491 auth-passwd (if (functionp auth-passwd) 4505 auth-passwd (if (functionp auth-passwd)
4492 (funcall auth-passwd) 4506 (funcall auth-passwd)
4493 auth-passwd)))) 4507 auth-passwd))))
4508
4494 ;; Try the password cache. 4509 ;; Try the password cache.
4495 (let ((password (password-read pw-prompt key))) 4510 (let ((password (password-read pw-prompt key)))
4496 ;; FIXME test password works before caching it. 4511 (setq tramp-password-save-function
4497 (password-cache-add key password) 4512 (lambda () (password-cache-add key password)))
4498 password) 4513 password)
4499 ;; Else, get the password interactively. 4514 ;; Else, get the password interactively.
4500 (read-passwd pw-prompt)) 4515 (read-passwd pw-prompt))
4501 (tramp-set-connection-property v "first-password-request" nil))) 4516 (tramp-set-connection-property v "first-password-request" nil)))
4517
4502 ;; Reenable the timers. 4518 ;; Reenable the timers.
4503 (with-timeout-unsuspend stimers)))) 4519 (with-timeout-unsuspend stimers))))
4504 4520
diff --git a/test/lisp/net/secrets-tests.el b/test/lisp/net/secrets-tests.el
index dc9c7f1004a..23512d48ee5 100644
--- a/test/lisp/net/secrets-tests.el
+++ b/test/lisp/net/secrets-tests.el
@@ -169,9 +169,16 @@
169 (should 169 (should
170 (equal 170 (equal
171 (secrets-get-attributes "session" "bar") 171 (secrets-get-attributes "session" "bar")
172 '((:host . "remote-host") (:user . "joe") 172 '((:xdg:schema . "org.freedesktop.Secret.Generic")
173 (:method . "sudo") 173 (:host . "remote-host") (:user . "joe") (:method . "sudo"))))
174 (:xdg:schema . "org.freedesktop.Secret.Generic")))) 174
175 ;; Create an item with another schema.
176 (secrets-create-item
177 "session" "baz" "secret" :xdg:schema "org.gnu.Emacs.foo")
178 (should
179 (equal
180 (secrets-get-attributes "session" "baz")
181 '((:xdg:schema . "org.gnu.Emacs.foo"))))
175 182
176 ;; Delete them. 183 ;; Delete them.
177 (dolist (item (secrets-list-items "session")) 184 (dolist (item (secrets-list-items "session"))
@@ -206,6 +213,8 @@
206 213
207 ;; Search the items. 214 ;; Search the items.
208 (should-not (secrets-search-items "session" :user "john")) 215 (should-not (secrets-search-items "session" :user "john"))
216 (should-not
217 (secrets-search-items "session" :xdg:schema "org.gnu.Emacs.foo"))
209 (should 218 (should
210 (equal 219 (equal
211 (sort (secrets-search-items "session" :user "joe") 'string-lessp) 220 (sort (secrets-search-items "session" :user "joe") 'string-lessp)