From f02bc9f08d04477062d426f5fe6ebdccbb6fe47d Mon Sep 17 00:00:00 2001 From: Ultimatespirit Date: Wed, 18 Sep 2024 18:07:00 -0400 Subject: [PATCH] Rewrites `pinentry` find code to be more resilient This change rewrites the custom `pinentry` search code to instead be a modified form of the standard `/usr/bin/pinetry` fallback script. The prior behaviour could not handle cases where a `pinentry` executable existed but was not actually usable. Now it checks using `ldd` for if an executable is functional or not. Additionally rewritten to be more clear and easier to extend with newer `pinentry` backends. `make test` was ran, and it passed the main usability tests for password entry. As my testing system uses swap it failed during the KDF test from an error from swap being enabled; I did not attempt further tests with swap disabled. This partially fixes Issue #542. --- tomb | 89 ++++++++++++++++++++++++++---------------------------------- 1 file changed, 39 insertions(+), 50 deletions(-) diff --git a/tomb b/tomb index a3265675..b4f7566a 100755 --- a/tomb +++ b/tomb @@ -487,12 +487,17 @@ ask_password() { local gtkrc local theme local pass_asked + local backends # Distributions have broken wrappers for pinentry: they do # implement fallback, but they disrupt the output somehow. We are - # better off relying on less intermediaries, so we implement our - # own fallback mechanisms. Pinentry supported: curses, gtk-2, qt4, qt5 - # and x11. + # better off relying on less intermediaries, so we copy in the + # fallback logic directly here. + # Pinentry supported: curses, tty, qt{,4,5}, gtk{,-2}, gnome3, and x11. + + # TODO: Implement a user option to specify a pinentry program and + # wrap pinentry with a `_pinentry()` function that implements the + # search logic if the user option is not specified: Issue #542 # make sure LANG is set, default to C LANG=${LANG:-C} @@ -501,55 +506,39 @@ ask_password() { pass_asked=0 - while true; do - [[ ! -z $WAYLAND_DISPLAY ]] && { - _verbose "wayland display detected" - _is_found "pinentry-gnome3" && { - _verbose "using pinentry-gnome3 on wayland" - output=$(pinentry_assuan_getpass | pinentry-gnome3) - break; } - # TODO: pinentry on KDE running in wayland? - } - [[ ! -z $DISPLAY ]] && [[ $pass_asked == 0 ]] && { - _verbose "X11 display detected" - _is_found "pinentry-gtk-2" && { - _verbose "using pinentry-gtk2" - output=$(pinentry_assuan_getpass | pinentry-gtk-2) - break; } - _is_found "pinentry-x11" && { - _verbose "using pinentry-x11" - output=$(pinentry_assuan_getpass | pinentry-x11) - break; } - _is_found "pinentry-gnome3" && { - _verbose "using pinentry-gnome3 on X11" - output=$(pinentry_assuan_getpass | pinentry-gnome3) - break; } - _is_found "pinentry-qt5" && { - _verbose "using pinentry-qt5" - output=$(pinentry_assuan_getpass | pinentry-qt5) - break; } - _is_found "pinentry-qt4" && { - _verbose "using pinentry-qt4" - output=$(pinentry_assuan_getpass | pinentry-qt4) - break; } - } - _verbose "no display detected" - _is_found "pinentry-curses" && { - _verbose "using pinentry-curses with no display" - output=$(pinentry_assuan_getpass | pinentry-curses) - break; } - _is_found "pinentry-tty" && { - _verbose "using pinentry-tty with no display" - output=$(pinentry_assuan_getpass | pinentry-tty) - break; } - # TODO: fallback using read -s - and beware - # using read with or without -r may break - # passwords, so this must be covered by a test - # for compatibility - _failure "Cannot find any pinentry and no DISPLAY detected." - exit 1 + # Guess preferred backend based on environment. + backends=(curses tty) + if [[ -n "$DISPLAY" || -n "$WAYLAND_DISPLAY" ]]; then + _verbose "Graphical display system detected" + case "$XDG_CURRENT_DESKTOP" in + KDE|LXQT|LXQt) + backends=(qt5 qt qt4 gnome3 gtk gtk-2 curses tty) + ;; + *) + backends=(gtk gtk-2 x11 gnome3 qt5 qt qt4 curses tty) + ;; + esac + fi + _verbose "Checking backends '${backends[@]}'" + + for backend in "${backends[@]}"; do + local pinentry + local lddout + pinentry="$(which pinentry-$backend)" + lddout=$(ldd "$pinentry" 2>/dev/null) || continue + [[ "$lddout" == *'not found'* ]] && continue + _verbose "using $pinentry" + output=$(pinentry_assuan_getpass | $pinentry) + pass_asked=1 + break done + # TODO: fallback using read -s - and beware using read with or + # without -r may break passwords, so this must be covered by a test + # for compatibility + [[ $pass_asked == 0 ]] && + _failure "Cannot find any viable pinentry." + # parse the pinentry output local pinentry_error for i in ${(f)output}; do