You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
279 lines
6.9 KiB
279 lines
6.9 KiB
/******************************************************************** |
|
KSld - the KDE Screenlocker Daemon |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 1999 Martin R. Jones <mjones@kde.org> |
|
Copyright (C) 2002 Luboš Luňák <l.lunak@kde.org> |
|
Copyright (C) 2003 Oswald Buddenhagen <ossi@kde.org> |
|
Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org> |
|
|
|
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, see <http://www.gnu.org/licenses/>. |
|
*********************************************************************/ |
|
#include "authenticator.h" |
|
#include <kcheckpass-enums.h> |
|
#include <config-ksmserver.h> |
|
|
|
// Qt |
|
#include <QFile> |
|
#include <QSocketNotifier> |
|
#include <QTimer> |
|
|
|
// system |
|
#include <errno.h> |
|
#include <sys/types.h> |
|
#include <sys/socket.h> |
|
#include <sys/wait.h> |
|
#include <unistd.h> |
|
|
|
Authenticator::Authenticator(QObject *parent) |
|
: QObject(parent) |
|
, m_graceLockTimer(new QTimer(this)) |
|
{ |
|
m_graceLockTimer->setSingleShot(true); |
|
m_graceLockTimer->setInterval(3000); |
|
connect(m_graceLockTimer, &QTimer::timeout, this, &Authenticator::graceLockedChanged); |
|
} |
|
|
|
Authenticator::~Authenticator() = default; |
|
|
|
void Authenticator::tryUnlock(const QString &password) |
|
{ |
|
if (isGraceLocked()) { |
|
emit failed(); |
|
return; |
|
} |
|
m_graceLockTimer->start(); |
|
emit graceLockedChanged(); |
|
|
|
KCheckPass *checkPass = new KCheckPass(password, this); |
|
connect(checkPass, &KCheckPass::succeeded, this, &Authenticator::succeeded); |
|
connect(checkPass, &KCheckPass::failed, this, &Authenticator::failed); |
|
connect(checkPass, &KCheckPass::message, this, &Authenticator::message); |
|
connect(checkPass, &KCheckPass::error, this, &Authenticator::error); |
|
checkPass->start(); |
|
} |
|
|
|
bool Authenticator::isGraceLocked() const |
|
{ |
|
return m_graceLockTimer->isActive(); |
|
} |
|
|
|
KCheckPass::KCheckPass(const QString &password, QObject *parent) |
|
: QObject(parent) |
|
, m_password(password) |
|
, m_notifier(nullptr) |
|
, m_pid(0) |
|
, m_fd(0) |
|
{ |
|
connect(this, &KCheckPass::succeeded, this, &QObject::deleteLater); |
|
connect(this, &KCheckPass::failed, this, &QObject::deleteLater); |
|
} |
|
|
|
KCheckPass::~KCheckPass() = default; |
|
|
|
void KCheckPass::start() |
|
{ |
|
int sfd[2]; |
|
char fdbuf[16]; |
|
|
|
if (m_notifier) |
|
return; |
|
if (::socketpair(AF_LOCAL, SOCK_STREAM, 0, sfd)) { |
|
cantCheck(); |
|
return; |
|
} |
|
if ((m_pid = ::fork()) < 0) { |
|
::close(sfd[0]); |
|
::close(sfd[1]); |
|
cantCheck(); |
|
return; |
|
} |
|
if (!m_pid) { |
|
::close(sfd[0]); |
|
sprintf(fdbuf, "%d", sfd[1]); |
|
execlp(QFile::encodeName(QStringLiteral(KCHECKPASS_BIN)).data(), |
|
"kcheckpass", |
|
"-m", "classic", |
|
"-S", fdbuf, |
|
(char *)0); |
|
_exit(20); |
|
} |
|
::close(sfd[1]); |
|
m_fd = sfd[0]; |
|
m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); |
|
connect(m_notifier, &QSocketNotifier::activated, this, &KCheckPass::handleVerify); |
|
} |
|
|
|
////// kckeckpass interface code |
|
|
|
int KCheckPass::Reader(void *buf, int count) |
|
{ |
|
int ret, rlen; |
|
|
|
for (rlen = 0; rlen < count; ) { |
|
dord: |
|
ret = ::read(m_fd, (void *)((char *)buf + rlen), count - rlen); |
|
if (ret < 0) { |
|
if (errno == EINTR) |
|
goto dord; |
|
if (errno == EAGAIN) |
|
break; |
|
return -1; |
|
} |
|
if (!ret) |
|
break; |
|
rlen += ret; |
|
} |
|
return rlen; |
|
} |
|
|
|
bool KCheckPass::GRead(void *buf, int count) |
|
{ |
|
return Reader(buf, count) == count; |
|
} |
|
|
|
bool KCheckPass::GWrite(const void *buf, int count) |
|
{ |
|
return ::write(m_fd, buf, count) == count; |
|
} |
|
|
|
bool KCheckPass::GSendInt(int val) |
|
{ |
|
return GWrite(&val, sizeof(val)); |
|
} |
|
|
|
bool KCheckPass::GSendStr(const char *buf) |
|
{ |
|
int len = buf ? ::strlen (buf) + 1 : 0; |
|
return GWrite(&len, sizeof(len)) && GWrite (buf, len); |
|
} |
|
|
|
bool KCheckPass::GSendArr(int len, const char *buf) |
|
{ |
|
return GWrite(&len, sizeof(len)) && GWrite (buf, len); |
|
} |
|
|
|
bool KCheckPass::GRecvInt(int *val) |
|
{ |
|
return GRead(val, sizeof(*val)); |
|
} |
|
|
|
bool KCheckPass::GRecvArr(char **ret) |
|
{ |
|
int len; |
|
char *buf; |
|
|
|
if (!GRecvInt(&len)) |
|
return false; |
|
if (!len) { |
|
*ret = 0; |
|
return true; |
|
} |
|
if (!(buf = (char *)::malloc (len))) |
|
return false; |
|
*ret = buf; |
|
if (GRead (buf, len)) { |
|
return true; |
|
} else { |
|
::free(buf); |
|
*ret = 0; |
|
return false; |
|
} |
|
} |
|
|
|
void KCheckPass::handleVerify() |
|
{ |
|
int ret; |
|
char *arr; |
|
|
|
if (GRecvInt( &ret )) { |
|
switch (ret) { |
|
case ConvGetBinary: |
|
if (!GRecvArr( &arr )) |
|
break; |
|
// FIXME: not supported |
|
cantCheck(); |
|
if (arr) |
|
::free( arr ); |
|
return; |
|
case ConvGetNormal: |
|
if (!GRecvArr( &arr )) |
|
break; |
|
GSendStr(m_password.toUtf8().constData()); |
|
if (!m_password.isEmpty()) { |
|
// IsSecret |
|
GSendInt(1); |
|
} |
|
if (arr) |
|
::free( arr ); |
|
return; |
|
case ConvGetHidden: |
|
if (!GRecvArr( &arr )) |
|
break; |
|
GSendStr(m_password.toUtf8().constData()); |
|
if (!m_password.isEmpty()) { |
|
// IsSecret |
|
GSendInt(1); |
|
} |
|
if (arr) |
|
::free( arr ); |
|
return; |
|
case ConvPutInfo: |
|
if (!GRecvArr( &arr )) |
|
break; |
|
emit message(QString::fromLocal8Bit(arr)); |
|
::free( arr ); |
|
return; |
|
case ConvPutError: |
|
if (!GRecvArr( &arr )) |
|
break; |
|
emit error(QString::fromLocal8Bit(arr)); |
|
::free( arr ); |
|
return; |
|
} |
|
} |
|
reapVerify(); |
|
} |
|
|
|
void KCheckPass::reapVerify() |
|
{ |
|
m_notifier->setEnabled( false ); |
|
m_notifier->deleteLater(); |
|
m_notifier = nullptr; |
|
::close( m_fd ); |
|
int status; |
|
while (::waitpid( m_pid, &status, 0 ) < 0) |
|
if (errno != EINTR) { // This should not happen ... |
|
cantCheck(); |
|
return; |
|
} |
|
if (WIFEXITED(status)) |
|
switch (WEXITSTATUS(status)) { |
|
case AuthOk: |
|
emit succeeded(); |
|
return; |
|
case AuthBad: |
|
emit failed(); |
|
return; |
|
case AuthAbort: |
|
return; |
|
} |
|
cantCheck(); |
|
} |
|
|
|
void KCheckPass::cantCheck() |
|
{ |
|
// TODO: better signal? |
|
emit failed(); |
|
}
|
|
|