|
signon
8.40
|
00001 /* 00002 * This file is part of signon 00003 * 00004 * Copyright (C) 2009-2010 Nokia Corporation. 00005 * 00006 * Contact: Alberto Mardegan <alberto.mardegan@canonical.com> 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public License 00010 * version 2.1 as published by the Free Software Foundation. 00011 * 00012 * This library is distributed in the hope that it will be useful, but 00013 * WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with this library; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 00020 * 02110-1301 USA 00021 */ 00022 00023 #include "pluginproxy.h" 00024 00025 #include <sys/types.h> 00026 #include <pwd.h> 00027 #include <unistd.h> 00028 00029 #include <QStringList> 00030 #include <QThreadStorage> 00031 #include <QThread> 00032 #include <QDataStream> 00033 00034 #include "signond-common.h" 00035 #include "SignOn/uisessiondata_priv.h" 00036 #include "SignOn/signonplugincommon.h" 00037 00038 /* 00039 * TODO: remove the "SignOn/authpluginif.h" include below after the removal 00040 * of the deprecated error handling (needed here only for the deprecated 00041 * AuthPluginError::PLUGIN_ERROR_GENERAL). 00042 */ 00043 #include "SignOn/authpluginif.h" 00044 00045 // signon-plugins-common 00046 #include "SignOn/blobiohandler.h" 00047 #include "SignOn/ipc.h" 00048 00049 using namespace SignOn; 00050 00051 #define REMOTEPLUGIN_BIN_PATH QLatin1String("signonpluginprocess") 00052 #define PLUGINPROCESS_START_TIMEOUT 5000 00053 #define PLUGINPROCESS_STOP_TIMEOUT 1000 00054 00055 using namespace SignOn; 00056 00057 namespace SignonDaemonNS { 00058 00059 /* ---------------------- PluginProcess ---------------------- */ 00060 00061 PluginProcess::PluginProcess(QObject *parent): 00062 QProcess(parent) 00063 { 00064 } 00065 00066 PluginProcess::~PluginProcess() 00067 { 00068 } 00069 00070 /* ---------------------- PluginProxy ---------------------- */ 00071 00072 PluginProxy::PluginProxy(QString type, QObject *parent): 00073 QObject(parent) 00074 { 00075 TRACE(); 00076 00077 m_type = type; 00078 m_isProcessing = false; 00079 m_isResultObtained = false; 00080 m_currentResultOperation = -1; 00081 m_process = new PluginProcess(this); 00082 00083 #ifdef SIGNOND_TRACE 00084 if (criticalsEnabled()) { 00085 const char *level = debugEnabled() ? "2" : "1"; 00086 QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); 00087 env.insert(QLatin1String("SSO_DEBUG"), QLatin1String(level)); 00088 m_process->setProcessEnvironment(env); 00089 } 00090 #endif 00091 00092 connect(m_process, SIGNAL(readyReadStandardError()), 00093 this, SLOT(onReadStandardError())); 00094 00095 /* 00096 * TODO: some error handling should be added here, at least remove of 00097 * current request data from the top of the queue and reply an error code 00098 */ 00099 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), 00100 this, SLOT(onExit(int, QProcess::ExitStatus))); 00101 connect(m_process, SIGNAL(error(QProcess::ProcessError)), 00102 this, SLOT(onError(QProcess::ProcessError))); 00103 } 00104 00105 PluginProxy::~PluginProxy() 00106 { 00107 if (m_process != NULL && 00108 m_process->state() != QProcess::NotRunning) 00109 { 00110 if (m_isProcessing) 00111 cancel(); 00112 00113 stop(); 00114 00115 /* Closing the write channel ensures that the plugin process 00116 * will not get stuck on the next read. 00117 */ 00118 m_process->closeWriteChannel(); 00119 00120 if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT)) { 00121 qCritical() << "The signon plugin does not react on demand to " 00122 "stop: need to kill it!!!"; 00123 m_process->kill(); 00124 00125 if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT)) 00126 { 00127 if (m_process->pid()) { 00128 qCritical() << "The signon plugin seems to ignore kill(), " 00129 "killing it from command line"; 00130 QString killProcessCommand(QString::fromLatin1("kill -9 %1").arg(m_process->pid())); 00131 QProcess::execute(killProcessCommand); 00132 } 00133 } 00134 } 00135 } 00136 } 00137 00138 PluginProxy* PluginProxy::createNewPluginProxy(const QString &type) 00139 { 00140 PluginProxy *pp = new PluginProxy(type); 00141 00142 QStringList args = QStringList() << pp->m_type; 00143 pp->m_process->start(REMOTEPLUGIN_BIN_PATH, args); 00144 00145 QByteArray tmp; 00146 00147 if (!pp->waitForStarted(PLUGINPROCESS_START_TIMEOUT)) { 00148 TRACE() << "The process cannot be started"; 00149 delete pp; 00150 return NULL; 00151 } 00152 00153 if (!pp->readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT)) { 00154 TRACE() << "The process cannot load plugin"; 00155 delete pp; 00156 return NULL; 00157 } 00158 00159 if (debugEnabled()) { 00160 QString pluginType = pp->queryType(); 00161 if (pluginType != pp->m_type) { 00162 BLAME() << QString::fromLatin1("Plugin returned type '%1', " 00163 "expected '%2'"). 00164 arg(pluginType).arg(pp->m_type); 00165 } 00166 } 00167 pp->m_mechanisms = pp->queryMechanisms(); 00168 00169 connect(pp->m_process, SIGNAL(readyRead()), 00170 pp, SLOT(onReadStandardOutput())); 00171 00172 TRACE() << "The process is started"; 00173 return pp; 00174 } 00175 00176 bool PluginProxy::process(const QString &cancelKey, 00177 const QVariantMap &inData, 00178 const QString &mechanism) 00179 { 00180 if (!restartIfRequired()) 00181 return false; 00182 00183 m_isResultObtained = false; 00184 m_cancelKey = cancelKey; 00185 QVariant value = inData.value(SSOUI_KEY_UIPOLICY); 00186 m_uiPolicy = value.toInt(); 00187 00188 QDataStream in(m_process); 00189 in << (quint32)PLUGIN_OP_PROCESS; 00190 in << mechanism; 00191 00192 m_blobIOHandler->sendData(inData); 00193 00194 m_isProcessing = true; 00195 return true; 00196 } 00197 00198 bool PluginProxy::processUi(const QString &cancelKey, const QVariantMap &inData) 00199 { 00200 TRACE(); 00201 00202 if (!restartIfRequired()) 00203 return false; 00204 00205 m_cancelKey = cancelKey; 00206 00207 QDataStream in(m_process); 00208 00209 in << (quint32)PLUGIN_OP_PROCESS_UI; 00210 00211 m_blobIOHandler->sendData(inData); 00212 00213 m_isProcessing = true; 00214 00215 return true; 00216 } 00217 00218 bool PluginProxy::processRefresh(const QString &cancelKey, 00219 const QVariantMap &inData) 00220 { 00221 TRACE(); 00222 00223 if (!restartIfRequired()) 00224 return false; 00225 00226 m_cancelKey = cancelKey; 00227 00228 QDataStream in(m_process); 00229 00230 in << (quint32)PLUGIN_OP_REFRESH; 00231 00232 m_blobIOHandler->sendData(inData); 00233 00234 m_isProcessing = true; 00235 00236 return true; 00237 } 00238 00239 void PluginProxy::cancel() 00240 { 00241 TRACE(); 00242 QDataStream in(m_process); 00243 in << (quint32)PLUGIN_OP_CANCEL; 00244 } 00245 00246 void PluginProxy::stop() 00247 { 00248 TRACE(); 00249 QDataStream in(m_process); 00250 in << (quint32)PLUGIN_OP_STOP; 00251 } 00252 00253 bool PluginProxy::readOnReady(QByteArray &buffer, int timeout) 00254 { 00255 bool ready = m_process->waitForReadyRead(timeout); 00256 00257 if (ready) { 00258 if (!m_process->bytesAvailable()) 00259 return false; 00260 00261 while (m_process->bytesAvailable()) 00262 buffer += m_process->readAllStandardOutput(); 00263 } 00264 00265 return ready; 00266 } 00267 00268 bool PluginProxy::isProcessing() 00269 { 00270 return m_isProcessing; 00271 } 00272 00273 void PluginProxy::blobIOError() 00274 { 00275 TRACE(); 00276 disconnect(m_blobIOHandler, SIGNAL(error()), this, SLOT(blobIOError())); 00277 stop(); 00278 00279 connect(m_process, SIGNAL(readyRead()), this, SLOT(onReadStandardOutput())); 00280 emit processError( 00281 m_cancelKey, 00282 (int)Error::InternalServer, 00283 QLatin1String("Failed to I/O session data to/from the authentication " 00284 "plugin.")); 00285 } 00286 00287 bool PluginProxy::isResultOperationCodeValid(const int opCode) const 00288 { 00289 if (opCode == PLUGIN_RESPONSE_RESULT 00290 || opCode == PLUGIN_RESPONSE_STORE 00291 || opCode == PLUGIN_RESPONSE_ERROR 00292 || opCode == PLUGIN_RESPONSE_SIGNAL 00293 || opCode == PLUGIN_RESPONSE_UI 00294 || opCode == PLUGIN_RESPONSE_REFRESHED) return true; 00295 00296 return false; 00297 } 00298 00299 void PluginProxy::onReadStandardOutput() 00300 { 00301 disconnect(m_process, SIGNAL(readyRead()), 00302 this, SLOT(onReadStandardOutput())); 00303 00304 if (!m_process->bytesAvailable()) { 00305 qCritical() << "No information available on process"; 00306 m_isProcessing = false; 00307 emit processError(m_cancelKey, Error::InternalServer, QString()); 00308 return; 00309 } 00310 00311 QDataStream reader(m_process); 00312 reader >> m_currentResultOperation; 00313 00314 TRACE() << "PROXY RESULT OPERATION:" << m_currentResultOperation; 00315 00316 if (!isResultOperationCodeValid(m_currentResultOperation)) { 00317 TRACE() << "Unknown operation code - skipping."; 00318 00319 //flushing the stdin channel 00320 Q_UNUSED(m_process->readAllStandardOutput()); 00321 00322 connect(m_process, SIGNAL(readyRead()), 00323 this, SLOT(onReadStandardOutput())); 00324 return; 00325 } 00326 00327 if (m_currentResultOperation != PLUGIN_RESPONSE_SIGNAL && 00328 m_currentResultOperation != PLUGIN_RESPONSE_ERROR) { 00329 00330 connect(m_blobIOHandler, SIGNAL(error()), 00331 this, SLOT(blobIOError())); 00332 00333 int expectedDataSize = 0; 00334 reader >> expectedDataSize; 00335 TRACE() << "PROXY EXPECTED DATA SIZE:" << expectedDataSize; 00336 00337 m_blobIOHandler->receiveData(expectedDataSize); 00338 } else { 00339 handlePluginResponse(m_currentResultOperation); 00340 } 00341 } 00342 00343 void PluginProxy::sessionDataReceived(const QVariantMap &map) 00344 { 00345 handlePluginResponse(m_currentResultOperation, map); 00346 } 00347 00348 void PluginProxy::handlePluginResponse(const quint32 resultOperation, 00349 const QVariantMap &sessionDataMap) 00350 { 00351 TRACE() << resultOperation; 00352 00353 if (resultOperation == PLUGIN_RESPONSE_RESULT) { 00354 TRACE() << "PLUGIN_RESPONSE_RESULT"; 00355 00356 m_isProcessing = false; 00357 00358 if (!m_isResultObtained) 00359 emit processResultReply(m_cancelKey, sessionDataMap); 00360 else 00361 BLAME() << "Unexpected plugin response: "; 00362 00363 m_isResultObtained = true; 00364 } else if (resultOperation == PLUGIN_RESPONSE_STORE) { 00365 TRACE() << "PLUGIN_RESPONSE_STORE"; 00366 00367 if (!m_isResultObtained) 00368 emit processStore(m_cancelKey, sessionDataMap); 00369 else 00370 BLAME() << "Unexpected plugin store: "; 00371 00372 } else if (resultOperation == PLUGIN_RESPONSE_UI) { 00373 TRACE() << "PLUGIN_RESPONSE_UI"; 00374 00375 if (!m_isResultObtained) { 00376 bool allowed = true; 00377 00378 if (m_uiPolicy == NoUserInteractionPolicy) 00379 allowed = false; 00380 00381 if (m_uiPolicy == ValidationPolicy) { 00382 bool credentialsQueried = 00383 (sessionDataMap.contains(SSOUI_KEY_QUERYUSERNAME) 00384 || sessionDataMap.contains(SSOUI_KEY_QUERYPASSWORD)); 00385 00386 bool captchaQueried = 00387 (sessionDataMap.contains(SSOUI_KEY_CAPTCHAIMG) 00388 || sessionDataMap.contains(SSOUI_KEY_CAPTCHAURL)); 00389 00390 if (credentialsQueried && !captchaQueried) 00391 allowed = false; 00392 } 00393 00394 if (!allowed) { 00395 //set error and return; 00396 TRACE() << "ui policy prevented ui launch"; 00397 00398 QVariantMap nonConstMap = sessionDataMap; 00399 nonConstMap.insert(SSOUI_KEY_ERROR, QUERY_ERROR_FORBIDDEN); 00400 processUi(m_cancelKey, nonConstMap); 00401 } else { 00402 TRACE() << "open ui"; 00403 emit processUiRequest(m_cancelKey, sessionDataMap); 00404 } 00405 } else { 00406 BLAME() << "Unexpected plugin ui response: "; 00407 } 00408 } else if (resultOperation == PLUGIN_RESPONSE_REFRESHED) { 00409 TRACE() << "PLUGIN_RESPONSE_REFRESHED"; 00410 00411 if (!m_isResultObtained) 00412 emit processRefreshRequest(m_cancelKey, sessionDataMap); 00413 else 00414 BLAME() << "Unexpected plugin ui response: "; 00415 } else if (resultOperation == PLUGIN_RESPONSE_ERROR) { 00416 TRACE() << "PLUGIN_RESPONSE_ERROR"; 00417 quint32 err; 00418 QString errorMessage; 00419 00420 QDataStream stream(m_process); 00421 stream >> err; 00422 stream >> errorMessage; 00423 m_isProcessing = false; 00424 00425 if (!m_isResultObtained) 00426 emit processError(m_cancelKey, (int)err, errorMessage); 00427 else 00428 BLAME() << "Unexpected plugin error: " << errorMessage; 00429 00430 m_isResultObtained = true; 00431 } else if (resultOperation == PLUGIN_RESPONSE_SIGNAL) { 00432 TRACE() << "PLUGIN_RESPONSE_SIGNAL"; 00433 quint32 state; 00434 QString message; 00435 00436 QDataStream stream(m_process); 00437 stream >> state; 00438 stream >> message; 00439 00440 if (!m_isResultObtained) 00441 emit stateChanged(m_cancelKey, (int)state, message); 00442 else 00443 BLAME() << "Unexpected plugin signal: " << state << message; 00444 } 00445 00446 connect(m_process, SIGNAL(readyRead()), this, SLOT(onReadStandardOutput())); 00447 } 00448 00449 void PluginProxy::onReadStandardError() 00450 { 00451 QString ba = QString::fromLatin1(m_process->readAllStandardError()); 00452 } 00453 00454 void PluginProxy::onExit(int exitCode, QProcess::ExitStatus exitStatus) 00455 { 00456 TRACE() << "Plugin process exit with code " << exitCode << 00457 " : " << exitStatus; 00458 00459 if (m_isProcessing || exitStatus == QProcess::CrashExit) { 00460 qCritical() << "Challenge produces CRASH!"; 00461 emit processError(m_cancelKey, Error::InternalServer, 00462 QLatin1String("plugin processed crashed")); 00463 } 00464 if (exitCode == 2) { 00465 TRACE() << "plugin process terminated because cannot change user"; 00466 } 00467 00468 m_isProcessing = false; 00469 } 00470 00471 void PluginProxy::onError(QProcess::ProcessError err) 00472 { 00473 TRACE() << "Error: " << err; 00474 } 00475 00476 QString PluginProxy::queryType() 00477 { 00478 TRACE(); 00479 00480 if (!restartIfRequired()) 00481 return QString(); 00482 00483 QDataStream ds(m_process); 00484 ds << (quint32)PLUGIN_OP_TYPE; 00485 00486 QByteArray buffer; 00487 bool result; 00488 00489 if (!(result = readOnReady(buffer, PLUGINPROCESS_START_TIMEOUT))) 00490 qCritical("PluginProxy returned NULL result"); 00491 00492 QString type; 00493 QDataStream out(buffer); 00494 out >> type; 00495 return type; 00496 } 00497 00498 QStringList PluginProxy::queryMechanisms() 00499 { 00500 TRACE(); 00501 00502 if (!restartIfRequired()) 00503 return QStringList(); 00504 00505 QDataStream in(m_process); 00506 in << (quint32)PLUGIN_OP_MECHANISMS; 00507 00508 QByteArray buffer; 00509 QStringList strList; 00510 bool result; 00511 00512 if ((result = readOnReady(buffer, PLUGINPROCESS_START_TIMEOUT))) { 00513 00514 QVariant mechanismsVar; 00515 QDataStream out(buffer); 00516 00517 out >> mechanismsVar; 00518 QVariantList varList = mechanismsVar.toList(); 00519 00520 for (int i = 0; i < varList.count(); i++) 00521 strList << varList.at(i).toString(); 00522 00523 TRACE() << strList; 00524 } else 00525 qCritical("PluginProxy returned NULL result"); 00526 00527 return strList; 00528 } 00529 00530 bool PluginProxy::waitForStarted(int timeout) 00531 { 00532 if (!m_process->waitForStarted(timeout)) 00533 return false; 00534 00535 m_blobIOHandler = new BlobIOHandler(m_process, m_process, this); 00536 00537 connect(m_blobIOHandler, 00538 SIGNAL(dataReceived(const QVariantMap &)), 00539 this, 00540 SLOT(sessionDataReceived(const QVariantMap &))); 00541 00542 QSocketNotifier *readNotifier = 00543 new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read, this); 00544 00545 readNotifier->setEnabled(false); 00546 m_blobIOHandler->setReadChannelSocketNotifier(readNotifier); 00547 00548 return true; 00549 } 00550 00551 bool PluginProxy::waitForFinished(int timeout) 00552 { 00553 return m_process->waitForFinished(timeout); 00554 } 00555 00556 bool PluginProxy::restartIfRequired() 00557 { 00558 if (m_process->state() == QProcess::NotRunning) { 00559 TRACE() << "RESTART REQUIRED"; 00560 m_process->start(REMOTEPLUGIN_BIN_PATH, QStringList(m_type)); 00561 00562 QByteArray tmp; 00563 if (!waitForStarted(PLUGINPROCESS_START_TIMEOUT) || 00564 !readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT)) 00565 return false; 00566 } 00567 return true; 00568 } 00569 00570 } //namespace SignonDaemonNS