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);
// Set authentication state
if (serverSettings->isLocal) {
socket->setProperty("Authenticated", true);
sockets << socket; 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,8 +52,10 @@ 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 (likely(isAuthenticated)) {
if (readData.startsWith("+dbg")) { if (readData.startsWith("+dbg")) {
socket->setProperty("ReceiveDbgMsg", true); socket->setProperty("ReceiveDbgMsg", true);
socket->write("Debug messages enabled!\n"); socket->write("Debug messages enabled!\n");
@ -60,6 +72,17 @@ void SMSubServer::readyRead()
socket->setProperty("ReceiveLog", false); socket->setProperty("ReceiveLog", false);
debugOutput(socket, "Log output disabled!"); 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")) { else if (readData.startsWith("kill")) {
emit killRequested(); emit killRequested();
debugOutput(socket, "Killing server!"); debugOutput(socket, "Killing server!");
@ -79,6 +102,22 @@ void SMSubServer::readyRead()
debugOutput(socket, "Write \"" + 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;
}
}
}
} }
void SMSubServer::deleteSocket() void SMSubServer::deleteSocket()
@ -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();