Bug 32269 - /etc/profile.d/99keychain.sh always executes keychain and cannot be disabled
Summary: /etc/profile.d/99keychain.sh always executes keychain and cannot be disabled
Status: NEW
Alias: None
Product: Mageia
Classification: Unclassified
Component: RPM Packages (show other bugs)
Version: 8
Hardware: All Linux
Priority: Normal normal
Target Milestone: ---
Assignee: All Packagers
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-09-11 00:12 CEST by Theodoros Kalamatianos
Modified: 2023-09-14 00:26 CEST (History)
3 users (show)

See Also:
Source RPM: keychain-2.8.5-3.mga8.src.rpm
CVE:
Status comment:


Attachments

Description Theodoros Kalamatianos 2023-09-11 00:12:51 CEST
Description of problem:

The /etc/profile.d/99keychain.sh script is sourced for all interactive shell instances and ends up unconditionally calling out to keychain on each Bash invocation, as long as ~/.keychain exists.

This creates two issues:

- The script runs with every single interactive invocation of Bash, which is a (small) waste of resources.

- It is currently impossible to disable this behavior on a per-session basis, e.g. to bring up a separate GnuPG or SSH agent. The only workaround is to disable keychain entirely by removing ~/.keychain.

As a comparison, the equivalent script from Fedora contains the following line at the top:


# Don't do anything if we're already done.
[ -n "$KEYCHAIN_DONE" ] && return


...with a conditional KEYCHAIN_DONE=1 statement near the end.


Version-Release number of selected component (if applicable):

keychain-2.8.5-3.mga8.src.rpm, but also present in the current Cauldron


How reproducible:

Always

Steps to Reproduce:
1. Run `bash -o xtrace` in a terminal
2. Inspect the output
3. Notice keychain being called
Comment 1 Dave Hodgins 2023-09-11 08:11:33 CEST
Bash only runs the profile scripts during a login shell, or when running
bash --login, not when being invoked after login such as with konsole or
gnome-terminal.

From man bash ...
INVOCATION
       A login shell is one whose first character of argument zero is a -, or one started with the --login option.

       An interactive shell is one started without non-option arguments (unless -s is specified) and without the -c option whose standard input and error are both connected to terminals (as determined  by
       isatty(3)), or one started with the -i option.  PS1 is set and $- includes i if bash is interactive, allowing a shell script or a startup file to test this state.

While the man page isn't very clear, I think have the -o xtrace is causing
it to be a login shell.

The following shows the difference between a non login shell an a login
shell ...
[dave@x3 ~]$ su dave
Password: 
History file is /home/dave/.bash_history.0
[dave@x3 ~]$ exit
exit
[dave@x3 ~]$ su -l dave
Password: 
History file is /home/dave/.bash_history.0
adding mageia ssh passphrase
Enter passphrase for .ssh/mageia: 
Identity added: .ssh/mageia (.ssh/mageia)

The bit about the history file comes from my .bashrc file.

CC: (none) => davidwhodgins

Comment 2 Theodoros Kalamatianos 2023-09-11 10:55:49 CEST
Thanks for looking into this Dave :)

This actually happens without xtrace; xtrace was just the simplest way I had to demonstrate the issue without too much fiddling. So, let's start from the basics, on my mga8 system. From bash(1):

"When an interactive shell that is not a login shell is started, bash reads and executes commands from ~/.bashrc, if that file exists."

OK, let's look at the default ~/.bashrc:


$ cat /etc/skel/.bashrc
# .bashrc

# User specific aliases and functions

# Source global definitions
if [ -f /etc/bashrc ]; then
	. /etc/bashrc
fi
$ rpmwp /etc/skel/.bashrc /etc/bashrc
bash-5.1-16.1.mga8
bash-5.1-16.1.mga8
$ rpm -V bash
$


So both files come from the bash package and are unmodified. Looking into /etc/bashrc, we have:


...
# are we an interactive shell?
if [ "$PS1" ]; then
...
    if [ -z "$loginsh" ]; then # We're not a login shell
	# Not all scripts in profile.d are compatible with other shells
	# TODO: make the scripts compatible or check the running shell by
	# themselves.
	if [ -n "${BASH_VERSION}${KSH_VERSION}${ZSH_VERSION}" ]; then
            for i in /etc/profile.d/*.sh; do
	        if [ -r $i ]; then
	            . $i
	        fi
	    done
	    unset i
	fi
    fi
fi

unset loginsh


I.e. if PS1 is set, which it is on interactive shells, and if $loginsh is empty which it will be on non-login shells (it's set in /etc/profile and unset by /etc/bashrc while the login shell is initializing), it will source all *.sh scripts in /etc/profile.d/. This, of course, includes 99keychain.sh.

Now, the keychain script will not touch the agents if they already exist (which is why you have no noise from it in a non-login shell, I think), but it still runs and it makes very fragile as-is to use a separate agent without some serious fiddling.

If you want, you can try the following:

{ echo; echo 'date >> ~/keychain.log'; } >> ~/.keychain/config

This will add a `date >> ~/keychain.log' logging command to the configuration file that is sourced by the keychain script. You will notice that you get new date entries appended to ~/keychain.log for every interactive shell, as happens if e.g. you type `bash' in an already existing shell.

I think we just need a sentinel variable in 99keychain.sh, like Fedora has. Taking a look at the Fedora keychain package as a whole might not be a bad idea either - their scripts seem simpler/different and the Mageia one has not been touched in 10 years, so there might be some historical cruft to clean up there.
Comment 3 Lewis Smith 2023-09-11 22:10:33 CEST
Unsure whether Dave will reply to that, but assigning to all packagers because this is a question of policy=dicussion before changing anything.

CC'ing the packagers who were most concerned, historically.

(In reply to Theodoros Kalamatianos from comment #0)
> Description of problem:
> The /etc/profile.d/99keychain.sh script is sourced for all interactive shell
> instances and ends up unconditionally calling out to keychain on each Bash
> invocation, as long as ~/.keychain exists.
> - It is currently impossible to disable this behavior on a per-session
> basis, e.g. to bring up a separate GnuPG or SSH agent. The only workaround
> is to disable keychain entirely by removing ~/.keychain.
This looks to be the essential problem (given that you could then "bring up a separate GnuPG or SSH agent").

Assignee: bugsquad => pkg-bugs
CC: (none) => geiger.david68210, mageia

Comment 4 Theodoros Kalamatianos 2023-09-14 00:26:44 CEST
FWIW, I think this should do the trick:


--- 99keychain.sh	2020-02-12 11:41:02.000000000 +0100
+++ 99keychain.sh.new	2023-09-14 00:24:26.029429663 +0200
@@ -16,6 +16,9 @@
 #     List of keys to add on startup. In this case do not try to guess
 #     keys here
 
+# Do not do anything if keychain has already been set up.
+[ -n "$KEYCHAIN_DONE" ] && return
+
 KEYCHAIN_OPTIONS=""
 KEYCHAIN_KEYS=""
 
@@ -54,3 +57,5 @@
 KEYCHAINFILEGPG=$HOME/.keychain/$HOSTNAME-sh-gpg
 
 [ -e $KEYCHAINFILEGPG ] && . $KEYCHAINFILEGPG
+
+KEYCHAIN_DONE=1

Note You need to log in before you can comment on or make changes to this bug.