setgid Operation not permitted - c

I have a C program that calls setgid() with the group id of the group "agrp", and it is saying "Operation not permitted" when I try to run it.
The program has the following ls -la listing:
-r-xr-s--x 1 root agrp 7508 Nov 18 18:48 setgidprogram
What I want, is setgidprogram to be able to access a file that has the owner otheruser and the group agrp, and permissions set to u+rw,g+rw (User and group read/writeable.)
What am I doing wrong? Does setgidprogram HAVE to have the setuid bit set also? (When I tried it, it worked.)
I am running Fedora 19, and I have SELinux disabled.
EDIT
Here is some example code:
wrap.c:
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
int main(void)
{
struct group *grp = getgrnam("agrp");
printf("%d\n",grp->gr_gid);
if(setgid(grp->gr_gid) != 0)
{
printf("%s.\n", strerror(errno));
return 1;
}
execl("/tmp/whoami_script.sh", NULL);
printf("%s.\n", strerror(errno));
return 0;
}
/tmp/whoami_script.sh:
#!/usr/bin/bash
id
$ ls -la /tmp/whoami_script.sh wrap
-r-xr-xr-x 1 root agrp 19 Nov 18 19:53 /tmp/whoami_script.sh
$ ./wrap
1234
uid=1000(auser) gid=1000(auser) groups=1000(auser),0(root),10(wheel)
---x--s--x 1 root agrp 7500 Nov 18 19:55 wrap
Is this enough information now?

The original version of the question showed 6550 permission on the file.
If you're not either user root or in group agrp, you need to be able to use the public execute permissions on the program — which are missing. Since it is a binary, you don't need read permission. To fix it:
# chmod o+x setgidprogram
(The # denotes 'as root or via sudo', or equivalent mechanisms.) As it stands, only people who already have the relevant privileges can use the program.
If the program is installed SGID agrp, there is no need for the program to try to do setgid(agrp_gid) internally. The effective GID will be the GID belonging to agrp and the program will be able to access files as any other member of agrp could.
That said, normally you can do a no-op successfully. For example, this code works fine:
#include <stdio.h>
#include <unistd.h>
#include "stderr.h"
int main(int argc, char **argv)
{
err_setarg0(argv[argc-argc]);
gid_t gid = getegid();
if (setgid(gid) != 0)
err_syserr("Failed to setgid(%d)\n", (int)gid);
puts("OK");
return 0;
}
(You just have to accept that the err_*() function do error reporting; the argc-argc trick avoids a warning/error from the compiler about otherwise unused argument argc.)
If you make the program SUID root, then the SGID property doesn't matter much; the program will run with EUID root and that means it can do (almost) anything. If it is SUID root, you should probably be resetting the EUID to the real UID:
setuid(getuid());
before invoking the other program. Otherwise, you're invoking the other program as root, which is likely to be dangerous.
Dissecting POSIX
In his answer, BenjiWiebe states:
The problem was I was only setting my effective GID, not my real GID. Therefore, when I exec'd, the child process was started with the EGID set to the RGID. So, in my code, I used setregid() which worked fine.
Yuck; which system does that? Linux trying to be protective? It is not the way things worked classically on Unix, that's for sure. However, the POSIX standard seems to have wriggle room in the verbiage (for execvp()):
If the ST_NOSUID bit is set for the file system containing the new process image file, then the effective user ID, effective group ID, saved set-user-ID, and saved set-group-ID are unchanged in the new process image. Otherwise, if the set-user-ID mode bit of the new process image file is set, the effective user ID of the new process image shall be set to the user ID of the new process image file. Similarly, if the set-group-ID mode bit of the new process image file is set, the effective group ID of the new process image shall be set to the group ID of the new process image file. The real user ID, real group ID, and supplementary group IDs of the new process image shall remain the same as those of the calling process image. The effective user ID and effective group ID of the new process image shall be saved (as the saved set-user-ID and the saved set-group-ID) for use by setuid().
If I'm parsing that right, then we have a number of scenarios:
ST_NOSUID is set.
ST_NOSUID is not set, but SUID or SGID bit is set on the executable.
ST_NOSUID is not set, but SUID or SGID biy is not set on the executable.
In case 1, it is fairly clearly stated that the EUID and EGID of the exec'd process are the same as in the original process (and if the EUID and RUID are different in the original process, they will be different in the child).
In case 2, if the SUID bit is set on the executable, the EUID will be set to the SUID. Likewise if the SGID bit is set on the executable, the EGID will be set to the SGID. It is not specified what happens if the SUID bit is set, the SGID bit is not set, and the original process has different values for EGID and RGID; nor, conversely, is it specified what happens if the SGID bit is set, the SUID bit is not set, and the original process has different values for EUID and RUID.
Case 3, where neither the SUID nor SGID bit is set on the executable, also seems to be unspecified behaviour.
Classically on Unix systems, the EUID and RUID could be different, and the difference would be inherited across multiple (fork() and) exec() operations if the executable does not override the EUID or EGID with its own SUID or SGID bits. However, it is not clear that the POSIX standard mandates or prohibits this; it seems to be unspecified behaviour. The rationale section provides no guidance on the intentions.
If my reading is correct, then I find it amusing that the ST_NOSUID bit means that if a program is launched by a process that is running SUID, then the program on the 'no SUID' file system will be run with different real and effective UID (RUID and EUID), which seems counter-intuitive. It doesn't matter what the SUID and SGID bits on the executable are set to (so the bits on the executable are ignored), but the inherited values of EUID and RUID are maintained.

This code finally worked:
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
int main(void)
{
gid_t g = getegid();
if(setregid(g, g) != 0)
{
printf("Error setting GID: %s.\n", strerror(errno));
}
execl("/tmp/whoami_script.sh", "/tmp/whoami_script.sh", NULL);
printf("Error: %s.\n", strerror(errno));
return 0;
}
The problem was I was only setting my effective GID, not my real GID. Therefore, when I exec'd, the child process was started with the EGID set to the RGID. So, in my code, I used setregid which worked fine.

Related

C program to mkdir in POSIX shared memory missing permissions

I have a POSIX shared memory directory which gets blown away on reboot and needs to be recreated on occasion. An example,
#include <sys/stat.h>
#include <sys/file.h>
int main(int argc, char argv[])
{
struct stat st = {0};
if (stat("/dev/shm/example", &st) == -1) {
mkdir("/dev/shm/example", 0777);
}
}
This creates the directory with missing write permissions for group/others:
drwxr-xr-x. 2 *** *** *** May 14 12:00 example
I've tried experimenting with the mode_t flags and even replaced "0777" with "S_IRWXU | S_IRWXG | S_IRWXO". I do need the directory to have permission flag 0777.
You need to set the permission explicitly or reset your file creation mask with the umask() function.
Per the POSIX mkdir() documentation (bolding mine):
The mkdir() function shall create a new directory with name path. The file permission bits of the new directory shall be initialized from mode. These file permission bits of the mode argument shall be modified by the process' file creation mask.
The only thread-safe way to create a file or directory with the exact permissions you want is to explicitly set them after creation:
mkdir("/dev/shm/example", 0777);
chmod("/dev/shm/example", 0777);
Of course, you'd do well to actually check the return values for those calls.
Calling umask() to set and restore your file creation mask is not thread-safe:
mode_t old = umask( 0 );
mkdir("/dev/shm/example", 0777);
umask( old );
There are multiple race conditions possible with doing that:
The old mask you get with the first call to umask() can be the 0 set by another thread
Either call can overwrite the value of the file creation mask currently in use by another thread
If either of those race conditions happens, either
your file/directory won't get the permissions you need
the other thread's file/directory won't get the permissions it needs
the original file creation mask setting will be lost
Or all three.
So don't call umask() when you need to set permission bits exactly. Set the mode explicitly with a call to [f]chmod()
(The possible issues that could arise from creating directories in /dev/shm is probably worth another question...)

Entering sudo password through c

I asked this question before but no-one gave a straight answer. I wanted to know how I can enter the sudo password through c code. I'm trying to write a script to be able to execute sudo bash and enter the password required. Also I know the risks of hardcoding passwords but I don't mind in this instance.
No. Doing it that way is an antipattern.
There are several alternatives to choose from, depending on the situation:
Use gksudo (if DISPLAY environment variable is set) for a graphical prompt.
Install the scripts to be executed in /usr/share/yourapp/scripts/ or /usr/lib/yourapp/scripts/, and the proper sudo configuration that allows running them with sudo without supplying a password in /etc/sudoers.d/yourapp (or /etc/sudoers in systems without /etc/sudoers.d/)
At the beginning of your program, check if geteuid() == 0. If not, re-execute self using gksudo/sudo, to obtain root privileges.
For normal operations, your program should use only the privileges of the real user who executed the program. To be able to raise the privileges later, the root privileges are "saved". So, initially, your program will drop the privileges using e.g.
uid_t uid = getuid();
gid_t gid = getgid();
if (setresgid(gid, gid, 0) == -1 ||
setresuid(uid, uid, 0) == -1) {
/* Failed: no root privileges! */
}
To re-elevate privileges, you use
if (setresgid(gid, 0, 0) == -1 ||
setresuid(uid, 0, 0) == -1) {
/* Failed: no root privileges! */
}
which changes only the effective identity to root (as setuid binaries do), or
if (setresgid(0, 0, 0) == -1 ||
setresuid(0, 0, 0) == -1) {
/* Failed: no root privileges! */
}
which changes both real and effective identity to root.
Often, the privileges are elevated for only forking a privileged child slave, after which the main program drops the privileges completely using
if (setresgid(gid, gid, gid) == -1 ||
setresuid(uid, uid, uid) == -1) {
/* Failed. */
}
keeping just a socket pair or pipes between the parent and the child; the child can then fork and execute new processes. (If an Unix domain socket is used, the parent can even send new descriptors to be used for the new processes' standard streams via ancillary messages.)
Use filesystem capabilities to give your program the capabilities it needs, without elevating all its privileges.
For example, sudo setcap CAP_NET_BIND_SERVICE=pe /usr/bin/yourapp gives /usr/bin/yourapp the CAP_NET_BIND_SERVICE capability (permitted and effective; not inherited), which allows your program to bind to any unused TCP/IP and UDP/IP ports, including 1-1024. See man 7 capabilities for detailed descriptions of the capabilities.
Use a trusted helper binary (program) to act as sudo for you. If you install this at e.g. /usr/lib/yourapp/execute, you can add the sudo configuration necessary to allow executing it without supplying a password. Alternatively, you can make it setuid root, or give it the necessary capabilities via filesystem capabilities.
To avoid other programs from exploiting this helper, you must ensure it is only executed by your program. One way to ensure that is to have your program create an Unix domain socket pair, leaving one end open in your program, and the other end for the helper in e.g. descriptor 3. Before doing anything, the helper checks that there is nothing to receive yet (to avoid "pipe stuffing" attacks), and writes a single byte to the parent. The parent responds with a single byte, but with its credentials in an ancillary message. The credentials contain the process ID of the parent. (Do not simply use getppid(), because that allows certain attacks; this socket approach verifies the parent is still alive when we do the check.) Finally, use readlink() to read the /proc/PID/exe pseudo-symlink, where PID is the parent process ID from the credentials ancillary message. At this point, the helper should send a byte, and receive a byte with the credentials again as an ancillary message, to ensure the parent process is still the same.
The verification process is complex, but necessary, to avoid making it easy to exploit root privileges by misusing the helper. For another approach to do exactly this, look into Apache suEXEC, the helper used by Apache to execute CGI programs with specific user privileges.
Let's say you are totally uninterested in doing things in a sensible way, and insist on using passwords. Fine; all I ask is that you don't publish such code, or at least warn your users that it is completely unsafe.
This is not just a crude hack: it is a suicidal one, similar to putting the password to your web site in your e-mail signature, because you only mail to friends who should have admin access to your site in the first place. So, footgun, with a hair trigger, no safety, and armed with buckshot coated in tetrodotoxin. With a nice label with big, child-readable letters saying "Please play with me! I'm safe!", stored in the kids bedroom.
The simplest thing to do is to execute sudo with the -S flag, which causes it to read the password from the standard input. For example, example.c:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
FILE *cmd;
int status;
cmd = popen("/usr/bin/sudo -S id -un 2>/dev/null", "w");
if (!cmd) {
fprintf(stderr, "Cannot run sudo: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
fprintf(cmd, "Password\n");
fflush(cmd);
status = pclose(cmd);
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == EXIT_SUCCESS)
fprintf(stderr, "No errors.\n");
else
fprintf(stderr, "Command failed with exit status %d.\n", WEXITSTATUS(status));
} else
if (WIFSIGNALED(status))
fprintf(stderr, "Command killed by signal %d.\n", WTERMSIG(status));
else
fprintf(stderr, "Command failed.\n");
return EXIT_SUCCESS;
}
The command pipe is opened in write mode, so that we can write the password to it. The 2>/dev/null redirection hides the password prompt.
If the password is correct, the above will output what id -un outputs when run as root (i.e.: root) to standard output, and No errors. to standard error.
If the password is incorrect, sudo will retry a couple of times (so nothing will happen for a few seconds), then the program will report Command failed with exit status 1. to standard error, because that's what sudo does when the password is incorrect.

setuid(###) followed by getuid() fails in C program with permissions bit set

I'm trying something very simple with setuid followed by getuid in a C program that is not working unless the user is the owner of the c program. I am setting the permissions bit using chmod 7777 in addition to thesetuid. Here is the C program...
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int x;
printf("Setting uid to 1111\n");
x=setuid(1111);
printf("setuid returned... %d\n",x);
printf("Testing state of uid using getuid()\n");
printf("uid: %d\n",getuid());
return 0;
}
Given that 1111 is the uid of the owner of the C program (returned by id -u).
after gcc, chmod 7777 on the binary produced.
Testing as the owner works fine...
% suid
Setting uid to 1111
setuid returned... 0
Testing state of uid using getuid()
uid: 1112
and to check the permissions on the binary...
ls -l suid
-rwsrwsrwx 1 katman design 6852 Mar 1 17:56 suid
Then I become another user (same group BTW) and run the "suid" executable again...
% suid
Setting uid to 1111
setuid returned... 0
Testing state of uid using getuid()
uid: 57849
Why isn't setuid working given that the owner granted permission to do this?
Also...
Q: If I can get this to work, I have heard that one can do a system() call to run a cshell script from a C program like this and the shell script should run as the owner of the C program. The uid is "passed down" to children. True or False?
Q: If the cshell script runs a perl script which in turn runs system calls, etc... will all these offspring shells run as the owner of the parent C binary that started started the whole cascade? IOW, is the uid "inherited" by children?

Why is unprivileged recursive unshare(CLONE_NEWUSER) not permitted?

I'm on Ubuntu 17.04.
Single unprivilleged unshare of mount namespace works. You can try using unshare(1) command:
$ unshare -m -U /bin/sh
#
However unshare within unshare is not permitted:
$ unshare -m -U /bin/sh
# unshare -m -U /bin/sh
unshare: Operation not permitted
#
Here is a C program that will basically do the same:
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <sys/mount.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
if(unshare(CLONE_NEWUSER|CLONE_NEWNS) == -1) {
perror("unshare");
return -1;
}
if(unshare(CLONE_NEWUSER|CLONE_NEWNS) == -1) {
perror("unshare2");
return -1;
}
return 0;
}
Why it's not permitted? Where I can find documentation about this? I failed to find this information in unshare or clone man page and in kernel unshare documentation.
Is there a system setting that would allow this?
What I want to achieve:
First unshare: I want to mask few binaries on system with my own versions.
Second unshare: unprivilleged chroot.
I'm somewhat guessing here, but I think that the reason is the UID mapping. In order to perform it, certain conditions must be met (from the user_namespaces man page):
In order for a process to write to the /proc/[pid]/uid_map (/proc/[pid]/gid_map) file, all of the following require‐
ments must be met:
1. The writing process must have the CAP_SETUID (CAP_SETGID) capability in the user namespace of the process pid.
2. The writing process must either be in the user namespace of the process pid or be in the parent user namespace of
the process pid.
3. The mapped user IDs (group IDs) must in turn have a mapping in the parent user namespace.
I believe what happens is that the first time you run, the mapping matches that of the parent UID. The second time, however, it does not, and this fails the system call.
From the unshare(2) manual page:
EPERM CLONE_NEWUSER was specified in flags, but either the effective user ID or the effective group ID of the caller
does not have a mapping in the parent namespace (see user_namespaces(7)).

Using the setuid bit in Linux

I have this C file:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("%s\n", getlogin());
printf("%i\n", getuid());
}
I compile it, set the UID and GID both to root and set the setuid bit, so that it looks like this:
-rwsrwsr-x 1 root root 8735 Apr 8 19:51 a.out
However when I call $ ./a.out I still get:
user
1000
What am I doing wrong?
The real user ID is still the user that called the program, but the effective user ID is root. In a setuid program, they are not the same.
To get the effective user ID, call geteuid(). You can also use cuserid() to get the name associated with the effective user ID.
Your program has got only the permission to change its uid. To actually switch to root you have to call setuid(0) in it.
Please have a look here

Resources