signon 8.54

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List
- File Members

pluginproxy.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of signon
3 *
4 * Copyright (C) 2009-2010 Nokia Corporation.
5 *
6 * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * version 2.1 as published by the Free Software Foundation.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 */
22
23 #include "pluginproxy.h"
24
25 #include <sys/types.h>
26 #include <pwd.h>
27 #include <unistd.h>
28
29 #include <QStringList>
30 #include <QThreadStorage>
31 #include <QThread>
32 #include <QDataStream>
33
34 #include "signond-common.h"
35 #include "SignOn/uisessiondata_priv.h"
36 #include "SignOn/signonplugincommon.h"
37
38 /*
39 * TODO: remove the "SignOn/authpluginif.h" include below after the removal
40 * of the deprecated error handling (needed here only for the deprecated
41 * AuthPluginError::PLUGIN_ERROR_GENERAL).
42 */
43 #include "SignOn/authpluginif.h"
44
45 // signon-plugins-common
46 #include "SignOn/blobiohandler.h"
47 #include "SignOn/ipc.h"
48
49 using namespace SignOn;
50
51 #define REMOTEPLUGIN_BIN_PATH QLatin1String("signonpluginprocess")
52 #define PLUGINPROCESS_START_TIMEOUT 5000
53 #define PLUGINPROCESS_STOP_TIMEOUT 1000
54
55 using namespace SignOn;
56
57 namespace SignonDaemonNS {
58
59 /* ---------------------- PluginProcess ---------------------- */
60
61 PluginProcess::PluginProcess(QObject *parent):
62 QProcess(parent)
63 {
64 }
65
66 PluginProcess::~PluginProcess()
67 {
68 }
69
70 /* ---------------------- PluginProxy ---------------------- */
71
72 PluginProxy::PluginProxy(QString type, QObject *parent):
73 QObject(parent)
74 {
75 TRACE();
76
77 m_type = type;
78 m_isProcessing = false;
79 m_isResultObtained = false;
80 m_currentResultOperation = -1;
81 m_process = new PluginProcess(this);
82
83 #ifdef SIGNOND_TRACE
84 if (criticalsEnabled()) {
85 const char *level = debugEnabled() ? "2" : "1";
86 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
87 env.insert(QLatin1String("SSO_DEBUG"), QLatin1String(level));
88 m_process->setProcessEnvironment(env);
89 }
90 #endif
91
92 connect(m_process, SIGNAL(readyReadStandardError()),
93 this, SLOT(onReadStandardError()));
94
95 /*
96 * TODO: some error handling should be added here, at least remove of
97 * current request data from the top of the queue and reply an error code
98 */
99 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
100 this, SLOT(onExit(int, QProcess::ExitStatus)));
101 connect(m_process, SIGNAL(error(QProcess::ProcessError)),
102 this, SLOT(onError(QProcess::ProcessError)));
103 }
104
105 PluginProxy::~PluginProxy()
106 {
107 if (m_process != NULL &&
108 m_process->state() != QProcess::NotRunning)
109 {
110 if (m_isProcessing)
111 cancel();
112
113 stop();
114
115 /* Closing the write channel ensures that the plugin process
116 * will not get stuck on the next read.
117 */
118 m_process->closeWriteChannel();
119
120 if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT)) {
121 qCritical() << "The signon plugin does not react on demand to "
122 "stop: need to kill it!!!";
123 m_process->kill();
124
125 if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT))
126 {
127 if (m_process->pid()) {
128 qCritical() << "The signon plugin seems to ignore kill(), "
129 "killing it from command line";
130 QString killProcessCommand(QString::fromLatin1("kill -9 %1").arg(m_process->pid()));
131 QProcess::execute(killProcessCommand);
132 }
133 }
134 }
135 }
136 }
137
138 PluginProxy* PluginProxy::createNewPluginProxy(const QString &type)
139 {
140 PluginProxy *pp = new PluginProxy(type);
141
142 QStringList args = QStringList() << pp->m_type;
143 pp->m_process->start(REMOTEPLUGIN_BIN_PATH, args);
144
145 QByteArray tmp;
146
147 if (!pp->waitForStarted(PLUGINPROCESS_START_TIMEOUT)) {
148 TRACE() << "The process cannot be started";
149 delete pp;
150 return NULL;
151 }
152
153 if (!pp->readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT)) {
154 TRACE() << "The process cannot load plugin";
155 delete pp;
156 return NULL;
157 }
158
159 if (debugEnabled()) {
160 QString pluginType = pp->queryType();
161 if (pluginType != pp->m_type) {
162 BLAME() << QString::fromLatin1("Plugin returned type '%1', "
163 "expected '%2'").
164 arg(pluginType).arg(pp->m_type);
165 }
166 }
167 pp->m_mechanisms = pp->queryMechanisms();
168
169 connect(pp->m_process, SIGNAL(readyRead()),
170 pp, SLOT(onReadStandardOutput()));
171
172 TRACE() << "The process is started";
173 return pp;
174 }
175
176 bool PluginProxy::process(const QVariantMap &inData,
177 const QString &mechanism)
178 {
179 if (!restartIfRequired())
180 return false;
181
182 m_isResultObtained = false;
183 QVariant value = inData.value(SSOUI_KEY_UIPOLICY);
184 m_uiPolicy = value.toInt();
185
186 QDataStream in(m_process);
187 in << (quint32)PLUGIN_OP_PROCESS;
188 in << mechanism;
189
190 m_blobIOHandler->sendData(inData);
191
192 m_isProcessing = true;
193 return true;
194 }
195
196 bool PluginProxy::processUi(const QVariantMap &inData)
197 {
198 TRACE();
199
200 if (!restartIfRequired())
201 return false;
202
203 QDataStream in(m_process);
204
205 in << (quint32)PLUGIN_OP_PROCESS_UI;
206
207 m_blobIOHandler->sendData(inData);
208
209 m_isProcessing = true;
210
211 return true;
212 }
213
214 bool PluginProxy::processRefresh(const QVariantMap &inData)
215 {
216 TRACE();
217
218 if (!restartIfRequired())
219 return false;
220
221 QDataStream in(m_process);
222
223 in << (quint32)PLUGIN_OP_REFRESH;
224
225 m_blobIOHandler->sendData(inData);
226
227 m_isProcessing = true;
228
229 return true;
230 }
231
232 void PluginProxy::cancel()
233 {
234 TRACE();
235 QDataStream in(m_process);
236 in << (quint32)PLUGIN_OP_CANCEL;
237 }
238
239 void PluginProxy::stop()
240 {
241 TRACE();
242 QDataStream in(m_process);
243 in << (quint32)PLUGIN_OP_STOP;
244 }
245
246 bool PluginProxy::readOnReady(QByteArray &buffer, int timeout)
247 {
248 bool ready = m_process->waitForReadyRead(timeout);
249
250 if (ready) {
251 if (!m_process->bytesAvailable())
252 return false;
253
254 while (m_process->bytesAvailable())
255 buffer += m_process->readAllStandardOutput();
256 }
257
258 return ready;
259 }
260
261 bool PluginProxy::isProcessing()
262 {
263 return m_isProcessing;
264 }
265
266 void PluginProxy::blobIOError()
267 {
268 TRACE();
269 disconnect(m_blobIOHandler, SIGNAL(error()), this, SLOT(blobIOError()));
270 stop();
271
272 connect(m_process, SIGNAL(readyRead()), this, SLOT(onReadStandardOutput()));
273 emit processError(
274 (int)Error::InternalServer,
275 QLatin1String("Failed to I/O session data to/from the authentication "
276 "plugin."));
277 }
278
279 bool PluginProxy::isResultOperationCodeValid(const int opCode) const
280 {
281 if (opCode == PLUGIN_RESPONSE_RESULT
282 || opCode == PLUGIN_RESPONSE_STORE
283 || opCode == PLUGIN_RESPONSE_ERROR
284 || opCode == PLUGIN_RESPONSE_SIGNAL
285 || opCode == PLUGIN_RESPONSE_UI
286 || opCode == PLUGIN_RESPONSE_REFRESHED) return true;
287
288 return false;
289 }
290
291 void PluginProxy::onReadStandardOutput()
292 {
293 disconnect(m_process, SIGNAL(readyRead()),
294 this, SLOT(onReadStandardOutput()));
295
296 if (!m_process->bytesAvailable()) {
297 qCritical() << "No information available on process";
298 m_isProcessing = false;
299 emit processError(Error::InternalServer, QString());
300 return;
301 }
302
303 QDataStream reader(m_process);
304 reader >> m_currentResultOperation;
305
306 TRACE() << "PROXY RESULT OPERATION:" << m_currentResultOperation;
307
308 if (!isResultOperationCodeValid(m_currentResultOperation)) {
309 TRACE() << "Unknown operation code - skipping.";
310
311 //flushing the stdin channel
312 Q_UNUSED(m_process->readAllStandardOutput());
313
314 connect(m_process, SIGNAL(readyRead()),
315 this, SLOT(onReadStandardOutput()));
316 return;
317 }
318
319 if (m_currentResultOperation != PLUGIN_RESPONSE_SIGNAL &&
320 m_currentResultOperation != PLUGIN_RESPONSE_ERROR) {
321
322 connect(m_blobIOHandler, SIGNAL(error()),
323 this, SLOT(blobIOError()));
324
325 int expectedDataSize = 0;
326 reader >> expectedDataSize;
327 TRACE() << "PROXY EXPECTED DATA SIZE:" << expectedDataSize;
328
329 m_blobIOHandler->receiveData(expectedDataSize);
330 } else {
331 handlePluginResponse(m_currentResultOperation);
332 }
333 }
334
335 void PluginProxy::sessionDataReceived(const QVariantMap &map)
336 {
337 handlePluginResponse(m_currentResultOperation, map);
338 }
339
340 void PluginProxy::handlePluginResponse(const quint32 resultOperation,
341 const QVariantMap &sessionDataMap)
342 {
343 TRACE() << resultOperation;
344
345 if (resultOperation == PLUGIN_RESPONSE_RESULT) {
346 TRACE() << "PLUGIN_RESPONSE_RESULT";
347
348 m_isProcessing = false;
349
350 if (!m_isResultObtained)
351 emit processResultReply(sessionDataMap);
352 else
353 BLAME() << "Unexpected plugin response: ";
354
355 m_isResultObtained = true;
356 } else if (resultOperation == PLUGIN_RESPONSE_STORE) {
357 TRACE() << "PLUGIN_RESPONSE_STORE";
358
359 if (!m_isResultObtained)
360 emit processStore(sessionDataMap);
361 else
362 BLAME() << "Unexpected plugin store: ";
363
364 } else if (resultOperation == PLUGIN_RESPONSE_UI) {
365 TRACE() << "PLUGIN_RESPONSE_UI";
366
367 if (!m_isResultObtained) {
368 bool allowed = true;
369
370 if (m_uiPolicy == NoUserInteractionPolicy)
371 allowed = false;
372
373 if (m_uiPolicy == ValidationPolicy) {
374 bool credentialsQueried =
375 (sessionDataMap.contains(SSOUI_KEY_QUERYUSERNAME)
376 || sessionDataMap.contains(SSOUI_KEY_QUERYPASSWORD));
377
378 bool captchaQueried =
379 (sessionDataMap.contains(SSOUI_KEY_CAPTCHAIMG)
380 || sessionDataMap.contains(SSOUI_KEY_CAPTCHAURL));
381
382 if (credentialsQueried && !captchaQueried)
383 allowed = false;
384 }
385
386 if (!allowed) {
387 //set error and return;
388 TRACE() << "ui policy prevented ui launch";
389
390 QVariantMap nonConstMap = sessionDataMap;
391 nonConstMap.insert(SSOUI_KEY_ERROR, QUERY_ERROR_FORBIDDEN);
392 processUi(nonConstMap);
393 } else {
394 TRACE() << "open ui";
395 emit processUiRequest(sessionDataMap);
396 }
397 } else {
398 BLAME() << "Unexpected plugin ui response: ";
399 }
400 } else if (resultOperation == PLUGIN_RESPONSE_REFRESHED) {
401 TRACE() << "PLUGIN_RESPONSE_REFRESHED";
402
403 if (!m_isResultObtained)
404 emit processRefreshRequest(sessionDataMap);
405 else
406 BLAME() << "Unexpected plugin ui response: ";
407 } else if (resultOperation == PLUGIN_RESPONSE_ERROR) {
408 TRACE() << "PLUGIN_RESPONSE_ERROR";
409 quint32 err;
410 QString errorMessage;
411
412 QDataStream stream(m_process);
413 stream >> err;
414 stream >> errorMessage;
415 m_isProcessing = false;
416
417 if (!m_isResultObtained)
418 emit processError((int)err, errorMessage);
419 else
420 BLAME() << "Unexpected plugin error: " << errorMessage;
421
422 m_isResultObtained = true;
423 } else if (resultOperation == PLUGIN_RESPONSE_SIGNAL) {
424 TRACE() << "PLUGIN_RESPONSE_SIGNAL";
425 quint32 state;
426 QString message;
427
428 QDataStream stream(m_process);
429 stream >> state;
430 stream >> message;
431
432 if (!m_isResultObtained)
433 emit stateChanged((int)state, message);
434 else
435 BLAME() << "Unexpected plugin signal: " << state << message;
436 }
437
438 connect(m_process, SIGNAL(readyRead()), this, SLOT(onReadStandardOutput()));
439 }
440
441 void PluginProxy::onReadStandardError()
442 {
443 QString ba = QString::fromLatin1(m_process->readAllStandardError());
444 }
445
446 void PluginProxy::onExit(int exitCode, QProcess::ExitStatus exitStatus)
447 {
448 TRACE() << "Plugin process exit with code " << exitCode <<
449 " : " << exitStatus;
450
451 if (m_isProcessing || exitStatus == QProcess::CrashExit) {
452 qCritical() << "Challenge produces CRASH!";
453 emit processError(Error::InternalServer,
454 QLatin1String("plugin processed crashed"));
455 }
456 if (exitCode == 2) {
457 TRACE() << "plugin process terminated because cannot change user";
458 }
459
460 m_isProcessing = false;
461 }
462
463 void PluginProxy::onError(QProcess::ProcessError err)
464 {
465 TRACE() << "Error: " << err;
466 }
467
468 QString PluginProxy::queryType()
469 {
470 TRACE();
471
472 if (!restartIfRequired())
473 return QString();
474
475 QDataStream ds(m_process);
476 ds << (quint32)PLUGIN_OP_TYPE;
477
478 QByteArray buffer;
479 bool result;
480
481 if (!(result = readOnReady(buffer, PLUGINPROCESS_START_TIMEOUT)))
482 qCritical("PluginProxy returned NULL result");
483
484 QString type;
485 QDataStream out(buffer);
486 out >> type;
487 return type;
488 }
489
490 QStringList PluginProxy::queryMechanisms()
491 {
492 TRACE();
493
494 if (!restartIfRequired())
495 return QStringList();
496
497 QDataStream in(m_process);
498 in << (quint32)PLUGIN_OP_MECHANISMS;
499
500 QByteArray buffer;
501 QStringList strList;
502 bool result;
503
504 if ((result = readOnReady(buffer, PLUGINPROCESS_START_TIMEOUT))) {
505
506 QVariant mechanismsVar;
507 QDataStream out(buffer);
508
509 out >> mechanismsVar;
510 QVariantList varList = mechanismsVar.toList();
511
512 for (int i = 0; i < varList.count(); i++)
513 strList << varList.at(i).toString();
514
515 TRACE() << strList;
516 } else
517 qCritical("PluginProxy returned NULL result");
518
519 return strList;
520 }
521
522 bool PluginProxy::waitForStarted(int timeout)
523 {
524 if (!m_process->waitForStarted(timeout))
525 return false;
526
527 m_blobIOHandler = new BlobIOHandler(m_process, m_process, this);
528
529 connect(m_blobIOHandler,
530 SIGNAL(dataReceived(const QVariantMap &)),
531 this,
532 SLOT(sessionDataReceived(const QVariantMap &)));
533
534 QSocketNotifier *readNotifier =
535 new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read, this);
536
537 readNotifier->setEnabled(false);
538 m_blobIOHandler->setReadChannelSocketNotifier(readNotifier);
539
540 return true;
541 }
542
543 bool PluginProxy::waitForFinished(int timeout)
544 {
545 return m_process->waitForFinished(timeout);
546 }
547
548 bool PluginProxy::restartIfRequired()
549 {
550 if (m_process->state() == QProcess::NotRunning) {
551 TRACE() << "RESTART REQUIRED";
552 m_process->start(REMOTEPLUGIN_BIN_PATH, QStringList(m_type));
553
554 QByteArray tmp;
555 if (!waitForStarted(PLUGINPROCESS_START_TIMEOUT) ||
556 !readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT))
557 return false;
558 }
559 return true;
560 }
561
562 } //namespace SignonDaemonNS
REMOTEPLUGIN_BIN_PATH
#define REMOTEPLUGIN_BIN_PATH
Definition: pluginproxy.cpp:51
BLAME
#define BLAME()
Definition: debug.h:32
SignonDaemonNS::PluginProxy
Plugin proxy.
Definition: pluginproxy.h:56
PLUGINPROCESS_STOP_TIMEOUT
#define PLUGINPROCESS_STOP_TIMEOUT
Definition: pluginproxy.cpp:53
signond-common.h
pluginproxy.h
PLUGINPROCESS_START_TIMEOUT
#define PLUGINPROCESS_START_TIMEOUT
Definition: pluginproxy.cpp:52
process
RemotePluginProcess * process
Definition: main.cpp:43
TRACE
#define TRACE()
Definition: debug.h:28