aboutsummaryrefslogblamecommitdiffstats
path: root/pinentry-rofi.sh
blob: 0eb3e4423d1732ace2eb722bfd14d095d7e38860 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
           
       
 










                                                                          
 


                                                                                       
                                                                                                     

                                                              












                                                                      
               
 


                                      
 
                                                 
 



                               
 
                                
 


                               
 

          



























                                                                                                    


                          





                                                                     
                                      



                                      

                                         


                                                                              





                                                  
                              

                        
















                                                                 
                                            
                                             
                                                          




                                                    




                                                                                                            




                                                                 
                                         
                                                                                     

                                                                                               
                                                          


                                           
                              




                                             
                                                                          



                                                    
                                                                          



                                                                  

                                          
                                        
                                            
          
                               
                                     

                                          
                                                                   







                                                                   
                                                             






                                     
#!/bin/bash
set -eu

# Using inspiration from
#    https://github.com/plattfot/pinentry-rofis
#    by (c) 2018-2023 Fredrik Salomonsson <plattfot <at> posteo <dot> net>
# which was using inspiration from
#    https://gist.github.com/sardemff7/759cbf956bea20d382a6128c641d2746
#    by (c) 2016 Quentin "Sardem FF7" Glidic
# however no line of code was actually taken from either script
# (they are in different language anyway).

# Copyright (c) 2023 Matěj Cepl <mcepl <at> cepl <dot> eu>
# Licensed under the terms of the include license in the file LICENSE.

# 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 "D $$"
        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]" && line_arr=("${line_arr[@]}")
        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("<", "&lt;").gsub(/%([0-9A-Fa-f]{2})/) { $1.to_i(16).chr }
        IFS=" " read -ra line_arr <<< "$line"
        unset "line_arr[0]" && line_arr=("${line_arr[@]}")
        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 | tr -s " \t\n" " ")"
        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][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
        log_debug "${rofi_cmd}"
        passw="$(eval "${rofi_cmd}")"
        passw_err=$?
        if [[ ${passw_err} -ne 0 ]] ; then
            # assuan_send "ERR 83886179 Operation cancelled <rofi>"
            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