I wish to start a root command from a 'set-user root' program,
so I wrote the following C sample program:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void main(int argc, char *argv[])
{
if(argc > 2) {
setuid(0);
printf("setuid(0) executed\n");
} else
printf("setuid(0) NOT executed\n");
system(argv[1]);
}
Testing it on Debian 6 (64 bit), I noticed that passing "/bin/sh" as the argument, I always get a ROOT SHELL, even if argc == 2:
$ gcc foo.c -o foo
$ su
Password: *****
# chown root:root ./foo
# chmod 4755 ./foo
# ls -l foo
-rwsr-xr-x 1 root root 6887 11 dic 17.44 foo
# exit
exit
$ ./foo /bin/sh
setuid(0) NOT executed
# exit <<<<< ROOT SHELL
$ ./foo /bin/sh 12345
setuid(0) executed
# exit <<<<< ROOT SHELL
On Slackware 14 (32 bit), it behaves differently:
$ gcc foo.c -o foo
$ su
Password: *****
bash-4.2# chown root:root ./foo
bash-4.2# chmod 4755 ./foo
bash-4.2# ls -l foo
-rwsr-xr-x 1 root root 6292 dic 11 17:53 foo
bash-4.2# exit
exit
$ foo /bin/sh
setuid(0) NOT executed
sh-4.2$ exit <<<<< USER SHELL
exit
$ foo /bin/sh 12345
setuid(0) executed
sh-4.2# exit <<<<< ROOT SHELL
exit
If I give "/usr/bin/dolphin" as argument, there is also a different behaviour.
On Debian I'm not able to get it work:
$ ./foo /usr/bin/dolphin
setuid(0) NOT executed
<unknown program name>(28884)/: KUniqueApplication: Cannot find the D-Bus session server: "Unable to autolaunch when setuid"
<unknown program name>(28883)/: KUniqueApplication: Pipe closed unexpectedly.
On Slackware, it works only if argc == 2, so I cannot start dolphin as root.
Why?
To explain the slightly peculiar setuid behaviour, you need to understand that /bin/sh may actually be bash, and the default behaviour of bash is to drop euid unless it's invoked with -p.
This means that if you invoke bash with -p, then you should see a 'root' like shell:-
natsu ~> id -a
uid=1000(me) gid=1000(me) groups=1000(me),4(adm),15(kmem)
natsu ~> ./foo "/bin/bash -p"
setuid(0) NOT executed
bash-4.2# id -a
uid=1000(me) gid=1000(me) euid=0(root) egid=0(root) groups=0(root),4(adm),15(kmem),1000(me)
Whereas invoked without the -p option yields the observed behaviour:
pshanahan#natsu ~> ./foo /bin/bash
setuid(0) NOT executed
bash-4.2$ id -a
uid=1000(me) gid=1000(me) groups=1000(me),4(adm),15(kmem)
but in reality, you only have effective user id 0, not real user id 0.
getting the GUI to run in this situation... That's another matter altogether; but this should help you understand the behaviour in this case.
Related
This seems to be a similar issue to the one listed in "cygheap read copy failed ... Win32 error 6" after linking program with gettext · Issue #1794 · msys2/MSYS2-packages · GitHub, but I cannot find a solution for my use case.
I'm working on Windows 10, and below is a bash script, that generates a simple test .c file that uses libintl, and builds it as an executable under Cygwin, while statically linking the Cygwin libintl and libiconv libraries. Simply copy the script and run it with bash test_intl.sh in a Cygwin shell (Cygwin64 Terminal):
test_intl.sh
# example from https://localazy.com/blog/make-multi-language-application-in-c-gettext-localazy
mkdir -p /tmp/testintl/locales/en/LC_MESSAGES
cd /tmp/testintl
cat > testintl.c <<'EOF'
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <libintl.h>
#include <locale.h>
#define _(String) gettext(String)
int main(int argc, char *argv[]) {
int opt;
char *filename = NULL;
int i_flag = 0;
int g_flag = 0;
bindtextdomain ("base", "./locales/");
textdomain ("base");
while ( (opt = getopt(argc, argv, "hi:g:")) != -1 ) {
switch(opt) {
case 'h':
printf(_("\033[1;37mThis application does absolutely nothing.\n\n"));
printf(_("Available options:\n"));
printf(_("-i <file>\tDoes nothing with specified file. \n"));
printf(_("-g <file>\tDoes nothing with specified file, but in \033[0;32mgreen color\033[0;37m.\n"));
break;
case 'i':
filename = optarg;
i_flag = 1;
break;
case 'g':
filename = optarg;
g_flag = 1;
break;
default:
printf(_("Use -h to display help.\n"));
return 0;
}
}
if (!i_flag && !g_flag) {
printf(_("Use -h to display help.\n"));
}
if ((i_flag || g_flag) && !filename) {
printf(_("You must specify a destination file name.\n"));
return 1;
}
if (i_flag) {
printf(_("Doing nothing with %s file...\n"), filename);
printf(_("Done.\n"));
}
if (g_flag) {
printf(_("\033[0;32mDoing nothing with %s file...\n"), filename);
printf(_("Done.\n"));
}
return 0;
}
EOF
set -x
xgettext -k_ -o locales/base.pot --language=C testintl.c
msginit --no-translator --input=locales/base.pot --locale=en_US --output=locales/en/base.po
msgfmt --output-file=locales/en/LC_MESSAGES/base.mo locales/en/base.po
gcc -g -Wall -Wextra -pedantic -Wno-unused-variable -std=c99 testintl.c -o testintl.exe /lib/libintl.a /lib/libiconv.a
./testintl.exe -h
# copy dependent libraries:
cp -a /bin/cygwin1.dll .
set +x
exepath=$(readlink -f ./testintl.exe)
echo "EXE location (cygwin):" $exepath
echo "EXE location (Windows):" $(cygpath -w $exepath)
When I run this in Cygwin shell, the output is:
user#DESKTOP-PC ~
$ bash test_intl.sh
+ xgettext -k_ -o locales/base.pot --language=C testintl.c
+ msginit --no-translator --input=locales/base.pot --locale=en_US --output=locales/en/base.po
Created locales/en/base.po.
+ msgfmt --output-file=locales/en/LC_MESSAGES/base.mo locales/en/base.po
+ gcc -g -Wall -Wextra -pedantic -Wno-unused-variable -std=c99 testintl.c -o testintl.exe /lib/libintl.a /lib/libiconv.a
+ ./testintl.exe -h
This application does absolutely nothing.
Available options:
-i <file> Does nothing with specified file.
-g <file> Does nothing with specified file, but in green color.
Use -h to display help.
+ cp -a /bin/cygwin1.dll .
+ set +x
EXE location (cygwin): /tmp/testintl/testintl.exe
EXE location (Windows): C:\cygwin64\tmp\testintl\testintl.exe
So, the executable clearly works in the Cygwin shell.
I also try running it from the Command Prompt (cmd.exe):
C:\Users\user>C:\cygwin64\tmp\testintl\testintl.exe -h
This application does absolutely nothing.
Available options:
-i <file> Does nothing with specified file.
-g <file> Does nothing with specified file, but in green color.
Use -h to display help.
So, the executable clearly works in the cmd.exe shell.
Now, I try to run this executable from a MSYS2 shell - either MSYS or MINGW64, makes no difference in behavior:
user#DESKTOP-PC 2023-01-15 MINGW64 ~
$ /c/cygwin64/tmp/testintl/testintl.exe -h
0 [main] testintl (19376) child_copy: cygheap read copy failed, 0x800000000..0x8000105A8, done 0, windows pid 19376, Win32 error 6
870 [main] testintl 1423 C:\cygwin64\tmp\testintl\testintl.exe: *** fatal error - couldn't create signal pipe, Win32 error 5
Clearly, the executable does not run under a MSYS2 shell.
Is there some workaround I can do, so this executable also works under a MSYS2 shell?
Here is a minimal reproducible example of my issue
#include <unistd.h>
int main (int argc, char *argv[])
{
char *a[] = {"ls", "-l", "~", "..", "-A", NULL};
execvp(a[0], a);
return 0;
}
I expected this to work as it would when I run ls -l ~ .. -A in the shell, but on running the executable after compiling I get the following output
❯ clang main.c -o a
❯ ./a
ls: -A: No such file or directory
ls: ~: No such file or directory
..:
total 0
drwxr-xr-x 3 me staff 96 Sep 1 11:56 test
I'm trying to merge a set of files using C system() call:
int main(int argc, char* argv[]) {
return system("cat output{1,2} > merged.out");
}
The result is:
$ gcc test.c
$ ./a.out
cat: output{1,2}: No such file or directory
It works if I do it directly in bash:
$ ls output{1,2}
output1 output2
$ cat output{1,2}
1,2
3,4
How can I enable parameter expansion in the system() call?
The reason for this is that system uses /bin/sh, which does not expand braces. For instance, try:
/bin/sh -c 'echo cat output{1,2}'
and compare
/bin/bash -c 'echo cat output{1,2}'
If you must, something like
system("/bin/bash -c 'cat output{1,2} > merged.out'");
but why not simply read both files and write the output to merged.out?
system passes the command to /bin/sh -c. If parameter expansion does not work as expected /bin/sh is not linked to /bin/bash on the system. You can explicitely start a bash shell using this:
return system("bash -c \"cat output{1,2} > merged.out\"");
Expansion is a shell duty. Something like `system("/usr/bin/bash -c \"cat output{1,2}\" > merged.out");" should work.
Here is a program which shows euid:
$ cat main.c
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv) {
printf("euid: %d\n", geteuid());
return 0;
}
$ gcc main.c -o main
$ ls -l main
-rwxr-xr-x 1 scdmb scdmb 6425 Mar 30 14:07 main
Let's set set-user-ID-on-execution option:
$ chmod u+s main
$ ls -l main
-rwsr-xr-x 1 scdmb scdmb 6425 Mar 30 14:07 main
Program executed as user scdmb shows right euid:
$ ./main
euid: 1000
$ id -u scdmb
1000
Let's execute program as other user:
$ id -u jakisuser
1001
$ su jakisuser
Password:
Now euid is the same as uid of user jakisuser:
$ ./main
euid: 1001
Why this set-user-ID-on-execution option doesn't cause that second time effective user id is not 1000 (as file owner) but 1001 (as the one who executes program)? Shouldn't it be the same as owner of file main?
I've just tried this here and your program works perfectly.
What I suspect is happening is that you have apparmor or selinux or something else in the way which is preventing your SUID bit from taking effect. I suggest you disable those and try again.
I am writing a user ls command code in C. When I compile this code with cc lss.c, an a.out file is created, but then using ./a.out to run, I get an error.
My lss.c:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char cmdline[100];
if ( argc > 2 )
{
printf(cmdline, "ls %s %s", argv[1], argv[2]);
system(cmdline);
}
return 0;
}
When I compile and run, this is what happens:
$ cd "/media/dilip/New Volume1/c"
$ cc lss.c
$ ./a.out
bash: ./a.out: Permission denied
$
What is the cause of this error?
I think, you are trying to run your program on an NTFS partition, different from the one on which Mint is installed. Try to compile the program in your ext4 partition and generate the a.out there. It should run.
Compile and Run it on the Volume where your linux is installed.