QProcess/Unix: add a simple way to reset the UID and GID for the child
This is done as one of the last steps inside QProcess itself, so the child modifier and all other tasks still run with the parent process' permissions. On Linux, setting the UID to non-zero will also automatically clear the effective capabilities(7) set. This feature is only useful for setuid or setgid applications, so this commit updates the QCoreApplication::setSetuidAllowed() documentation to mention the QProcess flag. Change-Id: I3e3bfef633af4130a03afffd175e940c0668d244 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
parent
13a1995e9d
commit
271901c5cf
@ -873,6 +873,12 @@ void QProcessPrivate::Channel::clear()
|
||||
terminate immediately; with this flag, the write operation fails
|
||||
without a signal and the child may continue executing.
|
||||
|
||||
\value [since 6.7] ResetIds Drops any retained, effective user or group
|
||||
ID the current process may still have (see \c{setuid(2)} and
|
||||
\c{setgid(2)}, plus QCoreApplication::setSetuidAllowed()). This is
|
||||
useful if the current process was setuid or setgid and does not wish
|
||||
the child process to retain the elevated privileges.
|
||||
|
||||
\value ResetSignalHandlers Resets all Unix signal handlers back to their
|
||||
default state (that is, pass \c SIG_DFL to \c{signal(2)}). This flag
|
||||
is useful to ensure any ignored (\c SIG_IGN) signal does not affect
|
||||
|
@ -184,6 +184,7 @@ public:
|
||||
UseVFork = 0x0020, // like POSIX_SPAWN_USEVFORK
|
||||
CreateNewSession = 0x0040, // like POSIX_SPAWN_SETSID
|
||||
DisconnectControllingTerminal = 0x0080,
|
||||
ResetIds = 0x0100, // like POSIX_SPAWN_RESETIDS
|
||||
};
|
||||
Q_DECLARE_FLAGS(UnixProcessFlags, UnixProcessFlag)
|
||||
struct UnixProcessParameters
|
||||
|
@ -848,6 +848,15 @@ static const char *applyProcessParameters(const QProcess::UnixProcessParameters
|
||||
}
|
||||
}
|
||||
|
||||
// Apply UID and GID parameters last. This isn't expected to fail either:
|
||||
// either we're trying to impersonate what we already are, or we're EUID or
|
||||
// EGID root, in which case we are allowed to do this.
|
||||
if (params.flags.testFlag(QProcess::UnixProcessFlag::ResetIds)) {
|
||||
int r = setgid(getgid());
|
||||
r = setuid(getuid());
|
||||
(void) r;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -987,7 +987,10 @@ QCoreApplication::~QCoreApplication()
|
||||
and must be set before a QCoreApplication instance is created.
|
||||
|
||||
\note It is strongly recommended not to enable this option since
|
||||
it introduces security risks.
|
||||
it introduces security risks. If this application does enable the flag and
|
||||
starts child processes, it should drop the privileges as early as possible
|
||||
by calling \c{setuid(2)} for itself, or at the latest by using the
|
||||
QProcess::UnixProcessParameters::ResetIds flag.
|
||||
*/
|
||||
void QCoreApplication::setSetuidAllowed(bool allow)
|
||||
{
|
||||
|
@ -29,6 +29,14 @@ int main(int argc, char **argv)
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (cmd == "reset-ids") {
|
||||
if (getuid() == geteuid() && getgid() == getegid())
|
||||
return EXIT_SUCCESS;
|
||||
fprintf(stderr, "Real: %d %d; Effective: %d %d\n",
|
||||
getuid(), getgid(), geteuid(), getegid());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (cmd == "reset-sighand") {
|
||||
bool ok = true;
|
||||
|
||||
|
@ -1733,6 +1733,7 @@ void tst_QProcess::unixProcessParameters_data()
|
||||
addRow("ignore-sigpipe", P::IgnoreSigPipe);
|
||||
addRow("file-descriptors", P::CloseFileDescriptors);
|
||||
addRow("setsid", P::CreateNewSession);
|
||||
addRow("reset-ids", P::ResetIds);
|
||||
|
||||
// On FreeBSD, we need to be session leader to disconnect from the CTTY
|
||||
addRow("noctty", P::DisconnectControllingTerminal | P::CreateNewSession);
|
||||
@ -1784,6 +1785,11 @@ void tst_QProcess::unixProcessParameters()
|
||||
}
|
||||
} scope;
|
||||
|
||||
if (params.flags & QProcess::UnixProcessFlag::ResetIds) {
|
||||
if (getuid() == geteuid() && getgid() == getegid())
|
||||
qInfo("Process has identical real and effective IDs; this test will do nothing");
|
||||
}
|
||||
|
||||
if (params.flags & QProcess::UnixProcessFlag::DisconnectControllingTerminal) {
|
||||
if (int fd = open("/dev/tty", O_RDONLY); fd < 0) {
|
||||
qInfo("Process has no controlling terminal; this test will do nothing");
|
||||
|
Loading…
x
Reference in New Issue
Block a user