feat: pass for WSL
This commit is contained in:
parent
e9e5ea9d67
commit
8ae722f037
470
bin/pinentry-wsl-ps1.sh
Executable file
470
bin/pinentry-wsl-ps1.sh
Executable file
|
@ -0,0 +1,470 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# pinentry-wsl-ps1
|
||||
#
|
||||
# (c) 2018 Dale Phurrough
|
||||
# Licensed under the Mozilla Public License 2.0
|
||||
#
|
||||
# Allows GnuPG to prompt and read passphrases by the pinentry standard
|
||||
# with a GUI when running within WSL (Windows Subsystem for Linux).
|
||||
# Works for all keys managed by gpg-agent (GPG, SSH, etc).
|
||||
# This is a drop-in GUI alternative to pinentry-curses, pinentry-gtk-2, etc.
|
||||
# https://www.gnupg.org/software/pinentry/index.html
|
||||
#
|
||||
# Setup:
|
||||
# 1. Save this script and set its permissions to be executable
|
||||
# 2. Configure gpg-agent to use this script for pinentry using
|
||||
# one of the following methods:
|
||||
# a) Set pinentry-program within ~/.gnupg/gpg-agent.conf
|
||||
# pinentry-program /mnt/c/repos/pinentry-wsl-ps1/pinentry-wsl-ps1.sh
|
||||
# b) Set the path to this script when you launch gpg-agent
|
||||
# gpg-agent --pinentry-program /mnt/c/repos/pinentry-wsl-ps1/pinentry-wsl-ps1.sh
|
||||
# 3. Optionally enable persistence of passwords.
|
||||
# Requires https://github.com/davotronic5000/PowerShell_Credential_Manager
|
||||
# Please follow instructions there to install from the Gallery or GitHub.
|
||||
# Note security perspectives like https://security.stackexchange.com/questions/119765/how-secure-is-the-windows-credential-manager
|
||||
# Possible values for PERSISTENCE are: "", "Session", "LocalMachine", or "Enterprise"
|
||||
# 4. Optionally disable toast notification of password retrieval from Credential Manager.
|
||||
# By default, this code notifies you with a toast notification every time gpg-agent
|
||||
# retrieves a password from the Windows Credential Manager. Gpg-agent caches passwords
|
||||
# by default (see gpg-agent settings like max-cache-ttl) so you may not see the notification
|
||||
# with every usage.
|
||||
# * Disable: edit the script, near the top, set NOTIFY to the value "0"
|
||||
# * Enable: edit the script, near the top, set NOTIFY to the value "1"
|
||||
PERSISTENCE=""
|
||||
NOTIFY="1"
|
||||
DEBUGLOG=""
|
||||
|
||||
# Do not casually edit the below values
|
||||
VERSION="0.2.1"
|
||||
TIMEOUT="0"
|
||||
DESCRIPTION="Enter password for GPG key"
|
||||
PROMPT="Password:"
|
||||
TITLE="GPG Key Credentials"
|
||||
CACHEPREFIX="gpgcache:"
|
||||
CACHEUSER=""
|
||||
KEYINFO=""
|
||||
OKBUTTON="&OK"
|
||||
CANCELBUTTON="&Cancel"
|
||||
NOTOKBUTTON="&Do not do this"
|
||||
PINERROR=""
|
||||
EXTPASSCACHE="0"
|
||||
REPEATPASSWORD="0"
|
||||
REPEATDESCRIPTION="Confirm password for GPG key"
|
||||
REPEATERROR="Error: Passwords did not match."
|
||||
GRABKEYBOARD="0"
|
||||
|
||||
# convert Assuan protocol error into an ERR number, e.g. echo -n $(( (5 << 24) | $1 ))
|
||||
assuan_result() {
|
||||
case $1 in
|
||||
0)
|
||||
echo -n "ERR 0 no error"
|
||||
;;
|
||||
62)
|
||||
echo -n "ERR 83886142 timeout"
|
||||
;;
|
||||
99)
|
||||
echo -n "ERR 83886179 cancelled"
|
||||
;;
|
||||
114)
|
||||
echo -n "ERR 83886194 not confirmed"
|
||||
;;
|
||||
174)
|
||||
echo -n "ERR 83886254 invalid option"
|
||||
;;
|
||||
257)
|
||||
echo -n "ERR 83886337 general error"
|
||||
;;
|
||||
261)
|
||||
echo -n "ERR 83886341 invalid value"
|
||||
;;
|
||||
275)
|
||||
echo -n "ERR 83886355 unknown command"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# GUI dialogs for passwords; text is dynamically set by gpg-agent via protocol
|
||||
getpassword() {
|
||||
if [ -n "$CACHEUSER" ]; then
|
||||
local creduser="$CACHEUSER"
|
||||
else
|
||||
if [ -n "$KEYINFO" ]; then
|
||||
local creduser="$KEYINFO"
|
||||
else
|
||||
local creduser="--not yet defined--"
|
||||
fi
|
||||
fi
|
||||
local cmd_prompt=$(cat <<-DLM
|
||||
\$cred = \$Host.ui.PromptForCredential("$TITLE",
|
||||
"$PINERROR$DESCRIPTION",
|
||||
"$creduser",
|
||||
"",
|
||||
"Generic",
|
||||
"None,ReadOnlyUserName")
|
||||
if (\$cred) {
|
||||
Write-Output \$cred.GetNetworkCredential().Password
|
||||
}
|
||||
DLM
|
||||
)
|
||||
local cmd_repeat=$(cat <<-DLM
|
||||
\$cred = \$Host.ui.PromptForCredential("$TITLE",
|
||||
"$REPEATDESCRIPTION",
|
||||
"$creduser",
|
||||
"",
|
||||
"Generic",
|
||||
"None,ReadOnlyUserName")
|
||||
if (\$cred) {
|
||||
Write-Output \$cred.GetNetworkCredential().Password
|
||||
}
|
||||
DLM
|
||||
)
|
||||
local cmd_lookup=$(cat <<-DLM
|
||||
\$cred = Get-StoredCredential -Target "$CACHEPREFIX$KEYINFO" -Type GENERIC
|
||||
if (\$cred) {
|
||||
Write-Output \$cred.GetNetworkCredential().Password
|
||||
}
|
||||
DLM
|
||||
)
|
||||
local cmd_store=$(cat <<-DLM
|
||||
\$pw = \$Input | Select-Object -First 1
|
||||
\$securepw = ConvertTo-SecureString \$pw -AsPlainText -Force
|
||||
New-StoredCredential -Target "$CACHEPREFIX$KEYINFO" -Type GENERIC -UserName "$creduser" -SecurePassword \$securepw -Persist $PERSISTENCE |
|
||||
out-null
|
||||
DLM
|
||||
)
|
||||
# idea from http://thewindowscollege.com/display-toast-notifications-windows-10.html
|
||||
# alt1: https://gist.github.com/loge5/7ec41e2e2f0e0293fdcc5155499e9072
|
||||
# alt2: https://gist.github.com/Windos/9aa6a684ac583e0d38a8fa68196bc2dc
|
||||
local cmd_toast=$(cat <<-DLM
|
||||
[reflection.assembly]::loadwithpartialname("System.Windows.Forms")
|
||||
[reflection.assembly]::loadwithpartialname("System.Drawing")
|
||||
\$notify = new-object system.windows.forms.notifyicon
|
||||
\$notify.icon = [System.Drawing.SystemIcons]::Information
|
||||
\$notify.visible = \$true
|
||||
\$notify.showballoontip(10, "GPG pinentry-wsl-ps1", "GPG password retrieved from Windows Credential Manager", [system.windows.forms.tooltipicon]::Info)
|
||||
DLM
|
||||
)
|
||||
local credpassword
|
||||
local credpasswordrepeat
|
||||
local passwordfromcache=0
|
||||
if [ -z "$PINERROR" ]; then
|
||||
if [ "$REPEATPASSWORD" == "0" ]; then
|
||||
if [ "$EXTPASSCACHE" == "1" ]; then
|
||||
if [ -n "$KEYINFO" ]; then
|
||||
credpassword="$(powershell.exe -nologo -noprofile -noninteractive -command "$cmd_lookup")"
|
||||
if [ -n "$credpassword" ]; then
|
||||
echo -e "S PASSWORD_FROM_CACHE\nD $credpassword\nOK"
|
||||
if [ "$NOTIFY" == "1" ]; then
|
||||
powershell.exe -nologo -noprofile -noninteractive -command "$cmd_toast" > /dev/null
|
||||
fi
|
||||
return
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
PINERROR=""
|
||||
credpassword="$(powershell.exe -nologo -noprofile -noninteractive -command "$cmd_prompt")"
|
||||
if [ -n "$credpassword" ]; then
|
||||
if [ "$REPEATPASSWORD" == "1" ]; then
|
||||
credpasswordrepeat="$(powershell.exe -nologo -noprofile -noninteractive -command "$cmd_repeat")"
|
||||
if [ "$credpassword" == "$credpasswordrepeat" ]; then
|
||||
echo -e "S PIN_REPEATED\nD $credpassword\nOK"
|
||||
else
|
||||
message "$REPEATERROR" > /dev/null
|
||||
echo "$(assuan_result 114)" # unsure this is the correct error
|
||||
return
|
||||
fi
|
||||
else
|
||||
echo -e "D $credpassword\nOK"
|
||||
fi
|
||||
if [ "$EXTPASSCACHE" == "1" ]; then
|
||||
if [ -n "$KEYINFO" ]; then
|
||||
# avoid setting password on visible param
|
||||
# alt is to always save on the single or last-of-repeat dialog. And if the repeat fails, then immediately delete it from the cred store
|
||||
builtin echo -n "$credpassword" | powershell.exe -nologo -noprofile -noninteractive -command "$cmd_store"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "$(assuan_result 99)"
|
||||
fi
|
||||
}
|
||||
|
||||
# remove password from persistent store
|
||||
removepassword() {
|
||||
if [ -z "$1" ]; then
|
||||
echo "$(assuan_result 261)"
|
||||
return
|
||||
fi
|
||||
local cmd_remove=$(cat <<-DLM
|
||||
try {
|
||||
Remove-StoredCredential -Target "$CACHEPREFIX$1" -Type GENERIC -ErrorAction Stop
|
||||
}
|
||||
catch {
|
||||
Write-Output "$(assuan_result 261)"
|
||||
return
|
||||
}
|
||||
Write-Output "OK"
|
||||
DLM
|
||||
)
|
||||
if [ "$EXTPASSCACHE" == "1" ]; then
|
||||
echo "$(powershell.exe -nologo -noprofile -noninteractive -command "$cmd_remove")"
|
||||
else
|
||||
echo "OK"
|
||||
fi
|
||||
}
|
||||
|
||||
# GUI dialog box with simple message and one OK button
|
||||
message() {
|
||||
local desc
|
||||
if [ -n "$1" ]; then
|
||||
desc="$1"
|
||||
else
|
||||
desc="$DESCRIPTION"
|
||||
fi
|
||||
local cmd=$(cat <<-DLM
|
||||
\$wshShell = New-Object -ComObject WScript.Shell
|
||||
\$options = 0x0 + 0x40 + 0x2000 + 0x10000 # 1 + 16 + 8192 + 65536
|
||||
\$result = \$wshShell.Popup("$desc", $TIMEOUT, "$TITLE", \$options)
|
||||
[System.Runtime.Interopservices.Marshal]::ReleaseComObject(\$wshShell) | Out-Null
|
||||
DLM
|
||||
)
|
||||
local result="$(powershell.exe -nologo -noprofile -noninteractive -command "$cmd")" #> /dev/null
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
# GUI dialog box with test and two buttons: OK, Cancel
|
||||
confirm() {
|
||||
PINERROR=""
|
||||
if [ "$1" == "--one-button" ]; then
|
||||
message
|
||||
return
|
||||
fi
|
||||
local cmd=$(cat <<-DLM
|
||||
\$wshShell = New-Object -ComObject WScript.Shell
|
||||
\$options = 0x1 + 0x30 + 0x2000 + 0x10000 # 1 + 16 + 8192 + 65536
|
||||
\$result = \$wshShell.Popup("$DESCRIPTION", $TIMEOUT, "$TITLE", \$options)
|
||||
[System.Runtime.Interopservices.Marshal]::ReleaseComObject(\$wshShell) | Out-Null
|
||||
if (\$result) {
|
||||
switch(\$result) {
|
||||
1 { Write-Output "OK" }
|
||||
2 { Write-Output "$(assuan_result 99)" }
|
||||
default { Write-Output "$(assuan_result 114)" }
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Output "$(assuan_result 114)"
|
||||
}
|
||||
DLM
|
||||
)
|
||||
local result="$(powershell.exe -nologo -noprofile -noninteractive -command "$cmd")"
|
||||
echo "$result"
|
||||
}
|
||||
|
||||
# set a timeout value in seconds after which prompts/dialogs are automatically cancelled
|
||||
# limited functionality in current codebase
|
||||
# potential improvements at https://stackoverflow.com/questions/21176487/adding-a-timeout-to-batch-powershell
|
||||
settimeout() {
|
||||
TIMEOUT="$1"
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
# helper function for decoding strings from gpg-agent into Windows-compatible format
|
||||
decodegpgagentstr() {
|
||||
local decode="${1//%0A/%0D%0A}" # convert hex LF into hex Windows CRLF
|
||||
decode="${decode//%/\\x}" # convert hex encoding style
|
||||
decode="$(echo -en "$decode")" # decode hex
|
||||
echo -n "${decode//\"/\`\"}" # escape double quotes for powershell
|
||||
}
|
||||
|
||||
# commonly used to set main text in GUI dialog boxes
|
||||
# also parses for key ids to display in GUI prompts
|
||||
setdescription() {
|
||||
DESCRIPTION="$(decodegpgagentstr "$1")"
|
||||
local searchfor='ID ([[:xdigit:]]{16})' # hack to search for first gpg key id in description
|
||||
if [[ "$1" =~ $searchfor ]]; then
|
||||
CACHEUSER="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
local searchfor='(([[:xdigit:]][[:xdigit:]]:){15}[[:xdigit:]][[:xdigit:]])' # hack to search for ssh fingerprint in description
|
||||
if [[ "$1" =~ $searchfor ]]; then
|
||||
CACHEUSER="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
setprompt() {
|
||||
PROMPT="$1"
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
settitle() {
|
||||
TITLE="$1"
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
setpinerror() {
|
||||
PINERROR="$(decodegpgagentstr "** $1 **")"$'\r'$'\n' # decode and add CRLF to separate line
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
setkeyinfo() {
|
||||
if [ "$1" == "--clear" ]; then
|
||||
KEYINFO=""
|
||||
else
|
||||
KEYINFO="$1"
|
||||
fi
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
setrepeatpassword() {
|
||||
REPEATPASSWORD="1"
|
||||
REPEATDESCRIPTION="$(decodegpgagentstr "$1")"
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
setrepeaterror () {
|
||||
REPEATERROR="$(decodegpgagentstr "$1")"
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
setokbutton() {
|
||||
OKBUTTON="${1//_/&}"
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
setcancelbutton() {
|
||||
CANCELBUTTON="${1//_/&}"
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
setnotokbutton() {
|
||||
NOTOKBUTTON="${1//_/&}"
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
getinfo() {
|
||||
if [ "$1" == "version" ]; then
|
||||
echo -e "D $VERSION\nOK"
|
||||
elif [ "$1" == "pid" ]; then
|
||||
echo -e "D $BASHPID\nOK"
|
||||
else
|
||||
echo "$(assuan_result 275)"
|
||||
fi
|
||||
}
|
||||
|
||||
# often called by gpg-agent to set default values
|
||||
setoption() {
|
||||
local key="$(echo "$1" | cut -d'=' -f1)"
|
||||
local value="$(echo "$1" | cut -d'=' -s -f2-)"
|
||||
case $key in
|
||||
allow-external-password-cache)
|
||||
if [ -n "$PERSISTENCE" ]; then
|
||||
EXTPASSCACHE=1
|
||||
fi
|
||||
echo "OK"
|
||||
;;
|
||||
default-ok)
|
||||
setokbutton "$value"
|
||||
;;
|
||||
default-cancel)
|
||||
setcancelbutton "$value"
|
||||
;;
|
||||
default-notok)
|
||||
setnotokbutton "$value"
|
||||
;;
|
||||
default-prompt)
|
||||
setprompt "$value"
|
||||
;;
|
||||
grab)
|
||||
GRABKEYBOARD="1"
|
||||
echo "OK"
|
||||
;;
|
||||
no-grab)
|
||||
GRABKEYBOARD="0"
|
||||
echo "OK"
|
||||
;;
|
||||
*)
|
||||
echo "OK"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# check that we are running within WSL
|
||||
if ! cat /proc/sys/kernel/osrelease | grep -q -i Microsoft; then
|
||||
echo "$(assuan_result 257)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# main loop to read stdin and respond
|
||||
echo "OK Your orders please"
|
||||
while IFS= read -r line; do
|
||||
if [ -n "$DEBUGLOG" ]; then
|
||||
echo "$line" >> "$DEBUGLOG"
|
||||
fi
|
||||
action="$(echo $line | cut -d' ' -f1)"
|
||||
args="$(echo $line | cut -d' ' -s -f2-)"
|
||||
case $action in
|
||||
BYE)
|
||||
echo "OK closing connection"
|
||||
exit 0
|
||||
;;
|
||||
GETPIN)
|
||||
getpassword
|
||||
;;
|
||||
SETTIMEOUT)
|
||||
settimeout "$args"
|
||||
;;
|
||||
SETDESC)
|
||||
setdescription "$args"
|
||||
;;
|
||||
SETPROMPT)
|
||||
setprompt "$args"
|
||||
;;
|
||||
SETTITLE)
|
||||
settitle "$args"
|
||||
;;
|
||||
SETKEYINFO)
|
||||
setkeyinfo "$args"
|
||||
;;
|
||||
SETOK)
|
||||
setokbutton "$args"
|
||||
;;
|
||||
SETCANCEL)
|
||||
setcancelbutton "$args"
|
||||
;;
|
||||
SETNOTOK)
|
||||
setnotokbutton "$args"
|
||||
;;
|
||||
CONFIRM)
|
||||
confirm "$args"
|
||||
;;
|
||||
MESSAGE)
|
||||
message "$args"
|
||||
;;
|
||||
SETERROR)
|
||||
setpinerror "$args"
|
||||
;;
|
||||
GETINFO)
|
||||
getinfo "$args"
|
||||
;;
|
||||
OPTION)
|
||||
setoption "$args"
|
||||
;;
|
||||
SETREPEAT)
|
||||
setrepeatpassword "$args"
|
||||
;;
|
||||
SETREPEATERROR)
|
||||
setrepeaterror "$args"
|
||||
;;
|
||||
CLEARPASSPHRASE)
|
||||
removepassword "$args"
|
||||
;;
|
||||
RESET)
|
||||
echo "OK"
|
||||
;;
|
||||
*)
|
||||
echo "OK"
|
||||
;;
|
||||
esac
|
||||
done
|
3
bin/wsl-win-path.sh
Executable file
3
bin/wsl-win-path.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
cmd.exe /c 'echo '$1 2>/dev/null | awk '{sub("C:", "/mnt/c"); gsub("\\\\","/"); gsub("\r$", ""); print}'
|
5
env.sh
5
env.sh
|
@ -8,6 +8,7 @@ XDG_CONFIG_HOME=${XDG_CONFIG_HOME-"$HOME/.config"}
|
|||
PREFIX=/usr/local
|
||||
PDIR=$(dirname "${DIR-$0}")
|
||||
GITHUB_PROXY=${GITHUB_PROXY-$HTTPS_PROXY}
|
||||
WSL=$(grep -i Microsoft /proc/sys/kernel/osrelease)
|
||||
|
||||
in_china() {
|
||||
! [ -f /tmp/myip_full ] && curl -s myip.ipip.net > /tmp/myip_full
|
||||
|
@ -94,6 +95,10 @@ pm_update() {
|
|||
echo "$CHECKSUM" > "$TSFILE"
|
||||
}
|
||||
|
||||
win_env_path() {
|
||||
cmd.exe /c 'echo '$1 2>/dev/null | awk '{sub("C:", "/mnt/c"); gsub("\\\\","/"); print}'
|
||||
}
|
||||
|
||||
|
||||
sudo mkdir -p $PREFIX
|
||||
|
||||
|
|
17
gui/pass.sh
17
gui/pass.sh
|
@ -11,10 +11,18 @@ fi
|
|||
# install pass
|
||||
case "$PM" in
|
||||
apt)
|
||||
sudo apt install -y pass pinentry-gtk2
|
||||
sudo apt install -y pass
|
||||
GNUPG=$HOME/.gnupg
|
||||
GPG_AGENT_CONF=$GNUPG/gpg-agent.conf
|
||||
PINENTRY=$(command -v pinentry-gtk-2)
|
||||
if [ -n "$WSL" ]; then
|
||||
PINENTRY=$PDIR/bin/pinentry-wsl-ps1.sh
|
||||
BROWSERPASS_NATIVE= "$(wsl-win-path.sh %USERPROFILE%)/browser-wsl.bat"
|
||||
echo "@echo off\r\nbash -c 'browserpass'" \
|
||||
> "$BROWSERPASS_NATIVE"
|
||||
else
|
||||
sudo apt install -y pinentry-gtk2
|
||||
PINENTRY=$(command -v pinentry-gtk-2)
|
||||
fi
|
||||
SETTING="pinentry-program $PINENTRY"
|
||||
mkdir -p "$GNUPG"
|
||||
if ! grep -Fq "$SETTING" "$GPG_AGENT_CONF"; then
|
||||
|
@ -49,3 +57,8 @@ cd -
|
|||
# [PasswordStore - sync / ui](https://f-droid.org/packages/dev.msfjarvis.aps/)
|
||||
|
||||
# configuration
|
||||
|
||||
if [ -n "$WSL" ]; then
|
||||
echo "Please update path in *-host.json file located at C:\Program Files\Browserpass to"
|
||||
echo $BROWSERPASS_NATIVE
|
||||
fi
|
||||
|
|
Loading…
Reference in New Issue
Block a user