@@ -1,305 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
17− */
18−
19−#include "Greeter.h"
20−#include "GreeterPrivate.h"
21−#include <QFuture>
22−#include <QFutureInterface>
23−#include <QFutureWatcher>
24−#include <QQueue>
25−#include <QtConcurrent>
26−#include <QVector>
27−#include <security/pam_appl.h>
28−
29−namespace QLightDM
30−{
31−
32−class GreeterImpl : public QObject
33−{
34− Q_OBJECT
35−
36− struct AppData
37− {
38− GreeterImpl *impl;
39− pam_handle *handle;
40− };
41−
42− typedef QFutureInterface<QString> ResponseFuture;
43−
44−public:
45− explicit GreeterImpl(Greeter *parent, GreeterPrivate *greeterPrivate)
46− : QObject(parent),
47− greeter(parent),
48− greeterPrivate(greeterPrivate),
49− pamHandle(nullptr)
50− {
51− qRegisterMetaType<QLightDM::GreeterImpl::ResponseFuture>("QLightDM::GreeterImpl::ResponseFuture");
52−
53− connect(&futureWatcher, SIGNAL(finished()),
54− this, SLOT(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);
61− }
62−
63− ~GreeterImpl()
64− {
65− cancelPam();
66− }
67−
68− void start(QString username)
69− {
70− // Clear out any existing PAM interactions first
71− cancelPam();
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.
77− return;
78− }
79−
80− AppData *appData = new AppData();
81− appData->impl = this;
82−
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);
87−
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));
91− } else {
92− delete appData;
93− greeterPrivate->authenticated = false;
94− Q_EMIT greeter->showMessage("Internal error: could not start PAM authentication", QLightDM::Greeter::MessageTypeError);
95− Q_EMIT greeter->authenticationComplete();
96− }
97− }
98−
99− static int authenticateWithPam(pam_handle* const& pamHandle)
100− {
101− int pamStatus = pam_authenticate(pamHandle, 0);
102− if (pamStatus == PAM_SUCCESS) {
103− pamStatus = pam_acct_mgmt(pamHandle, 0);
104− }
105− if (pamStatus == PAM_NEW_AUTHTOK_REQD) {
106− pamStatus = pam_chauthtok(pamHandle, PAM_CHANGE_EXPIRED_AUTHTOK);
107− }
108− if (pamStatus == PAM_SUCCESS) {
109− pam_setcred(pamHandle, PAM_REINITIALIZE_CRED);
110− }
111− return pamStatus;
112− }
113−
114− static int converseWithPam(int num_msg, const pam_message** msg,
115− pam_response** resp, void* appdata_ptr)
116− {
117− if (num_msg <= 0)
118− return PAM_CONV_ERR;
119−
120− auto* tmp_response = static_cast<pam_response*>(calloc(num_msg, sizeof(pam_response)));
121− if (!tmp_response)
122− return PAM_CONV_ERR;
123−
124− AppData *appData = static_cast<AppData*>(appdata_ptr);
125− GreeterImpl *impl = appData->impl;
126− pam_handle *handle = appData->handle;
127−
128− int count;
129− QVector<ResponseFuture> responses;
130−
131− for (count = 0; count < num_msg; ++count)
132− {
133− switch (msg[count]->msg_style)
134− {
135− case PAM_PROMPT_ECHO_ON:
136− {
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());
141− break;
142− }
143− case PAM_PROMPT_ECHO_OFF:
144− {
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());
149− break;
150− }
151− case PAM_TEXT_INFO:
152− {
153− QString message(msg[count]->msg);
154− Q_EMIT impl->showMessage(handle, message, Greeter::MessageTypeInfo);
155− break;
156− }
157− default:
158− {
159− QString message(msg[count]->msg);
160− Q_EMIT impl->showMessage(handle, message, Greeter::MessageTypeError);
161− break;
162− }
163− }
164− }
165−
166− int i = 0;
167− bool raise_error = false;
168−
169− for (auto &response : responses)
170− {
171− pam_response* resp_item = &tmp_response[i++];
172− resp_item->resp_retcode = 0;
173− resp_item->resp = strdup(response.future().result().toUtf8());
174−
175− if (!resp_item->resp)
176− {
177− raise_error = true;
178− break;
179− }
180− }
181−
182− delete appData;
183−
184− if (raise_error)
185− {
186− for (int i = 0; i < count; ++i)
187− free(tmp_response[i].resp);
188−
189− free(tmp_response);
190− return PAM_CONV_ERR;
191− }
192− else
193− {
194− *resp = tmp_response;
195− return PAM_SUCCESS;
196− }
197− }
198−
199−public Q_SLOTS:
200− bool respond(QString response)
201− {
202− if (!futures.isEmpty()) {
203− futures.dequeue().reportFinished(&response);
204− return true;
205− } else {
206− return false;
207− }
208− }
209−
210−Q_SIGNALS:
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);
213−
214−private Q_SLOTS:
215− void finishPam()
216− {
217− if (pamHandle == nullptr) {
218− return;
219− }
220−
221− int pamStatus = futureWatcher.result();
222−
223− pam_end(pamHandle, pamStatus);
224− pamHandle = nullptr;
225−
226− greeterPrivate->authenticated = (pamStatus == PAM_SUCCESS);
227− Q_EMIT greeter->authenticationComplete();
228− }
229−
230− void handleMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type)
231− {
232− if (handle != pamHandle)
233− return;
234−
235− Q_EMIT greeter->showMessage(text, type);
236− }
237−
238− void handlePrompt(pam_handle *handle, QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture future)
239− {
240− if (handle != pamHandle) {
241− future.reportResult(QString());
242− future.reportFinished();
243− return;
244− }
245−
246− futures.enqueue(future);
247− Q_EMIT greeter->showPrompt(text, type);
248− }
249−
250−private:
251− void cancelPam()
252− {
253− if (pamHandle != nullptr) {
254− QFuture<int> pamFuture = futureWatcher.future();
255− pam_handle *handle = pamHandle;
256− pamHandle = nullptr; // to disable normal finishPam() handling
257− pamFuture.cancel();
258−
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()));
263−
264− // Now let signal/slot handling happen so the thread can finish
265− while (!pamFuture.isFinished()) {
266− QCoreApplication::processEvents();
267− }
268−
269− pam_end(handle, PAM_CONV_ERR);
270− }
271− }
272−
273− Greeter *greeter;
274− GreeterPrivate *greeterPrivate;
275− pam_handle* pamHandle;
276− QFutureWatcher<int> futureWatcher;
277− QQueue<ResponseFuture> futures;
278−};
279−
280−GreeterPrivate::GreeterPrivate(Greeter* parent)
281− : authenticated(false),
282− authenticationUser(),
283− m_impl(new GreeterImpl(parent, this)),
284− q_ptr(parent)
285−{
286−}
287−
288−GreeterPrivate::~GreeterPrivate()
289−{
290− delete m_impl;
291−}
292−
293−void GreeterPrivate::handleAuthenticate()
294−{
295− m_impl->start(authenticationUser);
296−}
297−
298−void GreeterPrivate::handleRespond(const QString &response)
299−{
300− m_impl->respond(response);
301−}
302−
303−}
304−
305−#include "GreeterPrivate.moc"