<commit_before>/*
 * This file is part of signon
 *
 * Copyright (C) 2009-2010 Nokia Corporation.
 *
 * Contact: <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include "pluginproxy.h"

#include <sys/types.h>
#include <pwd.h>

#include <QStringList>
#include <QThreadStorage>
#include <QThread>
#include <QDataStream>

#include "signond-common.h"
#include "SignOn/uisessiondata_priv.h"
#include "SignOn/signonplugincommon.h"

/*
 *   TODO: remove the "SignOn/authpluginif.h" include below after the removal
 *         of the deprecated error handling (needed here only for the deprecated
 *         AuthPluginError::PLUGIN_ERROR_GENERAL).
 */
#include "SignOn/authpluginif.h"

// signon-plugins-common
#include "SignOn/blobiohandler.h"
#include "SignOn/encrypteddevice.h"
#include "SignOn/ipc.h"

#include <SignOnCrypto/Encryptor>

#include <openssl/rand.h>

using namespace SignOn;

//TODO get this from config
#define REMOTEPLUGIN_BIN_PATH QLatin1String("/usr/bin/signonpluginprocess")
#define PLUGINPROCESS_START_TIMEOUT 5000
#define PLUGINPROCESS_STOP_TIMEOUT 1000

using namespace SignOn;

namespace SignonDaemonNS {

    /* ---------------------- PluginProcess ---------------------- */

    PluginProcess::PluginProcess(QObject *parent) : QProcess(parent)
    {
    }

    PluginProcess::~PluginProcess()
    {
    }

    void PluginProcess::setupChildProcess()
    {
        // Drop all root privileges in the child process and switch to signon user
#ifndef NO_SIGNON_USER
        //get uid and gid
        struct passwd *passwdRecord = getpwnam("signon");
        if ( !passwdRecord ){
            fprintf(stderr, "failed to get user: signon\n");
            emit QProcess::finished(2, QProcess::NormalExit);
            exit(2);
        }
#ifdef SIGNOND_TRACE
        //this is run in remote plugin process, so trace should go to stderr
        fprintf(stderr, "got user: %s with uid: %d\n", passwdRecord->pw_name, passwdRecord->pw_uid);
#endif
        if (( ::setgid(passwdRecord->pw_gid))
                || (::setuid(passwdRecord->pw_uid))
                || (::getuid() != passwdRecord->pw_uid)
                ) {
            fprintf(stderr, "failed to set user: %s with uid: %d", passwdRecord->pw_name, passwdRecord->pw_uid);
            emit QProcess::finished(2, QProcess::NormalExit);
            exit(2);
        }
 #endif
    }

    /* ---------------------- PluginProxy ---------------------- */

    PluginProxy::PluginProxy(QString type, QObject *parent)
            : QObject(parent)
    {
        TRACE();

        m_type = type;
        m_isProcessing = false;
        m_isResultObtained = false;
        m_currentResultOperation = -1;
        m_process = new PluginProcess(this);

#ifdef SIGNOND_TRACE
        if (criticalsEnabled()) {
            const char *level = debugEnabled() ? "2" : "1";
            QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
            env.insert(QLatin1String("SSO_DEBUG"), QLatin1String(level));
            m_process->setProcessEnvironment(env);
        }
#endif

        connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(onReadStandardError()));

        /*
         * TODO: some error handling should be added here, at least remove of current
         * request data from the top of the queue and reply an error code
         * */
        connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onExit(int, QProcess::ExitStatus)));
        connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
    }

    PluginProxy::~PluginProxy()
    {
        if (m_process != NULL &&
            m_process->state() != QProcess::NotRunning)
        {
            if (m_isProcessing)
                cancel();

            stop();

            if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT)) {
                qCritical() << "The signon plugin does not react on demand to stop: need to kill it!!!";
                m_process->kill();

                if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT))
                {
                    if (m_process->pid()) {
                        qCritical() << "The signon plugin seems to ignore kill(), killing it from command line";
                        QString killProcessCommand(QString::fromLatin1("kill -9 %1").arg(m_process->pid()));
                        QProcess::execute(killProcessCommand);
                    }
                }
            }
        }
    }

    PluginProxy* PluginProxy::createNewPluginProxy(const QString &type)
    {
        PluginProxy *pp = new PluginProxy(type);

        QStringList args = QStringList() << pp->m_type;
        pp->m_process->start(REMOTEPLUGIN_BIN_PATH, args);

        QByteArray tmp;

        if (!pp->waitForStarted(PLUGINPROCESS_START_TIMEOUT)) {
            TRACE() << "The process cannot be started";
            delete pp;
            return NULL;
        }

        if (!pp->readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT)) {
            TRACE() << "The process cannot load plugin";
            delete pp;
            return NULL;
        }

        pp->m_type = pp->queryType();<commit_msg>signond: do not corrupt the plugin type variable<commit_after>/*
 * This file is part of signon
 *
 * Copyright (C) 2009-2010 Nokia Corporation.
 *
 * Contact: <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include "pluginproxy.h"

#include <sys/types.h>
#include <pwd.h>

#include <QStringList>
#include <QThreadStorage>
#include <QThread>
#include <QDataStream>

#include "signond-common.h"
#include "SignOn/uisessiondata_priv.h"
#include "SignOn/signonplugincommon.h"

/*
 *   TODO: remove the "SignOn/authpluginif.h" include below after the removal
 *         of the deprecated error handling (needed here only for the deprecated
 *         AuthPluginError::PLUGIN_ERROR_GENERAL).
 */
#include "SignOn/authpluginif.h"

// signon-plugins-common
#include "SignOn/blobiohandler.h"
#include "SignOn/encrypteddevice.h"
#include "SignOn/ipc.h"

#include <SignOnCrypto/Encryptor>

#include <openssl/rand.h>

using namespace SignOn;

//TODO get this from config
#define REMOTEPLUGIN_BIN_PATH QLatin1String("/usr/bin/signonpluginprocess")
#define PLUGINPROCESS_START_TIMEOUT 5000
#define PLUGINPROCESS_STOP_TIMEOUT 1000

using namespace SignOn;

namespace SignonDaemonNS {

    /* ---------------------- PluginProcess ---------------------- */

    PluginProcess::PluginProcess(QObject *parent) : QProcess(parent)
    {
    }

    PluginProcess::~PluginProcess()
    {
    }

    void PluginProcess::setupChildProcess()
    {
        // Drop all root privileges in the child process and switch to signon user
#ifndef NO_SIGNON_USER
        //get uid and gid
        struct passwd *passwdRecord = getpwnam("signon");
        if ( !passwdRecord ){
            fprintf(stderr, "failed to get user: signon\n");
            emit QProcess::finished(2, QProcess::NormalExit);
            exit(2);
        }
#ifdef SIGNOND_TRACE
        //this is run in remote plugin process, so trace should go to stderr
        fprintf(stderr, "got user: %s with uid: %d\n", passwdRecord->pw_name, passwdRecord->pw_uid);
#endif
        if (( ::setgid(passwdRecord->pw_gid))
                || (::setuid(passwdRecord->pw_uid))
                || (::getuid() != passwdRecord->pw_uid)
                ) {
            fprintf(stderr, "failed to set user: %s with uid: %d", passwdRecord->pw_name, passwdRecord->pw_uid);
            emit QProcess::finished(2, QProcess::NormalExit);
            exit(2);
        }
 #endif
    }

    /* ---------------------- PluginProxy ---------------------- */

    PluginProxy::PluginProxy(QString type, QObject *parent)
            : QObject(parent)
    {
        TRACE();

        m_type = type;
        m_isProcessing = false;
        m_isResultObtained = false;
        m_currentResultOperation = -1;
        m_process = new PluginProcess(this);

#ifdef SIGNOND_TRACE
        if (criticalsEnabled()) {
            const char *level = debugEnabled() ? "2" : "1";
            QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
            env.insert(QLatin1String("SSO_DEBUG"), QLatin1String(level));
            m_process->setProcessEnvironment(env);
        }
#endif

        connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(onReadStandardError()));

        /*
         * TODO: some error handling should be added here, at least remove of current
         * request data from the top of the queue and reply an error code
         * */
        connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onExit(int, QProcess::ExitStatus)));
        connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
    }

    PluginProxy::~PluginProxy()
    {
        if (m_process != NULL &&
            m_process->state() != QProcess::NotRunning)
        {
            if (m_isProcessing)
                cancel();

            stop();

            if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT)) {
                qCritical() << "The signon plugin does not react on demand to stop: need to kill it!!!";
                m_process->kill();

                if (!m_process->waitForFinished(PLUGINPROCESS_STOP_TIMEOUT))
                {
                    if (m_process->pid()) {
                        qCritical() << "The signon plugin seems to ignore kill(), killing it from command line";
                        QString killProcessCommand(QString::fromLatin1("kill -9 %1").arg(m_process->pid()));
                        QProcess::execute(killProcessCommand);
                    }
                }
            }
        }
    }

    PluginProxy* PluginProxy::createNewPluginProxy(const QString &type)
    {
        PluginProxy *pp = new PluginProxy(type);

        QStringList args = QStringList() << pp->m_type;
        pp->m_process->start(REMOTEPLUGIN_BIN_PATH, args);

        QByteArray tmp;

        if (!pp->waitForStarted(PLUGINPROCESS_START_TIMEOUT)) {
            TRACE() << "The process cannot be started";
            delete pp;
            return NULL;
        }

        if (!pp->readOnReady(tmp, PLUGINPROCESS_START_TIMEOUT)) {
            TRACE() << "The process cannot load plugin";
            delete pp;
            return NULL;
        }

        if (debugEnabled()) {
            QString pluginType = pp->queryType();
            if (pluginType != pp->m_type) {
                BLAME() << QString::fromLatin1("Plugin returned type '%1', "
                                               "expected '%2'").
                    arg(pluginType).arg(pp->m_type);
            }
        }
{'fraction_non_alphanumeric': 0.12133088725817212, 'fraction_numerical': 0.007254836557705137, 'mean_word_length': 2.3984131482006235, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 68, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '746534', 'n_tokens_mistral': 3505, 'n_tokens_neox': 3337, 'n_words': 928}