Workaround for the execrable qmake mistakenly thinking its setuid when run with uid==0 and euid!=0

Instead of dropping and regaining privileges, fork and run sub commands
in child with uid!=0
This commit is contained in:
Kovid Goyal 2025-07-15 14:07:21 +05:30
parent 4515dfbc85
commit abd93cd457
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 26 additions and 20 deletions

View File

@ -176,6 +176,7 @@ class Command:
SRC = SRC
RESOURCES = os.path.join(os.path.dirname(SRC), 'resources')
description = ''
drop_privileges_for_subcommands = False
sub_commands = []
@ -202,15 +203,6 @@ class Command:
if self.real_uid is not None:
os.seteuid(int(self.real_uid))
def regain_privileges(self):
if not islinux or ismacos or isfreebsd:
return
if os.geteuid() != 0 and self.orig_euid == 0:
self.info('Trying to get root privileges')
os.seteuid(0)
if os.getegid() != 0:
os.setegid(0)
def pre_sub_commands(self, opts):
pass
@ -225,8 +217,26 @@ class Command:
def run_cmd(self, cmd, opts):
from setup.commands import command_names
cmd.pre_sub_commands(opts)
for scmd in cmd.sub_commands:
self.run_cmd(scmd, opts)
if self.drop_privileges_for_subcommands and self.orig_euid is not None and os.getuid() == 0 and self.real_uid is not None:
if self.real_user is not None:
self.info('Dropping privileges to those of', self.real_user+':', self.real_uid)
pid = os.fork()
if pid == 0:
if self.real_gid is not None:
os.setgid(int(self.real_gid))
if self.real_uid is not None:
os.setuid(int(self.real_uid))
for scmd in cmd.sub_commands:
self.run_cmd(scmd, opts)
raise SystemExit(0)
else:
rpid, st = os.waitpid(pid, 0)
if code := os.waitstatus_to_exitcode(st) != 0:
sys.exit(code)
else:
for scmd in cmd.sub_commands:
self.run_cmd(scmd, opts)
st = time.time()
self.running(cmd)

View File

@ -109,10 +109,13 @@ def consolidate(envvar, default):
return [x for x in ans if x and os.path.exists(x)]
qraw = subprocess.check_output([QMAKE, '-query']).decode('utf-8')
qraw = None
def readvar(name):
global qraw
if qraw is None:
qraw = subprocess.check_output([QMAKE, '-query']).decode('utf-8')
return re.search(f'^{name}:(.+)$', qraw, flags=re.M).group(1).strip()

View File

@ -60,6 +60,7 @@ class Develop(Command):
''')
short_description = 'Setup a development environment for calibre'
MODE = 0o755
drop_privileges_for_subcommands = True
sub_commands = ['build', 'resources', 'iso639', 'iso3166', 'gui',]
@ -128,13 +129,6 @@ class Develop(Command):
'supported on linux. On other platforms, see the User Manual'
' for help with setting up a development environment.')
raise SystemExit(1)
if os.geteuid() == 0:
# We drop privileges for security, regaining them when installing
# files. Also ensures that any config files created as a side
# effect of the build process are not owned by root.
self.drop_privileges()
# Ensure any config files created as a side effect of importing calibre
# during the build process are in /tmp
os.environ['CALIBRE_CONFIG_DIRECTORY'] = os.environ.get('CALIBRE_CONFIG_DIRECTORY', '/tmp/calibre-install-config')
@ -142,7 +136,6 @@ class Develop(Command):
def run(self, opts):
self.manifest = []
self.opts = opts
self.regain_privileges()
self.consolidate_paths()
self.install_files()
self.write_templates()