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 <QCoreApplication>
#include <QTimer>
#include <QUuid>
#include "SMSubServer.h" #include "SMSubServer.h"
#include "smsub.h" #include "smsub.h"
SMSubServer::SMSubServer(const QString &socket) SMSubServer::SMSubServer(SMSubServerSettings *serverSettings, const QString &socket) : serverSettings(serverSettings)
{ {
setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::GroupAccessOption); setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::GroupAccessOption);
listen(socket); listen(socket);
@ -30,9 +32,17 @@ void SMSubServer::incomingConnection(quintptr socketDescriptor)
{ {
QLocalSocket *socket = new QLocalSocket(); QLocalSocket *socket = new QLocalSocket();
socket->setSocketDescriptor(socketDescriptor); socket->setSocketDescriptor(socketDescriptor);
QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead())); QObject::connect(socket, &QLocalSocket::readyRead, this, &SMSubServer::readyRead);
QObject::connect(socket, SIGNAL(disconnected()), this, SLOT(deleteSocket())); QObject::connect(socket, &QLocalSocket::disconnected, this, &SMSubServer::deleteSocket);
sockets << socket;
// Set authentication state
if (serverSettings->isLocal) {
socket->setProperty("Authenticated", true);
sockets << socket;
}
else {
socket->setProperty("Authenticated", false);
}
// Initial open writing // Initial open writing
socket->write(QString("SMSub Version %1\n").arg(QCoreApplication::applicationVersion()).toUtf8()); socket->write(QString("SMSub Version %1\n").arg(QCoreApplication::applicationVersion()).toUtf8());
@ -42,41 +52,70 @@ void SMSubServer::readyRead()
{ {
// Manage client input // Manage client input
QLocalSocket *socket = (QLocalSocket*)sender(); QLocalSocket *socket = (QLocalSocket*)sender();
bool isAuthenticated = socket->property("Authenticated").toBool();
while (socket->canReadLine()) { while (socket->canReadLine()) {
const QByteArray readData = socket->readLine().trimmed(); const QByteArray readData = socket->readLine().trimmed();
if (readData.startsWith("+dbg")) { if (likely(isAuthenticated)) {
socket->setProperty("ReceiveDbgMsg", true); if (readData.startsWith("+dbg")) {
socket->write("Debug messages enabled!\n"); 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")) { else {
socket->setProperty("ReceiveDbgMsg", false); if (unlikely(tokens.contains(QString::fromUtf8(readData)))) {
socket->write("Debug messages disabled!\n"); socket->setProperty("Authenticated", true);
} socket->write("Login successful!\n");
else if (readData.startsWith("+log")) { isAuthenticated = true;
socket->setProperty("ReceiveLog", true); sockets << socket;
debugOutput(socket, "Log output enabled!"); }
} else {
else if (readData.startsWith("-log")) { // Stop receiving data and disconnect socket
socket->setProperty("ReceiveLog", false); QObject::disconnect(socket, &QLocalSocket::readyRead, this, &SMSubServer::readyRead);
debugOutput(socket, "Log output disabled!"); socket->write("Incorrect token!\n");
} socket->disconnectFromServer();
else if (readData.startsWith("kill")) { return;
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 + "\"!");
} }
} }
} }
@ -117,3 +156,11 @@ void SMSubServer::writeOutput(const QByteArray &output)
it++; 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 <QLocalSocket>
#include <QObject> #include <QObject>
struct SMSubServerSettings
{
bool canRegister;
bool isLocal;
};
class SMSubServer : public QLocalServer class SMSubServer : public QLocalServer
{ {
Q_OBJECT Q_OBJECT
public: public:
SMSubServer(const QString &socket); SMSubServer(SMSubServerSettings *serverSettings, const QString &socket);
public slots: public slots:
void writeOutput(const QByteArray &output); void writeOutput(const QByteArray &output);
void registerToken(const QString &token);
private slots: private slots:
void incomingConnection(quintptr socketDescriptor); void incomingConnection(quintptr socketDescriptor);
@ -39,9 +46,12 @@ private slots:
private: private:
inline void debugOutput(QLocalSocket *socket, const QByteArray &message); inline void debugOutput(QLocalSocket *socket, const QByteArray &message);
SMSubServerSettings *serverSettings;
QVector<QLocalSocket*> sockets; QVector<QLocalSocket*> sockets;
QVector<QString> tokens;
signals: signals:
void tokenRegistered(const QString &password);
void inputWritten(const QByteArray &input); void inputWritten(const QByteArray &input);
void killRequested(); void killRequested();
void stopRequested(); void stopRequested();

View file

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