2021-05-21 13:22:26 +02:00
|
|
|
#include <QtCore/QCoreApplication>
|
|
|
|
|
#include <QtDBus/QtDBus>
|
|
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
2024-02-18 22:13:16 +01:00
|
|
|
#include <iostream>
|
|
|
|
|
|
2024-02-19 12:52:17 +01:00
|
|
|
constexpr const char *SERVICE_NAME = "org.tom.IsolationRunner";
|
2021-05-21 13:22:26 +02:00
|
|
|
|
|
|
|
|
class Client: public QObject
|
|
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
2024-02-18 22:13:16 +01:00
|
|
|
public:
|
|
|
|
|
void setVerbose(bool on) {
|
|
|
|
|
m_verbose = on;
|
|
|
|
|
}
|
2024-02-19 19:52:24 +01:00
|
|
|
void setApp(const QString &appName, const QString &appPath) {
|
|
|
|
|
m_appName = appName;
|
|
|
|
|
m_appPath = appPath;
|
2024-02-18 22:13:16 +01:00
|
|
|
}
|
|
|
|
|
|
2024-02-19 19:52:24 +01:00
|
|
|
void setListDetails(bool getDetails) {
|
|
|
|
|
m_listDetails = getDetails;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setAppArguments(const QList<QString> &arguments) {
|
|
|
|
|
m_arguments = arguments;
|
|
|
|
|
}
|
2024-02-18 23:42:08 +01:00
|
|
|
|
2024-02-25 23:29:33 +01:00
|
|
|
bool autodelete() const {
|
|
|
|
|
return m_autodelete;
|
|
|
|
|
}
|
|
|
|
|
void setAutodelete(bool ad) {
|
|
|
|
|
m_autodelete = ad;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool lockedDown() const {
|
|
|
|
|
return m_lockedDown;
|
|
|
|
|
}
|
|
|
|
|
void setLockedDown(bool newLockedDown) {
|
|
|
|
|
m_lockedDown = newLockedDown;
|
|
|
|
|
}
|
2024-04-21 23:55:14 +02:00
|
|
|
void setEnclosingJailId(const QString &jailId) {
|
|
|
|
|
m_enclosingJailId = jailId;
|
|
|
|
|
}
|
2024-02-25 23:29:33 +01:00
|
|
|
|
2026-04-11 14:54:32 +02:00
|
|
|
void setVpnOVPNPath(const QString &newVpnOVPNPath)
|
|
|
|
|
{
|
|
|
|
|
m_vpnOVPNPath = newVpnOVPNPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setVpnAcPath(const QString &newVpnAcPath)
|
|
|
|
|
{
|
|
|
|
|
m_vpnAcPath = newVpnAcPath;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 13:22:26 +02:00
|
|
|
public slots:
|
|
|
|
|
void start() {
|
|
|
|
|
struct ShutDown {
|
2024-02-18 22:13:16 +01:00
|
|
|
~ShutDown() { QCoreApplication::exit(rc); }
|
|
|
|
|
int rc = 0;
|
2021-05-21 13:22:26 +02:00
|
|
|
};
|
|
|
|
|
ShutDown shutDownHandler;
|
|
|
|
|
// find our remote
|
2024-02-18 22:13:16 +01:00
|
|
|
auto iface = new QDBusInterface(SERVICE_NAME, "/apps", SERVICE_NAME,
|
2021-05-21 13:22:26 +02:00
|
|
|
QDBusConnection::sessionBus(), this);
|
|
|
|
|
if (!iface->isValid()) {
|
2024-02-18 22:13:16 +01:00
|
|
|
shutDownHandler.rc = 1;
|
|
|
|
|
std::cerr << "Could not connect" << std::endl;
|
|
|
|
|
if (m_verbose)
|
|
|
|
|
std::cerr << "[" << qPrintable(QDBusConnection::sessionBus().lastError().message()) << "]" << std::endl;
|
2021-05-21 13:22:26 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-20 20:27:07 +01:00
|
|
|
QTextStream out(stdout);
|
2024-02-19 20:17:07 +01:00
|
|
|
QDBusMessage rc;
|
|
|
|
|
if (m_listDetails && m_appName.isEmpty() && m_appPath.isEmpty()) {
|
2024-02-25 19:48:41 +01:00
|
|
|
rc = iface->callWithArgumentList(QDBus::Block, "listProfiles",
|
|
|
|
|
QVariantList() << QVariant::fromValue<bool>(m_verbose));
|
2024-02-20 20:27:07 +01:00
|
|
|
out << "Available profiles:" << Qt::endl << Qt::endl;
|
2024-02-19 20:17:07 +01:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
QVariantList list;
|
|
|
|
|
list << m_appName;
|
|
|
|
|
if (!m_listDetails) {
|
|
|
|
|
// then its a call to 'run2'.
|
|
|
|
|
list.append(m_arguments);
|
|
|
|
|
list << m_appPath;
|
2024-02-25 23:29:33 +01:00
|
|
|
list << QVariant::fromValue(m_lockedDown);
|
|
|
|
|
list << QVariant::fromValue(m_autodelete);
|
2024-04-21 23:55:14 +02:00
|
|
|
list << QVariant::fromValue(m_enclosingJailId);
|
2026-04-11 14:54:32 +02:00
|
|
|
list << QVariant::fromValue(m_vpnAcPath);
|
|
|
|
|
list << QVariant::fromValue(m_vpnOVPNPath);
|
2024-02-19 20:17:07 +01:00
|
|
|
}
|
|
|
|
|
rc = iface->callWithArgumentList(QDBus::Block,
|
|
|
|
|
m_listDetails ? "details" : "run2", list);
|
2021-05-21 13:22:26 +02:00
|
|
|
}
|
2024-02-18 22:13:16 +01:00
|
|
|
if (rc.type() == QDBusMessage::ErrorMessage) {
|
|
|
|
|
QTextStream err(stderr);
|
2024-02-19 20:17:07 +01:00
|
|
|
err << "Failed: " << rc.errorMessage() << Qt::endl;
|
2024-02-19 10:47:36 +01:00
|
|
|
shutDownHandler.rc = 1;
|
2024-02-18 22:13:16 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (rc.type() != QDBusMessage::ReplyMessage) {
|
|
|
|
|
qWarning() << "huh? Not a reply received";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto &line : rc.arguments()) {
|
|
|
|
|
if (line.canConvert(QMetaType::QString)) {
|
2024-02-18 23:42:08 +01:00
|
|
|
QString content = line.toString();
|
|
|
|
|
if (m_listDetails) {
|
|
|
|
|
printDetailsXml(out, content);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-02-18 22:13:16 +01:00
|
|
|
out << line.toString() << Qt::endl;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// probably a wrapped one then.
|
|
|
|
|
auto embedded = line.value<QDBusVariant>().variant();
|
|
|
|
|
if (embedded.isValid()) {
|
2024-02-19 20:17:07 +01:00
|
|
|
if (embedded.canConvert(QMetaType::QStringList)) {
|
|
|
|
|
for (const auto &s : embedded.toStringList()) {
|
|
|
|
|
out << s << Qt::endl;
|
|
|
|
|
}
|
|
|
|
|
} else if (embedded.canConvert(QMetaType::QString)) {
|
|
|
|
|
out << embedded.toString() << Qt::endl;
|
|
|
|
|
}
|
2024-02-18 22:13:16 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-05-21 13:22:26 +02:00
|
|
|
}
|
2024-02-18 22:13:16 +01:00
|
|
|
|
|
|
|
|
private:
|
2024-02-18 23:42:08 +01:00
|
|
|
void printDetailsXml(QTextStream &out, const QString &xml) {
|
|
|
|
|
QXmlStreamReader parser(xml);
|
|
|
|
|
bool firstDeny = true;
|
|
|
|
|
bool firstAllow = true;
|
2024-02-25 19:48:41 +01:00
|
|
|
QStringView cur;
|
2024-02-18 23:42:08 +01:00
|
|
|
while (parser.readNextStartElement()) {
|
2024-02-25 19:48:41 +01:00
|
|
|
if (!cur.isEmpty() && cur != parser.name())
|
|
|
|
|
out << Qt::endl;
|
|
|
|
|
cur = QStringView();
|
2024-02-18 23:42:08 +01:00
|
|
|
if (parser.name() == QLatin1String("app")) {
|
|
|
|
|
auto att = parser.attributes();
|
2024-02-25 19:48:41 +01:00
|
|
|
out << "Profile: " << att.value("name") << Qt::endl;
|
|
|
|
|
out << " Path: " << att.value("exe") << Qt::endl;
|
2024-02-18 23:42:08 +01:00
|
|
|
if (m_verbose)
|
2024-02-25 19:48:41 +01:00
|
|
|
out << "Storage: $HOME/.local/jails/" << att.value("id") << "/" << Qt::endl;
|
2024-02-18 23:42:08 +01:00
|
|
|
}
|
|
|
|
|
else if (parser.name() == QLatin1String("denied")) {
|
|
|
|
|
if (firstDeny)
|
2024-02-25 19:48:41 +01:00
|
|
|
out << "Denied :";
|
2024-02-18 23:42:08 +01:00
|
|
|
firstDeny = false;
|
|
|
|
|
out << " ";
|
|
|
|
|
out << parser.readElementText();
|
2024-02-25 19:48:41 +01:00
|
|
|
cur = parser.name();
|
2024-02-18 23:42:08 +01:00
|
|
|
}
|
|
|
|
|
else if (parser.name() == QLatin1String("allowed")) {
|
2024-02-25 19:48:41 +01:00
|
|
|
if (firstAllow)
|
|
|
|
|
out << "Allowed:";
|
|
|
|
|
firstAllow = false;
|
2024-02-18 23:42:08 +01:00
|
|
|
out << " ";
|
|
|
|
|
out << parser.readElementText();
|
2024-02-25 19:48:41 +01:00
|
|
|
cur = parser.name();
|
|
|
|
|
}
|
|
|
|
|
else if (parser.name() == QLatin1String("lastRun")) {
|
|
|
|
|
auto att = parser.attributes();
|
|
|
|
|
auto start = QDateTime::fromString(att.value("start").toString(), Qt::ISODate);
|
|
|
|
|
auto convertedStart = start.toLocalTime();
|
|
|
|
|
out << "Started: " << convertedStart.toString() << Qt::endl;
|
|
|
|
|
if (!att.value("pid").isNull())
|
|
|
|
|
out << " [Currently Running] PID: " << att.value("pid").toString() << Qt::endl;
|
2024-02-18 23:42:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-18 22:13:16 +01:00
|
|
|
bool m_verbose = false;
|
2024-02-18 23:42:08 +01:00
|
|
|
bool m_listDetails = false;
|
2024-02-25 23:29:33 +01:00
|
|
|
bool m_lockedDown = false;
|
|
|
|
|
bool m_autodelete = false;
|
2024-02-19 19:52:24 +01:00
|
|
|
QString m_appName;
|
|
|
|
|
QString m_appPath;
|
2024-04-21 23:55:14 +02:00
|
|
|
QString m_enclosingJailId;
|
2024-02-19 19:52:24 +01:00
|
|
|
QStringList m_arguments;
|
2026-04-11 14:54:32 +02:00
|
|
|
|
|
|
|
|
// vpn
|
|
|
|
|
QString m_vpnOVPNPath;
|
|
|
|
|
QString m_vpnAcPath;
|
2021-05-21 13:22:26 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int main(int x, char **y) {
|
2024-02-18 22:13:16 +01:00
|
|
|
QCoreApplication app(x, y);
|
|
|
|
|
QCommandLineParser parser;
|
2024-02-19 12:52:17 +01:00
|
|
|
parser.setApplicationDescription("Isolation Application runner");
|
2024-02-25 19:48:41 +01:00
|
|
|
parser.addPositionalArgument("application", "The app you wish to run or list");
|
2024-02-18 22:13:16 +01:00
|
|
|
parser.addHelpOption(); // allows users to get an overview of options
|
2024-02-25 19:48:41 +01:00
|
|
|
QCommandLineOption details(QStringList() << "details" << "l", "list details instead");
|
|
|
|
|
parser.addOption(details);
|
2024-02-18 22:13:16 +01:00
|
|
|
QCommandLineOption verbose(QStringList() << "verbose" << "v", "More verbose output");
|
|
|
|
|
parser.addOption(verbose);
|
2024-02-25 19:48:41 +01:00
|
|
|
QCommandLineOption exe(QStringList() << "exe" << "e", "Full path of exe to run", "PATH");
|
2024-02-19 19:52:24 +01:00
|
|
|
parser.addOption(exe);
|
2024-02-25 23:29:33 +01:00
|
|
|
QCommandLineOption lockdown(QStringList() << "secure" << "s", "Run app with minimal permissions");
|
|
|
|
|
parser.addOption(lockdown);
|
|
|
|
|
QCommandLineOption autodelete(QStringList() << "rm", "Auto delete upon completion");
|
|
|
|
|
parser.addOption(autodelete);
|
2024-04-21 23:55:14 +02:00
|
|
|
QCommandLineOption inJail(QStringList() << "in", "Run exe IN argument jail", "JAIL");
|
|
|
|
|
parser.addOption(inJail);
|
2026-04-11 14:54:32 +02:00
|
|
|
QCommandLineOption vpnConf(QStringList() << "vpn", "A OVPN open vpn config file", "PATH");
|
|
|
|
|
parser.addOption(vpnConf);
|
|
|
|
|
QCommandLineOption vpnPwdFile(QStringList() << "vpnac", "VPN AccessCredentials file path", "PATH");
|
|
|
|
|
parser.addOption(vpnPwdFile);
|
2024-02-18 22:13:16 +01:00
|
|
|
parser.process(app);
|
|
|
|
|
|
|
|
|
|
|
2024-02-19 19:52:24 +01:00
|
|
|
/*
|
|
|
|
|
* Users may simply want to start an executable and they pass in that exe
|
|
|
|
|
* name as 'appName'. This should obviously be possible.
|
|
|
|
|
*
|
|
|
|
|
* What should also nicely work, it should be possible to start "socials" as
|
|
|
|
|
* an appName and on first run we can pass in the appPath (/bin/firefox) and
|
|
|
|
|
* any arguments.
|
|
|
|
|
*/
|
|
|
|
|
QString appName;
|
|
|
|
|
QString appPath;
|
|
|
|
|
if (parser.isSet(exe))
|
|
|
|
|
appPath = parser.value(exe);
|
|
|
|
|
const auto args = parser.positionalArguments();
|
|
|
|
|
if (!args.isEmpty())
|
|
|
|
|
appName = args.first();
|
|
|
|
|
|
|
|
|
|
if (appName.isEmpty())
|
|
|
|
|
appName = appPath;
|
2021-05-21 13:22:26 +02:00
|
|
|
|
2024-02-25 19:48:41 +01:00
|
|
|
if (appName.isEmpty() && !parser.isSet(details))
|
|
|
|
|
parser.showHelp(0);
|
|
|
|
|
|
2021-05-21 13:22:26 +02:00
|
|
|
if (!QDBusConnection::sessionBus().isConnected()) {
|
|
|
|
|
fprintf(stderr, "Cannot connect to the D-Bus session bus.\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Client client;
|
2024-02-18 22:13:16 +01:00
|
|
|
client.setVerbose(parser.isSet(verbose));
|
2024-02-18 23:42:08 +01:00
|
|
|
client.setListDetails(parser.isSet(details));
|
2024-02-25 23:29:33 +01:00
|
|
|
client.setAutodelete(parser.isSet(autodelete));
|
|
|
|
|
client.setLockedDown(parser.isSet(lockdown));
|
2026-04-11 14:54:32 +02:00
|
|
|
client.setVpnAcPath(parser.value(vpnPwdFile));
|
|
|
|
|
client.setVpnOVPNPath(parser.value(vpnConf));
|
2024-04-21 23:55:14 +02:00
|
|
|
if (parser.isSet(inJail))
|
|
|
|
|
client.setEnclosingJailId(parser.value(inJail));
|
2024-02-19 19:52:24 +01:00
|
|
|
client.setApp(appName, appPath);
|
|
|
|
|
client.setAppArguments(args.mid(1));
|
2021-05-21 13:22:26 +02:00
|
|
|
QTimer::singleShot(0, &client, &Client::start);
|
|
|
|
|
return app.exec();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#include "client.moc"
|