From 8f97bf0ba510a374d4ce3ab040a513c62779454f Mon Sep 17 00:00:00 2001 From: Arttu Tarkiainen Date: Thu, 25 Mar 2021 20:05:17 +0200 Subject: Use pkexec to start the headless process used for privileged operations Task-number: QTIFW-1794 Change-Id: I3225da785d52f0631b84341a4fe3d3db0fc0a171 Reviewed-by: Katja Marttila --- src/libs/installer/adminauthorization_x11.cpp | 234 ++------------------------ 1 file changed, 15 insertions(+), 219 deletions(-) (limited to 'src/libs/installer') diff --git a/src/libs/installer/adminauthorization_x11.cpp b/src/libs/installer/adminauthorization_x11.cpp index 14691c9de..f4951d523 100644 --- a/src/libs/installer/adminauthorization_x11.cpp +++ b/src/libs/installer/adminauthorization_x11.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -28,41 +28,18 @@ #include "adminauthorization.h" -#include -#include -#include +#include "globals.h" +#include #include -#include #include +#include -#include -#include #include -#include - -#ifdef Q_OS_LINUX -#include -#include -#else -#ifdef Q_OS_FREEBSD -#include -#include -#else -#include -#endif -#endif -#include -#include -#include -#include #include -#include "globals.h" - -#define SU_COMMAND "/usr/bin/sudo" -//#define SU_COMMAND "/bin/echo" +#define PKEXEC_COMMAND "/usr/bin/pkexec" namespace QInstaller { @@ -72,24 +49,6 @@ namespace QInstaller { \internal */ -static QString getPassword(QWidget *parent) -{ - if (qobject_cast (qApp) != 0) { - bool ok = false; - const QString result = QInputDialog::getText(parent, QObject::tr("Authorization required"), - QObject::tr("Enter your password to authorize for sudo:"), - QLineEdit::Password, QString(), &ok); - return ok ? result : QString(); - } else { - std::cout << QObject::tr("Authorization required").toStdString() << std::endl; - std::cout << QObject::tr("Enter your password to authorize for sudo:").toStdString() - << std::endl; - std::string password; - std::cin >> password; - return QString::fromStdString(password); - } -} - static void printError(QWidget *parent, const QString &value) { if (qobject_cast (qApp) != 0) { @@ -105,184 +64,21 @@ bool AdminAuthorization::execute(QWidget *parent, const QString &program, const const QString fallback = program + QLatin1String(" ") + arguments.join(QLatin1String(" ")); qCDebug(QInstaller::lcServer) << "Fallback:" << fallback; - // as we cannot pipe the password to su in QProcess, we need to setup a pseudo-terminal for it - int masterFD = -1; - int slaveFD = -1; - char ptsn[ PATH_MAX ]; + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + process.start(QLatin1String(PKEXEC_COMMAND), QStringList() << program << arguments, QIODevice::ReadOnly); - if (::openpty(&masterFD, &slaveFD, ptsn, 0, 0)) - return false; - - masterFD = ::posix_openpt(O_RDWR | O_NOCTTY); - if (masterFD < 0) - return false; - - const QByteArray ttyName = ::ptsname(masterFD); - - if (::grantpt(masterFD)) { - ::close(masterFD); - return false; - } - - ::revoke(ttyName); - ::unlockpt(masterFD); - - slaveFD = ::open(ttyName, O_RDWR | O_NOCTTY); - if (slaveFD < 0) { - ::close(masterFD); + if (!process.waitForStarted() || !process.waitForFinished(-1)) { + printError(parent, process.errorString()); + if (process.state() > QProcess::NotRunning) + process.kill(); return false; } - - ::fcntl(masterFD, F_SETFD, FD_CLOEXEC); - ::fcntl(slaveFD, F_SETFD, FD_CLOEXEC); - int pipedData[2]; - if (pipe(pipedData) != 0) - return false; - - int flags = ::fcntl(pipedData[0], F_GETFL); - if (flags != -1) - ::fcntl(pipedData[0], F_SETFL, flags | O_NONBLOCK); - - flags = ::fcntl(masterFD, F_GETFL); - if (flags != -1) - ::fcntl(masterFD, F_SETFL, flags | O_NONBLOCK); - - pid_t child = fork(); - - if (child < -1) { - ::close(masterFD); - ::close(slaveFD); - ::close(pipedData[0]); - ::close(pipedData[1]); - return false; - } - - // parent process - else if (child > 0) { - ::close(slaveFD); - //close writing end of pipe - ::close(pipedData[1]); - - QRegExp re(QLatin1String("[Pp]assword.*:")); - QByteArray data; - QByteArray errData; - int bytes = 0; - int errBytes = 0; - char buf[1024]; - char errBuf[1024]; - int status; - bool statusValid = false; - while (bytes >= 0) { - const pid_t waitResult = ::waitpid(child, &status, WNOHANG); - if (waitResult == -1) { - break; - } - if (waitResult == child) { - statusValid = true; - break; - } - bytes = ::read(masterFD, buf, 1023); - if (bytes == -1 && errno == EAGAIN) - bytes = 0; - else if (bytes > 0) - data.append(buf, bytes); - errBytes = ::read(pipedData[0], errBuf, 1023); - if (errBytes > 0) - { - errData.append(errBuf, errBytes); - errBytes=0; - } - if (bytes > 0) { - const QString line = QString::fromLatin1(data); - if (re.indexIn(line) != -1) { - const QString password = getPassword(parent); - if (password.isEmpty()) { - QByteArray pwd = password.toLatin1(); - for (int i = 0; i < 3; ++i) { - ::write(masterFD, pwd.data(), pwd.length()); - ::write(masterFD, "\n", 1); - } - return false; - } - QByteArray pwd = password.toLatin1(); - ::write(masterFD, pwd.data(), pwd.length()); - ::write(masterFD, "\n", 1); - ::read(masterFD, buf, pwd.length() + 1); - } - } - if (bytes == 0) - ::usleep(100000); - } - - while (true) { - errBytes = ::read(pipedData[0], errBuf, 1023); - if (errBytes == -1 && errno == EAGAIN) { - ::usleep(100000); - continue; - } - - if (errBytes <= 0) - break; - - errData.append(errBuf, errBytes); - } - - const bool success = statusValid && WIFEXITED(status) && WEXITSTATUS(status) == 0; - - if (!success && !errData.isEmpty()) { - printError(parent, QString::fromLocal8Bit(errData.constData())); - } - - ::close(pipedData[0]); - return success; - } - - // child process - else { - ::close(pipedData[0]); - // Reset signal handlers - for (int sig = 1; sig < NSIG; ++sig) - signal(sig, SIG_DFL); - signal(SIGHUP, SIG_IGN); - - ::setsid(); - - ::ioctl(slaveFD, TIOCSCTTY, 1); - int pgrp = ::getpid(); - ::tcsetpgrp(slaveFD, pgrp); - - ::dup2(slaveFD, 0); - ::dup2(slaveFD, 1); - ::dup2(pipedData[1], 2); - - // close all file descriptors - struct rlimit rlp; - getrlimit(RLIMIT_NOFILE, &rlp); - for (int i = 3; i < static_cast(rlp.rlim_cur); ++i) - ::close(i); - - char **argp = (char **) ::malloc((arguments.count() + 4) * sizeof(char *)); - QList args; - args.push_back(SU_COMMAND); - args.push_back("-b"); - args.push_back(program.toLocal8Bit()); - for (QStringList::const_iterator it = arguments.begin(); it != arguments.end(); ++it) - args.push_back(it->toLocal8Bit()); - - int i = 0; - for (QList::iterator it = args.begin(); it != args.end(); ++it, ++i) - argp[i] = it->data(); - argp[i] = 0; - - ::unsetenv("LANG"); - ::unsetenv("LC_ALL"); - - int exitStatus = 0; - if (::execv(SU_COMMAND, argp) == -1) - exitStatus = -errno; - _exit(exitStatus); + if (process.exitCode() != EXIT_SUCCESS) { + printError(parent, QLatin1String(process.readAll())); return false; } + return true; } // has no guarantee to work -- cgit v1.2.3