#!/usr/bin/env mksh
rcsid='$MirOS: contrib/hosted/tg/code/kwalletcli/pinentry-kwallet,v 1.16 2016/08/30 17:37:59 tg Exp $'
#-
# Copyright © 2009, 2010, 2011
# mirabilos <m@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
# are retained or reproduced in an accompanying document, permission
# is granted to deal in this work without restriction, including un‐
# limited rights to use, publicly perform, distribute, sell, modify,
# merge, give away, or sublicence.
#
# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
# the utmost extent permitted by applicable law, neither express nor
# implied; without malicious intent or gross negligence. In no event
# may a licensor, author or contributor be held liable for indirect,
# direct, other damage, loss, or other issues arising in any way out
# of dealing in the work, even if advised of the possibility of such
# damage or existence of a defect, except proven that it results out
# of said person’s immediate fault when using the work as intended.
unset LC_ALL LANGUAGE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES \
LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION
export LANG=C LC_CTYPE=en_US.UTF-8
set -U
iodebug=0
iodp=~/pinentry-kwallet.debug
if (( iodebug )); then
print "\n$$ === new $(date)" >>"$iodp"
chmod 0600 "$iodp"
fi
function io_p_in {
local io_line
IFS= read -r io_line || return $?
(( iodebug )) && print -r -- "$$ <p $io_line" >>"$iodp"
eval $1='${io_line%
}'
return 0
}
function io_s_in {
local io_line
IFS= read -pr io_line || return $?
(( iodebug )) && print -r -- "$$ <s $io_line" >>"$iodp"
eval $1=\$io_line
return 0
}
function io_p_out {
(( iodebug )) && print -r -- "$$ >p $(print "$@")" >>"$iodp"
print "$@"
}
function io_s_out {
(( iodebug )) && print -r -- "$$ >s $(print "$@")" >>"$iodp"
print -p "$@"
}
function log {
(( iodebug )) && print -r -- "$$ LOG $*" >>"$iodp"
}
if [[ -n $PINENTRY_KWALLET ]]; then
io_p_out ERR 7 trying to call me recursively
while io_p_in line; do
io_p_out ERR 7 trying to call me recursively
done
exit 1
fi
quiet=0
set -A args
last=
i=0
for arg in "$@"; do
log "argv[$((++i))]='${arg//\'/\'\\\'\'}'"
if [[ $last = --display ]]; then
v=DISPLAY
last=specvar
elif [[ $last = --ttyname ]]; then
v=GPG_TTY
last=specvar
elif [[ $last = --ttytype ]]; then
v=GPG_TERM
last=specvar
elif [[ $last = --lc-type ]]; then
v=LC_CTYPE
last=specvar
elif [[ $last = --lc-messages ]]; then
v=LC_MESSAGES
last=specvar
fi
if [[ $last = specvar ]]; then
eval $v=\$arg
eval export $v
last=addone
fi
if [[ $last = addone ]]; then
args[${#args[*]}]=$arg
last=
continue
fi
last=
if [[ $arg = -V ]]; then
(( quiet )) || print -ru2 -- "$rcsid"
exit 0
elif [[ $arg = --version ]]; then
print -r -- "$rcsid"
exit 0
elif [[ $arg = @(-h|--help) ]]; then
print "Usage: pinentry-kwallet [options]"
exit 0
elif [[ $arg = -q ]]; then
quiet=1
elif [[ $arg = \
@(-@(d|e|g)|--@(debug|enhanced|no-global-grab)) ]]; then
args[${#args[*]}]=$arg
elif [[ $arg = \
--@(display|ttyname|ttytype|lc-type|lc-messages) ]]; then
args[${#args[*]}]=$arg
last=$arg
elif [[ $arg = --parent-wid ]]; then
args[${#args[*]}]=$arg
last=addone
else
(( quiet )) || print -u2 "warning: unknown argument '$arg'"
args[${#args[*]}]=$arg
fi
done
if [[ -z $DISPLAY ]]; then
log "since DISPLAY is not set, replacing with: ${PINENTRY:-pinentry}"
PINENTRY_KWALLET=set exec "${PINENTRY:-pinentry}" "${args[@]}"
fi
PINENTRY_KWALLET=set "${PINENTRY:-pinentry}" "${args[@]}" |&
copid=$?
log "starting coproc $copid: PINENTRY_KWALLET=set '${PINENTRY:-pinentry}' args"
io_s_in resp || resp='ERR 14 no coprocess'
if [[ $resp = OK@(| *) ]]; then
have_sub=1
trap "kill $copid; exit 255" 1 2 3 5 13 15
else
have_sub=0
exec 3>&p; exec 3>&-
fi
log have_sub=$have_sub
io_p_out OK ready to listen to your demands
x_dsctxt=
x_prompt=
x_errtxt=
function getit {
local type=$1 key=〈${x_prompt}〉$x_dsctxt pw rv tw=0 d errcnt blst=0
copyline=0
# the errcnt handling is a little tricky, because GnuPG v2 does
# not reuse the pinentry session (suckers, unable to... *rant*)
if pw=$(kwalletcli -q -f pinentry-kwallet -e "$type-B-$key") && \
[[ $pw = yes* ]]; then
log "blacklisted"
blst=1
elif pw=$(kwalletcli -q -f pinentry-kwallet -e "$type-e-$key"); then
log "read errcnt: '$pw'"
set -A errcnt -- $pw
d=$(date -u +'%s')
(( errcnt[0] < (d - 15) )) && errcnt[1]=0
else
log "read errcnt failed"
fi
(( blst )) || [[ -z $x_errtxt ]] || (( errcnt[1]++ ))
(( blst )) || if (( errcnt[1] )); then
errcnt[0]=${d:-$(date -u +'%s')}
kwalletcli -q -f pinentry-kwallet -e "$type-e-$key" \
-p "${errcnt[*]}"
log "write errcnt: '${errcnt[*]}' -> $?"
fi
(( blst )) || if (( errcnt[1] < 2 )); then
pw=$(kwalletcli -q -f pinentry-kwallet -e "$type-v-$key")
rv=$?
log "read pass $rv: '$pw'"
case $type:$rv {
(bool:0)
if [[ $pw = \
@(1|-1|[Tt][Rr][Uu][Ee]|[Yy][Ee][Ss]) ]]; then
io_p_out OK
return
elif [[ $pw = \
@(0|[Ff][Aa][Ll][Ss][Ee]|[Nn][Oo]) ]]; then
io_p_out ERR 128 not confirmed
return
fi
;;
(pass:0)
[[ -n $pw ]] && io_p_out -r "D $pw"
io_p_out OK
return
;;
}
fi
if (( !have_sub )); then
io_p_out ERR 14 no coprocess
return
fi
if [[ $type = bool ]]; then
io_s_out CONFIRM
io_s_in resp
case $resp {
(OK@(| *))
pw=1
tw=1
;;
(ERR\ @(128|83886194|83886179)@(| *))
# 128 = not confirmed (hardy)
# 83886194 = not confirmed (wheezy)
# 83886179 = canceled (wheezy)
pw=0
tw=1
;;
}
else
io_s_out GETPIN
io_s_in resp
pw=
#XXX normally, read until OK|ERR
if [[ $resp = @(D )* ]]; then
pw=${resp#D }
io_s_in resp
fi
[[ $resp = OK@(| *) ]] && tw=1
fi
(( tw && !blst )) && if kwalletcli_getpin -q -b \
-t "Do you want to store your response for description
'$x_dsctxt',
prompt '$x_prompt' in the KDE Wallet?"; then
kwalletcli -q -f pinentry-kwallet -e "$type-v-$key" -p "$pw"
log "want store: yes, pw '$pw' -> $?"
else
# create blacklist entry for this answer
kwalletcli -q -f pinentry-kwallet -e "$type-B-$key" -p yes
log "want store: no"
fi
[[ $type = pass ]] && [[ -n $pw ]] && io_p_out -r "D $pw"
io_p_out -r -- "$resp"
}
while io_p_in line; do
[[ -z $line || $line = '#'* ]] && continue
copyline=1
case $line {
(SETDESC)
x_dsctxt=
;;
(SETDESC\ *)
x_dsctxt=${line#SETDESC }
;;
(SETPROMPT)
x_prompt=
;;
(SETPROMPT\ *)
x_prompt=${line#SETPROMPT }
;;
(SETTITLE@(| *))
;;
(SET@(OK|CANCEL|NOTOK)@(| *))
;;
(SETERROR)
x_errtxt=
;;
(SETERROR\ *)
x_errtxt=${line#SETERROR }
;;
(SETQUALITYBAR*)
;;
(GETPIN)
getit pass
;;
(CONFIRM)
getit bool
;;
(MESSAGE|CONFIRM\ --one-button)
;;
(OPTION\ ttyname=*)
GPG_TTY=${line#*=}
export GPG_TTY
;;
(OPTION\ ttytype=*)
GPG_TERM=${line#*=}
export GPG_TERM
;;
(OPTION\ lc-ctype=*)
LC_CTYPE=${line#*=}
export LC_CTYPE
;;
(OPTION\ lc-messages=*)
LC_MESSAGES=${line#*=}
export LC_MESSAGES
;;
(OPTION\ *)
;;
(CONFIRM\ *)
(( quiet )) || print -ru2 "warning: unknown CONFIRM" \
"option ${line#CONFIRM }"
;;
(BYE@(| *))
io_p_out OK
break
;;
(NOP@(| *))
# copy quietly, in case of keepalive
;;
(GETINFO\ pid)
# undocumented, but used by GnuPG v2
io_p_out D $$
io_p_out OK
copyline=0
;;
(GETINFO\ version)
# undocumented, but used by GnuPG v2
# just copy it quietly
#XXX maybe return "ERR 103 unknown command"?
;;
(GETINFO\ *)
# undocumented, but used by GnuPG v2
(( quiet )) || print -ru2 "warning: unknown GETINFO" \
"capability ${line#GETINFO }"
;;
(*)
(( quiet )) || print -ru2 "warning: unknown line '$line'"
;;
}
(( copyline )) && if (( have_sub )); then
io_s_out -r -- "$line"
resp=
while [[ $resp != @(OK|ERR)@(| *) ]]; do
io_s_in resp
io_p_out -r -- "$resp"
done
else
io_p_out OK
fi
done
if (( have_sub )); then
io_s_out BYE
io_s_in resp
fi
exec 2>/dev/null # avoid "no coprocess" warnings
exec 3>&p; exec 3>&-
exit 0