* shadow-core: add a shared terminal helper
Reading a line and reading a password were open-coded in pam.rs, and
every prompt outside a PAM conversation had no helper at all.
tty.rs holds all of it: prompt_line, read_password and read_stdin_line,
with the EchoGuard that restores the terminal. Ctrl-C at a password
prompt used to leave echo disabled -- the signal terminates the process
without unwinding, so the guard's destructor never ran. The read now
blocks SIGINT, SIGQUIT and SIGTSTP for its duration, which is what
readpassphrase(3) does, and restores the mask on the way out.
The prompt prefers /dev/tty and falls back to stderr and stdin when
there is no controlling terminal (ENXIO/ENODEV), the case that broke
password changes under su -c.
* chfn: prompt for fields, and check values before asking for a password
chfn(1) with no field option prompts for each field with its current
value; ours refused with 'no flags specified'. It now prompts, and an
empty answer keeps the field.
Three fixes come with it:
- CHFN_RESTRICT is enforced on the values, not on which options were
passed. Interactive input went through unrestricted, and a field the
caller may not change is now not prompted for at all.
- Values are validated before the PAM conversation, so a rejected
value no longer costs the caller a password prompt.
- mutate_passwd no longer calls setuid(0). euid 0 is all the lock and
the atomic write need; raising the real uid made caller_is_root(),
which is deliberately real-uid based, answer true for everyone.
The five sub-fields are now one Changes value with the collect,
validate, permission and apply steps on it, which is what let uumain
shrink rather than grow.
* chsh: prompt for the shell, and stop leaking path existence
Three changes to the self-service path.
chsh(1) with no -s prompts for the new shell with the current one in
brackets; ours refused with 'no shell specified'. Pressing ENTER, or
repeating the current value, is now a successful no-op.
An empty shell is accepted. passwd(5) gives the field no meaning of its
own and login falls back to /bin/sh, so -s '' selects the system default
rather than naming a program that does not exist.
The order of the two checks on a shell now matters and is documented.
chsh is setuid-root, so Path::exists answers with root's view of the
filesystem: testing existence first made the tool an oracle for paths
the caller cannot stat, one probe at a time --
chsh -s /root/.ssh/id_ed25519
answering 'is not listed in /etc/shells' for a file that exists and
'does not exist' for one that does not. For a non-root caller the
membership test now runs first and discloses nothing, /etc/shells being
world-readable. Root still bypasses the list but not existence.
Validation also moved ahead of the PAM conversation, so a refused shell
no longer costs a password prompt, and mutate_passwd no longer calls
setuid(0): euid 0 is enough, and raising the real uid made
caller_is_root() answer true for every caller.
* docs: rewrite the chfn and chsh man pages; cover self-service end to end
The man pages documented neither interactive mode, nor CHFN_RESTRICT,
nor the restricted-account rule, nor what an empty shell field means,
and chfn's still said a field option was mandatory. Both now carry
permissions, value restrictions, configuration, files and worked
examples, and chsh explains why the two shell checks run in the order
they do.
The deployment suite gains eighteen assertions against the real setuid
binaries: interactive chfn and chsh through PAM, an empty answer as a
no-op, CHFN_RESTRICT enforced on interactive input, the empty shell
field, and the oracle itself -- an existing and a missing path under
/root must give a non-root caller the same answer, with no password
prompt before it.
4.5 KiB
chfn(1) - change user finger information
NAME
chfn - change real user name and information
SYNOPSIS
chfn [options] [LOGIN]
DESCRIPTION
The chfn command changes a user's finger information: the full name, office room number and phone numbers that finger(1) and similar tools display. These are stored as comma-separated sub-fields of the GECOS field of /etc/passwd (see passwd(5)):
Full Name,Room Number,Work Phone,Home Phone,Other
With no LOGIN, chfn operates on the account of the user running it.
If no field option is given, chfn prompts for each field it is allowed to change, showing the current value in brackets. Pressing ENTER at a prompt keeps the current value; an empty run makes no change and exits successfully. Supply a field option to change one field without being prompted for the others, or an empty argument (for example -r '') to clear a field.
PERMISSIONS
A normal user may only change their own finger information, and only the fields listed in CHFN_RESTRICT (see below). The superuser may change any field of any account.
chfn is normally installed setuid-root, so it establishes who is asking before it writes: a non-root caller must authenticate through the chfn PAM service (typically /etc/pam.d/chfn, where pam_rootok admits root and the system authentication stack prompts everyone else). Authentication happens after the new values have been checked and accepted, so a rejected value never costs the caller a password prompt.
A build without PAM support cannot verify the caller, so it refuses every non-root invocation rather than applying an unauthenticated change.
OPTIONS
- -f, --full-name FULL_NAME
- Change the user's full name.
- -h, --home-phone HOME_PHONE
- Change the user's home phone number.
- -o, --other OTHER
- Change the user's other GECOS information. Only the superuser may set this field, in any configuration; CHFN_RESTRICT cannot delegate it.
- -r, --room ROOM
- Change the user's room number.
- -R, --root CHROOT_DIR
- Apply changes in the CHROOT_DIR directory. Only the superuser may use this option: it points a setuid-root program at files of the caller's choosing.
- -w, --work-phone WORK_PHONE
- Change the user's office phone number.
- --help
- Print a usage summary and exit.
- --version
- Print the version and exit.
VALUE RESTRICTIONS
Every value is checked before anything is locked or written. A value is rejected if it contains a colon, a newline, or any other control character: those would corrupt the record and could inject a second account into /etc/passwd.
A comma or an equals sign is additionally rejected in every field except other, the final sub-field. A comma would be read back as a sub-field separator, silently shifting every following field.
CONFIGURATION
CHFN_RESTRICT in /etc/login.defs (see login.defs(5)) lists which fields a non-root user may change, as a string of letters:
| Letter | Field |
|---|---|
| f | full name |
| r | room number |
| w | work phone |
| h | home phone |
The value yes is equivalent to rwh. If CHFN_RESTRICT is absent, or /etc/login.defs cannot be read, non-root users may change nothing and only the superuser can make changes.
In interactive mode a field the caller may not change is not prompted for, so no answer is collected only to be refused. A non-root caller allowed no field at all is told so immediately.
EXIT STATUS
- 0
- Success, including an interactive run in which every prompt was answered with ENTER.
- 1
- Permission denied, an invalid value, a missing account, or a failure to read or write /etc/passwd.
FILES
- /etc/passwd
- User account information.
- /etc/login.defs
- Shadow password suite configuration, read for CHFN_RESTRICT.
- /etc/pam.d/chfn
- PAM service used to authenticate a non-root caller.
EXAMPLES
Change your own full name and office phone in one command:
$ chfn -f 'Ada Lovelace' -w '+44 20 7946 0958'
Review and change every field you are allowed to change:
$ chfn
Changing the user information for ada
Enter the new value, or press ENTER for the default
Full Name [Ada Lovelace]:
Room Number [C-101]: C-204
Work Phone [+44 20 7946 0958]:
Home Phone []:
Only the room number is written; the fields answered with ENTER keep their current values.
Clear another user's room number as root:
# chfn -r '' ada
SEE ALSO
chsh(1), finger(1), login.defs(5), passwd(5)