Skip to content

Drop workaround for gnupg 1.4.11 #581

@Narrat

Description

@Narrat

The 1.4 release of gnupg is long time deprecated old and according to upstream shouldn't be used anymore (but it isn't EOL).
Various distros still have the package for compatibility reasons, but even Debian switched the name of the binary to gpg1. Even the case with Bullseye.
So in theory the majority should already use gpg2 as default.
Is there any distro around, which still ships gpg1 as gpg? (Hopefully not)

Reason being that it would allow to In my opinion we now can drop the gpg1 gnupg 1.4.11 specific workaround.
With that gone the gpg call could be restructured without worries about breaking something, as this early return is a nuisance.

What could be changed? There is still #304 around. And for the gpg call password and key are still combined via \n and piped into it.
There are in theory three options to retrieve a password with two being considered questionable

   --passphrase-fd n
          Read the passphrase from file descriptor n. Only the first line will
          be  read  from file descriptor n. If you use 0 for n, the passphrase
          will be read  from  STDIN.  This  can  only  be  used  if  only  one
          passphrase  is  supplied.  Note that this passphrase is only used if
          the option --batch has also been given.  This is different from gpg.

   --passphrase-file file
          Read the passphrase from file file. Only the first line will be read
          from file file. This can only be used if only one passphrase is sup‐
          plied. Obviously, a passphrase stored in a file is  of  questionable
          security if other users can read this file. Don't use this option if
          you can avoid it.  Note that this passphrase is  only  used  if  the
          option --batch has also been given.  This is different from gpg.

   --passphrase string
          Use  string  as  the  passphrase.  This can only be used if only one
          passphrase is supplied. Obviously,  this  is  of  very  questionable
          security  on  a  multi-user system. Don't use this option if you can
          avoid it.  Note that this passphrase is  only  used  if  the  option
          --batch has also been given.  This is different from gpg.

The first is being used, the third would be the easiest. But without verifying if the password would appear in ps or whatnot it shouldn't be used :D

Another way would be to change the file descriptor for $password
The resulting function could look like this (untested):

gpg_decrypt() {
        # gpgver could be removed if 2.0 if of no concern?
	local gpgver=$(gpg --version --no-permission-warning | awk '/^gpg/ {print $3}')
	local gpgpass="$1"
	local tmpres ret
	typeset -a gpgopt
	gpgpopt=(--batch --no-tty --passphrase-fd 3 --no-options)

	{ option_is_set -g } && {
		gpgpass="$TOMBKEY"
		gpgpopt=(--yes)

		# GPG option '--try-secret-key' exist since GPG 2.1
		{ option_is_set -R } && { autoload -U is-at-least && is-at-least "2.1" $gpgver } && {
			typeset -a recipients
			recipients=(${(s:,:)$(option_value -R)})
			{ is_valid_recipients $recipients } || {
				_failure "You set an invalid GPG ID."
			}
			gpgpopt+=(`_recipients_arg "--try-secret-key" $recipients`)
		}
	}

	_tmp_create
	tmpres=$TOMBTMP
	TOMBSECRET=`print - "$TOMBKEY" | \
		gpg --decrypt ${gpgpopt[@]} \
			--status-fd 2 --no-mdc-warning --no-permission-warning \
			--no-secmem-warning 3<<<"$gpgpass" 2> $tmpres`
	unset gpgpass
	ret=1
	for i in ${(f)"$(cat $tmpres)"}; do
		_verbose "$i"
		[[ "$i" =~ "DECRYPTION_OKAY" ]] && ret=0;
	done
	return $ret
}

Alternatively a anonymous pipe could be utilized in combination with --passphrase-file (untested):

gpg_decrypt() {
	# again only needed if 2.0 is of concern
	local gpgver=$(gpg --version --no-permission-warning | awk '/^gpg/ {print $3}')
	local gpgpass="$1"
	local tmpres ret
	typeset -a gpgopt
	gpgpopt=(--batch --no-tty --no-options)

	{ option_is_set -g } && {
		gpgpass="$TOMBKEY"
		gpgpopt=(--yes)

		# GPG option '--try-secret-key' exist since GPG 2.1
		{ option_is_set -R } && { autoload -U is-at-least && is-at-least "2.1" $gpgver } && {
			typeset -a recipients
			recipients=(${(s:,:)$(option_value -R)})
			{ is_valid_recipients $recipients } || {
				_failure "You set an invalid GPG ID."
			}
			gpgpopt+=(`_recipients_arg "--try-secret-key" $recipients`)
		}
	}

	_tmp_create
	tmpres=$TOMBTMP
	TOMBSECRET=`print - "$TOMBKEY" | \
		gpg --decrypt ${gpgpopt[@]} \
			--status-fd 2 --no-mdc-warning --no-permission-warning \
			--no-secmem-warning --passphrase-file <(print -R -n - "$gpgpass") 2> $tmpres`
	unset gpgpass
	ret=1
	for i in ${(f)"$(cat $tmpres)"}; do
		_verbose "$i"
		[[ "$i" =~ "DECRYPTION_OKAY" ]] && ret=0;
	done
	return $ret
}

This will make the password available via a file descriptor (as an anonymous pipe normally as /dev/fd/63). The lifetime is bound to the runtime of gpg. And only gpg should be able to read or write from this location. And regarding the visibility: It should only show /dev/fd/63 in the process monitor tools.

Light testing via those commands:

$ zmodload zsh/mapfile
$ FILECONT=${mapfile[test.key]}
$ print - "$FILECONT" | gpg --decrypt --batch --no-tty --no-options --status-fd 0 --passphrase-file <(echo "test")
gpg: AES256.CFB verschlüsselte Daten
gpg: Verschlüsselt mit einem Passwort
(...)
$ print - "$FILECONT" | gpg --decrypt --batch --no-tty --no-options --status-fd 0 --passphrase-fd 3 3<<<"test"
gpg: AES256.CFB verschlüsselte Daten
gpg: Verschlüsselt mit einem Passwort
(...)                                                      

But before looking further that, I would like to get rid of the restriction gpg1 :D

Edit: Some additional remarks for the POC section.
This is a pure zsh script, so it should use print instead of echo or printf (which also can have serious issues as I noticed).
Additionally zsh enables per default the interpretation of control sequences, opposit to bash. Therefore it would require echo -E or print -r.
And lastly: avoid adding a newline to the output (-n).
Need still to verify what redirect string does with control sequences.

Edit2: Reworded the proposal, as it doesn't remove the support for gnupg 1.4.x in general. It would only loose support for 1.4.11 which is really old and not the latest release of the 1.4.x series.
Additionally fix the snippets, as I got around testing them with tomb.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions