25 #include <sys/types.h>
29 #include <QStringList>
30 #include <QThreadStorage>
32 #include <QDataStream>
35 #include "SignOn/uisessiondata_priv.h"
36 #include "SignOn/signonplugincommon.h"
43 #include "SignOn/authpluginif.h"
46 #include "SignOn/blobiohandler.h"
47 #include "SignOn/ipc.h"
49 using namespace SignOn;
51 #define REMOTEPLUGIN_BIN_PATH QLatin1String("signonpluginprocess")
52 #define PLUGINPROCESS_START_TIMEOUT 5000
53 #define PLUGINPROCESS_STOP_TIMEOUT 1000
55 using namespace SignOn;
57 namespace SignonDaemonNS {
61 PluginProcess::PluginProcess(QObject *parent):
66 PluginProcess::~PluginProcess()
72 PluginProxy::PluginProxy(QString type, QObject *parent):
78 m_isProcessing =
false;
79 m_isResultObtained =
false;
80 m_currentResultOperation = -1;
81 m_process =
new PluginProcess(
this);
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);
92 connect(m_process, SIGNAL(readyReadStandardError()),
93 this, SLOT(onReadStandardError()));
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)));
105 PluginProxy::~PluginProxy()
107 if (m_process != NULL &&
108 m_process->state() != QProcess::NotRunning)
118 m_process->closeWriteChannel();
121 qCritical() <<
"The signon plugin does not react on demand to "
122 "stop: need to kill it!!!";
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);
138 PluginProxy* PluginProxy::createNewPluginProxy(
const QString &type)
142 QStringList args = QStringList() << pp->m_type;
148 TRACE() <<
"The process cannot be started";
154 TRACE() <<
"The process cannot load plugin";
159 if (debugEnabled()) {
160 QString pluginType = pp->queryType();
161 if (pluginType != pp->m_type) {
162 BLAME() << QString::fromLatin1(
"Plugin returned type '%1', "
164 arg(pluginType).arg(pp->m_type);
167 pp->m_mechanisms = pp->queryMechanisms();
169 connect(pp->m_process, SIGNAL(readyRead()),
170 pp, SLOT(onReadStandardOutput()));
172 TRACE() <<
"The process is started";
177 const QVariantMap &inData,
178 const QString &mechanism)
180 if (!restartIfRequired())
183 m_isResultObtained =
false;
184 m_cancelKey = cancelKey;
185 QVariant value = inData.value(SSOUI_KEY_UIPOLICY);
186 m_uiPolicy = value.toInt();
188 QDataStream in(m_process);
189 in << (quint32)PLUGIN_OP_PROCESS;
192 m_blobIOHandler->sendData(inData);
194 m_isProcessing =
true;
198 bool PluginProxy::processUi(
const QString &cancelKey,
const QVariantMap &inData)
202 if (!restartIfRequired())
205 m_cancelKey = cancelKey;
207 QDataStream in(m_process);
209 in << (quint32)PLUGIN_OP_PROCESS_UI;
211 m_blobIOHandler->sendData(inData);
213 m_isProcessing =
true;
218 bool PluginProxy::processRefresh(
const QString &cancelKey,
219 const QVariantMap &inData)
223 if (!restartIfRequired())
226 m_cancelKey = cancelKey;
228 QDataStream in(m_process);
230 in << (quint32)PLUGIN_OP_REFRESH;
232 m_blobIOHandler->sendData(inData);
234 m_isProcessing =
true;
239 void PluginProxy::cancel()
242 QDataStream in(m_process);
243 in << (quint32)PLUGIN_OP_CANCEL;
246 void PluginProxy::stop()
249 QDataStream in(m_process);
250 in << (quint32)PLUGIN_OP_STOP;
253 bool PluginProxy::readOnReady(QByteArray &buffer,
int timeout)
255 bool ready = m_process->waitForReadyRead(timeout);
258 if (!m_process->bytesAvailable())
261 while (m_process->bytesAvailable())
262 buffer += m_process->readAllStandardOutput();
268 bool PluginProxy::isProcessing()
270 return m_isProcessing;
273 void PluginProxy::blobIOError()
276 disconnect(m_blobIOHandler, SIGNAL(error()),
this, SLOT(blobIOError()));
279 connect(m_process, SIGNAL(readyRead()),
this, SLOT(onReadStandardOutput()));
282 (
int)Error::InternalServer,
283 QLatin1String(
"Failed to I/O session data to/from the authentication "
287 bool PluginProxy::isResultOperationCodeValid(
const int opCode)
const
289 if (opCode == PLUGIN_RESPONSE_RESULT
290 || opCode == PLUGIN_RESPONSE_STORE
291 || opCode == PLUGIN_RESPONSE_ERROR
292 || opCode == PLUGIN_RESPONSE_SIGNAL
293 || opCode == PLUGIN_RESPONSE_UI
294 || opCode == PLUGIN_RESPONSE_REFRESHED)
return true;
299 void PluginProxy::onReadStandardOutput()
301 disconnect(m_process, SIGNAL(readyRead()),
302 this, SLOT(onReadStandardOutput()));
304 if (!m_process->bytesAvailable()) {
305 qCritical() <<
"No information available on process";
306 m_isProcessing =
false;
307 emit processError(m_cancelKey, Error::InternalServer, QString());
311 QDataStream reader(m_process);
312 reader >> m_currentResultOperation;
314 TRACE() <<
"PROXY RESULT OPERATION:" << m_currentResultOperation;
316 if (!isResultOperationCodeValid(m_currentResultOperation)) {
317 TRACE() <<
"Unknown operation code - skipping.";
320 Q_UNUSED(m_process->readAllStandardOutput());
322 connect(m_process, SIGNAL(readyRead()),
323 this, SLOT(onReadStandardOutput()));
327 if (m_currentResultOperation != PLUGIN_RESPONSE_SIGNAL &&
328 m_currentResultOperation != PLUGIN_RESPONSE_ERROR) {
330 connect(m_blobIOHandler, SIGNAL(error()),
331 this, SLOT(blobIOError()));
333 int expectedDataSize = 0;
334 reader >> expectedDataSize;
335 TRACE() <<
"PROXY EXPECTED DATA SIZE:" << expectedDataSize;
337 m_blobIOHandler->receiveData(expectedDataSize);
339 handlePluginResponse(m_currentResultOperation);
343 void PluginProxy::sessionDataReceived(
const QVariantMap &map)
345 handlePluginResponse(m_currentResultOperation, map);
348 void PluginProxy::handlePluginResponse(
const quint32 resultOperation,
349 const QVariantMap &sessionDataMap)
351 TRACE() << resultOperation;
353 if (resultOperation == PLUGIN_RESPONSE_RESULT) {
354 TRACE() <<
"PLUGIN_RESPONSE_RESULT";
356 m_isProcessing =
false;
358 if (!m_isResultObtained)
359 emit processResultReply(m_cancelKey, sessionDataMap);
361 BLAME() <<
"Unexpected plugin response: ";
363 m_isResultObtained =
true;
364 }
else if (resultOperation == PLUGIN_RESPONSE_STORE) {
365 TRACE() <<
"PLUGIN_RESPONSE_STORE";
367 if (!m_isResultObtained)
368 emit processStore(m_cancelKey, sessionDataMap);
370 BLAME() <<
"Unexpected plugin store: ";
372 }
else if (resultOperation == PLUGIN_RESPONSE_UI) {
373 TRACE() <<
"PLUGIN_RESPONSE_UI";
375 if (!m_isResultObtained) {
378 if (m_uiPolicy == NoUserInteractionPolicy)
381 if (m_uiPolicy == ValidationPolicy) {
382 bool credentialsQueried =
383 (sessionDataMap.contains(SSOUI_KEY_QUERYUSERNAME)
384 || sessionDataMap.contains(SSOUI_KEY_QUERYPASSWORD));
386 bool captchaQueried =
387 (sessionDataMap.contains(SSOUI_KEY_CAPTCHAIMG)
388 || sessionDataMap.contains(SSOUI_KEY_CAPTCHAURL));
390 if (credentialsQueried && !captchaQueried)
396 TRACE() <<
"ui policy prevented ui launch";
398 QVariantMap nonConstMap = sessionDataMap;
399 nonConstMap.insert(SSOUI_KEY_ERROR, QUERY_ERROR_FORBIDDEN);
400 processUi(m_cancelKey, nonConstMap);
402 TRACE() <<
"open ui";
403 emit processUiRequest(m_cancelKey, sessionDataMap);
406 BLAME() <<
"Unexpected plugin ui response: ";
408 }
else if (resultOperation == PLUGIN_RESPONSE_REFRESHED) {
409 TRACE() <<
"PLUGIN_RESPONSE_REFRESHED";
411 if (!m_isResultObtained)
412 emit processRefreshRequest(m_cancelKey, sessionDataMap);
414 BLAME() <<
"Unexpected plugin ui response: ";
415 }
else if (resultOperation == PLUGIN_RESPONSE_ERROR) {
416 TRACE() <<
"PLUGIN_RESPONSE_ERROR";
418 QString errorMessage;
420 QDataStream stream(m_process);
422 stream >> errorMessage;
423 m_isProcessing =
false;
425 if (!m_isResultObtained)
426 emit processError(m_cancelKey, (
int)err, errorMessage);
428 BLAME() <<
"Unexpected plugin error: " << errorMessage;
430 m_isResultObtained =
true;
431 }
else if (resultOperation == PLUGIN_RESPONSE_SIGNAL) {
432 TRACE() <<
"PLUGIN_RESPONSE_SIGNAL";
436 QDataStream stream(m_process);
440 if (!m_isResultObtained)
441 emit stateChanged(m_cancelKey, (
int)state, message);
443 BLAME() <<
"Unexpected plugin signal: " << state << message;
446 connect(m_process, SIGNAL(readyRead()),
this, SLOT(onReadStandardOutput()));
449 void PluginProxy::onReadStandardError()
451 QString ba = QString::fromLatin1(m_process->readAllStandardError());
454 void PluginProxy::onExit(
int exitCode, QProcess::ExitStatus exitStatus)
456 TRACE() <<
"Plugin process exit with code " << exitCode <<
459 if (m_isProcessing || exitStatus == QProcess::CrashExit) {
460 qCritical() <<
"Challenge produces CRASH!";
461 emit processError(m_cancelKey, Error::InternalServer,
462 QLatin1String(
"plugin processed crashed"));
465 TRACE() <<
"plugin process terminated because cannot change user";
468 m_isProcessing =
false;
471 void PluginProxy::onError(QProcess::ProcessError err)
473 TRACE() <<
"Error: " << err;
476 QString PluginProxy::queryType()
480 if (!restartIfRequired())
483 QDataStream ds(m_process);
484 ds << (quint32)PLUGIN_OP_TYPE;
490 qCritical(
"PluginProxy returned NULL result");
493 QDataStream out(buffer);
498 QStringList PluginProxy::queryMechanisms()
502 if (!restartIfRequired())
503 return QStringList();
505 QDataStream in(m_process);
506 in << (quint32)PLUGIN_OP_MECHANISMS;
514 QVariant mechanismsVar;
515 QDataStream out(buffer);
517 out >> mechanismsVar;
518 QVariantList varList = mechanismsVar.toList();
520 for (
int i = 0; i < varList.count(); i++)
521 strList << varList.at(i).toString();
525 qCritical(
"PluginProxy returned NULL result");
530 bool PluginProxy::waitForStarted(
int timeout)
532 if (!m_process->waitForStarted(timeout))
535 m_blobIOHandler =
new BlobIOHandler(m_process, m_process,
this);
537 connect(m_blobIOHandler,
538 SIGNAL(dataReceived(
const QVariantMap &)),
540 SLOT(sessionDataReceived(
const QVariantMap &)));
542 QSocketNotifier *readNotifier =
543 new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read,
this);
545 readNotifier->setEnabled(
false);
546 m_blobIOHandler->setReadChannelSocketNotifier(readNotifier);
551 bool PluginProxy::waitForFinished(
int timeout)
553 return m_process->waitForFinished(timeout);
556 bool PluginProxy::restartIfRequired()
558 if (m_process->state() == QProcess::NotRunning) {
559 TRACE() <<
"RESTART REQUIRED";