1. Introduction

Shells are perhaps the main interface for Linux systems. Even without a graphical user interface (GUI), the shell facilitates full system control. Because of this and other reasons, our choice of default shell can be very important.

In this tutorial, we talk about an issue we may encounter when attempting to change the default shell via chsh. First, we briefly refresh our knowledge about default shell changes. After that, we understand how one of the ways to do that is influenced by a fundamental Linux security mechanism. Finally, we delve into the exact issue this mechanism can cause and ways to deal with it.

We tested the code in this tutorial on Debian 12 (Bookworm) with GNU Bash 5.2.15. It should work in most POSIX-compliant environments unless otherwise specified.

2. Change Default Shell

The default shell is the process that runs right after a user authenticates and logs in.

There are several ways to change the default shell. However, the most convenient is usually the chsh command, part of the util-linux package.

To begin with, let’s check the current default shell of a user via /etc/passwd:

$ cat /etc/passwd
[...]
fisher:x:1005:1005:,,,:/home/fisher:/bin/bash

In this case, user fisher has a default shell of /bin/bash. Naturally, fisher probably prefers the fish shell instead.

To facilitate that desire, we can use the –shell option of chsh and set a new default shell:

$ chsh --shell /usr/bin/fish fisher

Here, we set /usr/bin/fish as the default for user fisher.

Now, we can confirm the switch by checking the contents of /etc/passwd again:

$ cat /etc/passwd
[...]
fisher:x:1005:1005:,,,:/home/fisher:/usr/bin/fish

In this case, we see that the fisher user now employs /usr/bin/fish as their default shell.

To change the default shell of the current user, we just omit the username at the end of the command:

$ chsh --shell /usr/bin/fish

However, we may encounter an issue when running either of the above chsh commands.

3. chsh PAM Restrictions

Since a shell can take away or grant certain access, the use of chsh is mandated by a Pluggable Authentication Module (PAM).

As expected, the PAM settings for chsh are at /etc/pam.d/chsh:

$ cat /etc/pam.d/chsh
#
# The PAM configuration file for the Shadow `chsh' service
#

# This will not allow a user to change their shell unless
# their current one is listed in /etc/shells. This keeps
# accounts with special shells from changing them.
auth       required   pam_shells.so

# This allows root to change user shell without being
# prompted for a password
auth            sufficient      pam_rootok.so

# The standard Unix authentication modules, used with
# NIS (man nsswitch) as well as normal /etc/passwd and
# /etc/shadow entries.
@include common-auth
@include common-account
@include common-session

This fairly short configuration file contains two important lines:

  • auth required pam_shells.so verifies that the current shell of the authenticated user is listed in /etc/shells via the pam_shells.so module
  • auth sufficient pam_rootok.so checks whether the current user is root and stops further checks if so due to the sufficiency of the condition

In fact, the rest of the file only runs for non-root users to verify their account privileges and setup the session.

4. chsh Password Prompt and PAM: Authentication failure

For this section, let’s assume we work as root directly or via sudo. We indicate this with the # prompt.

4.1. Attempt Shell Change

First, we try to change the shell of fisher back to /bin/bash:

$ chsh --shell /bin/bash fisher
Password:

Unexpectedly, we get a Password: prompt.

Upon entering any password, regardless of whether it’s correct or not, we get a message:

# chsh --shell /bin/bash fisher
Password:
chsh: PAM: Authentication failure

Notably, this happens even when using sudo.

At this point, let’s take a step back and analyze the PAM conditions for chsh relative to our context.

4.2. PAM Check Analysis

Once we issue chsh as root, we can be certain that the second PAM condition for the command, pam_rootok.so passes by definition.

However, the first line of /etc/pam.d/chsh checks whether the shell of the user that runs chsh is in /etc/shells.

So, let’s check /etc/passwd and see what the shell of our current root user is:

# cat /etc/passwd
root:x:0:0:root:/root:fish
[...]

Seemingly, root uses the fish shell. Let’s map that to the /etc/shells file:

# cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/bin/dash
/usr/bin/dash
/usr/bin/tmux
/bin/zsh
/usr/bin/zsh
/usr/bin/screen
/bin/ksh93
/usr/bin/ksh93
/bin/rksh93
/usr/bin/rksh93
/bin/tcsh
/usr/bin/tcsh
/usr/bin/sh
/usr/bin/fish

Critically, /usr/bin/fish* is a shell, but just fish isn’t, according to */etc/shells. The same applies to /bin/bash and /usr/bin/bash on one hand and bash, on the other, as well as others in the list. In other words, the shell executable isn’t a valid setting in /etc/passwd.

In short, the PAM library calls pam_shells.so, which rejects fish as a valid shell.

4.3. Reason

Although a user having an invalid default shell is fairly uncommon, the ways this can happen are many.

For example, assuming root has a valid default shell of /bin/bash, we can issue a chsh to change that to fish, since no further checks are done for the new shell:

# chsh --shell badshell

At this point, badshell is the new entry in /etc/passwd:

# cat /etc/passwd
root:x:0:0:root:/root:badshell
[...]

Of course, we can also do this directly in the /etc/passwd file. In any case, from the moment of the change, we won’t be able to use root for changing shells.

In fact, this is precisely the reason why logging in as the root user directly is a bad practice: it allows too much control over the system, including ways to lock ourselves out.

4.4. Solutions and Workarounds

Naturally, we can always skip chsh and just edit /etc/passwd directly.

Yet, once we understand the problem, there are different ways to solve it for chsh:

  • temporarily comment out auth required pam_shells.so from /etc/pam.d/chsh, so the /etc/shells check isn’t done
  • permanently or temporarily switch the first two uncommented lines in /etc/pam.d/chsh, so root doesn’t require the /etc/shells check
  • manually change the default root shell in /etc/passwd, so the /etc/shells check passes
  • temporarily add the current default root shell to /etc/shells, so the /etc/shells check passes

After we apply any workaround, we can set the default root shell to a valid one and restore the changes. However, the second option might make sense in general, since root is usually an exception in many situations.

5. Summary

In this article, we talked about shell changes and restrictions imposed by the PAM system on chsh.

In conclusion, despite the default settings and potential consequences from them, we can work around PAM problems by making relevant changes to the chsh configuration file.