Consider the following simplified example which illustrates my problem.
The server's OS is Debian 11 armhf.
we're in directory /botm/test/
:
drwx------ 2 b b 4096 Sep 27 19:27 hide
-rwsr-xr-x 1 b b 8260 Sep 27 19:54 test1
-rwsr-xr-x 1 b b 8264 Sep 27 19:58 test2
-rw-r--r-- 1 b b 56 Sep 27 19:52 test1.awk
-rw-r--r-- 1 b b 172 Sep 27 19:54 test1.c
-rw-r--r-- 1 b b 186 Sep 27 19:58 test2.c
As can be seen, only the owner, b
can access the directory /botm/test/hide
.
We are now user test
We can not access the file /botm/test/hide/test.txt
:
$ cat hide/test.txt
cat: hide/test.txt: Permission denied
Ok. The programs test1
and test2
have the SETUID bit set.
So even if we are the user test
we can run these programs as if we were user b
.
First, test2.c
:
/* test2.c */
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int r;
r = execl("/usr/bin/cat","/usr/bin/cat","/botm/test/hide/test.txt", (char *)0);
printf("%d\n",r);
return r;
}
$ ./test2
hidden content
$
As expected, it works.
Now, test1.c
, test1.awk
:
/* test1.c */
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int r;
r = execl("/usr/bin/mawk","/usr/bin/mawk","-f","/botm/test/test1.awk", (char *)0);
printf("%d\n",r);
return r;
}
# test1.awk
BEGIN {
system("cat /botm/test/hide/test.txt");
};
$ ./test1
cat: /botm/test/hide/test.txt: Permission denied
$
Surprisingly, this does not work.
What I expect:
If I run the program test1
which has SETUID, then it should run as if the user b
.
So if the program calls /usr/bin/mawk
then that too should run as user b
.
So if the awk script then calls cat
, then cat
should also run as user b
.
So cat
should be able to access /botm/test/hide/test.txt
which is available only to user b
.
But this does not happen.
Some real context now.
This above was just a simplified example. I have a project with multiple C and AWK programs. The AWK programs are called from the C programs to process some text and generate some output. This includes sometimes calling cat
or other system tools. Some of these programs are run from the Apache server as the user www-data
which does not have access (and shouldn't have) to some of the temporary files created by these programs. That's why SETUID is used. And this kind of workflow is used a lot in this project (from 2014).
I'm now moving this from my old server (where this worked) to a new one (where this doesn't work). Changing the project to not rely on this mechanism would be a very big redesign which I don't want to do now.
Why is SETUID not preserved when a C program (with SETUID) calls an AWK script which calls a system tool (cat
, ...)?
What to do to preserve it again?
On a previous version of the system it worked.
EDITED TO ADD:
I verified that the AWK program still has access to the hidden file, only things called by system()
don't.
So I found out that the reason of this behavior is that when using system()
(or getline
with a pipe) then mawk
calls sh
to perform the command.
AWK doesn't have exec
or anything like that.
sh
will drop the SETUID unless called with -p
.
This is new behaviour. This part of man sh
is not present on my old server:
-p priviliged Do not attempt to reset effective uid if it does not match uid. This is not set by default to help avoid incorrect usage by
setuid root programs via system(3) or popen(3).
So to make my programs work again I have to achieve at least 1 of these things:
- make
/usr/bin/sh
not drop SETUID by default. - make
mawk
or other compatible AWK interpreter use-p
when using/usr/bin/sh
forsystem()
- find a different way to run programs from AWK
- redesign all the programs to not rely on this. For example do them in C, Perl, or Python which do have proper exec functionality not relying on
sh
.
Redesigning the entire project (and some other projects which also use this workflow) would take effort and time which I currently don't have so I really need to restore the full SETUID functionality somehow.
Actually, the functionality of cat
can be easily recreated inside the AWK program but also other tools are called with system()
in my project: [ -f
, cat
, cp
, mkdir
, mv
, sleep
, wget
.
And in most cases calling them without SETUID is not acceptable.
First link: does show the reason but the proposed solution is not relevant to my situation.
Second link: Since my AWK scripts are always called from C programs, the trick with
setresuid
looks promising. I will try it. – bicyclesonthemoon Sep 28 '22 at 20:32execl
and much less awk. – Vilinkameni Sep 29 '22 at 08:26