Package: sddm
Version: 0.19.0-5
Severity: normal
Tags: patch
X-Debbugs-Cc: adrelanos@kicksecure.com, arraybolt3@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
Attachment:
pgpuZEbU1gkgJ.pgp
Description: OpenPGP digital signature