This is not how setuid works. When a root depersonalizes itself, by design it cannot be restored to root. As soon as you give up root (in this context), it disappears.
If you use setuid as root, you cannot return.
I assume os.setuid is a thin proxy before level C is called. On the man page:
If the root user or program is installed, then the user ID root must be especially respected. The setuid () function checks the effective user ID of the caller, and if he is the superuser, all user IDs associated with the process are set to uid. After that, the program will not be able to restore root privileges.
As to why root cannot be obtained, consider typical use. Imagine an Apache server that goes down to www (or some non-privileged user) to handle actual requests. If you can restore root, a Python script (or PHP / Perl / CGI / etc) can restore root and cause complete chaos.
As for the solution, you can use seteuid instead (os.seteuid - once again, a simple proxy to level C seteuid ). The python documentation on setuid and seteuid looks pretty bad, but there is a ton of documentation on system calls.
As for the security of temporary root abandonment and restoration ... you need to be very careful. If the malicious code has the ability to get root, you will be screwed. For this reason, it would be nice to branch out the child process (as suggested by user 4815162342). The child process will not be able to restore the root directory. More about the problems can be found here . Read more about the common oddities of setuid here .
The idea is to set an effective user id using seteuid and create a new process. Due to how exec works, the effective user ID will be copied to the saved uid of the new process. Since the stored uid is no longer the root, root cannot be changed back. More interesting documentation can be found here .
The most important parts:
If the installation identifier bit is set in the program file pointed to by the file name, and the base file system is not set to nosuid (MS_NOSUID flag for mount (2)), and the calling process is not processed, the effective user identifier of the calling process is changed to the name of the owner program file. Similarly, when the set-group-ID bit of a program file is set, the effective group identifier of the calling process is set to the program file group.
The effective process user ID is copied to the stored set-user-ID; likewise, the effective group identifier is copied to the stored set-group-ID. This copying occurs after any effective identifier changes that occur due to the bits of the user ID set and the group ID set.