It's now possible to run commands as other users without shell
interpolation by using "--exec":
Read /etc/shadow as root without specifying user:
```
su --exec /bin/cat -- /etc/shadow
```
Or specify user:
```
su --exec /bin/cat root -- /etc/shadow
```
Mechanical rename distinguishing this variable from intended changes
supporting executing commands without using an interpretive shell
(i.e. no '/bin/sh -c').
In preparation for supporting --exec I was testing the robustness
of "--" handling and it became apparent that things are currently
a bit broken in `su`.
Since "--" is currently of limited utility, as the subsequent
words are simply passed to the shell after "-c","command_string",
it seems to have gone unnoticed for ages.
However, with --exec, it's expected that "--" would be an almost
required separator with every such usage, considering the
following flags must be passed verbatim to execve() and will
likely begin with hyphens looking indistinguishable from any
other flags in lieu of shell interpolation to worry about.
For some practical context of the existing situation, this
invocation doesn't work today:
```
$ su --command ls -- flags for shell
No passwd entry for user 'flags'
$
```
This should just run ls as root with "flags","for","shell"
forwarded to the shell after "-c","ls".
The "--" should block "flags" from being treated as the user.
That particular issue isn't a getopt one per-se, it's arguably
just a bug in su.c's implementation.
It *seemed* like an easy fix for this would be to add a check if
argv[optind-1] were "--" before treating argv[optind] as USER.
But testing that fix revealed getopt was rearranging things when
encountering "--", the "--" would always separate the handled
opts from the unhandled ones. USER would become shifted to
*after* "--" even when it occurred before it!
If we change the command to specify the user, it works as-is:
```
$ su --command ls root -- flags for shell
Password:
testfile
$
```
But what's rather surprising is how that works; the argv winds up:
"su","--command","ls","--","root","flags","for","shell"
with optind pointing at "root".
That arrangement of argv is indistinguishable from omitting the
user and having "root","flags","for","shell" as the stuff after
"--".
This makes it non-trivial to fix the bug of omitting user
treating the first word after "--" as the user, which one could
argue is a potentially serious security bug if you omit the user,
expect the command to run as root, and the first word after "--"
is a valid user, and what follows that something valid and
potentially destructive not only running in unintended form but
as whatever user happened to be the first word after "--".
So, it seems like something important to fix, and getopt seems to
be getting in the way of fixing it properly without being more
trouble than replacing getopt.
In disbelief of what I was seeing getopt doing with argv here, I
took a glance at the getopt source and found the following:
```
/* The special ARGV-element '--' means premature end of options.
Skip it like a null option,
then exchange with previous non-options as if it were an option,
then skip everything else like a non-option. */
if (d->optind != argc && !strcmp (argv[d->optind], "--"))
```
I basically never use getopt personally because ages ago it
annoyed me with its terrible API for what little it brought to
the table, and this brings it to a whole new level of awful.
This is a stability fix, not a security fix, because the affected -o
option can only be used by root and it takes a modified passwd file.
If a gecos field for a user has BUFSIZ characters without commas and an
equals sign (i.e. a huge slop/extra field) and chfn is called with -o,
then a buffer overflow occurs.
It is not possible to trigger this with shadow tools. Therefore, the
passwd file must be modified manually.
I have fixed this unlikely case the easiest and cleanest way possible.
Since chfn bails out if more than 80 characters excluding commas are
supposed to be written into gecos field, we can stop processing early on
if -o argument is too long.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
This is merely a stability fix, not a security fix.
As the root user, it is possible to set time values which later on
result in signed integer overflows.
For this to work, an sgetspent implementation must be used which
supports long values (glibc on amd64 only parses 32 bit, not 64).
Either use musl or simply call configure with following environment
variable:
$ ac_cv_func_sgetspent=no ./configure
Also it is recommended to compile with -fsanitize=undefined or
-ftrapv to see these issues easily.
Examples to trigger issues when calling "chage -l user":
$ chage -d 9223372036854775807 user
$ chage -d 106751991167300 user
$ chage -M 9999 user
$ chage -d 90000000000000 user
$ chage -I 90000000000000 user
$ chage -M 9999 user
$ chage -E 9223372036854775807 user
While at it, I fixed casting issues which could lead to signed integer
overflows on systems which still have a 32 bit time_t.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Closes#154
Currently this has three functions: one which returns the
list of subuid ranges for a user, one returning the subgids,
and one which frees the ranges lists.
I might be mistaken about what -disable-man means; some of
the code suggests it means just don't re-generate them, but
not totally ignore them. But that doesn't seem to really work,
so let's just ignore man/ when -disable-man.
Remove --disable-shared. I'm not sure why it was there, but it stems
from long, long ago, and I suspect it comes from some ancient
toolchain bug.
Create a tests/run_some, a shorter version of run_all. I'll
slowly add tests to this as I verify they work, then I can
work on fixing the once which don't.
Also, don't touch man/ if not -enable-man.
Changelog:
Apr 22: change the subid list api as recomended by Dan Walsh.
Apr 23: implement get_subid_owner
Apr 24: implement range add/release
Apr 25: finish tests and rebase
May 10: make @owner const
Signed-off-by: Serge Hallyn <serge@hallyn.com>
Although it is a good idea to check for an inadvertent typo
in the shell name it is possible that the shell might not be present
on the system yet when the user is added.
This option can be used to set a separate mode for useradd(8) and
newusers(8) to create the home directories with.
If this option is not set, the current behavior of using UMASK
or the default umask is preserved.
There are many distributions that set UMASK to 077 by default just
to create home directories not readable by others and use things like
/etc/profile, bashrc or sudo configuration files to set a less
restrictive
umask. This has always resulted in bug reports because it is hard
to follow as users tend to change files like bashrc and are not about
setting the umask to counteract the umask set in /etc/login.defs.
A recent change in sudo has also resulted in many bug reports about
this. sudo now tries to respect the umask set by pam modules and on
systems where pam does not set a umask, the login.defs UMASK value is
used.
This commit adds a from= field to the end of the useradd log entry.
Casting user_name to tallylog_reset to silence a compiler warning.
Changelog: Fixing tabs
Changelog: Changing function prototype to const char* to match user_name declaration.
This option can be used to set a separate mode for useradd(8) and
newusers(8) to create the home directories with.
If this option is not set, the current behavior of using UMASK
or the default umask is preserved.
There are many distributions that set UMASK to 077 by default just
to create home directories not readable by others and use things like
/etc/profile, bashrc or sudo configuration files to set a less
restrictive
umask. This has always resulted in bug reports because it is hard
to follow as users tend to change files like bashrc and are not about
setting the umask to counteract the umask set in /etc/login.defs.
A recent change in sudo has also resulted in many bug reports about
this. sudo now tries to respect the umask set by pam modules and on
systems where pam does not set a umask, the login.defs UMASK value is
used.
If SSH_ORIGINAL_COMMAND is set, it will be added to the syslog entry.
Closes#123.
Changelog: (SEH squashed commit): Fixing indentation
Changelog: (SEH) break up long line
`make` runs each line in a shell and bails out on error,
however, the shell is not started with `-e`, so commands in
`for` loops can fail without the error actually causing
`make` to bail out with a failure status.
For instance, the following make snippet will end
successfully, printing 'SUCCESS', despite the first `chmod`
failing:
all:
touch a b
for i in a-missing-file a b; do \
chmod 666 $$i; \
done
@echo SUCCESS
To prevent wrong paths in install scripts from remaining
unnoticed, let's activate `set -e` in the `for` loop
subshells.
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This reverts commit e293aa9cfc.
See https://github.com/shadow-maint/shadow/issues/196
Some distros still care about `/bin` vs `/usr/bin`. This commit makes
it so all binaries are always installed to `/bin`/`/sbin`. The only way to
restore the previous behaviour of installing some binaries to
`/usr/bin`/`/usr/sbin` is to revert the patch.
Closes#185
If vipw is suspended (e.g. via control-Z) and then resumed, it often gets
immediately suspended. This is easier to reproduce on a multi-core system.
root@buster:~# /usr/sbin/vipw
[1]+ Stopped /usr/sbin/vipw
root@buster:~# fg
/usr/sbin/vipw
[1]+ Stopped /usr/sbin/vipw
root@buster:~# fg
[vipw resumes on the second fg]
The problem is that vipw forks a child process and calls waitpid() with the
WUNTRACED flag. When the child process (running the editor) is suspended, the
parent sends itself SIGSTOP to suspend the main vipw process. However, because
the main vipw is in the same process group as the editor which received the ^Z,
the kernel already sent the main vipw SIGTSTP.
If the main vipw receives SIGTSTP before the child, it will be suspended and
then, once resumed, will proceed to suspend itself again.
To fix this, run the child process in its own process group as the foreground
process group. That way, control-Z will only affect the child process and the
parent can use the existing logic to suspend the parent.
Using hard-coded access vector ids is deprecated and can lead to issues with custom SELinux policies.
Switch to `selinux_check_access()`.
Also use the libselinux log callback and log if available to audit.
This makes it easier for users to catch SELinux denials.
Drop legacy shortcut logic for passwd, which avoided a SELinux check if uid 0 changes a password of a user which username equals the current SELinux user identifier.
Nowadays usernames rarely match SELinux user identifiers and the benefit of skipping a SELinux check is negligible.
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
With this, it is possible for Linux distributors to store their
supplied default configuration files somewhere below /usr, while
/etc only contains the changes made by the user. The new option
--enable-vendordir defines where the shadow suite should additional
look for login.defs if this file is not in /etc.
libeconf is a key/value configuration file reading library, which
handles the split of configuration files in different locations
and merges them transparently for the application.
suidubins should be suidusbins, since these binaries are installed
${prefix}/sbin. This historically hasn't broken the build because
chmod of newgidmap/newuidmap succeeds, causing make to think the command
succeeded. Configuring shadow with --with-fcaps removes these final two
entries and exposes the chmod failure to make.
new switch added to useradd command, --btrfs-subvolume-home. When
specified *and* the filesystem is detected as btrfs, it will create a
subvolume for user's home instead of a plain directory. This is done via
`btrfs subvolume` command. Specifying the new switch while trying to
create home on non-btrfs will result in an error.
userdel -r will handle and remove this subvolume transparently via
`btrfs subvolume` command. Previosuly this failed as you can't rmdir a
subvolume.
usermod, when moving user's home across devices, will detect if the home
is a subvolume and issue an error messages instead of copying it. Moving
user's home (as subvolume) on same btrfs works transparently.
From <https://github.com/shadow-maint/shadow/pull/71>:
```
The third field in the /etc/shadow file (sp_lstchg) contains the date of
the last password change expressed as the number of days since Jan 1, 1970.
As this is a relative time, creating a user today will result in:
username:17238:0:99999:7:::
whilst creating the same user tomorrow will result in:
username:17239:0:99999:7:::
This has an impact for the Reproducible Builds[0] project where we aim to
be independent of as many elements the build environment as possible,
including the current date.
This patch changes the behaviour to use the SOURCE_DATE_EPOCH[1]
environment variable (instead of Jan 1, 1970) if valid.
```
This updated PR adds some missing calls to gettime (). This was originally
filed by Johannes Schauer in Debian as #917773 [2].
[0] https://reproducible-builds.org/
[1] https://reproducible-builds.org/specs/source-date-epoch/
[2] https://bugs.debian.org/917773
In case the home directory is not a real home directory
(owned by the user) but things like / or /var or similar,
it is unsafe to change ownership of home directory content.
The test checks whether the home directory is owned by the
user him/herself, if not no ownership modification of contents
is performed.
As the large uids are usually provided by remote user identity and
authentication service, which also provide user login tracking,
there is no need to create a huge sparse file for them on every local
machine.
fixup! login.defs: Add LASTLOG_UID_MAX variable to limit lastlog to small uids.
do not install newuidmap/newgidmap as suid binaries. Running these
tools with the same euid as the owner of the user namespace to
configure requires only CAP_SETUID and CAP_SETGID instead of requiring
CAP_SYS_ADMIN when it is installed as a suid binary.
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
if the euid!=owner of the userns, the kernel returns EPERM when trying
to write the uidmap and there is no CAP_SYS_ADMIN in the parent
namespace.
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
Some distributions, notably Fedora, have the following order of nsswitch
modules by default:
passwd: sss files
group: sss files
The advantage of serving local users through SSSD is that the nss_sss
module has a fast mmapped-cache that speeds up NSS lookups compared to
accessing the disk an opening the files on each NSS request.
Traditionally, this has been done with the help of nscd, but using nscd
in parallel with sssd is cumbersome, as both SSSD and nscd use their own
independent caching, so using nscd in setups where sssd is also serving
users from some remote domain (LDAP, AD, ...) can result in a bit of
unpredictability.
More details about why Fedora chose to use sss before files can be found
on e.g.:
https://fedoraproject.org//wiki/Changes/SSSDCacheForLocalUsers
or:
https://docs.pagure.org/SSSD.sssd/design_pages/files_provider.html
Now, even though sssd watches the passwd and group files with the help
of inotify, there can still be a small window where someone requests a
user or a group, finds that it doesn't exist, adds the entry and checks
again. Without some support in shadow-utils that would explicitly drop
the sssd caches, the inotify watch can fire a little late, so a
combination of commands like this:
getent passwd user || useradd user; getent passwd user
can result in the second getent passwd not finding the newly added user
as the racy behaviour might still return the cached negative hit from
the first getent passwd.
This patch more or less copies the already existing support that
shadow-utils had for dropping nscd caches, except using the "sss_cache"
tool that sssd ships.
Sometimes getlogin() may fail, e.g., in a chroot() environment or due to NSS
misconfiguration. Loggin UID allows for investigation and troubleshooting in
such situation.
When "su -l" is used the behaviour is described as similar to
a direct login. However login.c is doing a setup_env(pw) and then a
pam_getenvlist() in this scenario. But su.c is doing it the other
way around. Which means that the value of PATH from /etc/environment
is overriden. I think this is a bug because:
The man-page claims that "-l": "provides an environment similar
to what the user would expect had the user logged in directly."
And login.c is using the PATH from /etc/environment.
This will fix:
https://bugs.launchpad.net/ubuntu/+source/shadow/+bug/984390
This allows shadow-utils to build on systems like Adélie, which have no
<utmp.h> header or `struct utmp`. We use a <utmpx.h>-based daemon,
utmps[1], which uses `struct utmpx` only.
Tested both `login` and `logoutd` with utmps and both work correctly.
[1]: http://skarnet.org/software/utmps/
Equivalent of `mkdir -p`. It will create all parent directories.
Example: `useradd -d /home2/testu1 -m testu1`
Based on https://github.com/shadow-maint/shadow/pull/2 by Thorsten Kukuk
and Thorsten Behrens which was Code from pwdutils 3.2.2 with slight adaptations.
Adapted to so it applies to current code.
Otherwise our spw_next() will cause us to skip an entry.
Ideally we'd be able to do an swp_rewind(1), but I don't
see a helper for this.
Closes#60
Signed-off-by: Serge Hallyn <shallyn@cisco.com>