]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-qt/greeter.cpp
950af627c438876a7b68e074e055cb57b090a5e7
[sojka/lightdm.git] / liblightdm-qt / greeter.cpp
1 /*
2  * Copyright (C) 2010-2011 David Edmundson
3  * Copyright (C) 2010-2011 Robert Ancell
4  * Author: David Edmundson <kde@davidedmundson.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by the Free
8  * Software Foundation; either version 3 of the License, or (at your option) any
9  * later version. See http://www.gnu.org/copyleft/lgpl.html the full text of the
10  * license.
11  */
12
13 #include "config.h"
14
15 #include "QLightDM/Greeter"
16
17 #include <security/pam_appl.h>
18 #include <QtCore/QDebug>
19 #include <QtCore/QDir>
20 #include <QtCore/QVariant>
21 #include <QtCore/QSettings>
22 #include <QtCore/QUrl>
23 #include <QtCore/QFile>
24 #include <QtCore/QHash>
25 #include <QtCore/QSocketNotifier>
26 #include <QtDBus/QDBusPendingReply>
27 #include <QtDBus/QDBusInterface>
28 #include <QtDBus/QDBusReply>
29
30 /* Messages from the greeter to the server */
31 typedef enum
32 {
33     GREETER_MESSAGE_CONNECT = 0,
34     GREETER_MESSAGE_AUTHENTICATE,
35     GREETER_MESSAGE_AUTHENTICATE_AS_GUEST,
36     GREETER_MESSAGE_CONTINUE_AUTHENTICATION,
37     GREETER_MESSAGE_START_SESSION,
38     GREETER_MESSAGE_CANCEL_AUTHENTICATION
39 } GreeterMessage;
40
41 /* Messages from the server to the greeter */
42 typedef enum
43 {
44     SERVER_MESSAGE_CONNECTED = 0,
45     SERVER_MESSAGE_QUIT,
46     SERVER_MESSAGE_PROMPT_AUTHENTICATION,
47     SERVER_MESSAGE_END_AUTHENTICATION,
48     SERVER_MESSAGE_SESSION_FAILED,
49 } ServerMessage;
50
51 #define HEADER_SIZE 8
52
53 using namespace QLightDM;
54
55 class GreeterPrivate
56 {
57 public:
58     QHash<QString, QString> hints;
59
60     int toServerFd;
61     int fromServerFd;
62     QSocketNotifier *n;
63     char *readBuffer;
64     int nRead;
65     bool inAuthentication;
66     bool isAuthenticated;
67     QString authenticationUser;
68     int authenticateSequenceNumber;
69     bool cancellingAuthentication;
70 };
71
72
73 Greeter::Greeter(QObject *parent) :
74     QObject(parent),
75     d(new GreeterPrivate)
76 {
77     d->readBuffer = (char *)malloc (HEADER_SIZE);
78     d->nRead = 0;
79     d->authenticateSequenceNumber = 0;
80 }
81
82 Greeter::~Greeter()
83 {
84     delete d->readBuffer;
85     delete d;
86 }
87
88 static int intLength()
89 {
90     return 4;
91 }
92
93 static int stringLength(QString value)
94 {
95     QByteArray a = value.toUtf8();
96     return intLength() + a.size();
97 }
98
99 void Greeter::writeInt(int value)
100 {
101     char buffer[4];
102     buffer[0] = value >> 24;
103     buffer[1] = (value >> 16) & 0xFF;
104     buffer[2] = (value >> 8) & 0xFF;
105     buffer[3] = value & 0xFF;
106     if (write(d->toServerFd, buffer, intLength()) != intLength()) {
107         qDebug() << "Error writing to server";
108     }
109 }
110
111 void Greeter::writeString(QString value)
112 {
113     QByteArray a = value.toUtf8();
114     writeInt(a.size());
115     if (write(d->toServerFd, a.data(), a.size()) != a.size()) {
116         qDebug() << "Error writing to server";
117     }
118 }
119
120 void Greeter::writeHeader(int id, int length)
121 {
122     writeInt(id);
123     writeInt(length);
124 }
125
126 void Greeter::flush()
127 {
128     fsync(d->toServerFd);
129 }
130
131 int Greeter::getPacketLength()
132 {
133     int offset = intLength();
134     return readInt(&offset);
135 }
136
137 int Greeter::readInt(int *offset)
138 {
139     if(d->nRead - *offset < intLength()) {
140         qDebug() << "Not enough space for int, need " << intLength() << ", got " << (d->nRead - *offset);
141         return 0;
142     }
143
144     char *buffer = d->readBuffer + *offset;
145     int value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
146     *offset += intLength();
147     return value;
148 }
149
150 QString Greeter::readString(int *offset)
151 {
152     int length = readInt(offset);
153     if(d->nRead - *offset < length) {
154         qDebug() << "Not enough space for string, need " << length << ", got " << (d->nRead - *offset);
155         return "";
156     }
157     char *start = d->readBuffer + *offset;
158     *offset += length;
159     return QString::fromUtf8(start, length);
160 }
161
162 void Greeter::connectToServer()
163 {
164     QDBusConnection busType = QDBusConnection::systemBus();
165     QString ldmBus(qgetenv("LIGHTDM_BUS"));
166     if(ldmBus == QLatin1String("SESSION")) {
167         busType = QDBusConnection::sessionBus();
168     }
169
170     char* fd = getenv("LIGHTDM_TO_SERVER_FD");
171     if(!fd) {
172        qDebug() << "No LIGHTDM_TO_SERVER_FD environment variable";
173        return;
174     }
175     d->toServerFd = atoi(fd);
176
177     qDebug() << "***connecting to server";
178     QFile toServer;
179     qDebug() << toServer.open(d->toServerFd, QIODevice::WriteOnly);
180
181     fd = getenv("LIGHTDM_FROM_SERVER_FD");
182     if(!fd) {
183        qDebug() << "No LIGHTDM_FROM_SERVER_FD environment variable";
184        return;
185     }
186     d->fromServerFd = atoi(fd);
187
188     d->n = new QSocketNotifier(d->fromServerFd, QSocketNotifier::Read);
189     connect(d->n, SIGNAL(activated(int)), this, SLOT(onRead(int)));
190
191     qDebug() << "Connecting to display manager...";
192     writeHeader(GREETER_MESSAGE_CONNECT, stringLength(VERSION));
193     writeString(VERSION);
194     flush();
195 }
196
197 void Greeter::authenticate(const QString &username)
198 {
199     d->inAuthentication = true;
200     d->isAuthenticated = false;
201     d->cancellingAuthentication = false;
202     d->authenticationUser = username;
203     qDebug() << "Starting authentication for user " << username << "...";
204     writeHeader(GREETER_MESSAGE_AUTHENTICATE, intLength() + stringLength(username));
205     d->authenticateSequenceNumber++;
206     writeInt(d->authenticateSequenceNumber);
207     writeString(username);
208     flush();
209 }
210
211 void Greeter::authenticateAsGuest()
212 {
213     d->authenticateSequenceNumber++;
214     d->inAuthentication = true;
215     d->isAuthenticated = false;
216     d->cancellingAuthentication = false;
217     d->authenticationUser = "";
218     qDebug() << "Starting authentication for guest account";
219     writeHeader(GREETER_MESSAGE_AUTHENTICATE_AS_GUEST, intLength());
220     writeInt(d->authenticateSequenceNumber);
221     flush();
222 }
223
224 void Greeter::respond(const QString &response)
225 {
226     qDebug() << "Providing response to display manager";
227     writeHeader(GREETER_MESSAGE_CONTINUE_AUTHENTICATION, intLength() + stringLength(response));
228     // FIXME: Could be multiple response required
229     writeInt(1);
230     writeString(response);
231     flush();
232 }
233
234 void Greeter::cancelAuthentication()
235 {
236     qDebug() << "Cancelling authentication";
237     d->cancellingAuthentication = true;
238     writeHeader(GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0);
239     flush();
240 }
241
242 bool Greeter::inAuthentication() const
243 {
244     return d->inAuthentication;
245 }
246
247 bool Greeter::isAuthenticated() const
248 {
249     return d->isAuthenticated;
250 }
251
252 QString Greeter::authenticationUser() const
253 {
254     return d->authenticationUser;
255 }
256
257 void Greeter::startSession(const QString &session)
258 {
259     qDebug() << "Starting session " << session;
260     writeHeader(GREETER_MESSAGE_START_SESSION, stringLength(session));
261     writeString(session);
262     flush();
263 }
264
265 void Greeter::onRead(int fd)
266 {
267     //qDebug() << "Reading from server";
268
269     int nToRead = HEADER_SIZE;
270     if(d->nRead >= HEADER_SIZE)
271         nToRead += getPacketLength();
272
273     ssize_t nRead;
274     nRead = read(fd, d->readBuffer + d->nRead, nToRead - d->nRead);
275     if(nRead < 0)
276     {
277         qDebug() << "Error reading from server";
278         return;
279     }
280     if (nRead == 0)
281     {
282         qDebug() << "EOF reading from server";
283         return;
284     }
285
286     //qDebug() << "Read " << nRead << "octets";
287     d->nRead += nRead;
288     if(d->nRead != nToRead)
289         return;
290
291     /* If have header, rerun for content */
292     if(d->nRead == HEADER_SIZE)
293     {
294         nToRead = getPacketLength();
295         if(nToRead > 0)
296         {
297             d->readBuffer = (char *)realloc(d->readBuffer, HEADER_SIZE + nToRead);
298             onRead(fd);
299             return;
300         }
301     }
302
303     int offset = 0;
304     int id = readInt(&offset);
305     int length = readInt(&offset);
306     int nMessages, sequenceNumber, returnCode;
307     QString version, username;
308     QString hintString = "";
309     switch(id)
310     {
311     case SERVER_MESSAGE_CONNECTED:
312         version = readString(&offset);
313         while (offset < length)
314         {
315             QString name = readString(&offset);
316             QString value = readString(&offset);
317             hintString.append (" ");
318             hintString.append (name);
319             hintString.append ("=");
320             hintString.append (value);
321         }
322
323         qDebug() << "Connected version=" << version << hintString;
324
325         emit connected();
326         break;
327     case SERVER_MESSAGE_QUIT:
328         qDebug() << "Got quit request from server";
329         emit quit();
330         break;
331     case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
332         sequenceNumber = readInt(&offset);
333
334         if (sequenceNumber == d->authenticateSequenceNumber &&
335             !d->cancellingAuthentication)
336         {
337             nMessages = readInt(&offset);
338             qDebug() << "Prompt user with " << nMessages << " message(s)";
339             for(int i = 0; i < nMessages; i++)
340             {
341                 int msg_style = readInt (&offset);
342                 QString msg = readString (&offset);
343
344                 // FIXME: Should stop on prompts?
345                 switch (msg_style)
346                 {
347                 case PAM_PROMPT_ECHO_OFF:
348                     emit showPrompt(msg, PROMPT_TYPE_SECRET);
349                     break;
350                 case PAM_PROMPT_ECHO_ON:
351                     emit showPrompt(msg, PROMPT_TYPE_QUESTION);
352                     break;
353                 case PAM_ERROR_MSG:
354                     emit showMessage(msg, MESSAGE_TYPE_ERROR);
355                     break;
356                 case PAM_TEXT_INFO:
357                     emit showMessage(msg, MESSAGE_TYPE_INFO);
358                     break;
359                 }
360             }
361         }
362         break;
363     case SERVER_MESSAGE_END_AUTHENTICATION:
364         sequenceNumber = readInt(&offset);
365         returnCode = readInt(&offset);
366
367         if (sequenceNumber == d->authenticateSequenceNumber)
368         {
369             qDebug() << "Authentication complete with return code " << returnCode;
370             d->cancellingAuthentication = false;
371             d->isAuthenticated = (returnCode == 0);
372             if(!d->isAuthenticated) {
373                 d->authenticationUser = "";
374             }
375             emit authenticationComplete();
376             d->inAuthentication = false;
377         }
378         else
379             qDebug () << "Ignoring end authentication with invalid sequence number " << sequenceNumber;
380         break;
381     case SERVER_MESSAGE_SESSION_FAILED:
382         qDebug() << "Session failed to start";
383         emit sessionFailed();
384         break;
385     default:
386         qDebug() << "Unknown message from server: " << id;
387     }
388
389     d->nRead = 0;
390 }
391
392 QString Greeter::getHint(QString name) const
393 {
394     return d->hints.value (name);
395 }
396
397 QString Greeter::defaultSessionHint() const
398 {
399     return getHint ("default-session");
400 }
401
402 bool Greeter::hideUsersHint() const
403 {
404     return d->hints.value ("hide-users", "true") == "true";
405 }
406
407 bool Greeter::hasGuestAccountHint() const
408 {
409     return d->hints.value ("has-guest-account", "false") == "true";
410 }
411
412 QString Greeter::selectUserHint() const
413 {
414     return getHint ("select-user");
415 }
416
417 bool Greeter::selectGuestHint() const
418 {
419     return d->hints.value ("select-guest", "false") == "true";
420 }
421
422 QString Greeter::autologinUserHint() const
423 {
424     return getHint ("autologin-user");
425 }
426
427 bool Greeter::autologinGuestHint() const
428 {
429     return d->hints.value ("autologin-guest", "false") == "true";
430 }
431
432 int Greeter::autologinTimeoutHint() const
433 {
434     return d->hints.value ("autologin-timeout", "0").toInt ();
435 }