diff --git a/client/core/controllers/vpnConfigurationController.cpp b/client/core/controllers/vpnConfigurationController.cpp index 27b18fd5b..9e46737c7 100644 --- a/client/core/controllers/vpnConfigurationController.cpp +++ b/client/core/controllers/vpnConfigurationController.cpp @@ -8,6 +8,58 @@ #include "configurators/wireguard_configurator.h" #include "configurators/xray_configurator.h" +#include + +namespace { +bool openVpnConfigHasInlineBlock(const QString &config, const QString &openTag, const QString &closeTag, const QString &marker) +{ + const int start = config.indexOf(openTag); + if (start < 0) { + return false; + } + const int end = config.indexOf(closeTag, start + openTag.size()); + if (end < 0 || end <= start) { + return false; + } + + const QString block = config.mid(start + openTag.size(), end - (start + openTag.size())); + return block.contains(marker); +} + +bool openVpnConfigHasClientCredentials(const QString &config) +{ + const bool hasCert = openVpnConfigHasInlineBlock(config, "", "", "BEGIN CERTIFICATE"); + const bool hasKey = openVpnConfigHasInlineBlock(config, "", "", "BEGIN PRIVATE KEY") + || openVpnConfigHasInlineBlock(config, "", "", "BEGIN RSA PRIVATE KEY"); + return hasCert && hasKey; +} + +QString openVpnConfigFromProtocolConfig(const QJsonObject &protocolConfig) +{ + const QString directConfig = protocolConfig.value(config_key::config).toString(); + if (!directConfig.isEmpty()) { + return directConfig; + } + + const QString lastConfig = protocolConfig.value(config_key::last_config).toString(); + if (lastConfig.isEmpty()) { + return {}; + } + + const QJsonDocument lastConfigDoc = QJsonDocument::fromJson(lastConfig.toUtf8()); + if (!lastConfigDoc.isObject()) { + return {}; + } + + return lastConfigDoc.object().value(config_key::config).toString(); +} + +bool openVpnConfigUsesUserPass(const QString &config) +{ + return config.contains("auth-user-pass"); +} +} // namespace + VpnConfigurationsController::VpnConfigurationsController(const std::shared_ptr &settings, QSharedPointer serverController, QObject *parent) : QObject { parent }, m_settings(settings), m_serverController(serverController) @@ -54,6 +106,41 @@ ErrorCode VpnConfigurationsController::createProtocolConfigForContainer(const Se return errorCode; } +ErrorCode VpnConfigurationsController::ensureContainerConfigReadyForConnection(const ServerCredentials &credentials, const int serverIndex, + const DockerContainer container, QJsonObject &containerConfig) +{ + if (!ContainerProps::protocolsForContainer(container).contains(Proto::OpenVpn)) { + return ErrorCode::NoError; + } + + QJsonObject openVpnConfig = containerConfig.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject(); + const bool isThirdParty = openVpnConfig.value(config_key::isThirdPartyConfig).toBool(false); + if (isThirdParty) { + return ErrorCode::NoError; + } + + const QString config = openVpnConfigFromProtocolConfig(openVpnConfig); + const bool configHasCreds = !config.isEmpty() && openVpnConfigHasClientCredentials(config); + const bool configAllowsUserPass = !config.isEmpty() && openVpnConfigUsesUserPass(config); + if (configHasCreds || configAllowsUserPass) { + return ErrorCode::NoError; + } + + if (!credentials.isValid()) { + qWarning() << "Missing OpenVPN client credentials and no valid server credentials to regenerate config"; + return ErrorCode::OpenVpnConfigMissing; + } + + const ErrorCode regenError = createProtocolConfigForContainer(credentials, container, containerConfig); + if (regenError != ErrorCode::NoError) { + qWarning() << "Failed to regenerate OpenVPN client config:" << regenError; + return regenError; + } + + m_settings->setContainerConfig(serverIndex, container, containerConfig); + return ErrorCode::NoError; +} + ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isApiConfig, const QPair &dns, const ServerCredentials &credentials, const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol, diff --git a/client/core/controllers/vpnConfigurationController.h b/client/core/controllers/vpnConfigurationController.h index 6d0d43b05..7ba163ba2 100644 --- a/client/core/controllers/vpnConfigurationController.h +++ b/client/core/controllers/vpnConfigurationController.h @@ -18,6 +18,8 @@ public: public slots: ErrorCode createProtocolConfigForContainer(const ServerCredentials &credentials, const DockerContainer container, QJsonObject &containerConfig); + ErrorCode ensureContainerConfigReadyForConnection(const ServerCredentials &credentials, const int serverIndex, + const DockerContainer container, QJsonObject &containerConfig); ErrorCode createProtocolConfigString(const bool isApiConfig, const QPair &dns, const ServerCredentials &credentials, const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol, QString &protocolConfigString); diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index 23f43bc1f..68f79b3b6 100644 --- a/client/ui/controllers/connectionController.cpp +++ b/client/ui/controllers/connectionController.cpp @@ -56,6 +56,12 @@ void ConnectionController::openConnection() QJsonObject containerConfig = m_containersModel->getContainerConfig(container); ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex); + const ErrorCode preparationError = + vpnConfigurationController.ensureContainerConfigReadyForConnection(credentials, serverIndex, container, containerConfig); + if (preparationError != ErrorCode::NoError) { + emit connectionErrorOccurred(preparationError); + return; + } auto dns = m_serversModel->getDnsPair(serverIndex); diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 12d6a248f..c41ef0c6c 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -16,11 +16,6 @@ #include #include -#if defined(Q_OS_IOS) || defined(MACOS_NE) - #include "core/controllers/serverController.h" - #include "core/controllers/vpnConfigurationController.h" -#endif - #ifdef AMNEZIA_DESKTOP #include "core/ipcclient.h" #include @@ -39,54 +34,6 @@ #include "core/networkUtilities.h" #include "vpnconnection.h" -#if defined(Q_OS_IOS) || defined(MACOS_NE) -static bool openVpnConfigHasInlineBlock(const QString &config, const QString &openTag, const QString &closeTag, const QString &marker) -{ - const int start = config.indexOf(openTag); - if (start < 0) { - return false; - } - const int end = config.indexOf(closeTag, start + openTag.size()); - if (end < 0 || end <= start) { - return false; - } - const QString block = config.mid(start + openTag.size(), end - (start + openTag.size())); - return block.contains(marker); -} - -static bool openVpnConfigHasClientCredentials(const QString &config) -{ - const bool hasCert = openVpnConfigHasInlineBlock(config, "", "", "BEGIN CERTIFICATE"); - const bool hasKey = openVpnConfigHasInlineBlock(config, "", "", "BEGIN PRIVATE KEY") - || openVpnConfigHasInlineBlock(config, "", "", "BEGIN RSA PRIVATE KEY"); - return hasCert && hasKey; -} - -static QString openVpnConfigFromProtocolConfig(const QJsonObject &protocolConfig) -{ - const QString directConfig = protocolConfig.value(config_key::config).toString(); - if (!directConfig.isEmpty()) { - return directConfig; - } - - const QString lastConfig = protocolConfig.value(config_key::last_config).toString(); - if (lastConfig.isEmpty()) { - return {}; - } - - const QJsonDocument lastConfigDoc = QJsonDocument::fromJson(lastConfig.toUtf8()); - if (!lastConfigDoc.isObject()) { - return {}; - } - return lastConfigDoc.object().value(config_key::config).toString(); -} - -static bool openVpnConfigUsesUserPass(const QString &config) -{ - return config.contains("auth-user-pass"); -} -#endif - VpnConnection::VpnConnection(std::shared_ptr settings, QObject *parent) : QObject(parent), m_settings(settings), m_checkTimer(new QTimer(this)) { @@ -356,49 +303,6 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede m_vpnProtocol.reset(androidVpnProtocol); #elif defined Q_OS_IOS || defined(MACOS_NE) - #if defined(Q_OS_IOS) || defined(MACOS_NE) - if (ContainerProps::protocolsForContainer(container).contains(Proto::OpenVpn)) { - QJsonObject openVpnConfig = m_vpnConfiguration.value(ProtocolProps::key_proto_config_data(Proto::OpenVpn)).toObject(); - const bool isThirdParty = openVpnConfig.value(config_key::isThirdPartyConfig).toBool(false); - const QString config = openVpnConfigFromProtocolConfig(openVpnConfig); - const bool configHasCreds = !config.isEmpty() && openVpnConfigHasClientCredentials(config); - const bool configAllowsUserPass = !config.isEmpty() && openVpnConfigUsesUserPass(config); - - if (!isThirdParty && (!configHasCreds && !configAllowsUserPass)) { - if (!credentials.isValid()) { - qWarning() << "Missing OpenVPN client credentials and no valid server credentials to regenerate config"; - emit connectionStateChanged(Vpn::ConnectionState::Error); - return; - } - - QSharedPointer serverController(new ServerController(m_settings)); - VpnConfigurationsController vpnConfigurationController(m_settings, serverController); - QJsonObject containerConfig = m_settings->containerConfig(serverIndex, container); - const QString storedConfig = - openVpnConfigFromProtocolConfig(containerConfig.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject()); - const bool storedHasCreds = !storedConfig.isEmpty() && openVpnConfigHasClientCredentials(storedConfig); - if (!storedHasCreds) { - const ErrorCode regenError = - vpnConfigurationController.createProtocolConfigForContainer(credentials, container, containerConfig); - if (regenError != ErrorCode::NoError) { - qWarning() << "Failed to regenerate OpenVPN client config:" << regenError; - emit connectionStateChanged(Vpn::ConnectionState::Error); - return; - } - } - m_settings->setContainerConfig(serverIndex, container, containerConfig); - - const QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString(); - const QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString(); - QJsonObject serverConfig = m_settings->server(serverIndex); - m_vpnConfiguration = vpnConfigurationController.createVpnConfiguration({ dns1, dns2 }, - serverConfig, - containerConfig, - container); - } - } - #endif - Proto proto = ContainerProps::defaultProtocol(container); IosController::Instance()->connectVpn(proto, m_vpnConfiguration); connect(&m_checkTimer, &QTimer::timeout, IosController::Instance(), &IosController::checkStatus);