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)));
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(QString username)
72 if (pamHandle !=
nullptr) {
80 AppData *appData =
new AppData();
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(QStringLiteral(
"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)
120 auto* tmp_response =
static_cast<pam_response*
>(calloc(num_msg,
sizeof(pam_response)));
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);
194 *resp = tmp_response;
200 bool respond(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);
226 greeterPrivate->authenticated = (pamStatus == PAM_SUCCESS);
227 Q_EMIT greeter->authenticationComplete();
230 void handleMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type)
232 if (handle != pamHandle)
235 Q_EMIT greeter->showMessage(text, type);
238 void handlePrompt(pam_handle *handle, 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;
262 while (respond(QString()));
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"