Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Are there any password manager plugins? #1222

Closed
nerdrew opened this issue Dec 11, 2018 · 15 comments
Closed

Are there any password manager plugins? #1222

nerdrew opened this issue Dec 11, 2018 · 15 comments

Comments

@nerdrew
Copy link
Contributor

nerdrew commented Dec 11, 2018

I'm looking for a way to enter passwords from my password manager at password prompts. The password manager has a CLI (in this case, the security command in MacOS). Are there any kittens that allow me to integrate my password manager? If there aren't any existing kittens, is there any way to detect if a password prompt is showing (i.e. if STDIN is set to noecho) so you don't accidentally enter your password into the wrong prompt?

Btw: thanks for responding so quickly to my last question! kitty is awesome!

@nerdrew nerdrew changed the title Are there any password maanger plugins? Are there any password manager plugins? Dec 11, 2018
@Luflosi
Copy link
Contributor

Luflosi commented Dec 11, 2018

Something like this would be really nice. I really liked the password manager of iTerm2, which uses macOS's keychain to store the passwords and has "a safety mechanism that ensures your password only gets entered at a password prompt". Currently I use the macOS keychain directly but that's a bit inconvenient and I have to copy the password to the clipboard.

@kovidgoyal
Copy link
Owner

There are no kitten that I know of, but it should be easy to write one. As for detecting noecho one can do it easily using termios. tcgetattr() is the name of the function IIRC. python has a termios module you can use for this.

@nerdrew
Copy link
Contributor Author

nerdrew commented Dec 11, 2018

Thanks!

@nerdrew
Copy link
Contributor Author

nerdrew commented Dec 11, 2018

Question: what fd do I pass to tcgetattr?

I currently have:

import termios
from kitty.utils import TTYIO

def main(args):
   pass

def handle_result(args, answer, target_window_id, boss):
    with TTYIO() as io:
        try:
            attr = termios.tcgetattr(io.tty_fd)
        except termios.error as err:
            errmsg = 'getecho() may not be called on this platform'
            if err.args[0] == errno.EINVAL:
                raise IOError(err.args[0], '%s: %s.' % (err.args[1], errmsg))
            raise

        if attr[3] & termios.ECHO == 0:
            w = boss.window_id_map.get(target_window_id)
            if w is not None:
                w.paste("boom")

handle_result.no_ui = True

Is TTYIO().tty_fd the right fd for the terminal?

@nerdrew
Copy link
Contributor Author

nerdrew commented Dec 11, 2018

It always returns that ECHO is not set.

@kovidgoyal
Copy link
Owner

why are you using TTYIO there? handle_result() is for after the kitten runs and you want to apply the output of the kitten to the target window. Use the fd of the target window. w.child.fd IIRC

since you want a kitten with no UI you never need to use TTYIO.

@nerdrew
Copy link
Contributor Author

nerdrew commented Dec 11, 2018

Sorry, ruby programmer here :/ Trying to learn python as I go. I'll try that now! Thanks!

@kovidgoyal
Copy link
Owner

no worries :)

@nerdrew
Copy link
Contributor Author

nerdrew commented Dec 12, 2018

I've got this much so far:

from termios import error, tcgetattr, ECHO, ICANON, ISIG, ECHOCTL, ECHOKE
import re
import subprocess
import sys

def main(args):
    if not sys.stdin.isatty():
        data = sys.stdin.read().strip().split('\n')[-1]
        return data
    else:
        print("\a")
        print("Tried to read a tty")

def handle_result(args, data, target_window_id, boss):
    w = boss.window_id_map.get(target_window_id)
    if w is not None:
        fd = w.child.child_fd
        try:
            c_lflag = tcgetattr(fd)[3]
        except error as err:
            errmsg = 'getecho() may not be called on this platform'
            if err.args[0] == errno.EINVAL:
                raise IOError(err.args[0], '%s: %s.' % (err.args[1], errmsg))
            raise

        def send_password():
            password = subprocess.run(args[1:], capture_output=True, text=True, check=True).stdout
            w.paste(password + '\r')

        def is_set(flag):
            return bool(c_lflag & flag)

        # print(f'c_lflag={c_lflag} icanon={c_lflag & ICANON} echo={c_lflag & ECHO} echoctl={c_lflag & ECHOCTL} echoke={c_lflag & ECHOKE} isig={c_lflag & ISIG} data={repr(data)}')
        if is_set(ISIG) and is_set(ICANON) and not is_set(ECHO):
            # ruby -rio/console -e 'STDIN.noecho { |io| puts io.gets.inspect }'
            # sudo <cmd>
            send_password()
        elif not is_set(ISIG) and not is_set(ICANON) and not is_set(ECHO) and re.match(r"Password.*:$", data):
            # ssh -t <host> <cmd> # same socket mode for kinit and cat :(
            send_password()
        else:
            print("\a")
            print("Ooops. Are you at a password prompt?")

handle_result.type_of_input = 'text'

I hit a couple spots in the documentation where I wished there'd been a little more detail. Open to documentation / example PRs? Specifically, to read STDIN in a kitten, you must do it from the main function, not the handle_result function.

@kovidgoyal
Copy link
Owner

Sure, docs PRs are most welcome.

@nerdrew
Copy link
Contributor Author

nerdrew commented Dec 16, 2018

Following up: here's the password manager kitten I wrote: https://github.com/nerdrew/kittens/blob/master/password.py

I've been using it for a few days and it seems to work for me.

I have the following config for it (I use a mac, security is the CLI for KeyChain Access):

map cmd+BACKSLASH kitten kittens/password.py security find-generic-password -a "Production Password" -w

@kovidgoyal
Copy link
Owner

cool, I really should setup some kind of repository for kittens some day.

@Varal7
Copy link

Varal7 commented Apr 15, 2022

I've been struggling with using the above code, so here's my version:

from typing import List, Optional
from kitty.boss import Boss
import subprocess
import sys
sys.path.insert(0, 'path-to-dist-packages')
from pyfzf.pyfzf import FzfPrompt

def main(args: List[str]) -> Optional[str]:
    fzf = FzfPrompt('path-to-fzf')
    answer = fzf.prompt(["password1-name-in-keychain", "password2-name-in-keychain"])
    if len(answer) > 0:
        return answer[0]
    return None

def handle_result(args: List[str], answer: Optional[str], target_window_id: int, boss: Boss) -> None:
    if answer is None:
        return

    args = ["security", "find-generic-password", "-l", answer, "-w"]

    password = subprocess.run(args, capture_output=True, text=True, check=True).stdout
    w = boss.window_id_map.get(target_window_id)

    if w is not None:
        w.paste(password)

It does not check that you're not typing the password in clear, but it works well for me, as I need multiple passwords and can use fzf to choose between them.

@dnanhkhoa
Copy link

I also made a simple plugin based on nerdrew's work. It enables you to select your credentials stored in Bitwarden via Fzf and have the selected password filled in automatically. You can check it out at https://github.com/dnanhkhoa/kitty-password-manager

@MarkJaroski
Copy link
Sponsor

I got @nerdrew's kitten to work in a very basic way with KeepassXC, but I'm not 100% satisfied with the solution and so I'm going to work on a solution more like @dnanhkhoa's involving search. But for the meantime KeepassXC users: it is possible to replace autotype with this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants