Package: sddm Version: 0.19.0-5 Severity: normal Tags: patch X-Debbugs-Cc: adrela...@kicksecure.com, arraybo...@gmail.com
NOTICE: This bug only affects Bookworm to my awareness. This bug is present in SDDM 0.19.0 upstream, and does not exist in SDDM 0.20.0 upstream from my testing, and both Trixie and Sid use SDDM 0.21.0. SDDM supports reading several kinds of messages from PAM, including informational messages intended for display to the end-user. These messages can be generated if, for instance, a line is present in /etc/pam.d/common-auth such as: auth optional pam_exec.so debug stdout seteuid /usr/bin/script-that-writes-to-stdout While SDDM currently lacks the ability to actually display these messages, it's designed to read them and log them, and their presence should not block login. Unfortunately, these informational message actually do block login on Debian 12. If any PAM module outputs info to stdout that SDDM considers "informational", it may result in SDDM hanging forever while trying to log in. To reproduce: * Install Debian 12 with a desktop environment that comes with SDDM. KDE Plasma and LXQt both work here. * Log in. * Create a script at /usr/bin/messup.sh, with the following contents: #!/bin/bash echo 'Mess1' echo 'Mess2' echo 'Mess3' echo 'Mess4' echo 'Mess5' echo 'Mess6' echo 'Mess7' * Set /usr/bin/messup.sh executable. * In a new terminal, run `sudo -i` and ensure you see seven lines of text, 'Mess1' through 'Mess7' displayed. * Add a line to /etc/pam.d/common-auth as follows: auth optional pam_exec.so debug stdout seteuid /usr/bin/messup.sh * Log out. * Attempt to log back in. SDDM will hang. * To recover, switch to a TTY, log in, run `sudo vi /usr/bin/messup.sh`, and comment out all seven 'Mess' lines. Save the file, then run `sudo systemctl restart sddm`. I tested SDDM upstream and discovered that 0.19.0 had this bug, but 0.20.0 did not. I then bisected between them, and found that the first good commit was 6a9d0875, "Port to Qt6 QRegularExpression". This significantly changed how several regular expressions in SDDM were parsed. I then applied the changes made in this commit to Debian 12's SDDM package (adjusting the patch as necessary for it to work with Debian's SDDM), tested it by repeating the steps above, and found that the issue persisted. After checking the systemd journal for anything about SDDM, I discovered that SDDM was failing to read all of the output from my messup.sh script. This bug was also already fixed in SDDM upstream, in commit 48a9ec7b, "Process all available auth messages in a loop". This commit also required some slight changes to make it apply to Debian's SDDM. After applying this patch as well, I ran the above tests again, and the issue was resolved. The final patch I used is as follows. Some notes: * I have no idea why changing the way regex matches are done fixes the problem. I've looked through all of the regexes in this patch and they all look exactly the same in both the old and new versions. * The changes to the src/auth/Auth.cpp file's Auth::Private::dataPending function (seen just below the line `SafeDataStream str(socket);` below) may look alarming, but it's only because of `diff -u` being weird. The only change I backported from upstream in this area was to wrap a large block of code in a `while (socket->bytesAvailable() > 0) { ... }` block, and fix indentation as appropriate. Apparently the extra intendation made diff throw a tantrum. ----- diff -r -u sddm-0.19.0.bak/src/auth/Auth.cpp sddm-0.19.0/src/auth/Auth.cpp --- sddm-0.19.0.bak/src/auth/Auth.cpp 2024-12-03 20:49:22.259092853 -0600 +++ sddm-0.19.0/src/auth/Auth.cpp 2024-12-03 21:01:03.363190219 -0600 @@ -24,6 +24,7 @@ #include "SafeDataStream.h" #include <QtCore/QProcess> +#include <QtCore/QRegularExpression> #include <QtCore/QUuid> #include <QtNetwork/QLocalServer> #include <QtNetwork/QLocalSocket> @@ -101,7 +102,7 @@ static std::unique_ptr<Auth::SocketServer> self; if (!self) { self.reset(new SocketServer()); - self->listen(QStringLiteral("sddm-auth%1").arg(QUuid::createUuid().toString().replace(QRegExp(QStringLiteral("[{}]")), QString()))); + self->listen(QStringLiteral("sddm-auth%1").arg(QUuid::createUuid().toString().replace(QRegularExpression(QStringLiteral("[{}]")), QString()))); } return self.get(); } @@ -152,55 +153,57 @@ Auth *auth = qobject_cast<Auth*>(parent()); Msg m = MSG_UNKNOWN; SafeDataStream str(socket); - 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); + 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); + str.reset(); + str << AUTHENTICATED << environment << cookie; + str.send(); + } + else { + Q_EMIT auth->authentication(user, false); + } + break; + } + case SESSION_STATUS: { + bool status; + str >> status; + Q_EMIT auth->sessionStarted(status); str.reset(); - str << AUTHENTICATED << environment << cookie; + str << SESSION_STATUS; str.send(); + break; } - else { - Q_EMIT auth->authentication(user, false); + default: { + Q_EMIT auth->error(QStringLiteral("Auth: Unexpected value received: %1").arg(m), ERROR_INTERNAL); } - break; - } - case SESSION_STATUS: { - bool status; - str >> status; - Q_EMIT auth->sessionStarted(status); - str.reset(); - str << SESSION_STATUS; - str.send(); - break; - } - default: { - Q_EMIT auth->error(QStringLiteral("Auth: Unexpected value received: %1").arg(m), ERROR_INTERNAL); } } } diff -r -u sddm-0.19.0.bak/src/greeter/XcbKeyboardBackend.cpp sddm-0.19.0/src/greeter/XcbKeyboardBackend.cpp --- sddm-0.19.0.bak/src/greeter/XcbKeyboardBackend.cpp 2024-12-03 20:49:22.259092853 -0600 +++ sddm-0.19.0/src/greeter/XcbKeyboardBackend.cpp 2024-12-03 20:49:35.899166662 -0600 @@ -19,6 +19,7 @@ #include <QtCore/QDebug> #include <QtCore/QObject> +#include <QtCore/QRegularExpression> #include "KeyboardModel.h" #include "KeyboardModel_p.h" @@ -267,8 +268,7 @@ } QList<QString> XcbKeyboardBackend::parseShortNames(QString text) { - QRegExp re(QStringLiteral(R"(\+([a-z]+))")); - re.setCaseSensitivity(Qt::CaseInsensitive); + QRegularExpression re(QStringLiteral(R"(\+([a-z]+))"), QRegularExpression::CaseInsensitiveOption); QList<QString> res; QSet<QString> blackList; // blacklist wrong tokens @@ -276,10 +276,11 @@ // Loop through matched substrings int pos = 0; - while ((pos = re.indexIn(text, pos)) != -1) { - if (!blackList.contains(re.cap(1))) - res << re.cap(1); - pos += re.matchedLength(); + QRegularExpressionMatch match; + while ((match = re.match(text, pos)).hasMatch()) { + if (!blackList.contains(match.captured(1))) + res << match.captured(1); + pos += match.capturedLength(); } return res; } diff -r -u sddm-0.19.0.bak/src/helper/backend/PamBackend.cpp sddm-0.19.0/src/helper/backend/PamBackend.cpp --- sddm-0.19.0.bak/src/helper/backend/PamBackend.cpp 2024-12-03 20:49:22.259092853 -0600 +++ sddm-0.19.0/src/helper/backend/PamBackend.cpp 2024-12-03 20:49:35.899166662 -0600 @@ -26,6 +26,7 @@ #include <QtCore/QString> #include <QtCore/QDebug> +#include <QtCore/QRegularExpression> #include <stdlib.h> @@ -58,14 +59,14 @@ 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 (message.indexOf(QRegExp(QStringLiteral("\\bpassword\\b"), Qt::CaseInsensitive)) >= 0) { - if (message.indexOf(QRegExp(QStringLiteral("\\b(re-?(enter|type)|again|confirm|repeat)\\b"), Qt::CaseInsensitive)) >= 0) { + 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 (message.indexOf(QRegExp(QStringLiteral("\\bnew\\b"), Qt::CaseInsensitive)) >= 0) { + else if ((QRegularExpression(QStringLiteral("\\bnew\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { return AuthPrompt::CHANGE_NEW; } - else if (message.indexOf(QRegExp(QStringLiteral("\\b(old|current)\\b"), Qt::CaseInsensitive)) >= 0) { + else if ((QRegularExpression(QStringLiteral("\\b(old|current)\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { return AuthPrompt::CHANGE_CURRENT; } else { @@ -153,7 +154,7 @@ } Auth::Info PamData::handleInfo(const struct pam_message* msg, bool predict) { - if (QString::fromLocal8Bit(msg->msg).indexOf(QRegExp(QStringLiteral("^Changing password for [^ ]+$")))) { + if ((QRegularExpression(QStringLiteral("^Changing password for [^ ]+$"))).match(QString::fromLocal8Bit(msg->msg)).hasMatch()) { if (predict) m_currentRequest = Request(changePassRequest); return Auth::INFO_PASS_CHANGE_REQUIRED; ----- -- System Information: Debian Release: 12.8 APT prefers stable-updates APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable') Architecture: amd64 (x86_64) Kernel: Linux 6.1.0-28-amd64 (SMP w/2 CPU threads; PREEMPT) Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE not set Shell: /bin/sh linked to /usr/bin/dash Init: systemd (via /run/systemd/system) LSM: AppArmor: enabled Versions of packages sddm depends on: ii adduser 3.134 ii debconf [debconf-2.0] 1.5.82 ii libc6 2.36-9+deb12u9 ii libgcc-s1 12.2.0-14 ii libpam0g 1.5.2-6+deb12u1 ii libqt5core5a 5.15.8+dfsg-11+deb12u2 ii libqt5dbus5 5.15.8+dfsg-11+deb12u2 ii libqt5gui5 5.15.8+dfsg-11+deb12u2 ii libqt5network5 5.15.8+dfsg-11+deb12u2 ii libqt5qml5 5.15.8+dfsg-3 ii libqt5quick5 5.15.8+dfsg-3 ii libstdc++6 12.2.0-14 ii libsystemd0 252.31-1~deb12u1 ii libxcb-xkb1 1.15-1 ii libxcb1 1.15-1 ii qml-module-qtquick2 5.15.8+dfsg-3 ii x11-common 1:7.7+23 ii xauth 1:1.1.2-1 ii xserver-xorg [xserver] 1:7.7+23 Versions of packages sddm recommends: ii libpam-systemd 252.31-1~deb12u1 ii sddm-theme-debian-elarun [sddm-theme] 0.19.0-5 Versions of packages sddm suggests: pn libpam-kwallet5 <none> pn qtvirtualkeyboard-plugin <none> -- debconf information: sddm/daemon_name: /usr/bin/sddm * shared/default-x-display-manager: sddm
pgpOckW53Y66D.pgp
Description: OpenPGP digital signature