diff --git a/ChangeLog.md b/ChangeLog.md index 4b113c08..dac5df65 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,4 +1,20 @@ # Tomb ChangeLog + +## Unreleased +### TBA + +Special character sequences like newline (`\n`), tabfeed (`\t`) and +similar from ANSI Escape Sequences caused unexpected behaviour when +setting passwords. The safe way to pipe the passwords into `gnupg` +would go through the shell and if sequences were encountered +they would be processed. +Which brings unintended changes in the entered password. In the case +of a newline the user could end up with a much weaker password +than expected and not notice it necessarily. +With this release the password won't be changed by the shell pipe +anymore. `Pinentry` and `libassuan` still do their thing though. +For an overview see [Known Bugs](./KNOWN_BUGS.md). + ## 2.13 ### July 2025 diff --git a/KNOWN_BUGS.md b/KNOWN_BUGS.md index f76bfc80..ddfbe012 100644 --- a/KNOWN_BUGS.md +++ b/KNOWN_BUGS.md @@ -1,3 +1,41 @@ +# Passwords getting silently altered +## Issues with Tomb version x.x to 2.13 + +The safe way of piping a password into gnupg would be interpret by the shell +and therefore character sequences like `ANSI Escape Sequences` could cause +unintended changes to the password and not necessarily be noticeable. +But also escaping characters can introduce changes to the final password. +In general a tomb created with 2.13 and before can still be unlocked, but +it needs to happen with unsafe options. +Below is a table with some examples: + +| Sequence | Theoretical | Real | Unlock 2.13 | Unlock >2.13 | Notes | +|----------|-------------|------|-------------|--------------|-------| +| newline | `test\ntest` | `test` | `test` or `test\ntest` or `test\nwhatever` | `test` | Position matters greatly +| horizontal tab | `test\test` | `testest` | `test\test` | `--tomb-pwd "test\test"` | +| formfeed | `test\ftest` | `test\xCtest` | `test\ftest` | `--tomb-pwd "test\ftest"` | dm-crypt will throw a warning | +| bell | `test\atest` | `test\x07test` | `test\atest` | `--tomb-pwd "test\atest"` | bell will appear in output +| backspace | `test\btest` | theoretically `testest`? | `test\btest` | `--tomb-pwd "test\btest"` | behaves unexpectedly +| vertical tab | `test\vtest` | `testtest` | `test\vtest` | `--tomb-pwd "test\vtest"` | +| carriage return | `test\rtest` | `testtest` | `test\rtest` | `--tomb-pwd "test\rtest"` | +| escape character (wrong) | `test\etest` | in shell `testest` | `test\etest` | `--tomb-pwd "test\etest"` | behaves unexpectedly +| escape character | `test\e[1Ktest` | in shell ` test` | `test\e[1Ktest` | `--tomb-pwd "test\e[1Ktest"` | erase from start to cursor +| escpape backslash | `testtest\` | - | - | - | created key is safely protected... + +It isn't explored what happened in cases like `\ntest` or `test\`. +In the first case would `test` be discarded or end up as the `LUKS` key? +Essentially the same question for the second case: +What does the `LUKS` key look like after the password was appended with the +intended key? +The general advice is to generate a new tomb to be on the safe side. + +Of note: This doesn't address the quirk with `%`s in a password. +Pinentry or rather Assuan have the requirement, that some special sequences +need to be percent escaped (`%` as `%25`, `CR` as `%0D` and `LF` as `%0A`). +If such a password was set via pinentry and is later supplied via +`--unsafe --tomb-pwd` it will need to be set as `--tomb-pwd test%25test`. + + # Password bug failing to open tombs ## Issue with Tomb version 2.12 (short lived) diff --git a/doc/tomb.1 b/doc/tomb.1 index 19631791..5a1ea85b 100644 --- a/doc/tomb.1 +++ b/doc/tomb.1 @@ -384,7 +384,8 @@ the non-blocking source of Linux kernel doesn't degrades the quality of random. .B .IP "--tomb-pwd " -Use string as password when needed on tomb. +Use string as password when needed on tomb. Refer to Password Input +for edge cases. .B .IP "--tomb-old-pwd " Use string as old password when needed in tomb commands requiring @@ -473,6 +474,10 @@ When using it from a remote ssh connection it might be necessary to force use of pinentry-tty for instance by unsetting the DISPLAY (X11) or WAYLAND_DISPLAY (Wayland) environment var. +If using \fI--tomb-pwd\fR special care must be taken with passwords that +contain what pinentry/assuan considers special sequences. One of those is +the percent symbol (\fI%\fR) which needs to be manually escaped as (\fI%25\fR). + .SH SWAP On execution of certain commands Tomb will complain about swap memory diff --git a/tomb b/tomb index 58ab90e0..8f359174 100755 --- a/tomb +++ b/tomb @@ -581,7 +581,7 @@ ask_password() { return 1 } - print "$password" + print -R -n - "$password" return 0 } @@ -1294,12 +1294,12 @@ _load_key() { # contains tweaks for different gpg versions # support both symmetric and asymmetric encryption gpg_decrypt() { - # fix for gpg 1.4.11 where the --status-* options don't work ;^/ + # gpg version check necessary for <2.1.x (although those aren't supported anymore) local gpgver=$(gpg --version --no-permission-warning | awk '/^gpg/ {print $3}') - local gpgpass="$1\n$TOMBKEY" + local gpgpass="$1" local tmpres ret typeset -a gpgopt - gpgpopt=(--batch --no-tty --passphrase-fd 0 --no-options) + gpgpopt=(--batch --no-tty --passphrase-fd 3 --no-options --no-mdc-warning --no-permission-warning --no-secmem-warning) { option_is_set -g } && { gpgpass="$TOMBKEY" @@ -1316,21 +1316,11 @@ gpg_decrypt() { } } - [[ $gpgver == "1.4.11" ]] && { - _verbose "GnuPG is version 1.4.11 - adopting status fix." - TOMBSECRET=`print - "$gpgpass" | \ - gpg --decrypt ${gpgpopt[@]}` - ret=$? - unset gpgpass - return $ret - } - _tmp_create tmpres=$TOMBTMP - TOMBSECRET=`print - "$gpgpass" | \ - gpg --decrypt ${gpgpopt[@]} \ - --status-fd 2 --no-mdc-warning --no-permission-warning \ - --no-secmem-warning 2> $tmpres` + TOMBSECRET=`print - "$TOMBKEY" | \ + gpg --decrypt ${gpgpopt[@]} --status-fd 2 \ + 3<<<"$gpgpass" 2> $tmpres` unset gpgpass ret=1 for i in ${(f)"$(cat $tmpres)"}; do @@ -1338,7 +1328,6 @@ gpg_decrypt() { [[ "$i" =~ "DECRYPTION_OKAY" ]] && ret=0; done return $ret - } @@ -1860,18 +1849,16 @@ gen_key() { fi # Set gpg inputs and options - gpgpass="${tombpass}\n$TOMBSECRET" - gpgopt=(--passphrase-fd 0 --symmetric --no-options) + gpgopt=(--passphrase-fd 3 --symmetric --no-options) opt='-n' fi _tmp_create local tmpres=$TOMBTMP - print $opt - "$gpgpass" \ + print $opt - "$TOMBSECRET" \ | gpg --openpgp --force-mdc --cipher-algo ${algo} \ - --batch --no-tty ${gpgopt} \ - --status-fd 2 -o - --armor 2> $tmpres >> "$1" - unset gpgpass + --batch --no-tty ${gpgopt} --status-fd 2 -o - \ + --armor 3<<<"$tombpass" 2> $tmpres >> "$1" # check result of gpg operation for i in ${(f)"$(cat $tmpres)"}; do _verbose "$i" @@ -1879,7 +1866,7 @@ gen_key() { # print -n "${tombpass}" \ # | gpg --openpgp --force-mdc --cipher-algo ${algo} \ - # --batch --no-options --no-tty --passphrase-fd 0 --status-fd 2 \ + # --batch --no-options --no-tty --passphrase-fd 3 --status-fd 2 \ # -o - -c -a ${lukskey} TOMBPASSWORD="$tombpass" # Set global variable