NixOS / nixpkgs

Nix Packages collection & NixOS
MIT License
17.78k stars 13.89k forks source link

newgrp in nix-shell gives "setgid: Operation not permitted" #20262

Open ryantm opened 7 years ago

ryantm commented 7 years ago

Issue description

When using newgrp inside a nix-shell, the group change fails with "setgid: Operation not permitted". newgrp outside of a nix shell works as expected. Also, changing to the current group inside a nix-shell succeeds. I've confirmed this is a problem on Nixos and Ubuntu using Nix. The issue occurs with our without the dash login flag to newgrp.

Steps to reproduce

nix-shell --pure -p shadow
newgrp - wheel # wheel is an example of one of a user's non-primary groups

log:

ryantm@home1 ~ (master)$ nixos-version
17.03pre94694.fa4167c (Gorilla)
ryantm@home1 ~ (master)$ nix-instantiate --eval '<nixpkgs>' -A lib.nixpkgsVersion
warning: Nix search path entry ‘/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs’ does not exist, ignoring
"17.03pre95294.54a0e90"
ryantm@home1 ~ (master)$ whoami
ryantm
ryantm@home1 ~ (master)$ groups
users wheel networkmanager
ryantm@home1 ~ (master)$ nix-shell --pure -pshadow
error: getting status of ‘/home/ryantm/default.nix’: No such file or directory
ryantm@home1 ~ (master)$ nix-shell --pure -p shadow
warning: Nix search path entry ‘/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs’ does not exist, ignoring

[nix-shell:~]$ whoami
ryantm

[nix-shell:~]$ groups
users wheel networkmanager

[nix-shell:~]$ newgrp - wheel
setgid: Operation not permitted

[nix-shell:~]$ exit
ryantm@home1 ~ (master)$ newgrp - wheel
WARNING: bad ownership on /nix/var/nix/profiles/per-user/
WARNING: bad ownership on /nix/var/nix/gcroots/per-user/
ryantm@home1 ~ (master)$ echo $?
0
ryantm@home1 ~ (master)$ logout
ryantm@home1 ~ (master)$ nix-shell --pure -p shadow
warning: Nix search path entry ‘/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs’ does not exist, ignoring

[nix-shell:~]$ newgrp - wheel
setgid: Operation not permitted

[nix-shell:~]$ echo $?
1

strace:

ryantm@home1 ~ (master)$ nix-shell --pure -p shadow strace
[nix-shell:~]$ strace newgrp - wheel
execve("/nix/store/nzx36nwgdy04gwrlfqmrml0w2gh05vnh-shadow-4.2.1/bin/newgrp", ["newgrp", "-", "wheel"], [/* 47 vars */]) = 0
brk(NULL)                               = 0x857000
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8fbffde000
access("/etc/ld-nix.so.preload", R_OK)  = -1 ENOENT (No such file or directory)
open("/nix/store/hwbvmr4imiyi5ziq6v2snynr7zf022b5-glibc-2.24/lib/tls/x86_64/libcrypt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/nix/store/hwbvmr4imiyi5ziq6v2snynr7zf022b5-glibc-2.24/lib/tls/x86_64", 0x7fff8705efb0) = -1 ENOENT (No such file or directory)
open("/nix/store/hwbvmr4imiyi5ziq6v2snynr7zf022b5-glibc-2.24/lib/tls/libcrypt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/nix/store/hwbvmr4imiyi5ziq6v2snynr7zf022b5-glibc-2.24/lib/tls", 0x7fff8705efb0) = -1 ENOENT (No such file or directory)
open("/nix/store/hwbvmr4imiyi5ziq6v2snynr7zf022b5-glibc-2.24/lib/x86_64/libcrypt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/nix/store/hwbvmr4imiyi5ziq6v2snynr7zf022b5-glibc-2.24/lib/x86_64", 0x7fff8705efb0) = -1 ENOENT (No such file or directory)
open("/nix/store/hwbvmr4imiyi5ziq6v2snynr7zf022b5-glibc-2.24/lib/libcrypt.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \v\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0555, st_size=57000, ...}) = 0
mmap(NULL, 2335072, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8fbfb84000
mprotect(0x7f8fbfb90000, 2093056, PROT_NONE) = 0
mmap(0x7f8fbfd8f000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xb000) = 0x7f8fbfd8f000
mmap(0x7f8fbfd91000, 184672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8fbfd91000
close(3)                                = 0
open("/nix/store/hwbvmr4imiyi5ziq6v2snynr7zf022b5-glibc-2.24/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\4\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0555, st_size=1908040, ...}) = 0
mmap(NULL, 3791104, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8fbf7e6000
mprotect(0x7f8fbf97b000, 2093056, PROT_NONE) = 0
mmap(0x7f8fbfb7a000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x194000) = 0x7f8fbfb7a000
mmap(0x7f8fbfb80000, 14592, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8fbfb80000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8fbffdc000
arch_prctl(ARCH_SET_FS, 0x7f8fbffdc700) = 0
mprotect(0x7f8fbfb7a000, 16384, PROT_READ) = 0
mprotect(0x7f8fbfd8f000, 4096, PROT_READ) = 0
mprotect(0x607000, 4096, PROT_READ)     = 0
mprotect(0x7f8fbffe1000, 4096, PROT_READ) = 0
brk(NULL)                               = 0x857000
brk(0x878000)                           = 0x878000
getgid()                                = 100
open("/proc/self/loginuid", O_RDONLY)   = 3
read(3, "1000", 12)                     = 4
close(3)                                = 0
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = 0
sendto(3, "\2\0\0\0\v\0\0\0\7\0\0\0passwd\0", 19, MSG_NOSIGNAL, NULL, 0) = 19
poll([{fd=3, events=POLLIN|POLLERR|POLLHUP}], 1, 5000) = 1 ([{fd=3, revents=POLLIN|POLLHUP}])
recvmsg(3, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="passwd\0", iov_len=7}, {iov_base="\310O\3\0\0\0\0\0", iov_len=8}], msg_iovlen=2, msg_control=[{cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, cmsg_data=[4]}], msg_controllen=20, msg_flags=MSG_CMSG_CLOEXEC}, MSG_CMSG_CLOEXEC) = 15
mmap(NULL, 217032, PROT_READ, MAP_SHARED, 4, 0) = 0x7f8fbffa7000
close(4)                                = 0
close(3)                                = 0
getuid()                                = 1000
getgroups(16, [1, 57, 100])             = 3
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = 0
sendto(3, "\2\0\0\0\f\0\0\0\6\0\0\0group\0", 18, MSG_NOSIGNAL, NULL, 0) = 18
poll([{fd=3, events=POLLIN|POLLERR|POLLHUP}], 1, 5000) = 1 ([{fd=3, revents=POLLIN|POLLHUP}])
recvmsg(3, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="group\0", iov_len=6}, {iov_base="\310O\3\0\0\0\0\0", iov_len=8}], msg_iovlen=2, msg_control=[{cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, cmsg_data=[4]}], msg_controllen=20, msg_flags=MSG_CMSG_CLOEXEC}, MSG_CMSG_CLOEXEC) = 14
mmap(NULL, 217032, PROT_READ, MAP_SHARED, 4, 0) = 0x7f8fbff72000
close(4)                                = 0
close(3)                                = 0
open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=172, ...}) = 0
read(3, "passwd:    files \ngroup:     fil"..., 4096) = 172
read(3, "", 4096)                       = 0
close(3)                                = 0
open("/nix/store/hwbvmr4imiyi5ziq6v2snynr7zf022b5-glibc-2.24/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/nix/store/hwbvmr4imiyi5ziq6v2snynr7zf022b5-glibc-2.24/lib/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320 \0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0555, st_size=55912, ...}) = 0
mmap(NULL, 2168280, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8fbf5d4000
mprotect(0x7f8fbf5de000, 2097152, PROT_NONE) = 0
mmap(0x7f8fbf7de000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xa000) = 0x7f8fbf7de000
mmap(0x7f8fbf7e0000, 21976, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8fbf7e0000
close(3)                                = 0
mprotect(0x7f8fbf7de000, 4096, PROT_READ) = 0
open("/etc/group", O_RDONLY|O_CLOEXEC)  = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=503, ...}) = 0
read(3, "root:x:0:\nwheel:x:1:ryantm\nkmem:"..., 4096) = 503
close(3)                                = 0
open("/etc/gshadow", O_RDONLY)          = -1 ENOENT (No such file or directory)
open("/etc/gshadow", O_RDONLY)          = -1 ENOENT (No such file or directory)
open("/etc/shadow", O_RDONLY|O_CLOEXEC) = -1 EACCES (Permission denied)
getuid()                                = 1000
open("/etc/login.defs", O_RDONLY)       = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=404, ...}) = 0
read(3, "DEFAULT_HOME yes\n\nSYS_UID_MIN  4"..., 4096) = 404
read(3, "", 4096)                       = 0
close(3)                                = 0
setgid(1)                               = -1 EPERM (Operation not permitted)
dup(2)                                  = 3
fcntl(3, F_GETFL)                       = 0x8002 (flags O_RDWR|O_LARGEFILE)
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
write(3, "setgid: Operation not permitted\n", 32setgid: Operation not permitted
) = 32
close(3)                                = 0
exit_group(1)                           = ?
+++ exited with 1 +++
ryantm commented 7 years ago

More debugging info

The newgrp outside of the shell uses a setuid-wrapper

ryantm@home1 ~ (master)$ which newgrp                                                                                                                                                                                                                                               
/var/setuid-wrappers/newgrp                                                                                                                                                                                                                                                         
ryantm@home1 ~ (master)$ nix-shell --pure -p shadow libcap which --command "which newgrp"                                                                                                                                                                                           
warning: Nix search path entry ‘/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs’ does not exist, ignoring                                                                                                                                                                
/nix/store/nzx36nwgdy04gwrlfqmrml0w2gh05vnh-shadow-4.2.1/bin/newgrp                                                                                                                                                                                                                 

The capabilities look the same:

ryantm@home1 ~ (master)$ capsh --print                                                                                                                                                                                                                                              
Current: =                                                                                                                                                                                                                                                                          
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_\
chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit\
_read                                                                                                                                                                                                                                                                               
Securebits: 00/0x0/1'b0                                                                                                                                                                                                                                                             
 secure-noroot: no (unlocked)                                                                                                                                                                                                                                                       
 secure-no-suid-fixup: no (unlocked)                                                                                                                                                                                                                                                
 secure-keep-caps: no (unlocked)                                                                                                                                                                                                                                                    
uid=1000(ryantm)                                                                                                                                                                                                                                                                    
gid=100(users)                                                                                                                                                                                                                                                                      
groups=1(wheel),57(networkmanager),100(users)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
ryantm@home1 ~ (master)$ nix-shell --pure -p shadow libcap which --command "capsh --print"                                                                                                                                                                                          
warning: Nix search path entry ‘/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs’ does not exist, ignoring                                                                                                                                                                
Current: =                                                                                                                                                                                                                                                                          
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_\
chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit\
_read                                                                                                                                                                                                                                                                               
Securebits: 00/0x0/1'b0                                                                                                                                                                                                                                                             
 secure-noroot: no (unlocked)                                                                                                                                                                                                                                                       
 secure-no-suid-fixup: no (unlocked)                                                                                                                                                                                                                                                
 secure-keep-caps: no (unlocked)                                                                                                                                                                                                                                                    
uid=1000(ryantm)                                                                                                                                                                                                                                                                    
gid=100(users)                                                                                                                                                                                                                                                                      
groups=1(wheel),57(networkmanager),100(users)   
dezgeg commented 7 years ago

Yes, that seems expected to not to work. newgrp seems to need setuid, and setuid binaries cannot exist in the store; the setuid wrapper indirection must be used.

ryantm commented 7 years ago

That's unfortunate, because newgrp seems like a good way to change a user's primary group temporarily, so the files and folders they create have the correct group.

vcunat commented 7 years ago

You can call /var/setuid-wrappers/newgrp explicitly (it's not pure, of course).

ryantm commented 7 years ago

@vcunat That works on Nixos, but not other operating systems.

vcunat commented 7 years ago

On other systems there's typically /usr/bin/newgrp ;-)

CMCDragonkai commented 7 years ago

Related issue: https://github.com/NixOS/nix/issues/1037

I think there needs to be better ergonomics with regards to developing applications that make use of setuid programs inside a nix-shell.

CMCDragonkai commented 7 years ago

@vcunat Is that now /run/wrappers/bin/ now?

vcunat commented 7 years ago

Yes, apparently, but generally one should try to avoid hardcoding such paths.

CMCDragonkai commented 7 years ago

@vcunat That still doesn't work because it this error:

The value for the SHELL variable was not found the /etc/shells file

This incident has been reported

Is there some sort of change to /etc/shells needed inside a nix-shell?

mmahut commented 5 years ago

Are there any updates on this issue, please?

ryantm commented 5 years ago

I confirmed this is still an issue with the latest nixpkgs master and nix version 2.2.2.

stale[bot] commented 4 years ago

Thank you for your contributions.

This has been automatically marked as stale because it has had no activity for 180 days.

If this is still important to you, we ask that you leave a comment below. Your comment can be as simple as "still important to me". This lets people see that at least one person still cares about this. Someone will have to do this at most twice a year if there is no other activity.

Here are suggestions that might help resolve this more quickly:

  1. Search for maintainers and people that previously touched the related code and @ mention them in a comment.
  2. Ask on the NixOS Discourse.
  3. Ask on the #nixos channel on irc.freenode.net.
hab25 commented 2 years ago

still important to me