-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix: ipc input validation #2852
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 4 commits
5d774e6
35acbad
041cdf3
a3dda0a
711699d
f03d419
213377a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,8 @@ | |
|
|
||
| #include <QObject> | ||
| #include <QString> | ||
| #include <QRegularExpression> | ||
| #include <QSet> | ||
|
|
||
| #include "../client/core/utils/utilities.h" | ||
|
|
||
|
|
@@ -15,7 +17,8 @@ enum PermittedProcess { | |
| OpenVPN, | ||
| Wireguard, | ||
| Tun2Socks, | ||
| CertUtil | ||
| CertUtil, | ||
| PermittedProcessCount | ||
| }; | ||
|
|
||
| inline QString permittedProcessPath(PermittedProcess pid) | ||
|
|
@@ -57,16 +60,46 @@ inline QStringList sanitizeArguments(PermittedProcess proc, const QStringList &a | |
| QList<Validator> positionalArgs; | ||
|
|
||
| switch (proc) { | ||
| case OpenVPN: { | ||
| // Whitelist only args actually used by the client: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please keep the code clean from the slope comments. They do not provide any information - it is written below
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
| // --config <path>, --management <host> <port>, --management-client | ||
| QStringList out; | ||
| for (int i = 0; i < args.size(); ++i) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a mechanism of validation, which is already implemented. Why do we need the same thingy twice?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reused existed code |
||
| const QString &arg = args[i]; | ||
| if (arg == QStringLiteral("--config") && i + 1 < args.size()) { | ||
| out << arg << args[++i]; | ||
| } else if (arg == QStringLiteral("--management") && i + 2 < args.size()) { | ||
| out << arg << args[i + 1] << args[i + 2]; | ||
| i += 2; | ||
| } else if (arg == QStringLiteral("--management-client")) { | ||
| out << arg; | ||
| } else { | ||
| qWarning() << "IPC: blocked unknown OpenVPN argument:" << arg; | ||
| } | ||
| } | ||
| return out; | ||
| } | ||
| case Wireguard: { | ||
| // Whitelist only subcommand + config file path (wg-quick up/down <conf>) | ||
| if (args.size() == 2) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
So, I would recommend neither:
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed it |
||
| const QString &sub = args[0]; | ||
| if (sub == QStringLiteral("up") || sub == QStringLiteral("down")) { | ||
| return args; | ||
| } | ||
| } | ||
| qWarning() << "IPC: blocked unexpected WireGuard arguments"; | ||
| return {}; | ||
| } | ||
| case Tun2Socks: | ||
| namedArgs["-device"] = [](const QString& v) { return v.startsWith("tun://"); }; | ||
| namedArgs["-proxy"] = [](const QString& v) { return v.startsWith("socks5://"); }; | ||
| break; | ||
| default: | ||
| //FIXME | ||
| case CertUtil: | ||
| return args; | ||
| default: | ||
| return {}; | ||
| } | ||
|
|
||
|
|
||
| QStringList sanitized; | ||
|
|
||
| for (int i = 0, pos = 0; i < args.size(); i++) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,27 @@ | |
| #include "tapcontroller_win.h" | ||
| #endif | ||
|
|
||
| #ifdef Q_OS_LINUX | ||
| #include <sys/socket.h> | ||
| #include <sys/types.h> | ||
|
|
||
| extern uid_t g_allowedUid; | ||
| extern bool g_allowedUidSet; | ||
|
|
||
| static bool checkPrivPeerCredentials(QLocalSocket *socket) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question as for the WireGuard's localServer daemon. This feature is doubtful
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed |
||
| struct ucred cred{}; | ||
| socklen_t len = sizeof(cred); | ||
| if (getsockopt(socket->socketDescriptor(), SOL_SOCKET, SO_PEERCRED, &cred, &len) != 0) { | ||
| qWarning() << "IpcServer: SO_PEERCRED failed, rejecting privileged process connection"; | ||
| return false; | ||
| } | ||
| if (cred.uid == 0) return true; | ||
| if (g_allowedUidSet && cred.uid == g_allowedUid) return true; | ||
| qWarning() << "IpcServer: rejected privileged process connection from unauthorized UID" << cred.uid; | ||
| return false; | ||
| } | ||
| #endif | ||
|
|
||
|
|
||
| IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent) | ||
| { | ||
|
|
@@ -48,8 +69,16 @@ int IpcServer::createPrivilegedProcess() | |
| // Make sure any connections are handed to QtRO | ||
| QObject::connect(pd.localServer.data(), &QLocalServer::newConnection, this, [pd]() { | ||
| qDebug() << "IpcServer new connection"; | ||
| QLocalSocket *conn = pd.localServer->nextPendingConnection(); | ||
| #ifdef Q_OS_LINUX | ||
| if (!checkPrivPeerCredentials(conn)) { | ||
| conn->close(); | ||
| conn->deleteLater(); | ||
| return; | ||
| } | ||
| #endif | ||
| if (pd.serverNode) { | ||
| pd.serverNode->addHostSideConnection(pd.localServer->nextPendingConnection()); | ||
| pd.serverNode->addHostSideConnection(conn); | ||
| pd.serverNode->enableRemoting(pd.ipcProcess.data()); | ||
| } | ||
| }); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,6 +77,11 @@ void IpcServerProcess::setProcessChannelMode(QProcess::ProcessChannelMode mode) | |
|
|
||
| void IpcServerProcess::setProgram(int programId) | ||
| { | ||
| if (programId <= static_cast<int>(amnezia::PermittedProcess::Invalid) || | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think these checks are making the code to look complex without a reason, and they do nothing actually.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add assert |
||
| programId >= static_cast<int>(amnezia::PermittedProcess::PermittedProcessCount)) { | ||
| qWarning() << "IPC: invalid programId" << programId << ", ignoring"; | ||
| return; | ||
| } | ||
| m_program = static_cast<amnezia::PermittedProcess>(programId); | ||
| m_process->setProgram(amnezia::permittedProcessPath(m_program)); | ||
| m_process->setArguments({}); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.