From 83b054b8c1c14ac7c67f9b1850964c7dd47b9132 Mon Sep 17 00:00:00 2001 From: Matěj Cepl Date: Thu, 3 Aug 2023 13:12:33 +0200 Subject: First steps --- pinentry-rofi-ABANDONED.sh | 189 +++++++++++++++++++++++++++++++++++++++++++++ pinentry-rofi.sh | 65 ++++++---------- 2 files changed, 214 insertions(+), 40 deletions(-) create mode 100755 pinentry-rofi-ABANDONED.sh mode change 100644 => 100755 pinentry-rofi.sh diff --git a/pinentry-rofi-ABANDONED.sh b/pinentry-rofi-ABANDONED.sh new file mode 100755 index 0000000..45e8d4e --- /dev/null +++ b/pinentry-rofi-ABANDONED.sh @@ -0,0 +1,189 @@ +#!/bin/bash +set -eu + +# SPDX-FileCopyrightText: 2016 Quentin "Sardem FF7" Glidic +# SPDX-FileCopyrightText: 2018-2023 Fredrik Salomonsson +# +# SPDX-License-Identifier: GPL-3.0-or-later + +# Some documentation +# https://info2html.sourceforge.net/cgi-bin/info2html-demo/info2html?(pinentry)Protocol +# https://superuser.com/a/1655428 +# TODO https://superuser.com/questions/1457167/i-want-to-make-pinentry-use-gui-locally-and-cli-on-ssh +# Even better +# https://github.com/gpg/libassuan/blob/master/doc/assuan.texi + +# Although it is called a PIN-Entry, it does allow to enter reasonably +# long strings (at least 2048 characters are supported by every +# pinentry). + +# The client using the PIN-Entry has to check for correctness. + +# Note that all strings are expected to be encoded as UTF-8; +# PINENTRY takes care of converting it to the locally used codeset. +# To include linefeeds or other special characters, you may +# percent-escape them (i.e. a line feed is encoded as `%0A', the +# percent sign itself is encoded as `%25'). + +VERSION="0.0.1" + +log_debug() { + echo "$@" >> /tmp/pinentry-log.txt +} + +rofi_cmd="rofi -dmenu -input /dev/null -password" + +assuan_send() { + log_debug "assuan_send: $*" + echo "$@" +} + +assuan_send "OK Please go ahead" + +win_title="Prompt for password" +win_prompt="Password" +win_mesg="" + +keyinfo="" + +# gpg-agent[676]: DBG: chan_9 -> OK Pleased to meet you, process 3073 +# gpg-agent[676]: DBG: chan_9 <- RESET +# gpg-agent[676]: DBG: chan_9 -> OK +# gpg-agent[676]: DBG: chan_9 <- OPTION ttyname=/dev/pts/0 +# gpg-agent[676]: DBG: chan_9 -> OK +# gpg-agent[676]: DBG: chan_9 <- OPTION ttytype=xterm-256color +# gpg-agent[676]: DBG: chan_9 -> OK +# gpg-agent[676]: DBG: chan_9 <- OPTION display=:0 +# gpg-agent[676]: DBG: chan_9 -> OK +# gpg-agent[676]: DBG: chan_9 <- OPTION putenv=WAYLAND_DISPLAY=wayland-1 +# gpg-agent[676]: DBG: chan_9 -> OK +# gpg-agent[676]: DBG: chan_9 <- OPTION putenv=XDG_SESSION_TYPE=wayland +# gpg-agent[676]: DBG: chan_9 -> OK +# gpg-agent[676]: DBG: chan_9 <- OPTION putenv=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus +# gpg-agent[676]: DBG: chan_9 -> OK +# gpg-agent[676]: DBG: chan_9 <- OPTION lc-ctype=en_US.UTF-8 +# gpg-agent[676]: DBG: chan_9 -> OK +# gpg-agent[676]: DBG: chan_9 <- OPTION lc-messages=cs_CZ.utf8 +# gpg-agent[676]: DBG: chan_9 -> OK +# gpg-agent[676]: DBG: chan_9 <- GETINFO version +# gpg-agent[676]: DBG: chan_9 -> D 2.3.8 +# gpg-agent[676]: DBG: chan_9 -> OK +# gpg-agent[676]: DBG: chan_9 <- OPTION allow-pinentry-notify +# gpg-agent[676]: DBG: chan_9 -> OK +# gpg-agent[676]: DBG: chan_9 <- OPTION agent-awareness=2.1.0 +# gpg-agent[676]: DBG: chan_9 -> OK +# gpg-agent[676]: DBG: chan_9 <- KEYINFO 8EA96F4B4BD616DA44A2E297F31C0448A74B9D33 + +while : ; do + read -r line + log_debug "line=$line" + # Set options for the connection. The syntax of such a line is + # OPTION name [ [=] value ] + # Leading and trailing spaces around name and value are + # allowed but should be ignored. For compatibility reasons, name + # may be prefixed with two dashes. The use of the equal sign + # is optional but suggested if value is given. + if [[ "$line" =~ ^OPTION ]] ; then + # OPTION grab + # OPTION ttyname=/dev/pts/1 + # OPTION ttytype=tmux-256color + # OPTION lc-messages=C + assuan_send "OK" + elif [[ "$line" =~ ^GETINFO ]] ; then + # https://www.gnupg.org/documentation/manuals/gnupg/Agent-GETINFO.html + # version or pid of this script? + # gpg-agent --version works but it must be filtered + IFS=" " read -ra line_arr <<< "$line" + subcommand=${line_arr[1]} + log_debug "subcommand=${subcommand}" + if [[ "$subcommand" == "version" ]] ; then + assuan_send "D ${VERSION}" + elif [[ "$subcommand" == "pid" ]] ; then + assuan_send f"D {os.getpid()}" + fi + assuan_send "OK" + # This command is reserved for future extensions. + # True NOOP + elif [[ "$line" =~ ^CANCEL ]] ; then + assuan_send "OK" + # This command is reserved for future extensions. Not yet + # specified as we don't implement it in the first phase. See + # Werner's mail to gpa-dev on 2001-10-25 about the rationale + # for measurements against local attacks. + # True NOOP + elif [[ "$line" =~ ^AUTH ]] ; then + assuan_send "OK" + # And this actually is NOOP + elif [[ "$line" =~ ^NOP ]] ; then + assuan_send "OK" + elif [[ "$line" =~ ^KEYINFO ]] ; then + assuan_send "${keyinfo}" + assuan_send "OK" + elif [[ "$line" =~ ^SETKEYINFO ]] ; then + IFS=" " read -ra line_arr <<< "$line" + unset "line_arr[0]" + if [[ "${line_arr[0]}" =~ ^--clear ]] ; then + keyinfo="" + else + keyinfo="${line_arr[*]}" + fi + assuan_send "OK" + elif [[ "$line" =~ ^SETOK|^SETNOTOK|^SETERROR|^SETCANCEL|^SETTIMEOUT|^SETQUALITYBAR|^SETGENPIN ]] ; then + assuan_send "OK" + elif [[ "$line" =~ ^CONFIRM|^MESSAGE ]] ; then + assuan_send "OK" + # Reset the connection but not any existing authentication. + # The server should release all resources associated with the + # connection. + elif [[ "$line" =~ ^RESET ]] ; then + assuan_send "OK" + elif [[ "$line" =~ ^SETDESC ]] ; then + #SETDESC Please enter the passphrase for the ssh key%0A ke:yf:in:ge:rp:ri:nt + # rofi << "-mesg" << $1.gsub("<", "<").gsub(/%([0-9A-Fa-f]{2})/) { $1.to_i(16).chr } + IFS=" " read -ra line_arr <<< "$line" + unset "line_arr[0]" + win_mesg="${line_arr[*]}" + assuan_send "OK" + elif [[ "$line" =~ ^SETPROMPT ]] ; then + #SETPROMPT Passphrase: + IFS=" " read -ra line_arr <<< "$line" + win_prompt="${line_arr[1]}" + assuan_send "OK" + elif [[ "$line" =~ ^GETPIN ]] ; then + passw=None + sys_env="$(systemctl --user show-environment)" + IFS=" " read -ra sys_env_arr <<< "$sys_env" + for env_line in "${sys_env_arr[@]}" ; do + log_debug "env_line=${env_line}" + # GPIN_VALID=re.compile(r) + if [[ "$env_line" =~ ^[A-Za-z_]+=\(.\+\)$ ]] ; then + log_debug "env_match=${BASH_REMATCH[*]}" + export "${BASH_REMATCH[1]}=${BASH_REMATCH[2]}" + fi + done + + rofi_cmd+=" -p ${win_prompt}" + rofi_cmd+=" -title ${win_title}" + if [[ -n "${win_mesg}" ]] ; then + rofi_cmd+=" -mesg ${win_mesg}" + fi + passw="$(eval "${rofi_cmd}")" + passw_err=$? + if [[ ${passw_err} -ne 0 ]] ; then + # assuan_send "ERR 83886179 Operation cancelled " + log_debug "rofi failed to run: ${passw} (${passw_err})" + exit $passw_err + else + if [[ -n ${passw} ]] ; then + assuan_send "D ${passw}" + fi + fi + assuan_send "OK" + # Close the connection. The server will respond with OK. + elif [[ ${line} =~ ^BYE ]] ; then + exit 0 + else + assuan_send "BYE" + exit 1 + fi +done diff --git a/pinentry-rofi.sh b/pinentry-rofi.sh old mode 100644 new mode 100755 index 4ca14d0..f956799 --- a/pinentry-rofi.sh +++ b/pinentry-rofi.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +set -eux # pinentry-wsl-ps1 # @@ -19,12 +20,12 @@ # 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 -# WSL-only 3. Optionally enable persistence of passwords. -# WSL-only Requires https://github.com/davotronic5000/PowerShell_Credential_Manager -# WSL-only Please follow instructions there to install from the Gallery or GitHub. -# WSL-only Note security perspectives like https://security.stackexchange.com/questions/119765/how-secure-is-the-windows-credential-manager -# WSL-only Possible values for PERSISTENCE are: "", "Session", "LocalMachine", or "Enterprise" -PERSISTENCE="" +# PORT 3. Optionally enable persistence of passwords. +# PORT Requires https://github.com/davotronic5000/PowerShell_Credential_Manager +# PORT Please follow instructions there to install from the Gallery or GitHub. +# PORT Note security perspectives like https://security.stackexchange.com/questions/119765/how-secure-is-the-windows-credential-manager +# PORT Possible values for PERSISTENCE are: "", "Session", "LocalMachine", or "Enterprise" +# PORT PERSISTENCE="" DEBUGLOG="" # Do not casually edit the below values @@ -46,6 +47,9 @@ REPEATDESCRIPTION="Confirm password for GPG key" REPEATERROR="Error: Passwords did not match." GRABKEYBOARD="0" + +rofi_cmd="rofi -dmenu -input /dev/null -password" + # convert Assuan protocol error into an ERR number, e.g. echo -n $(( (5 << 24) | $1 )) assuan_result() { case $1 in @@ -87,6 +91,8 @@ getpassword() { local creduser="--not yet defined--" fi fi + # $rofi_cmd -title $TITLE -p "$PINERROR$DESCRIPTION" + # Put somewhere $creduser local cmd_prompt=$(cat <<-DLM \$cred = \$Host.ui.PromptForCredential("$TITLE", "$PINERROR$DESCRIPTION", @@ -111,31 +117,19 @@ DLM } DLM ) - local cmd_lookup=$(cat <<-DLM - \$cred = Get-StoredCredential -Target "$CACHEPREFIX$KEYINFO" -Type GENERIC - if (\$cred) { - Write-Output \$cred.GetNetworkCredential().Password - } -DLM +# PORT local cmd_lookup=$(cat <<-DLM +# PORT \$cred = Get-StoredCredential -Target "$CACHEPREFIX$KEYINFO" -Type GENERIC +# PORT if (\$cred) { +# PORT Write-Output \$cred.GetNetworkCredential().Password +# PORT } +# PORT 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 +# PORT local cmd_store=$(cat <<-DLM +# PORT \$pw = \$Input | Select-Object -First 1 +# PORT \$securepw = ConvertTo-SecureString \$pw -AsPlainText -Force +# PORT New-StoredCredential -Target "$CACHEPREFIX$KEYINFO" -Type GENERIC -UserName "$creduser" -SecurePassword \$securepw -Persist $PERSISTENCE | +# PORT out-null +# PORT DLM ) local credpassword local credpasswordrepeat @@ -172,7 +166,7 @@ DLM 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" + # PORT builtin echo -n "$credpassword" | powershell.exe -nologo -noprofile -noninteractive -command "$cmd_store" fi fi else @@ -348,9 +342,6 @@ setoption() { 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) @@ -379,12 +370,6 @@ setoption() { 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 -- cgit