abo-abo / swiper

Ivy - a generic completion frontend for Emacs, Swiper - isearch with an overview, and more. Oh, man!
https://oremacs.com/swiper/
2.31k stars 338 forks source link

counsel-yank-pop displays the pinentry passphrase in cleartext #2843

Closed grolongo closed 3 years ago

grolongo commented 3 years ago

Tested on Windows and macOS with Emacs 27.2

using this minimal config:

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)

;; Make sure `use-package’ is available.
(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))

(eval-when-compile
  (require 'use-package))

(setq epg-pinentry-mode 'loopback)

(use-package auth-source
  :custom
  (auth-sources (list (convert-standard-filename
                       (expand-file-name "authinfo.gpg" user-emacs-directory)))))

(use-package erc
  :preface
  (defun my/freenode-erc ()
    (interactive)
    (erc-tls :server "irc.freenode.net" :port 7070 :nick "foo")))

(use-package ivy
  :ensure t
  :config (ivy-mode 1))

(use-package counsel
  :ensure t
  :config (counsel-mode 1))

Now do M-x my/freenode-erc, you get prompted for the passphrase to decrypt authinfo.gpg, copy your passphrase from where you store it and C-y to yank it into the Emacs prompt.

Problem: After successfuly or unsuccessfuly (doesn't matter) connecting to Freenode do M-x counsel-yank-pop -> the password appears in cleartext.

Can someone tell me if this intended behavior or just a bug? For the record I also tried with the Selectrum/Consult framework and consult-yank-pop also displays the password in cleartext too.

basil-conto commented 3 years ago

Can someone tell me if this intended behavior or just a bug?

It's intended behaviour. The key is where you say:

copy your passphrase from where you store it and C-y to yank it into the Emacs prompt.

Copying the passphrase puts it in your kill-ring, so anything that looks at your kill-ring will find it there in plaintext. counsel-yank-pop (as well as vanilla yank-pop in Emacs 28) just completes whatever is already in your kill-ring.

If you want to avoid having your password stored in plaintext in a variable, then don't copy it. This is why password managers like pass that place passwords in your clipboard only do so for a short duration like 30 seconds.

I don't use ERC, but AFAICT it can use auth-source-search to automatically retrieve your password. I think when it does so it's hashed and stored as a lexical variable in a closure (at least in the development version of Emacs). So I suggest configuring auth-source or similar to do this in a way that suits you. If all else fails you could even program it yourself, e.g. by querying auth-source-search and/or using the built-in password-cache.el with its timeout feature.

If ERC can't do what you want for some reason, then you should consider reporting it upstream via M-x report-emacs-bug RET.

HTH.

grolongo commented 3 years ago

Sorry but I don't understand some parts of your explanation, also sorry again because I understand this is not a specific Ivy/Consult issue but more about Emacs in general.

If you want to avoid having your password stored in plaintext in a variable, then don't copy it. This is why password managers like pass that place passwords in your clipboard only do so for a short duration like 30 seconds."

I'm actually using KeePass and I'm copying the passphrase from there. The clipboard gets cleared after exactly 12 seconds.

Copying the passphrase puts it in your kill-ring, so anything that looks at your kill-ring will find it there in plaintext. counsel-> yank-pop (as well as vanilla yank-pop in Emacs 28) just completes whatever is already in your kill-ring.

Do you mean copying or pasting that puts it in my Emacs kill-ring? Because if I copy a pass to the clipboard then go to an Emacs -Q instance and do Eval: kill-ring I get a nil return, without ever yanking it in any buffer.

The problem happens after I'm yanking the passphrase with C-y (which is shown blurred with asterisks in the minibuffer prompt) and then hit <RET>. Now if I do an Eval: kill-ring the passphrase is in cleartext and stays there until I close Emacs, despite being removed from the clipboard thanks to KeePass, if that makes sense.

basil-conto commented 3 years ago

Do you mean copying or pasting that puts it in my Emacs kill-ring?

Both.

Because if I copy a pass to the clipboard then go to an Emacs -Q instance and do Eval: kill-ring I get a nil return, without ever yanking it in any buffer.

As expected, because Emacs does not initialise kill-ring on startup based on the system clipboard or selection contents.

The problem happens after I'm yanking the passphrase with C-y (which is shown blurred with asterisks in the minibuffer prompt)

(Probably because read-passwd is being used.)

and then hit <RET>. Now if I do an Eval: kill-ring the passphrase is in cleartext and stays there until I close Emacs, despite being removed from the clipboard thanks to KeePass, if that makes sense.

That's because of the current design of C-y in Emacs. The way it gets text from the system clipboard is via interprogram-paste-function. yank calls current-kill, which in turn saves the value returned by interprogram-paste-function to the kill-ring.

I guess this isn't always desirable in the specific case of read-passwd, in which case you should probably chime in at https://bugs.gnu.org/36154.

In the meantime, you can avoid having your kill-ring modified during read-passwd using something like the following advice:

(define-advice read-passwd (:around (&rest args) my-inhibit-kill-ring)
  "Avoid modifying `kill-ring' in `read-passwd'."
  (let ((kill-ring kill-ring))
    (apply args)))

Alternatively, you could bind C-y in read-passwd-map to a command which does the same kill-ring saving dance before calling yank, or which calls interprogram-paste-function or similar directly.