on 29019df help sign in
unity8/tests/mocks/LightDM/demo/GreeterPrivate.cpp
242 lines 7.6 kB history raw
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 */
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>
29namespace QLightDM
32class GreeterImpl : public QObject
34 Q_OBJECT
36 typedef QFutureInterface<QString> ResponseFuture;
38public:
39 explicit GreeterImpl(Greeter *parent, GreeterPrivate *greeterPrivate)
40 : QObject(parent),
41 greeter(parent),
42 greeterPrivate(greeterPrivate),
43 pamHandle(NULL)
44 {
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)));
54 }
56 void start(QString username)
57 {
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);
65 }
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));
74 } else {
75 greeterPrivate->authenticated = false;
76 Q_EMIT greeter->showMessage("Internal error: could not start PAM authentication", QLightDM::Greeter::MessageTypeError);
77 Q_EMIT greeter->authenticationComplete();
78 }
79 }
81 static int authenticateWithPam(pam_handle* pamHandle)
82 {
83 int pamStatus = pam_authenticate(pamHandle, 0);
84 if (pamStatus == PAM_SUCCESS) {
85 pamStatus = pam_acct_mgmt(pamHandle, 0);
86 }
87 if (pamStatus == PAM_NEW_AUTHTOK_REQD) {
88 pamStatus = pam_chauthtok(pamHandle, PAM_CHANGE_EXPIRED_AUTHTOK);
89 }
90 if (pamStatus == PAM_SUCCESS) {
91 pam_setcred(pamHandle, PAM_REINITIALIZE_CRED);
92 }
93 return pamStatus;
94 }
96 static int converseWithPam(int num_msg, const pam_message** msg,
97 pam_response** resp, void* appdata_ptr)
98 {
99 if (num_msg <= 0)
100 return PAM_CONV_ERR;
102 auto* tmp_response = static_cast<pam_response*>(calloc(num_msg, sizeof(pam_response)));
103 if (!tmp_response)
104 return PAM_CONV_ERR;
106 GreeterImpl* impl = static_cast<GreeterImpl*>(appdata_ptr);
108 int count;
109 QVector<ResponseFuture> responses;
111 for (count = 0; count < num_msg; ++count)
112 {
113 switch (msg[count]->msg_style)
114 {
115 case PAM_PROMPT_ECHO_ON:
116 {
117 QString message(msg[count]->msg);
118 responses.append(ResponseFuture());
119 responses.last().reportStarted();
120 Q_EMIT impl->showPrompt(message, Greeter::PromptTypeQuestion, responses.last());
121 break;
122 }
123 case PAM_PROMPT_ECHO_OFF:
124 {
125 QString message(msg[count]->msg);
126 responses.append(ResponseFuture());
127 responses.last().reportStarted();
128 Q_EMIT impl->showPrompt(message, Greeter::PromptTypeSecret, responses.last());
129 break;
130 }
131 case PAM_TEXT_INFO:
132 {
133 QString message(msg[count]->msg);
134 Q_EMIT impl->showMessage(message, Greeter::MessageTypeInfo);
135 break;
136 }
137 default:
138 {
139 QString message(msg[count]->msg);
140 Q_EMIT impl->showMessage(message, Greeter::MessageTypeError);
141 break;
142 }
143 }
144 }
146 int i = 0;
147 bool raise_error = false;
149 for (auto &response : responses)
150 {
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)
156 {
157 raise_error = true;
158 break;
159 }
160 }
162 if (raise_error)
163 {
164 for (int i = 0; i < count; ++i)
165 free(tmp_response[i].resp);
167 free(tmp_response);
168 return PAM_CONV_ERR;
169 }
170 else
171 {
172 *resp = tmp_response;
173 return PAM_SUCCESS;
174 }
175 }
177public Q_SLOTS:
178 bool respond(QString response)
179 {
180 if (!futures.isEmpty()) {
181 futures.dequeue().reportFinished(&response);
182 return true;
183 } else {
184 return false;
185 }
186 }
188Q_SIGNALS:
189 void showMessage(QString text, QLightDM::Greeter::MessageType type);
190 void showPrompt(QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);
192private Q_SLOTS:
193 void finishPam()
194 {
195 if (pamHandle == NULL) {
196 return;
197 }
199 int pamStatus = futureWatcher.result();
201 pam_end(pamHandle, pamStatus);
202 pamHandle = NULL;
204 greeterPrivate->authenticated = (pamStatus == PAM_SUCCESS);
205 Q_EMIT greeter->authenticationComplete();
206 }
208 void handlePrompt(QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture future)
209 {
210 futures.enqueue(future);
211 Q_EMIT greeter->showPrompt(text, type);
212 }
214private:
215 Greeter *greeter;
216 GreeterPrivate *greeterPrivate;
217 pam_handle* pamHandle;
218 QFutureWatcher<int> futureWatcher;
219 QQueue<ResponseFuture> futures;
220};
222GreeterPrivate::GreeterPrivate(Greeter* parent)
223 : authenticated(false),
224 authenticationUser(),
225 m_impl(new GreeterImpl(parent, this)),
226 q_ptr(parent)
230void GreeterPrivate::handleAuthenticate()
232 m_impl->start(authenticationUser);
235void GreeterPrivate::handleRespond(const QString &response)
237 m_impl->respond(response);
242#include "GreeterPrivate.moc"