Commit message: Disable greeters from loading KDE's debug hander

Some themes may use KDE components which will automatically load KDE's
crash handler.

If the greeter were to then somehow crash, that would leave a crash
handler allowing other actions, albeit as the locked down SDDM user.

Only SDDM users using the breeze theme from plasma-workspace are
affected. Safest and simplest fix is to handle this inside SDDM
disabling kcrash via an environment variable for all future themes that
may use these libraries.

CVE-2015-0856
CVE: CVE-2015-0856
CWE: CWE-284
Vulnerability description: daemon/Greeter.cpp in sddm before 0.13.0 does not properly disable the KDE crash handler, which allows local users to gain privileges by crashing a greeter when using certain themes, as demonstrated by the plasma-workspace breeze theme.

Project: sddm
File: Greeter.cpp

    void Greeter::setDisplay(Display *display) {
        m_display = display;
    }

Project: sddm
File: Greeter.cpp

    void Greeter::setSocket(const QString &socket) {
        m_socket = socket;
    }

Project: sddm
File: Greeter.cpp

    void Greeter::onHelperFinished(Auth::HelperExitStatus status) {
        // reset flag
        m_started = false;

        // log message
        qDebug() << "Greeter stopped.";

        // clean up
        m_auth->deleteLater();
        m_auth = nullptr;
    }

Project: sddm
File: Greeter.cpp

    void Greeter::authInfo(const QString &message, Auth::Info info) {
        Q_UNUSED(info);
        qDebug() << "Information from greeter session:" << message;
    }

Project: sddm
File: Greeter.cpp

    void Greeter::authError(const QString &message, Auth::Error error) {
        Q_UNUSED(error);
        qWarning() << "Error from greeter session:" << message;
    }

Project: sddm
File: Greeter.cpp

    Greeter::~Greeter() {
        stop();
    }

Project: sddm
File: Greeter.cpp

    Greeter::Greeter(QObject *parent) : QObject(parent) {
    }

Project: sddm
File: Greeter.cpp

    void Greeter::stop() {
        // check flag
        if (!m_started)
            return;

        // log message
        qDebug() << "Greeter stopping...";

        if (daemonApp->testing()) {
            // terminate process
            m_process->terminate();

            // wait for finished
            if (!m_process->waitForFinished(5000))
                m_process->kill();
        }
    }

Project: sddm
File: Greeter.cpp

    void Greeter::finished() {
        // check flag
        if (!m_started)
            return;

        // reset flag
        m_started = false;

        // log message
        qDebug() << "Greeter stopped.";

        // clean up
        m_process->deleteLater();
        m_process = nullptr;
    }

Project: sddm
File: Greeter.cpp

    void Greeter::onRequestChanged() {
        m_auth->request()->setFinishAutomatically(true);
    }

Project: sddm
File: Greeter.cpp

    bool Greeter::start() {
        // check flag
        if (m_started)
            return false;

        if (daemonApp->testing()) {
            // create process
            m_process = new QProcess(this);

            // delete process on finish
            connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(finished()));

            connect(m_process, SIGNAL(readyReadStandardOutput()), SLOT(onReadyReadStandardOutput()));
            connect(m_process, SIGNAL(readyReadStandardError()), SLOT(onReadyReadStandardError()));

            // log message
            qDebug() << "Greeter starting...";

            // set process environment
            QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
            env.insert(QStringLiteral("DISPLAY"), m_display->name());
            env.insert(QStringLiteral("XAUTHORITY"), m_authPath);
            env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get());
            m_process->setProcessEnvironment(env);

            // start greeter
            QStringList args;
            if (daemonApp->testing())
                args << QStringLiteral("--test-mode");
            args << QStringLiteral("--socket") << m_socket
                 << QStringLiteral("--theme") << m_theme;
            m_process->start(QStringLiteral("%1/sddm-greeter").arg(QStringLiteral(BIN_INSTALL_DIR)), args);

            //if we fail to start bail immediately, and don't block in waitForStarted
            if (m_process->state() == QProcess::NotRunning) {
                qCritical() << "Greeter failed to launch.";
                return false;
            }
            // wait for greeter to start
            if (!m_process->waitForStarted()) {
                // log message
                qCritical() << "Failed to start greeter.";

                // return fail
                return false;
            }

            // log message
            qDebug() << "Greeter started.";

            // set flag
            m_started = true;
        } else {
            // authentication
            m_auth = new Auth(this);
            m_auth->setVerbose(true);
            connect(m_auth, SIGNAL(requestChanged()), this, SLOT(onRequestChanged()));
            connect(m_auth, SIGNAL(session(bool)), this, SLOT(onSessionStarted(bool)));
            connect(m_auth, SIGNAL(finished(Auth::HelperExitStatus)), this, SLOT(onHelperFinished(Auth::HelperExitStatus)));
            connect(m_auth, SIGNAL(info(QString,Auth::Info)), this, SLOT(authInfo(QString,Auth::Info)));
            connect(m_auth, SIGNAL(error(QString,Auth::Error)), this, SLOT(authError(QString,Auth::Error)));

            // greeter command
            QStringList args;
            args << QStringLiteral("%1/sddm-greeter").arg(QStringLiteral(BIN_INSTALL_DIR));
            args << QStringLiteral("--socket") << m_socket
                 << QStringLiteral("--theme") << m_theme;

            // greeter environment
            QProcessEnvironment env;
            QProcessEnvironment sysenv = QProcessEnvironment::systemEnvironment();

            insertEnvironmentList({QStringLiteral("LANG"), QStringLiteral("LANGUAGE"),
                                   QStringLiteral("LC_CTYPE"), QStringLiteral("LC_NUMERIC"), QStringLiteral("LC_TIME"), QStringLiteral("LC_COLLATE"),
                                   QStringLiteral("LC_MONETARY"), QStringLiteral("LC_MESSAGES"), QStringLiteral("LC_PAPER"), QStringLiteral("LC_NAME"),
                                   QStringLiteral("LC_ADDRESS"), QStringLiteral("LC_TELEPHONE"), QStringLiteral("LC_MEASUREMENT"), QStringLiteral("LC_IDENTIFICATION"),
                                   QStringLiteral("LD_LIBRARY_PATH"),
                                   QStringLiteral("QML2_IMPORT_PATH"),
                                   QStringLiteral("QT_PLUGIN_PATH"),
                                   QStringLiteral("XDG_DATA_DIRS")
            }, sysenv, env);

            env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get());
            env.insert(QStringLiteral("DISPLAY"), m_display->name());
            env.insert(QStringLiteral("XAUTHORITY"), m_authPath);
            env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get());
            env.insert(QStringLiteral("XDG_SEAT"), m_display->seat()->name());
            env.insert(QStringLiteral("XDG_SEAT_PATH"), daemonApp->displayManager()->seatPath(m_display->seat()->name()));
            env.insert(QStringLiteral("XDG_SESSION_PATH"), daemonApp->displayManager()->sessionPath(QStringLiteral("Session%1").arg(daemonApp->newSessionId())));
            env.insert(QStringLiteral("XDG_VTNR"), QString::number(m_display->terminalId()));
            env.insert(QStringLiteral("XDG_SESSION_CLASS"), QStringLiteral("greeter"));
            env.insert(QStringLiteral("XDG_SESSION_TYPE"), m_display->sessionType());

            //some themes may use KDE components and that will automatically load KDE's crash handler which we don't want
            //counterintuitively setting this env disables that handler
            env.insert(QStringLiteral("KDE_DEBUG"), QStringLiteral("1"));
            m_auth->insertEnvironment(env);

            // log message
            qDebug() << "Greeter starting...";

            // start greeter
            m_auth->setUser(QStringLiteral("sddm"));
            m_auth->setGreeter(true);
            m_auth->setSession(args.join(QLatin1Char(' ')));
            m_auth->start();
        }

        // return success
        return true;
    }

Project: sddm
File: Greeter.cpp

    void Greeter::onReadyReadStandardError()
    {
        if (m_process) {
            qDebug() << "Greeter errors:" << qPrintable(QString::fromLocal8Bit(m_process->readAllStandardError()));
        }
    }

Project: sddm
File: Greeter.cpp

    void Greeter::setTheme(const QString &theme) {
        m_theme = theme;
    }

Project: sddm
File: Greeter.cpp

    void Greeter::insertEnvironmentList(QStringList names, QProcessEnvironment sourceEnv, QProcessEnvironment &targetEnv) {
        for (QStringList::const_iterator it = names.constBegin(); it != names.constEnd(); ++it)
            if (sourceEnv.contains(*it))
                targetEnv.insert(*it, sourceEnv.value(*it));
    }

Project: sddm
File: Greeter.cpp

    void Greeter::onSessionStarted(bool success) {
        // set flag
        m_started = success;

        // log message
        if (success)
            qDebug() << "Greeter session started successfully";
        else
            qDebug() << "Greeter session failed to start";
    }

Project: sddm
File: Greeter.cpp

    void Greeter::onReadyReadStandardOutput()
    {
        if (m_process) {
            qDebug() << "Greeter output:" << qPrintable(QString::fromLocal8Bit(m_process->readAllStandardOutput()));
        }
    }

Project: sddm
File: Greeter.cpp

    void Greeter::setAuthPath(const QString &authPath) {
        m_authPath = authPath;
    }