mirror of
https://github.com/gaschz/qubes-pass.git
synced 2025-06-07 01:38:31 +02:00
Better clipboard support without leaks.
This commit is contained in:
parent
8d0bed69a8
commit
6716cb4d0d
97
bin/qvm-pass
97
bin/qvm-pass
@ -7,6 +7,7 @@ import os
|
|||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from pipes import quote
|
from pipes import quote
|
||||||
@ -28,7 +29,7 @@ usage_string = (
|
|||||||
"",
|
"",
|
||||||
" [ls|list|show]",
|
" [ls|list|show]",
|
||||||
" Retrieves the list of keys from the pass store.",
|
" Retrieves the list of keys from the pass store.",
|
||||||
" [show] [-c] <key>",
|
" [show] [-c | -q] <key>",
|
||||||
" Retrieves a key from the pass store.",
|
" Retrieves a key from the pass store.",
|
||||||
" generate [-n] [-f] <key> [pass-length]",
|
" generate [-n] [-f] <key> [pass-length]",
|
||||||
" Retrieves a key from the pass store; creates the key",
|
" Retrieves a key from the pass store; creates the key",
|
||||||
@ -36,7 +37,7 @@ usage_string = (
|
|||||||
" and returns the generated key on standard output.",
|
" and returns the generated key on standard output.",
|
||||||
" The -n option excludes symbols from being used",
|
" The -n option excludes symbols from being used",
|
||||||
" during password generation.",
|
" during password generation.",
|
||||||
" get-or-generate [-n] <key> [pass-length]",
|
" get-or-generate [-n | -c | -q] <key> [pass-length]",
|
||||||
" Retrieves a key from the pass store; creates the key",
|
" Retrieves a key from the pass store; creates the key",
|
||||||
" with 25 characters length if it does not exist yet,",
|
" with 25 characters length if it does not exist yet,",
|
||||||
" and returns the generated key on standard output.",
|
" and returns the generated key on standard output.",
|
||||||
@ -67,6 +68,11 @@ X_SELECTION="${PASSWORD_STORE_X_SELECTION:-clipboard}"
|
|||||||
CLIP_TIME="${PASSWORD_STORE_CLIP_TIME:-45}"
|
CLIP_TIME="${PASSWORD_STORE_CLIP_TIME:-45}"
|
||||||
BASE64="base64"
|
BASE64="base64"
|
||||||
|
|
||||||
|
die() {
|
||||||
|
echo "$@" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
clip() {
|
clip() {
|
||||||
if [[ -n $WAYLAND_DISPLAY ]]; then
|
if [[ -n $WAYLAND_DISPLAY ]]; then
|
||||||
local copy_cmd=( wl-copy )
|
local copy_cmd=( wl-copy )
|
||||||
@ -90,11 +96,12 @@ clip() {
|
|||||||
# trailing new lines.
|
# trailing new lines.
|
||||||
pkill -f "^$sleep_argv0" 2>/dev/null && sleep 0.5
|
pkill -f "^$sleep_argv0" 2>/dev/null && sleep 0.5
|
||||||
local before="$("${paste_cmd[@]}" 2>/dev/null | $BASE64)"
|
local before="$("${paste_cmd[@]}" 2>/dev/null | $BASE64)"
|
||||||
echo -n "$1" | "${copy_cmd[@]}" || die "Error: Could not copy data to the clipboard"
|
pwbased=$(cat "$1" | $BASE64) # Customized for qvm-pass.
|
||||||
|
echo -n "$pwbased" | $BASE64 -d | "${copy_cmd[@]}" || die "Error: Could not copy data to the clipboard"
|
||||||
(
|
(
|
||||||
( exec -a "$sleep_argv0" bash <<<"trap 'kill %1' TERM; sleep '$CLIP_TIME' & wait" )
|
( exec -a "$sleep_argv0" bash <<<"trap 'kill %1' TERM; sleep '$CLIP_TIME' & wait" )
|
||||||
local now="$("${paste_cmd[@]}" | $BASE64)"
|
local now="$("${paste_cmd[@]}" | $BASE64)"
|
||||||
[[ $now != $(echo -n "$1" | $BASE64) ]] && before="$now"
|
[[ $now != $(echo -n "$pwbased") ]] && before="$now"
|
||||||
|
|
||||||
# It might be nice to programatically check to see if klipper exists,
|
# It might be nice to programatically check to see if klipper exists,
|
||||||
# as well as checking for other common clipboard managers. But for now,
|
# as well as checking for other common clipboard managers. But for now,
|
||||||
@ -113,33 +120,63 @@ clip() {
|
|||||||
qrcode() {
|
qrcode() {
|
||||||
if [[ -n $DISPLAY || -n $WAYLAND_DISPLAY ]]; then
|
if [[ -n $DISPLAY || -n $WAYLAND_DISPLAY ]]; then
|
||||||
if type feh >/dev/null 2>&1; then
|
if type feh >/dev/null 2>&1; then
|
||||||
echo -n "$1" | qrencode --size 10 -o - | feh -x --title "pass: $2" -g +200+200 -
|
cat "$1" | qrencode --size 10 -o - | feh -x --title "pass: $2" -g +200+200 -
|
||||||
return
|
return
|
||||||
elif type gm >/dev/null 2>&1; then
|
elif type gm >/dev/null 2>&1; then
|
||||||
echo -n "$1" | qrencode --size 10 -o - | gm display -title "pass: $2" -geometry +200+200 -
|
cat "$1" | qrencode --size 10 -o - | gm display -title "pass: $2" -geometry +200+200 -
|
||||||
return
|
return
|
||||||
elif type display >/dev/null 2>&1; then
|
elif type display >/dev/null 2>&1; then
|
||||||
echo -n "$1" | qrencode --size 10 -o - | display -title "pass: $2" -geometry +200+200 -
|
cat "$1" | qrencode --size 10 -o - | display -title "pass: $2" -geometry +200+200 -
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo -n "$1" | qrencode -t utf8
|
cat "$1" | qrencode -t utf8
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def pass_frontend_shell(cmd):
|
def pass_frontend_shell(cmd, lineno=1):
|
||||||
|
cmd, data, path = cmd
|
||||||
|
line = data.splitlines()[lineno - 1]
|
||||||
global shell_functions
|
global shell_functions
|
||||||
quoted_cmd = shell_functions + "\n\n" + " ".join(quote(x) for x in cmd)
|
with tempfile.TemporaryDirectory(prefix="qubes-pass-") as d:
|
||||||
return subprocess.call(["bash", "-c", quoted_cmd])
|
fifo = os.path.join(d, "fifo")
|
||||||
|
os.mkfifo(fifo)
|
||||||
|
quoted_cmd = (
|
||||||
|
shell_functions + "\n\n" + " ".join(quote(x) for x in (cmd, fifo, path))
|
||||||
|
)
|
||||||
|
p = subprocess.Popen(["bash", "-c", quoted_cmd])
|
||||||
|
with open(fifo, "wb") as fifof:
|
||||||
|
fifof.write(line)
|
||||||
|
fifof.flush()
|
||||||
|
return p.wait()
|
||||||
|
|
||||||
|
|
||||||
def clip(data, path):
|
def clip(data, path, lineno=1):
|
||||||
return pass_frontend_shell(["clip", data, path])
|
try:
|
||||||
|
return pass_frontend_shell(["clip", data, path], lineno=lineno)
|
||||||
|
except IndexError:
|
||||||
|
print(
|
||||||
|
f"There is no password to put on the clipboard at line {lineno}.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def qrcode(data, path):
|
def qrcode(data, path, lineno=1):
|
||||||
return pass_frontend_shell(["qrcode", data, path])
|
try:
|
||||||
|
return pass_frontend_shell(["qrcode", data, path], lineno=lineno)
|
||||||
|
except IndexError:
|
||||||
|
print(
|
||||||
|
f"There is no password to put on the clipboard at line {lineno}.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def clipqrcodeexit(opts, stdout: bytes):
|
||||||
|
if opts.clip:
|
||||||
|
sys.exit(clip(stdout, opts.key, opts.clip))
|
||||||
|
elif opts.qrcode:
|
||||||
|
sys.exit(qrcode(stdout, opts.key, opts.qrcode))
|
||||||
|
|
||||||
|
|
||||||
parser_for_discrimination = argparse.ArgumentParser(description="(nobody sees this)")
|
parser_for_discrimination = argparse.ArgumentParser(description="(nobody sees this)")
|
||||||
@ -235,16 +272,16 @@ for p in ["show", "get-or-generate", "generate"]:
|
|||||||
_parsers[p].add_argument(
|
_parsers[p].add_argument(
|
||||||
"-c",
|
"-c",
|
||||||
"--clip",
|
"--clip",
|
||||||
action="store_true",
|
type=int,
|
||||||
help="copy password to clipboard instead of displaying onscreen",
|
help="copy password to clipboard instead of displaying onscreen",
|
||||||
default=False,
|
default=0,
|
||||||
)
|
)
|
||||||
_parsers[p].add_argument(
|
_parsers[p].add_argument(
|
||||||
"-q",
|
"-q",
|
||||||
"--qrcode",
|
"--qrcode",
|
||||||
action="store_true",
|
type=int,
|
||||||
help="display password as QR code",
|
help="display password as QR code",
|
||||||
default=False,
|
default=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
for p in ["mv", "cp", "rm", "insert", "generate"]:
|
for p in ["mv", "cp", "rm", "insert", "generate"]:
|
||||||
@ -338,6 +375,11 @@ arguments = sys.argv[1:]
|
|||||||
if "--help" in arguments or "-h" in arguments or "-?" in arguments:
|
if "--help" in arguments or "-h" in arguments or "-?" in arguments:
|
||||||
parser_for_subcommands.parse_known_args(arguments)
|
parser_for_subcommands.parse_known_args(arguments)
|
||||||
sys.exit(os.EX_USAGE)
|
sys.exit(os.EX_USAGE)
|
||||||
|
for n, arg in enumerate(arguments):
|
||||||
|
if arg == "-c" or arg == "--clip":
|
||||||
|
arguments[n] = "--clip=1"
|
||||||
|
elif arg == "-q" or arg == "--qrcode":
|
||||||
|
arguments[n] = "--qrcode=1"
|
||||||
|
|
||||||
|
|
||||||
global_opts, args = parser_for_discrimination.parse_known_args(arguments)
|
global_opts, args = parser_for_discrimination.parse_known_args(arguments)
|
||||||
@ -375,11 +417,7 @@ elif opts.subcommand == "show":
|
|||||||
ret, stdout = pass_read("get", opts.key, return_stdout=True)
|
ret, stdout = pass_read("get", opts.key, return_stdout=True)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
sys.exit(ret)
|
sys.exit(ret)
|
||||||
stdout = stdout.decode("utf-8")
|
clipqrcodeexit(opts, stdout)
|
||||||
if opts.clip:
|
|
||||||
sys.exit(clip(stdout, opts.key))
|
|
||||||
elif opts.qrcode:
|
|
||||||
sys.exit(qrcode(stdout, opts.key))
|
|
||||||
else:
|
else:
|
||||||
sys.exit(pass_read("get", opts.key))
|
sys.exit(pass_read("get", opts.key))
|
||||||
elif opts.subcommand in ("mv", "cp"):
|
elif opts.subcommand in ("mv", "cp"):
|
||||||
@ -423,10 +461,7 @@ elif opts.subcommand in ("get-or-generate", "generate"):
|
|||||||
ret, stdout = pass_read("get", opts.key, **kwargs)
|
ret, stdout = pass_read("get", opts.key, **kwargs)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
sys.exit(ret)
|
sys.exit(ret)
|
||||||
if opts.clip:
|
clipqrcodeexit(opts, stdout)
|
||||||
sys.exit(clip(stdout.decode("utf-8"), opts.key))
|
|
||||||
elif opts.qrcode:
|
|
||||||
sys.exit(qrcode(stdout.decode("utf-8"), opts.key))
|
|
||||||
sys.exit(ret)
|
sys.exit(ret)
|
||||||
|
|
||||||
with open(os.devnull, "w") as null:
|
with open(os.devnull, "w") as null:
|
||||||
@ -437,11 +472,7 @@ elif opts.subcommand in ("get-or-generate", "generate"):
|
|||||||
doit()
|
doit()
|
||||||
elif ret == 0:
|
elif ret == 0:
|
||||||
if opts.subcommand == "get-or-generate":
|
if opts.subcommand == "get-or-generate":
|
||||||
if opts.clip:
|
clipqrcodeexit(opts, stdout)
|
||||||
sys.exit(clip(stdout.decode("utf-8"), opts.key))
|
|
||||||
elif opts.qrcode:
|
|
||||||
sys.exit(qrcode(stdout.decode("utf-8"), opts.key))
|
|
||||||
else:
|
|
||||||
sys.stdout.buffer.write(stdout)
|
sys.stdout.buffer.write(stdout)
|
||||||
sys.exit(ret)
|
sys.exit(ret)
|
||||||
else: # generate
|
else: # generate
|
||||||
|
Loading…
x
Reference in New Issue
Block a user