@@ -1,305 +0,0 @@
2− * Copyright (C) 2013, 2015 Canonical, Ltd.
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
16− * Author: Michael Terry <michael.terry@canonical.com>
20−#include "GreeterPrivate.h"
22−#include <QFutureInterface>
23−#include <QFutureWatcher>
25−#include <QtConcurrent>
27−#include <security/pam_appl.h>
32−class GreeterImpl : public QObject
42− typedef QFutureInterface<QString> ResponseFuture;
45− explicit GreeterImpl(Greeter *parent, GreeterPrivate *greeterPrivate)
48− greeterPrivate(greeterPrivate),
51− qRegisterMetaType<QLightDM::GreeterImpl::ResponseFuture>("QLightDM::GreeterImpl::ResponseFuture");
53− connect(&futureWatcher, &QFutureWatcher<int>::finished,
54− this, &GreeterImpl::finishPam);
55− connect(this, SIGNAL(showMessage(pam_handle *, QString, QLightDM::Greeter::MessageType)),
56− this, SLOT(handleMessage(pam_handle *, QString, QLightDM::Greeter::MessageType)));
57− // This next connect is how we pass ResponseFutures between threads
58− connect(this, SIGNAL(showPrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),
59− this, SLOT(handlePrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),
60− Qt::BlockingQueuedConnection);
68− void start(const QString &username)
70− // Clear out any existing PAM interactions first
72− if (pamHandle != nullptr) {
73− // While we were cancelling pam above, we processed Qt events.
74− // Which may have allowed someone to call start() on us again.
75− // In which case, we'll bail on our current start() call.
76− // This isn't racy because it's all in the same thread.
80− AppData *appData = new AppData();
81− appData->impl = this;
83− // Now actually start a new conversation with PAM
84− pam_conv conversation;
85− conversation.conv = converseWithPam;
86− conversation.appdata_ptr = static_cast<void*>(appData);
88− if (pam_start("lightdm", username.toUtf8(), &conversation, &pamHandle) == PAM_SUCCESS) {
89− appData->handle = pamHandle;
90− futureWatcher.setFuture(QtConcurrent::mapped(QList<pam_handle*>() << pamHandle, authenticateWithPam));
93− greeterPrivate->authenticated = false;
94− Q_EMIT greeter->showMessage("Internal error: could not start PAM authentication", QLightDM::Greeter::MessageTypeError);
95− Q_EMIT greeter->authenticationComplete();
99− static int authenticateWithPam(pam_handle* const& pamHandle)
101− int pamStatus = pam_authenticate(pamHandle, 0);
102− if (pamStatus == PAM_SUCCESS) {
103− pamStatus = pam_acct_mgmt(pamHandle, 0);
105− if (pamStatus == PAM_NEW_AUTHTOK_REQD) {
106− pamStatus = pam_chauthtok(pamHandle, PAM_CHANGE_EXPIRED_AUTHTOK);
108− if (pamStatus == PAM_SUCCESS) {
109− pam_setcred(pamHandle, PAM_REINITIALIZE_CRED);
114− static int converseWithPam(int num_msg, const pam_message** msg,
115− pam_response** resp, void* appdata_ptr)
118− return PAM_CONV_ERR;
120− auto* tmp_response = static_cast<pam_response*>(calloc(num_msg, sizeof(pam_response)));
122− return PAM_CONV_ERR;
124− AppData *appData = static_cast<AppData*>(appdata_ptr);
125− GreeterImpl *impl = appData->impl;
126− pam_handle *handle = appData->handle;
129− QVector<ResponseFuture> responses;
131− for (count = 0; count < num_msg; ++count)
133− switch (msg[count]->msg_style)
135− case PAM_PROMPT_ECHO_ON:
137− QString message(msg[count]->msg);
138− responses.append(ResponseFuture());
139− responses.last().reportStarted();
140− Q_EMIT impl->showPrompt(handle, message, Greeter::PromptTypeQuestion, responses.last());
143− case PAM_PROMPT_ECHO_OFF:
145− QString message(msg[count]->msg);
146− responses.append(ResponseFuture());
147− responses.last().reportStarted();
148− Q_EMIT impl->showPrompt(handle, message, Greeter::PromptTypeSecret, responses.last());
153− QString message(msg[count]->msg);
154− Q_EMIT impl->showMessage(handle, message, Greeter::MessageTypeInfo);
159− QString message(msg[count]->msg);
160− Q_EMIT impl->showMessage(handle, message, Greeter::MessageTypeError);
167− bool raise_error = false;
169− for (auto &response : responses)
171− pam_response* resp_item = &tmp_response[i++];
172− resp_item->resp_retcode = 0;
173− resp_item->resp = strdup(response.future().result().toUtf8());
175− if (!resp_item->resp)
186− for (int i = 0; i < count; ++i)
187− free(tmp_response[i].resp);
190− return PAM_CONV_ERR;
194− *resp = tmp_response;
200− bool respond(const QString &response)
202− if (!futures.isEmpty()) {
203− futures.dequeue().reportFinished(&response);
211− void showMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type);
212− void showPrompt(pam_handle *handle, QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);
217− if (pamHandle == nullptr) {
221− int pamStatus = futureWatcher.result();
223− pam_end(pamHandle, pamStatus);
224− pamHandle = nullptr;
226− greeterPrivate->authenticated = (pamStatus == PAM_SUCCESS);
227− Q_EMIT greeter->authenticationComplete();
230− void handleMessage(pam_handle *handle, const QString &text, QLightDM::Greeter::MessageType type)
232− if (handle != pamHandle)
235− Q_EMIT greeter->showMessage(text, type);
238− void handlePrompt(pam_handle *handle, const QString &text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture future)
240− if (handle != pamHandle) {
241− future.reportResult(QString());
242− future.reportFinished();
246− futures.enqueue(future);
247− Q_EMIT greeter->showPrompt(text, type);
253− if (pamHandle != nullptr) {
254− QFuture<int> pamFuture = futureWatcher.future();
255− pam_handle *handle = pamHandle;
256− pamHandle = nullptr; // to disable normal finishPam() handling
259− // Note the empty loop, we just want to clear the futures queue.
260− // Any further prompts from the pam thread will be immediately
261− // responded to/dismissed in handlePrompt().
262− while (respond(QString()));
264− // Now let signal/slot handling happen so the thread can finish
265− while (!pamFuture.isFinished()) {
266− QCoreApplication::processEvents();
269− pam_end(handle, PAM_CONV_ERR);
274− GreeterPrivate *greeterPrivate;
275− pam_handle* pamHandle;
276− QFutureWatcher<int> futureWatcher;
277− QQueue<ResponseFuture> futures;
280−GreeterPrivate::GreeterPrivate(Greeter* parent)
281− : authenticated(false),
282− authenticationUser(),
283− m_impl(new GreeterImpl(parent, this)),
288−GreeterPrivate::~GreeterPrivate()
293−void GreeterPrivate::handleAuthenticate()
295− m_impl->start(authenticationUser);
298−void GreeterPrivate::handleRespond(const QString &response)
300− m_impl->respond(response);
305−#include "GreeterPrivate.moc"