summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2024-02-07 18:25:48 +0100
committerSimon Rettberg2024-02-07 18:25:48 +0100
commitbcd99e02640cc35bb9a3f9ec9ad919b51b1c3482 (patch)
tree8ef7123813bd14be302831dff42f160692fb75fc
parentCheck server's security hash, check username and password format (diff)
downloadslxgreeter-bcd99e02640cc35bb9a3f9ec9ad919b51b1c3482.tar.gz
slxgreeter-bcd99e02640cc35bb9a3f9ec9ad919b51b1c3482.tar.xz
slxgreeter-bcd99e02640cc35bb9a3f9ec9ad919b51b1c3482.zip
Add black/whitelist feature to browser-based login
-rw-r--r--src/global.cpp36
-rw-r--r--src/global.h4
-rw-r--r--src/nam.cpp54
-rw-r--r--src/nam.h38
-rw-r--r--src/settings.h2
-rw-r--r--src/webview.cpp60
6 files changed, 177 insertions, 17 deletions
diff --git a/src/global.cpp b/src/global.cpp
index fd6e321..4efccad 100644
--- a/src/global.cpp
+++ b/src/global.cpp
@@ -80,3 +80,39 @@ QImage Global::getConfigGradient()
}
return img;
}
+
+QStringList loadUrlList(const QString &file)
+{
+ QStringList stringList;
+ QFile textFile(file);
+ if (!textFile.open(QFile::ReadOnly)) {
+ QTextStream(stdout) << "Cannot open URL list\n";
+ return QStringList();
+ }
+ QTextStream textStream(&textFile);
+ while (true)
+ {
+ QString line = textStream.readLine();
+ if (line.isNull())
+ break;
+ else
+ stringList.append(line);
+ }
+ return stringList;
+}
+
+QStringList Global::urlBlacklist()
+{
+ auto path = Settings::urlBlacklistFile();
+ if (!QFile::exists(path))
+ return QStringList();
+ return loadUrlList(path);
+}
+
+QStringList Global::urlWhitelist()
+{
+ auto path = Settings::urlWhitelistFile();
+ if (!QFile::exists(path))
+ return QStringList();
+ return loadUrlList(path);
+}
diff --git a/src/global.h b/src/global.h
index 91b878f..bdc79cd 100644
--- a/src/global.h
+++ b/src/global.h
@@ -48,6 +48,10 @@ public:
static QImage getConfigGradient();
+ static QStringList urlWhitelist();
+
+ static QStringList urlBlacklist();
+
private:
static bool m_testMode;
static QLightDM::Greeter *m_Greeter;
diff --git a/src/nam.cpp b/src/nam.cpp
new file mode 100644
index 0000000..6128c40
--- /dev/null
+++ b/src/nam.cpp
@@ -0,0 +1,54 @@
+#include "nam.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+
+SlxDisabledNetworkReply::SlxDisabledNetworkReply(QObject *parent, const QNetworkRequest &req,
+ QNetworkAccessManager::Operation op)
+ : QNetworkReply(parent)
+{
+ setRequest(req);
+ setUrl(req.url());
+ setOperation(op);
+ setFinished(true);
+ qRegisterMetaType<QNetworkReply::NetworkError>();
+ QString msg = QCoreApplication::translate("QNetworkAccessManager",
+ "Network access is disabled.");
+ setError(UnknownNetworkError, msg);
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+}
+
+QNetworkReply* SlxNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &req, QIODevice *outgoingData)
+{
+ const QUrl url(req.url());
+ //qDebug() << url;
+ bool ok;
+ if (url.isLocalFile()) {
+ ok = true;
+ } else if (url.scheme() == QLatin1String("qrc") || url.scheme() == QLatin1String("data")) {
+ ok = true;
+ } else if (url.host().isEmpty()) {
+ ok = true;
+ } else {
+ auto str = url.toDisplayString(QUrl::NormalizePathSegments);
+ if (_white.isValid() && _white.match(str).hasMatch()) {
+ // We have a whitelist, and it matches - allow
+ ok = true;
+ } else if (_black.isValid() && _black.match(str).hasMatch()) {
+ // we have a blacklist, and it matches - deny
+ ok = false;
+ } else {
+ // neither matched; if we have a whitelist: deny; otherwise: allow
+ ok = !_white.isValid();
+ }
+ }
+ if (!ok) {
+ return new SlxDisabledNetworkReply(this, req, op);
+ }
+ auto cp(req);
+ cp.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(false));
+ return QNetworkAccessManager::createRequest(op, cp, outgoingData);
+}
diff --git a/src/nam.h b/src/nam.h
new file mode 100644
index 0000000..ec2bb17
--- /dev/null
+++ b/src/nam.h
@@ -0,0 +1,38 @@
+#ifndef NAM_H_
+#define NAM_H_
+
+#include <QNetworkAccessManager>
+#include <QRegularExpression>
+#include <QNetworkReply>
+
+/**
+ * Block certain requests based on URL
+ */
+class SlxNetworkAccessManager : public QNetworkAccessManager
+{
+Q_OBJECT
+public:
+ SlxNetworkAccessManager(QRegularExpression blackList, QRegularExpression whiteList, QObject *parent = nullptr)
+ : QNetworkAccessManager(parent), _black(blackList), _white(whiteList) {}
+protected:
+ QNetworkReply* createRequest(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &originalReq, QIODevice *outgoingData = nullptr) override;
+private:
+ QRegularExpression _black, _white;
+};
+
+
+class SlxDisabledNetworkReply : public QNetworkReply
+{
+ Q_OBJECT
+public:
+ SlxDisabledNetworkReply(QObject *parent, const QNetworkRequest &req,
+ QNetworkAccessManager::Operation op);
+
+ ~SlxDisabledNetworkReply() {}
+ void abort() override { }
+protected:
+ qint64 readData(char *, qint64) override { return -1; }
+};
+
+#endif
diff --git a/src/settings.h b/src/settings.h
index fe5f530..b00bce6 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -49,6 +49,8 @@ public:
static bool guestSessionEnabled() { return s_settings->value("guest-session-enabled").toBool(); }
static bool shibSessionEnabled() { return s_settings->value("shib-session-enabled").toBool(); }
static QString shibUrl() { return s_settings->value("shib-url").toString(); }
+ static QString urlWhitelistFile() { return s_settings->value("shib-url-whitelist").toString(); }
+ static QString urlBlacklistFile() { return s_settings->value("shib-url-blacklist").toString(); }
static QString shibSessionButtonText() { return s_settings->value("shib-session-button-text").toString(); }
static QString userSessionButtonText() { return s_settings->value("user-session-button-text").toString(); }
static QString guestSessionButtonText() { return s_settings->value("guest-session-button-text").toString(); }
diff --git a/src/webview.cpp b/src/webview.cpp
index e0b47f3..b178d73 100644
--- a/src/webview.cpp
+++ b/src/webview.cpp
@@ -1,4 +1,7 @@
#include "webview.h"
+#include "nam.h"
+#include "global.h"
+
#include <QWebFrame>
#include <QNetworkReply>
#include <QMessageBox>
@@ -11,11 +14,12 @@
#include <QWebElement>
#include <QRegularExpression>
#include <QWebPage>
-#include <QNetworkAccessManager>
static QRegularExpression R_USER("^[a-z_A-Z][a-zA-Z0-9_@.-]{1,32}$");
static QRegularExpression R_PASS("^[a-z0-9]{1,32}$");
+static QRegularExpression urlListToRegExp(const QStringList &list);
+
// Override user-agent to make it appear mobile
class UaWebPage : public QWebPage
{
@@ -27,19 +31,6 @@ public:
}
};
-class Nam : public QNetworkAccessManager
-{
-public:
- explicit Nam(QObject *parent = nullptr) : QNetworkAccessManager(parent) {}
-protected:
- virtual QNetworkReply *createRequest(Operation op, const QNetworkRequest &request,
- QIODevice *outgoingData = nullptr) override {
- auto cp(request);
- cp.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(false));
- return QNetworkAccessManager::createRequest(op, cp, outgoingData);
- }
-};
-
QRegularExpression UaWebPage::re("(\\S+)$");
WebView::WebView(QWidget* parent)
@@ -49,12 +40,15 @@ WebView::WebView(QWidget* parent)
_inErrorState(false),
_timerReset(new QTimer(this)),
_firstLoad(false)
- {
+{
this->setPage(new UaWebPage);
_timerAbortMessage->setSingleShot(true);
_timerReset->setSingleShot(true);
connect(page(), SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested()));
- page()->setNetworkAccessManager(new Nam(this));
+ auto bl = Global::urlBlacklist();
+ auto wl = Global::urlWhitelist();
+ page()->setNetworkAccessManager(new SlxNetworkAccessManager(urlListToRegExp(bl),
+ urlListToRegExp(wl), this));
page()->setForwardUnsupportedContent(true);
page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
//page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
@@ -181,7 +175,6 @@ void WebView::reset(const QString baseUrl)
q.addQueryItem("token", _token);
url.setQuery(q);
_urls.clear();
- qDebug() << "Bahnahne";
this->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar);
this->setUrl(url);
_firstLoad = true;
@@ -191,3 +184,36 @@ void WebView::reset(const QString baseUrl)
_timerReset->stop();
});
}
+
+static QRegularExpression urlListToRegExp(const QStringList &list)
+{
+ if (list.isEmpty())
+ return QRegularExpression("(["); // Return an invalid regex, we use .isValid to check if the list is to be used
+ // We search in the escaped string, so actually look for \*\* and \*
+ // Capture char before that because it must not be another backslash, as that
+ // means the star was already escaped in the list.
+ // Since these are C strings there are some additional backslashes here.
+ static const QRegularExpression STARSTAR("(^|[^\\\\])\\\\\\*\\\\\\*");
+ static const QRegularExpression STAR("(^|[^\\\\])\\\\\\*");
+ static const QRegularExpression QUEST("(^|[^\\\\])\\\\\\?");
+ static const QString STARSTAR_REP("\\1.*");
+ static const QString STAR_REP("\\1[^/]*");
+ static const QString QUEST_REP("\\1.?");
+ QStringList regexes; // All my regex's live in Regtexas
+ for (const QString &str : list) {
+ QString mangled;
+ if (str.contains(QLatin1String("//"))) {
+ mangled = str;
+ } else if (str.contains(QLatin1Char('/')) || str.contains(QLatin1String("**"))) {
+ mangled = "*//" + str;
+ } else {
+ mangled = "*//" + str + "/**";
+ }
+ mangled = QRegularExpression::escape(mangled);
+ mangled = mangled.replace(STARSTAR, STARSTAR_REP).replace(STAR, STAR_REP)
+ .replace(QUEST, QUEST_REP);
+ regexes << mangled;
+ }
+ qDebug() << regexes;
+ return QRegularExpression("^(" + regexes.join('|') + ")$");
+}