Token secured remote server for websockify

This commit is contained in:
Syping 2020-09-14 08:02:06 +02:00
parent 2b5b5c22f2
commit f13345e0f2
3 changed files with 160 additions and 52 deletions

View file

@ -17,10 +17,12 @@
*****************************************************************************/
#include <QCoreApplication>
#include <QTimer>
#include <QUuid>
#include "SMSubServer.h"
#include "smsub.h"
SMSubServer::SMSubServer(const QString &socket)
SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &socket) : serverSettings(serverSettings)
{
setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::GroupAccessOption);
listen(socket);
@ -30,9 +32,17 @@ void SMSubServer::incomingConnection(quintptr socketDescriptor)
{
QLocalSocket *socket = new QLocalSocket();
socket->setSocketDescriptor(socketDescriptor);
QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
QObject::connect(socket, SIGNAL(disconnected()), this, SLOT(deleteSocket()));
sockets << socket;
QObject::connect(socket, &QLocalSocket::readyRead, this, &SMSubServer::readyRead);
QObject::connect(socket, &QLocalSocket::disconnected, this, &SMSubServer::deleteSocket);
// Set authentication state
if (serverSettings->isLocal) {
socket->setProperty("Authenticated", true);
sockets << socket;
}
else {
socket->setProperty("Authenticated", false);
}
// Initial open writing
socket->write(QString("SMSub Version %1\n").arg(QCoreApplication::applicationVersion()).toUtf8());
@ -42,41 +52,70 @@ void SMSubServer::readyRead()
{
// Manage client input
QLocalSocket *socket = (QLocalSocket*)sender();
bool isAuthenticated = socket->property("Authenticated").toBool();
while (socket->canReadLine()) {
const QByteArray readData = socket->readLine().trimmed();
if (readData.startsWith("+dbg")) {
socket->setProperty("ReceiveDbgMsg", true);
socket->write("Debug messages enabled!\n");
if (likely(isAuthenticated)) {
if (readData.startsWith("+dbg")) {
socket->setProperty("ReceiveDbgMsg", true);
socket->write("Debug messages enabled!\n");
}
else if (readData.startsWith("-dbg")) {
socket->setProperty("ReceiveDbgMsg", false);
socket->write("Debug messages disabled!\n");
}
else if (readData.startsWith("+log")) {
socket->setProperty("ReceiveLog", true);
debugOutput(socket, "Log output enabled!");
}
else if (readData.startsWith("-log")) {
socket->setProperty("ReceiveLog", false);
debugOutput(socket, "Log output disabled!");
}
else if (readData.startsWith("+reg")) {
if (likely(serverSettings->canRegister)) {
QByteArray authUuid = QUuid::createUuid().toByteArray(QUuid::Id128);
authUuid = QByteArray::fromHex(authUuid).toBase64(QByteArray::OmitTrailingEquals);
emit tokenRegistered(QString::fromUtf8(authUuid));
socket->write("Token: " + authUuid + '\n');
}
else {
socket->write("Permission denied!\n");
}
}
else if (readData.startsWith("kill")) {
emit killRequested();
debugOutput(socket, "Killing server!");
}
else if (readData.startsWith("stop")) {
emit stopRequested();
debugOutput(socket, "Stopping server!");
}
else if (readData.startsWith("wl")) {
const QByteArray writeData = readData.mid(3);
emit inputWritten(writeData + '\n');
debugOutput(socket, "Write line \"" + writeData + "\"!");
}
else if (readData.startsWith("w")) {
const QByteArray writeData = readData.mid(2);
emit inputWritten(writeData);
debugOutput(socket, "Write \"" + writeData + "\"!");
}
}
else if (readData.startsWith("-dbg")) {
socket->setProperty("ReceiveDbgMsg", false);
socket->write("Debug messages disabled!\n");
}
else if (readData.startsWith("+log")) {
socket->setProperty("ReceiveLog", true);
debugOutput(socket, "Log output enabled!");
}
else if (readData.startsWith("-log")) {
socket->setProperty("ReceiveLog", false);
debugOutput(socket, "Log output disabled!");
}
else if (readData.startsWith("kill")) {
emit killRequested();
debugOutput(socket, "Killing server!");
}
else if (readData.startsWith("stop")) {
emit stopRequested();
debugOutput(socket, "Stopping server!");
}
else if (readData.startsWith("wl")) {
const QByteArray writeData = readData.mid(3);
emit inputWritten(writeData + '\n');
debugOutput(socket, "Write line \"" + writeData + "\"!");
}
else if (readData.startsWith("w")) {
const QByteArray writeData = readData.mid(2);
emit inputWritten(writeData);
debugOutput(socket, "Write \"" + writeData + "\"!");
else {
if (unlikely(tokens.contains(QString::fromUtf8(readData)))) {
socket->setProperty("Authenticated", true);
socket->write("Login successful!\n");
isAuthenticated = true;
sockets << socket;
}
else {
// Stop receiving data and disconnect socket
QObject::disconnect(socket, &QLocalSocket::readyRead, this, &SMSubServer::readyRead);
socket->write("Incorrect token!\n");
socket->disconnectFromServer();
return;
}
}
}
}
@ -117,3 +156,11 @@ void SMSubServer::writeOutput(const QByteArray &output)
it++;
}
}
void SMSubServer::registerToken(const QString &token)
{
tokens << token;
QTimer::singleShot(30000, [this, token]() {
tokens.removeAll(token);
});
}

View file

@ -23,14 +23,21 @@
#include <QLocalSocket>
#include <QObject>
struct SMSubServerSettings
{
bool canRegister;
bool isLocal;
};
class SMSubServer : public QLocalServer
{
Q_OBJECT
public:
SMSubServer(const QString &socket);
SMSubServer(SMSubServerSettings *serverSettings, const QString &socket);
public slots:
void writeOutput(const QByteArray &output);
void registerToken(const QString &token);
private slots:
void incomingConnection(quintptr socketDescriptor);
@ -39,9 +46,12 @@ private slots:
private:
inline void debugOutput(QLocalSocket *socket, const QByteArray &message);
SMSubServerSettings *serverSettings;
QVector<QLocalSocket*> sockets;
QVector<QString> tokens;
signals:
void tokenRegistered(const QString &password);
void inputWritten(const QByteArray &input);
void killRequested();
void stopRequested();

View file

@ -45,7 +45,7 @@ void catchUnixSignals(std::initializer_list<int> quitSignals) {
sigset_t blocking_mask;
sigemptyset(&blocking_mask);
for (auto sig : quitSignals)
for (int sig : quitSignals)
sigaddset(&blocking_mask, sig);
struct sigaction sa;
@ -53,7 +53,7 @@ void catchUnixSignals(std::initializer_list<int> quitSignals) {
sa.sa_mask = blocking_mask;
sa.sa_flags = 0;
for (auto sig : quitSignals)
for (int sig : quitSignals)
sigaction(sig, &sa, nullptr);
}
#endif
@ -62,7 +62,7 @@ int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
a.setApplicationName("Server Manager Subprocess");
a.setApplicationVersion("0.1");
a.setApplicationVersion("0.2");
#ifdef Q_OS_UNIX
catchUnixSignals({SIGQUIT, SIGINT, SIGTERM, SIGHUP});
@ -82,12 +82,19 @@ int main(int argc, char *argv[])
commandLineParser.addOption(processArguments);
#ifdef Q_OS_WIN
QCommandLineOption subprocessSocket(QStringList() << "sock" << "socket", "IPC socket used for communication.", "sock");
QCommandLineOption subprocessSocket(QStringList() << "sock" << "socket", "IPC socket used for local communication.", "sock");
#else
QCommandLineOption subprocessSocket(QStringList() << "sock" << "socket", "Unix socket used for communication.", "sock");
QCommandLineOption subprocessSocket(QStringList() << "sock" << "socket", "Unix socket used for local communication.", "sock");
#endif
commandLineParser.addOption(subprocessSocket);
#ifdef Q_OS_WIN
QCommandLineOption subprocessRemoteSocket(QStringList() << "rsock" << "rsocket", "IPC socket used for remote communication.", "rsock");
#else
QCommandLineOption subprocessRemoteSocket(QStringList() << "rsock" << "rsocket", "Unix socket used for remote communication.", "rsock");
#endif
commandLineParser.addOption(subprocessRemoteSocket);
commandLineParser.process(a);
if (unlikely(commandLineParser.isSet(processManifest) && commandLineParser.isSet(processExecutable))) {
@ -163,24 +170,68 @@ int main(int argc, char *argv[])
if (likely(commandLineParser.isSet(subprocessSocket))) {
socket = commandLineParser.value(subprocessSocket);
}
SMSubServer subServer(socket);
if (unlikely(!subServer.isListening())) {
else {
#ifdef Q_OS_WIN
QTextStream(stderr) << "Failed to start IPC socket!" << endl;
QTextStream(stderr) << "You must define at least a local IPC socket!" << endl;
#else
QTextStream(stderr) << "Failed to start Unix socket!" << endl;
QTextStream(stderr) << "You must define at least a local Unix socket!" << endl;
#endif
return 1;
}
SMSubServerSettings localSettings;
localSettings.isLocal = true;
SMSubServer subLocal(&localSettings, socket);
if (unlikely(!subLocal.isListening())) {
#ifdef Q_OS_WIN
QTextStream(stderr) << "Failed to start local IPC socket!" << endl;
#else
QTextStream(stderr) << "Failed to start local Unix socket!" << endl;
#endif
return 1;
}
QString rsocket;
if (unlikely(commandLineParser.isSet(subprocessRemoteSocket))) {
rsocket = commandLineParser.value(subprocessRemoteSocket);
}
SMSubServerSettings remoteSettings;
remoteSettings.canRegister = false;
remoteSettings.isLocal = false;
SMSubServer subRemote(&remoteSettings, rsocket);
if (unlikely(!rsocket.isEmpty())) {
if (unlikely(!subRemote.isListening())) {
#ifdef Q_OS_WIN
QTextStream(stderr) << "Failed to start remote IPC socket!" << endl;
#else
QTextStream(stderr) << "Failed to start remote Unix socket!" << endl;
#endif
return 1;
}
localSettings.canRegister = true;
}
else {
localSettings.canRegister = false;
}
SMSubProcess subProcess(executable, argumentList, workingDirectory);
QObject::connect(&subProcess, SIGNAL(outputWritten(QByteArray)), &subServer, SLOT(writeOutput(QByteArray)));
QObject::connect(&subServer, SIGNAL(inputWritten(QByteArray)), &subProcess, SLOT(writeInput(QByteArray)));
QObject::connect(&subServer, SIGNAL(killRequested()), &subProcess, SLOT(killProcess()));
QObject::connect(&subServer, SIGNAL(stopRequested()), &subProcess, SLOT(stopProcess()));
QObject::connect(&a, SIGNAL(aboutToQuit()), &subProcess, SLOT(aboutToQuit()));
QObject::connect(&subProcess, &SMSubProcess::outputWritten, &subLocal, &SMSubServer::writeOutput);
QObject::connect(&subLocal, &SMSubServer::inputWritten, &subProcess, &SMSubProcess::writeInput);
QObject::connect(&subLocal, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess);
QObject::connect(&subLocal, &SMSubServer::stopRequested, &subProcess, &SMSubProcess::stopProcess);
QObject::connect(&a, &QCoreApplication::aboutToQuit, &subProcess, &SMSubProcess::aboutToQuit);
if (unlikely(!rsocket.isEmpty())) {
QObject::connect(&subLocal, &SMSubServer::tokenRegistered, &subRemote, &SMSubServer::registerToken);
QObject::connect(&subProcess, &SMSubProcess::outputWritten, &subRemote, &SMSubServer::writeOutput);
QObject::connect(&subRemote, &SMSubServer::inputWritten, &subProcess, &SMSubProcess::writeInput);
QObject::connect(&subRemote, &SMSubServer::killRequested, &subProcess, &SMSubProcess::killProcess);
QObject::connect(&subRemote, &SMSubServer::stopRequested, &subProcess, &SMSubProcess::stopProcess);
}
subProcess.start();