diff --git a/REUSE.toml b/REUSE.toml index 6b95a98..f3b1e7c 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -69,7 +69,7 @@ SPDX-FileCopyrightText = "None" SPDX-License-Identifier = "GPL-2.0-or-later" [[annotations]] -path = ["releng/prepare-relnotes", "src/auth/Auth.cpp", "src/auth/Auth.h", "src/auth/AuthMessages.h", "src/auth/AuthPrompt.cpp", "src/auth/AuthPrompt.h", "src/auth/AuthRequest.cpp", "src/auth/AuthRequest.h", "src/common/ConfigReader.cpp", "src/common/ConfigReader.h", "src/common/Configuration.cpp", "src/common/Configuration.h", "src/common/MessageHandler.h", "src/common/Messages.h", "src/common/SafeDataStream.cpp", "src/common/SafeDataStream.h", "src/common/Session.cpp", "src/common/Session.h", "src/common/SignalHandler.cpp", "src/common/SignalHandler.h", "src/common/SocketWriter.cpp", "src/common/SocketWriter.h", "src/common/ThemeConfig.cpp", "src/common/ThemeConfig.h", "src/common/ThemeMetadata.cpp", "src/common/ThemeMetadata.h", "src/common/VirtualTerminal.cpp", "src/common/VirtualTerminal.h", "src/common/XAuth.cpp", "src/common/XAuth.h", "src/daemon/DaemonApp.cpp", "src/daemon/DaemonApp.h", "src/daemon/Display.cpp", "src/daemon/Display.h", "src/daemon/DisplayManager.cpp", "src/daemon/DisplayManager.h", "src/daemon/DisplayServer.cpp", "src/daemon/DisplayServer.h", "src/daemon/Greeter.cpp", "src/daemon/Greeter.h", "src/daemon/PowerManager.cpp", "src/daemon/PowerManager.h", "src/daemon/Seat.cpp", "src/daemon/Seat.h", "src/daemon/SeatManager.cpp", "src/daemon/SeatManager.h", "src/daemon/SocketServer.cpp", "src/daemon/SocketServer.h", "src/daemon/TreelandConnector.cpp", "src/daemon/TreelandConnector.h", "src/daemon/Utils.h", "src/daemon/WaylandDisplayServer.cpp", "src/daemon/WaylandDisplayServer.h", "src/daemon/XorgDisplayServer.cpp", "src/daemon/XorgDisplayServer.h", "src/daemon/XorgUserDisplayServer.cpp", "src/daemon/XorgUserDisplayServer.h", "src/greeter/GreeterApp.h", "src/greeter/GreeterProxy.cpp", "src/greeter/GreeterProxy.h", "src/greeter/SessionModel.cpp", "src/greeter/SessionModel.h", "src/greeter/UserModel.cpp", "src/greeter/UserModel.h", "src/helper/Pam.cpp", "src/helper/Pam.h", "src/helper/HelperApp.cpp", "src/helper/HelperApp.h", "src/helper/HelperStartWayland.cpp", "src/helper/HelperStartX11User.cpp", "src/helper/UserSession.cpp", "src/helper/UserSession.h", "src/helper/waylandhelper.cpp", "src/helper/waylandhelper.h", "src/helper/waylandsocketwatcher.cpp", "src/helper/waylandsocketwatcher.h", "src/helper/xorguserhelper.cpp", "src/helper/xorguserhelper.h", "src/common/LogindDBusTypes.cpp", "src/common/LogindDBusTypes.h", "src/greeter/GreeterApp.cpp"] +path = ["releng/prepare-relnotes", "src/daemon/Auth.cpp", "src/daemon/Auth.h", "src/common/ConfigReader.cpp", "src/common/ConfigReader.h", "src/common/Configuration.cpp", "src/common/Configuration.h", "src/common/MessageHandler.h", "src/common/Messages.h", "src/common/SafeDataStream.cpp", "src/common/SafeDataStream.h", "src/common/Session.cpp", "src/common/Session.h", "src/common/SignalHandler.cpp", "src/common/SignalHandler.h", "src/common/SocketWriter.cpp", "src/common/SocketWriter.h", "src/common/ThemeConfig.cpp", "src/common/ThemeConfig.h", "src/common/ThemeMetadata.cpp", "src/common/ThemeMetadata.h", "src/common/VirtualTerminal.cpp", "src/common/VirtualTerminal.h", "src/common/XAuth.cpp", "src/common/XAuth.h", "src/daemon/DaemonApp.cpp", "src/daemon/DaemonApp.h", "src/daemon/Display.cpp", "src/daemon/Display.h", "src/daemon/DisplayManager.cpp", "src/daemon/DisplayManager.h", "src/daemon/DisplayServer.cpp", "src/daemon/DisplayServer.h", "src/daemon/Greeter.cpp", "src/daemon/Greeter.h", "src/daemon/PowerManager.cpp", "src/daemon/PowerManager.h", "src/daemon/Seat.cpp", "src/daemon/Seat.h", "src/daemon/SeatManager.cpp", "src/daemon/SeatManager.h", "src/daemon/SocketServer.cpp", "src/daemon/SocketServer.h", "src/daemon/TreelandConnector.cpp", "src/daemon/TreelandConnector.h", "src/daemon/Utils.h", "src/daemon/WaylandDisplayServer.cpp", "src/daemon/WaylandDisplayServer.h", "src/daemon/XorgDisplayServer.cpp", "src/daemon/XorgDisplayServer.h", "src/daemon/XorgUserDisplayServer.cpp", "src/daemon/XorgUserDisplayServer.h", "src/greeter/GreeterApp.h", "src/greeter/GreeterProxy.cpp", "src/greeter/GreeterProxy.h", "src/greeter/SessionModel.cpp", "src/greeter/SessionModel.h", "src/greeter/UserModel.cpp", "src/greeter/UserModel.h", "src/daemon/Pam.cpp", "src/daemon/Pam.h", "src/daemon/UserSession.cpp", "src/daemon/UserSession.h", "src/common/LogindDBusTypes.cpp", "src/common/LogindDBusTypes.h", "src/greeter/GreeterApp.cpp"] precedence = "aggregate" SPDX-FileCopyrightText = "None" SPDX-License-Identifier = "GPL-2.0-or-later" diff --git a/debian/ddm.install b/debian/ddm.install index d6a843e..33ae3be 100644 --- a/debian/ddm.install +++ b/debian/ddm.install @@ -1,5 +1,4 @@ usr/bin/* -usr/libexec/* usr/share/* usr/lib/systemd/* usr/lib/tmpfiles.d/* diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c2c4925..e6fb938 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,4 @@ if (QT_KNOWN_POLICY_QTP0001) endif() add_subdirectory(common) -add_subdirectory(auth) add_subdirectory(daemon) -add_subdirectory(helper) diff --git a/src/auth/Auth.cpp b/src/auth/Auth.cpp deleted file mode 100644 index 50ad8f1..0000000 --- a/src/auth/Auth.cpp +++ /dev/null @@ -1,508 +0,0 @@ -/* - * Qt Authentication Library - * Copyright (C) 2013 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "Auth.h" -#include "Constants.h" -#include "AuthMessages.h" -#include "SafeDataStream.h" - -#include -#include -#include -#include - -#include - -#include - -#include -#include - -namespace DDM { - class Auth::SocketServer : public QLocalServer { - Q_OBJECT - public slots: - void handleNewConnection(); - public: - static SocketServer *instance(); - - QMap helpers; - private: - SocketServer(); - }; - - class Auth::Private : public QObject { - Q_OBJECT - public: - Private(Auth *parent); - ~Private(); - void setSocket(QLocalSocket *socket); - public slots: - void dataPending(); - void childExited(int exitCode, QProcess::ExitStatus exitStatus); - void childError(QProcess::ProcessError error); - void requestFinished(); - public: - AuthRequest *request { nullptr }; - QProcess *child { nullptr }; - QLocalSocket *socket { nullptr }; - QString displayServerCmd; - QString sessionPath { }; - Session::Type sessionType { Session::UnknownSession }; - QString sessionFileName { }; - QString user { }; - QString password { }; - QByteArray cookie { }; - bool autologin { false }; - bool greeter { false }; - bool singleMode { false }; - bool identifyOnly { false }; - bool skipAuth { false }; - QProcessEnvironment environment { }; - qint64 id { 0 }; - static qint64 lastId; - QString sessionId; - int tty { 0 }; - int xdgSessionId { 0 }; - }; - - qint64 Auth::Private::lastId = 1; - - - - Auth::SocketServer::SocketServer() - : QLocalServer() { - connect(this, &QLocalServer::newConnection, this, &Auth::SocketServer::handleNewConnection); - } - - void Auth::SocketServer::handleNewConnection() { - while (hasPendingConnections()) { - Msg m = Msg::MSG_UNKNOWN; - qint64 id; - QLocalSocket *socket = nextPendingConnection(); - SafeDataStream str(socket); - str.receive(); - str >> m >> id; - if (m == Msg::HELLO && id && SocketServer::instance()->helpers.contains(id)) { - helpers[id]->setSocket(socket); - if (socket->bytesAvailable() > 0) - helpers[id]->dataPending(); - } - } - } - - Auth::SocketServer* Auth::SocketServer::instance() { - static std::unique_ptr self; - if (!self) { - self.reset(new SocketServer()); - self->listen(QStringLiteral("ddm-auth-%1").arg(QUuid::createUuid().toString(QUuid::WithoutBraces))); - } - return self.get(); - } - - - Auth::Private::Private(Auth *parent) - : QObject(parent) - , request(new AuthRequest(parent)) - , child(new QProcess(this)) - , id(lastId++) { - SocketServer::instance()->helpers[id] = this; - QProcessEnvironment env = child->processEnvironment(); - bool langEmpty = true; - QFile localeFile(QStringLiteral("/etc/locale.conf")); - if (localeFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - QTextStream in(&localeFile); - while (!in.atEnd()) { - QStringList parts = in.readLine().split(QLatin1Char('=')); - if (parts.size() >= 2) { - env.insert(parts[0], parts[1]); - if (parts[0] == QLatin1String("LANG")) - langEmpty = false; - } - } - localeFile.close(); - } - if (langEmpty) - env.insert(QStringLiteral("LANG"), QStringLiteral("C")); - child->setProcessEnvironment(env); - connect(child, QOverload::of(&QProcess::finished), this, &Auth::Private::childExited); - connect(child, &QProcess::errorOccurred, this, &Auth::Private::childError); - connect(request, &AuthRequest::finished, this, &Auth::Private::requestFinished); - connect(request, &AuthRequest::promptsChanged, parent, &Auth::requestChanged); - } - - Auth::Private::~Private() - { - SocketServer::instance()->helpers.remove(id); - } - - - void Auth::Private::setSocket(QLocalSocket *socket) { - this->socket = socket; - connect(socket, &QLocalSocket::readyRead, this, &Auth::Private::dataPending); - } - - void Auth::Private::dataPending() { - Auth *auth = qobject_cast(parent()); - Msg m = MSG_UNKNOWN; - SafeDataStream str(socket); - while (socket->bytesAvailable() > 0) { - str.receive(); - str >> m; - switch (m) { - case ERROR: { - QString message; - Error type = ERROR_NONE; - str >> message >> type; - Q_EMIT auth->error(message, type); - break; - } - case INFO: { - QString message; - Info type = INFO_NONE; - str >> message >> type; - Q_EMIT auth->info(message, type); - break; - } - case REQUEST: { - Request r; - str >> r; - request->setRequest(&r); - break; - } - case AUTHENTICATED: { - QString user; - str >> user; - if (!user.isEmpty()) { - auth->setUser(user); - Q_EMIT auth->authentication(user, true, auth->identifyOnly()); - str.reset(); - str << AUTHENTICATED << environment << cookie; - str.send(); - } - else { - Q_EMIT auth->authentication(auth->user(), false, auth->identifyOnly()); - } - break; - } - case SESSION_STATUS: { - bool status; - int sessionId; - str >> status >> sessionId; - if(!auth->identifyOnly()) { - Q_EMIT auth->sessionStarted(status, sessionId); - } - str.reset(); - str << SESSION_STATUS; - str.send(); - break; - } - case DISPLAY_SERVER_STARTED: { - QString displayName; - str >> displayName; - Q_EMIT auth->displayServerReady(displayName); - str.reset(); - str << DISPLAY_SERVER_STARTED; - str.send(); - break; - } - default: { - Q_EMIT auth->error(QStringLiteral("Auth: Unexpected value received: %1").arg(m), ERROR_INTERNAL); - } - } - } - } - - void Auth::Private::childExited(int exitCode, QProcess::ExitStatus exitStatus) { - if (exitStatus != QProcess::NormalExit) { - qWarning("Auth: ddm-helper (%s) crashed (exit code %d)", - qPrintable(child->arguments().join(QLatin1Char(' '))), - HelperExitStatus(exitStatus)); - Q_EMIT qobject_cast(parent())->error(child->errorString(), ERROR_INTERNAL); - } - - if (exitCode == HELPER_SUCCESS) - qDebug() << "Auth: ddm-helper exited successfully"; - else - qWarning("Auth: ddm-helper exited with %d", exitCode); - - Q_EMIT qobject_cast(parent())->finished((Auth::HelperExitStatus)exitCode); - } - - void Auth::Private::childError(QProcess::ProcessError error) { - Q_UNUSED(error); - Q_EMIT qobject_cast(parent())->error(child->errorString(), ERROR_INTERNAL); - } - - void Auth::Private::requestFinished() { - SafeDataStream str(socket); - Request r = request->request(); - str << REQUEST << r; - str.send(); - request->setRequest(); - } - - - Auth::Auth(const QString &user, const QString &session, bool autologin, QObject *parent, bool verbose) - : QObject(parent) - , d(new Private(this)) { - setUser(user); - setAutologin(autologin); - setSession(session); - setVerbose(verbose); - } - - Auth::Auth(QObject* parent) - : QObject(parent) - , d(new Private(this)) { - } - - Auth::~Auth() { - stop(); - delete d; - } - - void Auth::registerTypes() { - qmlRegisterAnonymousType("Auth", 1); - qmlRegisterAnonymousType("Auth", 1); - qmlRegisterType("Auth", 1, 0, "Auth"); - } - - bool Auth::autologin() const { - return d->autologin; - } - - bool Auth::isGreeter() const - { - return d->greeter; - } - - const QByteArray& Auth::cookie() const { - return d->cookie; - } - - const QString &Auth::session() const { - return d->sessionPath; - } - - const QString &Auth::password() const { - return d->password; - } - - const QString &Auth::user() const { - return d->user; - } - - bool Auth::verbose() const { - return d->child->processChannelMode() == QProcess::ForwardedChannels; - } - - AuthRequest *Auth::request() { - return d->request; - } - - QString Auth::sessionId() const { - return d->sessionId; - } - - Session::Type Auth::sessionType() const { - return d->sessionType; - } - - QString Auth::sessionFileName() const { - return d->sessionFileName; - } - - bool Auth::isSingleMode() const { - return d->singleMode; - } - - bool Auth::isActive() const { - return d->child->state() != QProcess::NotRunning; - } - - void Auth::insertEnvironment(const QProcessEnvironment &env) { - d->environment.insert(env); - } - - void Auth::insertEnvironment(const QString &key, const QString &value) { - d->environment.insert(key, value); - } - - void Auth::setCookie(const QByteArray& cookie) { - if (cookie != d->cookie) { - d->cookie = cookie; - Q_EMIT cookieChanged(); - } - } - - void Auth::setUser(const QString &user) { - if (user != d->user) { - d->user = user; - Q_EMIT userChanged(); - } - } - - void Auth::setPassword(const QString &password) { - if (password != d->password) { - d->password = password; - } - } - - void Auth::setAutologin(bool on) { - if (on != d->autologin) { - d->autologin = on; - Q_EMIT autologinChanged(); - } - } - - void Auth::setGreeter(bool on) - { - if (on != d->greeter) { - d->greeter = on; - Q_EMIT greeterChanged(); - } - } - - void Auth::setDisplayServerCommand(const QString &command) - { - if (d->displayServerCmd != command) { - d->displayServerCmd = command; - Q_EMIT displayServerCommandChanged(); - } - } - - void Auth::setSession(const QString& path) { - if (path != d->sessionPath) { - d->sessionPath = path; - Q_EMIT sessionChanged(); - } - } - - void Auth::setSingleMode(bool on) - { - if (on != d->singleMode) { - d->singleMode = on; - Q_EMIT singleModeChanged(); - } - } - - void Auth::setSessionId(const QString& sessionId) - { - if (sessionId != d->sessionId) { - d->sessionId = sessionId; - } - } - - void Auth::setSessionType(const Session::Type type) { - d->sessionType = type; - } - - void Auth::setSessionFileName(const QString &fileName) { - d->sessionFileName = fileName; - } - - int Auth::tty() const { - return d->tty; - } - - void Auth::setTTY(int tty) { - if (tty != d->tty) { - d->tty = tty; - } - } - - int Auth::xdgSessionId() const { - return d->xdgSessionId; - } - - void Auth::setXdgSessionId(int xdgSessionId) { - d->xdgSessionId = xdgSessionId; - } - - void Auth::setVerbose(bool on) { - if (on != verbose()) { - if (on) - d->child->setProcessChannelMode(QProcess::ForwardedChannels); - else - d->child->setProcessChannelMode(QProcess::SeparateChannels); - Q_EMIT verboseChanged(); - } - } - - bool Auth::identifyOnly() const { - return d->identifyOnly; - } - - void Auth::setIdentifyOnly(bool on) { - if (on != d->identifyOnly) { - d->identifyOnly = on; - } - } - - void Auth::setSkipAuth(bool on) { - if (on != d->skipAuth) { - d->skipAuth = on; - } - } - - void Auth::start() { - QStringList args; - args << QStringLiteral("--socket") << SocketServer::instance()->fullServerName(); - args << QStringLiteral("--id") << QString::number(d->id); - if (!d->sessionPath.isEmpty()) - args << QStringLiteral("--start") << d->sessionPath; - if (!d->user.isEmpty()) - args << QStringLiteral("--user") << d->user; - if (d->autologin) - args << QStringLiteral("--autologin"); - if (!d->displayServerCmd.isEmpty()) - args << QStringLiteral("--display-server") << d->displayServerCmd; - if (d->greeter) - args << QStringLiteral("--greeter"); - if (d->singleMode) - args << QStringLiteral("--single-mode"); - if (d->identifyOnly) - args << QStringLiteral("--identify-only"); - if (d->skipAuth) - args << QStringLiteral("--skip-auth"); - d->child->start(QStringLiteral("%1/ddm-helper").arg(QStringLiteral(LIBEXEC_INSTALL_DIR)), args); - } - - void Auth::stop() { - if (d->child->state() == QProcess::NotRunning) { - return; - } - - d->child->terminate(); - - // wait for finished - // TODO: Cut off the waiting. - // The code will be executed when user trying to start sessions other - // than treeland, which will stop the currentAuth and start a new one. - // This process involves the removal of seatd client, which needs a - // small amount of time to wait. Consider to make this process under - // control. - if (!d->child->waitForFinished(500)) - d->child->kill(); - } -} - -#include "Auth.moc" diff --git a/src/auth/Auth.h b/src/auth/Auth.h deleted file mode 100644 index 79ef928..0000000 --- a/src/auth/Auth.h +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Qt Authentication library - * Copyright (C) 2013 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef DDM_AUTH_H -#define DDM_AUTH_H - -#include "AuthRequest.h" -#include "AuthPrompt.h" -#include "Session.h" - -#include -#include - -namespace DDM { - /** - * \brief - * Main class triggering the authentication and handling all communication - * - * \section description - * There are three basic kinds of authentication: - * - * * Checking only the validity of the user's secrets - The default values - * - * * Logging the user in after authenticating him - You'll have to set the - * \ref session property to do that. - * - * * Logging the user in without authenticating - You'll have to set the - * \ref session and \ref autologin properties to do that. - * - * Usage: - * - * Just construct, connect the signals (especially \ref requestChanged) - * and fire up \ref start - */ - class Auth : public QObject { - Q_OBJECT - // not setting NOTIFY for the properties - they should be set only once before calling start - Q_PROPERTY(bool autologin READ autologin WRITE setAutologin NOTIFY autologinChanged) - Q_PROPERTY(bool greeter READ isGreeter WRITE setGreeter NOTIFY greeterChanged) - Q_PROPERTY(bool verbose READ verbose WRITE setVerbose NOTIFY verboseChanged) - Q_PROPERTY(bool identifyOnly READ identifyOnly WRITE setIdentifyOnly) - Q_PROPERTY(QByteArray cookie READ cookie WRITE setCookie NOTIFY cookieChanged) - Q_PROPERTY(QString user READ user WRITE setUser NOTIFY userChanged) - Q_PROPERTY(QString session READ session WRITE setSession NOTIFY sessionChanged) - Q_PROPERTY(AuthRequest* request READ request NOTIFY requestChanged) - public: - explicit Auth(const QString &user = QString(), const QString &session = QString(), bool autologin = false, QObject *parent = 0, bool verbose = false); - explicit Auth(QObject *parent); - ~Auth(); - - enum Info { - INFO_NONE = 0, - INFO_UNKNOWN, - INFO_PASS_CHANGE_REQUIRED, - _INFO_LAST - }; - Q_ENUM(Info) - - enum Error { - ERROR_NONE = 0, - ERROR_UNKNOWN, - ERROR_AUTHENTICATION, - ERROR_INTERNAL, - _ERROR_LAST - }; - Q_ENUM(Error) - - enum HelperExitStatus { - HELPER_SUCCESS = 0, - HELPER_AUTH_ERROR, - HELPER_SESSION_ERROR, - HELPER_OTHER_ERROR, - HELPER_DISPLAYSERVER_ERROR, - HELPER_TTY_ERROR, - }; - Q_ENUM(HelperExitStatus) - - static void registerTypes(); - - bool autologin() const; - bool isGreeter() const; - bool verbose() const; - bool identifyOnly() const; - bool isSingleMode() const; - const QByteArray &cookie() const; - const QString &user() const; - const QString &session() const; - const QString &password() const; - AuthRequest *request(); - QString sessionId() const; - Session::Type sessionType() const; - QString sessionFileName() const; - int tty() const; - int xdgSessionId() const; - - void setTTY(int tty); - /** - * True if an authentication or session is in progress - */ - bool isActive() const; - - /** - * If starting a session, you will probably want to provide some basic env variables for the session. - * This only inserts the variables - if the current key already had a value, it will be overwritten. - * User-specific data such as $HOME is generated automatically. - * @param env the environment - */ - void insertEnvironment(const QProcessEnvironment &env); - - /** - * Works the same as \ref insertEnvironment but only for one key-value pair - * @param key key - * @param value value - */ - void insertEnvironment(const QString &key, const QString &value); - - /** - * Set mode to autologin. - * Ignored if session is not started - * @param on true if should autologin - */ - void setAutologin(bool on = true); - - /** - * Set mode to greeter - * This will bypass authentication checks - */ - void setGreeter(bool on = true); - - /** - * Forwards the output of the underlying authenticator to the current process - * @param on true if should forward the output - */ - void setVerbose(bool on = true); - - void setIdentifyOnly(bool on = false); - - void setSkipAuth(bool on = true); - /** - * Sets the user which will then authenticate - * @param user username - */ - void setUser(const QString &user); - - - void setPassword(const QString &password); - - /** - * Set the display server command to be started before the greeter. - * @param command Command of the display server to be started - */ - void setDisplayServerCommand(const QString &command); - - /** - * Set the session to be started after authenticating. - * @param path Path of the session executable to be started - */ - void setSession(const QString &path); - - /** - * Set the display server cookie, to be inserted into the user's $XAUTHORITY - * @param cookie cookie data - */ - void setCookie(const QByteArray &cookie); - - /** - * Set the display server single mode - * @param on true use DDE single wayland mode - */ - void setSingleMode(bool on = true); - - void setSessionId(const QString &sessionId); - - void setSessionType(const Session::Type type); - - void setSessionFileName(const QString &fileName); - - void setXdgSessionId(int xdgSessionId); - - public Q_SLOTS: - /** - * Sets up the environment and starts the authentication - */ - void start(); - - /** - * Indicates that we do not need the process anymore. - */ - void stop(); - - Q_SIGNALS: - void autologinChanged(); - void greeterChanged(); - void verboseChanged(); - void cookieChanged(); - void userChanged(); - void displayServerCommandChanged(); - void sessionChanged(); - void requestChanged(); - void singleModeChanged(); - - /** - * Emitted when authentication phase finishes - * - * @note If you want to set some environment variables for the session right before the - * session is started, connect to this signal using a blocking connection and insert anything - * you need in the slot. - * @param user username - * @param success true if succeeded - */ - void authentication(QString user, bool success, bool identifyOnly); - - /** - * Emitted when session starting phase finishes - * - * @param success true if succeeded - */ - void sessionStarted(bool success, int xdgSessionId); - - /** - * Emitted when the display server is ready. - * - * @param displayName display name - */ - void displayServerReady(const QString &displayName); - - /** - * Emitted when the helper quits, either after authentication or when the session ends. - * Or, when something goes wrong. - * - * @param success true if every underlying task went fine - */ - void finished(Auth::HelperExitStatus status); - - /** - * Emitted on error - * - * @param message message to be displayed to the user - */ - void error(QString message, Auth::Error type); - - /** - * Information from the underlying stack is to be presented to the user - * - * @param message message to be displayed to the user - */ - void info(QString message, Auth::Info type); - - private: - class Private; - class SocketServer; - friend Private; - friend SocketServer; - Private *d { nullptr }; - }; -} - -#endif // DDM_AUTH_H diff --git a/src/auth/AuthMessages.h b/src/auth/AuthMessages.h deleted file mode 100644 index 98083cb..0000000 --- a/src/auth/AuthMessages.h +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Message IDs to pass between the library and the helper - * Copyright (C) 2013 Martin Bříza - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#ifndef MESSAGES_H -#define MESSAGES_H - -#include -#include - -#include "Auth.h" - -namespace DDM { - class Prompt { - public: - Prompt() { } - Prompt(AuthPrompt::Type type, QString message, bool hidden) - : type(type), message(message), hidden(hidden) { } - Prompt(const Prompt &o) - : type(o.type), response(o.response), message(o.message), hidden(o.hidden) { } - ~Prompt() { - clear(); - } - Prompt& operator=(const Prompt &o) { - type = o.type; - response = o.response; - message = o.message; - hidden = o.hidden; - return *this; - } - bool operator==(const Prompt &o) const { - return type == o.type && response == o.response && message == o.message && hidden == o.hidden; - } - bool valid() const { - return !(type == AuthPrompt::NONE && response.isEmpty() && message.isEmpty()); - } - void clear() { - type = AuthPrompt::NONE; - // overwrite the whole thing with zeroes before clearing - memset(response.data(), 0, response.length()); - response.clear(); - message.clear(); - hidden = false; - } - - AuthPrompt::Type type { AuthPrompt::NONE }; - QByteArray response { }; - QString message { }; - bool hidden { false }; - }; - - class Request { - public: - Request() { } - Request(QList prompts) - : prompts(prompts) { } - Request(const Request &o) - : prompts(o.prompts) { } - Request& operator=(const Request &o) { - prompts = QList(o.prompts); - return *this; - } - bool operator==(const Request &o) const { - return prompts == o.prompts; - } - bool valid() const { - return !(prompts.isEmpty()); - } - void clear() { - prompts.clear(); - } - - QList prompts { }; - }; - - enum Msg { - MSG_UNKNOWN = 0, - HELLO = 1, - ERROR, - INFO, - REQUEST, - AUTHENTICATED, - SESSION_STATUS, - DISPLAY_SERVER_STARTED, - MSG_LAST, - }; - - inline QDataStream& operator<<(QDataStream &s, const Msg &m) { - s << qint32(m); - return s; - } - - inline QDataStream& operator>>(QDataStream &s, Msg &m) { - // TODO seriously? - qint32 i; - s >> i; - if (i >= MSG_LAST || i <= MSG_UNKNOWN) { - s.setStatus(QDataStream::ReadCorruptData); - return s; - } - m = Msg(i); - return s; - } - - inline QDataStream& operator<<(QDataStream &s, const Auth::Error &m) { - s << qint32(m); - return s; - } - - inline QDataStream& operator>>(QDataStream &s, Auth::Error &m) { - // TODO seriously? - qint32 i; - s >> i; - if (i >= Auth::_ERROR_LAST || i < Auth::ERROR_NONE) { - s.setStatus(QDataStream::ReadCorruptData); - return s; - } - m = Auth::Error(i); - return s; - } - - inline QDataStream& operator<<(QDataStream &s, const Auth::Info &m) { - s << qint32(m); - return s; - } - - inline QDataStream& operator>>(QDataStream &s, Auth::Info &m) { - // TODO seriously? - qint32 i; - s >> i; - if (i >= Auth::_INFO_LAST || i < Auth::INFO_NONE) { - s.setStatus(QDataStream::ReadCorruptData); - return s; - } - m = Auth::Info(i); - return s; - } - - inline QDataStream& operator<<(QDataStream &s, const QProcessEnvironment &m) { - s << m.toStringList(); - return s; - } - - inline QDataStream& operator>>(QDataStream &s, QProcessEnvironment &m) { - QStringList l; - s >> l; - for (QString s : l) { - int pos = s.indexOf(QLatin1Char('=')); - m.insert(s.left(pos), s.mid(pos + 1)); - } - return s; - } - - inline QDataStream& operator<<(QDataStream &s, const Prompt &m) { - s << qint32(m.type) << m.message << m.hidden << m.response; - return s; - } - - inline QDataStream& operator>>(QDataStream &s, Prompt &m) { - qint32 type; - QString message; - bool hidden; - QByteArray response; - s >> type >> message >> hidden >> response; - m.type = AuthPrompt::Type(type); - m.message = message; - m.hidden = hidden; - m.response = response; - return s; - } - - inline QDataStream& operator<<(QDataStream &s, const Request &m) { - qint32 length = m.prompts.length(); - s << length; - for (const Prompt &p : std::as_const(m.prompts)) { - s << p; - } - return s; - } - - inline QDataStream& operator>>(QDataStream &s, Request &m) { - QList prompts; - qint32 length; - s >> length; - for (int i = 0; i < length; i++) { - Prompt p; - s >> p; - prompts << p; - } - if (prompts.length() != length) { - s.setStatus(QDataStream::ReadCorruptData); - return s; - } - m.prompts = prompts; - return s; - } -} - -#endif // MESSAGES_H diff --git a/src/auth/AuthPrompt.cpp b/src/auth/AuthPrompt.cpp deleted file mode 100644 index 0c4d36e..0000000 --- a/src/auth/AuthPrompt.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Qt Authentication Library - * Copyright (C) 2013 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "AuthPrompt.h" -#include "Auth.h" -#include "AuthMessages.h" - -namespace DDM { - class AuthPrompt::Private : public Prompt { - public: - Private(const Prompt *p) { - // initializers are too mainstream i guess - type = p->type; - hidden = p->hidden; - message = p->message; - response = p->response; - } - }; - - AuthPrompt::AuthPrompt(const Prompt *prompt, AuthRequest *parent) - : QObject(parent) - , d(new Private(prompt)) { - } - - AuthPrompt::~AuthPrompt() { - delete d; - } - - AuthPrompt::Type AuthPrompt::type() const { - return d->type; - } - - QString AuthPrompt::message() const { - return d->message; - } - - QByteArray AuthPrompt::response() const { - return d->response; - } - - QByteArray AuthPrompt::responseFake() { - return QByteArray(); - } - - void AuthPrompt::setResponse(const QByteArray &r) { - if (r != d->response) { - d->response = r; - Q_EMIT responseChanged(); - } - } - - bool AuthPrompt::hidden() const { - return d->hidden; - } -} diff --git a/src/auth/AuthPrompt.h b/src/auth/AuthPrompt.h deleted file mode 100644 index dd3205c..0000000 --- a/src/auth/AuthPrompt.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * - * Copyright (C) 2013 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef PROMPT_H -#define PROMPT_H - -#include - -namespace DDM { - class Auth; - class AuthRequest; - class Prompt; - /** - * \brief - * One prompt input for the authentication - * - * \section description - * The main, not completely obvious rationale for this class is: - * - * \warning Don't use the \ref message property if you have your own strings for - * the \ref Type -s. PAM sends horrible horrible stuff and passwd obviously - * doesn't tell us a thing. - */ - class AuthPrompt : public QObject { - Q_OBJECT - Q_PROPERTY(Type type READ type CONSTANT) - Q_PROPERTY(QString message READ message CONSTANT) - Q_PROPERTY(bool hidden READ hidden CONSTANT) - Q_PROPERTY(QByteArray response READ responseFake WRITE setResponse NOTIFY responseChanged) - public: - virtual ~AuthPrompt(); - /** - * \note In hex not for binary operations but to leave space for adding other codes - */ - enum Type { - NONE = 0x0000, ///< No type - UNKNOWN = 0x0001, ///< Unknown type - CHANGE_CURRENT = 0x0010, ///< On changing the password: Current one - CHANGE_NEW, ///< On changing the password: The new one - CHANGE_REPEAT, ///< On changing the password: The new one, repeated - LOGIN_USER = 0x0080, ///< On logging in: The username - LOGIN_PASSWORD ///< On logging in: The password - }; - Q_ENUM(Type) - /** - * @return the type of the prompt - */ - Type type() const; - /** - * @warning the preferred way is to use \ref type - * @return message from the stack - */ - QString message() const; - /** - * @return true if user's input should not be shown in readable form - */ - bool hidden() const; - /** - * Public getter for the response data. - * The property is write-only though, so it returns garbage. - * Contained only to keep the MOC parser happy. - * @warning do not use, doesn't return valid data - * @return empty byte array - */ - QByteArray responseFake(); - /** - * Setter for the response data - * @param r data entered by the user - */ - void setResponse(const QByteArray &r); - Q_SIGNALS: - /** - * Emitted when the response was entered by the user - */ - void responseChanged(); - private: - AuthPrompt(const Prompt *prompt, AuthRequest *parent = 0); - QByteArray response() const; - friend class AuthRequest; - class Private; - Private *d { nullptr }; - }; -} - -#endif //PROMPT_H diff --git a/src/auth/AuthRequest.cpp b/src/auth/AuthRequest.cpp deleted file mode 100644 index 34ac109..0000000 --- a/src/auth/AuthRequest.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Qt Authentication Library - * Copyright (C) 2013 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "AuthRequest.h" -#include "Auth.h" -#include "AuthMessages.h" - -namespace DDM { - class AuthRequest::Private : public QObject { - Q_OBJECT - public slots: - void responseChanged(); - public: - Private(QObject *parent); - QList prompts { }; - bool finishAutomatically { false }; - bool finished { true }; - }; - - AuthRequest::Private::Private(QObject* parent) - : QObject(parent) { } - - void AuthRequest::Private::responseChanged() { - for (const AuthPrompt *qap : std::as_const(prompts)) { - if (qap->response().isEmpty()) - return; - } - if (finishAutomatically && prompts.length() > 0) - qobject_cast(parent())->done(); - } - - AuthRequest::AuthRequest(Auth *parent) - : QObject(parent) - , d(new Private(this)) { } - - void AuthRequest::setRequest(const Request *request) { - QList promptsCopy(d->prompts); - d->prompts.clear(); - if (request != nullptr) { - for (const Prompt &p : std::as_const(request->prompts)) { - AuthPrompt *qap = new AuthPrompt(&p, this); - d->prompts << qap; - if (finishAutomatically()) - connect(qap, &AuthPrompt::responseChanged, d, &AuthRequest::Private::responseChanged); - } - d->finished = false; - } - Q_EMIT promptsChanged(); - if (request == nullptr) { - qDeleteAll(promptsCopy); - } - } - - QList AuthRequest::prompts() { - return d->prompts; - } - - QQmlListProperty AuthRequest::promptsDecl() { - return QQmlListProperty(this, &d->prompts); - } - - void AuthRequest::done() { - if (!d->finished) { - d->finished = true; - Q_EMIT finished(); - } - } - - bool AuthRequest::finishAutomatically() { - return d->finishAutomatically; - } - - void AuthRequest::setFinishAutomatically(bool value) { - if (value != d->finishAutomatically) { - d->finishAutomatically = value; - Q_EMIT finishAutomaticallyChanged(); - } - } - - Request AuthRequest::request() const { - Request r; - for (const AuthPrompt *qap : std::as_const(d->prompts)) { - Prompt p; - p.hidden = qap->hidden(); - p.message = qap->message(); - p.response = qap->response(); - p.type = qap->type(); - r.prompts << p; - } - return r; - } -} - -#include "AuthRequest.moc" diff --git a/src/auth/AuthRequest.h b/src/auth/AuthRequest.h deleted file mode 100644 index bb6f6a3..0000000 --- a/src/auth/AuthRequest.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Qt Authentication library - * Copyright (C) 2013 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef REQUEST_H -#define REQUEST_H - -#include - -#include - -namespace DDM { - class Auth; - class AuthPrompt; - class Request; - /** - * \brief - * AuthRequest is the main class for tracking requests from the underlying auth stack - * - * \section description - * Typically, when logging in, you'll receive a list containing one or two fields: - * - * * First one for the username (if you didn't provide it before); - * hidden = false, type = LOGIN_USER, message = whatever the stack provides - * - * * Second one for the user's password - * hidden = true, type = LOGIN_PASSWORD, message = whatever the stack provides - * - * It's up to you to fill the \ref AuthPrompt::response property. - * When all the fields are filled to your satisfaction, just trigger the \ref done - * slot and the response will go back to the authenticator. - * - * \todo Decide if it's sane to use the info messages from PAM or to somehow parse them - * and make the password changing message into a Request::Type of some kind - */ - class AuthRequest : public QObject { - Q_OBJECT - Q_PROPERTY(QQmlListProperty prompts READ promptsDecl NOTIFY promptsChanged) - Q_PROPERTY(bool finishAutomatically READ finishAutomatically WRITE setFinishAutomatically NOTIFY finishAutomaticallyChanged) - public: - /** - * @return list of the contained prompts - */ - QList prompts(); - /** - * For QML apps - * @return list of the contained prompts - */ - QQmlListProperty promptsDecl(); - - static AuthRequest *empty(); - - bool finishAutomatically(); - void setFinishAutomatically(bool value); - public Q_SLOTS: - /** - * Call this slot when all prompts has been filled to your satisfaction - */ - void done(); - Q_SIGNALS: - /** - * Emitted when \ref done was called - */ - void finished(); - - void finishAutomaticallyChanged(); - void promptsChanged(); - private: - AuthRequest(Auth *parent); - void setRequest(const Request *request = nullptr); - Request request() const; - friend class Auth; - class Private; - Private *d { nullptr }; - }; -} - -#endif //REQUEST_H diff --git a/src/auth/CMakeLists.txt b/src/auth/CMakeLists.txt deleted file mode 100644 index a44c598..0000000 --- a/src/auth/CMakeLists.txt +++ /dev/null @@ -1,63 +0,0 @@ -set(PUBLIC_HEADERS - ${CMAKE_SOURCE_DIR}/src/auth/Auth.h - ${CMAKE_SOURCE_DIR}/src/auth/AuthMessages.h - ${CMAKE_SOURCE_DIR}/src/auth/AuthPrompt.h - ${CMAKE_SOURCE_DIR}/src/auth/AuthRequest.h -) - -set(SRCS - ${CMAKE_SOURCE_DIR}/src/auth/Auth.cpp - ${CMAKE_SOURCE_DIR}/src/auth/AuthPrompt.cpp - ${CMAKE_SOURCE_DIR}/src/auth/AuthRequest.cpp -) - -add_library(auth SHARED ${PUBLIC_HEADERS} ${SRCS}) - -set_target_properties(auth PROPERTIES - VERSION ${CMAKE_PROJECT_VERSION} - SOVERSION ${CMAKE_PROJECT_VERSION_MAJOR} - OUTPUT_NAME ddm-auth - EXPORT_NAME Auth -) - -target_link_libraries(auth - PUBLIC - common - Qt${QT_MAJOR_VERSION}::Network - Qt${QT_MAJOR_VERSION}::Qml -) - -target_include_directories(auth INTERFACE - $ - $ - $ -) -target_link_directories(auth INTERFACE - $ - $ -) - -if(PAM_FOUND) - target_link_libraries(auth PRIVATE ${PAM_LIBRARIES}) -else() - target_link_libraries(auth PRIVATE crypt) -endif() - -# 安装库和头文件 -install( - TARGETS auth - EXPORT AuthTargets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION bin - INCLUDES DESTINATION include/ddm/auth -) - -install(FILES ${PUBLIC_HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ddm/auth") - -# 导出目标文件 -install(EXPORT AuthTargets - FILE AuthTargets.cmake - NAMESPACE DDM:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/DDM -) diff --git a/src/daemon/Auth.cpp b/src/daemon/Auth.cpp new file mode 100644 index 0000000..002211b --- /dev/null +++ b/src/daemon/Auth.cpp @@ -0,0 +1,202 @@ +/* + * Qt Authentication Library + * Copyright (C) 2013 Martin Bříza + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "Auth.h" + +#include "Pam.h" +#include "UserSession.h" + +#include +#include +#include +#include + +namespace DDM { + int Auth::lastId = 0; + + Auth::Auth(QObject *parent) + : QObject(parent) + , id(++lastId) + , m_pam(new Pam(this)) + , m_session(new UserSession(this)) { + connect(this, &Auth::finished, this, [this] { + active = false; + }); + } + + Auth::~Auth() { + stop(); + } + +#define LOGIN_FAILED \ + const QString displayId = environment.value(QStringLiteral("DISPLAY")); \ + const QString vt = environment.value(QStringLiteral("XDG_VTNR")); \ + utmpLogin(vt, displayId, user, 0, false); \ + Q_EMIT finished(AUTH_ERROR); \ + return; + + void Auth::start(const QByteArray &secret) { + Q_ASSERT(getuid() == 0); + + active = true; + m_pam->user = user; + if (!m_pam->start()) { + Q_EMIT authentication(user, false, identifyOnly); + LOGIN_FAILED + } + + if (!skipAuth && !m_pam->authenticate(secret)) { + Q_EMIT authentication(user, false, identifyOnly); + LOGIN_FAILED + } + + Q_EMIT authentication(user, true, identifyOnly); + + if (!sessionPath.isEmpty()) { + auto sessionEnv = m_pam->openSession(environment); + if (!sessionEnv.has_value()) { + Q_EMIT sessionStarted(false, 0); + Q_EMIT finished(SESSION_ERROR); + return; + } + + QProcessEnvironment env = *sessionEnv; + xdgSessionId = env.value(QStringLiteral("XDG_SESSION_ID")).toInt(); + Q_EMIT sessionStarted(true, xdgSessionId); + + struct passwd *pw; + pw = getpwnam(qPrintable(user)); + if (pw) { + env.insert(QStringLiteral("HOME"), QString::fromLocal8Bit(pw->pw_dir)); + env.insert(QStringLiteral("PWD"), QString::fromLocal8Bit(pw->pw_dir)); + env.insert(QStringLiteral("SHELL"), QString::fromLocal8Bit(pw->pw_shell)); + env.insert(QStringLiteral("USER"), QString::fromLocal8Bit(pw->pw_name)); + env.insert(QStringLiteral("LOGNAME"), QString::fromLocal8Bit(pw->pw_name)); + } + m_session->setProcessEnvironment(env); + m_session->start(); + + // write successful login to utmp/wtmp + const QString displayId = env.value(QStringLiteral("DISPLAY")); + const QString vt = env.value(QStringLiteral("XDG_VTNR")); + // cache pid for session end + utmpLogin(vt, displayId, user, m_session->processId(), true); + } + return; + } + + void Auth::stop() { + Q_ASSERT(getuid() == 0); + + qint64 pid = m_session->processId(); + m_session->stop(); + if (!identifyOnly && m_pam->sessionOpened) { + m_pam->closeSession(); + } + + // write logout to utmp/wtmp + if (pid <= 0) + return; + QProcessEnvironment env = m_session->processEnvironment(); + QString vt = env.value(QStringLiteral("XDG_VTNR")); + QString displayId = env.value(QStringLiteral("DISPLAY")); + utmpLogout(vt, displayId, pid); + } + + void Auth::utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful) { + struct utmpx entry { }; + struct timeval tv; + + entry.ut_type = USER_PROCESS; + entry.ut_pid = pid; + + // ut_line: vt + if (!vt.isEmpty()) { + QString tty = QStringLiteral("tty"); + tty.append(vt); + QByteArray ttyBa = tty.toLocal8Bit(); + const char* ttyChar = ttyBa.constData(); + strncpy(entry.ut_line, ttyChar, sizeof(entry.ut_line) - 1); + } + + // ut_host: displayName + QByteArray displayBa = displayName.toLocal8Bit(); + const char* displayChar = displayBa.constData(); + strncpy(entry.ut_host, displayChar, sizeof(entry.ut_host) - 1); + + // ut_user: user + QByteArray userBa = user.toLocal8Bit(); + const char* userChar = userBa.constData(); + strncpy(entry.ut_user, userChar, sizeof(entry.ut_user) -1); + + gettimeofday(&tv, NULL); + entry.ut_tv.tv_sec = tv.tv_sec; + entry.ut_tv.tv_usec = tv.tv_usec; + + // write to utmp + setutxent(); + if (!pututxline (&entry)) + qWarning() << "Failed to write utmpx: " << strerror(errno); + endutxent(); + + // append to failed login database btmp + if (!authSuccessful) { + updwtmpx("/var/log/btmp", &entry); + } else { + // append to wtmp + updwtmpx("/var/log/wtmp", &entry); + } + } + + void Auth::utmpLogout(const QString &vt, const QString &displayName, qint64 pid) { + struct utmpx entry { }; + struct timeval tv; + + entry.ut_type = DEAD_PROCESS; + entry.ut_pid = pid; + + // ut_line: vt + if (!vt.isEmpty()) { + QString tty = QStringLiteral("tty"); + tty.append(vt); + QByteArray ttyBa = tty.toLocal8Bit(); + const char* ttyChar = ttyBa.constData(); + strncpy(entry.ut_line, ttyChar, sizeof(entry.ut_line) - 1); + } + + // ut_host: displayName + QByteArray displayBa = displayName.toLocal8Bit(); + const char* displayChar = displayBa.constData(); + strncpy(entry.ut_host, displayChar, sizeof(entry.ut_host) - 1); + + gettimeofday(&tv, NULL); + entry.ut_tv.tv_sec = tv.tv_sec; + entry.ut_tv.tv_usec = tv.tv_usec; + + // write to utmp + setutxent(); + if (!pututxline (&entry)) + qWarning() << "Failed to write utmpx: " << strerror(errno); + endutxent(); + + // append to wtmp + updwtmpx("/var/log/wtmp", &entry); + } +} // namespace DDM diff --git a/src/daemon/Auth.h b/src/daemon/Auth.h new file mode 100644 index 0000000..39a942f --- /dev/null +++ b/src/daemon/Auth.h @@ -0,0 +1,128 @@ +/* + * Qt Authentication library + * Copyright (C) 2013 Martin Bříza + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef DDM_AUTH_H +#define DDM_AUTH_H + +#include "Session.h" + +#include +#include + +namespace DDM { + class Pam; + class UserSession; + + class Auth : public QObject { + Q_OBJECT + public: + Auth(QObject *parent); + ~Auth(); + + enum ExitStatus { + SUCCESS = 0, + AUTH_ERROR, + SESSION_ERROR, + OTHER_ERROR, + DISPLAYSERVER_ERROR, + TTY_ERROR, + }; + Q_ENUM(ExitStatus) + + bool active{ false }; + QString displayServerCmd{}; + QString sessionPath{}; + Session::Type sessionType{ Session::UnknownSession }; + QString sessionFileName{}; + QString user{}; + QByteArray cookie{}; + bool autologin{ false }; + bool greeter{ false }; + bool singleMode{ false }; + bool identifyOnly{ false }; + bool skipAuth{ false }; + QProcessEnvironment environment{ }; + int id{ 0 }; + static int lastId; + QString sessionId{}; + int tty{ 0 }; + int xdgSessionId{ 0 }; + public Q_SLOTS: + /** + * Sets up the environment and starts the authentication + */ + void start(const QByteArray &secret); + + /** + * Indicates that we do not need the process anymore. + */ + void stop(); + + Q_SIGNALS: + /** + * Emitted when authentication phase finishes + * + * @note If you want to set some environment variables for the session right before the + * session is started, connect to this signal using a blocking connection and insert anything + * you need in the slot. + * @param user username + * @param success true if succeeded + */ + void authentication(QString user, bool success, bool identifyOnly); + + /** + * Emitted when session starting phase finishes + * + * @param success true if succeeded + */ + void sessionStarted(bool success, int xdgSessionId); + + /** + * Emitted when the session ends. + * + * @param success true if every underlying task went fine + */ + void finished(Auth::ExitStatus status); + + private: + Pam *m_pam { nullptr }; + UserSession *m_session{ nullptr }; + + /** + * Write utmp/wtmp/btmp records when a user logs in + * @param vt Virtual terminal (tty7, tty8,...) + * @param displayName Display (:0, :1,...) + * @param user User logging in + * @param pid User process ID (e.g. PID of startkde) + * @param authSuccessful Was authentication successful + */ + void utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful); + + /** + * Write utmp/wtmp records when a user logs out + * @param vt Virtual terminal (tty7, tty8,...) + * @param displayName Display (:0, :1,...) + * @param pid User process ID (e.g. PID of startkde) + */ + void utmpLogout(const QString &vt, const QString &displayName, qint64 pid); + }; +} + +#endif // DDM_AUTH_H diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index b5721b5..013f325 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -25,16 +25,19 @@ add_custom_command( configure_file(config.h.in config.h IMMEDIATE @ONLY) set(DAEMON_SOURCES + Auth.cpp DaemonApp.cpp Display.cpp DisplayManager.cpp DisplayServer.cpp Greeter.cpp + Pam.cpp PowerManager.cpp Seat.cpp SeatManager.cpp SocketServer.cpp TreelandConnector.cpp + UserSession.cpp XorgDisplayServer.cpp XorgUserDisplayServer.cpp XorgUserDisplayServer.h @@ -73,12 +76,13 @@ qt_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.fr add_executable(ddm ${DAEMON_SOURCES}) target_link_libraries(ddm PRIVATE - auth + common PUBLIC Qt${QT_MAJOR_VERSION}::DBus Qt${QT_MAJOR_VERSION}::Network Qt${QT_MAJOR_VERSION}::Qml ${LIBXCB_LIBRARIES} + ${PAM_LIBRARIES} PkgConfig::WAYLAND ) diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp index e3101ef..e6c2ff7 100644 --- a/src/daemon/Display.cpp +++ b/src/daemon/Display.cpp @@ -181,7 +181,7 @@ namespace DDM { connect(m_greeter, &Greeter::displayServerFailed, this, &Display::displayServerFailed); connect(m_greeter, &Greeter::greeterStarted, this, [this] { if (m_currentAuth) { - switchToUser(m_currentAuth->user(), m_currentAuth->xdgSessionId()); + switchToUser(m_currentAuth->user, m_currentAuth->xdgSessionId); } }); } @@ -205,7 +205,7 @@ namespace DDM { int Display::terminalId() const { // TODO: if not use single wayland mode, m_auths only have one element. - return !m_auths.isEmpty() && m_auths.first()->isActive() ? m_sessionTerminalId : m_terminalId; + return !m_auths.isEmpty() && m_auths.first()->active ? m_sessionTerminalId : m_terminalId; } const QString &Display::name() const { @@ -358,8 +358,8 @@ namespace DDM { // send logined user (for possible crash recovery) SocketWriter writer(socket); for (Auth *auth : loginedSession()) { - if (auth->isActive()) - writer << quint32(DaemonMessages::UserLoggedIn) << auth->user() << auth->xdgSessionId(); + if (auth->active) + writer << quint32(DaemonMessages::UserLoggedIn) << auth->user << auth->xdgSessionId; } } @@ -446,18 +446,12 @@ namespace DDM { auth->setObjectName("userIdentify"); m_auths.push_back(auth); - auth->setVerbose(true); - connect(auth, &Auth::requestChanged, this, &Display::slotRequestChanged); connect(auth, &Auth::authentication, this, &Display::slotAuthenticationFinished); connect(auth, &Auth::finished, this, &Display::slotHelperFinished); - connect(auth, &Auth::info, this, &Display::slotAuthInfo); - connect(auth, &Auth::error, this, &Display::slotAuthError); - - auth->setVerbose(true); - auth->setPassword(password); - auth->setUser(user); - auth->setIdentifyOnly(true); - auth->start(); + + auth->user = user; + auth->identifyOnly = true; + auth->start(password.toLocal8Bit()); } void Display::startAuth(const QString &user, const QString &password, const Session &session) { @@ -465,7 +459,7 @@ namespace DDM { // respond to authentication requests Auth *auth = nullptr; for (auto *item : m_auths) { - if (item->user() == user) { + if (item->user == user) { auth = item; break; } @@ -476,22 +470,15 @@ namespace DDM { m_auths << auth; } - auth->setVerbose(true); - auth->setAutologin(mainConfig.Autologin.User.get() == user); - connect(auth, &Auth::requestChanged, this, &Display::slotRequestChanged); connect(auth, &Auth::authentication, this, &Display::slotAuthenticationFinished); connect(auth, &Auth::sessionStarted, this, &Display::slotSessionStarted); connect(auth, &Auth::finished, this, &Display::slotHelperFinished); - connect(auth, &Auth::info, this, &Display::slotAuthInfo); - connect(auth, &Auth::error, this, &Display::slotAuthError); - if (auth->isActive()) { + if (auth->active) { qWarning() << "Existing authentication ongoing, aborting"; return; } - auth->setPassword(password); - // sanity check if (!session.isValid()) { qCritical() << "Invalid session" << session.fileName(); @@ -525,14 +512,14 @@ namespace DDM { } } - auth->setTTY(m_terminalId); + auth->tty = m_terminalId; if ((session.type() == Session::WaylandSession && m_displayServerType == X11DisplayServerType) || (m_greeter->isRunning() && m_displayServerType != X11DisplayServerType) || (m_displayServerType == SingleCompositerServerType)) { // Create a new VT when we need to have another compositor running - auth->setTTY(VirtualTerminal::setUpNewVt()); + auth->tty = VirtualTerminal::setUpNewVt(); } // some information - qDebug() << "Session" << session.fileName() << "selected, command:" << session.exec() << "for VT" << auth->tty(); + qDebug() << "Session" << session.fileName() << "selected, command:" << session.exec() << "for VT" << auth->tty; QProcessEnvironment env; env.insert(session.additionalEnv()); @@ -542,9 +529,9 @@ namespace DDM { // session id { const QString sessionId = QStringLiteral("Session%1").arg(daemonApp->newSessionId()); - daemonApp->displayManager()->AddSession(sessionId, seat()->name(), user, auth->tty()); + daemonApp->displayManager()->AddSession(sessionId, seat()->name(), user, auth->tty); env.insert(QStringLiteral("XDG_SESSION_PATH"), daemonApp->displayManager()->sessionPath(sessionId)); - auth->setSessionId(sessionId); + auth->sessionId = sessionId; } env.insert(QStringLiteral("DESKTOP_SESSION"), session.desktopSession()); @@ -552,7 +539,7 @@ namespace DDM { env.insert(QStringLiteral("XDG_CURRENT_DESKTOP"), session.desktopNames()); env.insert(QStringLiteral("XDG_SESSION_CLASS"), QStringLiteral("user")); env.insert(QStringLiteral("XDG_SESSION_TYPE"), session.xdgSessionType()); - env.insert(QStringLiteral("XDG_VTNR"), QString::number(auth->tty())); + env.insert(QStringLiteral("XDG_VTNR"), QString::number(auth->tty)); env.insert(QStringLiteral("XDG_SEAT"), seat()->name()); env.insert(QStringLiteral("XDG_SEAT_PATH"), daemonApp->displayManager()->seatPath(seat()->name())); @@ -564,40 +551,40 @@ namespace DDM { if (m_displayServerType == X11DisplayServerType) env.insert(QStringLiteral("DISPLAY"), name()); else - auth->setDisplayServerCommand(XorgUserDisplayServer::command(this)); + auth->displayServerCmd = XorgUserDisplayServer::command(this); } else { if (m_displayServerType == DisplayServerType::SingleCompositerServerType) { env.insert("DDE_CURRENT_COMPOSITOR", "TreeLand"); } - auth->setDisplayServerCommand(QStringLiteral()); + auth->displayServerCmd = QStringLiteral(); } - auth->setUser(user); - auth->setSessionType(session.type()); - auth->setSessionFileName(session.fileName()); + auth->user = user; + auth->sessionType = session.type(); + auth->sessionFileName = session.fileName(); if (m_reuseSessionId.isNull()) { - auth->setSession(session.exec()); + auth->sessionPath = session.exec(); } - auth->insertEnvironment(env); - auth->setSingleMode(session.isSingleMode()); - auth->start(); + auth->environment.insert(env); + auth->singleMode = session.isSingleMode(); + auth->start(password.toLocal8Bit()); } void Display::slotAuthenticationFinished(const QString &user, bool success, bool identifyOnly) { Auth* auth = nullptr; if(identifyOnly){ - auto ret = std::find_if(m_auths.crbegin(),m_auths.crend(),[&user](Auth *ptr) { qDebug() << ptr->user(); return ptr->user() == user;}); + auto ret = std::find_if(m_auths.crbegin(),m_auths.crend(),[&user](Auth *ptr) { qDebug() << ptr->user; return ptr->user == user;}); Q_ASSERT(ret != m_auths.crend()); auth = *ret; } else { - auto ret = std::find_if(m_auths.cbegin(),m_auths.cend(),[&user](Auth *ptr) { return ptr->user() == user;}); + auto ret = std::find_if(m_auths.cbegin(),m_auths.cend(),[&user](Auth *ptr) { return ptr->user == user;}); Q_ASSERT(ret != m_auths.cend()); auth = *ret; } - Q_ASSERT(auth && auth->identifyOnly() == identifyOnly); + Q_ASSERT(auth && auth->identifyOnly == identifyOnly); m_currentAuth = auth; m_greeter->setUserActivated(success); @@ -611,38 +598,38 @@ namespace DDM { m_started = true; } else { if (qobject_cast(m_displayServer)) - auth->setCookie(qobject_cast(m_displayServer)->cookie()); + auth->cookie = qobject_cast(m_displayServer)->cookie(); } // save last user and last session if (mainConfig.Users.RememberLastUser.get()) - stateConfig.Last.User.set(auth->user()); + stateConfig.Last.User.set(auth->user); else stateConfig.Last.User.setDefault(); if (mainConfig.Users.RememberLastSession.get()) - stateConfig.Last.Session.set(auth->sessionFileName()); + stateConfig.Last.Session.set(auth->sessionFileName); else stateConfig.Last.Session.setDefault(); stateConfig.save(); - if (auth->identifyOnly()) { + if (auth->identifyOnly) { auto* server = reinterpret_cast(m_displayServer); server->onLoginSucceeded(user); // TODO: Use exact ID when there're multiple sessions for a user int xdgSessionId = 0; for (auto *auth : m_auths) - if (auth->user() == user && auth->xdgSessionId() > 0) { - xdgSessionId = auth->xdgSessionId(); + if (auth->user == user && auth->xdgSessionId > 0) { + xdgSessionId = auth->xdgSessionId; break; } switchToUser(user, xdgSessionId); - } else if (auth->isSingleMode()) { + } else if (auth->singleMode) { auto* server = reinterpret_cast(m_displayServer); server->onLoginSucceeded(user); } else { if (m_socket) { emit loginSucceeded(m_socket, user); - daemonApp->displayManager()->setLastSession(auth->sessionId()); + daemonApp->displayManager()->setLastSession(auth->sessionId); } // Stop the original suit of displayServer since it has finished its job. @@ -659,7 +646,7 @@ namespace DDM { m_terminalId = m_sessionTerminalId = fetchAvailableVt(); m_started = false; - if (auth->sessionType() == Session::X11Session) { + if (auth->sessionType == Session::X11Session) { m_displayServer = new XorgDisplayServer(this); m_displayServerType = X11DisplayServerType; } else { @@ -673,7 +660,7 @@ namespace DDM { // The greeter here is used to start user session. m_greeter = new Greeter(this); - m_greeter->setDisplayServerCommand(auth->session()); + m_greeter->setDisplayServerCommand(auth->sessionPath); m_greeter->setUser(user); m_greeter->setSkipAuth(); @@ -687,35 +674,7 @@ namespace DDM { m_socket = nullptr; } - void Display::slotAuthInfo(const QString &message, Auth::Info info) { - qWarning() << "Authentication information:" << info << message; - - if (!m_socket) - return; - - m_socketServer->informationMessage(m_socket, message); - } - - void Display::slotAuthError(const QString &message, Auth::Error error) { - Auth* auth = qobject_cast(sender()); - - qWarning() << "Authentication error:" << error << message; - - if (!m_socket) - return; - - m_socketServer->informationMessage(m_socket, message); - if (error == Auth::ERROR_AUTHENTICATION) { - if (m_displayServerType == DisplayServerType::SingleCompositerServerType) { - auto* server = reinterpret_cast(m_displayServer); - server->onLoginFailed(auth->user()); - } else { - emit loginFailed(m_socket, auth->user()); - } - } - } - - void Display::slotHelperFinished(Auth::HelperExitStatus status) { + void Display::slotHelperFinished(Auth::ExitStatus status) { Auth* auth = qobject_cast(sender()); // Don't restart greeter and display server unless ddm-helper exited // with an internal error or the user session finished successfully, @@ -731,8 +690,8 @@ namespace DDM { m_auths.removeOne(auth); auth->deleteLater(); - if(!auth->identifyOnly()) { - daemonApp->displayManager()->RemoveSession(auth->sessionId()); + if(!auth->identifyOnly) { + daemonApp->displayManager()->RemoveSession(auth->sessionId); if (m_displayServerType == DisplayServerType::SingleCompositerServerType) { auto* server = reinterpret_cast(m_displayServer); // TODO: switch to greeter @@ -745,28 +704,15 @@ namespace DDM { } // Don't restart display when ddm-helper crashed (exit code 9) - if (status != Auth::HELPER_AUTH_ERROR && status != Auth::HelperExitStatus(9) && m_displayServerType != DisplayServerType::SingleCompositerServerType) + if (status != Auth::AUTH_ERROR && status != Auth::ExitStatus(9) && m_displayServerType != DisplayServerType::SingleCompositerServerType) stop(); } - void Display::slotRequestChanged() { - Auth* auth = qobject_cast(sender()); - if (auth->request()->prompts().length() == 1) { - auth->request()->prompts()[0]->setResponse(qPrintable(auth->password())); - auth->request()->done(); - } else if (auth->request()->prompts().length() == 2) { - auth->request()->prompts()[0]->setResponse(qPrintable(auth->user())); - auth->request()->prompts()[1]->setResponse(qPrintable(auth->password())); - auth->request()->done(); - } - } - void Display::slotSessionStarted(bool success, int xdgSessionId) { qDebug() << "Session started" << success; Auth* auth = qobject_cast(sender()); - auth->setXdgSessionId(xdgSessionId); - if (auth->isSingleMode()) { - switchToUser(auth->user(), xdgSessionId); + if (auth->singleMode) { + switchToUser(auth->user, xdgSessionId); } if (success && m_displayServerType != SingleCompositerServerType) { diff --git a/src/daemon/Display.h b/src/daemon/Display.h index b0f4939..593dbca 100644 --- a/src/daemon/Display.h +++ b/src/daemon/Display.h @@ -120,12 +120,9 @@ namespace DDM { Greeter *m_greeter { nullptr }; private slots: - void slotRequestChanged(); void slotAuthenticationFinished(const QString &user, bool success, bool identifyOnly); void slotSessionStarted(bool success, int xdgSessionId); - void slotHelperFinished(Auth::HelperExitStatus status); - void slotAuthInfo(const QString &message, Auth::Info info); - void slotAuthError(const QString &message, Auth::Error error); + void slotHelperFinished(Auth::ExitStatus status); }; } diff --git a/src/daemon/Greeter.cpp b/src/daemon/Greeter.cpp index 28797c8..3d948e5 100644 --- a/src/daemon/Greeter.cpp +++ b/src/daemon/Greeter.cpp @@ -190,13 +190,8 @@ namespace DDM { } else { // authentication m_auth = new Auth(this); - m_auth->setVerbose(true); - connect(m_auth, &Auth::requestChanged, this, &Greeter::onRequestChanged); connect(m_auth, &Auth::sessionStarted, this, &Greeter::onSessionStarted); - connect(m_auth, &Auth::displayServerReady, this, &Greeter::onDisplayServerReady); connect(m_auth, &Auth::finished, this, &Greeter::onHelperFinished); - connect(m_auth, &Auth::info, this, &Greeter::authInfo); - connect(m_auth, &Auth::error, this, &Greeter::authError); // command QStringList cmd; @@ -235,7 +230,7 @@ namespace DDM { env.insert(QStringLiteral("DISPLAY"), m_display->name()); env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb")); if (m_display->sessionType() == "x11") - m_auth->setCookie(qobject_cast(displayServer)->cookie()); + m_auth->cookie = qobject_cast(displayServer)->cookie(); } else if (m_display->displayServerType() == Display::WaylandDisplayServerType) { env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland")); env.insert(QStringLiteral("QT_WAYLAND_SHELL_INTEGRATION"), QStringLiteral("fullscreen-shell-v1")); @@ -243,21 +238,21 @@ namespace DDM { env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland")); env.insert(QStringLiteral("QT_WAYLAND_SHELL_INTEGRATION"), QStringLiteral("fullscreen-shell-v1")); } - m_auth->insertEnvironment(env); + m_auth->environment.insert(env); // log message qDebug() << "Greeter starting..."; // start greeter - m_auth->setUser(m_user); + m_auth->user = m_user; QString displayServerCmd = m_displayServerCmd; if (m_singleMode) { displayServerCmd += " --lockscreen"; } - m_auth->setDisplayServerCommand(displayServerCmd); - m_auth->setGreeter(true); - m_auth->setSession(cmd.join(QLatin1Char(' '))); - m_auth->setSingleMode(m_singleMode); + m_auth->displayServerCmd = displayServerCmd; + m_auth->greeter = true; + m_auth->sessionPath = cmd.join(QLatin1Char(' ')); + m_auth->singleMode = m_singleMode; // TODO: single compositer mode not need greeter if (m_singleMode) { @@ -265,10 +260,10 @@ namespace DDM { } if (m_skipAuth) { - m_auth->setSkipAuth(); + m_auth->skipAuth = true; } - m_auth->start(); + m_auth->start(""); m_tryTimer->start(); } @@ -321,10 +316,6 @@ namespace DDM { } } - void Greeter::onRequestChanged() { - m_auth->request()->setFinishAutomatically(true); - } - void Greeter::onSessionStarted(bool success,[[maybe_unused]] int xdgSessionId) { // set flag m_started = success; @@ -336,20 +327,7 @@ namespace DDM { qDebug() << "Greeter session failed to start"; } - void Greeter::onDisplayServerReady(const QString &displayName) - { - auto *displayServer = m_display->displayServer(); - - auto *xorgUser = qobject_cast(displayServer); - if (xorgUser) - xorgUser->setDisplayName(displayName); - - auto *wayland = qobject_cast(displayServer); - if (wayland) - wayland->setDisplayName(displayName); - } - - void Greeter::onHelperFinished(Auth::HelperExitStatus status) { + void Greeter::onHelperFinished(Auth::ExitStatus status) { if (m_singleMode && m_currentRetry <= m_maxRetry) { m_currentRetry += 1; qDebug() << "Restart treeland"; @@ -357,8 +335,8 @@ namespace DDM { if (!m_userActivated) { displayServerCmd += " --lockscreen"; } - m_auth->setDisplayServerCommand(displayServerCmd); - m_auth->start(); + m_auth->displayServerCmd = displayServerCmd; + m_auth->start(""); if (m_tryTimer->isActive()) { m_tryTimer->stop(); @@ -378,20 +356,20 @@ namespace DDM { m_auth->deleteLater(); m_auth = nullptr; - if (status == Auth::HELPER_DISPLAYSERVER_ERROR) { + if (status == Auth::DISPLAYSERVER_ERROR) { Q_EMIT displayServerFailed(); - } else if (status == Auth::HELPER_TTY_ERROR) { + } else if (status == Auth::TTY_ERROR) { Q_EMIT ttyFailed(); - } else if (status == Auth::HELPER_SESSION_ERROR) { + } else if (status == Auth::SESSION_ERROR) { Q_EMIT failed(); - } else if (status == Auth::HELPER_SUCCESS) { + } else if (status == Auth::SUCCESS) { Q_EMIT succeed(); } } bool Greeter::isRunning() const { return (m_process && m_process->state() == QProcess::Running) - || (m_auth && m_auth->isActive()); + || (m_auth && m_auth->active); } void Greeter::onReadyReadStandardError() @@ -407,14 +385,4 @@ namespace DDM { qDebug() << "Greeter output:" << m_process->readAllStandardOutput().constData(); } } - - void Greeter::authInfo(const QString &message, Auth::Info info) { - Q_UNUSED(info); - qDebug() << "Information from greeter session:" << message; - } - - void Greeter::authError(const QString &message, Auth::Error error) { - Q_UNUSED(error); - qWarning() << "Error from greeter session:" << message; - } } diff --git a/src/daemon/Greeter.h b/src/daemon/Greeter.h index 8f08b3e..c3ee9f6 100644 --- a/src/daemon/Greeter.h +++ b/src/daemon/Greeter.h @@ -56,14 +56,10 @@ namespace DDM { void finished(); private slots: - void onRequestChanged(); void onSessionStarted(bool success, int xdgSessionId); - void onDisplayServerReady(const QString &displayName); - void onHelperFinished(Auth::HelperExitStatus status); + void onHelperFinished(Auth::ExitStatus status); void onReadyReadStandardOutput(); void onReadyReadStandardError(); - void authInfo(const QString &message, Auth::Info info); - void authError(const QString &message, Auth::Error error); signals: void ttyFailed(); diff --git a/src/helper/Pam.cpp b/src/daemon/Pam.cpp similarity index 100% rename from src/helper/Pam.cpp rename to src/daemon/Pam.cpp diff --git a/src/helper/Pam.h b/src/daemon/Pam.h similarity index 100% rename from src/helper/Pam.h rename to src/daemon/Pam.h diff --git a/src/helper/UserSession.cpp b/src/daemon/UserSession.cpp similarity index 71% rename from src/helper/UserSession.cpp rename to src/daemon/UserSession.cpp index 757d8ab..018150e 100644 --- a/src/helper/UserSession.cpp +++ b/src/daemon/UserSession.cpp @@ -19,18 +19,18 @@ * */ +#include #include #include "Configuration.h" #include "Constants.h" #include "UserSession.h" -#include "HelperApp.h" +#include "Auth.h" #include "VirtualTerminal.h" #include "XAuth.h" #include #include -#include #include #include #include @@ -42,54 +42,43 @@ #include #include #include -#ifdef Q_OS_FREEBSD -#include -#endif namespace DDM { - UserSession::UserSession(HelperApp *parent) + UserSession::UserSession(Auth *parent) : QProcess(parent) - , m_helperApp(parent) + , m_auth(parent) { - connect(this, QOverload::of(&QProcess::finished), this, &UserSession::finished); + connect(this, + QOverload::of(&QProcess::finished), + this, + [this](int code) { + Q_EMIT m_auth->finished(static_cast(code)); + }); setChildProcessModifier(std::bind(&UserSession::childModifier, this)); } - bool UserSession::start() { - auto helper = qobject_cast(parent()); + void UserSession::start() { QProcessEnvironment env = processEnvironment(); - bool isWaylandGreeter = false; - - if (m_helperApp->isSingleMode()) { - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { - setProgram(QStringLiteral(LIBEXEC_INSTALL_DIR "/ddm-helper-start-single-wayland")); - // TODO: should we use 'treeland' command? - setArguments({m_displayServerCmd, m_path}); - QProcess::start(); - isWaylandGreeter = true; - } - else { - setProgram(mainConfig.Single.SessionCommand.get()); - setArguments(QStringList{m_path}); - qInfo() << "Starting Wayland user session:" << program() << m_path; - QProcess::start(); - closeWriteChannel(); - closeReadChannel(QProcess::StandardOutput); - } + if (m_auth->singleMode) { + setProgram(mainConfig.Single.SessionCommand.get()); + setArguments(QStringList{ m_auth->sessionPath }); + qInfo() << "Starting Wayland user session:" << program() << m_auth->sessionPath; + QProcess::start(); + closeWriteChannel(); + closeReadChannel(QProcess::StandardOutput); } else { // If the Xorg display server was already started, write the passed // auth cookie to /tmp/xauth_XXXXXX. This is done in the parent process // so that it can clean up the file on session end. if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) { - // Create the Xauthority file - QByteArray cookie = helper->cookie(); + QByteArray cookie = m_auth->cookie; if (cookie.isEmpty()) { qCritical() << "Can't start X11 session with empty auth cookie"; - return false; + return; } - + // Create the Xauthority file // Place it into /tmp, which is guaranteed to be read/writeable by // everyone while having the sticky bit set to avoid messing with // other's files. @@ -97,7 +86,7 @@ namespace DDM { if (!m_xauthFile.open()) { qCritical() << "Could not create the Xauthority file"; - return false; + return; } QString display = processEnvironment().value(QStringLiteral("DISPLAY")); @@ -105,55 +94,29 @@ namespace DDM { if (!XAuth::writeCookieToFile(display, m_xauthFile.fileName(), cookie)) { qCritical() << "Failed to write the Xauthority file"; m_xauthFile.close(); - return false; + return; } env.insert(QStringLiteral("XAUTHORITY"), m_xauthFile.fileName()); setProcessEnvironment(env); - QString command; - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { - command = m_path; - } else { - command = QStringLiteral("%1 \"%2\"").arg(mainConfig.X11.SessionCommand.get()).arg(m_path); - } - - qInfo() << "Starting X11 session:" << m_displayServerCmd << command; - auto args = QProcess::splitCommand(m_displayServerCmd); + QString command = QStringLiteral("%1 \"%2\"").arg(mainConfig.X11.SessionCommand.get()).arg(m_auth->sessionPath); + qInfo() << "Starting X11 session:" << m_auth->displayServerCmd << command; + auto args = QProcess::splitCommand(m_auth->displayServerCmd); setProgram(args.takeFirst()); setArguments(args); QProcess::start(); - } else if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) { - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { - Q_ASSERT(!m_displayServerCmd.isEmpty()); - setProgram(QStringLiteral(LIBEXEC_INSTALL_DIR "/ddm-helper-start-wayland")); - setArguments({m_displayServerCmd, m_path}); - QProcess::start(); - isWaylandGreeter = true; - } else { - setProgram(mainConfig.Wayland.SessionCommand.get()); - setArguments(QStringList{m_path}); - qInfo() << "Starting Wayland user session:" << program() << m_path; - QProcess::start(); - closeWriteChannel(); - closeReadChannel(QProcess::StandardOutput); - } + setProgram(mainConfig.Wayland.SessionCommand.get()); + setArguments(QStringList{ m_auth->sessionPath }); + qInfo() << "Starting Wayland user session:" << program() << m_auth->sessionPath; + QProcess::start(); + closeWriteChannel(); + closeReadChannel(QProcess::StandardOutput); } else { qCritical() << "Unable to run user session: unknown session type"; } } - - const bool started = waitForStarted(); - m_cachedProcessId = processId(); - if (started) { - return true; - } else if (isWaylandGreeter) { - // This is probably fine, we need the compositor to start first - return true; - } - - return false; } void UserSession::stop() @@ -170,33 +133,15 @@ namespace DDM { } } } else { - Q_EMIT finished(Auth::HELPER_OTHER_ERROR); + Q_EMIT finished(Auth::OTHER_ERROR); } } - QString UserSession::displayServerCommand() const - { - return m_displayServerCmd; - } - - void UserSession::setDisplayServerCommand(const QString &command) - { - m_displayServerCmd = command; - } - - void UserSession::setPath(const QString& path) { - m_path = path; - } - - QString UserSession::path() const { - return m_path; - } - void UserSession::childModifier() { // Session type QString sessionType = processEnvironment().value(QStringLiteral("XDG_SESSION_TYPE")); QString sessionClass = processEnvironment().value(QStringLiteral("XDG_SESSION_CLASS")); - const bool hasDisplayServer = !m_displayServerCmd.isEmpty(); + const bool hasDisplayServer = !m_auth->displayServerCmd.isEmpty(); const bool waylandUserSession = sessionType == QLatin1String("wayland") && sessionClass == QLatin1String("user"); // When the display server is part of the session, we leak the VT into @@ -224,7 +169,7 @@ namespace DDM { if (setsid() < 0) { qCritical("Failed to set pid %lld as leader of the new session and process group: %s", QCoreApplication::applicationPid(), strerror(errno)); - _exit(Auth::HELPER_OTHER_ERROR); + _exit(Auth::OTHER_ERROR); } @@ -233,36 +178,34 @@ namespace DDM { if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0) { const auto error = strerror(errno); qCritical().nospace() << "Failed to take control of " << ttyString << " (" << QFileInfo(ttyString).owner() << "): " << error; - _exit(Auth::HELPER_TTY_ERROR); + _exit(Auth::TTY_ERROR); } if (ioctl(STDIN_FILENO, KDSKBMODE, K_OFF) == -1) { qCritical().nospace() << "Failed to set keyboard mode to K_OFF"; - _exit(Auth::HELPER_TTY_ERROR); + _exit(Auth::TTY_ERROR); } } VirtualTerminal::jumpToVt(vtNumber, !waylandUserSession); } -#ifdef Q_OS_LINUX // enter Linux namespaces for (const QString &ns: mainConfig.Namespaces.get()) { qInfo() << "Entering namespace" << ns; int fd = ::open(qPrintable(ns), O_RDONLY); if (fd < 0) { qCritical("open(%s) failed: %s", qPrintable(ns), strerror(errno)); - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } if (setns(fd, 0) != 0) { qCritical("setns(open(%s), 0) failed: %s", qPrintable(ns), strerror(errno)); - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } ::close(fd); } -#endif // switch user - const QByteArray username = qobject_cast(parent())->user().toLocal8Bit(); + const QByteArray username = qobject_cast(parent())->user.toLocal8Bit(); struct passwd pw; struct passwd *rpw; long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); @@ -271,7 +214,7 @@ namespace DDM { QScopedPointer buffer(static_cast(malloc(bufsize))); if (buffer.isNull()) { qCritical() << "Could not allocate buffer of size" << bufsize; - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } int err = getpwnam_r(username.constData(), &pw, buffer.data(), bufsize, &rpw); if (rpw == NULL) { @@ -279,26 +222,18 @@ namespace DDM { qCritical() << "getpwnam_r(" << username << ") username not found!"; else qCritical() << "getpwnam_r(" << username << ") failed with error: " << strerror(err); - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } const int xauthHandle = m_xauthFile.handle(); if (xauthHandle != -1 && fchown(xauthHandle, pw.pw_uid, pw.pw_gid) != 0) { qCritical() << "fchown failed for" << m_xauthFile.fileName(); - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } -#if defined(Q_OS_FREEBSD) - // execve() uses the environment prepared in Backend::openSession(), - // therefore environment variables which are set here are ignored. - if (setusercontext(NULL, &pw, pw.pw_uid, LOGIN_SETALL) != 0) { - qCritical() << "setusercontext(NULL, *, " << pw.pw_uid << ", LOGIN_SETALL) failed for user: " << username; - exit(Auth::HELPER_OTHER_ERROR); - } -#else if (setgid(pw.pw_gid) != 0) { qCritical() << "setgid(" << pw.pw_gid << ") failed for user: " << username; - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } // fetch ambient groups from PAM's environment; @@ -310,7 +245,7 @@ namespace DDM { if ((n_pam_groups = getgroups(n_pam_groups, pam_groups)) == -1) { qCritical() << "getgroups() failed to fetch supplemental" << "PAM groups for user:" << username; - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } } else { n_pam_groups = 0; @@ -327,7 +262,7 @@ namespace DDM { &n_user_groups)) == -1 ) { qCritical() << "getgrouplist(" << pw.pw_name << ", " << pw.pw_gid << ") failed"; - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } } @@ -343,7 +278,7 @@ namespace DDM { // setgroups(2) handles duplicate groups if (setgroups(n_groups, groups) != 0) { qCritical() << "setgroups() failed for user: " << username; - exit (Auth::HELPER_OTHER_ERROR); + exit (Auth::OTHER_ERROR); } delete[] groups; } @@ -352,13 +287,13 @@ namespace DDM { if (setuid(pw.pw_uid) != 0) { qCritical() << "setuid(" << pw.pw_uid << ") failed for user: " << username; - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } -#endif /* Q_OS_FREEBSD */ + if (chdir(pw.pw_dir) != 0) { qCritical() << "chdir(" << pw.pw_dir << ") failed for user: " << username; qCritical() << "verify directory exist and has sufficient permissions"; - exit(Auth::HELPER_OTHER_ERROR); + exit(Auth::OTHER_ERROR); } if (sessionClass != QLatin1String("greeter")) { @@ -397,9 +332,4 @@ namespace DDM { } } } - - qint64 UserSession::cachedProcessId() { - return m_cachedProcessId; - } - } diff --git a/src/helper/UserSession.h b/src/daemon/UserSession.h similarity index 65% rename from src/helper/UserSession.h rename to src/daemon/UserSession.h index f6fd625..8cb2599 100644 --- a/src/helper/UserSession.h +++ b/src/daemon/UserSession.h @@ -27,50 +27,29 @@ #include namespace DDM { - class HelperApp; + class Auth; class XOrgUserHelper; class WaylandHelper; class UserSession : public QProcess { Q_OBJECT public: - explicit UserSession(HelperApp *parent); + explicit UserSession(Auth *parent); - bool start(); + void start(); void stop(); - QString displayServerCommand() const; - void setDisplayServerCommand(const QString &command); - - void setPath(const QString &path); - QString path() const; - - /*! - \brief Gets m_cachedProcessId - \return The cached process ID - */ - qint64 cachedProcessId(); - - - Q_SIGNALS: - void finished(int exitCode); + /** + * Needed for getting the PID of a finished UserSession and calling HelperApp::utmpLogout + */ + qint64 cachedProcessId = -1; private: - void setup(); - // Don't call it directly, it will be invoked by the child process only void childModifier(); - QString m_path { }; QTemporaryFile m_xauthFile; - QString m_displayServerCmd; - - HelperApp *m_helperApp; - - /*! - Needed for getting the PID of a finished UserSession and calling HelperApp::utmpLogout - */ - qint64 m_cachedProcessId = -1; + Auth *m_auth; }; } diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt deleted file mode 100644 index d877f7d..0000000 --- a/src/helper/CMakeLists.txt +++ /dev/null @@ -1,81 +0,0 @@ -include(CheckLibraryExists) - -include_directories( - ${LIBXAU_INCLUDE_DIRS} -) - -set(HELPER_SOURCES - HelperApp.cpp - UserSession.cpp - Pam.cpp -) - -add_executable(ddm-helper ${HELPER_SOURCES}) -target_link_libraries(ddm-helper - PRIVATE - auth - ${PAM_LIBRARIES} - PUBLIC - Qt${QT_MAJOR_VERSION}::Network - Qt${QT_MAJOR_VERSION}::DBus - Qt${QT_MAJOR_VERSION}::Qml -) -if("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") - # On FreeBSD (possibly other BSDs as well), we want to use - # setusercontext() to set up the login configuration from login.conf - find_library(_have_libutil util) - if(_have_libutil) - check_library_exists(${_have_libutil} setusercontext "" _have_setusercontext) - endif() - if(_have_libutil AND _have_setusercontext) - target_link_libraries(ddm-helper PUBLIC ${_have_libutil}) - endif() -endif() - -install(TARGETS ddm-helper RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") - -add_executable(ddm-helper-start-wayland HelperStartWayland.cpp waylandsocketwatcher.cpp waylandhelper.cpp) -target_link_libraries(ddm-helper-start-wayland - PRIVATE - auth - PUBLIC - Qt${QT_MAJOR_VERSION}::Core -) -install(TARGETS ddm-helper-start-wayland RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") - -add_executable(ddm-helper-start-single-wayland HelperStartSingleMode.cpp singlewaylandhelper.cpp) -target_link_libraries(ddm-helper-start-single-wayland - PRIVATE - auth - PUBLIC - Qt${QT_MAJOR_VERSION}::Core -) -install(TARGETS ddm-helper-start-single-wayland RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") - -add_executable(ddm-helper-start-x11user HelperStartX11User.cpp xorguserhelper.cpp) -target_link_libraries(ddm-helper-start-x11user - PRIVATE - auth - PUBLIC - Qt${QT_MAJOR_VERSION}::Core -) -install(TARGETS ddm-helper-start-x11user RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") - -if(JOURNALD_FOUND) - target_link_libraries(ddm-helper - PUBLIC - ${JOURNALD_LIBRARIES} - ) - target_link_libraries(ddm-helper-start-x11user - PUBLIC - ${JOURNALD_LIBRARIES} - ) - target_link_libraries(ddm-helper-start-wayland - PUBLIC - ${JOURNALD_LIBRARIES} - ) - target_link_libraries(ddm-helper-start-single-wayland - PUBLIC - ${JOURNALD_LIBRARIES} - ) -endif() diff --git a/src/helper/HelperApp.cpp b/src/helper/HelperApp.cpp deleted file mode 100644 index 1fe0f5f..0000000 --- a/src/helper/HelperApp.cpp +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Main authentication application class - * Copyright (C) 2013 Martin Bříza - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include "HelperApp.h" -#include "Pam.h" -#include "Configuration.h" -#include "UserSession.h" -#include "SafeDataStream.h" - -#include "MessageHandler.h" -#include "VirtualTerminal.h" -#include "SignalHandler.h" -#include "Messages.h" -#include "SafeDataStream.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#if defined(Q_OS_LINUX) -#include -#endif -#include -#include -#include - -namespace DDM { - static Request passwordRequest{ - { { AuthPrompt::LOGIN_PASSWORD, QStringLiteral("Password: "), true } } - }; - - HelperApp::HelperApp(int& argc, char** argv) - : QCoreApplication(argc, argv) - , m_pam(new Pam(this)) - , m_session(new UserSession(this)) - , m_socket(new QLocalSocket(this)) { - qInstallMessageHandler(HelperMessageHandler); - //TODO: replace this workaround in the future - - QTimer::singleShot(0, this, SLOT(setUp())); - } - - void HelperApp::setUp() { - const QStringList args = QCoreApplication::arguments(); - QString server; - int pos; - - if ((pos = args.indexOf(QStringLiteral("--socket"))) >= 0) { - if (pos >= args.length() - 1) { - qCritical() << "This application is not supposed to be executed manually"; - exit(Auth::HELPER_OTHER_ERROR); - return; - } - server = args[pos + 1]; - } - - if ((pos = args.indexOf(QStringLiteral("--id"))) >= 0) { - if (pos >= args.length() - 1) { - qCritical() << "This application is not supposed to be executed manually"; - exit(Auth::HELPER_OTHER_ERROR); - return; - } - m_id = QString(args[pos + 1]).toLongLong(); - } - - if ((pos = args.indexOf(QStringLiteral("--start"))) >= 0) { - if (pos >= args.length() - 1) { - qCritical() << "This application is not supposed to be executed manually"; - exit(Auth::HELPER_OTHER_ERROR); - return; - } - m_session->setPath(args[pos + 1]); - } - - if ((pos = args.indexOf(QStringLiteral("--user"))) >= 0) { - if (pos >= args.length() - 1) { - qCritical() << "This application is not supposed to be executed manually"; - exit(Auth::HELPER_OTHER_ERROR); - return; - } - m_user = args[pos + 1]; - m_pam->user = m_user; - } - - if ((pos = args.indexOf(QStringLiteral("--display-server"))) >= 0) { - if (pos >= args.length() - 1) { - qCritical() << "This application is not supposed to be executed manually"; - exit(Auth::HELPER_OTHER_ERROR); - return; - } - m_session->setDisplayServerCommand(args[pos + 1]); - } - - if ((pos = args.indexOf(QStringLiteral("--identify-only"))) >= 0) { - m_identifyOnly = true; - } - - if ((pos = args.indexOf(QStringLiteral("--skip-auth"))) >= 0) { - m_skipAuth = true; - } - - if (server.isEmpty() || m_id <= 0) { - qCritical() << "This application is not supposed to be executed manually"; - exit(Auth::HELPER_OTHER_ERROR); - return; - } - - connect(m_socket, &QLocalSocket::connected, this, &HelperApp::doAuth); - if(!m_identifyOnly){ - connect(m_session, &UserSession::finished, this, &HelperApp::sessionFinished); - } - - connect(new SignalHandler(this), &SignalHandler::sigtermReceived, this, [] { - qDebug() << "sigterm received."; - qApp->quit(); - }); - - m_socket->connectToServer(server, QIODevice::ReadWrite | QIODevice::Unbuffered); - } - - void HelperApp::doAuth() { - SafeDataStream str(m_socket); - str << Msg::HELLO << m_id; - str.send(); - if (str.status() != QDataStream::Ok) - qCritical() << "Couldn't write initial message:" << str.status(); - - if (!m_pam->start()) { - authenticated(QString()); - - // write failed login to btmp - const QProcessEnvironment env = m_session->processEnvironment(); - const QString displayId = env.value(QStringLiteral("DISPLAY")); - const QString vt = env.value(QStringLiteral("XDG_VTNR")); - utmpLogin(vt, displayId, m_user, 0, false); - - exit(Auth::HELPER_AUTH_ERROR); - return; - } - - Q_ASSERT(getuid() == 0); - if (!m_skipAuth) { - Request req = request(passwordRequest); - if (req.prompts.length() <= 0 || !m_pam->authenticate(req.prompts[0].response)) { - authenticated(QString()); - - // write failed login to btmp - const QProcessEnvironment env = m_session->processEnvironment(); - const QString displayId = env.value(QStringLiteral("DISPLAY")); - const QString vt = env.value(QStringLiteral("XDG_VTNR")); - utmpLogin(vt, displayId, m_user, 0, false); - - exit(Auth::HELPER_AUTH_ERROR); - return; - } - } - - QProcessEnvironment env = authenticated(m_user); - - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { - // Qt internally may load the xdg portal system early on, prevent this, we do not have a functional session running. - env.insert(QStringLiteral("QT_NO_XDG_DESKTOP_PORTAL"), QStringLiteral("1")); - for (const auto &entry : mainConfig.GreeterEnvironment.get()) { - const int index = entry.indexOf(QLatin1Char('=')); - if (index < 0) { - qWarning() << "Malformed environment variable" << entry; - continue; - } - env.insert(entry.left(index), entry.mid(index + 1)); - } - } - - if (!m_session->path().isEmpty()) { - env.insert(m_session->processEnvironment()); - m_session->setProcessEnvironment(env); - - auto sessionEnv = m_pam->openSession(env); - if (!sessionEnv.has_value()) { - sessionOpened(false, 0); - exit(Auth::HELPER_SESSION_ERROR); - return; - } - - env = *sessionEnv; - int sessionId = env.value(QStringLiteral("XDG_SESSION_ID")).toInt(); - sessionOpened(true, sessionId); - - struct passwd *pw; - pw = getpwnam(qPrintable(m_user)); - if (pw) { - env.insert(QStringLiteral("HOME"), QString::fromLocal8Bit(pw->pw_dir)); - env.insert(QStringLiteral("PWD"), QString::fromLocal8Bit(pw->pw_dir)); - env.insert(QStringLiteral("SHELL"), QString::fromLocal8Bit(pw->pw_shell)); - env.insert(QStringLiteral("USER"), QString::fromLocal8Bit(pw->pw_name)); - env.insert(QStringLiteral("LOGNAME"), QString::fromLocal8Bit(pw->pw_name)); - } - m_session->setProcessEnvironment(env); - m_session->start(); - - // write successful login to utmp/wtmp - const QString displayId = env.value(QStringLiteral("DISPLAY")); - const QString vt = env.value(QStringLiteral("XDG_VTNR")); - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) != QLatin1String("greeter")) { - // cache pid for session end - utmpLogin(vt, displayId, m_user, m_session->processId(), true); - } - } - else - exit(Auth::HELPER_SUCCESS); - } - - void HelperApp::sessionFinished(int status) { - exit(status); - } - - void HelperApp::info(const QString& message, Auth::Info type) { - SafeDataStream str(m_socket); - str << Msg::INFO << message << type; - str.send(); - m_socket->waitForBytesWritten(); - } - - void HelperApp::error(const QString& message, Auth::Error type) { - SafeDataStream str(m_socket); - str << Msg::ERROR << message << type; - str.send(); - m_socket->waitForBytesWritten(); - } - - bool HelperApp::isSingleMode() const { - const QStringList args = QCoreApplication::arguments(); - return args.indexOf(QStringLiteral("--single-mode")) >= 0; - } - - bool HelperApp::isGreeter() const { - const QStringList args = QCoreApplication::arguments(); - return args.indexOf(QStringLiteral("--greeter")) >= 0; - } - - Request HelperApp::request(const Request& request) { - Msg m = Msg::MSG_UNKNOWN; - Request response; - SafeDataStream str(m_socket); - str << Msg::REQUEST << request; - str.send(); - str.receive(); - str >> m >> response; - if (m != REQUEST) { - response = Request(); - qCritical() << "Received a wrong opcode instead of REQUEST:" << m; - } - return response; - } - - QProcessEnvironment HelperApp::authenticated(const QString &user) { - Msg m = Msg::MSG_UNKNOWN; - QProcessEnvironment env; - SafeDataStream str(m_socket); - str << Msg::AUTHENTICATED << user; - str.send(); - if (user.isEmpty()) - return env; - str.receive(); - str >> m >> env >> m_cookie; - if (m != AUTHENTICATED) { - env = QProcessEnvironment(); - m_cookie = {}; - qCritical() << "Received a wrong opcode instead of AUTHENTICATED:" << m; - } - return env; - } - - void HelperApp::sessionOpened(bool success, int sessionId) { - Msg m = Msg::MSG_UNKNOWN; - SafeDataStream str(m_socket); - str << Msg::SESSION_STATUS << success << sessionId; - str.send(); - str.receive(); - str >> m; - if (m != SESSION_STATUS) { - qCritical() << "Received a wrong opcode instead of SESSION_STATUS:" << m; - } - } - - void HelperApp::displayServerStarted(const QString &displayName) - { - Msg m = Msg::MSG_UNKNOWN; - SafeDataStream str(m_socket); - str << Msg::DISPLAY_SERVER_STARTED << displayName; - str.send(); - str.receive(); - str >> m; - if (m != DISPLAY_SERVER_STARTED) { - qCritical() << "Received a wrong opcode instead of DISPLAY_SERVER_STARTED:" << m; - } - } - - UserSession *HelperApp::session() { - return m_session; - } - - const QString& HelperApp::user() const { - return m_user; - } - - const QByteArray& HelperApp::cookie() const { - return m_cookie; - } - - HelperApp::~HelperApp() { - Q_ASSERT(getuid() == 0); - - m_session->stop(); - if(!m_identifyOnly && m_pam->sessionOpened){ - m_pam->closeSession(); - } - - // write logout to utmp/wtmp - qint64 pid = m_session->cachedProcessId(); - if (pid < 0) { - return; - } - QProcessEnvironment env = m_session->processEnvironment(); - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) != QLatin1String("greeter")) { - QString vt = env.value(QStringLiteral("XDG_VTNR")); - QString displayId = env.value(QStringLiteral("DISPLAY")); - utmpLogout(vt, displayId, pid); - } - } - - void HelperApp::utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful) { - struct utmpx entry { }; - struct timeval tv; - - entry.ut_type = USER_PROCESS; - entry.ut_pid = pid; - - // ut_line: vt - if (!vt.isEmpty()) { - QString tty = QStringLiteral("tty"); - tty.append(vt); - QByteArray ttyBa = tty.toLocal8Bit(); - const char* ttyChar = ttyBa.constData(); - strncpy(entry.ut_line, ttyChar, sizeof(entry.ut_line) - 1); - } - - // ut_host: displayName - QByteArray displayBa = displayName.toLocal8Bit(); - const char* displayChar = displayBa.constData(); - strncpy(entry.ut_host, displayChar, sizeof(entry.ut_host) - 1); - - // ut_user: user - QByteArray userBa = user.toLocal8Bit(); - const char* userChar = userBa.constData(); - strncpy(entry.ut_user, userChar, sizeof(entry.ut_user) -1); - - gettimeofday(&tv, NULL); - entry.ut_tv.tv_sec = tv.tv_sec; - entry.ut_tv.tv_usec = tv.tv_usec; - - // write to utmp - setutxent(); - if (!pututxline (&entry)) - qWarning() << "Failed to write utmpx: " << strerror(errno); - endutxent(); - -#if !defined(Q_OS_FREEBSD) - // append to failed login database btmp - if (!authSuccessful) { -#if defined(Q_OS_LINUX) - updwtmpx("/var/log/btmp", &entry); -#endif - } - - // append to wtmp - else { -#if defined(Q_OS_LINUX) - updwtmpx("/var/log/wtmp", &entry); -#endif - } -#endif - } - - void HelperApp::utmpLogout(const QString &vt, const QString &displayName, qint64 pid) { - struct utmpx entry { }; - struct timeval tv; - - entry.ut_type = DEAD_PROCESS; - entry.ut_pid = pid; - - // ut_line: vt - if (!vt.isEmpty()) { - QString tty = QStringLiteral("tty"); - tty.append(vt); - QByteArray ttyBa = tty.toLocal8Bit(); - const char* ttyChar = ttyBa.constData(); - strncpy(entry.ut_line, ttyChar, sizeof(entry.ut_line) - 1); - } - - // ut_host: displayName - QByteArray displayBa = displayName.toLocal8Bit(); - const char* displayChar = displayBa.constData(); - strncpy(entry.ut_host, displayChar, sizeof(entry.ut_host) - 1); - - gettimeofday(&tv, NULL); - entry.ut_tv.tv_sec = tv.tv_sec; - entry.ut_tv.tv_usec = tv.tv_usec; - - // write to utmp - setutxent(); - if (!pututxline (&entry)) - qWarning() << "Failed to write utmpx: " << strerror(errno); - endutxent(); - -#if defined(Q_OS_LINUX) - // append to wtmp - updwtmpx("/var/log/wtmp", &entry); -#elif defined(Q_OS_FREEBSD) - pututxline(&entry); -#endif - } -} - -int main(int argc, char** argv) { - DDM::HelperApp app(argc, argv); - return app.exec(); -} diff --git a/src/helper/HelperApp.h b/src/helper/HelperApp.h deleted file mode 100644 index e88ab81..0000000 --- a/src/helper/HelperApp.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Main authentication application class - * Copyright (C) 2013 Martin Bříza - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#ifndef Auth_H -#define Auth_H - -#include -#include - -#include "AuthMessages.h" - -class QLocalSocket; - -namespace DDM { - class Pam; - class UserSession; - class HelperApp : public QCoreApplication - { - Q_OBJECT - public: - HelperApp(int& argc, char** argv); - virtual ~HelperApp(); - - UserSession *session(); - const QString &user() const; - const QByteArray &cookie() const; - - bool isSingleMode() const; - bool isGreeter() const; - - public slots: - Request request(const Request &request); - void info(const QString &message, Auth::Info type); - void error(const QString &message, Auth::Error type); - QProcessEnvironment authenticated(const QString &user); - void displayServerStarted(const QString &displayName); - void sessionOpened(bool success, int sessionId); - - private slots: - void setUp(); - void doAuth(); - - void sessionFinished(int status); - - private: - qint64 m_id { -1 }; - Pam *m_pam { nullptr }; - UserSession *m_session { nullptr }; - QLocalSocket *m_socket { nullptr }; - QString m_user { }; - // TODO: get rid of this in a nice clean way along the way with moving to user session X server - QByteArray m_cookie { }; - bool m_skipAuth = false; - bool m_identifyOnly = false; - - /*! - \brief Write utmp/wtmp/btmp records when a user logs in - \param vt Virtual terminal (tty7, tty8,...) - \param displayName Display (:0, :1,...) - \param user User logging in - \param pid User process ID (e.g. PID of startkde) - \param authSuccessful Was authentication successful - */ - void utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful); - - /*! - \brief Write utmp/wtmp records when a user logs out - \param vt Virtual terminal (tty7, tty8,...) - \param displayName Display (:0, :1,...) - \param pid User process ID (e.g. PID of startkde) - */ - void utmpLogout(const QString &vt, const QString &displayName, qint64 pid); - }; -} - -#endif // Auth_H diff --git a/src/helper/HelperStartSingleMode.cpp b/src/helper/HelperStartSingleMode.cpp deleted file mode 100644 index 5f9344c..0000000 --- a/src/helper/HelperStartSingleMode.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2023 Dingyuan Zhang . -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include -#include - -#include "singlewaylandhelper.h" -#include "MessageHandler.h" -#include -#include "Auth.h" -#include "SignalHandler.h" - -void WaylandHelperMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - DDM::messageHandler(type, context, QStringLiteral("WaylandHelper: "), msg); -} - -int main(int argc, char** argv) -{ - qInstallMessageHandler(WaylandHelperMessageHandler); - QCoreApplication app(argc, argv); - using namespace DDM; - DDM::SignalHandler s; - - //Q_ASSERT(::getuid() != 0); - if (argc < 3) { - QTextStream(stderr) << "Wrong number of arguments\n"; - return Auth::HELPER_OTHER_ERROR; - } - - SingleWaylandHelper helper; - QObject::connect(&s, &DDM::SignalHandler::sigtermReceived, &app, [] { - QCoreApplication::exit(0); - }); - - QStringList args; - args += app.arguments()[1].split(" "); - args += app.arguments()[2].split(" "); - - if (!helper.start(args.takeFirst(), args.join(" "))) { - qWarning() << "DDM was unable to start" << app.arguments(); - return Auth::HELPER_DISPLAYSERVER_ERROR; - } - - return app.exec(); -} diff --git a/src/helper/HelperStartWayland.cpp b/src/helper/HelperStartWayland.cpp deleted file mode 100644 index 6043d3d..0000000 --- a/src/helper/HelperStartWayland.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Session process wrapper - * Copyright (C) 2021 Aleix Pol Gonzalez - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -/** - * This application sole purpose is to launch a wayland compositor (first - * argument) and as soon as it's set up to launch a client (second argument) - */ - -#include -#include -#include -#include -#include -#include - -#include "waylandhelper.h" -#include "MessageHandler.h" -#include -#include "Auth.h" -#include "SignalHandler.h" - -void WaylandHelperMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - DDM::messageHandler(type, context, QStringLiteral("WaylandHelper: "), msg); -} - -int main(int argc, char** argv) -{ - qInstallMessageHandler(WaylandHelperMessageHandler); - QCoreApplication app(argc, argv); - using namespace DDM; - DDM::SignalHandler s; - - Q_ASSERT(::getuid() != 0); - if (argc < 3) { - QTextStream(stderr) << "Wrong number of arguments\n"; - return Auth::HELPER_OTHER_ERROR; - } - - WaylandHelper helper; - QObject::connect(&s, &DDM::SignalHandler::sigtermReceived, &app, [] { - QCoreApplication::exit(0); - }); - QObject::connect(&app, &QCoreApplication::aboutToQuit, &helper, [&helper] { - qDebug("quitting helper-start-wayland"); - helper.stop(); - }); - QObject::connect(&helper, &WaylandHelper::failed, &app, [&app] { - QTextStream(stderr) << "Failed to start wayland session" << Qt::endl; - app.exit(Auth::HELPER_SESSION_ERROR); - }); - - if (!helper.startCompositor(app.arguments()[1])) { - qWarning() << "DDM was unable to start" << app.arguments()[1]; - return Auth::HELPER_DISPLAYSERVER_ERROR; - } - - helper.startGreeter(app.arguments()[2]); - - return app.exec(); -} diff --git a/src/helper/HelperStartX11User.cpp b/src/helper/HelperStartX11User.cpp deleted file mode 100644 index 6f3a8c4..0000000 --- a/src/helper/HelperStartX11User.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Session process wrapper - * Copyright (C) 2021 Aleix Pol Gonzalez - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -/** - * This application sole purpose is to launch an X11 rootless compositor compositor (first - * argument) and as soon as it's set up to launch a client (second argument) - */ - -#include -#include -#include -#include -#include -#include "xorguserhelper.h" -#include "MessageHandler.h" -#include -#include "SignalHandler.h" - -void X11UserHelperMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - DDM::messageHandler(type, context, QStringLiteral("X11UserHelper: "), msg); -} - -int main(int argc, char** argv) -{ - qInstallMessageHandler(X11UserHelperMessageHandler); - QCoreApplication app(argc, argv); - DDM::SignalHandler s; - QObject::connect(&s, &DDM::SignalHandler::sigtermReceived, &app, [] { - QCoreApplication::instance()->exit(-1); - }); - - Q_ASSERT(::getuid() != 0); - if (argc != 3) { - QTextStream(stderr) << "Wrong number of arguments\n"; - return 33; - } - - using namespace DDM; - XOrgUserHelper helper; - QObject::connect(&app, &QCoreApplication::aboutToQuit, &helper, [&helper] { - qDebug("quitting helper-start-x11"); - helper.stop(); - }); - QObject::connect(&helper, &XOrgUserHelper::displayChanged, &app, [&helper, &app] { - qDebug() << "starting XOrg Greeter..." << helper.sessionEnvironment().value(QStringLiteral("DISPLAY")); - auto args = QProcess::splitCommand(app.arguments()[2]); - - QProcess *process = new QProcess(&app); - process->setProcessChannelMode(QProcess::ForwardedChannels); - process->setProgram(args.takeFirst()); - process->setArguments(args); - process->setProcessEnvironment(helper.sessionEnvironment()); - process->start(); - QObject::connect(process, QOverload::of(&QProcess::finished), &app, &QCoreApplication::quit); - }); - - helper.start(app.arguments()[1]); - return app.exec(); -} diff --git a/src/helper/singlewaylandhelper.cpp b/src/helper/singlewaylandhelper.cpp deleted file mode 100644 index 3320e2c..0000000 --- a/src/helper/singlewaylandhelper.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2023 Dingyuan Zhang . -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "singlewaylandhelper.h" - -#include -#include -#include -#include - -SingleWaylandHelper::SingleWaylandHelper(QObject *parent) - : QObject(parent) -{} - -bool SingleWaylandHelper::start(const QString &compositor, const QString &cmd) -{ - auto args = QProcess::splitCommand(cmd); - - m_process = new QProcess(this); - m_process->setProgram(compositor); - m_process->setArguments(args); - m_process->setStandardOutputFile(QProcess::nullDevice()); - m_process->setStandardErrorFile(QProcess::nullDevice()); - m_process->setStandardInputFile(QProcess::nullDevice()); - m_process->setProcessEnvironment([]{ - auto env = QProcessEnvironment::systemEnvironment(); - env.insert("LIBSEAT_BACKEND", "seatd"); - env.insert("DSG_APP_ID", "org.deepin.dde.treeland"); - return env; - }()); - - connect(m_process, QOverload::of(&QProcess::finished), - qApp, &QCoreApplication::quit); - - m_process->start(); - if (!m_process->waitForStarted(10000)) { - qWarning("Failed to start \"%s\": %s", - qPrintable(cmd), - qPrintable(m_process->errorString())); - return false; - } - - return true; -} diff --git a/src/helper/singlewaylandhelper.h b/src/helper/singlewaylandhelper.h deleted file mode 100644 index 838edc9..0000000 --- a/src/helper/singlewaylandhelper.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2023 Dingyuan Zhang . -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -class QProcess; - -class SingleWaylandHelper : public QObject { - Q_OBJECT -public: - explicit SingleWaylandHelper(QObject *parent = nullptr); - - bool start(const QString& compositor, const QString& args); - -private: - QProcess *m_process; -}; diff --git a/src/helper/waylandhelper.cpp b/src/helper/waylandhelper.cpp deleted file mode 100644 index 4a4acfc..0000000 --- a/src/helper/waylandhelper.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2021 Aleix Pol Gonzalez -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#include -#include -#include - -#include "Configuration.h" - -#include "waylandhelper.h" -#include "waylandsocketwatcher.h" -#include "VirtualTerminal.h" - -#include -#include - -namespace DDM { - -WaylandHelper::WaylandHelper(QObject *parent) - : QObject(parent) - , m_environment(QProcessEnvironment::systemEnvironment()) - , m_watcher(new WaylandSocketWatcher(this)) -{ -} - -bool WaylandHelper::startCompositor(const QString &cmd) -{ - m_watcher->start(); - return startProcess(cmd, &m_serverProcess); -} - -void stopProcess(QProcess *process) -{ - if (process && process->state() != QProcess::NotRunning) { - qInfo() << "Stopping..." << process->program(); - process->terminate(); - if (!process->waitForFinished(5000)) { - process->kill(); - process->waitForFinished(25000); - } - process->deleteLater(); - process = nullptr; - } -} - -void WaylandHelper::stop() -{ - m_watcher->stop(); - stopProcess(m_greeterProcess); - stopProcess(m_serverProcess); -} - -bool WaylandHelper::startProcess(const QString &cmd, QProcess **p) -{ - auto *process = new QProcess(this); - process->setProcessEnvironment(m_environment); - process->setInputChannelMode(QProcess::ForwardedInputChannel); - connect(process, &QProcess::readyReadStandardError, this, [process] { - qWarning() << process->readAllStandardError(); - }); - connect(process, &QProcess::readyReadStandardOutput, this, [process] { - qInfo() << process->readAllStandardOutput(); - }); - qDebug() << "Starting Wayland process" << cmd << m_environment.value(QStringLiteral("USER")); - connect(process, QOverload::of(&QProcess::finished), - process, [](int exitCode, QProcess::ExitStatus exitStatus) { - qDebug() << "wayland compositor finished" << exitCode << exitStatus; - if (exitCode != 0 || exitStatus != QProcess::NormalExit) - QCoreApplication::instance()->quit(); - }); - - auto args = QProcess::splitCommand(cmd); - const auto program = args.takeFirst(); - process->start(program, args); - if (!process->waitForStarted(10000)) { - qWarning("Failed to start \"%s\": %s", - qPrintable(cmd), - qPrintable(process->errorString())); - return false; - } - - if (p) - *p = process; - - qDebug() << "started succesfully" << cmd; - return true; -} - -void WaylandHelper::startGreeter(const QString &cmd) -{ - auto args = QProcess::splitCommand(cmd); - - m_greeterProcess = new QProcess(this); - m_greeterProcess->setProgram(args.takeFirst()); - m_greeterProcess->setArguments(args); - connect(m_greeterProcess, &QProcess::readyReadStandardError, this, [this] { - qWarning() << m_greeterProcess->readAllStandardError(); - }); - connect(m_greeterProcess, &QProcess::readyReadStandardOutput, this, [this] { - qInfo() << m_greeterProcess->readAllStandardOutput(); - }); - connect(m_greeterProcess, QOverload::of(&QProcess::finished), - m_greeterProcess, [](int exitCode, QProcess::ExitStatus exitStatus) { - qDebug() << "wayland greeter finished" << exitCode << exitStatus; - QCoreApplication::instance()->quit(); - }); - - if (m_watcher->status() == WaylandSocketWatcher::Started) { - m_environment.insert(QStringLiteral("WAYLAND_DISPLAY"), m_watcher->socketName()); - m_greeterProcess->setProcessEnvironment(m_environment); - m_greeterProcess->start(); - } else if (m_watcher->status() == WaylandSocketWatcher::Failed) { - Q_EMIT failed(); - } else { - connect(m_watcher, &WaylandSocketWatcher::failed, this, &WaylandHelper::failed); - connect(m_watcher, &WaylandSocketWatcher::started, this, [this] { - m_watcher->stop(); - m_environment.insert(QStringLiteral("WAYLAND_DISPLAY"), m_watcher->socketName()); - m_greeterProcess->setProcessEnvironment(m_environment); - m_greeterProcess->start(); - }); - } -} - -} // namespace DDM diff --git a/src/helper/waylandhelper.h b/src/helper/waylandhelper.h deleted file mode 100644 index ad5b056..0000000 --- a/src/helper/waylandhelper.h +++ /dev/null @@ -1,54 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2021 Aleix Pol Gonzalez -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#ifndef WAYLANDHELPER_H -#define WAYLANDHELPER_H - -#include - -namespace DDM { -class WaylandSocketWatcher; - -class WaylandHelper : public QObject -{ - Q_OBJECT -public: - explicit WaylandHelper(QObject *parent = nullptr); - - bool startCompositor(const QString &cmd); - void startGreeter(const QString &cmd); - void stop(); - -Q_SIGNALS: - void failed(); - -private: - QProcessEnvironment m_environment; - QProcess *m_serverProcess = nullptr; - QProcess *m_greeterProcess = nullptr; - WaylandSocketWatcher * const m_watcher; - - bool startProcess(const QString &cmd, QProcess **p = nullptr); -}; - -} // namespace DDM - - - -#endif diff --git a/src/helper/waylandsocketwatcher.cpp b/src/helper/waylandsocketwatcher.cpp deleted file mode 100644 index 82cf2cd..0000000 --- a/src/helper/waylandsocketwatcher.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2021 Pier Luigi Fiorini -* Copyright (C) 2021 Aleix Pol Gonzalez -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#include -#include -#include - -#include "waylandsocketwatcher.h" - -namespace DDM { - -WaylandSocketWatcher::WaylandSocketWatcher(QObject *parent ) - : QObject(parent) - , m_runtimeDir(QDir(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation))) -{ - m_runtimeDir.setFilter(QDir::Files | QDir::System); - m_runtimeDir.setNameFilters(QStringList() << QLatin1String("wayland-?")); -} - -WaylandSocketWatcher::Status WaylandSocketWatcher::status() const -{ - return m_status; -} - -QString WaylandSocketWatcher::socketName() const -{ - return m_socketName; -} - -void WaylandSocketWatcher::start() -{ - m_watcher = new QFileSystemWatcher(this); - - // Give the compositor some time to start - m_timer.setSingleShot(true); - m_timer.setInterval(15000); - connect(&m_timer, &QTimer::timeout, this, [this] { - // Time is up and a socket was not found - if (!m_watcher.isNull()) - m_watcher->deleteLater(); - qWarning("Wayland socket watcher timed out"); - m_status = Failed; - Q_EMIT failed(); - }); - - // Check if the socket exists - connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, - [this](const QString &path) { - qDebug() << "Directory" << path << "has changed, checking for Wayland socket"; - - m_runtimeDir.refresh(); - const QFileInfoList fileInfoList = m_runtimeDir.entryInfoList(); - for (const QFileInfo &fileInfo : fileInfoList) { - if (fileInfo.ownerId() == ::getuid()) { - qDebug() << "Found Wayland socket" << fileInfo.absoluteFilePath(); - m_timer.stop(); - if (!m_watcher.isNull()) - m_watcher->deleteLater(); - m_socketName = fileInfo.fileName(); - m_status = Started; - Q_EMIT started(); - break; - } - } - }); - - // Watch for runtime directory changes - if (!m_runtimeDir.exists() || !m_watcher->addPath(m_runtimeDir.absolutePath())) { - qWarning("Cannot watch directory \"%s\" for Wayland socket", - qPrintable(m_runtimeDir.absolutePath())); - m_watcher->deleteLater(); - m_status = Failed; - Q_EMIT failed(); - } - - // Start - m_timer.start(); -} - -void WaylandSocketWatcher::stop() -{ - m_timer.stop(); - if (!m_watcher.isNull()) - m_watcher->deleteLater(); - m_watcher.clear(); - m_status = Stopped; - Q_EMIT stopped(); -} - -} // namespace DDM diff --git a/src/helper/waylandsocketwatcher.h b/src/helper/waylandsocketwatcher.h deleted file mode 100644 index 17cab52..0000000 --- a/src/helper/waylandsocketwatcher.h +++ /dev/null @@ -1,64 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2021 Pier Luigi Fiorini -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#ifndef WAYLANDSOCKETWATCHER_H -#define WAYLANDSOCKETWATCHER_H - -#include -#include -#include -#include - -namespace DDM { - -class WaylandSocketWatcher : public QObject -{ - Q_OBJECT -public: - enum Status { - Started, - Stopped, - Failed - }; - Q_ENUM(Status) - - explicit WaylandSocketWatcher(QObject *parent = nullptr); - - Status status() const; - QString socketName() const; - - void start(); - void stop(); - -Q_SIGNALS: - void started(); - void stopped(); - void failed(); - -private: - Status m_status = Stopped; - QDir m_runtimeDir; - QString m_socketName; - QTimer m_timer; - QPointer m_watcher; -}; - -} // namespace DDM - -#endif // WAYLANDSOCKETWATCHER_H diff --git a/src/helper/xorguserhelper.cpp b/src/helper/xorguserhelper.cpp deleted file mode 100644 index 919dd2c..0000000 --- a/src/helper/xorguserhelper.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2021 Pier Luigi Fiorini -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#include -#include -#include - -#include "Configuration.h" - -#include "xorguserhelper.h" - -#include -#include - -namespace DDM { - -XOrgUserHelper::XOrgUserHelper(QObject *parent) - : QObject(parent) -{ -} - -QProcessEnvironment XOrgUserHelper::sessionEnvironment() const -{ - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(QStringLiteral("DISPLAY"), m_display); - env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); - env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb")); - return env; -} - -QString XOrgUserHelper::display() const -{ - return m_display; -} - -bool XOrgUserHelper::start(const QString &cmd) -{ - // Create xauthority - m_xauth.setAuthDirectory(qEnvironmentVariable("XDG_RUNTIME_DIR")); - m_xauth.setup(); - - // Start server process - if (!startServer(cmd)) - return false; - - // Setup display - startDisplayCommand(); - - return true; -} - -void XOrgUserHelper::stop() -{ - if (m_serverProcess) { - qInfo("Stopping server..."); - m_serverProcess->terminate(); - if (!m_serverProcess->waitForFinished(5000)) { - m_serverProcess->kill(); - m_serverProcess->waitForFinished(25000); - } - m_serverProcess->deleteLater(); - m_serverProcess = nullptr; - - displayFinished(); - } -} - -bool XOrgUserHelper::startProcess(const QString &cmd, - const QProcessEnvironment &env, - QProcess **p) -{ - auto args = QProcess::splitCommand(cmd); - const auto program = args.takeFirst(); - - // Make sure to forward the input of this process into the Xorg - // server, otherwise it will complain that only console users are allowed - auto *process = new QProcess(this); - process->setProcessEnvironment(env); - process->setInputChannelMode(QProcess::ForwardedInputChannel); - connect(process, &QProcess::readyReadStandardError, this, [process] { - qWarning() << process->readAllStandardError(); - }); - connect(process, &QProcess::readyReadStandardOutput, this, [process] { - qInfo() << process->readAllStandardOutput(); - }); - connect(process, QOverload::of(&QProcess::finished), - process, [](int exitCode, QProcess::ExitStatus exitStatus) { - if (exitCode != 0 || exitStatus != QProcess::NormalExit) - QCoreApplication::instance()->quit(); - }); - - process->start(program, args); - if (!process->waitForStarted(10000)) { - qWarning("Failed to start \"%s\": %s", - qPrintable(cmd), - qPrintable(process->errorString())); - return false; - } - - if (p) - *p = process; - - return true; -} - -bool XOrgUserHelper::startServer(const QString &cmd) -{ - QString serverCmd = cmd; - - // Create pipe for communicating with X server - // 0 == read from X, 1 == write to X - int pipeFds[2]; - if (::pipe(pipeFds) != 0) { - qCritical("Could not create pipe to start X server"); - return false; - } - - // Do not leak the read endpoint to the X server process - fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC); - - // Server environment - // Not setting XORG_RUN_AS_USER_OK=1 will make Xorg require root privileges - // under Fedora and all distros that use their patch. - // https://src.fedoraproject.org/rpms/xorg-x11-server/blob/rawhide/f/0001-Fedora-hack-Make-the-suid-root-wrapper-always-start-.patch - // https://fedoraproject.org/wiki/Changes/XorgWithoutRootRights - auto serverEnv = QProcessEnvironment::systemEnvironment(); - serverEnv.insert(QStringLiteral("XORG_RUN_AS_USER_OK"), QStringLiteral("1")); - - // Append xauth and display fd to the command - auto args = QStringList() - << QStringLiteral("-auth") << m_xauth.authPath() - << QStringLiteral("-displayfd") << QString::number(pipeFds[1]); - - // Append VT from environment - args << QStringLiteral("vt%1").arg(serverEnv.value(QStringLiteral("XDG_VTNR"))); - - // Command string - serverCmd += QLatin1Char(' ') + args.join(QLatin1Char(' ')); - - // Start the server process - qInfo("Running server: %s", qPrintable(serverCmd)); - if (!startProcess(serverCmd, serverEnv, &m_serverProcess)) { - ::close(pipeFds[0]); - return false; - } - - // Close the other side of pipe in our process, otherwise reading - // from it may stuck even X server exit - ::close(pipeFds[1]); - - // Read the display number from the pipe - QFile readPipe; - if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) { - qCritical("Failed to open pipe to start X Server"); - ::close(pipeFds[0]); - return false; - } - QByteArray displayNumber = readPipe.readLine(); - if (displayNumber.size() < 2) { - // X server gave nothing (or a whitespace) - qCritical("Failed to read display number from pipe"); - ::close(pipeFds[0]); - return false; - } - displayNumber.prepend(QByteArray(":")); - displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace - m_display = QString::fromLocal8Bit(displayNumber); - qDebug("X11 display: %s", qPrintable(m_display)); - Q_EMIT displayChanged(m_display); - - // Generate xauthority file - // For the X server's copy, the display number doesn't matter. - // An empty file would result in no access control! - if (!m_xauth.addCookie(m_display)) { - qCritical("Failed to write xauth file"); - return false; - } - - // Close our pipe - ::close(pipeFds[0]); - - return true; -} - -void XOrgUserHelper::startDisplayCommand() -{ - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(QStringLiteral("DISPLAY"), m_display); - env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); - - // Set cursor - qInfo("Setting default cursor..."); - QProcess *setCursor = nullptr; - if (startProcess(QStringLiteral("xsetroot -cursor_name left_ptr"), env, &setCursor)) { - if (!setCursor->waitForFinished(1000)) { - qWarning() << "Could not setup default cursor"; - setCursor->kill(); - } - setCursor->deleteLater(); - } - - // Display setup script - auto cmd = mainConfig.X11.DisplayCommand.get(); - qInfo("Running display setup script: %s", qPrintable(cmd)); - QProcess *displayScript = nullptr; - if (startProcess(cmd, env, &displayScript)) { - if (!displayScript->waitForFinished(30000)) - displayScript->kill(); - displayScript->deleteLater(); - } -} - -void XOrgUserHelper::displayFinished() -{ - auto cmd = mainConfig.X11.DisplayStopCommand.get(); - qInfo("Running display stop script: %s", qPrintable(cmd)); - QProcess *displayStopScript = nullptr; - if (startProcess(cmd, sessionEnvironment(), &displayStopScript)) { - if (!displayStopScript->waitForFinished(5000)) - displayStopScript->kill(); - displayStopScript->deleteLater(); - } -} - -} // namespace DDM diff --git a/src/helper/xorguserhelper.h b/src/helper/xorguserhelper.h deleted file mode 100644 index fe8ff2e..0000000 --- a/src/helper/xorguserhelper.h +++ /dev/null @@ -1,61 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2021 Pier Luigi Fiorini -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#ifndef XORGUSERHELPER_H -#define XORGUSERHELPER_H - -#include - -#include "XAuth.h" - -namespace DDM { - -class XOrgUserHelper : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString display READ display NOTIFY displayChanged) -public: - explicit XOrgUserHelper(QObject *parent = nullptr); - - /// @returns the system environment plus the variables we need here - QProcessEnvironment sessionEnvironment() const; - - QString display() const; - - bool start(const QString &cmd); - void stop(); - -Q_SIGNALS: - void displayChanged(const QString &display); - -private: - QString m_display = QStringLiteral(":0"); - XAuth m_xauth; - QProcess *m_serverProcess = nullptr; - - bool startProcess(const QString &cmd, const QProcessEnvironment &env, - QProcess **p = nullptr); - bool startServer(const QString &cmd); - void startDisplayCommand(); - void displayFinished(); -}; - -} // namespace DDM - -#endif // XORGUSERHELPER_H