Install scriptlets source root users .bashrc when pacman is run through sudo
Hi, recently I had very weird behavior when running pacman from an unprivileged user account using sudo compared to running `sudo -i` and then running pacman from the root user on one of my Arch Linux systems. In the first case, post_upgrade, post_install or similar where NOT run, in the second case they ran fine. I was very confused by that, spent quite some time staring at pacman.conf, /etc/sudoers and /etc/sudo.conf comparing those to machines where this weird behavior did not happen and not spotting any differences... I then had the idea to `strace -f` both cases, and found the following: When running `sudo pacman ...` the bash process spawned by pacman to execute the install script reads and executes "/root/.bashrc" and I had some old leftover code in there which basicly did `exit 0` (my default shell is zsh, so that went unnoticed) thus exiting bash before it could execute the packages install script. When executing pacman from a root shell (even when gained by `sudo -i` for example) the spawned bash does not source "/root/.bashrc". (Which is the behavior I'd expect, since it should be a non-interactive bash process, right?) I've added a simple testcase below that shows both behaviors. So, I guess in the first case bash considers itself to be interactive somehow even if it should not? I've got no idea how it could come to that conclusion... IIRC, one usually checks if stdin is a tty, pty or something like that and bash does indeed something like that if I read this correctly: https://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n523 Though having '-c' should even short-cuircuit that "if" to not even try to check for ttys. But maybe it does something weird later... Also in _alpm_run_chroot() I see that stdin, stdout and stderr get closed and then connected to pipes, before execv() so they couldn't even be a tty or pty... or can they? But then they would have to be even when pacman is not run through sudo... I'd guess. So I'm kind of stuck at figuring out why that happens. Anyone of you seeing something I'm missing here? I did of course remove that old code from that .bashrc which caused the initial problem, so package install scripts run fine now. But pacman should maybe try to disable/remove such side effects if it can? root@pc # cat /root/.bashrc echo "This is /root/.bashrc !" user@pc % sudo pacman -U installtest-1-1-x86_64.pkg.tar.zst loading packages... warning: installtest-1-1 is up to date -- reinstalling resolving dependencies... looking for conflicting packages... Package (1) Old Version New Version Net Change installtest 1-1 1-1 0.00 MiB :: Proceed with installation? [Y/n] (1/1) checking keys in keyring [######################] 100% (1/1) checking package integrity [######################] 100% (1/1) loading package files [######################] 100% (1/1) checking for file conflicts [######################] 100% (1/1) checking available disk space [######################] 100% :: Processing package changes... (1/1) reinstalling installtest [######################] 100% This is /root/.bashrc ! post_upgrade 1-1 1-1 :: Running post-transaction hooks... (1/1) Update pacman-ps data user@pc % sudo -i root@pc # pacman -U installtest-1-1-x86_64.pkg.tar.zst loading packages... warning: installtest-1-1 is up to date -- reinstalling resolving dependencies... looking for conflicting packages... Package (1) Old Version New Version Net Change installtest 1-1 1-1 0.00 MiB :: Proceed with installation? [Y/n] (1/1) checking keys in keyring [######################] 100% (1/1) checking package integrity [######################] 100% (1/1) loading package files [######################] 100% (1/1) checking for file conflicts [######################] 100% (1/1) checking available disk space [######################] 100% :: Processing package changes... (1/1) reinstalling installtest [######################] 100% post_upgrade 1-1 1-1 :: Running post-transaction hooks... (1/1) Update pacman-ps data user@pc % cat PKGBUILD pkgname=installtest pkgver=1 pkgrel=1 arch=(x86_64) install=installtest.install package() { true } user@pc % cat installtest.install post_upgrade() { echo "post_upgrade" "$@" } post_install() { echo "post_install" "$@" } post_remove() { echo "post_remove" "$@" } -- regards, brainpower
/sigh man 1 bash: Bash attempts to determine when it is being run with its standard input connected to a network connection, as when executed by the remote shell daemon, usually rshd, or the secure shell daemon sshd. If bash determines it is being run in this fashion, it reads and executes commands from ~/.bashrc, if that file exists and is readable. It will not do this if invoked as sh. The --norc option may be used to inhibit this behavior, and the --rcfile option may be used to force another file to be read, but neither rshd nor sshd generally invoke the shell with those options or allow them to be specified. bash thinks it's being run remotely because we connect to the child over sockets. On 11/06/21 at 05:25pm, brainpower via pacman-dev wrote:
Hi,
recently I had very weird behavior when running pacman from an unprivileged user account using sudo compared to running `sudo -i` and then running pacman from the root user on one of my Arch Linux systems.
In the first case, post_upgrade, post_install or similar where NOT run, in the second case they ran fine. I was very confused by that, spent quite some time staring at pacman.conf, /etc/sudoers and /etc/sudo.conf comparing those to machines where this weird behavior did not happen and not spotting any differences...
I then had the idea to `strace -f` both cases, and found the following:
When running `sudo pacman ...` the bash process spawned by pacman to execute the install script reads and executes "/root/.bashrc" and I had some old leftover code in there which basicly did `exit 0` (my default shell is zsh, so that went unnoticed) thus exiting bash before it could execute the packages install script.
When executing pacman from a root shell (even when gained by `sudo -i` for example) the spawned bash does not source "/root/.bashrc". (Which is the behavior I'd expect, since it should be a non-interactive bash process, right?)
I've added a simple testcase below that shows both behaviors.
So, I guess in the first case bash considers itself to be interactive somehow even if it should not? I've got no idea how it could come to that conclusion... IIRC, one usually checks if stdin is a tty, pty or something like that and bash does indeed something like that if I read this correctly: https://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n523 Though having '-c' should even short-cuircuit that "if" to not even try to check for ttys. But maybe it does something weird later...
Also in _alpm_run_chroot() I see that stdin, stdout and stderr get closed and then connected to pipes, before execv() so they couldn't even be a tty or pty... or can they? But then they would have to be even when pacman is not run through sudo... I'd guess.
So I'm kind of stuck at figuring out why that happens.
Anyone of you seeing something I'm missing here?
Am 06.11.21 um 19:15 schrieb Andrew Gregory:
bash thinks it's being run remotely because we connect to the child over sockets.
oh, right, that makes sense... I knew I was missing something rather obvious. :/ (I shouldn't have just skimmed the manpage after all... I missed it b/c I was only looking for the word "interactive", which isn't in that paragraph) But what I'm still not quite getting is, what's the difference of pacman being run by sudo vs. directly? Shouldn't bash also think the sockets were remote if pacman is not run using sudo? But it probably can detect somehow that it runs inside a interactive zsh when pacman is run directly, while there is no shell running between sudo and pacman when running it using sudo... or something like that. Well mystery somewhat solved, thanks for pointing me in the right direction. -- regards, brainpower
On 11/06/21 at 09:08pm, brainpower wrote:
Am 06.11.21 um 19:15 schrieb Andrew Gregory:
bash thinks it's being run remotely because we connect to the child over sockets.
oh, right, that makes sense... I knew I was missing something rather obvious. :/ (I shouldn't have just skimmed the manpage after all... I missed it b/c I was only looking for the word "interactive", which isn't in that paragraph)
But what I'm still not quite getting is, what's the difference of pacman being run by sudo vs. directly? Shouldn't bash also think the sockets were remote if pacman is not run using sudo?
But it probably can detect somehow that it runs inside a interactive zsh when pacman is run directly, while there is no shell running between sudo and pacman when running it using sudo... or something like that.
Well mystery somewhat solved, thanks for pointing me in the right direction.
I'd guess it's because it also checks for $SHLVL < 2.
Am 06.11.21 um 21:34 schrieb Andrew Gregory:
On 11/06/21 at 09:08pm, brainpower wrote:
Am 06.11.21 um 19:15 schrieb Andrew Gregory:
bash thinks it's being run remotely because we connect to the child over sockets.
oh, right, that makes sense... I knew I was missing something rather obvious. :/ (I shouldn't have just skimmed the manpage after all... I missed it b/c I was only looking for the word "interactive", which isn't in that paragraph)
But what I'm still not quite getting is, what's the difference of pacman being run by sudo vs. directly? Shouldn't bash also think the sockets were remote if pacman is not run using sudo?
But it probably can detect somehow that it runs inside a interactive zsh when pacman is run directly, while there is no shell running between sudo and pacman when running it using sudo... or something like that.
Well mystery somewhat solved, thanks for pointing me in the right direction.
I'd guess it's because it also checks for $SHLVL < 2.
It does! I've checked by adding 'Defaults env_keep += "SHLVL"' to /etc/sudoers. Would it make sense to export/set SHLVL=1 in _alpm_run_chroot() if SHLVL does not exist before executing the shell to make sure its non-interactive? I've first thought of adding --norc, but I think that's a non standard option not all shells users may configure pacman to use support... Exporting that variable if it does not exist should work with every shell and also should not hurt non-bash shells, I think? -- regards, brainpower
On 11/06/21 at 10:43pm, brainpower wrote:
Am 06.11.21 um 21:34 schrieb Andrew Gregory:
On 11/06/21 at 09:08pm, brainpower wrote:
Am 06.11.21 um 19:15 schrieb Andrew Gregory:
bash thinks it's being run remotely because we connect to the child over sockets.
oh, right, that makes sense... I knew I was missing something rather obvious. :/ (I shouldn't have just skimmed the manpage after all... I missed it b/c I was only looking for the word "interactive", which isn't in that paragraph)
But what I'm still not quite getting is, what's the difference of pacman being run by sudo vs. directly? Shouldn't bash also think the sockets were remote if pacman is not run using sudo?
But it probably can detect somehow that it runs inside a interactive zsh when pacman is run directly, while there is no shell running between sudo and pacman when running it using sudo... or something like that.
Well mystery somewhat solved, thanks for pointing me in the right direction.
I'd guess it's because it also checks for $SHLVL < 2.
It does! I've checked by adding 'Defaults env_keep += "SHLVL"' to /etc/sudoers.
Would it make sense to export/set SHLVL=1 in _alpm_run_chroot() if SHLVL does not exist before executing the shell to make sure its non-interactive?
I've first thought of adding --norc, but I think that's a non standard option not all shells users may configure pacman to use support... Exporting that variable if it does not exist should work with every shell and also should not hurt non-bash shells, I think?
The SHLVL check isn't documented anywhere that I can see, I just happened to find it in bash's source, so it's not something we should rely on. I'd reach out to the bash developer first and see what they say.
participants (2)
-
Andrew Gregory
-
brainpower