Commit ef2f4f38 authored by David Edmundson's avatar David Edmundson
Browse files

fingerprint unlock

parent 29153f16
......@@ -11,6 +11,7 @@ set(kscreenlocker_greet_SRCS
authenticator.cpp
greeterapp.cpp
main.cpp
pamhandle.cpp
noaccessnetworkaccessmanagerfactory.cpp
)
......@@ -50,6 +51,7 @@ target_link_libraries(kscreenlocker_greet
${X11_LIBRARIES}
KF5::WaylandClient
Wayland::Client
${PAM_LIBRARIES}
)
install(TARGETS kscreenlocker_greet DESTINATION ${KDE_INSTALL_LIBEXECDIR})
......
......@@ -77,6 +77,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// *must* be "0" for every public commit!
#define TEST_SCREENSAVER 0
#include "pamhandle.h"
namespace ScreenLocker
{
......@@ -120,6 +122,17 @@ UnlockApp::UnlockApp(int &argc, char **argv)
, m_lnfIntegration(new LnFIntegration(this))
{
m_authenticator = createAuthenticator();
// TODO This needs guarding from a config or something
qDebug() << "starting auth for " << KUser().loginName();
auto fingerAuthJob = new AuthenticateUserJob(QStringLiteral("kde-finger"), KUser().loginName());
connect(fingerAuthJob, &AuthenticateUserJob::finished, this, [](bool success) {
if (success) {
qApp->quit();
}
});
fingerAuthJob->start();
connect(m_authenticator, &Authenticator::succeeded, this, &QCoreApplication::quit);
initialize();
connect(this, &UnlockApp::screenAdded, this, &UnlockApp::desktopResized);
......
/*
* Copyright 2020 David Edmundson <davidedmundson@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) version 3, or any
* later version accepted by the membership of KDE e.V. (or its
* successor approved by the membership of KDE e.V.), which shall
* act as a proxy defined in Section 6 of version 3 of the license.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pamhandle.h"
#include <QDebug>
#include <QEventLoop>
#include <security/pam_appl.h>
class PamWorker : public QObject
{
Q_OBJECT
public:
PamWorker();
~PamWorker();
void start(const QString &service, const QString &user);
void authenticate();
void changePassword();
static int converse(int n, const struct pam_message **msg, struct pam_response **resp, void *data);
Q_SIGNALS:
void prompt(const QString &prompt);
void finished(bool success);
// internal
void promptReceived(const QByteArray &prompt);
void cancelled();
private:
pam_handle_t *m_handle = nullptr; //< the actual PAM handle
struct pam_conv m_conv;
int m_result;
};
int PamWorker::converse(int n, const struct pam_message **msg, struct pam_response **resp, void *data)
{
PamWorker *c = static_cast<PamWorker *>(data);
*resp = (struct pam_response *)calloc(n, sizeof(struct pam_response));
if (!*resp) {
return PAM_BUF_ERR;
}
for (int i = 0; i < n; i++) {
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_OFF:
case PAM_PROMPT_ECHO_ON: {
const QString prompt = QString::fromLocal8Bit(msg[i]->msg);
emit c->prompt(prompt);
qDebug() << "new request" << prompt;
QByteArray response;
bool cancelled = false;
QEventLoop e;
QObject::connect(c, &PamWorker::promptReceived, &e, [&](const QByteArray &_response) {
response = _response;
e.quit();
});
QObject::connect(c, &PamWorker::cancelled, &e, [&]() {
cancelled = true;
e.quit();
});
e.exec();
if (cancelled) {
return PAM_CONV_ERR;
}
resp[i]->resp = (char *)malloc(response.length() + 1);
// on error, get rid of everything
if (!resp[i]->resp) {
for (int j = 0; j < n; j++) {
free(resp[i]->resp);
resp[i]->resp = nullptr;
}
free(*resp);
*resp = nullptr;
return PAM_BUF_ERR;
}
memcpy(resp[i]->resp, response.constData(), response.length());
resp[i]->resp[response.length()] = '\0';
break;
}
case PAM_ERROR_MSG:
qDebug() << QString::fromLocal8Bit(msg[i]->msg);
break;
case PAM_TEXT_INFO:
// if there's only the info message, let's predict the prompts too
qDebug() << QString::fromLocal8Bit(msg[i]->msg);
break;
default:
break;
}
}
qDebug() << "DONE";
return PAM_SUCCESS;
}
PamWorker::PamWorker()
: QObject(nullptr)
{
m_conv = {&PamWorker::converse, this};
}
PamWorker::~PamWorker()
{
if (m_handle) {
pam_end(m_handle, PAM_SUCCESS);
}
}
void PamWorker::authenticate()
{
qDebug() << "Start auth";
int rc = pam_authenticate(m_handle, PAM_SILENT);
qDebug() << "Auth done RC" << rc;
bool success = rc == PAM_SUCCESS;
emit finished(success);
}
void PamWorker::start(const QString &service, const QString &user)
{
if (user.isEmpty())
m_result = pam_start(qPrintable(service), nullptr, &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;
} else {
qDebug() << "[PAM] Starting...";
}
}
PamJob::PamJob()
: QObject(nullptr)
{
d = new PamWorker;
d->moveToThread(&m_thread);
connect(&m_thread, &QThread::finished, d, &QObject::deleteLater);
connect(d, &PamWorker::prompt, this, &PamJob::prompt);
connect(d, &PamWorker::finished, this, &PamJob::finished);
m_thread.start();
}
PamJob::~PamJob()
{
cancel();
m_thread.quit();
m_thread.wait();
}
void PamJob::init(const QString &service, const QString &user)
{
QMetaObject::invokeMethod(d, [this, service, user]() {
d->start(service, user);
});
}
void PamJob::authenticate()
{
QMetaObject::invokeMethod(d, &PamWorker::authenticate);
}
void PamJob::changePassword()
{
QMetaObject::invokeMethod(d, &PamWorker::changePassword);
}
void PamJob::respond(const QByteArray &response)
{
QMetaObject::invokeMethod(
d,
[this, response]() {
emit d->promptReceived(response);
},
Qt::QueuedConnection);
}
void PamJob::cancel()
{
QMetaObject::invokeMethod(d, &PamWorker::cancelled);
}
AuthenticateUserJob::AuthenticateUserJob(const QString &service, const QString &user)
: PamJob()
{
init(service, user);
}
void AuthenticateUserJob::start()
{
authenticate();
}
#include "pamhandle.moc"
/*
* Copyright 2020 David Edmundson <davidedmundson@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) version 3, or any
* later version accepted by the membership of KDE e.V. (or its
* successor approved by the membership of KDE e.V.), which shall
* act as a proxy defined in Section 6 of version 3 of the license.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef PAMHANDLE_H
#define PAMHANDLE_H
#include <QObject>
#include <QThread>
class PamWorker;
class PamJob : public QObject
{
Q_OBJECT
public:
~PamJob();
Q_SIGNALS:
void prompt(const QString &prompt);
void finished(bool success);
public Q_SLOTS:
void respond(const QByteArray &response);
void cancel();
protected:
void init(const QString &service, const QString &user);
void authenticate();
void changePassword();
PamJob(); // no parent, it follows a job like pattern
// Note that "d" lives in a separate thread
private:
QThread m_thread;
PamWorker *d;
};
class AuthenticateUserJob : public PamJob
{
Q_OBJECT
public:
AuthenticateUserJob(const QString &service, const QString &user);
void start();
};
#endif // PAMHANDLE_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment