@@ -18,53 +18,225 @@
19#include "../Greeter.h"
19#include "../Greeter.h"
20#include "../GreeterPrivate.h"
20#include "../GreeterPrivate.h"
21−#include <QtCore/QDir>
21+#include <QFuture>
22−#include <QtCore/QSettings>
22+#include <QFutureInterface>
23+#include <QFutureWatcher>
25+#include <QtConcurrent>
27+#include <security/pam_appl.h>
24namespace QLightDM
29namespace QLightDM
32+class GreeterImpl : public QObject
36+ typedef QFutureInterface<QString> ResponseFuture;
39+ explicit GreeterImpl(Greeter *parent, GreeterPrivate *greeterPrivate)
42+ greeterPrivate(greeterPrivate),
45+ qRegisterMetaType<QLightDM::GreeterImpl::ResponseFuture>("QLightDM::GreeterImpl::ResponseFuture");
47+ connect(&futureWatcher, SIGNAL(finished()),
48+ this, SLOT(finishPam()));
49+ connect(this, SIGNAL(showMessage(QString, QLightDM::Greeter::MessageType)),
50+ greeter, SIGNAL(showMessage(QString, QLightDM::Greeter::MessageType)));
51+ // This next connect is how we pass ResponseFutures between threads
52+ connect(this, SIGNAL(showPrompt(QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),
53+ this, SLOT(handlePrompt(QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)));
56+ void start(QString username)
58+ // Clear out any existing PAM interactions first (we can't simply
59+ // cancel our QFuture because QtConcurrent::run doesn't support cancel)
60+ if (pamHandle != NULL) {
61+ pam_handle *handle = pamHandle;
62+ pamHandle = NULL; // to disable normal finishPam() handling
63+ while (respond(QString())); // clear our local queue of QFutures
64+ pam_end(handle, PAM_CONV_ERR);
67+ // Now actually start a new conversation with PAM
68+ pam_conv conversation;
69+ conversation.conv = converseWithPam;
70+ conversation.appdata_ptr = static_cast<void*>(this);
72+ if (pam_start("lightdm", username.toUtf8(), &conversation, &pamHandle) == PAM_SUCCESS) {
73+ futureWatcher.setFuture(QtConcurrent::run(authenticateWithPam, pamHandle));
75+ greeterPrivate->authenticated = false;
76+ Q_EMIT greeter->showMessage("Internal error: could not start PAM authentication", QLightDM::Greeter::MessageTypeError);
77+ Q_EMIT greeter->authenticationComplete();
81+ static int authenticateWithPam(pam_handle* pamHandle)
83+ int pamStatus = pam_authenticate(pamHandle, 0);
84+ if (pamStatus == PAM_SUCCESS) {
85+ pamStatus = pam_acct_mgmt(pamHandle, 0);
87+ if (pamStatus == PAM_NEW_AUTHTOK_REQD) {
88+ pamStatus = pam_chauthtok(pamHandle, PAM_CHANGE_EXPIRED_AUTHTOK);
90+ if (pamStatus == PAM_SUCCESS) {
91+ pam_setcred(pamHandle, PAM_REINITIALIZE_CRED);
96+ static int converseWithPam(int num_msg, const pam_message** msg,
97+ pam_response** resp, void* appdata_ptr)
100+ return PAM_CONV_ERR;
102+ auto* tmp_response = static_cast<pam_response*>(calloc(num_msg, sizeof(pam_response)));
104+ return PAM_CONV_ERR;
106+ GreeterImpl* impl = static_cast<GreeterImpl*>(appdata_ptr);
109+ QVector<ResponseFuture> responses;
111+ for (count = 0; count < num_msg; ++count)
113+ switch (msg[count]->msg_style)
115+ case PAM_PROMPT_ECHO_ON:
117+ QString message(msg[count]->msg);
118+ responses.append(ResponseFuture());
119+ responses.last().reportStarted();
120+ Q_EMIT impl->showPrompt(message, Greeter::PromptTypeQuestion, responses.last());
123+ case PAM_PROMPT_ECHO_OFF:
125+ QString message(msg[count]->msg);
126+ responses.append(ResponseFuture());
127+ responses.last().reportStarted();
128+ Q_EMIT impl->showPrompt(message, Greeter::PromptTypeSecret, responses.last());
133+ QString message(msg[count]->msg);
134+ Q_EMIT impl->showMessage(message, Greeter::MessageTypeInfo);
139+ QString message(msg[count]->msg);
140+ Q_EMIT impl->showMessage(message, Greeter::MessageTypeError);
147+ bool raise_error = false;
149+ for (auto &response : responses)
151+ pam_response* resp_item = &tmp_response[i++];
152+ resp_item->resp_retcode = 0;
153+ resp_item->resp = strdup(response.future().result().toUtf8());
155+ if (!resp_item->resp)
164+ for (int i = 0; i < count; ++i)
165+ free(tmp_response[i].resp);
168+ return PAM_CONV_ERR;
172+ *resp = tmp_response;
178+ bool respond(QString response)
180+ if (!futures.isEmpty()) {
181+ futures.dequeue().reportFinished(&response);
189+ void showMessage(QString text, QLightDM::Greeter::MessageType type);
190+ void showPrompt(QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);
195+ if (pamHandle == NULL) {
199+ int pamStatus = futureWatcher.result();
201+ pam_end(pamHandle, pamStatus);
204+ greeterPrivate->authenticated = (pamStatus == PAM_SUCCESS);
205+ Q_EMIT greeter->authenticationComplete();
208+ void handlePrompt(QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture future)
210+ futures.enqueue(future);
211+ Q_EMIT greeter->showPrompt(text, type);
216+ GreeterPrivate *greeterPrivate;
217+ pam_handle* pamHandle;
218+ QFutureWatcher<int> futureWatcher;
219+ QQueue<ResponseFuture> futures;
27GreeterPrivate::GreeterPrivate(Greeter* parent)
222GreeterPrivate::GreeterPrivate(Greeter* parent)
28 : authenticated(false),
223 : authenticated(false),
29 authenticationUser(),
224 authenticationUser(),
225+ m_impl(new GreeterImpl(parent, this)),
30 q_ptr(parent)
226 q_ptr(parent)
34void GreeterPrivate::handleAuthenticate()
230void GreeterPrivate::handleAuthenticate()
38− QSettings settings(QDir::homePath() + "/.unity8-greeter-demo", QSettings::NativeFormat);
39− settings.beginGroup(authenticationUser);
232+ m_impl->start(authenticationUser);
40− QVariant password = settings.value("password", "none");
42− if (password == "pin") {
43− Q_EMIT q->showPrompt("PIN", Greeter::PromptTypeSecret);
44− } else if (password == "keyboard") {
45− Q_EMIT q->showPrompt("Password", Greeter::PromptTypeSecret);
47− authenticated = true;
48− Q_EMIT q->authenticationComplete();
52void GreeterPrivate::handleRespond(const QString &response)
235void GreeterPrivate::handleRespond(const QString &response)
54− Q_Q(Greeter);
237+ m_impl->respond(response);
56− QSettings settings(QDir::homePath() + "/.unity8-greeter-demo", QSettings::NativeFormat);
57− settings.beginGroup(authenticationUser);
58− QVariant password = settings.value("password", "none");
60− QString passwordValue;
61− if (password == "pin") {
62− passwordValue = settings.value("passwordValue", "1234").toString();
64− passwordValue = settings.value("passwordValue", "password").toString();
66− authenticated = (response == passwordValue);
67− Q_EMIT q->authenticationComplete();
242+#include "GreeterPrivate.moc"