diff --git a/REUSE.toml b/REUSE.toml index 40891e5..6b95a98 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/Backend.cpp", "src/helper/Backend.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/backend/PamBackend.cpp", "src/helper/backend/PamBackend.h", "src/helper/backend/PamHandle.cpp", "src/helper/backend/PamHandle.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/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"] precedence = "aggregate" SPDX-FileCopyrightText = "None" SPDX-License-Identifier = "GPL-2.0-or-later" diff --git a/src/helper/Backend.cpp b/src/helper/Backend.cpp deleted file mode 100644 index 0db6f03..0000000 --- a/src/helper/Backend.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Base backend class to be inherited further - * 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 "Backend.h" -#include "HelperApp.h" - -#include "backend/PamBackend.h" -#include "Configuration.h" -#include "UserSession.h" - -#include - -#include - -#if defined(Q_OS_FREEBSD) -#include -#include -#endif - -namespace DDM { - Backend::Backend(HelperApp* parent) - : QObject(parent) - , m_app(parent) { - } - - Backend *Backend::get(HelperApp* parent) - { - return new PamBackend(parent); - } - - void Backend::setAutologin(bool on) { - m_autologin = on; - } - - void Backend::setDisplayServer(bool on) - { - m_displayServer = on; - } - - void Backend::setGreeter(bool on) { - m_greeter = on; - } - - bool Backend::identifyOnly() const { - return m_identifyOnly; - } - - void Backend::setIdentifyOnly(bool on) { - m_identifyOnly = on; - } - - bool Backend::openSession() { - struct passwd *pw; - pw = getpwnam(qPrintable(qobject_cast(parent())->user())); - if (pw) { - QProcessEnvironment env = m_app->session()->processEnvironment(); - 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)); -#if defined(Q_OS_FREEBSD) - /* get additional environment variables via setclassenvironment(); - this needs to be done here instead of in UserSession::setupChildProcess - as the environment for execve() is prepared here - */ - login_cap_t *lc; - - if (lc = login_getpwclass(pw)) { - // save, clear and later restore DDM's environment because - // setclassenvironment() mangles it - QProcessEnvironment savedEnv = QProcessEnvironment::systemEnvironment(); - QProcessEnvironment::systemEnvironment().clear(); - QString savedLang = env.value(QStringLiteral("LANG")); - - // setclassenvironment() is the implementation inside setusercontext() - // so use lowest-level function there - setclassenvironment(lc, pw, 1); /* path variables */ - setclassenvironment(lc, pw, 0); /* non-path variables */ - login_close(lc); - if (lc = login_getuserclass(pw)) { - setclassenvironment(lc, pw, 1); - setclassenvironment(lc, pw, 0); - login_close(lc); - } - - // copy all environment variables that are now set - env.insert(QProcessEnvironment::systemEnvironment()); - // for dde itself, we don't want to set LANG from capabilities. - // instead, honour sddm_lang variable from rc script - if (qobject_cast(parent())->user() == QStringLiteral("dde")) - env.insert(QStringLiteral("LANG"), savedLang); - // finally, restore original helper environment - QProcessEnvironment::systemEnvironment().clear(); - QProcessEnvironment::systemEnvironment().insert(savedEnv); - } -#endif - // TODO: I'm fairly sure this shouldn't be done for PAM sessions, investigate! - m_app->session()->setProcessEnvironment(env); - } - return m_app->session()->start(); - } - - bool Backend::closeSession() { - return true; - } -} diff --git a/src/helper/Backend.h b/src/helper/Backend.h deleted file mode 100644 index 1c1caf7..0000000 --- a/src/helper/Backend.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Base backend class to be inherited further - * 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 BACKEND_H -#define BACKEND_H - -#include - -namespace DDM { - class HelperApp; - class Backend : public QObject - { - Q_OBJECT - public: - /** - * Requests allocation of a new backend instance. - * The method chooses the most suitable one for the current system. - */ - static Backend *get(HelperApp *parent); - - void setAutologin(bool on = true); - void setDisplayServer(bool on = true); - void setGreeter(bool on = true); - bool identifyOnly() const; - void setIdentifyOnly(bool on); - - inline bool isGreeter() const { return m_greeter; } - inline int sessionId() const { return m_sessionId; } - - public slots: - virtual bool start(const QString &user = QString()) = 0; - virtual bool authenticate() = 0; - virtual bool openSession(); - virtual bool closeSession(); - - virtual QString userName() = 0; - - protected: - Backend(HelperApp *parent); - HelperApp *m_app; - bool m_autologin { false }; - bool m_displayServer = false; - bool m_greeter { false }; - bool m_identifyOnly { false }; - int m_sessionId; - }; -} - -#endif // BACKEND_H diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt index 27c16a9..d877f7d 100644 --- a/src/helper/CMakeLists.txt +++ b/src/helper/CMakeLists.txt @@ -5,11 +5,9 @@ include_directories( ) set(HELPER_SOURCES - Backend.cpp HelperApp.cpp UserSession.cpp - backend/PamHandle.cpp - backend/PamBackend.cpp + Pam.cpp ) add_executable(ddm-helper ${HELPER_SOURCES}) diff --git a/src/helper/HelperApp.cpp b/src/helper/HelperApp.cpp index dd2ba8d..1fe0f5f 100644 --- a/src/helper/HelperApp.cpp +++ b/src/helper/HelperApp.cpp @@ -19,7 +19,7 @@ */ #include "HelperApp.h" -#include "Backend.h" +#include "Pam.h" #include "Configuration.h" #include "UserSession.h" #include "SafeDataStream.h" @@ -49,9 +49,13 @@ #include namespace DDM { + static Request passwordRequest{ + { { AuthPrompt::LOGIN_PASSWORD, QStringLiteral("Password: "), true } } + }; + HelperApp::HelperApp(int& argc, char** argv) : QCoreApplication(argc, argv) - , m_backend(Backend::get(this)) + , m_pam(new Pam(this)) , m_session(new UserSession(this)) , m_socket(new QLocalSocket(this)) { qInstallMessageHandler(HelperMessageHandler); @@ -99,6 +103,7 @@ namespace DDM { return; } m_user = args[pos + 1]; + m_pam->user = m_user; } if ((pos = args.indexOf(QStringLiteral("--display-server"))) >= 0) { @@ -108,19 +113,10 @@ namespace DDM { return; } m_session->setDisplayServerCommand(args[pos + 1]); - m_backend->setDisplayServer(true); - } - - if ((pos = args.indexOf(QStringLiteral("--autologin"))) >= 0) { - m_backend->setAutologin(true); - } - - if ((pos = args.indexOf(QStringLiteral("--greeter"))) >= 0) { - m_backend->setGreeter(true); } if ((pos = args.indexOf(QStringLiteral("--identify-only"))) >= 0) { - m_backend->setIdentifyOnly(true); + m_identifyOnly = true; } if ((pos = args.indexOf(QStringLiteral("--skip-auth"))) >= 0) { @@ -134,7 +130,7 @@ namespace DDM { } connect(m_socket, &QLocalSocket::connected, this, &HelperApp::doAuth); - if(!m_backend->identifyOnly()){ + if(!m_identifyOnly){ connect(m_session, &UserSession::finished, this, &HelperApp::sessionFinished); } @@ -153,7 +149,7 @@ namespace DDM { if (str.status() != QDataStream::Ok) qCritical() << "Couldn't write initial message:" << str.status(); - if (!m_backend->start(m_user)) { + if (!m_pam->start()) { authenticated(QString()); // write failed login to btmp @@ -167,20 +163,22 @@ namespace DDM { } Q_ASSERT(getuid() == 0); - if (!m_skipAuth && !m_backend->authenticate()) { - 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; + 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; + } } - m_user = m_backend->userName(); QProcessEnvironment env = authenticated(m_user); if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { @@ -200,16 +198,30 @@ namespace DDM { env.insert(m_session->processEnvironment()); m_session->setProcessEnvironment(env); - if (!m_backend->openSession()) { + auto sessionEnv = m_pam->openSession(env); + if (!sessionEnv.has_value()) { sessionOpened(false, 0); exit(Auth::HELPER_SESSION_ERROR); return; } - sessionOpened(true, m_backend->sessionId()); + 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 QProcessEnvironment env = m_session->processEnvironment(); const QString displayId = env.value(QStringLiteral("DISPLAY")); const QString vt = env.value(QStringLiteral("XDG_VTNR")); if (env.value(QStringLiteral("XDG_SESSION_CLASS")) != QLatin1String("greeter")) { @@ -323,8 +335,8 @@ namespace DDM { Q_ASSERT(getuid() == 0); m_session->stop(); - if(!m_backend->identifyOnly()){ - m_backend->closeSession(); + if(!m_identifyOnly && m_pam->sessionOpened){ + m_pam->closeSession(); } // write logout to utmp/wtmp diff --git a/src/helper/HelperApp.h b/src/helper/HelperApp.h index 9efdc9c..e88ab81 100644 --- a/src/helper/HelperApp.h +++ b/src/helper/HelperApp.h @@ -29,7 +29,7 @@ class QLocalSocket; namespace DDM { - class Backend; + class Pam; class UserSession; class HelperApp : public QCoreApplication { @@ -61,13 +61,14 @@ namespace DDM { private: qint64 m_id { -1 }; - Backend *m_backend { nullptr }; + 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 diff --git a/src/helper/Pam.cpp b/src/helper/Pam.cpp new file mode 100644 index 0000000..4c8bf0b --- /dev/null +++ b/src/helper/Pam.cpp @@ -0,0 +1,202 @@ +// Copyright (C) 2025 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "Pam.h" + +#include "VirtualTerminal.h" + +#include + +#include + +namespace DDM { + /** PAM conversation function */ + static int converse(int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + void *secret_ptr) { + *resp = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response)); + if (!*resp) + return PAM_BUF_ERR; + + // We only handle secret (password) sending here, which is + // prompt by PAM_PROMPT_ECHO_OFF. Message types (error/info) + // are just logged. + // + // Prompts with PAM_PROMPT_ECHO_ON (most likely asking for + // username) are not expected, since we required username is + // set before. + for (int i = 0; i < num_msg; ++i) { + switch (msg[i]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + resp[i]->resp = strdup(*static_cast(secret_ptr)); + resp[i]->resp_retcode = 0; + break; + case PAM_ERROR_MSG: + qWarning() << "[PAM] Error message:" << msg[i]->msg; + resp[i]->resp = nullptr; + resp[i]->resp_retcode = 0; + break; + case PAM_TEXT_INFO: + qInfo() << "[PAM] Info message:" << msg[i]->msg; + resp[i]->resp = nullptr; + resp[i]->resp_retcode = 0; + break; + default: + qCritical("[PAM] Unsupported message style %d: %s", msg[i]->msg_style, msg[i]->msg); + for (int j = 0; j < i; j++) { + free(resp[j]->resp); + resp[j]->resp = nullptr; + } + free(*resp); + *resp = nullptr; + return PAM_CONV_ERR; + } + } + return PAM_SUCCESS; + } + + class PamPrivate : public QObject { + Q_OBJECT + public: + PamPrivate(Pam *parent) + : QObject(parent) + , secretPtr(new const char *) + , conv({ converse, static_cast(secretPtr) }) { + *secretPtr = nullptr; + } + + ~PamPrivate() { + delete secretPtr; + } + + pam_handle_t *handle{ nullptr }; + const char **secretPtr{}; + pam_conv conv{}; + int ret{}; + }; + + Pam::Pam(QObject *parent, QString user) + : QObject(parent) + , user(user) + , d(new PamPrivate(this)) { } + + Pam::~Pam() { + if (!d->handle) + return; + if (sessionOpened) + closeSession(); + d->ret = pam_end(d->handle, d->ret); + if (d->ret != PAM_SUCCESS) + qWarning() << "[PAM] end:" << pam_strerror(d->handle, d->ret); + else + qDebug() << "[PAM] Ended."; + } + + bool Pam::start() { + d->ret = pam_start("ddm", user.toLocal8Bit().constData(), &d->conv, &d->handle); + if (d->ret != PAM_SUCCESS) { + qWarning() << "[PAM] start" << pam_strerror(d->handle, d->ret); + return false; + } + qDebug() << "[PAM] Starting..."; + return true; + } + + bool Pam::authenticate(const QByteArray &secret) { + qDebug() << "[PAM] Authenticating user" << user; + + // Set the secret, authenticate, then clear the secret + // immediately to avoid leak + *d->secretPtr = secret.constData(); + d->ret = pam_authenticate(d->handle, 0); + *d->secretPtr = nullptr; + + if (d->ret != PAM_SUCCESS) { + qWarning() << "[PAM] authenticate:" << pam_strerror(d->handle, d->ret); + return false; + } + qDebug() << "[PAM] Authenticated."; + + d->ret = pam_acct_mgmt(d->handle, 0); + if (d->ret != PAM_SUCCESS) { + qWarning() << "[PAM] acct_mgmt:" << pam_strerror(d->handle, d->ret); + return false; + } + return true; + } + +#define CHECK_RET_OPEN \ + if (d->ret != PAM_SUCCESS) { \ + qWarning() << "[PAM] openSession:" << pam_strerror(d->handle, d->ret); \ + return std::nullopt; \ + } + std::optional Pam::openSession(QProcessEnvironment sessionEnv) { + qDebug() << "[PAM] Opening session for user" << user; + + d->ret = pam_setcred(d->handle, PAM_ESTABLISH_CRED); + CHECK_RET_OPEN + + // Set PAM_TTY + QString tty = VirtualTerminal::path(sessionEnv.value(QStringLiteral("XDG_VTNR")).toInt()); + d->ret = pam_set_item(d->handle, PAM_TTY, qPrintable(tty)); + CHECK_RET_OPEN + + // Set PAM_XDISPLAY + QString display = sessionEnv.value(QStringLiteral("DISPLAY")); + if (!display.isEmpty()) { + d->ret = pam_set_item(d->handle, PAM_XDISPLAY, qPrintable(display)); + CHECK_RET_OPEN + } + + // Insert environments into new session + for (const QString &s : sessionEnv.toStringList()) { + d->ret = pam_putenv(d->handle, qPrintable(s)); + CHECK_RET_OPEN + } + + // OPEN!!! + d->ret = pam_open_session(d->handle, 0); + CHECK_RET_OPEN + + qDebug() << "[PAM] Session opened."; + sessionOpened = true; + + // Retrieve env vars in new session, which contain XDG_SESSION_ID we need. + QProcessEnvironment env; + char **envlist = pam_getenvlist(d->handle); + if (envlist) { + for (int i = 0; envlist[i] != nullptr; ++i) { + QString str = QString::fromLocal8Bit(envlist[i]); + int equalPos = str.indexOf('='); + if (equalPos != -1) + env.insert(str.left(equalPos), str.mid(equalPos + 1)); + free(envlist[i]); + } + free(envlist); + } + return env; + } + +#define CHECK_RET_CLOSE \ + if (d->ret != PAM_SUCCESS) { \ + qWarning() << "[PAM] closeSession:" << pam_strerror(d->handle, d->ret); \ + return false; \ + } + bool Pam::closeSession() { + qDebug() << "[PAM] Closing session for user" << user; + + d->ret = pam_close_session(d->handle, 0); + CHECK_RET_CLOSE + + sessionOpened = false; + d->ret = pam_setcred(d->handle, PAM_DELETE_CRED); + CHECK_RET_CLOSE + + qDebug() << "[PAM] Session closed."; + return true; + } + +} // namespace DDM + +#include "Pam.moc" diff --git a/src/helper/Pam.h b/src/helper/Pam.h new file mode 100644 index 0000000..4b35792 --- /dev/null +++ b/src/helper/Pam.h @@ -0,0 +1,61 @@ +// Copyright (C) 2025 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef Pam_H +#define Pam_H + +#include +#include + +#include + +namespace DDM { + class PamPrivate; + + /** + * PAM authenticate module. + * + * The `user' property must be set before calling start(). + * To validate user's secret, call start() then authenticate(). + * To open a session, call openSession() after authenticate(). + * Existing opened session will be closed automatically on destruction, + * but you can also close it manually with closeSession(). + */ + class Pam : public QObject { + Q_OBJECT + public: + Pam(QObject *parent = nullptr, QString user = QString()); + ~Pam(); + + /** + * Start PAM transaction + * @return true on success, false on failure + */ + bool start(); + /** + * Authenticate user with given secret (e.g. password) + * @return true on success, false on failure + */ + bool authenticate(const QByteArray &secret); + /** + * Open PAM session with given environment variables + * @return Environment variables set by PAM modules on success, std::nullopt on failure + */ + std::optional openSession(QProcessEnvironment sessionEnv); + /** + * Close PAM session + * @return true on success, false on failure + */ + bool closeSession(); + + /** Username. Must be set before calling start() */ + QString user{}; + /** A boolean value indicating whether a session is opened with this PAM handle */ + bool sessionOpened{ false }; + + private: + PamPrivate *d{ nullptr }; + }; +} // namespace DDM + +#endif // Pam_H diff --git a/src/helper/backend/PamBackend.cpp b/src/helper/backend/PamBackend.cpp deleted file mode 100644 index b1ef37b..0000000 --- a/src/helper/backend/PamBackend.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/* - * PAM authentication backend - * 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 "PamBackend.h" -#include "PamHandle.h" -#include "HelperApp.h" -#include "UserSession.h" -#include "Auth.h" -#include "VirtualTerminal.h" - -#include -#include -#include - -#include - -namespace DDM { - static Request loginRequest { - { { AuthPrompt::LOGIN_USER, QStringLiteral("login:"), false }, - { AuthPrompt::LOGIN_PASSWORD, QStringLiteral("Password: "), true } - } - }; - - static Request changePassRequest { - { { AuthPrompt::CHANGE_CURRENT, QStringLiteral("(current) UNIX password: "), true }, - { AuthPrompt::CHANGE_NEW, QStringLiteral("New password: "), true }, - { AuthPrompt::CHANGE_REPEAT, QStringLiteral("Retype new password: "), true } - } - }; - - static Request changePassNoOldRequest { - { { AuthPrompt::CHANGE_NEW, QStringLiteral("New password: "), true }, - { AuthPrompt::CHANGE_REPEAT, QStringLiteral("Retype new password: "), true } - } - }; - - static Request invalidRequest { {} }; - - static Prompt invalidPrompt {}; - - PamData::PamData() { } - - AuthPrompt::Type PamData::detectPrompt(const struct pam_message* msg) const { - if (msg->msg_style == PAM_PROMPT_ECHO_OFF) { - QString message = QString::fromLocal8Bit(msg->msg); - if ((QRegularExpression(QStringLiteral("\\bpassword\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { - if ((QRegularExpression(QStringLiteral("\\b(re-?(enter|type)|again|confirm|repeat)\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { - return AuthPrompt::CHANGE_REPEAT; - } - else if ((QRegularExpression(QStringLiteral("\\bnew\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { - return AuthPrompt::CHANGE_NEW; - } - else if ((QRegularExpression(QStringLiteral("\\b(old|current)\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { - return AuthPrompt::CHANGE_CURRENT; - } - else { - return AuthPrompt::LOGIN_PASSWORD; - } - } - } - else { - return AuthPrompt::LOGIN_USER; - } - - return AuthPrompt::UNKNOWN; - } - - const Prompt& PamData::findPrompt(const struct pam_message* msg) const { - AuthPrompt::Type type = detectPrompt(msg); - - for (const Prompt &p : m_currentRequest.prompts) { - if (type == p.type && p.message == QString::fromLocal8Bit(msg->msg)) - return p; - } - - return invalidPrompt; - } - - Prompt& PamData::findPrompt(const struct pam_message* msg) { - AuthPrompt::Type type = detectPrompt(msg); - - for (Prompt &p : m_currentRequest.prompts) { - if (type == AuthPrompt::UNKNOWN && QString::fromLocal8Bit(msg->msg) == p.message) - return p; - if (type == p.type) - return p; - } - - return invalidPrompt; - } - - /* - * Expects an empty prompt list if the previous request has been processed - */ - bool PamData::insertPrompt(const struct pam_message* msg, bool predict) { - Prompt &p = findPrompt(msg); - - // first, check if we already have stored this propmpt - if (p.valid()) { - // we have a response already - do nothing - if (m_sent) - return false; - // we don't have a response yet - replace the message and prepare to send it - p.message = QString::fromLocal8Bit(msg->msg); - return true; - } - // this prompt is not stored but we have some prompts - else if (m_currentRequest.prompts.length() != 0) { - // check if we have already sent this - if we did, get rid of the answers - if (m_sent) { - m_currentRequest.clear(); - m_sent = false; - } - } - - // we'll predict what will come next - if (predict) { - AuthPrompt::Type type = detectPrompt(msg); - switch (type) { - case AuthPrompt::LOGIN_USER: - m_currentRequest = Request(loginRequest); - return true; - case AuthPrompt::CHANGE_CURRENT: - m_currentRequest = Request(changePassRequest); - return true; - case AuthPrompt::CHANGE_NEW: - m_currentRequest = Request(changePassNoOldRequest); - return true; - default: - break; - } - } - - // or just add whatever comes exactly as it comes - m_currentRequest.prompts.append(Prompt(detectPrompt(msg), QString::fromLocal8Bit(msg->msg), msg->msg_style == PAM_PROMPT_ECHO_OFF)); - - return true; - } - - Auth::Info PamData::handleInfo(const struct pam_message* msg, bool predict) { - if ((QRegularExpression(QStringLiteral("^Changing password for [^ ]+$"))).match(QString::fromLocal8Bit(msg->msg)).hasMatch()) { - if (predict) - m_currentRequest = Request(changePassRequest); - return Auth::INFO_PASS_CHANGE_REQUIRED; - } - return Auth::INFO_UNKNOWN; - } - - /* - * Destroys the prompt with that response - */ - QByteArray PamData::getResponse(const struct pam_message* msg) { - QByteArray response = findPrompt(msg).response; - m_currentRequest.prompts.removeOne(findPrompt(msg)); - if (m_currentRequest.prompts.length() == 0) - m_sent = false; - return response; - } - - const Request& PamData::getRequest() const { - if (!m_sent) - return m_currentRequest; - else - return invalidRequest; - } - - void PamData::completeRequest(const Request& request) { - if (request.prompts.length() != m_currentRequest.prompts.length()) { - qWarning() << "[PAM] Different request/response list length, ignoring"; - return; - } - - for (int i = 0; i < request.prompts.length(); i++) { - if (request.prompts[i].type != m_currentRequest.prompts[i].type - || request.prompts[i].message != m_currentRequest.prompts[i].message - || request.prompts[i].hidden != m_currentRequest.prompts[i].hidden) { - qWarning() << "[PAM] Order or type of the messages doesn't match, ignoring"; - return; - } - } - - m_currentRequest = request; - m_sent = true; - } - - - - - PamBackend::PamBackend(HelperApp *parent) - : Backend(parent) - , m_data(new PamData()) - , m_pam(new PamHandle(this)) { - } - - PamBackend::~PamBackend() { - delete m_data; - delete m_pam; - } - - bool PamBackend::start(const QString &user) { - bool result; - - QString service = QStringLiteral("ddm"); - - if (user == QStringLiteral("dde") && m_greeter) - service = QStringLiteral("ddm-greeter"); - else if (m_autologin) - service = QStringLiteral("ddm-autologin"); - result = m_pam->start(service, user); - - if (!result) - m_app->error(m_pam->errorString(), Auth::ERROR_INTERNAL); - - return result; - } - - bool PamBackend::authenticate() { - if (!m_pam->authenticate()) { - m_app->error(m_pam->errorString(), Auth::ERROR_AUTHENTICATION); - return false; - } - if (!m_pam->acctMgmt()) { - m_app->error(m_pam->errorString(), Auth::ERROR_AUTHENTICATION); - return false; - } - return true; - } - - bool PamBackend::openSession() { - if (!m_pam->setCred(PAM_ESTABLISH_CRED)) { - m_app->error(m_pam->errorString(), Auth::ERROR_AUTHENTICATION); - return false; - } - - QProcessEnvironment sessionEnv = m_app->session()->processEnvironment(); - const auto sessionType = sessionEnv.value(QStringLiteral("XDG_SESSION_TYPE")); - const auto sessionClass = sessionEnv.value(QStringLiteral("XDG_SESSION_CLASS")); - QString tty = VirtualTerminal::path(sessionEnv.value(QStringLiteral("XDG_VTNR")).toInt()); - m_pam->setItem(PAM_TTY, qPrintable(tty)); - if (sessionType == QLatin1String("x11") && (sessionClass == QLatin1String("user") || !m_displayServer)) { - QString display = sessionEnv.value(QStringLiteral("DISPLAY")); - if (!display.isEmpty()) { -#ifdef PAM_XDISPLAY - m_pam->setItem(PAM_XDISPLAY, qPrintable(display)); -#else - m_pam->setItem(PAM_TTY, qPrintable(display)); -#endif - } - } - - if (!m_pam->putEnv(sessionEnv)) { - m_app->error(m_pam->errorString(), Auth::ERROR_INTERNAL); - return false; - } - if (!m_pam->openSession()) { - m_app->error(m_pam->errorString(), Auth::ERROR_INTERNAL); - return false; - } - sessionEnv.insert(m_pam->getEnv()); - m_app->session()->setProcessEnvironment(sessionEnv); - m_sessionId = sessionEnv.value(QStringLiteral("XDG_SESSION_ID")).toInt(); - return Backend::openSession(); - } - - bool PamBackend::closeSession() { - if (m_pam->isOpen()) { - qDebug() << "[PAM] Closing session"; - m_pam->closeSession(); - m_pam->setCred(PAM_DELETE_CRED); - return true; - } - qWarning() << "[PAM] Asked to close the session but it wasn't previously open"; - return Backend::closeSession(); - } - - QString PamBackend::userName() { - return QString::fromLocal8Bit((const char*) m_pam->getItem(PAM_USER)); - } - - int PamBackend::converse(int n, const struct pam_message **msg, struct pam_response **resp) { - qDebug() << "[PAM] Conversation with" << n << "messages"; - - bool newRequest = false; - - if (n <= 0 || n > PAM_MAX_NUM_MSG) - return PAM_CONV_ERR; - - for (int i = 0; i < n; i++) { - switch(msg[i]->msg_style) { - case PAM_PROMPT_ECHO_OFF: - case PAM_PROMPT_ECHO_ON: - newRequest = m_data->insertPrompt(msg[i], n == 1); - break; - case PAM_ERROR_MSG: - m_app->error(QString::fromLocal8Bit(msg[i]->msg), Auth::ERROR_AUTHENTICATION); - break; - case PAM_TEXT_INFO: - // if there's only the info message, let's predict the prompts too - m_app->info(QString::fromLocal8Bit(msg[i]->msg), m_data->handleInfo(msg[i], n == 1)); - break; - default: - break; - } - } - - if (newRequest) { - Request sent = m_data->getRequest(); - Request received; - - if (sent.valid()) { - received = m_app->request(sent); - - if (!received.valid()) - return PAM_CONV_ERR; - - m_data->completeRequest(received); - } - } - - *resp = (struct pam_response *) calloc(n, sizeof(struct pam_response)); - if (!*resp) { - return PAM_BUF_ERR; - } - - for (int i = 0; i < n; i++) { - QByteArray response = m_data->getResponse(msg[i]); - - resp[i]->resp = (char *) malloc(response.length() + 1); - // on error, get rid of everything - if (!resp[i]->resp) { - for (int j = 0; j < i; j++) { - free(resp[j]->resp); - resp[j]->resp = nullptr; - } - free(*resp); - *resp = nullptr; - return PAM_BUF_ERR; - } - - memcpy(resp[i]->resp, response.constData(), response.length()); - resp[i]->resp[response.length()] = '\0'; - } - - return PAM_SUCCESS; - } -} diff --git a/src/helper/backend/PamBackend.h b/src/helper/backend/PamBackend.h deleted file mode 100644 index f85f26e..0000000 --- a/src/helper/backend/PamBackend.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * PAM authentication backend - * 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. - * - */ - -#if !defined(PAMBACKEND_H) -#define PAMBACKEND_H - -#include "Constants.h" -#include "AuthMessages.h" -#include "../Backend.h" - -#include - -#include - -namespace DDM { - class PamHandle; - class PamBackend; - class PamData { - public: - PamData(); - - bool insertPrompt(const struct pam_message *msg, bool predict = true); - Auth::Info handleInfo(const struct pam_message *msg, bool predict); - - const Request& getRequest() const; - void completeRequest(const Request& request); - - QByteArray getResponse(const struct pam_message *msg); - - private: - AuthPrompt::Type detectPrompt(const struct pam_message *msg) const; - - const Prompt& findPrompt(const struct pam_message *msg) const; - Prompt& findPrompt(const struct pam_message *msg); - - bool m_sent { false }; - Request m_currentRequest { }; - }; - - class PamBackend : public Backend - { - Q_OBJECT - public: - explicit PamBackend(HelperApp *parent); - virtual ~PamBackend(); - int converse(int n, const struct pam_message **msg, struct pam_response **resp); - - public slots: - virtual bool start(const QString &user = QString()); - virtual bool authenticate(); - virtual bool openSession(); - virtual bool closeSession(); - - virtual QString userName(); - - private: - PamData *m_data { nullptr }; - PamHandle *m_pam { nullptr }; - }; -} - -#endif // PAMBACKEND_H diff --git a/src/helper/backend/PamHandle.cpp b/src/helper/backend/PamHandle.cpp deleted file mode 100644 index c0f37d6..0000000 --- a/src/helper/backend/PamHandle.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/* - * PAM API Qt wrapper - * 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 "PamHandle.h" -#include "PamBackend.h" - -#include - -namespace DDM { - bool PamHandle::putEnv(const QProcessEnvironment& env) { - const auto envs = env.toStringList(); - for (const QString& s : envs) { - m_result = pam_putenv(m_handle, qPrintable(s)); - if (m_result != PAM_SUCCESS) { - qWarning() << "[PAM] putEnv:" << pam_strerror(m_handle, m_result); - return false; - } - } - return true; - } - - QProcessEnvironment PamHandle::getEnv() { - QProcessEnvironment env; - // get pam environment - char **envlist = pam_getenvlist(m_handle); - if (envlist == NULL) { - qWarning() << "[PAM] getEnv: Returned NULL"; - return env; - } - - // copy it to the env map - for (int i = 0; envlist[i] != nullptr; ++i) { - QString s = QString::fromLocal8Bit(envlist[i]); - - // find equal sign - int index = s.indexOf(QLatin1Char('=')); - - // add to the hash - if (index != -1) - env.insert(s.left(index), s.mid(index + 1)); - - free(envlist[i]); - } - free(envlist); - return env; - } - - bool PamHandle::chAuthTok(int flags) { - m_result = pam_chauthtok(m_handle, flags | m_silent); - if (m_result != PAM_SUCCESS) { - qWarning() << "[PAM] chAuthTok:" << pam_strerror(m_handle, m_result); - } - return m_result == PAM_SUCCESS; - } - - bool PamHandle::acctMgmt(int flags) { - m_result = pam_acct_mgmt(m_handle, flags | m_silent); - if (m_result == PAM_NEW_AUTHTOK_REQD) { - // TODO see if this should really return the value or just true regardless of the outcome - return chAuthTok(PAM_CHANGE_EXPIRED_AUTHTOK); - } - else if (m_result != PAM_SUCCESS) { - qWarning() << "[PAM] acctMgmt:" << pam_strerror(m_handle, m_result); - return false; - } - return true; - } - - bool PamHandle::authenticate(int flags) { - qDebug() << "[PAM] Authenticating..."; - m_result = pam_authenticate(m_handle, flags | m_silent); - if (m_result != PAM_SUCCESS) { - qWarning() << "[PAM] authenticate:" << pam_strerror(m_handle, m_result); - } - qDebug() << "[PAM] returning."; - return m_result == PAM_SUCCESS; - } - - bool PamHandle::setCred(int flags) { - m_result = pam_setcred(m_handle, flags | m_silent); - if (m_result != PAM_SUCCESS) { - qWarning() << "[PAM] setCred:" << pam_strerror(m_handle, m_result); - } - return m_result == PAM_SUCCESS; - } - - bool PamHandle::openSession() { - m_result = pam_open_session(m_handle, m_silent); - if (m_result != PAM_SUCCESS) { - qWarning() << "[PAM] openSession:" << pam_strerror(m_handle, m_result); - } - m_open = m_result == PAM_SUCCESS; - return m_open; - } - - bool PamHandle::closeSession() { - m_result = pam_close_session(m_handle, m_silent); - if (m_result != PAM_SUCCESS) { - qWarning() << "[PAM] closeSession:" << pam_strerror(m_handle, m_result); - } - return m_result == PAM_SUCCESS; - } - - bool PamHandle::isOpen() const { - return m_open; - } - - bool PamHandle::setItem(int item_type, const void* item) { - m_result = pam_set_item(m_handle, item_type, item); - if (m_result != PAM_SUCCESS) { - qWarning() << "[PAM] setItem:" << pam_strerror(m_handle, m_result); - } - return m_result == PAM_SUCCESS; - } - - const void* PamHandle::getItem(int item_type) { - const void *item; - m_result = pam_get_item(m_handle, item_type, &item); - if (m_result != PAM_SUCCESS) { - qWarning() << "[PAM] getItem:" << pam_strerror(m_handle, m_result); - } - return item; - } - - int PamHandle::converse(int n, const struct pam_message **msg, struct pam_response **resp, void *data) { - qDebug() << "[PAM] Preparing to converse..."; - PamBackend *c = static_cast(data); - return c->converse(n, msg, resp); - } - - bool PamHandle::start(const QString &service, const QString &user) { - if (user.isEmpty()) - m_result = pam_start(qPrintable(service), NULL, &m_conv, &m_handle); - else - m_result = pam_start(qPrintable(service), qPrintable(user), &m_conv, &m_handle); - if (m_result != PAM_SUCCESS) { - qWarning() << "[PAM] start" << pam_strerror(m_handle, m_result); - return false; - } - else { - qDebug() << "[PAM] Starting..."; - } - return true; - } - - bool PamHandle::end(int flags) { - if (!m_handle) - return false; - m_result = pam_end(m_handle, m_result | flags); - if (m_result != PAM_SUCCESS) { - qWarning() << "[PAM] end:" << pam_strerror(m_handle, m_result); - return false; - } - else { - qDebug() << "[PAM] Ended."; - } - m_handle = NULL; - return true; - } - - QString PamHandle::errorString() { - return QString::fromLocal8Bit(pam_strerror(m_handle, m_result)); - } - - PamHandle::PamHandle(PamBackend *parent) : m_conv{&PamHandle::converse, parent} { // create context - } - - PamHandle::~PamHandle() { - // stop service - end(); - } -} diff --git a/src/helper/backend/PamHandle.h b/src/helper/backend/PamHandle.h deleted file mode 100644 index c86eea3..0000000 --- a/src/helper/backend/PamHandle.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * PAM API Qt wrapper - * 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 PAMHANDLE_H -#define PAMHANDLE_H - -#include -#include -#include - -namespace DDM { - class PamBackend; - /** - * Class wrapping the standard Linux-PAM library calls - * - * Almost everything is left the same except the following things: - * - * Mainly, state returns - if you call pam_start, pam_open_session and then - * pam_start again, the session will get closed and the conversation closed - * - * You don't need to pass PAM_SILENT to every call if you want PAM to be quiet. - * You can set the flag globally by using the \ref setSilence method. - * - * \ref acctMgmt doesn't require you to handle the PAM_NEW_AUTHTOK_REQD condition, - * it calls chAuthTok on its own. - * - * Error messages are automatically reported to qDebug - */ - class PamHandle { - public: - /** - * ctor - * \param parent parent backend - */ - explicit PamHandle(PamBackend *parent = 0); - - virtual ~PamHandle(); - - /** - * Returns whether the session is open. - * \sa openSession - */ - bool isOpen() const; - - /** - * pam_set_item - set and update PAM informations - * - * \param item_type PAM item type - * \param item item pointer - * - * \return true on success - */ - bool setItem(int item_type, const void *item); - - /** - * pam_get_item - getting PAM informations - * - * \param item_type - * - * \return item pointer or NULL on failure - */ - const void *getItem(int item_type); - - /** - * pam_open_session - start PAM session management - * - * \return true on success - */ - bool openSession(); - - /** - * pam_close_session - terminate PAM session management - * - * \return true on success - */ - bool closeSession(); - - /** - * pam_setcred - establish / delete user credentials - * - * \param flags PAM flag(s) - * - * \return true on success - */ - bool setCred(int flags = 0); - - /** - * pam_authenticate - account authentication - * - * \param flags PAM flag(s) - * - * \return true on success - */ - bool authenticate(int flags = 0); - - /** - * pam_acct_mgmt - PAM account validation management - * - * @note Automatically calls \ref chAuthTok if the password is expired - * - * \param flags PAM flag(s) - * - * \return true on success - */ - bool acctMgmt(int flags = 0); - - /** - * pam_chauthtok - updating authentication tokens - * - * \param flags PAM flag(s) - * - * \return true on success - */ - bool chAuthTok(int flags = 0); - - /** - * pam_getenv - get PAM environment - * - * \return Complete process environment - */ - QProcessEnvironment getEnv(); - - /** - * pam_putenv - set or change PAM environment - * - * \param env environment to be merged into the PAM one - * - * \return true on success - */ - bool putEnv(const QProcessEnvironment& env); - - /** - * pam_end - termination of PAM transaction - * - * \param flags to be OR'd with the status (PAM_DATA_SILENT) - * \return true on success - */ - bool end(int flags = 0); - - /** - * pam_start - initialization of PAM transaction - * - * \param service PAM service name, e.g. "ddm" - * \param pam_conversation pointer to the PAM conversation structure to be used - * \param user username - * - * \return true on success - */ - bool start(const QString &service, const QString &user = QString()); - - /** - * Set PAM_SILENT upon the contained calls - * \param silent true if silent - */ - void setSilence(bool silent); - - /** - * Generates an error message according to the internal state - * - * \return error string - */ - QString errorString(); - - private: - /** - * Conversation function for the pam_conv structure - * - * Calls ((PamHandle*)pam_conv.appdata_ptr)->doConverse() with its parameters - * - * Not to be called directly, therefore private - */ - static int converse(int n, const struct pam_message **msg, struct pam_response **resp, void *data); - - int m_silent { 0 }; ///< flag mask for silence of the contained calls - - struct pam_conv m_conv; ///< the current conversation - pam_handle_t *m_handle { nullptr }; ///< the actual PAM handle - int m_result { 0 }; ///< PAM result - bool m_open { false }; ///< whether the session is open - }; -} - -#endif // PAMHANDLE_H