fix: reset faillock after a face unlock of the lock screen
This commit is contained in:
1 parent
2ff27160e3
commit
7c87adb733
5 files changed
+39
No files matched your search
@@ -181,6 +181,11 @@ After a scan that did not get anybody in, the next one waits for the person to
|
|||||||
be still for two seconds and then touch something again, so typing the password
|
be still for two seconds and then touch something again, so typing the password
|
||||||
does not start a scan with every key.
|
does not start a scan with every key.
|
||||||
|
|
||||||
|
Unlocked through logind, the lock screen cuts off its own password prompt and
|
||||||
|
counts that as a wrong password. After a face unlock the daemon resets the
|
||||||
|
failed logins of *pam_faillock*(8), as a correct password does, so face unlocks
|
||||||
|
never lock the account.
|
||||||
|
|
||||||
The bubble is a layer-shell surface that KWin keeps above the lock screen
|
The bubble is a layer-shell surface that KWin keeps above the lock screen
|
||||||
(kde_lockscreen_overlay_v1). KWin only allows that for a program whose desktop
|
(kde_lockscreen_overlay_v1). KWin only allows that for a program whose desktop
|
||||||
file asks for it, which is
|
file asks for it, which is
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ CapabilityBoundingSet=CAP_DAC_READ_SEARCH
|
|||||||
# runtime directory, to tell the bubble about a sudo scan.
|
# runtime directory, to tell the bubble about a sudo scan.
|
||||||
NoNewPrivileges=yes
|
NoNewPrivileges=yes
|
||||||
ProtectSystem=strict
|
ProtectSystem=strict
|
||||||
|
# To take back the failed login the lock screen counts for a face unlock.
|
||||||
|
ReadWritePaths=-/run/faillock
|
||||||
ProtectHome=read-only
|
ProtectHome=read-only
|
||||||
PrivateTmp=yes
|
PrivateTmp=yes
|
||||||
PrivateNetwork=yes
|
PrivateNetwork=yes
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ QJsonObject result(bool ok, const QString &reason = {})
|
|||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A face unlock and the lock screen going away this soon after belong
|
||||||
|
// together.
|
||||||
|
constexpr qint64 UnlockPairSeconds = 30;
|
||||||
|
|
||||||
bool isFailureThatCounts(const QString &reason)
|
bool isFailureThatCounts(const QString &reason)
|
||||||
{
|
{
|
||||||
// A face that did not match, a fake, or a match that never showed a sign
|
// A face that did not match, a fake, or a match that never showed a sign
|
||||||
@@ -403,6 +407,15 @@ void Server::handleWatch(Client *client)
|
|||||||
void Server::handleUnlocked(Client *client)
|
void Server::handleUnlocked(Client *client)
|
||||||
{
|
{
|
||||||
UserState state = UserState::load(m_options.stateDir, client->uid());
|
UserState state = UserState::load(m_options.stateDir, client->uid());
|
||||||
|
|
||||||
|
// Our unlock goes through logind. The lock screen's password prompt is
|
||||||
|
// cut off mid-question by it and counts that as a wrong password, so a
|
||||||
|
// few face unlocks would lock the account (pam_faillock). Taken back here.
|
||||||
|
const qint64 now = QDateTime::currentSecsSinceEpoch();
|
||||||
|
if (geteuid() == 0 && state.lastPurpose == u"unlock" && now - state.lastUnlock <= UnlockPairSeconds) {
|
||||||
|
System::resetFailedLogins(client->uid());
|
||||||
|
}
|
||||||
|
|
||||||
if (state.failures || state.lockedUntil) {
|
if (state.failures || state.lockedUntil) {
|
||||||
state.failures = 0;
|
state.failures = 0;
|
||||||
state.lockedUntil = 0;
|
state.lockedUntil = 0;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <QDBusInterface>
|
#include <QDBusInterface>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -79,4 +81,17 @@ quint64 processStartTime(pid_t pid)
|
|||||||
const QList<QByteArray> fields = line.mid(close + 2).split(' ');
|
const QList<QByteArray> fields = line.mid(close + 2).split(' ');
|
||||||
return fields.size() > 19 ? fields.at(19).toULongLong() : 0;
|
return fields.size() > 19 ? fields.at(19).toULongLong() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool resetFailedLogins(uid_t uid)
|
||||||
|
{
|
||||||
|
// Not from PATH: this runs as root.
|
||||||
|
const QString faillock = QStandardPaths::findExecutable(QStringLiteral("faillock"),
|
||||||
|
{QStringLiteral("/usr/sbin"), QStringLiteral("/usr/bin"), QStringLiteral("/sbin")});
|
||||||
|
if (faillock.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QProcess p;
|
||||||
|
p.start(faillock, {QStringLiteral("--user"), nameOf(uid), QStringLiteral("--reset")});
|
||||||
|
return p.waitForFinished(3000) && p.exitStatus() == QProcess::NormalExit && p.exitCode() == 0;
|
||||||
|
}
|
||||||
} // namespace System
|
} // namespace System
|
||||||
@@ -20,4 +20,8 @@ bool lidClosed();
|
|||||||
// When a process started, in clock ticks since boot, as polkit wants it to
|
// When a process started, in clock ticks since boot, as polkit wants it to
|
||||||
// tell a process from a later one that got the same pid.
|
// tell a process from a later one that got the same pid.
|
||||||
quint64 processStartTime(pid_t pid);
|
quint64 processStartTime(pid_t pid);
|
||||||
|
|
||||||
|
// Forget the failed logins pam_faillock counted for somebody, as a correct
|
||||||
|
// password does. False without faillock.
|
||||||
|
bool resetFailedLogins(uid_t uid);
|
||||||
} // namespace System
|
} // namespace System
|
||||||
Reference in new issue
Block a user