File acess permissions missing after setuid() system call
3
votes
1
answer
49
views
I have a file access problem in a self developed daemon process after a setuid() system call. I already post this question to SO but the impression is that the problem is not C++ related but Linux related and so maybe there is someone here who could help me solving it.
My daemon program cannot access a configuration file after a setuid(iUid) systemcall even though iUid is owner of the configuration file. Why?
I am writing a controller daemon in C++ for home automation which finally will run on an raspberry pi with Raspberry Pi OS. It is started with root permissions as after start it should read an SSL certifacate which only root is granted read access. After the SSL certifacte is read the daemon should switch to user 'pvmonitor' as root permissions are no longer needed. This is done by
setuid( iUid );
and I have checked with ps that the process runs as user 'pvmonitor'.
The configuration file for this daemon is located at /etc/SmartHome/converd.conf and is owned by user pvmonitor.
ls -la /etc/SmartHome/
total 24
drwxrwx---+ 2 pvmonitor www-data 4096 Jul 17 20:07 .
drwxr-xr-x+ 107 root root 4096 Jul 17 20:07 ..
-rw-r-----+ 1 pvmonitor www-data 705 Jul 17 20:07 coverd.conf
The raspberry pi is booted from network and the file system is mounted from a NAS which provides an ACL. Also ACL grants access permission to user pvmonitor:
getfacl /etc/
getfacl: Removing leading '/' from absolute path names
# file: etc/
# owner: root
# group: root
user::rwx
[...]
group::---
group:users:rwx #effective:r-x
group:www-data:r-x
mask::r-x
other::r-x
[...]
getfacl /etc/SmartHome/
getfacl: Removing leading '/' from absolute path names
# file: etc/SmartHome/
# owner: pvmonitor
# group: www-data
user::rwx
[...]
user:pvmonitor:rwx
[...]
group::---
[...]
group:www-data:r-x
mask::rwx
other::---
[...]
getfacl /etc/SmartHome/coverd.conf
getfacl: Removing leading '/' from absolute path names
# file: etc/SmartHome/coverd.conf
# owner: pvmonitor
# group: www-data
user::rw-
[...]
user:pvmonitor:rwx #effective:r--
[...]
group::---
[...]
group:www-data:r-x #effective:r--
mask::r--
other::---
In addition the output of stat:
stat /etc
File: /etc
Size: 4096 Blocks: 16 IO Block: 4096 directory
Device: 0,22 Inode: 74579976 Links: 107
Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2024-12-03 22:14:03.809660810 +0100
Modify: 2025-07-17 20:07:13.645754180 +0200
Change: 2025-07-17 20:07:13.645754180 +0200
Birth: -
stat /etc/SmartHome/
File: /etc/SmartHome/
Size: 4096 Blocks: 16 IO Block: 4096 directory
Device: 0,22 Inode: 74581572 Links: 2
Access: (0770/drwxrwx---) Uid: ( 1004/pvmonitor) Gid: ( 133/www-data)
Access: 2025-07-17 20:06:03.525754180 +0200
Modify: 2025-07-17 20:07:08.395754180 +0200
Change: 2025-07-17 20:35:52.235754180 +0200
Birth: -
stat /etc/SmartHome/coverd.conf
File: /etc/SmartHome/coverd.conf
Size: 705 Blocks: 16 IO Block: 131072 regular file
Device: 0,22 Inode: 74581810 Links: 1
Access: (0640/-rw-r-----) Uid: ( 1004/pvmonitor) Gid: ( 133/www-data)
Access: 2025-07-17 20:07:08.395754180 +0200
Modify: 2025-07-17 20:07:08.395754180 +0200
Change: 2025-07-18 09:33:38.783696180 +0200
Birth: -
With
sudo -u pvmonitor less /etc/SmartHome/coverd.conf
I can read the configuration file without any problem.
But when I try to open the configuration file in my daemon process after the setuid(); command I get an "permission denied" error. Here is a minimum reproducable example which is based on excerpts of my daemons code:
#include
#include
#include
#include
const char *ptConfigFile = "/etc/SmartHome/coverd.conf";
void printConfig( void )
{
std::cout << "Try to open file " << ptConfigFile << std::endl;
FILE *ptfTest;
ptfTest = fopen( ptConfigFile, "r" );
if (ptfTest != nullptr)
{
char sLine;
while (!feof(ptfTest))
{
fgets(sLine,1023,ptfTest);
std::cout << sLine;
}
fclose( ptfTest );
}
else
perror( "Failed to open file" );
}
int main(int argc, char **argv )
{
int iUid = 1004;
std::cout << "User id is now " << getuid() << std::endl;
printConfig();
std::cout << "Switch to user id " << iUid << std::endl;
if (iUid == 0 || setuid(iUid)== 0)
{
std::cout << "User id is now " << getuid() << std::endl;
printConfig();
return 0;
}
std::cerr << "Could not switch user id." << std::endl;
return -1;
}
1004 is the user id of user pvmonitor. The output of this example is:
sudo ./test
User id is now 0
Try to open file /etc/SmartHome/coverd.conf
CERTFILE=[...]
[...]
Switch to user id 1004
User id is now 1004
Try to open file /etc/SmartHome/coverd.conf
Failed to open file: Permission denied
In addition here is the output when I run the test program with strace:
sudo strace ./test
execve("./test", ["./test"], 0x7fc90538b0 /* 13 vars */) = 0
[...]
setuid(1004) = 0
getuid() = 1004
write(1, "User id is now 1004\n", 20User id is now 1004
) = 20
write(1, "Try to open file /etc/SmartHome/"..., 44Try to open file /etc/SmartHome/coverd.conf
) = 44
openat(AT_FDCWD, "/etc/SmartHome/coverd.conf", O_RDONLY) = -1 EACCES (Permission denied)
dup(2) = 3
fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
newfstatat(3, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x2), ...}, AT_EMPTY_PATH) = 0
write(3, "Failed to open file: Permission "..., 39Failed to open file: Permission denied
) = 39
close(3) = 0
exit_group(0) = ?
What am I doing wrong?
Asked by Holger
(33 rep)
Jul 17, 2025, 06:37 PM
Last activity: Jul 18, 2025, 12:24 PM
Last activity: Jul 18, 2025, 12:24 PM